summaryrefslogtreecommitdiffstats
path: root/forum
diff options
context:
space:
mode:
Diffstat (limited to 'forum')
-rw-r--r--forum/feed.py6
-rw-r--r--forum/forms.py54
-rw-r--r--forum/forms.py.orig352
-rw-r--r--forum/management/commands/clean_award_badges.py7
-rw-r--r--forum/management/commands/message_to_everyone.py12
-rw-r--r--forum/management/commands/multi_award_badges.py47
-rw-r--r--forum/management/commands/once_award_badges.py23
-rw-r--r--forum/management/commands/send_email_alerts.py106
-rw-r--r--forum/management/commands/subscribe_everyone.py9
-rw-r--r--forum/managers.py15
-rw-r--r--forum/models.py19
-rw-r--r--forum/sitemap.py3
-rw-r--r--forum/templatetags/extra_tags.py7
-rw-r--r--forum/templatetags/smart_if.py802
-rw-r--r--forum/urls.py3
-rw-r--r--forum/views.py76
16 files changed, 948 insertions, 593 deletions
diff --git a/forum/feed.py b/forum/feed.py
index ad1d5cbd..e4b929e9 100644
--- a/forum/feed.py
+++ b/forum/feed.py
@@ -13,16 +13,16 @@
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.translation import ugettext as _
from models import Question
-import settings
+from django.conf import settings
class RssLastestQuestionsFeed(Feed):
title = settings.APP_TITLE + _(' - ')+ _('latest questions')
- link = settings.APP_URL + '/' + _('question/')
+ link = settings.APP_URL #+ '/' + _('question/')
description = settings.APP_DESCRIPTION
#ttl = 10
copyright = settings.APP_COPYRIGHT
def item_link(self, item):
- return self.link + '%s/' % item.id
+ return self.link + item.get_absolute_url()
def item_author_name(self, item):
return item.author.username
diff --git a/forum/forms.py b/forum/forms.py
index ad2c5bac..308f853b 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -4,8 +4,10 @@ from django import forms
from models import *
from const import *
from django.utils.translation import ugettext as _
-from django_authopenid.forms import NextUrlField, UserNameField
-import settings
+from utils.forms import NextUrlField, UserNameField
+from recaptcha_django import ReCaptchaField
+from django.conf import settings
+import logging
class TitleField(forms.CharField):
def __init__(self, *args, **kwargs):
@@ -109,6 +111,9 @@ class ModerateUserForm(forms.ModelForm):
model = User
fields = ('is_approved',)
+class NotARobotForm(forms.Form):
+ recaptcha = ReCaptchaField()
+
class FeedbackForm(forms.Form):
name = forms.CharField(label=_('Your name:'), required=False)
email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
@@ -194,8 +199,9 @@ class EditAnswerForm(forms.Form):
self.fields['text'].initial = revision.text
class EditUserForm(forms.Form):
- email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- username = UserNameField(label=_('Screen name'))
+ email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ if settings.EDITABLE_SCREEN_NAME:
+ username = UserNameField(label=_('Screen name'))
realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
@@ -204,7 +210,10 @@ class EditUserForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super(EditUserForm, self).__init__(*args, **kwargs)
- self.fields['username'].initial = user.username
+ logging.debug('initializing the form')
+ if settings.EDITABLE_SCREEN_NAME:
+ self.fields['username'].initial = user.username
+ self.fields['username'].user_instance = user
self.fields['email'].initial = user.email
self.fields['realname'].initial = user.real_name
self.fields['website'].initial = user.website
@@ -298,14 +307,24 @@ class EditUserEmailFeedsForm(forms.Form):
self.initial = self.NO_EMAIL_INITIAL
return self
- def save(self,user):
+ def save(self,user,save_unbound=False):
+ """
+ with save_unbound==True will bypass form validation and save initial values
+ """
changed = False
for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
feed_type=feed_type)
- new_value = self.cleaned_data[form_field]
+ if save_unbound:
+ #just save initial values instead
+ if form_field in self.initial:
+ new_value = self.initial[form_field]
+ else:
+ new_value = self.fields[form_field].initial
+ else:
+ new_value = self.cleaned_data[form_field]
if s.frequency != new_value:
- s.frequency = self.cleaned_data[form_field]
+ s.frequency = new_value
s.save()
changed = True
else:
@@ -315,3 +334,22 @@ class EditUserEmailFeedsForm(forms.Form):
feed_type = ContentType.objects.get_for_model(Question)
user.followed_questions.clear()
return changed
+
+
+class SimpleEmailSubscribeForm(forms.Form):
+ SIMPLE_SUBSCRIBE_CHOICES = (
+ ('y',_('okay, let\'s try!')),
+ ('n',_('no OSQA community email please, thanks'))
+ )
+ subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
+ error_messages={'required':_('please choose one of the options above')},
+ choices=SIMPLE_SUBSCRIBE_CHOICES)
+
+ def save(self,user=None):
+ EFF = EditUserEmailFeedsForm
+ if self.cleaned_data['subscribe'] == 'y':
+ email_settings_form = EFF()
+ logging.debug('%s wants to subscribe' % user.username)
+ else:
+ email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
+ email_settings_form.save(user,save_unbound=True)
diff --git a/forum/forms.py.orig b/forum/forms.py.orig
new file mode 100644
index 00000000..42becc11
--- /dev/null
+++ b/forum/forms.py.orig
@@ -0,0 +1,352 @@
+import re
+from datetime import date
+from django import forms
+from models import *
+from const import *
+from django.utils.translation import ugettext as _
+from utils.forms import NextUrlField, UserNameField
+from recaptcha_django import ReCaptchaField
+from django.conf import settings
+import logging
+
+class TitleField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(TitleField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
+ self.max_length = 255
+ self.label = _('title')
+ self.help_text = _('please enter a descriptive title for your question')
+ self.initial = ''
+
+ def clean(self, value):
+ if len(value) < 10:
+ raise forms.ValidationError(_('title must be > 10 characters'))
+
+ return value
+
+class EditorField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(EditorField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.Textarea(attrs={'id':'editor'})
+ self.label = _('content')
+ self.help_text = u''
+ self.initial = ''
+
+ def clean(self, value):
+ if len(value) < 10:
+ raise forms.ValidationError(_('question content must be > 10 characters'))
+
+ return value
+
+class TagNamesField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(TagNamesField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
+ self.max_length = 255
+ self.label = _('tags')
+ #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
+ self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
+ self.initial = ''
+
+ def clean(self, value):
+ value = super(TagNamesField, self).clean(value)
+ data = value.strip()
+ if len(data) < 1:
+ raise forms.ValidationError(_('tags are required'))
+
+ split_re = re.compile(r'[ ,]+')
+ list = split_re.split(data)
+ list_temp = []
+ if len(list) > 5:
+ raise forms.ValidationError(_('please use 5 tags or less'))
+ for tag in list:
+ if len(tag) > 20:
+ raise forms.ValidationError(_('tags must be shorter than 20 characters'))
+ #take tag regex from settings
+ tagname_re = re.compile(r'[a-z0-9]+')
+ if not tagname_re.match(tag):
+ raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
+ # only keep one same tag
+ if tag not in list_temp and len(tag.strip()) > 0:
+ list_temp.append(tag)
+ return u' '.join(list_temp)
+
+class WikiField(forms.BooleanField):
+ def __init__(self, *args, **kwargs):
+ super(WikiField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.label = _('community wiki')
+ self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
+ def clean(self,value):
+ return value and settings.WIKI_ON
+
+class EmailNotifyField(forms.BooleanField):
+ def __init__(self, *args, **kwargs):
+ super(EmailNotifyField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.widget.attrs['class'] = 'nomargin'
+
+class SummaryField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(SummaryField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
+ self.max_length = 300
+ self.label = _('update summary:')
+ self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
+
+class ModerateUserForm(forms.ModelForm):
+ is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
+ required=False)
+
+ def clean_is_approved(self):
+ if 'is_approved' not in self.cleaned_data:
+ self.cleaned_data['is_approved'] = False
+ return self.cleaned_data['is_approved']
+
+ class Meta:
+ model = User
+ fields = ('is_approved',)
+
+class NotARobotForm(forms.Form):
+ recaptcha = ReCaptchaField()
+
+class FeedbackForm(forms.Form):
+ name = forms.CharField(label=_('Your name:'), required=False)
+ email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
+ message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
+ next = NextUrlField()
+
+class AskForm(forms.Form):
+ title = TitleField()
+ text = EditorField()
+ tags = TagNamesField()
+ wiki = WikiField()
+
+ openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
+ user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+
+class AnswerForm(forms.Form):
+ text = EditorField()
+ wiki = WikiField()
+ openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
+ user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email_notify = EmailNotifyField()
+ def __init__(self, question, user, *args, **kwargs):
+ super(AnswerForm, self).__init__(*args, **kwargs)
+ self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
+ if question.wiki and settings.WIKI_ON:
+ self.fields['wiki'].initial = True
+ if user.is_authenticated():
+ if user in question.followed_by.all():
+ self.fields['email_notify'].initial = True
+ return
+ self.fields['email_notify'].initial = False
+
+
+class CloseForm(forms.Form):
+ reason = forms.ChoiceField(choices=CLOSE_REASONS)
+
+class RetagQuestionForm(forms.Form):
+ tags = TagNamesField()
+ # initialize the default values
+ def __init__(self, question, *args, **kwargs):
+ super(RetagQuestionForm, self).__init__(*args, **kwargs)
+ self.fields['tags'].initial = question.tagnames
+
+class RevisionForm(forms.Form):
+ """
+ Lists revisions of a Question or Answer
+ """
+ revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
+
+ def __init__(self, post, latest_revision, *args, **kwargs):
+ super(RevisionForm, self).__init__(*args, **kwargs)
+ revisions = post.revisions.all().values_list(
+ 'revision', 'author__username', 'revised_at', 'summary')
+ date_format = '%c'
+ self.fields['revision'].choices = [
+ (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
+ for r in revisions]
+ self.fields['revision'].initial = latest_revision.revision
+
+class EditQuestionForm(forms.Form):
+ title = TitleField()
+ text = EditorField()
+ tags = TagNamesField()
+ summary = SummaryField()
+
+ def __init__(self, question, revision, *args, **kwargs):
+ super(EditQuestionForm, self).__init__(*args, **kwargs)
+ self.fields['title'].initial = revision.title
+ self.fields['text'].initial = revision.text
+ self.fields['tags'].initial = revision.tagnames
+ # Once wiki mode is enabled, it can't be disabled
+ if not question.wiki:
+ self.fields['wiki'] = WikiField()
+
+class EditAnswerForm(forms.Form):
+ text = EditorField()
+ summary = SummaryField()
+
+ def __init__(self, answer, revision, *args, **kwargs):
+ super(EditAnswerForm, self).__init__(*args, **kwargs)
+ self.fields['text'].initial = revision.text
+
+class EditUserForm(forms.Form):
+ email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ #username = UserNameField(label=_('Screen name'))
+ realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
+ about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
+
+ def __init__(self, user, *args, **kwargs):
+ super(EditUserForm, self).__init__(*args, **kwargs)
+ self.fields['username'].initial = user.username
+ self.fields['username'].user_instance = user
+ self.fields['email'].initial = user.email
+ self.fields['realname'].initial = user.real_name
+ self.fields['website'].initial = user.website
+ self.fields['city'].initial = user.location
+
+ if user.date_of_birth is not None:
+ self.fields['birthday'].initial = user.date_of_birth
+ else:
+ self.fields['birthday'].initial = '1990-01-01'
+ self.fields['about'].initial = user.about
+ self.user = user
+
+ def clean_email(self):
+ """For security reason one unique email in database"""
+ if self.user.email != self.cleaned_data['email']:
+ #todo dry it, there is a similar thing in openidauth
+ if settings.EMAIL_UNIQUE == True:
+ if 'email' in self.cleaned_data:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(_('this email has already been registered, please use another one'))
+ raise forms.ValidationError(_('this email has already been registered, please use another one'))
+ return self.cleaned_data['email']
+
+class TagFilterSelectionForm(forms.ModelForm):
+ tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
+ initial='ignored',
+ label=_('Choose email tag filter'),
+ widget=forms.RadioSelect)
+ class Meta:
+ model = User
+ fields = ('tag_filter_setting',)
+
+ def save(self):
+ before = self.instance.tag_filter_setting
+ super(TagFilterSelectionForm, self).save()
+ after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
+ if before != after:
+ return True
+ return False
+
+class EditUserEmailFeedsForm(forms.Form):
+ WN = (('w',_('weekly')),('n',_('no email')))
+ DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
+ FORM_TO_MODEL_MAP = {
+ 'all_questions':'q_all',
+ 'asked_by_me':'q_ask',
+ 'answered_by_me':'q_ans',
+ 'individually_selected':'q_sel',
+ }
+ NO_EMAIL_INITIAL = {
+ 'all_questions':'n',
+ 'asked_by_me':'n',
+ 'answered_by_me':'n',
+ 'individually_selected':'n',
+ }
+ asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Asked by me'))
+ answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Answered by me'))
+ individually_selected = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Individually selected'))
+ all_questions = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Entire forum (tag filtered)'),)
+
+ def set_initial_values(self,user=None):
+ KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
+ if user != None:
+ settings = EmailFeedSetting.objects.filter(subscriber=user)
+ initial_values = {}
+ for setting in settings:
+ feed_type = setting.feed_type
+ form_field = KEY_MAP[feed_type]
+ frequency = setting.frequency
+ initial_values[form_field] = frequency
+ self.initial = initial_values
+ return self
+
+ def reset(self):
+ self.cleaned_data['all_questions'] = 'n'
+ self.cleaned_data['asked_by_me'] = 'n'
+ self.cleaned_data['answered_by_me'] = 'n'
+ self.cleaned_data['individually_selected'] = 'n'
+ self.initial = self.NO_EMAIL_INITIAL
+ return self
+
+ def save(self,user,save_unbound=False):
+ """
+ with save_unbound==True will bypass form validation and save initial values
+ """
+ changed = False
+ for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
+ s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
+ feed_type=feed_type)
+ if save_unbound:
+ #just save initial values instead
+ if form_field in self.initial:
+ new_value = self.initial[form_field]
+ else:
+ new_value = self.fields[form_field].initial
+ else:
+ new_value = self.cleaned_data[form_field]
+ if s.frequency != new_value:
+ s.frequency = new_value
+ s.save()
+ changed = True
+ else:
+ if created:
+ s.save()
+ if form_field == 'individually_selected':
+ feed_type = ContentType.objects.get_for_model(Question)
+ user.followed_questions.clear()
+ return changed
+
+
+class SimpleEmailSubscribeForm(forms.Form):
+ SIMPLE_SUBSCRIBE_CHOICES = (
+ ('y',_('okay, let\'s try!')),
+ ('n',_('no OSQA community email please, thanks'))
+ )
+ subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
+ error_messages={'required':_('please choose one of the options above')},
+ choices=SIMPLE_SUBSCRIBE_CHOICES)
+
+ def save(self,user=None):
+ EFF = EditUserEmailFeedsForm
+ if self.cleaned_data['subscribe'] == 'y':
+ email_settings_form = EFF()
+ logging.debug('%s wants to subscribe' % user.username)
+ else:
+ email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
+ email_settings_form.save(user,save_unbound=True)
diff --git a/forum/management/commands/clean_award_badges.py b/forum/management/commands/clean_award_badges.py
index df3d2917..117e3a5f 100644
--- a/forum/management/commands/clean_award_badges.py
+++ b/forum/management/commands/clean_award_badges.py
@@ -21,9 +21,10 @@ from forum.models import *
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
- self.clean_awards()
- except Exception, e:
- print e
+ try:
+ self.clean_awards()
+ except Exception, e:
+ print e
finally:
connection.close()
diff --git a/forum/management/commands/message_to_everyone.py b/forum/management/commands/message_to_everyone.py
new file mode 100644
index 00000000..c020c178
--- /dev/null
+++ b/forum/management/commands/message_to_everyone.py
@@ -0,0 +1,12 @@
+from django.core.management.base import NoArgsCommand
+from django.contrib.auth.models import User
+import sys
+
+class Command(NoArgsCommand):
+ def handle_noargs(self, **options):
+ msg = None
+ if msg == None:
+ print 'to run this command, please first edit the file %s' % __file__
+ sys.exit(1)
+ for u in User.objects.all():
+ u.message_set.create(message = msg % u.username)
diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py
index 723a8cec..6b330cf9 100644
--- a/forum/management/commands/multi_award_badges.py
+++ b/forum/management/commands/multi_award_badges.py
@@ -82,27 +82,28 @@ TYPE_ACTIVITY_USER_FULL_UPDATED = 17
class Command(BaseCommand):
def handle_noargs(self, **options):
try:
- self.delete_question_be_voted_up_3()
- self.delete_answer_be_voted_up_3()
- self.delete_question_be_vote_down_3()
- self.delete_answer_be_voted_down_3()
- self.answer_be_voted_up_10()
- self.question_be_voted_up_10()
- self.question_view_1000()
- self.answer_self_question_be_voted_up_3()
- self.answer_be_voted_up_100()
- self.question_be_voted_up_100()
- self.question_be_favorited_100()
- self.question_view_10000()
- self.answer_be_voted_up_25()
- self.question_be_voted_up_25()
- self.question_be_favorited_25()
- self.question_view_2500()
- self.answer_be_accepted_and_voted_up_40()
- self.question_be_answered_after_60_days_and_be_voted_up_5()
- self.created_tag_be_used_in_question_50()
- except Exception, e:
- print e
+ try:
+ self.delete_question_be_voted_up_3()
+ self.delete_answer_be_voted_up_3()
+ self.delete_question_be_vote_down_3()
+ self.delete_answer_be_voted_down_3()
+ self.answer_be_voted_up_10()
+ self.question_be_voted_up_10()
+ self.question_view_1000()
+ self.answer_self_question_be_voted_up_3()
+ self.answer_be_voted_up_100()
+ self.question_be_voted_up_100()
+ self.question_be_favorited_100()
+ self.question_view_10000()
+ self.answer_be_voted_up_25()
+ self.question_be_voted_up_25()
+ self.question_be_favorited_25()
+ self.question_view_2500()
+ self.answer_be_accepted_and_voted_up_40()
+ self.question_be_answered_after_60_days_and_be_voted_up_5()
+ self.created_tag_be_used_in_question_50()
+ except Exception, e:
+ print e
finally:
connection.close()
@@ -317,7 +318,7 @@ class Command(BaseCommand):
object_id = row[2]
user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=objet_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
award.save()
if update_auditted:
@@ -344,4 +345,4 @@ class Command(BaseCommand):
award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
award.save()
finally:
- cursor.close() \ No newline at end of file
+ cursor.close()
diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py
index 03982c79..8c913348 100644
--- a/forum/management/commands/once_award_badges.py
+++ b/forum/management/commands/once_award_badges.py
@@ -94,17 +94,18 @@ BADGE_AWARD_TYPE_FIRST = {
class Command(BaseCommand):
def handle_noargs(self, **options):
try:
- self.alpha_user()
- self.beta_user()
- self.first_type_award()
- self.first_ask_be_voted()
- self.first_answer_be_voted()
- self.first_answer_be_voted_10()
- self.vote_count_300()
- self.edit_count_100()
- self.comment_count_10()
- except Exception, e:
- print e
+ try:
+ self.alpha_user()
+ self.beta_user()
+ self.first_type_award()
+ self.first_ask_be_voted()
+ self.first_answer_be_voted()
+ self.first_answer_be_voted_10()
+ self.vote_count_300()
+ self.edit_count_100()
+ self.comment_count_10()
+ except Exception, e:
+ print e
finally:
connection.close()
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index 283d5683..5e1eb3d0 100644
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
@@ -2,42 +2,30 @@ from django.core.management.base import NoArgsCommand
from django.db import connection
from django.db.models import Q, F
from forum.models import *
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
-=======
from forum import const
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
from django.core.mail import EmailMessage
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
import datetime
-import settings
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
-=======
+from django.conf import settings
import logging
from utils.odict import OrderedDict
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
class Command(NoArgsCommand):
def handle_noargs(self,**options):
try:
- self.send_email_alerts()
- except Exception, e:
- print e
+ try:
+ self.send_email_alerts()
+ except Exception, e:
+ print e
finally:
connection.close()
def get_updated_questions_for_user(self,user):
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- q_sel = []
- q_ask = []
- q_ans = []
- q_all = []
-=======
q_sel = None
q_ask = None
q_ans = None
q_all = None
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
now = datetime.datetime.now()
Q_set1 = Question.objects.exclude(
last_activity_by=user,
@@ -51,28 +39,17 @@ class Command(NoArgsCommand):
).exclude(
closed=True
)
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
-=======
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
for feed in user_feeds:
cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
if feed.reported_at == None or feed.reported_at <= cutoff_time:
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)
-=======
Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
feed.reported_at = now
feed.save()#may not actually report anything, depending on filters below
if feed.feed_type == 'q_sel':
q_sel = Q_set.filter(followed_by=user)
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- q_sel.cutoff_time = cutoff_time
-=======
q_sel.cutoff_time = cutoff_time #store cutoff time per query set
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
elif feed.feed_type == 'q_ask':
q_ask = Q_set.filter(author=user)
q_ask.cutoff_time = cutoff_time
@@ -80,58 +57,12 @@ class Command(NoArgsCommand):
q_ans = Q_set.filter(answers__author=user)
q_ans.cutoff_time = cutoff_time
elif feed.feed_type == 'q_all':
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- q_all = Q_set
- q_all.cutoff_time = cutoff_time
- #build list in this order
- q_tbl = {}
- def extend_question_list(src, dst):
- if isinstance(src,list):
- return
- cutoff_time = src.cutoff_time
- for q in src:
- if q in dst:
- if cutoff_time < dst[q]:
- dst[q] = cutoff_time
- else:
- dst[q] = cutoff_time
-
- extend_question_list(q_sel, q_tbl)
- extend_question_list(q_ask, q_tbl)
- extend_question_list(q_ans, q_tbl)
- extend_question_list(q_all, q_tbl)
-
- ctype = ContentType.objects.get_for_model(Question)
- out = {}
- for q, cutoff_time in q_tbl.items():
- #todo use Activity, but first start keeping more Activity records
- #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
- #get info on question edits, answer edits, comments
- out[q] = {}
- q_rev = QuestionRevision.objects.filter(question=q,revised_at__lt=cutoff_time)
- q_rev = q_rev.exclude(author=user)
- out[q]['q_rev'] = len(q_rev)
- if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
- out[q]['q_rev'] = 0
- out[q]['new_q'] = True
- else:
- out[q]['new_q'] = False
-
- new_ans = Answer.objects.filter(question=q,added_at__lt=cutoff_time)
- new_ans = new_ans.exclude(author=user)
- out[q]['new_ans'] = len(new_ans)
- ans_rev = AnswerRevision.objects.filter(answer__question=q,revised_at__lt=cutoff_time)
- ans_rev = ans_rev.exclude(author=user)
- out[q]['ans_rev'] = len(ans_rev)
- return out
- def __act_count(self,string,number,output):
-=======
if user.tag_filter_setting == 'ignored':
- ignored_tags = Tag.objects.filter(user_selections___reason='bad',user_selections__user=user)
+ ignored_tags = Tag.objects.filter(user_selections__reason='bad',user_selections__user=user)
q_all = Q_set.exclude( tags__in=ignored_tags )
else:
- selected_tags = Tag.objects.filter(user_selections___reason='good',user_selections__user=user)
+ selected_tags = Tag.objects.filter(user_selections__reason='good',user_selections__user=user)
q_all = Q_set.filter( tags__in=selected_tags )
q_all.cutoff_time = cutoff_time
#build list in this order
@@ -206,17 +137,10 @@ class Command(NoArgsCommand):
return q_list
def __action_count(self,string,number,output):
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
if number > 0:
output.append(_(string) % {'num':number})
def send_email_alerts(self):
-
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- for user in User.objects.all():
- q_list = self.get_updated_questions_for_user(user)
- num_q = len(q_list)
-=======
#todo: move this to template
for user in User.objects.all():
q_list = self.get_updated_questions_for_user(user)
@@ -227,28 +151,15 @@ class Command(NoArgsCommand):
num_q += 1
else:
num_moot += 1
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
if num_q > 0:
url_prefix = settings.APP_URL
subject = _('email update message subject')
+ print 'have %d updated questions for %s' % (num_q, user.username)
text = ungettext('%(name)s, this is an update message header for a question',
'%(name)s, this is an update message header for %(num)d questions',num_q) \
% {'num':num_q, 'name':user.username}
text += '<ul>'
-<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
- for q, act in q_list.items():
- act_list = []
- if act['new_q']:
- act_list.append(_('new question'))
- self.__act_count('%(num)d rev', act['q_rev'],act_list)
- self.__act_count('%(num)d ans', act['new_ans'],act_list)
- self.__act_count('%(num)d ans rev',act['ans_rev'],act_list)
- act_token = ', '.join(act_list)
- text += '<li><a href="%s?sort=latest">%s</a> <font color="#777777">(%s)</font></li>' \
- % (url_prefix + q.get_absolute_url(), q.title, act_token)
- text += '</ul>'
-=======
for q, meta_data in q_list.items():
act_list = []
if meta_data['nothing_new']:
@@ -273,7 +184,6 @@ class Command(NoArgsCommand):
text += _('Perhaps you could look up previously sent forum reminders in your mailbox.')
text += '</p>'
->>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
link = url_prefix + user.get_profile_url() + '?sort=email_subscriptions'
text += _('go to %(link)s to change frequency of email updates or %(email)s administrator') \
% {'link':link, 'email':settings.ADMINS[0][1]}
diff --git a/forum/management/commands/subscribe_everyone.py b/forum/management/commands/subscribe_everyone.py
index 3f8da9ec..c79528f3 100644
--- a/forum/management/commands/subscribe_everyone.py
+++ b/forum/management/commands/subscribe_everyone.py
@@ -6,14 +6,15 @@ from django.core.mail import EmailMessage
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
import datetime
-import settings
+from django.conf import settings
class Command(NoArgsCommand):
def handle_noargs(self,**options):
try:
- self.subscribe_everyone()
- except Exception, e:
- print e
+ try:
+ self.subscribe_everyone()
+ except Exception, e:
+ print e
finally:
connection.close()
diff --git a/forum/managers.py b/forum/managers.py
index 06fae761..9079e9a6 100644
--- a/forum/managers.py
+++ b/forum/managers.py
@@ -1,4 +1,5 @@
import datetime
+import time
import logging
from django.contrib.auth.models import User, UserManager
from django.db import connection, models, transaction
@@ -122,7 +123,7 @@ class TagManager(models.Manager):
'SET used_count = ('
'SELECT COUNT(*) FROM question_tags '
'INNER JOIN question ON question_id=question.id '
- 'WHERE tag_id = tag.id AND question.deleted=0'
+ 'WHERE tag_id = tag.id AND question.deleted=False'
') '
'WHERE id IN (%s)')
@@ -200,7 +201,7 @@ class AnswerManager(models.Manager):
class VoteManager(models.Manager):
COUNT_UP_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = 1"
COUNT_DOWN_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = -1"
- COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = DATE(NOW())"
+ COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = %s"
def get_up_vote_count_from_user(self, user):
if user is not None:
cursor = connection.cursor()
@@ -222,7 +223,7 @@ class VoteManager(models.Manager):
def get_votes_count_today_from_user(self, user):
if user is not None:
cursor = connection.cursor()
- cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id])
+ cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
row = cursor.fetchone()
return row[0]
@@ -230,11 +231,11 @@ class VoteManager(models.Manager):
return 0
class FlaggedItemManager(models.Manager):
- COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = DATE(NOW())"
+ COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = %s"
def get_flagged_items_count_today(self, user):
if user is not None:
cursor = connection.cursor()
- cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id])
+ cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
row = cursor.fetchone()
return row[0]
@@ -242,7 +243,7 @@ class FlaggedItemManager(models.Manager):
return 0
class ReputeManager(models.Manager):
- COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = DATE(NOW())"
+ COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = %s"
def get_reputation_by_upvoted_today(self, user):
"""
For one user in one day, he can only earn rep till certain score (ep. +200)
@@ -251,7 +252,7 @@ class ReputeManager(models.Manager):
"""
if user is not None:
cursor = connection.cursor()
- cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id])
+ cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
row = cursor.fetchone()
return row[0]
diff --git a/forum/models.py b/forum/models.py
index f1876588..fe2121b5 100644
--- a/forum/models.py
+++ b/forum/models.py
@@ -22,7 +22,7 @@ import settings
=======
from django.contrib.sitemaps import ping_google
import django.dispatch
-import settings
+from django.conf import settings
import logging
if settings.USE_SPHINX_SEARCH == True:
@@ -415,7 +415,7 @@ class QuestionRevision(models.Model):
return self.question.title
def get_absolute_url(self):
- print 'in QuestionRevision.get_absolute_url()'
+ #print 'in QuestionRevision.get_absolute_url()'
return reverse('question_revisions', args=[self.question.id])
def save(self, **kwargs):
@@ -442,7 +442,7 @@ class AnonymousAnswer(models.Model):
def publish(self,user):
from forum.views import create_new_answer
added_at = datetime.datetime.now()
- print user.id
+ #print user.id
create_new_answer(question=self.question,wiki=self.wiki,
added_at=added_at,text=self.text,
author=user)
@@ -510,8 +510,11 @@ class Answer(models.Model):
>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def get_user_vote(self, user):
+ if user.__class__.__name__ == "AnonymousUser":
+ return None
+
votes = self.votes.filter(user=user)
- if votes.count() > 0:
+ if votes and votes.count() > 0:
return votes[0]
else:
return None
@@ -729,13 +732,13 @@ def user_is_username_taken(cls,username):
return False
def user_get_q_sel_email_feed_frequency(self):
- print 'looking for frequency for user %s' % self
+ #print 'looking for frequency for user %s' % self
try:
feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel')
except Exception, e:
- print 'have error %s' % e.message
+ #print 'have error %s' % e.message
raise e
- print 'have freq=%s' % feed_setting.frequency
+ #print 'have freq=%s' % feed_setting.frequency
return feed_setting.frequency
User.add_to_class('is_approved', models.BooleanField(default=False))
@@ -871,7 +874,7 @@ def notify_award_message(instance, created, **kwargs):
"""
if created:
user = instance.user
- user.message_set.create(message=u"%s" % instance.badge.name)
+ user.message_set.create(message=u"Congratulations, you have received a badge '%s'" % instance.badge.name)
def record_answer_accepted(instance, created, **kwargs):
"""
diff --git a/forum/sitemap.py b/forum/sitemap.py
index dc97a009..c0c60b5e 100644
--- a/forum/sitemap.py
+++ b/forum/sitemap.py
@@ -9,3 +9,6 @@ class QuestionsSitemap(Sitemap):
def lastmod(self, obj):
return obj.last_activity_at
+
+ def location(self, obj):
+ return obj.get_absolute_url()
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 8ed79d3c..29fdd831 100644
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -1,5 +1,6 @@
import time
import os
+import posixpath
import datetime
import math
import re
@@ -244,9 +245,9 @@ def diff_date(date, limen=2):
if days > 2:
if date.year == now.year:
- return date.strftime(_("%b %d at %H:%M"))
+ return date.strftime("%b %d at %H:%M")
else:
- return date.strftime(_("%b %d '%y at %H:%M"))
+ return date.strftime("%b %d '%y at %H:%M")
elif days == 2:
return _('2 days ago')
elif days == 1:
@@ -281,7 +282,7 @@ def get_latest_changed_timestamp():
@register.simple_tag
def href(url):
url = '///' + settings.FORUM_SCRIPT_ALIAS + '/' + url
- return os.path.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
+ return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
class ItemSeparatorNode(template.Node):
def __init__(self,separator):
diff --git a/forum/templatetags/smart_if.py b/forum/templatetags/smart_if.py
index a8fc1944..ca3b43fe 100644
--- a/forum/templatetags/smart_if.py
+++ b/forum/templatetags/smart_if.py
@@ -1,401 +1,401 @@
-"""
-A smarter {% if %} tag for django templates.
-
-While retaining current Django functionality, it also handles equality,
-greater than and less than operators. Some common case examples::
-
- {% if articles|length >= 5 %}...{% endif %}
- {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
-"""
-import unittest
-from django import template
-
-
-register = template.Library()
-
-
-#==============================================================================
-# Calculation objects
-#==============================================================================
-
-class BaseCalc(object):
- def __init__(self, var1, var2=None, negate=False):
- self.var1 = var1
- self.var2 = var2
- self.negate = negate
-
- def resolve(self, context):
- try:
- var1, var2 = self.resolve_vars(context)
- outcome = self.calculate(var1, var2)
- except:
- outcome = False
- if self.negate:
- return not outcome
- return outcome
-
- def resolve_vars(self, context):
- var2 = self.var2 and self.var2.resolve(context)
- return self.var1.resolve(context), var2
-
- def calculate(self, var1, var2):
- raise NotImplementedError()
-
-
-class Or(BaseCalc):
- def calculate(self, var1, var2):
- return var1 or var2
-
-
-class And(BaseCalc):
- def calculate(self, var1, var2):
- return var1 and var2
-
-
-class Equals(BaseCalc):
- def calculate(self, var1, var2):
- return var1 == var2
-
-
-class Greater(BaseCalc):
- def calculate(self, var1, var2):
- return var1 > var2
-
-
-class GreaterOrEqual(BaseCalc):
- def calculate(self, var1, var2):
- return var1 >= var2
-
-
-class In(BaseCalc):
- def calculate(self, var1, var2):
- return var1 in var2
-
-
-#==============================================================================
-# Tests
-#==============================================================================
-
-class TestVar(object):
- """
- A basic self-resolvable object similar to a Django template variable. Used
- to assist with tests.
- """
- def __init__(self, value):
- self.value = value
-
- def resolve(self, context):
- return self.value
-
-
-class SmartIfTests(unittest.TestCase):
- def setUp(self):
- self.true = TestVar(True)
- self.false = TestVar(False)
- self.high = TestVar(9000)
- self.low = TestVar(1)
-
- def assertCalc(self, calc, context=None):
- """
- Test a calculation is True, also checking the inverse "negate" case.
- """
- context = context or {}
- self.assert_(calc.resolve(context))
- calc.negate = not calc.negate
- self.assertFalse(calc.resolve(context))
-
- def assertCalcFalse(self, calc, context=None):
- """
- Test a calculation is False, also checking the inverse "negate" case.
- """
- context = context or {}
- self.assertFalse(calc.resolve(context))
- calc.negate = not calc.negate
- self.assert_(calc.resolve(context))
-
- def test_or(self):
- self.assertCalc(Or(self.true))
- self.assertCalcFalse(Or(self.false))
- self.assertCalc(Or(self.true, self.true))
- self.assertCalc(Or(self.true, self.false))
- self.assertCalc(Or(self.false, self.true))
- self.assertCalcFalse(Or(self.false, self.false))
-
- def test_and(self):
- self.assertCalc(And(self.true, self.true))
- self.assertCalcFalse(And(self.true, self.false))
- self.assertCalcFalse(And(self.false, self.true))
- self.assertCalcFalse(And(self.false, self.false))
-
- def test_equals(self):
- self.assertCalc(Equals(self.low, self.low))
- self.assertCalcFalse(Equals(self.low, self.high))
-
- def test_greater(self):
- self.assertCalc(Greater(self.high, self.low))
- self.assertCalcFalse(Greater(self.low, self.low))
- self.assertCalcFalse(Greater(self.low, self.high))
-
- def test_greater_or_equal(self):
- self.assertCalc(GreaterOrEqual(self.high, self.low))
- self.assertCalc(GreaterOrEqual(self.low, self.low))
- self.assertCalcFalse(GreaterOrEqual(self.low, self.high))
-
- def test_in(self):
- list_ = TestVar([1,2,3])
- invalid_list = TestVar(None)
- self.assertCalc(In(self.low, list_))
- self.assertCalcFalse(In(self.low, invalid_list))
-
- def test_parse_bits(self):
- var = IfParser([True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([False, 'or', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([False, 'and', True]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser(['not', False, 'and', 'not', False]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser(['not', 'not', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '=', 1]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, 'not', '=', 1]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([1, 'not', 'not', '=', 1]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '!=', 1]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([3, '>', 2]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '<', 2]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([2, 'not', 'in', [2, 3]]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([1, 'or', 1, '=', 2]).parse()
- self.assert_(var.resolve({}))
-
- def test_boolean(self):
- var = IfParser([True, 'and', True, 'and', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False, 'or', False, 'or', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([True, 'and', False, 'or', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False, 'or', True, 'and', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([True, 'and', True, 'and', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'or', False, 'or', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'or', True, 'and', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'and', True, 'or', False]).parse()
- self.assertFalse(var.resolve({}))
-
- def test_invalid(self):
- self.assertRaises(ValueError, IfParser(['not']).parse)
- self.assertRaises(ValueError, IfParser(['==']).parse)
- self.assertRaises(ValueError, IfParser([1, 'in']).parse)
- self.assertRaises(ValueError, IfParser([1, '>', 'in']).parse)
- self.assertRaises(ValueError, IfParser([1, '==', 'not', 'not']).parse)
- self.assertRaises(ValueError, IfParser([1, 2]).parse)
-
-
-OPERATORS = {
- '=': (Equals, True),
- '==': (Equals, True),
- '!=': (Equals, False),
- '>': (Greater, True),
- '>=': (GreaterOrEqual, True),
- '<=': (Greater, False),
- '<': (GreaterOrEqual, False),
- 'or': (Or, True),
- 'and': (And, True),
- 'in': (In, True),
-}
-BOOL_OPERATORS = ('or', 'and')
-
-
-class IfParser(object):
- error_class = ValueError
-
- def __init__(self, tokens):
- self.tokens = tokens
-
- def _get_tokens(self):
- return self._tokens
-
- def _set_tokens(self, tokens):
- self._tokens = tokens
- self.len = len(tokens)
- self.pos = 0
-
- tokens = property(_get_tokens, _set_tokens)
-
- def parse(self):
- if self.at_end():
- raise self.error_class('No variables provided.')
- var1 = self.get_bool_var()
- while not self.at_end():
- op, negate = self.get_operator()
- var2 = self.get_bool_var()
- var1 = op(var1, var2, negate=negate)
- return var1
-
- def get_token(self, eof_message=None, lookahead=False):
- negate = True
- token = None
- pos = self.pos
- while token is None or token == 'not':
- if pos >= self.len:
- if eof_message is None:
- raise self.error_class()
- raise self.error_class(eof_message)
- token = self.tokens[pos]
- negate = not negate
- pos += 1
- if not lookahead:
- self.pos = pos
- return token, negate
-
- def at_end(self):
- return self.pos >= self.len
-
- def create_var(self, value):
- return TestVar(value)
-
- def get_bool_var(self):
- """
- Returns either a variable by itself or a non-boolean operation (such as
- ``x == 0`` or ``x < 0``).
-
- This is needed to keep correct precedence for boolean operations (i.e.
- ``x or x == 0`` should be ``x or (x == 0)``, not ``(x or x) == 0``).
- """
- var = self.get_var()
- if not self.at_end():
- op_token = self.get_token(lookahead=True)[0]
- if isinstance(op_token, basestring) and (op_token not in
- BOOL_OPERATORS):
- op, negate = self.get_operator()
- return op(var, self.get_var(), negate=negate)
- return var
-
- def get_var(self):
- token, negate = self.get_token('Reached end of statement, still '
- 'expecting a variable.')
- if isinstance(token, basestring) and token in OPERATORS:
- raise self.error_class('Expected variable, got operator (%s).' %
- token)
- var = self.create_var(token)
- if negate:
- return Or(var, negate=True)
- return var
-
- def get_operator(self):
- token, negate = self.get_token('Reached end of statement, still '
- 'expecting an operator.')
- if not isinstance(token, basestring) or token not in OPERATORS:
- raise self.error_class('%s is not a valid operator.' % token)
- if self.at_end():
- raise self.error_class('No variable provided after "%s".' % token)
- op, true = OPERATORS[token]
- if not true:
- negate = not negate
- return op, negate
-
-
-#==============================================================================
-# Actual templatetag code.
-#==============================================================================
-
-class TemplateIfParser(IfParser):
- error_class = template.TemplateSyntaxError
-
- def __init__(self, parser, *args, **kwargs):
- self.template_parser = parser
- return super(TemplateIfParser, self).__init__(*args, **kwargs)
-
- def create_var(self, value):
- return self.template_parser.compile_filter(value)
-
-
-class SmartIfNode(template.Node):
- def __init__(self, var, nodelist_true, nodelist_false=None):
- self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
- self.var = var
-
- def render(self, context):
- if self.var.resolve(context):
- return self.nodelist_true.render(context)
- if self.nodelist_false:
- return self.nodelist_false.render(context)
- return ''
-
- def __repr__(self):
- return "<Smart If node>"
-
- def __iter__(self):
- for node in self.nodelist_true:
- yield node
- if self.nodelist_false:
- for node in self.nodelist_false:
- yield node
-
- def get_nodes_by_type(self, nodetype):
- nodes = []
- if isinstance(self, nodetype):
- nodes.append(self)
- nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
- if self.nodelist_false:
- nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
- return nodes
-
-
-@register.tag('if')
-def smart_if(parser, token):
- """
- A smarter {% if %} tag for django templates.
-
- While retaining current Django functionality, it also handles equality,
- greater than and less than operators. Some common case examples::
-
- {% if articles|length >= 5 %}...{% endif %}
- {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
-
- Arguments and operators _must_ have a space between them, so
- ``{% if 1>2 %}`` is not a valid smart if tag.
-
- All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),
- ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
- """
- bits = token.split_contents()[1:]
- var = TemplateIfParser(parser, bits).parse()
- nodelist_true = parser.parse(('else', 'endif'))
- token = parser.next_token()
- if token.contents == 'else':
- nodelist_false = parser.parse(('endif',))
- parser.delete_first_token()
- else:
- nodelist_false = None
- return SmartIfNode(var, nodelist_true, nodelist_false)
-
-
-if __name__ == '__main__':
- unittest.main()
+"""
+A smarter {% if %} tag for django templates.
+
+While retaining current Django functionality, it also handles equality,
+greater than and less than operators. Some common case examples::
+
+ {% if articles|length >= 5 %}...{% endif %}
+ {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
+"""
+import unittest
+from django import template
+
+
+register = template.Library()
+
+
+#==============================================================================
+# Calculation objects
+#==============================================================================
+
+class BaseCalc(object):
+ def __init__(self, var1, var2=None, negate=False):
+ self.var1 = var1
+ self.var2 = var2
+ self.negate = negate
+
+ def resolve(self, context):
+ try:
+ var1, var2 = self.resolve_vars(context)
+ outcome = self.calculate(var1, var2)
+ except:
+ outcome = False
+ if self.negate:
+ return not outcome
+ return outcome
+
+ def resolve_vars(self, context):
+ var2 = self.var2 and self.var2.resolve(context)
+ return self.var1.resolve(context), var2
+
+ def calculate(self, var1, var2):
+ raise NotImplementedError()
+
+
+class Or(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 or var2
+
+
+class And(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 and var2
+
+
+class Equals(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 == var2
+
+
+class Greater(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 > var2
+
+
+class GreaterOrEqual(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 >= var2
+
+
+class In(BaseCalc):
+ def calculate(self, var1, var2):
+ return var1 in var2
+
+
+#==============================================================================
+# Tests
+#==============================================================================
+
+class TestVar(object):
+ """
+ A basic self-resolvable object similar to a Django template variable. Used
+ to assist with tests.
+ """
+ def __init__(self, value):
+ self.value = value
+
+ def resolve(self, context):
+ return self.value
+
+
+class SmartIfTests(unittest.TestCase):
+ def setUp(self):
+ self.true = TestVar(True)
+ self.false = TestVar(False)
+ self.high = TestVar(9000)
+ self.low = TestVar(1)
+
+ def assertCalc(self, calc, context=None):
+ """
+ Test a calculation is True, also checking the inverse "negate" case.
+ """
+ context = context or {}
+ self.assert_(calc.resolve(context))
+ calc.negate = not calc.negate
+ self.assertFalse(calc.resolve(context))
+
+ def assertCalcFalse(self, calc, context=None):
+ """
+ Test a calculation is False, also checking the inverse "negate" case.
+ """
+ context = context or {}
+ self.assertFalse(calc.resolve(context))
+ calc.negate = not calc.negate
+ self.assert_(calc.resolve(context))
+
+ def test_or(self):
+ self.assertCalc(Or(self.true))
+ self.assertCalcFalse(Or(self.false))
+ self.assertCalc(Or(self.true, self.true))
+ self.assertCalc(Or(self.true, self.false))
+ self.assertCalc(Or(self.false, self.true))
+ self.assertCalcFalse(Or(self.false, self.false))
+
+ def test_and(self):
+ self.assertCalc(And(self.true, self.true))
+ self.assertCalcFalse(And(self.true, self.false))
+ self.assertCalcFalse(And(self.false, self.true))
+ self.assertCalcFalse(And(self.false, self.false))
+
+ def test_equals(self):
+ self.assertCalc(Equals(self.low, self.low))
+ self.assertCalcFalse(Equals(self.low, self.high))
+
+ def test_greater(self):
+ self.assertCalc(Greater(self.high, self.low))
+ self.assertCalcFalse(Greater(self.low, self.low))
+ self.assertCalcFalse(Greater(self.low, self.high))
+
+ def test_greater_or_equal(self):
+ self.assertCalc(GreaterOrEqual(self.high, self.low))
+ self.assertCalc(GreaterOrEqual(self.low, self.low))
+ self.assertCalcFalse(GreaterOrEqual(self.low, self.high))
+
+ def test_in(self):
+ list_ = TestVar([1,2,3])
+ invalid_list = TestVar(None)
+ self.assertCalc(In(self.low, list_))
+ self.assertCalcFalse(In(self.low, invalid_list))
+
+ def test_parse_bits(self):
+ var = IfParser([True]).parse()
+ self.assert_(var.resolve({}))
+ var = IfParser([False]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([False, 'or', True]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([False, 'and', True]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser(['not', False, 'and', 'not', False]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser(['not', 'not', True]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '=', 1]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, 'not', '=', 1]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([1, 'not', 'not', '=', 1]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '!=', 1]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([3, '>', 2]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([1, '<', 2]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([2, 'not', 'in', [2, 3]]).parse()
+ self.assertFalse(var.resolve({}))
+
+ var = IfParser([1, 'or', 1, '=', 2]).parse()
+ self.assert_(var.resolve({}))
+
+ def test_boolean(self):
+ var = IfParser([True, 'and', True, 'and', True]).parse()
+ self.assert_(var.resolve({}))
+ var = IfParser([False, 'or', False, 'or', True]).parse()
+ self.assert_(var.resolve({}))
+ var = IfParser([True, 'and', False, 'or', True]).parse()
+ self.assert_(var.resolve({}))
+ var = IfParser([False, 'or', True, 'and', True]).parse()
+ self.assert_(var.resolve({}))
+
+ var = IfParser([True, 'and', True, 'and', False]).parse()
+ self.assertFalse(var.resolve({}))
+ var = IfParser([False, 'or', False, 'or', False]).parse()
+ self.assertFalse(var.resolve({}))
+ var = IfParser([False, 'or', True, 'and', False]).parse()
+ self.assertFalse(var.resolve({}))
+ var = IfParser([False, 'and', True, 'or', False]).parse()
+ self.assertFalse(var.resolve({}))
+
+ def test_invalid(self):
+ self.assertRaises(ValueError, IfParser(['not']).parse)
+ self.assertRaises(ValueError, IfParser(['==']).parse)
+ self.assertRaises(ValueError, IfParser([1, 'in']).parse)
+ self.assertRaises(ValueError, IfParser([1, '>', 'in']).parse)
+ self.assertRaises(ValueError, IfParser([1, '==', 'not', 'not']).parse)
+ self.assertRaises(ValueError, IfParser([1, 2]).parse)
+
+
+OPERATORS = {
+ '=': (Equals, True),
+ '==': (Equals, True),
+ '!=': (Equals, False),
+ '>': (Greater, True),
+ '>=': (GreaterOrEqual, True),
+ '<=': (Greater, False),
+ '<': (GreaterOrEqual, False),
+ 'or': (Or, True),
+ 'and': (And, True),
+ 'in': (In, True),
+}
+BOOL_OPERATORS = ('or', 'and')
+
+
+class IfParser(object):
+ error_class = ValueError
+
+ def __init__(self, tokens):
+ self.tokens = tokens
+
+ def _get_tokens(self):
+ return self._tokens
+
+ def _set_tokens(self, tokens):
+ self._tokens = tokens
+ self.len = len(tokens)
+ self.pos = 0
+
+ tokens = property(_get_tokens, _set_tokens)
+
+ def parse(self):
+ if self.at_end():
+ raise self.error_class('No variables provided.')
+ var1 = self.get_bool_var()
+ while not self.at_end():
+ op, negate = self.get_operator()
+ var2 = self.get_bool_var()
+ var1 = op(var1, var2, negate=negate)
+ return var1
+
+ def get_token(self, eof_message=None, lookahead=False):
+ negate = True
+ token = None
+ pos = self.pos
+ while token is None or token == 'not':
+ if pos >= self.len:
+ if eof_message is None:
+ raise self.error_class()
+ raise self.error_class(eof_message)
+ token = self.tokens[pos]
+ negate = not negate
+ pos += 1
+ if not lookahead:
+ self.pos = pos
+ return token, negate
+
+ def at_end(self):
+ return self.pos >= self.len
+
+ def create_var(self, value):
+ return TestVar(value)
+
+ def get_bool_var(self):
+ """
+ Returns either a variable by itself or a non-boolean operation (such as
+ ``x == 0`` or ``x < 0``).
+
+ This is needed to keep correct precedence for boolean operations (i.e.
+ ``x or x == 0`` should be ``x or (x == 0)``, not ``(x or x) == 0``).
+ """
+ var = self.get_var()
+ if not self.at_end():
+ op_token = self.get_token(lookahead=True)[0]
+ if isinstance(op_token, basestring) and (op_token not in
+ BOOL_OPERATORS):
+ op, negate = self.get_operator()
+ return op(var, self.get_var(), negate=negate)
+ return var
+
+ def get_var(self):
+ token, negate = self.get_token('Reached end of statement, still '
+ 'expecting a variable.')
+ if isinstance(token, basestring) and token in OPERATORS:
+ raise self.error_class('Expected variable, got operator (%s).' %
+ token)
+ var = self.create_var(token)
+ if negate:
+ return Or(var, negate=True)
+ return var
+
+ def get_operator(self):
+ token, negate = self.get_token('Reached end of statement, still '
+ 'expecting an operator.')
+ if not isinstance(token, basestring) or token not in OPERATORS:
+ raise self.error_class('%s is not a valid operator.' % token)
+ if self.at_end():
+ raise self.error_class('No variable provided after "%s".' % token)
+ op, true = OPERATORS[token]
+ if not true:
+ negate = not negate
+ return op, negate
+
+
+#==============================================================================
+# Actual templatetag code.
+#==============================================================================
+
+class TemplateIfParser(IfParser):
+ error_class = template.TemplateSyntaxError
+
+ def __init__(self, parser, *args, **kwargs):
+ self.template_parser = parser
+ return super(TemplateIfParser, self).__init__(*args, **kwargs)
+
+ def create_var(self, value):
+ return self.template_parser.compile_filter(value)
+
+
+class SmartIfNode(template.Node):
+ def __init__(self, var, nodelist_true, nodelist_false=None):
+ self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
+ self.var = var
+
+ def render(self, context):
+ if self.var.resolve(context):
+ return self.nodelist_true.render(context)
+ if self.nodelist_false:
+ return self.nodelist_false.render(context)
+ return ''
+
+ def __repr__(self):
+ return "<Smart If node>"
+
+ def __iter__(self):
+ for node in self.nodelist_true:
+ yield node
+ if self.nodelist_false:
+ for node in self.nodelist_false:
+ yield node
+
+ def get_nodes_by_type(self, nodetype):
+ nodes = []
+ if isinstance(self, nodetype):
+ nodes.append(self)
+ nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
+ if self.nodelist_false:
+ nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
+ return nodes
+
+
+@register.tag('if')
+def smart_if(parser, token):
+ """
+ A smarter {% if %} tag for django templates.
+
+ While retaining current Django functionality, it also handles equality,
+ greater than and less than operators. Some common case examples::
+
+ {% if articles|length >= 5 %}...{% endif %}
+ {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
+
+ Arguments and operators _must_ have a space between them, so
+ ``{% if 1>2 %}`` is not a valid smart if tag.
+
+ All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),
+ ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
+ """
+ bits = token.split_contents()[1:]
+ var = TemplateIfParser(parser, bits).parse()
+ nodelist_true = parser.parse(('else', 'endif'))
+ token = parser.next_token()
+ if token.contents == 'else':
+ nodelist_false = parser.parse(('endif',))
+ parser.delete_first_token()
+ else:
+ nodelist_false = None
+ return SmartIfNode(var, nodelist_true, nodelist_false)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/forum/urls.py b/forum/urls.py
index 62e70161..42746d44 100644
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -54,7 +54,7 @@ urlpatterns = patterns('',
app.delete_comment, kwargs={'commented_object_type':'answer'}, \
name='delete_answer_comment'), \
#place general question item in the end of other operations
- url(r'^%s(?P<id>\d+)//*' % _('question/'), app.question, name='question'),
+ url(r'^%s(?P<id>\d+)/' % _('question/'), app.question, name='question'),
url(r'^%s$' % _('tags/'), app.tags, name='tags'),
url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag, name='tag_questions'),
@@ -86,6 +86,7 @@ urlpatterns = patterns('',
url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
url(r'^%s$' % _('search/'), app.search, name='search'),
url(r'^%s$' % _('feedback/'), app.feedback, name='feedback'),
+ (r'^%sfb/' % _('account/'), include('fbconnect.urls')),
(r'^%s' % _('account/'), include('django_authopenid.urls')),
(r'^i18n/', include('django.conf.urls.i18n')),
)
diff --git a/forum/views.py b/forum/views.py
index 296745f9..ade6aaa7 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -33,7 +33,7 @@ from forum.auth import *
from forum.const import *
from forum.user import *
from forum import auth
-from django_authopenid.util import get_next_url
+from utils.forms import get_next_url
# used in index page
INDEX_PAGE_SIZE = 20
@@ -202,7 +202,7 @@ def questions(request, tagname=None, unanswered=False):
'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ 'WHERE forum_markedtag.user_id = %s '
+ 'AND forum_markedtag.tag_id = question_tags.tag_id '
- + 'AND forum_markedtag.reason = "good" '
+ + 'AND forum_markedtag.reason = \'good\' '
+ 'AND question_tags.question_id = question.id'
),
]),
@@ -220,7 +220,7 @@ def questions(request, tagname=None, unanswered=False):
'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ 'WHERE forum_markedtag.user_id = %s '
+ 'AND forum_markedtag.tag_id = question_tags.tag_id '
- + 'AND forum_markedtag.reason = "bad" '
+ + 'AND forum_markedtag.reason = \'bad\' '
+ 'AND question_tags.question_id = question.id'
)
]),
@@ -436,6 +436,21 @@ def question(request, id):
logging.debug('view_id=' + str(view_id))
question = get_object_or_404(Question, id=id)
+ try:
+ pattern = r'/%s%s%d/([\w-]+)' % (settings.FORUM_SCRIPT_ALIAS,_('question/'), question.id)
+ path_re = re.compile(pattern)
+ logging.debug(pattern)
+ logging.debug(request.path)
+ m = path_re.match(request.path)
+ if m:
+ slug = m.group(1)
+ logging.debug('have slug %s' % slug)
+ assert(slug == slugify(question.title))
+ else:
+ logging.debug('no match!')
+ except:
+ return HttpResponseRedirect(question.get_absolute_url())
+
if question.deleted and not can_view_deleted_post(request.user, question):
raise Http404
answer_form = AnswerForm(question,request.user)
@@ -1236,7 +1251,7 @@ def edit_user(request, id):
from django_authopenid.views import set_new_email
set_new_email(user, new_email)
- user.username = sanitize_html(form.cleaned_data['username'])
+ #user.username = sanitize_html(form.cleaned_data['username'])
user.real_name = sanitize_html(form.cleaned_data['realname'])
user.website = sanitize_html(form.cleaned_data['website'])
user.location = sanitize_html(form.cleaned_data['city'])
@@ -1273,7 +1288,7 @@ def user_stats(request, user_id, user_view):
},
select_params=[user_id],
tables=['question', 'auth_user'],
- where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
+ where=['question.deleted=False AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
params=[user_id],
order_by=['-vote_count', '-last_activity_at']
).values('vote_count',
@@ -1309,7 +1324,7 @@ def user_stats(request, user_id, user_view):
'comment_count' : 'answer.comment_count'
},
tables=['question', 'answer'],
- where=['answer.deleted=0 AND question.deleted=0 AND answer.author_id=%s AND answer.question_id=question.id'],
+ where=['answer.deleted=False AND question.deleted=False AND answer.author_id=%s AND answer.question_id=question.id'],
params=[user_id],
order_by=['-vote_count', '-answer_id'],
select_params=[user_id]
@@ -1431,7 +1446,7 @@ def user_recent(request, user_id, user_view):
},
tables=['activity', 'question'],
where=['activity.content_type_id = %s AND activity.object_id = ' +
- 'question.id AND question.deleted=0 AND activity.user_id = %s AND activity.activity_type = %s'],
+ 'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'],
params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION],
order_by=['-activity.active_at']
).values(
@@ -1456,8 +1471,8 @@ def user_recent(request, user_id, user_view):
},
tables=['activity', 'answer', 'question'],
where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' +
- 'answer.question_id=question.id AND answer.deleted=0 AND activity.user_id=%s AND '+
- 'activity.activity_type=%s AND question.deleted=0'],
+ 'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+
+ 'activity.activity_type=%s AND question.deleted=False'],
params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER],
order_by=['-activity.active_at']
).values(
@@ -1485,7 +1500,7 @@ def user_recent(request, user_id, user_view):
where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+
'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' +
- 'question.deleted=0'],
+ 'question.deleted=False'],
params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION],
order_by=['-comment.added_at']
).values(
@@ -1515,7 +1530,7 @@ def user_recent(request, user_id, user_view):
'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+
'comment.content_type_id=%s AND question.id = answer.question_id AND '+
'activity.user_id = %s AND activity.activity_type=%s AND '+
- 'answer.deleted=0 AND question.deleted=0'],
+ 'answer.deleted=False AND question.deleted=False'],
params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER],
order_by=['-comment.added_at']
).values(
@@ -1542,7 +1557,7 @@ def user_recent(request, user_id, user_view):
},
tables=['activity', 'question_revision', 'question'],
where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+
- 'question_revision.id=question.id AND question.deleted=0 AND '+
+ 'question_revision.id=question.id AND question.deleted=False AND '+
'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+
'activity.activity_type=%s'],
params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION],
@@ -1575,7 +1590,7 @@ def user_recent(request, user_id, user_view):
where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+
'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+
'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+
- 'question.deleted=0 AND answer.deleted=0 AND '+
+ 'question.deleted=False AND answer.deleted=False AND '+
'activity.activity_type=%s'],
params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER],
order_by=['-activity.active_at']
@@ -1604,7 +1619,7 @@ def user_recent(request, user_id, user_view):
tables=['activity', 'answer', 'question'],
where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+
'activity.user_id = question.author_id AND activity.user_id = %s AND '+
- 'answer.deleted=0 AND question.deleted=0 AND '+
+ 'answer.deleted=False AND question.deleted=False AND '+
'answer.question_id=question.id AND activity.activity_type=%s'],
params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER],
order_by=['-activity.active_at']
@@ -1680,7 +1695,7 @@ def user_responses(request, user_id, user_view):
},
select_params=[user_id],
tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
+ where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
params=[user_id, user_id],
order_by=['-answer.id']
@@ -1711,7 +1726,7 @@ def user_responses(request, user_id, user_view):
'user_id' : 'auth_user.id'
},
tables=['question', 'auth_user', 'comment'],
- where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+
+ where=['question.deleted=False AND question.author_id = %s AND comment.object_id=question.id AND '+
'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
params=[user_id, question_type_id, user_id],
order_by=['-comment.added_at']
@@ -1741,7 +1756,7 @@ def user_responses(request, user_id, user_view):
'user_id' : 'auth_user.id'
},
tables=['answer', 'auth_user', 'comment', 'question'],
- where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND '+
+ where=['answer.deleted=False AND answer.author_id = %s AND comment.object_id=answer.id AND '+
'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+
'AND question.id = answer.question_id'],
params=[user_id, answer_type_id, user_id],
@@ -1774,8 +1789,8 @@ def user_responses(request, user_id, user_view):
},
select_params=[user_id],
tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
- 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'],
+ where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
+ 'answer.author_id = %s AND answer.accepted=True AND question.author_id=auth_user.id'],
params=[user_id],
order_by=['-answer.id']
).values(
@@ -1922,7 +1937,7 @@ def user_favorites(request, user_id, user_view):
},
select_params=[user_id],
tables=['question', 'auth_user', 'favorite_question'],
- where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id '+
+ where=['question.deleted=True AND question.last_activity_by_id = auth_user.id '+
'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
params=[user_id],
order_by=['-vote_count', '-question.id']
@@ -2093,7 +2108,7 @@ def badge(request, id):
tables=['award', 'auth_user'],
where=['badge_id=%s AND user_id=auth_user.id'],
params=[id]
- ).values('id').distinct()
+ ).distinct('id')
return render_to_response('badge.html', {
'awards' : awards,
@@ -2337,8 +2352,18 @@ def search(request):
except KeyError:
view_id = "latest"
orderby = "-added_at"
-
- if settings.USE_SPHINX_SEARCH == True:
+
+ if settings.USE_PG_FTS:
+ objects = Question.objects.filter(deleted=False).extra(
+ select={
+ 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)",
+ },
+ where=["tsv @@ plainto_tsquery(%s)"],
+ params=[keywords],
+ select_params=[keywords]
+ ).order_by('-ranking')
+
+ elif settings.USE_SPHINX_SEARCH == True:
#search index is now free of delete questions and answers
#so there is not "antideleted" filtering here
objects = Question.search.query(keywords)
@@ -2359,6 +2384,9 @@ def search(request):
if tag not in related_tags:
related_tags.append(tag)
+ #if is_search is true in the context, prepend this string to soting tabs urls
+ search_uri = "?q=%s&page=%d&t=question" % ("+".join(keywords.split()), page)
+
return render_to_response(template_file, {
"questions" : questions,
"tab_id" : view_id,
@@ -2368,6 +2396,8 @@ def search(request):
"searchtitle" : keywords,
"keywords" : keywords,
"is_unanswered" : False,
+ "is_search": True,
+ "search_uri": search_uri,
"context" : {
'is_paginated' : True,
'pages': objects_list.num_pages,