summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-03-01 18:55:54 -0500
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-03-01 18:55:54 -0500
commit9593e92c4b18745c7e39171d9ad67c85cd5b22b1 (patch)
tree69a5f05b6bf4c605878e6da8f6d1c4d68b99cfe4
parenta89452f6f9a0309466e612a88410185febd2d3ec (diff)
downloadaskbot-9593e92c4b18745c7e39171d9ad67c85cd5b22b1.tar.gz
askbot-9593e92c4b18745c7e39171d9ad67c85cd5b22b1.tar.bz2
askbot-9593e92c4b18745c7e39171d9ad67c85cd5b22b1.zip
dos2unix
-rwxr-xr-xforum/authentication/__init__.py52
-rwxr-xr-xforum/authentication/base.py78
-rwxr-xr-xforum/authentication/forms.py62
-rwxr-xr-xforum/management/__init__.py4
-rwxr-xr-xforum/models/__init__.py684
-rwxr-xr-xforum/models/answer.py268
-rwxr-xr-xforum/models/base.py276
-rwxr-xr-xforum/models/meta.py176
-rwxr-xr-xforum/models/question.py672
-rwxr-xr-xforum/models/repute.py218
-rwxr-xr-xforum/models/tag.py168
-rwxr-xr-xforum/models/user.py152
-rwxr-xr-xforum/modules.py156
-rwxr-xr-xforum/skins/default/media/style/auth.css94
-rwxr-xr-xforum/views/auth.py422
-rwxr-xr-xforum_modules/books/__init__.py6
-rwxr-xr-xforum_modules/books/models.py124
-rwxr-xr-xforum_modules/books/urls.py18
-rwxr-xr-xforum_modules/books/views.py282
-rwxr-xr-xforum_modules/facebookauth/authentication.py168
-rwxr-xr-xforum_modules/facebookauth/settings.py4
-rwxr-xr-xforum_modules/facebookauth/urls.py16
-rwxr-xr-xforum_modules/facebookauth/views.py20
-rwxr-xr-xforum_modules/localauth/authentication.py34
-rwxr-xr-xforum_modules/localauth/forms.py152
-rwxr-xr-xforum_modules/localauth/urls.py14
-rwxr-xr-xforum_modules/localauth/views.py58
-rwxr-xr-xforum_modules/oauthauth/authentication.py80
-rwxr-xr-xforum_modules/oauthauth/consumer.py174
-rwxr-xr-xforum_modules/oauthauth/lib/oauth.py1186
-rwxr-xr-xforum_modules/oauthauth/settings.py6
-rwxr-xr-xforum_modules/openidauth/authentication.py390
-rwxr-xr-xforum_modules/openidauth/consumer.py222
-rwxr-xr-xforum_modules/openidauth/models.py52
-rwxr-xr-xforum_modules/openidauth/settings.py16
-rwxr-xr-xforum_modules/openidauth/store.py158
-rwxr-xr-xforum_modules/pgfulltext/__init__.py16
-rwxr-xr-xforum_modules/pgfulltext/handlers.py20
-rwxr-xr-xforum_modules/pgfulltext/management.py58
-rwxr-xr-xforum_modules/sphinxfulltext/dependencies.py4
-rwxr-xr-xforum_modules/sphinxfulltext/handlers.py6
-rwxr-xr-xforum_modules/sphinxfulltext/models.py20
-rwxr-xr-xforum_modules/sphinxfulltext/settings.py10
-rwxr-xr-xsettings_local.py.dist220
44 files changed, 3508 insertions, 3508 deletions
diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py
index e83ba872..5326c45c 100755
--- a/forum/authentication/__init__.py
+++ b/forum/authentication/__init__.py
@@ -1,27 +1,27 @@
-import re
-from forum.modules import get_modules_script_classes
-from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext
-
-class ConsumerAndContext():
- def __init__(self, id, consumer, context):
- self.id = id
- self.consumer = consumer()
-
- context.id = id
- self.context = context
-
-consumers = dict([
- (re.sub('AuthConsumer$', '', name).lower(), cls) for name, cls
- in get_modules_script_classes('authentication', AuthenticationConsumer).items()
- if not re.search('AbstractAuthConsumer$', name)
- ])
-
-contexts = dict([
- (re.sub('AuthContext$', '', name).lower(), cls) for name, cls
- in get_modules_script_classes('authentication', ConsumerTemplateContext).items()
- ])
-
-AUTH_PROVIDERS = dict([
- (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys()
- if name in contexts
+import re
+from forum.modules import get_modules_script_classes
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext
+
+class ConsumerAndContext():
+ def __init__(self, id, consumer, context):
+ self.id = id
+ self.consumer = consumer()
+
+ context.id = id
+ self.context = context
+
+consumers = dict([
+ (re.sub('AuthConsumer$', '', name).lower(), cls) for name, cls
+ in get_modules_script_classes('authentication', AuthenticationConsumer).items()
+ if not re.search('AbstractAuthConsumer$', name)
+ ])
+
+contexts = dict([
+ (re.sub('AuthContext$', '', name).lower(), cls) for name, cls
+ in get_modules_script_classes('authentication', ConsumerTemplateContext).items()
+ ])
+
+AUTH_PROVIDERS = dict([
+ (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys()
+ if name in contexts
]) \ No newline at end of file
diff --git a/forum/authentication/base.py b/forum/authentication/base.py
index 995f7c96..08534adc 100755
--- a/forum/authentication/base.py
+++ b/forum/authentication/base.py
@@ -1,40 +1,40 @@
-
-class AuthenticationConsumer(object):
-
- def prepare_authentication_request(self, request, redirect_to):
- raise NotImplementedError()
-
- def process_authentication_request(self, response):
- raise NotImplementedError()
-
- def get_user_data(self, key):
- raise NotImplementedError()
-
-
-class ConsumerTemplateContext(object):
- """
- Class that provides information about a certain authentication provider context in the signin page.
-
- class attributes:
-
- mode - one of BIGICON, SMALLICON, FORM
-
- human_name - the human readable name of the provider
-
- extra_js - some providers require us to load extra javascript on the signin page for them to work,
- this is the place to add those files in the form of a list
-
- extra_css - same as extra_js but for css files
- """
- mode = ''
- weight = 500
- human_name = ''
- extra_js = []
- extra_css = []
- show_to_logged_in_user = True
-
-class InvalidAuthentication(Exception):
- def __init__(self, message):
- self.message = message
-
+
+class AuthenticationConsumer(object):
+
+ def prepare_authentication_request(self, request, redirect_to):
+ raise NotImplementedError()
+
+ def process_authentication_request(self, response):
+ raise NotImplementedError()
+
+ def get_user_data(self, key):
+ raise NotImplementedError()
+
+
+class ConsumerTemplateContext(object):
+ """
+ Class that provides information about a certain authentication provider context in the signin page.
+
+ class attributes:
+
+ mode - one of BIGICON, SMALLICON, FORM
+
+ human_name - the human readable name of the provider
+
+ extra_js - some providers require us to load extra javascript on the signin page for them to work,
+ this is the place to add those files in the form of a list
+
+ extra_css - same as extra_js but for css files
+ """
+ mode = ''
+ weight = 500
+ human_name = ''
+ extra_js = []
+ extra_css = []
+ show_to_logged_in_user = True
+
+class InvalidAuthentication(Exception):
+ def __init__(self, message):
+ self.message = message
+
\ No newline at end of file
diff --git a/forum/authentication/forms.py b/forum/authentication/forms.py
index 04841341..019c85f3 100755
--- a/forum/authentication/forms.py
+++ b/forum/authentication/forms.py
@@ -1,31 +1,31 @@
-from forum.utils.forms import NextUrlField, UserNameField, UserEmailField
-from forum.models import EmailFeedSetting, Question
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django import forms
-from forum.forms import EditUserEmailFeedsForm
-import logging
-
-class SimpleRegistrationForm(forms.Form):
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
-
-
-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)
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField
+from forum.models import EmailFeedSetting, Question
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext as _
+from django import forms
+from forum.forms import EditUserEmailFeedsForm
+import logging
+
+class SimpleRegistrationForm(forms.Form):
+ next = NextUrlField()
+ username = UserNameField()
+ email = UserEmailField()
+
+
+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/__init__.py b/forum/management/__init__.py
index 8266592a..b654caaa 100755
--- a/forum/management/__init__.py
+++ b/forum/management/__init__.py
@@ -1,3 +1,3 @@
-from forum.modules import get_modules_script
-
+from forum.modules import get_modules_script
+
get_modules_script('management') \ No newline at end of file
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
index 12a02395..77825079 100755
--- a/forum/models/__init__.py
+++ b/forum/models/__init__.py
@@ -1,343 +1,343 @@
-from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion
-from answer import Answer, AnonymousAnswer, AnswerRevision
-from tag import Tag, MarkedTag
-from meta import Vote, Comment, FlaggedItem
-from user import Activity, AnonymousEmail, EmailFeedSetting, AuthKeyUserAssociation
-from repute import Badge, Award, Repute
-
-from base import *
-
-# User extend properties
-QUESTIONS_PER_PAGE_CHOICES = (
- (10, u'10'),
- (30, u'30'),
- (50, u'50'),
-)
-
-def user_is_username_taken(cls,username):
- try:
- cls.objects.get(username=username)
- return True
- except cls.MultipleObjectsReturned:
- return True
- except cls.DoesNotExist:
- return False
-
-def user_get_q_sel_email_feed_frequency(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
- raise e
- #print 'have freq=%s' % feed_setting.frequency
- return feed_setting.frequency
-
-User.add_to_class('is_approved', models.BooleanField(default=False))
-User.add_to_class('email_isvalid', models.BooleanField(default=False))
-User.add_to_class('email_key', models.CharField(max_length=32, null=True))
-User.add_to_class('reputation', models.PositiveIntegerField(default=1))
-User.add_to_class('gravatar', models.CharField(max_length=32))
-
-#User.add_to_class('favorite_questions',
-# models.ManyToManyField(Question, through=FavoriteQuestion,
-# related_name='favorited_by'))
-
-#User.add_to_class('badges', models.ManyToManyField(Badge, through=Award,
-# related_name='awarded_to'))
-User.add_to_class('gold', models.SmallIntegerField(default=0))
-User.add_to_class('silver', models.SmallIntegerField(default=0))
-User.add_to_class('bronze', models.SmallIntegerField(default=0))
-User.add_to_class('questions_per_page',
- models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))
-User.add_to_class('last_seen',
- models.DateTimeField(default=datetime.datetime.now))
-User.add_to_class('real_name', models.CharField(max_length=100, blank=True))
-User.add_to_class('website', models.URLField(max_length=200, blank=True))
-User.add_to_class('location', models.CharField(max_length=100, blank=True))
-User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
-User.add_to_class('about', models.TextField(blank=True))
-User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
-User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
-User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
-User.add_to_class('tag_filter_setting',
- models.CharField(
- max_length=16,
- choices=TAG_EMAIL_FILTER_CHOICES,
- default='ignored'
- )
- )
-
-# custom signal
-tags_updated = django.dispatch.Signal(providing_args=["question"])
-edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
-delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
-mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
-user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
-user_logged_in = django.dispatch.Signal(providing_args=["session"])
-
-
-def get_messages(self):
- messages = []
- for m in self.message_set.all():
- messages.append(m.message)
- return messages
-
-def delete_messages(self):
- self.message_set.all().delete()
-
-def get_profile_url(self):
- """Returns the URL for this User's profile."""
- return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
-
-def get_profile_link(self):
- profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
- logging.debug('in get profile link %s' % profile_link)
- return mark_safe(profile_link)
-
-User.add_to_class('get_profile_url', get_profile_url)
-User.add_to_class('get_profile_link', get_profile_link)
-User.add_to_class('get_messages', get_messages)
-User.add_to_class('delete_messages', delete_messages)
-
-def calculate_gravatar_hash(instance, **kwargs):
- """Calculates a User's gravatar hash from their email address."""
- if kwargs.get('raw', False):
- return
- instance.gravatar = hashlib.md5(instance.email).hexdigest()
-
-def record_ask_event(instance, created, **kwargs):
- if created:
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION)
- activity.save()
-
-def record_answer_event(instance, created, **kwargs):
- if created:
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER)
- activity.save()
-
-def record_comment_event(instance, created, **kwargs):
- if created:
- from django.contrib.contenttypes.models import ContentType
- question_type = ContentType.objects.get_for_model(Question)
- question_type_id = question_type.id
- if (instance.content_type_id == question_type_id):
- type = TYPE_ACTIVITY_COMMENT_QUESTION
- else:
- type = TYPE_ACTIVITY_COMMENT_ANSWER
- activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type)
- activity.save()
-
-def record_revision_question_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION)
- activity.save()
-
-def record_revision_answer_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER)
- activity.save()
-
-def record_award_event(instance, created, **kwargs):
- """
- After we awarded a badge to user, we need to record this activity and notify user.
- We also recaculate awarded_count of this badge and user information.
- """
- if created:
- activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance,
- activity_type=TYPE_ACTIVITY_PRIZE)
- activity.save()
-
- instance.badge.awarded_count += 1
- instance.badge.save()
-
- if instance.badge.type == Badge.GOLD:
- instance.user.gold += 1
- if instance.badge.type == Badge.SILVER:
- instance.user.silver += 1
- if instance.badge.type == Badge.BRONZE:
- instance.user.bronze += 1
- instance.user.save()
-
-def notify_award_message(instance, created, **kwargs):
- """
- Notify users when they have been awarded badges by using Django message.
- """
- if created:
- user = instance.user
- user.message_set.create(message=u"Congratulations, you have received a badge '%s'" % instance.badge.name)
-
-def record_answer_accepted(instance, created, **kwargs):
- """
- when answer is accepted, we record this for question author - who accepted it.
- """
- if not created and instance.accepted:
- activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \
- content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER)
- activity.save()
-
-def update_last_seen(instance, created, **kwargs):
- """
- when user has activities, we update 'last_seen' time stamp for him
- """
- user = instance.user
- user.last_seen = datetime.datetime.now()
- user.save()
-
-def record_vote(instance, created, **kwargs):
- """
- when user have voted
- """
- if created:
- if instance.vote == 1:
- vote_type = TYPE_ACTIVITY_VOTE_UP
- else:
- vote_type = TYPE_ACTIVITY_VOTE_DOWN
-
- activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type)
- activity.save()
-
-def record_cancel_vote(instance, **kwargs):
- """
- when user canceled vote, the vote will be deleted.
- """
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE)
- activity.save()
-
-def record_delete_question(instance, delete_by, **kwargs):
- """
- when user deleted the question
- """
- if instance.__class__ == "Question":
- activity_type = TYPE_ACTIVITY_DELETE_QUESTION
- else:
- activity_type = TYPE_ACTIVITY_DELETE_ANSWER
-
- activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type)
- activity.save()
-
-def record_mark_offensive(instance, mark_by, **kwargs):
- activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE)
- activity.save()
-
-def record_update_tags(question, **kwargs):
- """
- when user updated tags of the question
- """
- activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS)
- activity.save()
-
-def record_favorite_question(instance, created, **kwargs):
- """
- when user add the question in him favorite questions list.
- """
- if created:
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE)
- activity.save()
-
-def record_user_full_updated(instance, **kwargs):
- activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED)
- activity.save()
-
-def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
- aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
- aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
- import settings
- if settings.EMAIL_VALIDATION == 'on':#add user to the record
- for aq in aq_list:
- aq.author = user
- aq.save()
- for aa in aa_list:
- aa.author = user
- aa.save()
- #maybe add pending posts message?
- else: #just publish the questions
- for aq in aq_list:
- aq.publish(user)
- for aa in aa_list:
- aa.publish(user)
-
-#signal for User modle save changes
-
-pre_save.connect(calculate_gravatar_hash, sender=User)
-post_save.connect(record_ask_event, sender=Question)
-post_save.connect(record_answer_event, sender=Answer)
-post_save.connect(record_comment_event, sender=Comment)
-post_save.connect(record_revision_question_event, sender=QuestionRevision)
-post_save.connect(record_revision_answer_event, sender=AnswerRevision)
-post_save.connect(record_award_event, sender=Award)
-post_save.connect(notify_award_message, sender=Award)
-post_save.connect(record_answer_accepted, sender=Answer)
-post_save.connect(update_last_seen, sender=Activity)
-post_save.connect(record_vote, sender=Vote)
-post_delete.connect(record_cancel_vote, sender=Vote)
-delete_post_or_answer.connect(record_delete_question, sender=Question)
-delete_post_or_answer.connect(record_delete_question, sender=Answer)
-mark_offensive.connect(record_mark_offensive, sender=Question)
-mark_offensive.connect(record_mark_offensive, sender=Answer)
-tags_updated.connect(record_update_tags, sender=Question)
-post_save.connect(record_favorite_question, sender=FavoriteQuestion)
-user_updated.connect(record_user_full_updated, sender=User)
-user_logged_in.connect(post_stored_anonymous_content)
-
-Question = Question
-QuestionRevision = QuestionRevision
-QuestionView = QuestionView
-FavoriteQuestion = FavoriteQuestion
-AnonymousQuestion = AnonymousQuestion
-
-Answer = Answer
-AnswerRevision = AnswerRevision
-AnonymousAnswer = AnonymousAnswer
-
-Tag = Tag
-Comment = Comment
-Vote = Vote
-FlaggedItem = FlaggedItem
-MarkedTag = MarkedTag
-
-Badge = Badge
-Award = Award
-Repute = Repute
-
-Activity = Activity
-EmailFeedSetting = EmailFeedSetting
-AnonymousEmail = AnonymousEmail
-AuthKeyUserAssociation = AuthKeyUserAssociation
-
-__all__ = [
- 'Question',
- 'QuestionRevision',
- 'QuestionView',
- 'FavoriteQuestion',
- 'AnonymousQuestion',
-
- 'Answer',
- 'AnswerRevision',
- 'AnonymousAnswer',
-
- 'Tag',
- 'Comment',
- 'Vote',
- 'FlaggedItem',
- 'MarkedTag',
-
- 'Badge',
- 'Award',
- 'Repute',
-
- 'Activity',
- 'EmailFeedSetting',
- 'AnonymousEmail',
- 'AuthKeyUserAssociation',
-
- 'User'
- ]
-
-
-from forum.modules import get_modules_script_classes
-
-for k, v in get_modules_script_classes('models', models.Model).items():
- if not k in __all__:
- __all__.append(k)
+from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion
+from answer import Answer, AnonymousAnswer, AnswerRevision
+from tag import Tag, MarkedTag
+from meta import Vote, Comment, FlaggedItem
+from user import Activity, AnonymousEmail, EmailFeedSetting, AuthKeyUserAssociation
+from repute import Badge, Award, Repute
+
+from base import *
+
+# User extend properties
+QUESTIONS_PER_PAGE_CHOICES = (
+ (10, u'10'),
+ (30, u'30'),
+ (50, u'50'),
+)
+
+def user_is_username_taken(cls,username):
+ try:
+ cls.objects.get(username=username)
+ return True
+ except cls.MultipleObjectsReturned:
+ return True
+ except cls.DoesNotExist:
+ return False
+
+def user_get_q_sel_email_feed_frequency(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
+ raise e
+ #print 'have freq=%s' % feed_setting.frequency
+ return feed_setting.frequency
+
+User.add_to_class('is_approved', models.BooleanField(default=False))
+User.add_to_class('email_isvalid', models.BooleanField(default=False))
+User.add_to_class('email_key', models.CharField(max_length=32, null=True))
+User.add_to_class('reputation', models.PositiveIntegerField(default=1))
+User.add_to_class('gravatar', models.CharField(max_length=32))
+
+#User.add_to_class('favorite_questions',
+# models.ManyToManyField(Question, through=FavoriteQuestion,
+# related_name='favorited_by'))
+
+#User.add_to_class('badges', models.ManyToManyField(Badge, through=Award,
+# related_name='awarded_to'))
+User.add_to_class('gold', models.SmallIntegerField(default=0))
+User.add_to_class('silver', models.SmallIntegerField(default=0))
+User.add_to_class('bronze', models.SmallIntegerField(default=0))
+User.add_to_class('questions_per_page',
+ models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))
+User.add_to_class('last_seen',
+ models.DateTimeField(default=datetime.datetime.now))
+User.add_to_class('real_name', models.CharField(max_length=100, blank=True))
+User.add_to_class('website', models.URLField(max_length=200, blank=True))
+User.add_to_class('location', models.CharField(max_length=100, blank=True))
+User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
+User.add_to_class('about', models.TextField(blank=True))
+User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
+User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
+User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
+User.add_to_class('tag_filter_setting',
+ models.CharField(
+ max_length=16,
+ choices=TAG_EMAIL_FILTER_CHOICES,
+ default='ignored'
+ )
+ )
+
+# custom signal
+tags_updated = django.dispatch.Signal(providing_args=["question"])
+edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
+delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
+mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
+user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
+user_logged_in = django.dispatch.Signal(providing_args=["session"])
+
+
+def get_messages(self):
+ messages = []
+ for m in self.message_set.all():
+ messages.append(m.message)
+ return messages
+
+def delete_messages(self):
+ self.message_set.all().delete()
+
+def get_profile_url(self):
+ """Returns the URL for this User's profile."""
+ return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
+
+def get_profile_link(self):
+ profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
+ logging.debug('in get profile link %s' % profile_link)
+ return mark_safe(profile_link)
+
+User.add_to_class('get_profile_url', get_profile_url)
+User.add_to_class('get_profile_link', get_profile_link)
+User.add_to_class('get_messages', get_messages)
+User.add_to_class('delete_messages', delete_messages)
+
+def calculate_gravatar_hash(instance, **kwargs):
+ """Calculates a User's gravatar hash from their email address."""
+ if kwargs.get('raw', False):
+ return
+ instance.gravatar = hashlib.md5(instance.email).hexdigest()
+
+def record_ask_event(instance, created, **kwargs):
+ if created:
+ activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION)
+ activity.save()
+
+def record_answer_event(instance, created, **kwargs):
+ if created:
+ activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER)
+ activity.save()
+
+def record_comment_event(instance, created, **kwargs):
+ if created:
+ from django.contrib.contenttypes.models import ContentType
+ question_type = ContentType.objects.get_for_model(Question)
+ question_type_id = question_type.id
+ if (instance.content_type_id == question_type_id):
+ type = TYPE_ACTIVITY_COMMENT_QUESTION
+ else:
+ type = TYPE_ACTIVITY_COMMENT_ANSWER
+ activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type)
+ activity.save()
+
+def record_revision_question_event(instance, created, **kwargs):
+ if created and instance.revision <> 1:
+ activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION)
+ activity.save()
+
+def record_revision_answer_event(instance, created, **kwargs):
+ if created and instance.revision <> 1:
+ activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER)
+ activity.save()
+
+def record_award_event(instance, created, **kwargs):
+ """
+ After we awarded a badge to user, we need to record this activity and notify user.
+ We also recaculate awarded_count of this badge and user information.
+ """
+ if created:
+ activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance,
+ activity_type=TYPE_ACTIVITY_PRIZE)
+ activity.save()
+
+ instance.badge.awarded_count += 1
+ instance.badge.save()
+
+ if instance.badge.type == Badge.GOLD:
+ instance.user.gold += 1
+ if instance.badge.type == Badge.SILVER:
+ instance.user.silver += 1
+ if instance.badge.type == Badge.BRONZE:
+ instance.user.bronze += 1
+ instance.user.save()
+
+def notify_award_message(instance, created, **kwargs):
+ """
+ Notify users when they have been awarded badges by using Django message.
+ """
+ if created:
+ user = instance.user
+ user.message_set.create(message=u"Congratulations, you have received a badge '%s'" % instance.badge.name)
+
+def record_answer_accepted(instance, created, **kwargs):
+ """
+ when answer is accepted, we record this for question author - who accepted it.
+ """
+ if not created and instance.accepted:
+ activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \
+ content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER)
+ activity.save()
+
+def update_last_seen(instance, created, **kwargs):
+ """
+ when user has activities, we update 'last_seen' time stamp for him
+ """
+ user = instance.user
+ user.last_seen = datetime.datetime.now()
+ user.save()
+
+def record_vote(instance, created, **kwargs):
+ """
+ when user have voted
+ """
+ if created:
+ if instance.vote == 1:
+ vote_type = TYPE_ACTIVITY_VOTE_UP
+ else:
+ vote_type = TYPE_ACTIVITY_VOTE_DOWN
+
+ activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type)
+ activity.save()
+
+def record_cancel_vote(instance, **kwargs):
+ """
+ when user canceled vote, the vote will be deleted.
+ """
+ activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE)
+ activity.save()
+
+def record_delete_question(instance, delete_by, **kwargs):
+ """
+ when user deleted the question
+ """
+ if instance.__class__ == "Question":
+ activity_type = TYPE_ACTIVITY_DELETE_QUESTION
+ else:
+ activity_type = TYPE_ACTIVITY_DELETE_ANSWER
+
+ activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type)
+ activity.save()
+
+def record_mark_offensive(instance, mark_by, **kwargs):
+ activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE)
+ activity.save()
+
+def record_update_tags(question, **kwargs):
+ """
+ when user updated tags of the question
+ """
+ activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS)
+ activity.save()
+
+def record_favorite_question(instance, created, **kwargs):
+ """
+ when user add the question in him favorite questions list.
+ """
+ if created:
+ activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE)
+ activity.save()
+
+def record_user_full_updated(instance, **kwargs):
+ activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED)
+ activity.save()
+
+def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
+ aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
+ aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
+ import settings
+ if settings.EMAIL_VALIDATION == 'on':#add user to the record
+ for aq in aq_list:
+ aq.author = user
+ aq.save()
+ for aa in aa_list:
+ aa.author = user
+ aa.save()
+ #maybe add pending posts message?
+ else: #just publish the questions
+ for aq in aq_list:
+ aq.publish(user)
+ for aa in aa_list:
+ aa.publish(user)
+
+#signal for User modle save changes
+
+pre_save.connect(calculate_gravatar_hash, sender=User)
+post_save.connect(record_ask_event, sender=Question)
+post_save.connect(record_answer_event, sender=Answer)
+post_save.connect(record_comment_event, sender=Comment)
+post_save.connect(record_revision_question_event, sender=QuestionRevision)
+post_save.connect(record_revision_answer_event, sender=AnswerRevision)
+post_save.connect(record_award_event, sender=Award)
+post_save.connect(notify_award_message, sender=Award)
+post_save.connect(record_answer_accepted, sender=Answer)
+post_save.connect(update_last_seen, sender=Activity)
+post_save.connect(record_vote, sender=Vote)
+post_delete.connect(record_cancel_vote, sender=Vote)
+delete_post_or_answer.connect(record_delete_question, sender=Question)
+delete_post_or_answer.connect(record_delete_question, sender=Answer)
+mark_offensive.connect(record_mark_offensive, sender=Question)
+mark_offensive.connect(record_mark_offensive, sender=Answer)
+tags_updated.connect(record_update_tags, sender=Question)
+post_save.connect(record_favorite_question, sender=FavoriteQuestion)
+user_updated.connect(record_user_full_updated, sender=User)
+user_logged_in.connect(post_stored_anonymous_content)
+
+Question = Question
+QuestionRevision = QuestionRevision
+QuestionView = QuestionView
+FavoriteQuestion = FavoriteQuestion
+AnonymousQuestion = AnonymousQuestion
+
+Answer = Answer
+AnswerRevision = AnswerRevision
+AnonymousAnswer = AnonymousAnswer
+
+Tag = Tag
+Comment = Comment
+Vote = Vote
+FlaggedItem = FlaggedItem
+MarkedTag = MarkedTag
+
+Badge = Badge
+Award = Award
+Repute = Repute
+
+Activity = Activity
+EmailFeedSetting = EmailFeedSetting
+AnonymousEmail = AnonymousEmail
+AuthKeyUserAssociation = AuthKeyUserAssociation
+
+__all__ = [
+ 'Question',
+ 'QuestionRevision',
+ 'QuestionView',
+ 'FavoriteQuestion',
+ 'AnonymousQuestion',
+
+ 'Answer',
+ 'AnswerRevision',
+ 'AnonymousAnswer',
+
+ 'Tag',
+ 'Comment',
+ 'Vote',
+ 'FlaggedItem',
+ 'MarkedTag',
+
+ 'Badge',
+ 'Award',
+ 'Repute',
+
+ 'Activity',
+ 'EmailFeedSetting',
+ 'AnonymousEmail',
+ 'AuthKeyUserAssociation',
+
+ 'User'
+ ]
+
+
+from forum.modules import get_modules_script_classes
+
+for k, v in get_modules_script_classes('models', models.Model).items():
+ if not k in __all__:
+ __all__.append(k)
exec "%s = v" % k \ No newline at end of file
diff --git a/forum/models/answer.py b/forum/models/answer.py
index 14199de3..9a0fc215 100755
--- a/forum/models/answer.py
+++ b/forum/models/answer.py
@@ -1,134 +1,134 @@
-from base import *
-
-from question import Question
-
-class AnswerManager(models.Manager):
- @staticmethod
- def create_new(cls, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
- answer = Answer(
- question = question,
- author = author,
- added_at = added_at,
- wiki = wiki,
- html = text
- )
- if answer.wiki:
- answer.last_edited_by = answer.author
- answer.last_edited_at = added_at
- answer.wikified_at = added_at
-
- answer.save()
-
- #update question data
- question.last_activity_at = added_at
- question.last_activity_by = author
- question.save()
- Question.objects.update_answer_count(question)
-
- AnswerRevision.objects.create(
- answer = answer,
- revision = 1,
- author = author,
- revised_at = added_at,
- summary = CONST['default_version'],
- text = text
- )
-
- #set notification/delete
- if email_notify:
- if author not in question.followed_by.all():
- question.followed_by.add(author)
- else:
- #not sure if this is necessary. ajax should take care of this...
- try:
- question.followed_by.remove(author)
- except:
- pass
-
- #GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
- def get_answers_from_question(self, question, user=None):
- """
- Retrieves visibile answers for the given question. Delete answers
- are only visibile to the person who deleted them.
- """
-
- if user is None or not user.is_authenticated():
- return self.filter(question=question, deleted=False)
- else:
- return self.filter(models.Q(question=question),
- models.Q(deleted=False) | models.Q(deleted_by=user))
-
- #todo: I think this method is not being used anymore, I'll just comment it for now
- #def get_answers_from_questions(self, user_id):
- # """
- # Retrieves visibile answers for the given question. Which are not included own answers
- # """
- # cursor = connection.cursor()
- # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
- # return cursor.fetchall()
-
-class Answer(Content, DeletableContent):
- question = models.ForeignKey('Question', related_name='answers')
- accepted = models.BooleanField(default=False)
- accepted_at = models.DateTimeField(null=True, blank=True)
-
- objects = AnswerManager()
-
- class Meta(Content.Meta):
- db_table = u'answer'
-
- def get_user_vote(self, user):
- if user.__class__.__name__ == "AnonymousUser":
- return None
-
- votes = self.votes.filter(user=user)
- if votes and votes.count() > 0:
- return votes[0]
- else:
- return None
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
-
- def __unicode__(self):
- return self.html
-
-
-class AnswerRevision(ContentRevision):
- """A revision of an Answer."""
- answer = models.ForeignKey('Answer', related_name='revisions')
-
- def get_absolute_url(self):
- return reverse('answer_revisions', kwargs={'id':self.answer.id})
-
- def get_question_title(self):
- return self.answer.question.title
-
- class Meta(ContentRevision.Meta):
- db_table = u'answer_revision'
- ordering = ('-revision',)
-
- def save(self, **kwargs):
- """Looks up the next available revision number if not set."""
- if not self.revision:
- self.revision = AnswerRevision.objects.filter(
- answer=self.answer).values_list('revision',
- flat=True)[0] + 1
- super(AnswerRevision, self).save(**kwargs)
-
-class AnonymousAnswer(AnonymousContent):
- question = models.ForeignKey('Question', related_name='anonymous_answers')
-
- def publish(self,user):
- added_at = datetime.datetime.now()
- #print user.id
- AnswerManager.create_new(question=self.question,wiki=self.wiki,
- added_at=added_at,text=self.text,
- author=user)
- self.delete()
+from base import *
+
+from question import Question
+
+class AnswerManager(models.Manager):
+ @staticmethod
+ def create_new(cls, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
+ answer = Answer(
+ question = question,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ html = text
+ )
+ if answer.wiki:
+ answer.last_edited_by = answer.author
+ answer.last_edited_at = added_at
+ answer.wikified_at = added_at
+
+ answer.save()
+
+ #update question data
+ question.last_activity_at = added_at
+ question.last_activity_by = author
+ question.save()
+ Question.objects.update_answer_count(question)
+
+ AnswerRevision.objects.create(
+ answer = answer,
+ revision = 1,
+ author = author,
+ revised_at = added_at,
+ summary = CONST['default_version'],
+ text = text
+ )
+
+ #set notification/delete
+ if email_notify:
+ if author not in question.followed_by.all():
+ question.followed_by.add(author)
+ else:
+ #not sure if this is necessary. ajax should take care of this...
+ try:
+ question.followed_by.remove(author)
+ except:
+ pass
+
+ #GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
+ def get_answers_from_question(self, question, user=None):
+ """
+ Retrieves visibile answers for the given question. Delete answers
+ are only visibile to the person who deleted them.
+ """
+
+ if user is None or not user.is_authenticated():
+ return self.filter(question=question, deleted=False)
+ else:
+ return self.filter(models.Q(question=question),
+ models.Q(deleted=False) | models.Q(deleted_by=user))
+
+ #todo: I think this method is not being used anymore, I'll just comment it for now
+ #def get_answers_from_questions(self, user_id):
+ # """
+ # Retrieves visibile answers for the given question. Which are not included own answers
+ # """
+ # cursor = connection.cursor()
+ # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
+ # return cursor.fetchall()
+
+class Answer(Content, DeletableContent):
+ question = models.ForeignKey('Question', related_name='answers')
+ accepted = models.BooleanField(default=False)
+ accepted_at = models.DateTimeField(null=True, blank=True)
+
+ objects = AnswerManager()
+
+ class Meta(Content.Meta):
+ db_table = u'answer'
+
+ def get_user_vote(self, user):
+ if user.__class__.__name__ == "AnonymousUser":
+ return None
+
+ votes = self.votes.filter(user=user)
+ if votes and votes.count() > 0:
+ return votes[0]
+ else:
+ return None
+
+ def get_latest_revision(self):
+ return self.revisions.all()[0]
+
+ def get_question_title(self):
+ return self.question.title
+
+ def get_absolute_url(self):
+ return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
+
+ def __unicode__(self):
+ return self.html
+
+
+class AnswerRevision(ContentRevision):
+ """A revision of an Answer."""
+ answer = models.ForeignKey('Answer', related_name='revisions')
+
+ def get_absolute_url(self):
+ return reverse('answer_revisions', kwargs={'id':self.answer.id})
+
+ def get_question_title(self):
+ return self.answer.question.title
+
+ class Meta(ContentRevision.Meta):
+ db_table = u'answer_revision'
+ ordering = ('-revision',)
+
+ def save(self, **kwargs):
+ """Looks up the next available revision number if not set."""
+ if not self.revision:
+ self.revision = AnswerRevision.objects.filter(
+ answer=self.answer).values_list('revision',
+ flat=True)[0] + 1
+ super(AnswerRevision, self).save(**kwargs)
+
+class AnonymousAnswer(AnonymousContent):
+ question = models.ForeignKey('Question', related_name='anonymous_answers')
+
+ def publish(self,user):
+ added_at = datetime.datetime.now()
+ #print user.id
+ AnswerManager.create_new(question=self.question,wiki=self.wiki,
+ added_at=added_at,text=self.text,
+ author=user)
+ self.delete()
diff --git a/forum/models/base.py b/forum/models/base.py
index 2c28a470..44fa6e66 100755
--- a/forum/models/base.py
+++ b/forum/models/base.py
@@ -1,139 +1,139 @@
-import datetime
-import hashlib
-from urllib import quote_plus, urlencode
-from django.db import models, IntegrityError, connection, transaction
-from django.utils.http import urlquote as django_urlquote
-from django.utils.html import strip_tags
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.template.defaultfilters import slugify
-from django.db.models.signals import post_delete, post_save, pre_save
-from django.utils.translation import ugettext as _
-from django.utils.safestring import mark_safe
-from django.contrib.sitemaps import ping_google
-import django.dispatch
-from django.conf import settings
-import logging
-
-if settings.USE_SPHINX_SEARCH == True:
- from djangosphinx.models import SphinxSearch
-
-from forum.const import *
-
-class MetaContent(models.Model):
- """
- Base class for Vote, Comment and FlaggedItem
- """
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='%(class)ss')
-
- class Meta:
- abstract = True
- app_label = 'forum'
-
-
-class DeletableContent(models.Model):
- deleted = models.BooleanField(default=False)
- deleted_at = models.DateTimeField(null=True, blank=True)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss')
-
- class Meta:
- abstract = True
- app_label = 'forum'
-
-
-class ContentRevision(models.Model):
- """
- Base class for QuestionRevision and AnswerRevision
- """
- revision = models.PositiveIntegerField()
- author = models.ForeignKey(User, related_name='%(class)ss')
- revised_at = models.DateTimeField()
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
-
- class Meta:
- abstract = True
- app_label = 'forum'
-
-
-class AnonymousContent(models.Model):
- """
- Base class for AnonymousQuestion and AnonymousAnswer
- """
- session_key = models.CharField(max_length=40) #session id for anonymous questions
- wiki = models.BooleanField(default=False)
- added_at = models.DateTimeField(default=datetime.datetime.now)
- ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
- author = models.ForeignKey(User,null=True)
- text = models.TextField()
- summary = models.CharField(max_length=180)
-
- class Meta:
- abstract = True
- app_label = 'forum'
-
-
-from meta import Comment, Vote, FlaggedItem
-
-class Content(models.Model):
- """
- Base class for Question and Answer
- """
- author = models.ForeignKey(User, related_name='%(class)ss')
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- wiki = models.BooleanField(default=False)
- wikified_at = models.DateTimeField(null=True, blank=True)
-
- locked = models.BooleanField(default=False)
- locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_%(class)ss')
- locked_at = models.DateTimeField(null=True, blank=True)
-
- score = models.IntegerField(default=0)
- vote_up_count = models.IntegerField(default=0)
- vote_down_count = models.IntegerField(default=0)
-
- comment_count = models.PositiveIntegerField(default=0)
- offensive_flag_count = models.SmallIntegerField(default=0)
-
- last_edited_at = models.DateTimeField(null=True, blank=True)
- last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
-
- html = models.TextField()
- comments = generic.GenericRelation(Comment)
- votes = generic.GenericRelation(Vote)
- flagged_items = generic.GenericRelation(FlaggedItem)
-
- class Meta:
- abstract = True
- app_label = 'forum'
-
- def save(self,**kwargs):
- super(Content,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def get_object_comments(self):
- comments = self.comments.all().order_by('id')
- return comments
-
- def post_get_last_update_info(self):
- when = self.added_at
- who = self.author
- if self.last_edited_at and self.last_edited_at > when:
- when = self.last_edited_at
- who = self.last_edited_by
- comments = self.comments.all()
- if len(comments) > 0:
- for c in comments:
- if c.added_at > when:
- when = c.added_at
- who = c.user
+import datetime
+import hashlib
+from urllib import quote_plus, urlencode
+from django.db import models, IntegrityError, connection, transaction
+from django.utils.http import urlquote as django_urlquote
+from django.utils.html import strip_tags
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.template.defaultfilters import slugify
+from django.db.models.signals import post_delete, post_save, pre_save
+from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
+from django.contrib.sitemaps import ping_google
+import django.dispatch
+from django.conf import settings
+import logging
+
+if settings.USE_SPHINX_SEARCH == True:
+ from djangosphinx.models import SphinxSearch
+
+from forum.const import *
+
+class MetaContent(models.Model):
+ """
+ Base class for Vote, Comment and FlaggedItem
+ """
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ user = models.ForeignKey(User, related_name='%(class)ss')
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class DeletableContent(models.Model):
+ deleted = models.BooleanField(default=False)
+ deleted_at = models.DateTimeField(null=True, blank=True)
+ deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss')
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class ContentRevision(models.Model):
+ """
+ Base class for QuestionRevision and AnswerRevision
+ """
+ revision = models.PositiveIntegerField()
+ author = models.ForeignKey(User, related_name='%(class)ss')
+ revised_at = models.DateTimeField()
+ summary = models.CharField(max_length=300, blank=True)
+ text = models.TextField()
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class AnonymousContent(models.Model):
+ """
+ Base class for AnonymousQuestion and AnonymousAnswer
+ """
+ session_key = models.CharField(max_length=40) #session id for anonymous questions
+ wiki = models.BooleanField(default=False)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+ ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
+ author = models.ForeignKey(User,null=True)
+ text = models.TextField()
+ summary = models.CharField(max_length=180)
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+from meta import Comment, Vote, FlaggedItem
+
+class Content(models.Model):
+ """
+ Base class for Question and Answer
+ """
+ author = models.ForeignKey(User, related_name='%(class)ss')
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ wiki = models.BooleanField(default=False)
+ wikified_at = models.DateTimeField(null=True, blank=True)
+
+ locked = models.BooleanField(default=False)
+ locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_%(class)ss')
+ locked_at = models.DateTimeField(null=True, blank=True)
+
+ score = models.IntegerField(default=0)
+ vote_up_count = models.IntegerField(default=0)
+ vote_down_count = models.IntegerField(default=0)
+
+ comment_count = models.PositiveIntegerField(default=0)
+ offensive_flag_count = models.SmallIntegerField(default=0)
+
+ last_edited_at = models.DateTimeField(null=True, blank=True)
+ last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
+
+ html = models.TextField()
+ comments = generic.GenericRelation(Comment)
+ votes = generic.GenericRelation(Vote)
+ flagged_items = generic.GenericRelation(FlaggedItem)
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+ def save(self,**kwargs):
+ super(Content,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def get_object_comments(self):
+ comments = self.comments.all().order_by('id')
+ return comments
+
+ def post_get_last_update_info(self):
+ when = self.added_at
+ who = self.author
+ if self.last_edited_at and self.last_edited_at > when:
+ when = self.last_edited_at
+ who = self.last_edited_by
+ comments = self.comments.all()
+ if len(comments) > 0:
+ for c in comments:
+ if c.added_at > when:
+ when = c.added_at
+ who = c.user
return when, who \ No newline at end of file
diff --git a/forum/models/meta.py b/forum/models/meta.py
index 3dfd3e86..7c3f5d36 100755
--- a/forum/models/meta.py
+++ b/forum/models/meta.py
@@ -1,89 +1,89 @@
-from base import *
-
-class VoteManager(models.Manager):
- def get_up_vote_count_from_user(self, user):
- if user is not None:
- return self.filter(user=user, vote=1).count()
- else:
- return 0
-
- def get_down_vote_count_from_user(self, user):
- if user is not None:
- return self.filter(user=user, vote=-1).count()
- else:
- return 0
-
- def get_votes_count_today_from_user(self, user):
- if user is not None:
- today = datetime.date.today()
- return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count()
-
- else:
- return 0
-
-
-class Vote(MetaContent):
- VOTE_UP = +1
- VOTE_DOWN = -1
- VOTE_CHOICES = (
- (VOTE_UP, u'Up'),
- (VOTE_DOWN, u'Down'),
- )
-
- vote = models.SmallIntegerField(choices=VOTE_CHOICES)
- voted_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = VoteManager()
-
- class Meta(MetaContent.Meta):
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'vote'
-
- def __unicode__(self):
- return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote)
-
- def is_upvote(self):
- return self.vote == self.VOTE_UP
-
- def is_downvote(self):
- return self.vote == self.VOTE_DOWN
-
-
-class FlaggedItemManager(models.Manager):
- def get_flagged_items_count_today(self, user):
- if user is not None:
- today = datetime.date.today()
- return self.filter(user=user, flagged_at__range=(today, today + datetime.timedelta(1))).count()
- else:
- return 0
-
-class FlaggedItem(MetaContent):
- """A flag on a Question or Answer indicating offensive content."""
- flagged_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = FlaggedItemManager()
-
- class Meta(MetaContent.Meta):
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'flagged_item'
-
- def __unicode__(self):
- return '[%s] flagged at %s' %(self.user, self.flagged_at)
-
-class Comment(MetaContent):
- comment = models.CharField(max_length=300)
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta(MetaContent.Meta):
- ordering = ('-added_at',)
- db_table = u'comment'
-
- def save(self,**kwargs):
- super(Comment,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def __unicode__(self):
+from base import *
+
+class VoteManager(models.Manager):
+ def get_up_vote_count_from_user(self, user):
+ if user is not None:
+ return self.filter(user=user, vote=1).count()
+ else:
+ return 0
+
+ def get_down_vote_count_from_user(self, user):
+ if user is not None:
+ return self.filter(user=user, vote=-1).count()
+ else:
+ return 0
+
+ def get_votes_count_today_from_user(self, user):
+ if user is not None:
+ today = datetime.date.today()
+ return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count()
+
+ else:
+ return 0
+
+
+class Vote(MetaContent):
+ VOTE_UP = +1
+ VOTE_DOWN = -1
+ VOTE_CHOICES = (
+ (VOTE_UP, u'Up'),
+ (VOTE_DOWN, u'Down'),
+ )
+
+ vote = models.SmallIntegerField(choices=VOTE_CHOICES)
+ voted_at = models.DateTimeField(default=datetime.datetime.now)
+
+ objects = VoteManager()
+
+ class Meta(MetaContent.Meta):
+ unique_together = ('content_type', 'object_id', 'user')
+ db_table = u'vote'
+
+ def __unicode__(self):
+ return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote)
+
+ def is_upvote(self):
+ return self.vote == self.VOTE_UP
+
+ def is_downvote(self):
+ return self.vote == self.VOTE_DOWN
+
+
+class FlaggedItemManager(models.Manager):
+ def get_flagged_items_count_today(self, user):
+ if user is not None:
+ today = datetime.date.today()
+ return self.filter(user=user, flagged_at__range=(today, today + datetime.timedelta(1))).count()
+ else:
+ return 0
+
+class FlaggedItem(MetaContent):
+ """A flag on a Question or Answer indicating offensive content."""
+ flagged_at = models.DateTimeField(default=datetime.datetime.now)
+
+ objects = FlaggedItemManager()
+
+ class Meta(MetaContent.Meta):
+ unique_together = ('content_type', 'object_id', 'user')
+ db_table = u'flagged_item'
+
+ def __unicode__(self):
+ return '[%s] flagged at %s' %(self.user, self.flagged_at)
+
+class Comment(MetaContent):
+ comment = models.CharField(max_length=300)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta(MetaContent.Meta):
+ ordering = ('-added_at',)
+ db_table = u'comment'
+
+ def save(self,**kwargs):
+ super(Comment,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def __unicode__(self):
return self.comment \ No newline at end of file
diff --git a/forum/models/question.py b/forum/models/question.py
index f916e656..05762c3e 100755
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -1,336 +1,336 @@
-from base import *
-from tag import Tag
-
-class QuestionManager(models.Manager):
- @staticmethod
- def create_new(cls, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
- question = Question(
- title = title,
- author = author,
- added_at = added_at,
- last_activity_at = added_at,
- last_activity_by = author,
- wiki = wiki,
- tagnames = tagnames,
- html = text,
- summary = summary
- )
- if question.wiki:
- question.last_edited_by = question.author
- question.last_edited_at = added_at
- question.wikified_at = added_at
-
- question.save()
-
- # create the first revision
- QuestionRevision.objects.create(
- question = question,
- revision = 1,
- title = question.title,
- author = author,
- revised_at = added_at,
- tagnames = question.tagnames,
- summary = CONST['default_version'],
- text = text
- )
- return question
-
- def update_tags(self, question, tagnames, user):
- """
- Updates Tag associations for a question to match the given
- tagname string.
-
- Returns ``True`` if tag usage counts were updated as a result,
- ``False`` otherwise.
- """
-
- current_tags = list(question.tags.all())
- current_tagnames = set(t.name for t in current_tags)
- updated_tagnames = set(t for t in tagnames.split(' ') if t)
- modified_tags = []
-
- removed_tags = [t for t in current_tags
- if t.name not in updated_tagnames]
- if removed_tags:
- modified_tags.extend(removed_tags)
- question.tags.remove(*removed_tags)
-
- added_tagnames = updated_tagnames - current_tagnames
- if added_tagnames:
- added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
- user)
- modified_tags.extend(added_tags)
- question.tags.add(*added_tags)
-
- if modified_tags:
- Tag.objects.update_use_counts(modified_tags)
- return True
-
- return False
-
- def update_answer_count(self, question):
- """
- Executes an UPDATE query to update denormalised data with the
- number of answers the given question has.
- """
-
- # for some reasons, this Answer class failed to be imported,
- # although we have imported all classes from models on top.
- from answer import Answer
- self.filter(id=question.id).update(
- answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
-
- def update_view_count(self, question):
- """
- update counter+1 when user browse question page
- """
- self.filter(id=question.id).update(view_count = question.view_count + 1)
-
- def update_favorite_count(self, question):
- """
- update favourite_count for given question
- """
- self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
-
- def get_similar_questions(self, question):
- """
- Get 10 similar questions for given one.
- This will search the same tag list for give question(by exactly same string) first.
- Questions with the individual tags will be added to list if above questions are not full.
- """
- #print datetime.datetime.now()
- questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
-
- tags_list = question.tags.all()
- for tag in tags_list:
- extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
- for item in extend_questions:
- if item not in questions and len(questions) < 10:
- questions.append(item)
-
- #print datetime.datetime.now()
- return questions
-
-class Question(Content, DeletableContent):
- title = models.CharField(max_length=300)
- tags = models.ManyToManyField('Tag', related_name='questions')
- answer_accepted = models.BooleanField(default=False)
- closed = models.BooleanField(default=False)
- closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
- closed_at = models.DateTimeField(null=True, blank=True)
- close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
- followed_by = models.ManyToManyField(User, related_name='followed_questions')
-
- # Denormalised data
- answer_count = models.PositiveIntegerField(default=0)
- view_count = models.PositiveIntegerField(default=0)
- favourite_count = models.PositiveIntegerField(default=0)
- last_activity_at = models.DateTimeField(default=datetime.datetime.now)
- last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=180)
-
- favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions')
-
- objects = QuestionManager()
-
- class Meta(Content.Meta):
- db_table = u'question'
-
- def delete(self):
- super(Question, self).delete()
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def save(self, **kwargs):
- """
- Overridden to manually manage addition of tags when the object
- is first saved.
-
- This is required as we're using ``tagnames`` as the sole means of
- adding and editing tags.
- """
- initial_addition = (self.id is None)
-
- super(Question, self).save(**kwargs)
-
- if initial_addition:
- tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
- self.author)
- self.tags.add(*tags)
- Tag.objects.update_use_counts(tags)
-
- def tagname_list(self):
- """Creates a list of Tag names from the ``tagnames`` attribute."""
- return [name for name in self.tagnames.split(u' ')]
-
- def tagname_meta_generator(self):
- return u','.join([unicode(tag) for tag in self.tagname_list()])
-
- def get_absolute_url(self):
- return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
-
- def has_favorite_by_user(self, user):
- if not user.is_authenticated():
- return False
-
- return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
-
- def get_answer_count_by_user(self, user_id):
- from answer import Answer
- query_set = Answer.objects.filter(author__id=user_id)
- return query_set.filter(question=self).count()
-
- def get_question_title(self):
- if self.closed:
- attr = CONST['closed']
- elif self.deleted:
- attr = CONST['deleted']
- else:
- attr = None
- if attr is not None:
- return u'%s %s' % (self.title, attr)
- else:
- return self.title
-
- def get_revision_url(self):
- return reverse('question_revisions', args=[self.id])
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- def get_last_update_info(self):
- when, who = self.post_get_last_update_info()
-
- answers = self.answers.all()
- if len(answers) > 0:
- for a in answers:
- a_when, a_who = a.post_get_last_update_info()
- if a_when > when:
- when = a_when
- who = a_who
-
- return when, who
-
- def get_update_summary(self,last_reported_at=None,recipient_email=''):
- edited = False
- if self.last_edited_at and self.last_edited_at > last_reported_at:
- if self.last_edited_by.email != recipient_email:
- edited = True
- comments = []
- for comment in self.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- comments.append(comment)
- new_answers = []
- answer_comments = []
- modified_answers = []
- commented_answers = []
- import sets
- commented_answers = sets.Set([])
- for answer in self.answers.all():
- if (answer.added_at > last_reported_at and answer.author.email != recipient_email):
- new_answers.append(answer)
- if (answer.last_edited_at
- and answer.last_edited_at > last_reported_at
- and answer.last_edited_by.email != recipient_email):
- modified_answers.append(answer)
- for comment in answer.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- commented_answers.add(answer)
- answer_comments.append(comment)
-
- #create the report
- if edited or new_answers or modified_answers or answer_comments:
- out = []
- if edited:
- out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username})
- if new_answers:
- names = sets.Set(map(lambda x: x.author.username,new_answers))
- people = ', '.join(names)
- out.append(_('%(people)s posted %(new_answer_count)s new answers') \
- % {'new_answer_count':len(new_answers),'people':people})
- if comments:
- names = sets.Set(map(lambda x: x.user.username,comments))
- people = ', '.join(names)
- out.append(_('%(people)s commented the question') % {'people':people})
- if answer_comments:
- names = sets.Set(map(lambda x: x.user.username,answer_comments))
- people = ', '.join(names)
- if len(commented_answers) > 1:
- out.append(_('%(people)s commented answers') % {'people':people})
- else:
- out.append(_('%(people)s commented an answer') % {'people':people})
- url = settings.APP_URL + self.get_absolute_url()
- retval = '<a href="%s">%s</a>:<br>\n' % (url,self.title)
- out = map(lambda x: '<li>' + x + '</li>',out)
- retval += '<ul>' + '\n'.join(out) + '</ul><br>\n'
- return retval
- else:
- return None
-
- def __unicode__(self):
- return self.title
-
-
-class QuestionView(models.Model):
- question = models.ForeignKey(Question, related_name='viewed')
- who = models.ForeignKey(User, related_name='question_views')
- when = models.DateTimeField()
-
- class Meta:
- app_label = 'forum'
-
-class FavoriteQuestion(models.Model):
- """A favorite Question of a User."""
- question = models.ForeignKey(Question)
- user = models.ForeignKey(User, related_name='user_favorite_questions')
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta:
- app_label = 'forum'
- db_table = u'favorite_question'
- def __unicode__(self):
- return '[%s] favorited at %s' %(self.user, self.added_at)
-
-class QuestionRevision(ContentRevision):
- """A revision of a Question."""
- question = models.ForeignKey(Question, related_name='revisions')
- title = models.CharField(max_length=300)
- tagnames = models.CharField(max_length=125)
-
- class Meta(ContentRevision.Meta):
- db_table = u'question_revision'
- ordering = ('-revision',)
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- #print 'in QuestionRevision.get_absolute_url()'
- return reverse('question_revisions', args=[self.question.id])
-
- def save(self, **kwargs):
- """Looks up the next available revision number."""
- if not self.revision:
- self.revision = QuestionRevision.objects.filter(
- question=self.question).values_list('revision',
- flat=True)[0] + 1
- super(QuestionRevision, self).save(**kwargs)
-
- def __unicode__(self):
- return u'revision %s of %s' % (self.revision, self.title)
-
-class AnonymousQuestion(AnonymousContent):
- title = models.CharField(max_length=300)
- tagnames = models.CharField(max_length=125)
-
- def publish(self,user):
- added_at = datetime.datetime.now()
- QuestionManager.create_new(title=self.title, author=user, added_at=added_at,
- wiki=self.wiki, tagnames=self.tagnames,
- summary=self.summary, text=self.text)
- self.delete()
-
-from answer import Answer, AnswerManager
+from base import *
+from tag import Tag
+
+class QuestionManager(models.Manager):
+ @staticmethod
+ def create_new(cls, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
+ question = Question(
+ title = title,
+ author = author,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = author,
+ wiki = wiki,
+ tagnames = tagnames,
+ html = text,
+ summary = summary
+ )
+ if question.wiki:
+ question.last_edited_by = question.author
+ question.last_edited_at = added_at
+ question.wikified_at = added_at
+
+ question.save()
+
+ # create the first revision
+ QuestionRevision.objects.create(
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = author,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = text
+ )
+ return question
+
+ def update_tags(self, question, tagnames, user):
+ """
+ Updates Tag associations for a question to match the given
+ tagname string.
+
+ Returns ``True`` if tag usage counts were updated as a result,
+ ``False`` otherwise.
+ """
+
+ current_tags = list(question.tags.all())
+ current_tagnames = set(t.name for t in current_tags)
+ updated_tagnames = set(t for t in tagnames.split(' ') if t)
+ modified_tags = []
+
+ removed_tags = [t for t in current_tags
+ if t.name not in updated_tagnames]
+ if removed_tags:
+ modified_tags.extend(removed_tags)
+ question.tags.remove(*removed_tags)
+
+ added_tagnames = updated_tagnames - current_tagnames
+ if added_tagnames:
+ added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
+ user)
+ modified_tags.extend(added_tags)
+ question.tags.add(*added_tags)
+
+ if modified_tags:
+ Tag.objects.update_use_counts(modified_tags)
+ return True
+
+ return False
+
+ def update_answer_count(self, question):
+ """
+ Executes an UPDATE query to update denormalised data with the
+ number of answers the given question has.
+ """
+
+ # for some reasons, this Answer class failed to be imported,
+ # although we have imported all classes from models on top.
+ from answer import Answer
+ self.filter(id=question.id).update(
+ answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
+
+ def update_view_count(self, question):
+ """
+ update counter+1 when user browse question page
+ """
+ self.filter(id=question.id).update(view_count = question.view_count + 1)
+
+ def update_favorite_count(self, question):
+ """
+ update favourite_count for given question
+ """
+ self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
+
+ def get_similar_questions(self, question):
+ """
+ Get 10 similar questions for given one.
+ This will search the same tag list for give question(by exactly same string) first.
+ Questions with the individual tags will be added to list if above questions are not full.
+ """
+ #print datetime.datetime.now()
+ questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
+
+ tags_list = question.tags.all()
+ for tag in tags_list:
+ extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
+ for item in extend_questions:
+ if item not in questions and len(questions) < 10:
+ questions.append(item)
+
+ #print datetime.datetime.now()
+ return questions
+
+class Question(Content, DeletableContent):
+ title = models.CharField(max_length=300)
+ tags = models.ManyToManyField('Tag', related_name='questions')
+ answer_accepted = models.BooleanField(default=False)
+ closed = models.BooleanField(default=False)
+ closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
+ closed_at = models.DateTimeField(null=True, blank=True)
+ close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
+ followed_by = models.ManyToManyField(User, related_name='followed_questions')
+
+ # Denormalised data
+ answer_count = models.PositiveIntegerField(default=0)
+ view_count = models.PositiveIntegerField(default=0)
+ favourite_count = models.PositiveIntegerField(default=0)
+ last_activity_at = models.DateTimeField(default=datetime.datetime.now)
+ last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
+ tagnames = models.CharField(max_length=125)
+ summary = models.CharField(max_length=180)
+
+ favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions')
+
+ objects = QuestionManager()
+
+ class Meta(Content.Meta):
+ db_table = u'question'
+
+ def delete(self):
+ super(Question, self).delete()
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def save(self, **kwargs):
+ """
+ Overridden to manually manage addition of tags when the object
+ is first saved.
+
+ This is required as we're using ``tagnames`` as the sole means of
+ adding and editing tags.
+ """
+ initial_addition = (self.id is None)
+
+ super(Question, self).save(**kwargs)
+
+ if initial_addition:
+ tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
+ self.author)
+ self.tags.add(*tags)
+ Tag.objects.update_use_counts(tags)
+
+ def tagname_list(self):
+ """Creates a list of Tag names from the ``tagnames`` attribute."""
+ return [name for name in self.tagnames.split(u' ')]
+
+ def tagname_meta_generator(self):
+ return u','.join([unicode(tag) for tag in self.tagname_list()])
+
+ def get_absolute_url(self):
+ return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
+
+ def has_favorite_by_user(self, user):
+ if not user.is_authenticated():
+ return False
+
+ return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
+
+ def get_answer_count_by_user(self, user_id):
+ from answer import Answer
+ query_set = Answer.objects.filter(author__id=user_id)
+ return query_set.filter(question=self).count()
+
+ def get_question_title(self):
+ if self.closed:
+ attr = CONST['closed']
+ elif self.deleted:
+ attr = CONST['deleted']
+ else:
+ attr = None
+ if attr is not None:
+ return u'%s %s' % (self.title, attr)
+ else:
+ return self.title
+
+ def get_revision_url(self):
+ return reverse('question_revisions', args=[self.id])
+
+ def get_latest_revision(self):
+ return self.revisions.all()[0]
+
+ def get_last_update_info(self):
+ when, who = self.post_get_last_update_info()
+
+ answers = self.answers.all()
+ if len(answers) > 0:
+ for a in answers:
+ a_when, a_who = a.post_get_last_update_info()
+ if a_when > when:
+ when = a_when
+ who = a_who
+
+ return when, who
+
+ def get_update_summary(self,last_reported_at=None,recipient_email=''):
+ edited = False
+ if self.last_edited_at and self.last_edited_at > last_reported_at:
+ if self.last_edited_by.email != recipient_email:
+ edited = True
+ comments = []
+ for comment in self.comments.all():
+ if comment.added_at > last_reported_at and comment.user.email != recipient_email:
+ comments.append(comment)
+ new_answers = []
+ answer_comments = []
+ modified_answers = []
+ commented_answers = []
+ import sets
+ commented_answers = sets.Set([])
+ for answer in self.answers.all():
+ if (answer.added_at > last_reported_at and answer.author.email != recipient_email):
+ new_answers.append(answer)
+ if (answer.last_edited_at
+ and answer.last_edited_at > last_reported_at
+ and answer.last_edited_by.email != recipient_email):
+ modified_answers.append(answer)
+ for comment in answer.comments.all():
+ if comment.added_at > last_reported_at and comment.user.email != recipient_email:
+ commented_answers.add(answer)
+ answer_comments.append(comment)
+
+ #create the report
+ if edited or new_answers or modified_answers or answer_comments:
+ out = []
+ if edited:
+ out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username})
+ if new_answers:
+ names = sets.Set(map(lambda x: x.author.username,new_answers))
+ people = ', '.join(names)
+ out.append(_('%(people)s posted %(new_answer_count)s new answers') \
+ % {'new_answer_count':len(new_answers),'people':people})
+ if comments:
+ names = sets.Set(map(lambda x: x.user.username,comments))
+ people = ', '.join(names)
+ out.append(_('%(people)s commented the question') % {'people':people})
+ if answer_comments:
+ names = sets.Set(map(lambda x: x.user.username,answer_comments))
+ people = ', '.join(names)
+ if len(commented_answers) > 1:
+ out.append(_('%(people)s commented answers') % {'people':people})
+ else:
+ out.append(_('%(people)s commented an answer') % {'people':people})
+ url = settings.APP_URL + self.get_absolute_url()
+ retval = '<a href="%s">%s</a>:<br>\n' % (url,self.title)
+ out = map(lambda x: '<li>' + x + '</li>',out)
+ retval += '<ul>' + '\n'.join(out) + '</ul><br>\n'
+ return retval
+ else:
+ return None
+
+ def __unicode__(self):
+ return self.title
+
+
+class QuestionView(models.Model):
+ question = models.ForeignKey(Question, related_name='viewed')
+ who = models.ForeignKey(User, related_name='question_views')
+ when = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
+
+class FavoriteQuestion(models.Model):
+ """A favorite Question of a User."""
+ question = models.ForeignKey(Question)
+ user = models.ForeignKey(User, related_name='user_favorite_questions')
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'favorite_question'
+ def __unicode__(self):
+ return '[%s] favorited at %s' %(self.user, self.added_at)
+
+class QuestionRevision(ContentRevision):
+ """A revision of a Question."""
+ question = models.ForeignKey(Question, related_name='revisions')
+ title = models.CharField(max_length=300)
+ tagnames = models.CharField(max_length=125)
+
+ class Meta(ContentRevision.Meta):
+ db_table = u'question_revision'
+ ordering = ('-revision',)
+
+ def get_question_title(self):
+ return self.question.title
+
+ def get_absolute_url(self):
+ #print 'in QuestionRevision.get_absolute_url()'
+ return reverse('question_revisions', args=[self.question.id])
+
+ def save(self, **kwargs):
+ """Looks up the next available revision number."""
+ if not self.revision:
+ self.revision = QuestionRevision.objects.filter(
+ question=self.question).values_list('revision',
+ flat=True)[0] + 1
+ super(QuestionRevision, self).save(**kwargs)
+
+ def __unicode__(self):
+ return u'revision %s of %s' % (self.revision, self.title)
+
+class AnonymousQuestion(AnonymousContent):
+ title = models.CharField(max_length=300)
+ tagnames = models.CharField(max_length=125)
+
+ def publish(self,user):
+ added_at = datetime.datetime.now()
+ QuestionManager.create_new(title=self.title, author=user, added_at=added_at,
+ wiki=self.wiki, tagnames=self.tagnames,
+ summary=self.summary, text=self.text)
+ self.delete()
+
+from answer import Answer, AnswerManager
diff --git a/forum/models/repute.py b/forum/models/repute.py
index a47ce47d..495ba7e9 100755
--- a/forum/models/repute.py
+++ b/forum/models/repute.py
@@ -1,109 +1,109 @@
-from base import *
-
-from django.utils.translation import ugettext as _
-
-class Badge(models.Model):
- """Awarded for notable actions performed on the site by Users."""
- GOLD = 1
- SILVER = 2
- BRONZE = 3
- TYPE_CHOICES = (
- (GOLD, _('gold')),
- (SILVER, _('silver')),
- (BRONZE, _('bronze')),
- )
-
- name = models.CharField(max_length=50)
- type = models.SmallIntegerField(choices=TYPE_CHOICES)
- slug = models.SlugField(max_length=50, blank=True)
- description = models.CharField(max_length=300)
- multiple = models.BooleanField(default=False)
- # Denormalised data
- awarded_count = models.PositiveIntegerField(default=0)
-
- awarded_to = models.ManyToManyField(User, through='Award', related_name='badges')
-
- class Meta:
- app_label = 'forum'
- db_table = u'badge'
- ordering = ('name',)
- unique_together = ('name', 'type')
-
- def __unicode__(self):
- return u'%s: %s' % (self.get_type_display(), self.name)
-
- def save(self, **kwargs):
- if not self.slug:
- self.slug = self.name#slugify(self.name)
- super(Badge, self).save(**kwargs)
-
- def get_absolute_url(self):
- return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
-
-class AwardManager(models.Manager):
- def get_recent_awards(self):
- awards = super(AwardManager, self).extra(
- select={'badge_id': 'badge.id', 'badge_name':'badge.name',
- 'badge_description': 'badge.description', 'badge_type': 'badge.type',
- 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
- },
- tables=['award', 'badge', 'auth_user'],
- order_by=['-awarded_at'],
- where=['auth_user.id=award.user_id AND badge_id=badge.id'],
- ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
- return awards
-
-class Award(models.Model):
- """The awarding of a Badge to a User."""
- user = models.ForeignKey(User, related_name='award_user')
- badge = models.ForeignKey('Badge', related_name='award_badge')
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- awarded_at = models.DateTimeField(default=datetime.datetime.now)
- notified = models.BooleanField(default=False)
-
- objects = AwardManager()
-
- def __unicode__(self):
- return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at)
-
- class Meta:
- app_label = 'forum'
- db_table = u'award'
-
-class ReputeManager(models.Manager):
- def get_reputation_by_upvoted_today(self, user):
- """
- For one user in one day, he can only earn rep till certain score (ep. +200)
- by upvoted(also substracted from upvoted canceled). This is because we need
- to prohibit gaming system by upvoting/cancel again and again.
- """
- if user is not None:
- today = datetime.date.today()
- sums = self.filter(models.Q(reputation_type=1) | models.Q(reputation_type=-8),
- user=user, reputed_at__range=(today, today + datetime.timedelta(1))). \
- agregate(models.Sum('positive'), models.Sum('negative'))
-
- return sums['positive__sum'] + sums['negative__sum']
- else:
- return 0
-
-class Repute(models.Model):
- """The reputation histories for user"""
- user = models.ForeignKey(User)
- positive = models.SmallIntegerField(default=0)
- negative = models.SmallIntegerField(default=0)
- question = models.ForeignKey('Question')
- reputed_at = models.DateTimeField(default=datetime.datetime.now)
- reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION)
- reputation = models.IntegerField(default=1)
-
- objects = ReputeManager()
-
- def __unicode__(self):
- return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at)
-
- class Meta:
- app_label = 'forum'
- db_table = u'repute'
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class Badge(models.Model):
+ """Awarded for notable actions performed on the site by Users."""
+ GOLD = 1
+ SILVER = 2
+ BRONZE = 3
+ TYPE_CHOICES = (
+ (GOLD, _('gold')),
+ (SILVER, _('silver')),
+ (BRONZE, _('bronze')),
+ )
+
+ name = models.CharField(max_length=50)
+ type = models.SmallIntegerField(choices=TYPE_CHOICES)
+ slug = models.SlugField(max_length=50, blank=True)
+ description = models.CharField(max_length=300)
+ multiple = models.BooleanField(default=False)
+ # Denormalised data
+ awarded_count = models.PositiveIntegerField(default=0)
+
+ awarded_to = models.ManyToManyField(User, through='Award', related_name='badges')
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'badge'
+ ordering = ('name',)
+ unique_together = ('name', 'type')
+
+ def __unicode__(self):
+ return u'%s: %s' % (self.get_type_display(), self.name)
+
+ def save(self, **kwargs):
+ if not self.slug:
+ self.slug = self.name#slugify(self.name)
+ super(Badge, self).save(**kwargs)
+
+ def get_absolute_url(self):
+ return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
+
+class AwardManager(models.Manager):
+ def get_recent_awards(self):
+ awards = super(AwardManager, self).extra(
+ select={'badge_id': 'badge.id', 'badge_name':'badge.name',
+ 'badge_description': 'badge.description', 'badge_type': 'badge.type',
+ 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
+ },
+ tables=['award', 'badge', 'auth_user'],
+ order_by=['-awarded_at'],
+ where=['auth_user.id=award.user_id AND badge_id=badge.id'],
+ ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
+ return awards
+
+class Award(models.Model):
+ """The awarding of a Badge to a User."""
+ user = models.ForeignKey(User, related_name='award_user')
+ badge = models.ForeignKey('Badge', related_name='award_badge')
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ awarded_at = models.DateTimeField(default=datetime.datetime.now)
+ notified = models.BooleanField(default=False)
+
+ objects = AwardManager()
+
+ def __unicode__(self):
+ return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'award'
+
+class ReputeManager(models.Manager):
+ def get_reputation_by_upvoted_today(self, user):
+ """
+ For one user in one day, he can only earn rep till certain score (ep. +200)
+ by upvoted(also substracted from upvoted canceled). This is because we need
+ to prohibit gaming system by upvoting/cancel again and again.
+ """
+ if user is not None:
+ today = datetime.date.today()
+ sums = self.filter(models.Q(reputation_type=1) | models.Q(reputation_type=-8),
+ user=user, reputed_at__range=(today, today + datetime.timedelta(1))). \
+ agregate(models.Sum('positive'), models.Sum('negative'))
+
+ return sums['positive__sum'] + sums['negative__sum']
+ else:
+ return 0
+
+class Repute(models.Model):
+ """The reputation histories for user"""
+ user = models.ForeignKey(User)
+ positive = models.SmallIntegerField(default=0)
+ negative = models.SmallIntegerField(default=0)
+ question = models.ForeignKey('Question')
+ reputed_at = models.DateTimeField(default=datetime.datetime.now)
+ reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION)
+ reputation = models.IntegerField(default=1)
+
+ objects = ReputeManager()
+
+ def __unicode__(self):
+ return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'repute'
diff --git a/forum/models/tag.py b/forum/models/tag.py
index 28b9e572..8d26d6f4 100755
--- a/forum/models/tag.py
+++ b/forum/models/tag.py
@@ -1,85 +1,85 @@
-from base import *
-
-from django.utils.translation import ugettext as _
-
-class TagManager(models.Manager):
- UPDATE_USED_COUNTS_QUERY = (
- 'UPDATE tag '
- 'SET used_count = ('
- 'SELECT COUNT(*) FROM question_tags '
- 'INNER JOIN question ON question_id=question.id '
- 'WHERE tag_id = tag.id AND question.deleted=False'
- ') '
- 'WHERE id IN (%s)')
-
- def get_valid_tags(self, page_size):
- tags = self.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
- return tags
-
- def get_or_create_multiple(self, names, user):
- """
- Fetches a list of Tags with the given names, creating any Tags
- which don't exist when necesssary.
- """
- tags = list(self.filter(name__in=names))
- #Set all these tag visible
- for tag in tags:
- if tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
- if len(tags) < len(names):
- existing_names = set(tag.name for tag in tags)
- new_names = [name for name in names if name not in existing_names]
- tags.extend([self.create(name=name, created_by=user)
- for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
-
- return tags
-
- def update_use_counts(self, tags):
- """Updates the given Tags with their current use counts."""
- if not tags:
- return
- cursor = connection.cursor()
- query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
- cursor.execute(query, [tag.id for tag in tags])
- transaction.commit_unless_managed()
-
- def get_tags_by_questions(self, questions):
- question_ids = []
- for question in questions:
- question_ids.append(question.id)
-
- question_ids_str = ','.join([str(id) for id in question_ids])
- related_tags = self.extra(
- tables=['tag', 'question_tags'],
- where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
- ).distinct()
-
- return related_tags
-
-class Tag(DeletableContent):
- name = models.CharField(max_length=255, unique=True)
- created_by = models.ForeignKey(User, related_name='created_tags')
- # Denormalised data
- used_count = models.PositiveIntegerField(default=0)
-
- objects = TagManager()
-
- class Meta(DeletableContent.Meta):
- db_table = u'tag'
- ordering = ('-used_count', 'name')
-
- def __unicode__(self):
- return self.name
-
-class MarkedTag(models.Model):
- TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
- tag = models.ForeignKey('Tag', related_name='user_selections')
- user = models.ForeignKey(User, related_name='tag_selections')
- reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
-
- class Meta:
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class TagManager(models.Manager):
+ UPDATE_USED_COUNTS_QUERY = (
+ 'UPDATE tag '
+ 'SET used_count = ('
+ 'SELECT COUNT(*) FROM question_tags '
+ 'INNER JOIN question ON question_id=question.id '
+ 'WHERE tag_id = tag.id AND question.deleted=False'
+ ') '
+ 'WHERE id IN (%s)')
+
+ def get_valid_tags(self, page_size):
+ tags = self.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
+ return tags
+
+ def get_or_create_multiple(self, names, user):
+ """
+ Fetches a list of Tags with the given names, creating any Tags
+ which don't exist when necesssary.
+ """
+ tags = list(self.filter(name__in=names))
+ #Set all these tag visible
+ for tag in tags:
+ if tag.deleted:
+ tag.deleted = False
+ tag.deleted_by = None
+ tag.deleted_at = None
+ tag.save()
+
+ if len(tags) < len(names):
+ existing_names = set(tag.name for tag in tags)
+ new_names = [name for name in names if name not in existing_names]
+ tags.extend([self.create(name=name, created_by=user)
+ for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
+
+ return tags
+
+ def update_use_counts(self, tags):
+ """Updates the given Tags with their current use counts."""
+ if not tags:
+ return
+ cursor = connection.cursor()
+ query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
+ cursor.execute(query, [tag.id for tag in tags])
+ transaction.commit_unless_managed()
+
+ def get_tags_by_questions(self, questions):
+ question_ids = []
+ for question in questions:
+ question_ids.append(question.id)
+
+ question_ids_str = ','.join([str(id) for id in question_ids])
+ related_tags = self.extra(
+ tables=['tag', 'question_tags'],
+ where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
+ ).distinct()
+
+ return related_tags
+
+class Tag(DeletableContent):
+ name = models.CharField(max_length=255, unique=True)
+ created_by = models.ForeignKey(User, related_name='created_tags')
+ # Denormalised data
+ used_count = models.PositiveIntegerField(default=0)
+
+ objects = TagManager()
+
+ class Meta(DeletableContent.Meta):
+ db_table = u'tag'
+ ordering = ('-used_count', 'name')
+
+ def __unicode__(self):
+ return self.name
+
+class MarkedTag(models.Model):
+ TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
+ tag = models.ForeignKey('Tag', related_name='user_selections')
+ user = models.ForeignKey(User, related_name='tag_selections')
+ reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
+
+ class Meta:
app_label = 'forum' \ No newline at end of file
diff --git a/forum/models/user.py b/forum/models/user.py
index 95024162..3a600e00 100755
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -1,77 +1,77 @@
-from base import *
-
-from django.utils.translation import ugettext as _
-
-class Activity(models.Model):
- """
- We keep some history data for user activities
- """
- user = models.ForeignKey(User)
- activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY)
- active_at = models.DateTimeField(default=datetime.datetime.now)
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- is_auditted = models.BooleanField(default=False)
-
- def __unicode__(self):
- return u'[%s] was active at %s' % (self.user.username, self.active_at)
-
- class Meta:
- app_label = 'forum'
- db_table = u'activity'
-
-class EmailFeedSetting(models.Model):
- DELTA_TABLE = {
- 'w':datetime.timedelta(7),
- 'd':datetime.timedelta(1),
- 'n':datetime.timedelta(-1),
- }
- FEED_TYPES = (
- ('q_all',_('Entire forum')),
- ('q_ask',_('Questions that I asked')),
- ('q_ans',_('Questions that I answered')),
- ('q_sel',_('Individually selected questions')),
- )
- UPDATE_FREQUENCY = (
- ('w',_('Weekly')),
- ('d',_('Daily')),
- ('n',_('No email')),
- )
- subscriber = models.ForeignKey(User)
- feed_type = models.CharField(max_length=16,choices=FEED_TYPES)
- frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n')
- added_at = models.DateTimeField(auto_now_add=True)
- reported_at = models.DateTimeField(null=True)
-
- def save(self,*args,**kwargs):
- type = self.feed_type
- subscriber = self.subscriber
- similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id)
- if len(similar) > 0:
- raise IntegrityError('email feed setting already exists')
- super(EmailFeedSetting,self).save(*args,**kwargs)
-
- class Meta:
- app_label = 'forum'
-
-class AnonymousEmail(models.Model):
- #validation key, if used
- key = models.CharField(max_length=32)
- email = models.EmailField(null=False,unique=True)
- isvalid = models.BooleanField(default=False)
-
- class Meta:
- app_label = 'forum'
-
-
-class AuthKeyUserAssociation(models.Model):
- key = models.CharField(max_length=256,null=False,unique=True)
- provider = models.CharField(max_length=64)
- user = models.ForeignKey(User)
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta:
- app_label = 'forum'
-
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class Activity(models.Model):
+ """
+ We keep some history data for user activities
+ """
+ user = models.ForeignKey(User)
+ activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY)
+ active_at = models.DateTimeField(default=datetime.datetime.now)
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ is_auditted = models.BooleanField(default=False)
+
+ def __unicode__(self):
+ return u'[%s] was active at %s' % (self.user.username, self.active_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'activity'
+
+class EmailFeedSetting(models.Model):
+ DELTA_TABLE = {
+ 'w':datetime.timedelta(7),
+ 'd':datetime.timedelta(1),
+ 'n':datetime.timedelta(-1),
+ }
+ FEED_TYPES = (
+ ('q_all',_('Entire forum')),
+ ('q_ask',_('Questions that I asked')),
+ ('q_ans',_('Questions that I answered')),
+ ('q_sel',_('Individually selected questions')),
+ )
+ UPDATE_FREQUENCY = (
+ ('w',_('Weekly')),
+ ('d',_('Daily')),
+ ('n',_('No email')),
+ )
+ subscriber = models.ForeignKey(User)
+ feed_type = models.CharField(max_length=16,choices=FEED_TYPES)
+ frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n')
+ added_at = models.DateTimeField(auto_now_add=True)
+ reported_at = models.DateTimeField(null=True)
+
+ def save(self,*args,**kwargs):
+ type = self.feed_type
+ subscriber = self.subscriber
+ similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id)
+ if len(similar) > 0:
+ raise IntegrityError('email feed setting already exists')
+ super(EmailFeedSetting,self).save(*args,**kwargs)
+
+ class Meta:
+ app_label = 'forum'
+
+class AnonymousEmail(models.Model):
+ #validation key, if used
+ key = models.CharField(max_length=32)
+ email = models.EmailField(null=False,unique=True)
+ isvalid = models.BooleanField(default=False)
+
+ class Meta:
+ app_label = 'forum'
+
+
+class AuthKeyUserAssociation(models.Model):
+ key = models.CharField(max_length=256,null=False,unique=True)
+ provider = models.CharField(max_length=64)
+ user = models.ForeignKey(User)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta:
+ app_label = 'forum'
+
\ No newline at end of file
diff --git a/forum/modules.py b/forum/modules.py
index 9c072330..b7d7c5ca 100755
--- a/forum/modules.py
+++ b/forum/modules.py
@@ -1,79 +1,79 @@
-import os
-import types
-import re
-
-from django.template import Template, TemplateDoesNotExist
-
-MODULES_PACKAGE = 'forum_modules'
-
-MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE)
-
-MODULE_LIST = [
- __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules'])
- for f in os.listdir(MODULES_FOLDER)
- if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and
- os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and
- not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f))
-]
-
-def get_modules_script(script_name):
- all = []
-
- for m in MODULE_LIST:
- try:
- all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__]))
- except Exception, e:
- #print script_name + ":" + str(e)
- pass
-
- return all
-
-def get_modules_script_classes(script_name, base_class):
- scripts = get_modules_script(script_name)
- all_classes = {}
-
- for script in scripts:
- all_classes.update(dict([
- (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)]
- if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class)
- ]))
-
- return all_classes
-
-def get_all_handlers(name):
- handler_files = get_modules_script('handlers')
-
- return [
- h for h in [
- getattr(f, name) for f in handler_files
- if hasattr(f, name)
- ]
-
- if callable(h)
- ]
-
-def get_handler(name, default):
- all = get_all_handlers(name)
- print(len(all))
- return len(all) and all[0] or default
-
-module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
-
-def module_templates_loader(name, dirs=None):
- result = module_template_re.search(name)
-
- if result is not None:
- file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2))
-
- if os.path.exists(file_name):
- try:
- f = open(file_name, 'r')
- source = f.read()
- f.close()
- return (source, file_name)
- except:
- pass
-
- raise TemplateDoesNotExist, name
-
+import os
+import types
+import re
+
+from django.template import Template, TemplateDoesNotExist
+
+MODULES_PACKAGE = 'forum_modules'
+
+MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE)
+
+MODULE_LIST = [
+ __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules'])
+ for f in os.listdir(MODULES_FOLDER)
+ if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and
+ os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and
+ not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f))
+]
+
+def get_modules_script(script_name):
+ all = []
+
+ for m in MODULE_LIST:
+ try:
+ all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__]))
+ except Exception, e:
+ #print script_name + ":" + str(e)
+ pass
+
+ return all
+
+def get_modules_script_classes(script_name, base_class):
+ scripts = get_modules_script(script_name)
+ all_classes = {}
+
+ for script in scripts:
+ all_classes.update(dict([
+ (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)]
+ if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class)
+ ]))
+
+ return all_classes
+
+def get_all_handlers(name):
+ handler_files = get_modules_script('handlers')
+
+ return [
+ h for h in [
+ getattr(f, name) for f in handler_files
+ if hasattr(f, name)
+ ]
+
+ if callable(h)
+ ]
+
+def get_handler(name, default):
+ all = get_all_handlers(name)
+ print(len(all))
+ return len(all) and all[0] or default
+
+module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
+
+def module_templates_loader(name, dirs=None):
+ result = module_template_re.search(name)
+
+ if result is not None:
+ file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2))
+
+ if os.path.exists(file_name):
+ try:
+ f = open(file_name, 'r')
+ source = f.read()
+ f.close()
+ return (source, file_name)
+ except:
+ pass
+
+ raise TemplateDoesNotExist, name
+
module_templates_loader.is_usable = True \ No newline at end of file
diff --git a/forum/skins/default/media/style/auth.css b/forum/skins/default/media/style/auth.css
index 64ec6f3e..33702758 100755
--- a/forum/skins/default/media/style/auth.css
+++ b/forum/skins/default/media/style/auth.css
@@ -1,48 +1,48 @@
-#bigicon_providers, #smallicon_providers {
- display: block;
- padding: 0px;
- width:600px;
- margin:0px 0px 5px 0px;
-}
-
-.provider_logo {
- display: inline-block;
- padding: 4px;
- border: 1px solid #DDD;
- text-align: center;
- vertical-align: middle;
-}
-
-.provider_logo.big {
- height: 40px;
- width: 90px;
-}
-
-.provider_logo.small {
- height: 32px;
- width: 32px;
-}
-
-.provider_logo.selected {
- outline: 2px solid #FFF8C6;
-}
-
-.provider_logo .provider_url {
- display: none;
-}
-
-.signin_form input[type="text"], .signin_form input[type="password"], .signin_form input[type="submit"] {
- height: 28px;
- line-height: 22px;
- font-size: 140%;
- border: 1px solid #999;
-}
-
-.signin_form .icon_input {
- padding-left: 20px;
-}
-
-.or_label {
- margin-top: 20px;
- margin-bottom: 10px;
+#bigicon_providers, #smallicon_providers {
+ display: block;
+ padding: 0px;
+ width:600px;
+ margin:0px 0px 5px 0px;
+}
+
+.provider_logo {
+ display: inline-block;
+ padding: 4px;
+ border: 1px solid #DDD;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.provider_logo.big {
+ height: 40px;
+ width: 90px;
+}
+
+.provider_logo.small {
+ height: 32px;
+ width: 32px;
+}
+
+.provider_logo.selected {
+ outline: 2px solid #FFF8C6;
+}
+
+.provider_logo .provider_url {
+ display: none;
+}
+
+.signin_form input[type="text"], .signin_form input[type="password"], .signin_form input[type="submit"] {
+ height: 28px;
+ line-height: 22px;
+ font-size: 140%;
+ border: 1px solid #999;
+}
+
+.signin_form .icon_input {
+ padding-left: 20px;
+}
+
+.or_label {
+ margin-top: 20px;
+ margin-bottom: 10px;
} \ No newline at end of file
diff --git a/forum/views/auth.py b/forum/views/auth.py
index 2e953163..cf27f1bb 100755
--- a/forum/views/auth.py
+++ b/forum/views/auth.py
@@ -1,212 +1,212 @@
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.http import HttpResponseRedirect
-from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext as _
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth import login, logout
-from django.http import get_host
-import types
-
-from forum.models import AuthKeyUserAssociation
-from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm
-
-from forum.authentication.base import InvalidAuthentication
-from forum.authentication import AUTH_PROVIDERS
-
-from forum.models import Question, Answer
-
-def signin_page(request, action=None):
- if action is None:
- request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
- else:
- request.session['on_signin_action'] = action
-
- all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
-
- sort = lambda c1, c2: c1.weight - c2.weight
- can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
-
- bigicon_providers = sorted([
- context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
- ], sort)
-
- smallicon_providers = sorted([
- context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
- ], sort)
-
- stackitem_providers = sorted([
- context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
- ], sort)
-
- try:
- msg = request.session['auth_error']
- del request.session['auth_error']
- except:
- msg = None
-
- return render_to_response(
- 'auth/signin.html',
- {
- 'msg': msg,
- 'all_providers': all_providers,
- 'bigicon_providers': bigicon_providers,
- 'stackitem_providers': stackitem_providers,
- 'smallicon_providers': smallicon_providers,
- },
- RequestContext(request))
-
-def prepare_provider_signin(request, provider):
- force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
- request.session['force_email_request'] = force_email_request
-
- if provider in AUTH_PROVIDERS:
- provider_class = AUTH_PROVIDERS[provider].consumer
-
- try:
- request_url = provider_class.prepare_authentication_request(request,
- reverse('auth_provider_done', kwargs={'provider': provider}))
-
- return HttpResponseRedirect(request_url)
- except NotImplementedError, e:
- return process_provider_signin(request, provider)
- except InvalidAuthentication, e:
- request.session['auth_error'] = e.message
-
- return HttpResponseRedirect(reverse('auth_signin'))
-
-
-def process_provider_signin(request, provider):
- if provider in AUTH_PROVIDERS:
- provider_class = AUTH_PROVIDERS[provider].consumer
-
- try:
- assoc_key = provider_class.process_authentication_request(request)
- except InvalidAuthentication, e:
- request.session['auth_error'] = e.message
- return HttpResponseRedirect(reverse('auth_signin'))
-
- if request.user.is_authenticated():
- if isinstance(assoc_key, (type, User)):
- if request.user != assoc_key:
- request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
- else:
- request.session['auth_error'] = _("You are already logged in with that user.")
- else:
- try:
- assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
- if assoc.user == request.user:
- request.session['auth_error'] = _("These login credentials are already associated with your account.")
- else:
- request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
- except:
- uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
- uassoc.save()
- request.session['auth_error'] = _("These new credentials are now associated with your account.")
- return HttpResponseRedirect(reverse('auth_signin'))
-
- try:
- assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
- user_ = assoc.user
- return login_and_forward(request, user_)
- except:
- request.session['assoc_key'] = assoc_key
- request.session['auth_provider'] = provider
- return HttpResponseRedirect(reverse('auth_external_register'))
-
- return HttpResponseRedirect(reverse('auth_signin'))
-
-def external_register(request):
- if request.method == 'POST' and 'bnewaccount' in request.POST:
- form1 = SimpleRegistrationForm(request.POST)
- email_feeds_form = SimpleEmailSubscribeForm(request.POST)
-
- if (form1.is_valid() and email_feeds_form.is_valid()):
- tmp_pwd = User.objects.make_random_password()
- user_ = User.objects.create_user(form1.cleaned_data['username'],
- form1.cleaned_data['email'], tmp_pwd)
-
- user_.set_unusable_password()
-
- uassoc = AuthKeyUserAssociation(user=user_, key=request.session['assoc_key'], provider=request.session['auth_provider'])
- uassoc.save()
-
- email_feeds_form.save(user_)
-
- del request.session['assoc_key']
- del request.session['auth_provider']
- return login_and_forward(request, user_)
- else:
- provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer
- user_data = provider_class.get_user_data(request.session['assoc_key'])
-
- username = user_data.get('username', '')
- email = user_data.get('email', '')
-
- if not email:
- email = request.session.get('auth_email_request', '')
-
- form1 = SimpleRegistrationForm(initial={
- 'next': '/',
- 'username': username,
- 'email': email,
- })
- email_feeds_form = SimpleEmailSubscribeForm()
-
- provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
-
- return render_to_response('auth/complete.html', {
- 'form1': form1,
- 'email_feeds_form': email_feeds_form,
- 'provider':mark_safe(provider_context.human_name),
- 'login_type':provider_context.id,
- 'gravatar_faq_url':reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
-
-def newquestion_signin_action(user):
- question = Question.objects.filter(author=user).order_by('-added_at')[0]
- return question.get_absolute_url()
-
-def newanswer_signin_action(user):
- answer = Answer.objects.filter(author=user).order_by('-added_at')[0]
- return answer.get_absolute_url()
-
-POST_SIGNIN_ACTIONS = {
- 'newquestion': newquestion_signin_action,
- 'newanswer': newanswer_signin_action,
-}
-
-def login_and_forward(request, user):
- old_session = request.session.session_key
- user.backend = "django.contrib.auth.backends.ModelBackend"
- login(request, user)
-
- from forum.models import user_logged_in
- user_logged_in.send(user=user,session_key=old_session,sender=None)
-
- redirect = request.session.get('on_signin_url', None)
-
- if not redirect:
- signin_action = request.session.get('on_signin_action', None)
- if not signin_action:
- redirect = reverse('index')
- else:
- try:
- redirect = POST_SIGNIN_ACTIONS[signin_action](user)
- except:
- redirect = reverse('index')
-
- return HttpResponseRedirect(redirect)
-
-@login_required
-def signout(request):
- """
- signout from the website. Remove openid from session and kill it.
-
- url : /signout/"
- """
-
- logout(request)
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.http import HttpResponseRedirect
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth import login, logout
+from django.http import get_host
+import types
+
+from forum.models import AuthKeyUserAssociation
+from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm
+
+from forum.authentication.base import InvalidAuthentication
+from forum.authentication import AUTH_PROVIDERS
+
+from forum.models import Question, Answer
+
+def signin_page(request, action=None):
+ if action is None:
+ request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
+ else:
+ request.session['on_signin_action'] = action
+
+ all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
+
+ sort = lambda c1, c2: c1.weight - c2.weight
+ can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
+
+ bigicon_providers = sorted([
+ context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
+ ], sort)
+
+ smallicon_providers = sorted([
+ context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
+ ], sort)
+
+ stackitem_providers = sorted([
+ context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
+ ], sort)
+
+ try:
+ msg = request.session['auth_error']
+ del request.session['auth_error']
+ except:
+ msg = None
+
+ return render_to_response(
+ 'auth/signin.html',
+ {
+ 'msg': msg,
+ 'all_providers': all_providers,
+ 'bigicon_providers': bigicon_providers,
+ 'stackitem_providers': stackitem_providers,
+ 'smallicon_providers': smallicon_providers,
+ },
+ RequestContext(request))
+
+def prepare_provider_signin(request, provider):
+ force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
+ request.session['force_email_request'] = force_email_request
+
+ if provider in AUTH_PROVIDERS:
+ provider_class = AUTH_PROVIDERS[provider].consumer
+
+ try:
+ request_url = provider_class.prepare_authentication_request(request,
+ reverse('auth_provider_done', kwargs={'provider': provider}))
+
+ return HttpResponseRedirect(request_url)
+ except NotImplementedError, e:
+ return process_provider_signin(request, provider)
+ except InvalidAuthentication, e:
+ request.session['auth_error'] = e.message
+
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+
+def process_provider_signin(request, provider):
+ if provider in AUTH_PROVIDERS:
+ provider_class = AUTH_PROVIDERS[provider].consumer
+
+ try:
+ assoc_key = provider_class.process_authentication_request(request)
+ except InvalidAuthentication, e:
+ request.session['auth_error'] = e.message
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+ if request.user.is_authenticated():
+ if isinstance(assoc_key, (type, User)):
+ if request.user != assoc_key:
+ request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
+ else:
+ request.session['auth_error'] = _("You are already logged in with that user.")
+ else:
+ try:
+ assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
+ if assoc.user == request.user:
+ request.session['auth_error'] = _("These login credentials are already associated with your account.")
+ else:
+ request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
+ except:
+ uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
+ uassoc.save()
+ request.session['auth_error'] = _("These new credentials are now associated with your account.")
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+ try:
+ assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
+ user_ = assoc.user
+ return login_and_forward(request, user_)
+ except:
+ request.session['assoc_key'] = assoc_key
+ request.session['auth_provider'] = provider
+ return HttpResponseRedirect(reverse('auth_external_register'))
+
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+def external_register(request):
+ if request.method == 'POST' and 'bnewaccount' in request.POST:
+ form1 = SimpleRegistrationForm(request.POST)
+ email_feeds_form = SimpleEmailSubscribeForm(request.POST)
+
+ if (form1.is_valid() and email_feeds_form.is_valid()):
+ tmp_pwd = User.objects.make_random_password()
+ user_ = User.objects.create_user(form1.cleaned_data['username'],
+ form1.cleaned_data['email'], tmp_pwd)
+
+ user_.set_unusable_password()
+
+ uassoc = AuthKeyUserAssociation(user=user_, key=request.session['assoc_key'], provider=request.session['auth_provider'])
+ uassoc.save()
+
+ email_feeds_form.save(user_)
+
+ del request.session['assoc_key']
+ del request.session['auth_provider']
+ return login_and_forward(request, user_)
+ else:
+ provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer
+ user_data = provider_class.get_user_data(request.session['assoc_key'])
+
+ username = user_data.get('username', '')
+ email = user_data.get('email', '')
+
+ if not email:
+ email = request.session.get('auth_email_request', '')
+
+ form1 = SimpleRegistrationForm(initial={
+ 'next': '/',
+ 'username': username,
+ 'email': email,
+ })
+ email_feeds_form = SimpleEmailSubscribeForm()
+
+ provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
+
+ return render_to_response('auth/complete.html', {
+ 'form1': form1,
+ 'email_feeds_form': email_feeds_form,
+ 'provider':mark_safe(provider_context.human_name),
+ 'login_type':provider_context.id,
+ 'gravatar_faq_url':reverse('faq') + '#gravatar',
+ }, context_instance=RequestContext(request))
+
+def newquestion_signin_action(user):
+ question = Question.objects.filter(author=user).order_by('-added_at')[0]
+ return question.get_absolute_url()
+
+def newanswer_signin_action(user):
+ answer = Answer.objects.filter(author=user).order_by('-added_at')[0]
+ return answer.get_absolute_url()
+
+POST_SIGNIN_ACTIONS = {
+ 'newquestion': newquestion_signin_action,
+ 'newanswer': newanswer_signin_action,
+}
+
+def login_and_forward(request, user):
+ old_session = request.session.session_key
+ user.backend = "django.contrib.auth.backends.ModelBackend"
+ login(request, user)
+
+ from forum.models import user_logged_in
+ user_logged_in.send(user=user,session_key=old_session,sender=None)
+
+ redirect = request.session.get('on_signin_url', None)
+
+ if not redirect:
+ signin_action = request.session.get('on_signin_action', None)
+ if not signin_action:
+ redirect = reverse('index')
+ else:
+ try:
+ redirect = POST_SIGNIN_ACTIONS[signin_action](user)
+ except:
+ redirect = reverse('index')
+
+ return HttpResponseRedirect(redirect)
+
+@login_required
+def signout(request):
+ """
+ signout from the website. Remove openid from session and kill it.
+
+ url : /signout/"
+ """
+
+ logout(request)
return HttpResponseRedirect(reverse('index')) \ No newline at end of file
diff --git a/forum_modules/books/__init__.py b/forum_modules/books/__init__.py
index bdd27b2b..a182c87c 100755
--- a/forum_modules/books/__init__.py
+++ b/forum_modules/books/__init__.py
@@ -1,3 +1,3 @@
-NAME = 'Osqa Books'
-DESCRIPTION = "Allows discussion around books."
-CAN_ENABLE = True
+NAME = 'Osqa Books'
+DESCRIPTION = "Allows discussion around books."
+CAN_ENABLE = True
diff --git a/forum_modules/books/models.py b/forum_modules/books/models.py
index ecde34b3..a78c0e76 100755
--- a/forum_modules/books/models.py
+++ b/forum_modules/books/models.py
@@ -1,63 +1,63 @@
-from django.db import models
-from django.contrib.auth.models import User
-from forum.models import Question
-from django.core.urlresolvers import reverse
-from django.utils.http import urlquote as django_urlquote
-from django.template.defaultfilters import slugify
-
-class Book(models.Model):
- """
- Model for book info
- """
- user = models.ForeignKey(User)
- title = models.CharField(max_length=255)
- short_name = models.CharField(max_length=255)
- author = models.CharField(max_length=255)
- price = models.DecimalField(max_digits=6, decimal_places=2)
- pages = models.SmallIntegerField()
- published_at = models.DateTimeField()
- publication = models.CharField(max_length=255)
- cover_img = models.CharField(max_length=255)
- tagnames = models.CharField(max_length=125)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
- questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
-
- def get_absolute_url(self):
- return reverse('book', args=[django_urlquote(slugify(self.short_name))])
-
- def __unicode__(self):
- return self.title
-
- class Meta:
- app_label = 'forum'
- db_table = u'book'
-
-class BookAuthorInfo(models.Model):
- """
- Model for book author info
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- blog_url = models.CharField(max_length=255)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
-
- class Meta:
- app_label = 'forum'
- db_table = u'book_author_info'
-
-class BookAuthorRss(models.Model):
- """
- Model for book author blog rss
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- title = models.CharField(max_length=255)
- url = models.CharField(max_length=255)
- rss_created_at = models.DateTimeField()
- added_at = models.DateTimeField()
-
- class Meta:
- app_label = 'forum'
+from django.db import models
+from django.contrib.auth.models import User
+from forum.models import Question
+from django.core.urlresolvers import reverse
+from django.utils.http import urlquote as django_urlquote
+from django.template.defaultfilters import slugify
+
+class Book(models.Model):
+ """
+ Model for book info
+ """
+ user = models.ForeignKey(User)
+ title = models.CharField(max_length=255)
+ short_name = models.CharField(max_length=255)
+ author = models.CharField(max_length=255)
+ price = models.DecimalField(max_digits=6, decimal_places=2)
+ pages = models.SmallIntegerField()
+ published_at = models.DateTimeField()
+ publication = models.CharField(max_length=255)
+ cover_img = models.CharField(max_length=255)
+ tagnames = models.CharField(max_length=125)
+ added_at = models.DateTimeField()
+ last_edited_at = models.DateTimeField()
+ questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
+
+ def get_absolute_url(self):
+ return reverse('book', args=[django_urlquote(slugify(self.short_name))])
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'book'
+
+class BookAuthorInfo(models.Model):
+ """
+ Model for book author info
+ """
+ user = models.ForeignKey(User)
+ book = models.ForeignKey(Book)
+ blog_url = models.CharField(max_length=255)
+ added_at = models.DateTimeField()
+ last_edited_at = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'book_author_info'
+
+class BookAuthorRss(models.Model):
+ """
+ Model for book author blog rss
+ """
+ user = models.ForeignKey(User)
+ book = models.ForeignKey(Book)
+ title = models.CharField(max_length=255)
+ url = models.CharField(max_length=255)
+ rss_created_at = models.DateTimeField()
+ added_at = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
db_table = u'book_author_rss' \ No newline at end of file
diff --git a/forum_modules/books/urls.py b/forum_modules/books/urls.py
index 0e0432ba..bc0811e7 100755
--- a/forum_modules/books/urls.py
+++ b/forum_modules/books/urls.py
@@ -1,10 +1,10 @@
-from django.conf.urls.defaults import *
-from django.utils.translation import ugettext as _
-
-import views as app
-
-urlpatterns = patterns('',
- url(r'^%s$' % _('books/'), app.books, name='books'),
- url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'),
- url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
+from django.conf.urls.defaults import *
+from django.utils.translation import ugettext as _
+
+import views as app
+
+urlpatterns = patterns('',
+ url(r'^%s$' % _('books/'), app.books, name='books'),
+ url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'),
+ url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
) \ No newline at end of file
diff --git a/forum_modules/books/views.py b/forum_modules/books/views.py
index 31c82971..35e9f0fe 100755
--- a/forum_modules/books/views.py
+++ b/forum_modules/books/views.py
@@ -1,142 +1,142 @@
-from django.shortcuts import render_to_response, get_object_or_404
-from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
-from django.template import RequestContext
-from django.contrib.auth.decorators import login_required
-from django.core.urlresolvers import reverse
-from django.utils.html import *
-
-from models import *
-
-from forum.forms import AskForm
-from forum.views.readers import _get_tags_cache_json
-from forum.models import *
-from forum.utils.html import sanitize_html
-
-def books(request):
- return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang')
-
-def book(request, short_name, unanswered=False):
- """
- 1. questions list
- 2. book info
- 3. author info and blog rss items
- """
- """
- List of Questions, Tagged questions, and Unanswered questions.
- """
- books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
- match_count = len(books)
- if match_count == 0:
- raise Http404
- else:
- # the book info
- book = books[0]
- # get author info
- author_info = BookAuthorInfo.objects.get(book=book)
- # get author rss info
- author_rss = BookAuthorRss.objects.filter(book=book)
-
- # get pagesize from session, if failed then get default value
- user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
- # set pagesize equal to logon user specified value in database
- if request.user.is_authenticated() and request.user.questions_per_page > 0:
- user_page_size = request.user.questions_per_page
-
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
- try:
- orderby = view_dic[view_id]
- except KeyError:
- view_id = "latest"
- orderby = "-added_at"
-
- # check if request is from tagged questions
- if unanswered:
- # check if request is from unanswered questions
- # Article.objects.filter(publications__id__exact=1)
- objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby)
- else:
- objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby)
-
- # RISK - inner join queries
- objects = objects.select_related();
- objects_list = Paginator(objects, user_page_size)
- questions = objects_list.page(page)
-
- return render_to_response('book.html', {
- "book" : book,
- "author_info" : author_info,
- "author_rss" : author_rss,
- "questions" : questions,
- "context" : {
- 'is_paginated' : True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url' : request.path + '?sort=%s&' % view_id,
- 'pagesize' : user_page_size
- }
- }, context_instance=RequestContext(request))
-
-@login_required
-def ask_book(request, short_name):
- if request.method == "POST":
- form = AskForm(request.POST)
- if form.is_valid():
- added_at = datetime.datetime.now()
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- question = Question(
- title = strip_tags(form.cleaned_data['title']),
- author = request.user,
- added_at = added_at,
- last_activity_at = added_at,
- last_activity_by = request.user,
- wiki = form.cleaned_data['wiki'],
- tagnames = form.cleaned_data['tags'].strip(),
- html = html,
- summary = strip_tags(html)[:120]
- )
- if question.wiki:
- question.last_edited_by = question.author
- question.last_edited_at = added_at
- question.wikified_at = added_at
-
- question.save()
-
- # create the first revision
- QuestionRevision.objects.create(
- question = question,
- revision = 1,
- title = question.title,
- author = request.user,
- revised_at = added_at,
- tagnames = question.tagnames,
- summary = CONST['default_version'],
- text = form.cleaned_data['text']
- )
-
- books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
- match_count = len(books)
- if match_count == 1:
- # the book info
- book = books[0]
- book.questions.add(question)
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = AskForm()
-
- tags = _get_tags_cache_json()
- return render_to_response('ask.html', {
- 'form' : form,
- 'tags' : tags,
- 'email_validation_faq_url': reverse('faq') + '#validate',
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
+from django.template import RequestContext
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
+from django.utils.html import *
+
+from models import *
+
+from forum.forms import AskForm
+from forum.views.readers import _get_tags_cache_json
+from forum.models import *
+from forum.utils.html import sanitize_html
+
+def books(request):
+ return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang')
+
+def book(request, short_name, unanswered=False):
+ """
+ 1. questions list
+ 2. book info
+ 3. author info and blog rss items
+ """
+ """
+ List of Questions, Tagged questions, and Unanswered questions.
+ """
+ books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
+ match_count = len(books)
+ if match_count == 0:
+ raise Http404
+ else:
+ # the book info
+ book = books[0]
+ # get author info
+ author_info = BookAuthorInfo.objects.get(book=book)
+ # get author rss info
+ author_rss = BookAuthorRss.objects.filter(book=book)
+
+ # get pagesize from session, if failed then get default value
+ user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
+ # set pagesize equal to logon user specified value in database
+ if request.user.is_authenticated() and request.user.questions_per_page > 0:
+ user_page_size = request.user.questions_per_page
+
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ view_id = request.GET.get('sort', None)
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
+ try:
+ orderby = view_dic[view_id]
+ except KeyError:
+ view_id = "latest"
+ orderby = "-added_at"
+
+ # check if request is from tagged questions
+ if unanswered:
+ # check if request is from unanswered questions
+ # Article.objects.filter(publications__id__exact=1)
+ objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby)
+ else:
+ objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby)
+
+ # RISK - inner join queries
+ objects = objects.select_related();
+ objects_list = Paginator(objects, user_page_size)
+ questions = objects_list.page(page)
+
+ return render_to_response('book.html', {
+ "book" : book,
+ "author_info" : author_info,
+ "author_rss" : author_rss,
+ "questions" : questions,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : user_page_size
+ }
+ }, context_instance=RequestContext(request))
+
+@login_required
+def ask_book(request, short_name):
+ if request.method == "POST":
+ form = AskForm(request.POST)
+ if form.is_valid():
+ added_at = datetime.datetime.now()
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ question = Question(
+ title = strip_tags(form.cleaned_data['title']),
+ author = request.user,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = request.user,
+ wiki = form.cleaned_data['wiki'],
+ tagnames = form.cleaned_data['tags'].strip(),
+ html = html,
+ summary = strip_tags(html)[:120]
+ )
+ if question.wiki:
+ question.last_edited_by = question.author
+ question.last_edited_at = added_at
+ question.wikified_at = added_at
+
+ question.save()
+
+ # create the first revision
+ QuestionRevision.objects.create(
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = request.user,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = form.cleaned_data['text']
+ )
+
+ books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
+ match_count = len(books)
+ if match_count == 1:
+ # the book info
+ book = books[0]
+ book.questions.add(question)
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = AskForm()
+
+ tags = _get_tags_cache_json()
+ return render_to_response('ask.html', {
+ 'form' : form,
+ 'tags' : tags,
+ 'email_validation_faq_url': reverse('faq') + '#validate',
}, context_instance=RequestContext(request)) \ No newline at end of file
diff --git a/forum_modules/facebookauth/authentication.py b/forum_modules/facebookauth/authentication.py
index f2c5b6ba..512367a3 100755
--- a/forum_modules/facebookauth/authentication.py
+++ b/forum_modules/facebookauth/authentication.py
@@ -1,85 +1,85 @@
-import hashlib
-from time import time
-from datetime import datetime
-from urllib import urlopen, urlencode
-from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
-from django.utils.translation import ugettext as _
-
-import settings
-
-try:
- from json import load as load_json
-except:
- from django.utils.simplejson import JSONDecoder
-
- def load_json(json):
- decoder = JSONDecoder()
- return decoder.decode(json.read())
-
-class FacebookAuthConsumer(AuthenticationConsumer):
-
- def process_authentication_request(self, request):
- API_KEY = settings.FB_API_KEY
-
- if API_KEY in request.COOKIES:
- if self.check_cookies_signature(request.COOKIES):
- if self.check_session_expiry(request.COOKIES):
- return request.COOKIES[API_KEY + '_user']
- else:
- raise InvalidAuthentication(_('Sorry, your Facebook session has expired, please try again'))
- else:
- raise InvalidAuthentication(_('The authentication with Facebook connect failed due to an invalid signature'))
- else:
- raise InvalidAuthentication(_('The authentication with Facebook connect failed, cannot find authentication tokens'))
-
- def generate_signature(self, values):
- keys = []
-
- for key in sorted(values.keys()):
- keys.append(key)
-
- signature = ''.join(['%s=%s' % (key, values[key]) for key in keys]) + settings.FB_APP_SECRET
- return hashlib.md5(signature).hexdigest()
-
- def check_session_expiry(self, cookies):
- return datetime.fromtimestamp(float(cookies[settings.FB_API_KEY+'_expires'])) > datetime.now()
-
- def check_cookies_signature(self, cookies):
- API_KEY = settings.FB_API_KEY
-
- values = {}
-
- for key in cookies.keys():
- if (key.startswith(API_KEY + '_')):
- values[key.replace(API_KEY + '_', '')] = cookies[key]
-
- return self.generate_signature(values) == cookies[API_KEY]
-
- def get_user_data(self, key):
- request_data = {
- 'method': 'Users.getInfo',
- 'api_key': settings.FB_API_KEY,
- 'call_id': time(),
- 'v': '1.0',
- 'uids': key,
- 'fields': 'name,first_name,last_name,email',
- 'format': 'json',
- }
-
- request_data['sig'] = self.generate_signature(request_data)
- fb_response = load_json(urlopen(settings.REST_SERVER, urlencode(request_data)))[0]
-
- return {
- 'username': fb_response['first_name'] + ' ' + fb_response['last_name'],
- 'email': fb_response['email']
- }
-
-class FacebookAuthContext(ConsumerTemplateContext):
- mode = 'BIGICON'
- type = 'CUSTOM'
- weight = 100
- human_name = 'Facebook'
- code_template = 'modules/facebookauth/button.html'
- extra_css = ["http://www.facebook.com/css/connect/connect_button.css"]
-
+import hashlib
+from time import time
+from datetime import datetime
+from urllib import urlopen, urlencode
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
+from django.utils.translation import ugettext as _
+
+import settings
+
+try:
+ from json import load as load_json
+except:
+ from django.utils.simplejson import JSONDecoder
+
+ def load_json(json):
+ decoder = JSONDecoder()
+ return decoder.decode(json.read())
+
+class FacebookAuthConsumer(AuthenticationConsumer):
+
+ def process_authentication_request(self, request):
+ API_KEY = settings.FB_API_KEY
+
+ if API_KEY in request.COOKIES:
+ if self.check_cookies_signature(request.COOKIES):
+ if self.check_session_expiry(request.COOKIES):
+ return request.COOKIES[API_KEY + '_user']
+ else:
+ raise InvalidAuthentication(_('Sorry, your Facebook session has expired, please try again'))
+ else:
+ raise InvalidAuthentication(_('The authentication with Facebook connect failed due to an invalid signature'))
+ else:
+ raise InvalidAuthentication(_('The authentication with Facebook connect failed, cannot find authentication tokens'))
+
+ def generate_signature(self, values):
+ keys = []
+
+ for key in sorted(values.keys()):
+ keys.append(key)
+
+ signature = ''.join(['%s=%s' % (key, values[key]) for key in keys]) + settings.FB_APP_SECRET
+ return hashlib.md5(signature).hexdigest()
+
+ def check_session_expiry(self, cookies):
+ return datetime.fromtimestamp(float(cookies[settings.FB_API_KEY+'_expires'])) > datetime.now()
+
+ def check_cookies_signature(self, cookies):
+ API_KEY = settings.FB_API_KEY
+
+ values = {}
+
+ for key in cookies.keys():
+ if (key.startswith(API_KEY + '_')):
+ values[key.replace(API_KEY + '_', '')] = cookies[key]
+
+ return self.generate_signature(values) == cookies[API_KEY]
+
+ def get_user_data(self, key):
+ request_data = {
+ 'method': 'Users.getInfo',
+ 'api_key': settings.FB_API_KEY,
+ 'call_id': time(),
+ 'v': '1.0',
+ 'uids': key,
+ 'fields': 'name,first_name,last_name,email',
+ 'format': 'json',
+ }
+
+ request_data['sig'] = self.generate_signature(request_data)
+ fb_response = load_json(urlopen(settings.REST_SERVER, urlencode(request_data)))[0]
+
+ return {
+ 'username': fb_response['first_name'] + ' ' + fb_response['last_name'],
+ 'email': fb_response['email']
+ }
+
+class FacebookAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'CUSTOM'
+ weight = 100
+ human_name = 'Facebook'
+ code_template = 'modules/facebookauth/button.html'
+ extra_css = ["http://www.facebook.com/css/connect/connect_button.css"]
+
API_KEY = settings.FB_API_KEY \ No newline at end of file
diff --git a/forum_modules/facebookauth/settings.py b/forum_modules/facebookauth/settings.py
index b9a01013..67bf80c1 100755
--- a/forum_modules/facebookauth/settings.py
+++ b/forum_modules/facebookauth/settings.py
@@ -1,3 +1,3 @@
-REST_SERVER = 'http://api.facebook.com/restserver.php'
-FB_API_KEY = 'f773fab7be12aea689948208f37ad336'
+REST_SERVER = 'http://api.facebook.com/restserver.php'
+FB_API_KEY = 'f773fab7be12aea689948208f37ad336'
FB_APP_SECRET = '894547c1b8db54d77f919b1695ae879c' \ No newline at end of file
diff --git a/forum_modules/facebookauth/urls.py b/forum_modules/facebookauth/urls.py
index de1715c5..cbe3b6c7 100755
--- a/forum_modules/facebookauth/urls.py
+++ b/forum_modules/facebookauth/urls.py
@@ -1,9 +1,9 @@
-from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-
-from views import user_is_registered
-
-urlpatterns = patterns('',
- url(r'^xd_receiver.htm$', direct_to_template, {'template': 'modules/facebookauth/xd_receiver.html'}, name='xd_receiver'),
- url(r'^facebook/user_is_registered/', user_is_registered, name="facebook_user_is_registered"),
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+from views import user_is_registered
+
+urlpatterns = patterns('',
+ url(r'^xd_receiver.htm$', direct_to_template, {'template': 'modules/facebookauth/xd_receiver.html'}, name='xd_receiver'),
+ url(r'^facebook/user_is_registered/', user_is_registered, name="facebook_user_is_registered"),
) \ No newline at end of file
diff --git a/forum_modules/facebookauth/views.py b/forum_modules/facebookauth/views.py
index f1e8f3be..d1d72c5d 100755
--- a/forum_modules/facebookauth/views.py
+++ b/forum_modules/facebookauth/views.py
@@ -1,11 +1,11 @@
-from forum.models import AuthKeyUserAssociation
-from django.http import HttpResponse
-
-def user_is_registered(request):
- try:
- fb_uid = request.POST['fb_uid']
- print fb_uid
- AuthKeyUserAssociation.objects.get(key=fb_uid)
- return HttpResponse('yes')
- except:
+from forum.models import AuthKeyUserAssociation
+from django.http import HttpResponse
+
+def user_is_registered(request):
+ try:
+ fb_uid = request.POST['fb_uid']
+ print fb_uid
+ AuthKeyUserAssociation.objects.get(key=fb_uid)
+ return HttpResponse('yes')
+ except:
return HttpResponse('no') \ No newline at end of file
diff --git a/forum_modules/localauth/authentication.py b/forum_modules/localauth/authentication.py
index 3169a99c..770ea08f 100755
--- a/forum_modules/localauth/authentication.py
+++ b/forum_modules/localauth/authentication.py
@@ -1,18 +1,18 @@
-from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
-from forms import ClassicLoginForm
-
-class LocalAuthConsumer(AuthenticationConsumer):
- def process_authentication_request(self, request):
- form_auth = ClassicLoginForm(request.POST)
-
- if form_auth.is_valid():
- return form_auth.get_user()
- else:
- raise InvalidAuthentication(" ".join(form_auth.errors.values()[0]))
-
-class LocalAuthContext(ConsumerTemplateContext):
- mode = 'STACK_ITEM'
- weight = 1000
- human_name = 'Local authentication'
- stack_item_template = 'modules/localauth/loginform.html'
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
+from forms import ClassicLoginForm
+
+class LocalAuthConsumer(AuthenticationConsumer):
+ def process_authentication_request(self, request):
+ form_auth = ClassicLoginForm(request.POST)
+
+ if form_auth.is_valid():
+ return form_auth.get_user()
+ else:
+ raise InvalidAuthentication(" ".join(form_auth.errors.values()[0]))
+
+class LocalAuthContext(ConsumerTemplateContext):
+ mode = 'STACK_ITEM'
+ weight = 1000
+ human_name = 'Local authentication'
+ stack_item_template = 'modules/localauth/loginform.html'
show_to_logged_in_user = False \ No newline at end of file
diff --git a/forum_modules/localauth/forms.py b/forum_modules/localauth/forms.py
index 59f5d565..8afa2b05 100755
--- a/forum_modules/localauth/forms.py
+++ b/forum_modules/localauth/forms.py
@@ -1,77 +1,77 @@
-from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
-from forum.models import EmailFeedSetting, Question
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django.contrib.auth import authenticate
-from django import forms
-import logging
-
-class ClassicRegisterForm(SetPasswordForm):
- """ legacy registration form """
-
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
- #fields password1 and password2 are inherited
- #recaptcha = ReCaptchaField()
-
-class ClassicLoginForm(forms.Form):
- """ legacy account signin form """
- next = NextUrlField()
- username = UserNameField(required=False,skip_clean=True)
- password = forms.CharField(max_length=128,
- widget=forms.widgets.PasswordInput(attrs={'class':'required login'}),
- required=False)
-
- def __init__(self, data=None, files=None, auto_id='id_%s',
- prefix=None, initial=None):
- super(ClassicLoginForm, self).__init__(data, files, auto_id,
- prefix, initial)
- self.user_cache = None
-
- def _clean_nonempty_field(self,field):
- value = None
- if field in self.cleaned_data:
- value = str(self.cleaned_data[field]).strip()
- if value == '':
- value = None
- self.cleaned_data[field] = value
- return value
-
- def clean_username(self):
- return self._clean_nonempty_field('username')
-
- def clean_password(self):
- return self._clean_nonempty_field('password')
-
- def clean(self):
- error_list = []
- username = self.cleaned_data['username']
- password = self.cleaned_data['password']
-
- self.user_cache = None
- if username and password:
- self.user_cache = authenticate(username=username, password=password)
-
- if self.user_cache is None:
- del self.cleaned_data['username']
- del self.cleaned_data['password']
- error_list.insert(0,(_("Please enter valid username and password "
- "(both are case-sensitive).")))
- elif self.user_cache.is_active == False:
- error_list.append(_("This account is inactive."))
- if len(error_list) > 0:
- error_list.insert(0,_('Login failed.'))
- elif password == None and username == None:
- error_list.append(_('Please enter username and password'))
- elif password == None:
- error_list.append(_('Please enter your password'))
- elif username == None:
- error_list.append(_('Please enter user name'))
- if len(error_list) > 0:
- self._errors['__all__'] = forms.util.ErrorList(error_list)
- return self.cleaned_data
-
- def get_user(self):
- """ get authenticated user """
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
+from forum.models import EmailFeedSetting, Question
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext as _
+from django.contrib.auth import authenticate
+from django import forms
+import logging
+
+class ClassicRegisterForm(SetPasswordForm):
+ """ legacy registration form """
+
+ next = NextUrlField()
+ username = UserNameField()
+ email = UserEmailField()
+ #fields password1 and password2 are inherited
+ #recaptcha = ReCaptchaField()
+
+class ClassicLoginForm(forms.Form):
+ """ legacy account signin form """
+ next = NextUrlField()
+ username = UserNameField(required=False,skip_clean=True)
+ password = forms.CharField(max_length=128,
+ widget=forms.widgets.PasswordInput(attrs={'class':'required login'}),
+ required=False)
+
+ def __init__(self, data=None, files=None, auto_id='id_%s',
+ prefix=None, initial=None):
+ super(ClassicLoginForm, self).__init__(data, files, auto_id,
+ prefix, initial)
+ self.user_cache = None
+
+ def _clean_nonempty_field(self,field):
+ value = None
+ if field in self.cleaned_data:
+ value = str(self.cleaned_data[field]).strip()
+ if value == '':
+ value = None
+ self.cleaned_data[field] = value
+ return value
+
+ def clean_username(self):
+ return self._clean_nonempty_field('username')
+
+ def clean_password(self):
+ return self._clean_nonempty_field('password')
+
+ def clean(self):
+ error_list = []
+ username = self.cleaned_data['username']
+ password = self.cleaned_data['password']
+
+ self.user_cache = None
+ if username and password:
+ self.user_cache = authenticate(username=username, password=password)
+
+ if self.user_cache is None:
+ del self.cleaned_data['username']
+ del self.cleaned_data['password']
+ error_list.insert(0,(_("Please enter valid username and password "
+ "(both are case-sensitive).")))
+ elif self.user_cache.is_active == False:
+ error_list.append(_("This account is inactive."))
+ if len(error_list) > 0:
+ error_list.insert(0,_('Login failed.'))
+ elif password == None and username == None:
+ error_list.append(_('Please enter username and password'))
+ elif password == None:
+ error_list.append(_('Please enter your password'))
+ elif username == None:
+ error_list.append(_('Please enter user name'))
+ if len(error_list) > 0:
+ self._errors['__all__'] = forms.util.ErrorList(error_list)
+ return self.cleaned_data
+
+ def get_user(self):
+ """ get authenticated user """
return self.user_cache \ No newline at end of file
diff --git a/forum_modules/localauth/urls.py b/forum_modules/localauth/urls.py
index e2a3b263..aeebc40a 100755
--- a/forum_modules/localauth/urls.py
+++ b/forum_modules/localauth/urls.py
@@ -1,8 +1,8 @@
-from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-from django.utils.translation import ugettext as _
-import views as app
-
-urlpatterns = patterns('',
- url(r'^%s%s%s$' % (_('account/'), _('local/'), _('register/')), app.register, name='auth_local_register'),
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+from django.utils.translation import ugettext as _
+import views as app
+
+urlpatterns = patterns('',
+ url(r'^%s%s%s$' % (_('account/'), _('local/'), _('register/')), app.register, name='auth_local_register'),
) \ No newline at end of file
diff --git a/forum_modules/localauth/views.py b/forum_modules/localauth/views.py
index acad16cf..1d1e0b3d 100755
--- a/forum_modules/localauth/views.py
+++ b/forum_modules/localauth/views.py
@@ -1,30 +1,30 @@
-from django.contrib.auth.models import User
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-
-from forms import ClassicRegisterForm
-from forum.authentication.forms import SimpleEmailSubscribeForm
-from forum.views.auth import login_and_forward
-
-def register(request):
- if request.method == 'POST':
- form = ClassicRegisterForm(request.POST)
- email_feeds_form = SimpleEmailSubscribeForm(request.POST)
-
- if form.is_valid() and email_feeds_form.is_valid():
- username = form.cleaned_data['username']
- password = form.cleaned_data['password1']
- email = form.cleaned_data['email']
-
- user_ = User.objects.create_user( username,email,password )
- email_feeds_form.save(user_)
- #todo: email validation
- return login_and_forward(request, user_)
- else:
- form = ClassicRegisterForm(initial={'next':'/'})
- email_feeds_form = SimpleEmailSubscribeForm()
-
- return render_to_response('auth/signup.html', {
- 'form': form,
- 'email_feeds_form': email_feeds_form
+from django.contrib.auth.models import User
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from forms import ClassicRegisterForm
+from forum.authentication.forms import SimpleEmailSubscribeForm
+from forum.views.auth import login_and_forward
+
+def register(request):
+ if request.method == 'POST':
+ form = ClassicRegisterForm(request.POST)
+ email_feeds_form = SimpleEmailSubscribeForm(request.POST)
+
+ if form.is_valid() and email_feeds_form.is_valid():
+ username = form.cleaned_data['username']
+ password = form.cleaned_data['password1']
+ email = form.cleaned_data['email']
+
+ user_ = User.objects.create_user( username,email,password )
+ email_feeds_form.save(user_)
+ #todo: email validation
+ return login_and_forward(request, user_)
+ else:
+ form = ClassicRegisterForm(initial={'next':'/'})
+ email_feeds_form = SimpleEmailSubscribeForm()
+
+ return render_to_response('auth/signup.html', {
+ 'form': form,
+ 'email_feeds_form': email_feeds_form
}, context_instance=RequestContext(request)) \ No newline at end of file
diff --git a/forum_modules/oauthauth/authentication.py b/forum_modules/oauthauth/authentication.py
index cfe81568..04145461 100755
--- a/forum_modules/oauthauth/authentication.py
+++ b/forum_modules/oauthauth/authentication.py
@@ -1,41 +1,41 @@
-from consumer import OAuthAbstractAuthConsumer
-from forum.authentication.base import ConsumerTemplateContext
-
-try:
- import json as simplejson
-except ImportError:
- from django.utils import simplejson
-
-from lib import oauth
-import settings
-
-class TwitterAuthConsumer(OAuthAbstractAuthConsumer):
- def __init__(self):
- OAuthAbstractAuthConsumer.__init__(self,
- settings.TWITTER_CONSUMER_KEY,
- settings.TWITTER_CONSUMER_SECRET,
- "twitter.com",
- "https://twitter.com/oauth/request_token",
- "https://twitter.com/oauth/access_token",
- "https://twitter.com/oauth/authorize",
- )
-
- def get_user_data(self, key):
- json = self.fetch_data(key, "https://twitter.com/account/verify_credentials.json")
-
- if 'screen_name' in json:
- creds = simplejson.loads(json)
-
- return {
- 'username': creds['screen_name']
- }
-
-
- return {}
-
-class TwitterAuthContext(ConsumerTemplateContext):
- mode = 'BIGICON'
- type = 'DIRECT'
- weight = 150
- human_name = 'Twitter'
+from consumer import OAuthAbstractAuthConsumer
+from forum.authentication.base import ConsumerTemplateContext
+
+try:
+ import json as simplejson
+except ImportError:
+ from django.utils import simplejson
+
+from lib import oauth
+import settings
+
+class TwitterAuthConsumer(OAuthAbstractAuthConsumer):
+ def __init__(self):
+ OAuthAbstractAuthConsumer.__init__(self,
+ settings.TWITTER_CONSUMER_KEY,
+ settings.TWITTER_CONSUMER_SECRET,
+ "twitter.com",
+ "https://twitter.com/oauth/request_token",
+ "https://twitter.com/oauth/access_token",
+ "https://twitter.com/oauth/authorize",
+ )
+
+ def get_user_data(self, key):
+ json = self.fetch_data(key, "https://twitter.com/account/verify_credentials.json")
+
+ if 'screen_name' in json:
+ creds = simplejson.loads(json)
+
+ return {
+ 'username': creds['screen_name']
+ }
+
+
+ return {}
+
+class TwitterAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 150
+ human_name = 'Twitter'
icon = '/media/images/openid/twitter.png' \ No newline at end of file
diff --git a/forum_modules/oauthauth/consumer.py b/forum_modules/oauthauth/consumer.py
index e3cbda6c..74734145 100755
--- a/forum_modules/oauthauth/consumer.py
+++ b/forum_modules/oauthauth/consumer.py
@@ -1,87 +1,87 @@
-import urllib
-import urllib2
-import httplib
-import time
-
-from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
-from django.utils.translation import ugettext as _
-
-from lib import oauth
-
-class OAuthAbstractAuthConsumer(AuthenticationConsumer):
-
- def __init__(self, consumer_key, consumer_secret, server_url, request_token_url, access_token_url, authorization_url):
- self.consumer_secret = consumer_secret
- self.consumer_key = consumer_key
-
- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
- self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
-
- self.server_url = server_url
- self.request_token_url = request_token_url
- self.access_token_url = access_token_url
- self.authorization_url = authorization_url
-
- def prepare_authentication_request(self, request, redirect_to):
- request_token = self.fetch_request_token()
- request.session['unauthed_token'] = request_token.to_string()
- return self.authorize_token_url(request_token)
-
- def process_authentication_request(self, request):
- unauthed_token = request.session.get('unauthed_token', None)
- if not unauthed_token:
- raise InvalidAuthentication(_('Error, the oauth token is not on the server'))
-
- token = oauth.OAuthToken.from_string(unauthed_token)
-
- if token.key != request.GET.get('oauth_token', 'no-token'):
- raise InvalidAuthentication(_("Something went wrong! Auth tokens do not match"))
-
- access_token = self.fetch_access_token(token)
-
- return access_token.to_string()
-
- def get_user_data(self, key):
- #token = oauth.OAuthToken.from_string(access_token)
- return {}
-
- def fetch_request_token(self):
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
- oauth_request.sign_request(self.signature_method, self.consumer, None)
- params = oauth_request.parameters
- data = urllib.urlencode(params)
- full_url='%s?%s'%(self.request_token_url, data)
- response = urllib2.urlopen(full_url)
- return oauth.OAuthToken.from_string(response.read())
-
- def authorize_token_url(self, token, callback_url=None):
- oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token,\
- callback=callback_url, http_url=self.authorization_url)
- params = oauth_request.parameters
- data = urllib.urlencode(params)
- full_url='%s?%s'%(self.authorization_url, data)
- return full_url
-
- def fetch_access_token(self, token):
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url)
- oauth_request.sign_request(self.signature_method, self.consumer, token)
- params = oauth_request.parameters
- data = urllib.urlencode(params)
- full_url='%s?%s'%(self.access_token_url, data)
- response = urllib2.urlopen(full_url)
- return oauth.OAuthToken.from_string(response.read())
-
- def fetch_data(self, token, http_url, parameters=None):
- access_token = oauth.OAuthToken.from_string(token)
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
- self.consumer, token=access_token, http_method="GET",
- http_url=http_url, parameters=parameters,
- )
- oauth_request.sign_request(self.signature_method, self.consumer, access_token)
-
- url = oauth_request.to_url()
- connection = httplib.HTTPSConnection(self.server_url)
- connection.request(oauth_request.http_method, url)
-
- return connection.getresponse().read()
-
+import urllib
+import urllib2
+import httplib
+import time
+
+from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
+from django.utils.translation import ugettext as _
+
+from lib import oauth
+
+class OAuthAbstractAuthConsumer(AuthenticationConsumer):
+
+ def __init__(self, consumer_key, consumer_secret, server_url, request_token_url, access_token_url, authorization_url):
+ self.consumer_secret = consumer_secret
+ self.consumer_key = consumer_key
+
+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+
+ self.server_url = server_url
+ self.request_token_url = request_token_url
+ self.access_token_url = access_token_url
+ self.authorization_url = authorization_url
+
+ def prepare_authentication_request(self, request, redirect_to):
+ request_token = self.fetch_request_token()
+ request.session['unauthed_token'] = request_token.to_string()
+ return self.authorize_token_url(request_token)
+
+ def process_authentication_request(self, request):
+ unauthed_token = request.session.get('unauthed_token', None)
+ if not unauthed_token:
+ raise InvalidAuthentication(_('Error, the oauth token is not on the server'))
+
+ token = oauth.OAuthToken.from_string(unauthed_token)
+
+ if token.key != request.GET.get('oauth_token', 'no-token'):
+ raise InvalidAuthentication(_("Something went wrong! Auth tokens do not match"))
+
+ access_token = self.fetch_access_token(token)
+
+ return access_token.to_string()
+
+ def get_user_data(self, key):
+ #token = oauth.OAuthToken.from_string(access_token)
+ return {}
+
+ def fetch_request_token(self):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
+ oauth_request.sign_request(self.signature_method, self.consumer, None)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.request_token_url, data)
+ response = urllib2.urlopen(full_url)
+ return oauth.OAuthToken.from_string(response.read())
+
+ def authorize_token_url(self, token, callback_url=None):
+ oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token,\
+ callback=callback_url, http_url=self.authorization_url)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.authorization_url, data)
+ return full_url
+
+ def fetch_access_token(self, token):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url)
+ oauth_request.sign_request(self.signature_method, self.consumer, token)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.access_token_url, data)
+ response = urllib2.urlopen(full_url)
+ return oauth.OAuthToken.from_string(response.read())
+
+ def fetch_data(self, token, http_url, parameters=None):
+ access_token = oauth.OAuthToken.from_string(token)
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+ self.consumer, token=access_token, http_method="GET",
+ http_url=http_url, parameters=parameters,
+ )
+ oauth_request.sign_request(self.signature_method, self.consumer, access_token)
+
+ url = oauth_request.to_url()
+ connection = httplib.HTTPSConnection(self.server_url)
+ connection.request(oauth_request.http_method, url)
+
+ return connection.getresponse().read()
+
diff --git a/forum_modules/oauthauth/lib/oauth.py b/forum_modules/oauthauth/lib/oauth.py
index e2af5c0f..89abf858 100755
--- a/forum_modules/oauthauth/lib/oauth.py
+++ b/forum_modules/oauthauth/lib/oauth.py
@@ -1,594 +1,594 @@
-"""
-The MIT License
-
-Copyright (c) 2007 Leah Culver
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-"""
-
-import cgi
-import urllib
-import time
-import random
-import urlparse
-import hmac
-import binascii
-
-
-VERSION = '1.0' # Hi Blaine!
-HTTP_METHOD = 'GET'
-SIGNATURE_METHOD = 'PLAINTEXT'
-
-
-class OAuthError(RuntimeError):
- """Generic exception class."""
- def __init__(self, message='OAuth error occured.'):
- self.message = message
-
-def build_authenticate_header(realm=''):
- """Optional WWW-Authenticate header (401 error)"""
- return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
-def escape(s):
- """Escape a URL including any /."""
- return urllib.quote(s, safe='~')
-
-def _utf8_str(s):
- """Convert unicode to utf-8."""
- if isinstance(s, unicode):
- return s.encode("utf-8")
- else:
- return str(s)
-
-def generate_timestamp():
- """Get seconds since epoch (UTC)."""
- return int(time.time())
-
-def generate_nonce(length=8):
- """Generate pseudorandom number."""
- return ''.join([str(random.randint(0, 9)) for i in range(length)])
-
-
-class OAuthConsumer(object):
- """Consumer of OAuth authentication.
-
- OAuthConsumer is a data type that represents the identity of the Consumer
- via its shared secret with the Service Provider.
-
- """
- key = None
- secret = None
-
- def __init__(self, key, secret):
- self.key = key
- self.secret = secret
-
-
-class OAuthToken(object):
- """OAuthToken is a data type that represents an End User via either an access
- or request token.
-
- key -- the token
- secret -- the token secret
-
- """
- key = None
- secret = None
-
- def __init__(self, key, secret):
- self.key = key
- self.secret = secret
-
- def to_string(self):
- return urllib.urlencode({'oauth_token': self.key,
- 'oauth_token_secret': self.secret})
-
- def from_string(s):
- """ Returns a token from something like:
- oauth_token_secret=xxx&oauth_token=xxx
- """
- params = cgi.parse_qs(s, keep_blank_values=False)
- key = params['oauth_token'][0]
- secret = params['oauth_token_secret'][0]
- return OAuthToken(key, secret)
- from_string = staticmethod(from_string)
-
- def __str__(self):
- return self.to_string()
-
-
-class OAuthRequest(object):
- """OAuthRequest represents the request and can be serialized.
-
- OAuth parameters:
- - oauth_consumer_key
- - oauth_token
- - oauth_signature_method
- - oauth_signature
- - oauth_timestamp
- - oauth_nonce
- - oauth_version
- ... any additional parameters, as defined by the Service Provider.
- """
- parameters = None # OAuth parameters.
- http_method = HTTP_METHOD
- http_url = None
- version = VERSION
-
- def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
- self.http_method = http_method
- self.http_url = http_url
- self.parameters = parameters or {}
-
- def set_parameter(self, parameter, value):
- self.parameters[parameter] = value
-
- def get_parameter(self, parameter):
- try:
- return self.parameters[parameter]
- except:
- raise OAuthError('Parameter not found: %s' % parameter)
-
- def _get_timestamp_nonce(self):
- return self.get_parameter('oauth_timestamp'), self.get_parameter(
- 'oauth_nonce')
-
- def get_nonoauth_parameters(self):
- """Get any non-OAuth parameters."""
- parameters = {}
- for k, v in self.parameters.iteritems():
- # Ignore oauth parameters.
- if k.find('oauth_') < 0:
- parameters[k] = v
- return parameters
-
- def to_header(self, realm=''):
- """Serialize as a header for an HTTPAuth request."""
- auth_header = 'OAuth realm="%s"' % realm
- # Add the oauth parameters.
- if self.parameters:
- for k, v in self.parameters.iteritems():
- if k[:6] == 'oauth_':
- auth_header += ', %s="%s"' % (k, escape(str(v)))
- return {'Authorization': auth_header}
-
- def to_postdata(self):
- """Serialize as post data for a POST request."""
- return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
- for k, v in self.parameters.iteritems()])
-
- def to_url(self):
- """Serialize as a URL for a GET request."""
- return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
-
- def get_normalized_parameters(self):
- """Return a string that contains the parameters that must be signed."""
- params = self.parameters
- try:
- # Exclude the signature if it exists.
- del params['oauth_signature']
- except:
- pass
- # Escape key values before sorting.
- key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
- for k,v in params.items()]
- # Sort lexicographically, first after key, then after value.
- key_values.sort()
- # Combine key value pairs into a string.
- return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
-
- def get_normalized_http_method(self):
- """Uppercases the http method."""
- return self.http_method.upper()
-
- def get_normalized_http_url(self):
- """Parses the URL and rebuilds it to be scheme://host/path."""
- parts = urlparse.urlparse(self.http_url)
- scheme, netloc, path = parts[:3]
- # Exclude default port numbers.
- if scheme == 'http' and netloc[-3:] == ':80':
- netloc = netloc[:-3]
- elif scheme == 'https' and netloc[-4:] == ':443':
- netloc = netloc[:-4]
- return '%s://%s%s' % (scheme, netloc, path)
-
- def sign_request(self, signature_method, consumer, token):
- """Set the signature parameter to the result of build_signature."""
- # Set the signature method.
- self.set_parameter('oauth_signature_method',
- signature_method.get_name())
- # Set the signature.
- self.set_parameter('oauth_signature',
- self.build_signature(signature_method, consumer, token))
-
- def build_signature(self, signature_method, consumer, token):
- """Calls the build signature method within the signature method."""
- return signature_method.build_signature(self, consumer, token)
-
- def from_request(http_method, http_url, headers=None, parameters=None,
- query_string=None):
- """Combines multiple parameter sources."""
- if parameters is None:
- parameters = {}
-
- # Headers
- if headers and 'Authorization' in headers:
- auth_header = headers['Authorization']
- # Check that the authorization header is OAuth.
- if auth_header.index('OAuth') > -1:
- auth_header = auth_header.lstrip('OAuth ')
- try:
- # Get the parameters from the header.
- header_params = OAuthRequest._split_header(auth_header)
- parameters.update(header_params)
- except:
- raise OAuthError('Unable to parse OAuth parameters from '
- 'Authorization header.')
-
- # GET or POST query string.
- if query_string:
- query_params = OAuthRequest._split_url_string(query_string)
- parameters.update(query_params)
-
- # URL parameters.
- param_str = urlparse.urlparse(http_url)[4] # query
- url_params = OAuthRequest._split_url_string(param_str)
- parameters.update(url_params)
-
- if parameters:
- return OAuthRequest(http_method, http_url, parameters)
-
- return None
- from_request = staticmethod(from_request)
-
- def from_consumer_and_token(oauth_consumer, token=None,
- http_method=HTTP_METHOD, http_url=None, parameters=None):
- if not parameters:
- parameters = {}
-
- defaults = {
- 'oauth_consumer_key': oauth_consumer.key,
- 'oauth_timestamp': generate_timestamp(),
- 'oauth_nonce': generate_nonce(),
- 'oauth_version': OAuthRequest.version,
- }
-
- defaults.update(parameters)
- parameters = defaults
-
- if token:
- parameters['oauth_token'] = token.key
-
- return OAuthRequest(http_method, http_url, parameters)
- from_consumer_and_token = staticmethod(from_consumer_and_token)
-
- def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
- http_url=None, parameters=None):
- if not parameters:
- parameters = {}
-
- parameters['oauth_token'] = token.key
-
- if callback:
- parameters['oauth_callback'] = callback
-
- return OAuthRequest(http_method, http_url, parameters)
- from_token_and_callback = staticmethod(from_token_and_callback)
-
- def _split_header(header):
- """Turn Authorization: header into parameters."""
- params = {}
- parts = header.split(',')
- for param in parts:
- # Ignore realm parameter.
- if param.find('realm') > -1:
- continue
- # Remove whitespace.
- param = param.strip()
- # Split key-value.
- param_parts = param.split('=', 1)
- # Remove quotes and unescape the value.
- params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
- return params
- _split_header = staticmethod(_split_header)
-
- def _split_url_string(param_str):
- """Turn URL string into parameters."""
- parameters = cgi.parse_qs(param_str, keep_blank_values=False)
- for k, v in parameters.iteritems():
- parameters[k] = urllib.unquote(v[0])
- return parameters
- _split_url_string = staticmethod(_split_url_string)
-
-class OAuthServer(object):
- """A worker to check the validity of a request against a data store."""
- timestamp_threshold = 300 # In seconds, five minutes.
- version = VERSION
- signature_methods = None
- data_store = None
-
- def __init__(self, data_store=None, signature_methods=None):
- self.data_store = data_store
- self.signature_methods = signature_methods or {}
-
- def set_data_store(self, data_store):
- self.data_store = data_store
-
- def get_data_store(self):
- return self.data_store
-
- def add_signature_method(self, signature_method):
- self.signature_methods[signature_method.get_name()] = signature_method
- return self.signature_methods
-
- def fetch_request_token(self, oauth_request):
- """Processes a request_token request and returns the
- request token on success.
- """
- try:
- # Get the request token for authorization.
- token = self._get_token(oauth_request, 'request')
- except OAuthError:
- # No token required for the initial token request.
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- self._check_signature(oauth_request, consumer, None)
- # Fetch a new token.
- token = self.data_store.fetch_request_token(consumer)
- return token
-
- def fetch_access_token(self, oauth_request):
- """Processes an access_token request and returns the
- access token on success.
- """
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- # Get the request token.
- token = self._get_token(oauth_request, 'request')
- self._check_signature(oauth_request, consumer, token)
- new_token = self.data_store.fetch_access_token(consumer, token)
- return new_token
-
- def verify_request(self, oauth_request):
- """Verifies an api call and checks all the parameters."""
- # -> consumer and token
- version = self._get_version(oauth_request)
- consumer = self._get_consumer(oauth_request)
- # Get the access token.
- token = self._get_token(oauth_request, 'access')
- self._check_signature(oauth_request, consumer, token)
- parameters = oauth_request.get_nonoauth_parameters()
- return consumer, token, parameters
-
- def authorize_token(self, token, user):
- """Authorize a request token."""
- return self.data_store.authorize_request_token(token, user)
-
- def get_callback(self, oauth_request):
- """Get the callback URL."""
- return oauth_request.get_parameter('oauth_callback')
-
- def build_authenticate_header(self, realm=''):
- """Optional support for the authenticate header."""
- return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
-
- def _get_version(self, oauth_request):
- """Verify the correct version request for this server."""
- try:
- version = oauth_request.get_parameter('oauth_version')
- except:
- version = VERSION
- if version and version != self.version:
- raise OAuthError('OAuth version %s not supported.' % str(version))
- return version
-
- def _get_signature_method(self, oauth_request):
- """Figure out the signature with some defaults."""
- try:
- signature_method = oauth_request.get_parameter(
- 'oauth_signature_method')
- except:
- signature_method = SIGNATURE_METHOD
- try:
- # Get the signature method object.
- signature_method = self.signature_methods[signature_method]
- except:
- signature_method_names = ', '.join(self.signature_methods.keys())
- raise OAuthError('Signature method %s not supported try one of the '
- 'following: %s' % (signature_method, signature_method_names))
-
- return signature_method
-
- def _get_consumer(self, oauth_request):
- consumer_key = oauth_request.get_parameter('oauth_consumer_key')
- consumer = self.data_store.lookup_consumer(consumer_key)
- if not consumer:
- raise OAuthError('Invalid consumer.')
- return consumer
-
- def _get_token(self, oauth_request, token_type='access'):
- """Try to find the token for the provided request token key."""
- token_field = oauth_request.get_parameter('oauth_token')
- token = self.data_store.lookup_token(token_type, token_field)
- if not token:
- raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
- return token
-
- def _check_signature(self, oauth_request, consumer, token):
- timestamp, nonce = oauth_request._get_timestamp_nonce()
- self._check_timestamp(timestamp)
- self._check_nonce(consumer, token, nonce)
- signature_method = self._get_signature_method(oauth_request)
- try:
- signature = oauth_request.get_parameter('oauth_signature')
- except:
- raise OAuthError('Missing signature.')
- # Validate the signature.
- valid_sig = signature_method.check_signature(oauth_request, consumer,
- token, signature)
- if not valid_sig:
- key, base = signature_method.build_signature_base_string(
- oauth_request, consumer, token)
- raise OAuthError('Invalid signature. Expected signature base '
- 'string: %s' % base)
- built = signature_method.build_signature(oauth_request, consumer, token)
-
- def _check_timestamp(self, timestamp):
- """Verify that timestamp is recentish."""
- timestamp = int(timestamp)
- now = int(time.time())
- lapsed = now - timestamp
- if lapsed > self.timestamp_threshold:
- raise OAuthError('Expired timestamp: given %d and now %s has a '
- 'greater difference than threshold %d' %
- (timestamp, now, self.timestamp_threshold))
-
- def _check_nonce(self, consumer, token, nonce):
- """Verify that the nonce is uniqueish."""
- nonce = self.data_store.lookup_nonce(consumer, token, nonce)
- if nonce:
- raise OAuthError('Nonce already used: %s' % str(nonce))
-
-
-class OAuthClient(object):
- """OAuthClient is a worker to attempt to execute a request."""
- consumer = None
- token = None
-
- def __init__(self, oauth_consumer, oauth_token):
- self.consumer = oauth_consumer
- self.token = oauth_token
-
- def get_consumer(self):
- return self.consumer
-
- def get_token(self):
- return self.token
-
- def fetch_request_token(self, oauth_request):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_access_token(self, oauth_request):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def access_resource(self, oauth_request):
- """-> Some protected resource."""
- raise NotImplementedError
-
-
-class OAuthDataStore(object):
- """A database abstraction used to lookup consumers and tokens."""
-
- def lookup_consumer(self, key):
- """-> OAuthConsumer."""
- raise NotImplementedError
-
- def lookup_token(self, oauth_consumer, token_type, token_token):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_request_token(self, oauth_consumer):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def fetch_access_token(self, oauth_consumer, oauth_token):
- """-> OAuthToken."""
- raise NotImplementedError
-
- def authorize_request_token(self, oauth_token, user):
- """-> OAuthToken."""
- raise NotImplementedError
-
-
-class OAuthSignatureMethod(object):
- """A strategy class that implements a signature method."""
- def get_name(self):
- """-> str."""
- raise NotImplementedError
-
- def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
- """-> str key, str raw."""
- raise NotImplementedError
-
- def build_signature(self, oauth_request, oauth_consumer, oauth_token):
- """-> str."""
- raise NotImplementedError
-
- def check_signature(self, oauth_request, consumer, token, signature):
- built = self.build_signature(oauth_request, consumer, token)
- return built == signature
-
-
-class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
-
- def get_name(self):
- return 'HMAC-SHA1'
-
- def build_signature_base_string(self, oauth_request, consumer, token):
- sig = (
- escape(oauth_request.get_normalized_http_method()),
- escape(oauth_request.get_normalized_http_url()),
- escape(oauth_request.get_normalized_parameters()),
- )
-
- key = '%s&' % escape(consumer.secret)
- if token:
- key += escape(token.secret)
- raw = '&'.join(sig)
- return key, raw
-
- def build_signature(self, oauth_request, consumer, token):
- """Builds the base signature string."""
- key, raw = self.build_signature_base_string(oauth_request, consumer,
- token)
-
- # HMAC object.
- try:
- import hashlib # 2.5
- hashed = hmac.new(key, raw, hashlib.sha1)
- except:
- import sha # Deprecated
- hashed = hmac.new(key, raw, sha)
-
- # Calculate the digest base 64.
- return binascii.b2a_base64(hashed.digest())[:-1]
-
-
-class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
-
- def get_name(self):
- return 'PLAINTEXT'
-
- def build_signature_base_string(self, oauth_request, consumer, token):
- """Concatenates the consumer key and secret."""
- sig = '%s&' % escape(consumer.secret)
- if token:
- sig = sig + escape(token.secret)
- return sig, sig
-
- def build_signature(self, oauth_request, consumer, token):
- key, raw = self.build_signature_base_string(oauth_request, consumer,
- token)
+"""
+The MIT License
+
+Copyright (c) 2007 Leah Culver
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import cgi
+import urllib
+import time
+import random
+import urlparse
+import hmac
+import binascii
+
+
+VERSION = '1.0' # Hi Blaine!
+HTTP_METHOD = 'GET'
+SIGNATURE_METHOD = 'PLAINTEXT'
+
+
+class OAuthError(RuntimeError):
+ """Generic exception class."""
+ def __init__(self, message='OAuth error occured.'):
+ self.message = message
+
+def build_authenticate_header(realm=''):
+ """Optional WWW-Authenticate header (401 error)"""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+def escape(s):
+ """Escape a URL including any /."""
+ return urllib.quote(s, safe='~')
+
+def _utf8_str(s):
+ """Convert unicode to utf-8."""
+ if isinstance(s, unicode):
+ return s.encode("utf-8")
+ else:
+ return str(s)
+
+def generate_timestamp():
+ """Get seconds since epoch (UTC)."""
+ return int(time.time())
+
+def generate_nonce(length=8):
+ """Generate pseudorandom number."""
+ return ''.join([str(random.randint(0, 9)) for i in range(length)])
+
+
+class OAuthConsumer(object):
+ """Consumer of OAuth authentication.
+
+ OAuthConsumer is a data type that represents the identity of the Consumer
+ via its shared secret with the Service Provider.
+
+ """
+ key = None
+ secret = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+
+class OAuthToken(object):
+ """OAuthToken is a data type that represents an End User via either an access
+ or request token.
+
+ key -- the token
+ secret -- the token secret
+
+ """
+ key = None
+ secret = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+ def to_string(self):
+ return urllib.urlencode({'oauth_token': self.key,
+ 'oauth_token_secret': self.secret})
+
+ def from_string(s):
+ """ Returns a token from something like:
+ oauth_token_secret=xxx&oauth_token=xxx
+ """
+ params = cgi.parse_qs(s, keep_blank_values=False)
+ key = params['oauth_token'][0]
+ secret = params['oauth_token_secret'][0]
+ return OAuthToken(key, secret)
+ from_string = staticmethod(from_string)
+
+ def __str__(self):
+ return self.to_string()
+
+
+class OAuthRequest(object):
+ """OAuthRequest represents the request and can be serialized.
+
+ OAuth parameters:
+ - oauth_consumer_key
+ - oauth_token
+ - oauth_signature_method
+ - oauth_signature
+ - oauth_timestamp
+ - oauth_nonce
+ - oauth_version
+ ... any additional parameters, as defined by the Service Provider.
+ """
+ parameters = None # OAuth parameters.
+ http_method = HTTP_METHOD
+ http_url = None
+ version = VERSION
+
+ def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
+ self.http_method = http_method
+ self.http_url = http_url
+ self.parameters = parameters or {}
+
+ def set_parameter(self, parameter, value):
+ self.parameters[parameter] = value
+
+ def get_parameter(self, parameter):
+ try:
+ return self.parameters[parameter]
+ except:
+ raise OAuthError('Parameter not found: %s' % parameter)
+
+ def _get_timestamp_nonce(self):
+ return self.get_parameter('oauth_timestamp'), self.get_parameter(
+ 'oauth_nonce')
+
+ def get_nonoauth_parameters(self):
+ """Get any non-OAuth parameters."""
+ parameters = {}
+ for k, v in self.parameters.iteritems():
+ # Ignore oauth parameters.
+ if k.find('oauth_') < 0:
+ parameters[k] = v
+ return parameters
+
+ def to_header(self, realm=''):
+ """Serialize as a header for an HTTPAuth request."""
+ auth_header = 'OAuth realm="%s"' % realm
+ # Add the oauth parameters.
+ if self.parameters:
+ for k, v in self.parameters.iteritems():
+ if k[:6] == 'oauth_':
+ auth_header += ', %s="%s"' % (k, escape(str(v)))
+ return {'Authorization': auth_header}
+
+ def to_postdata(self):
+ """Serialize as post data for a POST request."""
+ return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
+ for k, v in self.parameters.iteritems()])
+
+ def to_url(self):
+ """Serialize as a URL for a GET request."""
+ return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
+
+ def get_normalized_parameters(self):
+ """Return a string that contains the parameters that must be signed."""
+ params = self.parameters
+ try:
+ # Exclude the signature if it exists.
+ del params['oauth_signature']
+ except:
+ pass
+ # Escape key values before sorting.
+ key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
+ for k,v in params.items()]
+ # Sort lexicographically, first after key, then after value.
+ key_values.sort()
+ # Combine key value pairs into a string.
+ return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
+
+ def get_normalized_http_method(self):
+ """Uppercases the http method."""
+ return self.http_method.upper()
+
+ def get_normalized_http_url(self):
+ """Parses the URL and rebuilds it to be scheme://host/path."""
+ parts = urlparse.urlparse(self.http_url)
+ scheme, netloc, path = parts[:3]
+ # Exclude default port numbers.
+ if scheme == 'http' and netloc[-3:] == ':80':
+ netloc = netloc[:-3]
+ elif scheme == 'https' and netloc[-4:] == ':443':
+ netloc = netloc[:-4]
+ return '%s://%s%s' % (scheme, netloc, path)
+
+ def sign_request(self, signature_method, consumer, token):
+ """Set the signature parameter to the result of build_signature."""
+ # Set the signature method.
+ self.set_parameter('oauth_signature_method',
+ signature_method.get_name())
+ # Set the signature.
+ self.set_parameter('oauth_signature',
+ self.build_signature(signature_method, consumer, token))
+
+ def build_signature(self, signature_method, consumer, token):
+ """Calls the build signature method within the signature method."""
+ return signature_method.build_signature(self, consumer, token)
+
+ def from_request(http_method, http_url, headers=None, parameters=None,
+ query_string=None):
+ """Combines multiple parameter sources."""
+ if parameters is None:
+ parameters = {}
+
+ # Headers
+ if headers and 'Authorization' in headers:
+ auth_header = headers['Authorization']
+ # Check that the authorization header is OAuth.
+ if auth_header.index('OAuth') > -1:
+ auth_header = auth_header.lstrip('OAuth ')
+ try:
+ # Get the parameters from the header.
+ header_params = OAuthRequest._split_header(auth_header)
+ parameters.update(header_params)
+ except:
+ raise OAuthError('Unable to parse OAuth parameters from '
+ 'Authorization header.')
+
+ # GET or POST query string.
+ if query_string:
+ query_params = OAuthRequest._split_url_string(query_string)
+ parameters.update(query_params)
+
+ # URL parameters.
+ param_str = urlparse.urlparse(http_url)[4] # query
+ url_params = OAuthRequest._split_url_string(param_str)
+ parameters.update(url_params)
+
+ if parameters:
+ return OAuthRequest(http_method, http_url, parameters)
+
+ return None
+ from_request = staticmethod(from_request)
+
+ def from_consumer_and_token(oauth_consumer, token=None,
+ http_method=HTTP_METHOD, http_url=None, parameters=None):
+ if not parameters:
+ parameters = {}
+
+ defaults = {
+ 'oauth_consumer_key': oauth_consumer.key,
+ 'oauth_timestamp': generate_timestamp(),
+ 'oauth_nonce': generate_nonce(),
+ 'oauth_version': OAuthRequest.version,
+ }
+
+ defaults.update(parameters)
+ parameters = defaults
+
+ if token:
+ parameters['oauth_token'] = token.key
+
+ return OAuthRequest(http_method, http_url, parameters)
+ from_consumer_and_token = staticmethod(from_consumer_and_token)
+
+ def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
+ http_url=None, parameters=None):
+ if not parameters:
+ parameters = {}
+
+ parameters['oauth_token'] = token.key
+
+ if callback:
+ parameters['oauth_callback'] = callback
+
+ return OAuthRequest(http_method, http_url, parameters)
+ from_token_and_callback = staticmethod(from_token_and_callback)
+
+ def _split_header(header):
+ """Turn Authorization: header into parameters."""
+ params = {}
+ parts = header.split(',')
+ for param in parts:
+ # Ignore realm parameter.
+ if param.find('realm') > -1:
+ continue
+ # Remove whitespace.
+ param = param.strip()
+ # Split key-value.
+ param_parts = param.split('=', 1)
+ # Remove quotes and unescape the value.
+ params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
+ return params
+ _split_header = staticmethod(_split_header)
+
+ def _split_url_string(param_str):
+ """Turn URL string into parameters."""
+ parameters = cgi.parse_qs(param_str, keep_blank_values=False)
+ for k, v in parameters.iteritems():
+ parameters[k] = urllib.unquote(v[0])
+ return parameters
+ _split_url_string = staticmethod(_split_url_string)
+
+class OAuthServer(object):
+ """A worker to check the validity of a request against a data store."""
+ timestamp_threshold = 300 # In seconds, five minutes.
+ version = VERSION
+ signature_methods = None
+ data_store = None
+
+ def __init__(self, data_store=None, signature_methods=None):
+ self.data_store = data_store
+ self.signature_methods = signature_methods or {}
+
+ def set_data_store(self, data_store):
+ self.data_store = data_store
+
+ def get_data_store(self):
+ return self.data_store
+
+ def add_signature_method(self, signature_method):
+ self.signature_methods[signature_method.get_name()] = signature_method
+ return self.signature_methods
+
+ def fetch_request_token(self, oauth_request):
+ """Processes a request_token request and returns the
+ request token on success.
+ """
+ try:
+ # Get the request token for authorization.
+ token = self._get_token(oauth_request, 'request')
+ except OAuthError:
+ # No token required for the initial token request.
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ self._check_signature(oauth_request, consumer, None)
+ # Fetch a new token.
+ token = self.data_store.fetch_request_token(consumer)
+ return token
+
+ def fetch_access_token(self, oauth_request):
+ """Processes an access_token request and returns the
+ access token on success.
+ """
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ # Get the request token.
+ token = self._get_token(oauth_request, 'request')
+ self._check_signature(oauth_request, consumer, token)
+ new_token = self.data_store.fetch_access_token(consumer, token)
+ return new_token
+
+ def verify_request(self, oauth_request):
+ """Verifies an api call and checks all the parameters."""
+ # -> consumer and token
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ # Get the access token.
+ token = self._get_token(oauth_request, 'access')
+ self._check_signature(oauth_request, consumer, token)
+ parameters = oauth_request.get_nonoauth_parameters()
+ return consumer, token, parameters
+
+ def authorize_token(self, token, user):
+ """Authorize a request token."""
+ return self.data_store.authorize_request_token(token, user)
+
+ def get_callback(self, oauth_request):
+ """Get the callback URL."""
+ return oauth_request.get_parameter('oauth_callback')
+
+ def build_authenticate_header(self, realm=''):
+ """Optional support for the authenticate header."""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+ def _get_version(self, oauth_request):
+ """Verify the correct version request for this server."""
+ try:
+ version = oauth_request.get_parameter('oauth_version')
+ except:
+ version = VERSION
+ if version and version != self.version:
+ raise OAuthError('OAuth version %s not supported.' % str(version))
+ return version
+
+ def _get_signature_method(self, oauth_request):
+ """Figure out the signature with some defaults."""
+ try:
+ signature_method = oauth_request.get_parameter(
+ 'oauth_signature_method')
+ except:
+ signature_method = SIGNATURE_METHOD
+ try:
+ # Get the signature method object.
+ signature_method = self.signature_methods[signature_method]
+ except:
+ signature_method_names = ', '.join(self.signature_methods.keys())
+ raise OAuthError('Signature method %s not supported try one of the '
+ 'following: %s' % (signature_method, signature_method_names))
+
+ return signature_method
+
+ def _get_consumer(self, oauth_request):
+ consumer_key = oauth_request.get_parameter('oauth_consumer_key')
+ consumer = self.data_store.lookup_consumer(consumer_key)
+ if not consumer:
+ raise OAuthError('Invalid consumer.')
+ return consumer
+
+ def _get_token(self, oauth_request, token_type='access'):
+ """Try to find the token for the provided request token key."""
+ token_field = oauth_request.get_parameter('oauth_token')
+ token = self.data_store.lookup_token(token_type, token_field)
+ if not token:
+ raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
+ return token
+
+ def _check_signature(self, oauth_request, consumer, token):
+ timestamp, nonce = oauth_request._get_timestamp_nonce()
+ self._check_timestamp(timestamp)
+ self._check_nonce(consumer, token, nonce)
+ signature_method = self._get_signature_method(oauth_request)
+ try:
+ signature = oauth_request.get_parameter('oauth_signature')
+ except:
+ raise OAuthError('Missing signature.')
+ # Validate the signature.
+ valid_sig = signature_method.check_signature(oauth_request, consumer,
+ token, signature)
+ if not valid_sig:
+ key, base = signature_method.build_signature_base_string(
+ oauth_request, consumer, token)
+ raise OAuthError('Invalid signature. Expected signature base '
+ 'string: %s' % base)
+ built = signature_method.build_signature(oauth_request, consumer, token)
+
+ def _check_timestamp(self, timestamp):
+ """Verify that timestamp is recentish."""
+ timestamp = int(timestamp)
+ now = int(time.time())
+ lapsed = now - timestamp
+ if lapsed > self.timestamp_threshold:
+ raise OAuthError('Expired timestamp: given %d and now %s has a '
+ 'greater difference than threshold %d' %
+ (timestamp, now, self.timestamp_threshold))
+
+ def _check_nonce(self, consumer, token, nonce):
+ """Verify that the nonce is uniqueish."""
+ nonce = self.data_store.lookup_nonce(consumer, token, nonce)
+ if nonce:
+ raise OAuthError('Nonce already used: %s' % str(nonce))
+
+
+class OAuthClient(object):
+ """OAuthClient is a worker to attempt to execute a request."""
+ consumer = None
+ token = None
+
+ def __init__(self, oauth_consumer, oauth_token):
+ self.consumer = oauth_consumer
+ self.token = oauth_token
+
+ def get_consumer(self):
+ return self.consumer
+
+ def get_token(self):
+ return self.token
+
+ def fetch_request_token(self, oauth_request):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_access_token(self, oauth_request):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def access_resource(self, oauth_request):
+ """-> Some protected resource."""
+ raise NotImplementedError
+
+
+class OAuthDataStore(object):
+ """A database abstraction used to lookup consumers and tokens."""
+
+ def lookup_consumer(self, key):
+ """-> OAuthConsumer."""
+ raise NotImplementedError
+
+ def lookup_token(self, oauth_consumer, token_type, token_token):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_request_token(self, oauth_consumer):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_access_token(self, oauth_consumer, oauth_token):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def authorize_request_token(self, oauth_token, user):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+
+class OAuthSignatureMethod(object):
+ """A strategy class that implements a signature method."""
+ def get_name(self):
+ """-> str."""
+ raise NotImplementedError
+
+ def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
+ """-> str key, str raw."""
+ raise NotImplementedError
+
+ def build_signature(self, oauth_request, oauth_consumer, oauth_token):
+ """-> str."""
+ raise NotImplementedError
+
+ def check_signature(self, oauth_request, consumer, token, signature):
+ built = self.build_signature(oauth_request, consumer, token)
+ return built == signature
+
+
+class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
+
+ def get_name(self):
+ return 'HMAC-SHA1'
+
+ def build_signature_base_string(self, oauth_request, consumer, token):
+ sig = (
+ escape(oauth_request.get_normalized_http_method()),
+ escape(oauth_request.get_normalized_http_url()),
+ escape(oauth_request.get_normalized_parameters()),
+ )
+
+ key = '%s&' % escape(consumer.secret)
+ if token:
+ key += escape(token.secret)
+ raw = '&'.join(sig)
+ return key, raw
+
+ def build_signature(self, oauth_request, consumer, token):
+ """Builds the base signature string."""
+ key, raw = self.build_signature_base_string(oauth_request, consumer,
+ token)
+
+ # HMAC object.
+ try:
+ import hashlib # 2.5
+ hashed = hmac.new(key, raw, hashlib.sha1)
+ except:
+ import sha # Deprecated
+ hashed = hmac.new(key, raw, sha)
+
+ # Calculate the digest base 64.
+ return binascii.b2a_base64(hashed.digest())[:-1]
+
+
+class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
+
+ def get_name(self):
+ return 'PLAINTEXT'
+
+ def build_signature_base_string(self, oauth_request, consumer, token):
+ """Concatenates the consumer key and secret."""
+ sig = '%s&' % escape(consumer.secret)
+ if token:
+ sig = sig + escape(token.secret)
+ return sig, sig
+
+ def build_signature(self, oauth_request, consumer, token):
+ key, raw = self.build_signature_base_string(oauth_request, consumer,
+ token)
return key \ No newline at end of file
diff --git a/forum_modules/oauthauth/settings.py b/forum_modules/oauthauth/settings.py
index d503fef6..67567b63 100755
--- a/forum_modules/oauthauth/settings.py
+++ b/forum_modules/oauthauth/settings.py
@@ -1,3 +1,3 @@
-TWITTER_CONSUMER_KEY = "sAAGwWILliIbgbrG37GztQ"
-TWITTER_CONSUMER_SECRET = "AZv0pHTZQaf4rxxZOrj3Jm1RKgmlV4MnYJAsrY7M0"
-
+TWITTER_CONSUMER_KEY = "sAAGwWILliIbgbrG37GztQ"
+TWITTER_CONSUMER_SECRET = "AZv0pHTZQaf4rxxZOrj3Jm1RKgmlV4MnYJAsrY7M0"
+
diff --git a/forum_modules/openidauth/authentication.py b/forum_modules/openidauth/authentication.py
index d34591e2..c04c44b9 100755
--- a/forum_modules/openidauth/authentication.py
+++ b/forum_modules/openidauth/authentication.py
@@ -1,196 +1,196 @@
-from consumer import OpenIdAbstractAuthConsumer
-from forum.authentication.base import ConsumerTemplateContext
-
-class GoogleAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- return 'https://www.google.com/accounts/o8/id'
-
-class GoogleAuthContext(ConsumerTemplateContext):
- mode = 'BIGICON'
- type = 'DIRECT'
- weight = 200
- human_name = 'Google'
- icon = '/media/images/openid/google.gif'
-
-
-
-class YahooAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- return 'http://yahoo.com/'
-
-class YahooAuthContext(ConsumerTemplateContext):
- mode = 'BIGICON'
- type = 'DIRECT'
- weight = 300
- human_name = 'Yahoo'
- icon = '/media/images/openid/yahoo.gif'
-
-
-
-class AolAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- uname = request.POST['input_field']
- return 'http://openid.aol.com/' + uname
-
-class AolAuthContext(ConsumerTemplateContext):
- mode = 'BIGICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'AOL screen name'
- }
- weight = 400
- human_name = 'AOL'
- icon = '/media/images/openid/aol.gif'
-
-
-class MyOpenIdAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.myopenid.com/" % blog_name
-
-class MyOpenIdAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'MyOpenID user name'
- }
- weight = 200
- human_name = 'MyOpenID'
- icon = '/media/images/openid/myopenid.ico'
-
-
-class FlickrAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://flickr.com/%s/" % blog_name
-
-class FlickrAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Flickr user name'
- }
- weight = 250
- human_name = 'Flickr'
- icon = '/media/images/openid/flickr.ico'
-
-
-class TechnoratiAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://technorati.com/people/technorati/%s/" % blog_name
-
-class TechnoratiAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Technorati user name'
- }
- weight = 260
- human_name = 'Technorati'
- icon = '/media/images/openid/technorati.ico'
-
-
-class WordpressAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.wordpress.com/" % blog_name
-
-class WordpressAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Wordpress blog name'
- }
- weight = 270
- human_name = 'Wordpress'
- icon = '/media/images/openid/wordpress.ico'
-
-
-class BloggerAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.blogspot.com/" % blog_name
-
-class BloggerAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Blogger blog name'
- }
- weight = 300
- human_name = 'Blogger'
- icon = '/media/images/openid/blogger.ico'
-
-
-class LiveJournalAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.livejournal.com/" % blog_name
-
-class LiveJournalAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'LiveJournal blog name'
- }
- weight = 310
- human_name = 'LiveJournal'
- icon = '/media/images/openid/livejournal.ico'
-
-
-class ClaimIdAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://claimid.com/%s" % blog_name
-
-class ClaimIdAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'ClaimID user name'
- }
- weight = 320
- human_name = 'ClaimID'
- icon = '/media/images/openid/claimid.ico'
-
-class VidoopAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.myvidoop.com/" % blog_name
-
-class VidoopAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Vidoop user name'
- }
- weight = 330
- human_name = 'Vidoop'
- icon = '/media/images/openid/vidoop.ico'
-
-class VerisignAuthConsumer(OpenIdAbstractAuthConsumer):
- def get_user_url(self, request):
- blog_name = request.POST['input_field']
- return "http://%s.pip.verisignlabs.com/" % blog_name
-
-class VerisignAuthContext(ConsumerTemplateContext):
- mode = 'SMALLICON'
- type = 'SIMPLE_FORM'
- simple_form_context = {
- 'your_what': 'Verisign user name'
- }
- weight = 340
- human_name = 'Verisign'
- icon = '/media/images/openid/verisign.ico'
-
-
-class OpenIdUrlAuthConsumer(OpenIdAbstractAuthConsumer):
- pass
-
-class OpenIdUrlAuthContext(ConsumerTemplateContext):
- mode = 'STACK_ITEM'
- weight = 300
- human_name = 'OpenId url'
- stack_item_template = 'modules/openidauth/openidurl.html'
+from consumer import OpenIdAbstractAuthConsumer
+from forum.authentication.base import ConsumerTemplateContext
+
+class GoogleAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ return 'https://www.google.com/accounts/o8/id'
+
+class GoogleAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 200
+ human_name = 'Google'
+ icon = '/media/images/openid/google.gif'
+
+
+
+class YahooAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ return 'http://yahoo.com/'
+
+class YahooAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 300
+ human_name = 'Yahoo'
+ icon = '/media/images/openid/yahoo.gif'
+
+
+
+class AolAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ uname = request.POST['input_field']
+ return 'http://openid.aol.com/' + uname
+
+class AolAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'AOL screen name'
+ }
+ weight = 400
+ human_name = 'AOL'
+ icon = '/media/images/openid/aol.gif'
+
+
+class MyOpenIdAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.myopenid.com/" % blog_name
+
+class MyOpenIdAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'MyOpenID user name'
+ }
+ weight = 200
+ human_name = 'MyOpenID'
+ icon = '/media/images/openid/myopenid.ico'
+
+
+class FlickrAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://flickr.com/%s/" % blog_name
+
+class FlickrAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Flickr user name'
+ }
+ weight = 250
+ human_name = 'Flickr'
+ icon = '/media/images/openid/flickr.ico'
+
+
+class TechnoratiAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://technorati.com/people/technorati/%s/" % blog_name
+
+class TechnoratiAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Technorati user name'
+ }
+ weight = 260
+ human_name = 'Technorati'
+ icon = '/media/images/openid/technorati.ico'
+
+
+class WordpressAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.wordpress.com/" % blog_name
+
+class WordpressAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Wordpress blog name'
+ }
+ weight = 270
+ human_name = 'Wordpress'
+ icon = '/media/images/openid/wordpress.ico'
+
+
+class BloggerAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.blogspot.com/" % blog_name
+
+class BloggerAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Blogger blog name'
+ }
+ weight = 300
+ human_name = 'Blogger'
+ icon = '/media/images/openid/blogger.ico'
+
+
+class LiveJournalAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.livejournal.com/" % blog_name
+
+class LiveJournalAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'LiveJournal blog name'
+ }
+ weight = 310
+ human_name = 'LiveJournal'
+ icon = '/media/images/openid/livejournal.ico'
+
+
+class ClaimIdAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://claimid.com/%s" % blog_name
+
+class ClaimIdAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'ClaimID user name'
+ }
+ weight = 320
+ human_name = 'ClaimID'
+ icon = '/media/images/openid/claimid.ico'
+
+class VidoopAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.myvidoop.com/" % blog_name
+
+class VidoopAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Vidoop user name'
+ }
+ weight = 330
+ human_name = 'Vidoop'
+ icon = '/media/images/openid/vidoop.ico'
+
+class VerisignAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.pip.verisignlabs.com/" % blog_name
+
+class VerisignAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Verisign user name'
+ }
+ weight = 340
+ human_name = 'Verisign'
+ icon = '/media/images/openid/verisign.ico'
+
+
+class OpenIdUrlAuthConsumer(OpenIdAbstractAuthConsumer):
+ pass
+
+class OpenIdUrlAuthContext(ConsumerTemplateContext):
+ mode = 'STACK_ITEM'
+ weight = 300
+ human_name = 'OpenId url'
+ stack_item_template = 'modules/openidauth/openidurl.html'
icon = '/media/images/openid/openid-inputicon.gif' \ No newline at end of file
diff --git a/forum_modules/openidauth/consumer.py b/forum_modules/openidauth/consumer.py
index 17d1378f..68035968 100755
--- a/forum_modules/openidauth/consumer.py
+++ b/forum_modules/openidauth/consumer.py
@@ -1,112 +1,112 @@
-from django.utils.html import escape
-from django.http import get_host
-
-from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
-import settings
-
-from openid.yadis import xri
-from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
-from openid.consumer.discover import DiscoveryFailure
-from openid.extensions.sreg import SRegRequest, SRegResponse
-from openid.extensions.ax import FetchRequest as AXFetchRequest, AttrInfo, FetchResponse as AXFetchResponse
-from django.utils.translation import ugettext as _
-
-from store import OsqaOpenIDStore
-
-class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
-
- def get_user_url(self, request):
- try:
- return request.POST['openid_identifier']
- except:
- raise NotImplementedError()
-
- def prepare_authentication_request(self, request, redirect_to):
- if not redirect_to.startswith('http://') or redirect_to.startswith('https://'):
- redirect_to = get_url_host(request) + redirect_to
-
- user_url = self.get_user_url(request)
-
- if xri.identifierScheme(user_url) == 'XRI' and getattr(
- settings, 'OPENID_DISALLOW_INAMES', False
- ):
- raise InvalidAuthentication('i-names are not supported')
-
- consumer = Consumer(request.session, OsqaOpenIDStore())
-
- try:
- auth_request = consumer.begin(user_url)
- except DiscoveryFailure:
- raise InvalidAuthentication(_('Sorry, but your input is not a valid OpenId'))
-
- #sreg = getattr(settings, 'OPENID_SREG', False)
-
- #if sreg:
- # s = SRegRequest()
- # for sarg in sreg:
- # if sarg.lower().lstrip() == "policy_url":
- # s.policy_url = sreg[sarg]
- # else:
- # for v in sreg[sarg].split(','):
- # s.requestField(field_name=v.lower().lstrip(), required=(sarg.lower().lstrip() == "required"))
- # auth_request.addExtension(s)
-
- #auth_request.addExtension(SRegRequest(required=['email']))
-
- if request.session.get('force_email_request', True):
- axr = AXFetchRequest()
- axr.add(AttrInfo("http://axschema.org/contact/email", 1, True, "email"))
- auth_request.addExtension(axr)
-
- trust_root = getattr(
- settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
- )
-
-
- return auth_request.redirectURL(trust_root, redirect_to)
-
- def process_authentication_request(self, request):
- consumer = Consumer(request.session, OsqaOpenIDStore())
-
- query_dict = dict([
- (k.encode('utf8'), v.encode('utf8')) for k, v in request.GET.items()
- ])
-
- #for i in query_dict.items():
- # print "%s : %s" % i
-
- url = get_url_host(request) + request.path
- openid_response = consumer.complete(query_dict, url)
-
- if openid_response.status == SUCCESS:
- if request.session.get('force_email_request', True):
- try:
- ax = AXFetchResponse.fromSuccessResponse(openid_response)
- email = ax.getExtensionArgs()['value.ext0.1']
- request.session['auth_email_request'] = email
- except Exception, e:
- pass
-
- return request.GET['openid.identity']
- elif openid_response.status == CANCEL:
- raise InvalidAuthentication(_('The OpenId authentication request was canceled'))
- elif openid_response.status == FAILURE:
- raise InvalidAuthentication(_('The OpenId authentication failed: ') + openid_response.message)
- elif openid_response.status == SETUP_NEEDED:
- raise InvalidAuthentication(_('Setup needed'))
- else:
- raise InvalidAuthentication(_('The OpenId authentication failed with an unknown status: ') + openid_response.status)
-
- def get_user_data(self, key):
- return {}
-
-def get_url_host(request):
- if request.is_secure():
- protocol = 'https'
- else:
- protocol = 'http'
- host = escape(get_host(request))
- return '%s://%s' % (protocol, host)
-
-def get_full_url(request):
+from django.utils.html import escape
+from django.http import get_host
+
+from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
+import settings
+
+from openid.yadis import xri
+from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
+from openid.consumer.discover import DiscoveryFailure
+from openid.extensions.sreg import SRegRequest, SRegResponse
+from openid.extensions.ax import FetchRequest as AXFetchRequest, AttrInfo, FetchResponse as AXFetchResponse
+from django.utils.translation import ugettext as _
+
+from store import OsqaOpenIDStore
+
+class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
+
+ def get_user_url(self, request):
+ try:
+ return request.POST['openid_identifier']
+ except:
+ raise NotImplementedError()
+
+ def prepare_authentication_request(self, request, redirect_to):
+ if not redirect_to.startswith('http://') or redirect_to.startswith('https://'):
+ redirect_to = get_url_host(request) + redirect_to
+
+ user_url = self.get_user_url(request)
+
+ if xri.identifierScheme(user_url) == 'XRI' and getattr(
+ settings, 'OPENID_DISALLOW_INAMES', False
+ ):
+ raise InvalidAuthentication('i-names are not supported')
+
+ consumer = Consumer(request.session, OsqaOpenIDStore())
+
+ try:
+ auth_request = consumer.begin(user_url)
+ except DiscoveryFailure:
+ raise InvalidAuthentication(_('Sorry, but your input is not a valid OpenId'))
+
+ #sreg = getattr(settings, 'OPENID_SREG', False)
+
+ #if sreg:
+ # s = SRegRequest()
+ # for sarg in sreg:
+ # if sarg.lower().lstrip() == "policy_url":
+ # s.policy_url = sreg[sarg]
+ # else:
+ # for v in sreg[sarg].split(','):
+ # s.requestField(field_name=v.lower().lstrip(), required=(sarg.lower().lstrip() == "required"))
+ # auth_request.addExtension(s)
+
+ #auth_request.addExtension(SRegRequest(required=['email']))
+
+ if request.session.get('force_email_request', True):
+ axr = AXFetchRequest()
+ axr.add(AttrInfo("http://axschema.org/contact/email", 1, True, "email"))
+ auth_request.addExtension(axr)
+
+ trust_root = getattr(
+ settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
+ )
+
+
+ return auth_request.redirectURL(trust_root, redirect_to)
+
+ def process_authentication_request(self, request):
+ consumer = Consumer(request.session, OsqaOpenIDStore())
+
+ query_dict = dict([
+ (k.encode('utf8'), v.encode('utf8')) for k, v in request.GET.items()
+ ])
+
+ #for i in query_dict.items():
+ # print "%s : %s" % i
+
+ url = get_url_host(request) + request.path
+ openid_response = consumer.complete(query_dict, url)
+
+ if openid_response.status == SUCCESS:
+ if request.session.get('force_email_request', True):
+ try:
+ ax = AXFetchResponse.fromSuccessResponse(openid_response)
+ email = ax.getExtensionArgs()['value.ext0.1']
+ request.session['auth_email_request'] = email
+ except Exception, e:
+ pass
+
+ return request.GET['openid.identity']
+ elif openid_response.status == CANCEL:
+ raise InvalidAuthentication(_('The OpenId authentication request was canceled'))
+ elif openid_response.status == FAILURE:
+ raise InvalidAuthentication(_('The OpenId authentication failed: ') + openid_response.message)
+ elif openid_response.status == SETUP_NEEDED:
+ raise InvalidAuthentication(_('Setup needed'))
+ else:
+ raise InvalidAuthentication(_('The OpenId authentication failed with an unknown status: ') + openid_response.status)
+
+ def get_user_data(self, key):
+ return {}
+
+def get_url_host(request):
+ if request.is_secure():
+ protocol = 'https'
+ else:
+ protocol = 'http'
+ host = escape(get_host(request))
+ return '%s://%s' % (protocol, host)
+
+def get_full_url(request):
return get_url_host(request) + request.get_full_path() \ No newline at end of file
diff --git a/forum_modules/openidauth/models.py b/forum_modules/openidauth/models.py
index d6cc991e..d76902df 100755
--- a/forum_modules/openidauth/models.py
+++ b/forum_modules/openidauth/models.py
@@ -1,26 +1,26 @@
-from django.db import models
-
-class OpenIdNonce(models.Model):
- server_url = models.URLField()
- timestamp = models.IntegerField()
- salt = models.CharField( max_length=50 )
-
- def __unicode__(self):
- return "Nonce: %s" % self.nonce
-
- class Meta:
- app_label = 'forum'
-
-class OpenIdAssociation(models.Model):
- server_url = models.TextField(max_length=2047)
- handle = models.CharField(max_length=255)
- secret = models.TextField(max_length=255) # Stored base64 encoded
- issued = models.IntegerField()
- lifetime = models.IntegerField()
- assoc_type = models.TextField(max_length=64)
-
- def __unicode__(self):
- return "Association: %s, %s" % (self.server_url, self.handle)
-
- class Meta:
- app_label = 'forum'
+from django.db import models
+
+class OpenIdNonce(models.Model):
+ server_url = models.URLField()
+ timestamp = models.IntegerField()
+ salt = models.CharField( max_length=50 )
+
+ def __unicode__(self):
+ return "Nonce: %s" % self.nonce
+
+ class Meta:
+ app_label = 'forum'
+
+class OpenIdAssociation(models.Model):
+ server_url = models.TextField(max_length=2047)
+ handle = models.CharField(max_length=255)
+ secret = models.TextField(max_length=255) # Stored base64 encoded
+ issued = models.IntegerField()
+ lifetime = models.IntegerField()
+ assoc_type = models.TextField(max_length=64)
+
+ def __unicode__(self):
+ return "Association: %s, %s" % (self.server_url, self.handle)
+
+ class Meta:
+ app_label = 'forum'
diff --git a/forum_modules/openidauth/settings.py b/forum_modules/openidauth/settings.py
index 3b1c2eec..f7c641a1 100755
--- a/forum_modules/openidauth/settings.py
+++ b/forum_modules/openidauth/settings.py
@@ -1,9 +1,9 @@
-OPENID_SREG = {
- "required": "nickname, email",
- "optional": "postcode, country",
- "policy_url": ""
-}
-OPENID_AX = [
- {"type_uri": "http://axschema.org/contact/email", "count": 1, "required": True, "alias": "email"},
- {"type_uri": "fullname", "count":1 , "required": False, "alias": "fullname"}
+OPENID_SREG = {
+ "required": "nickname, email",
+ "optional": "postcode, country",
+ "policy_url": ""
+}
+OPENID_AX = [
+ {"type_uri": "http://axschema.org/contact/email", "count": 1, "required": True, "alias": "email"},
+ {"type_uri": "fullname", "count":1 , "required": False, "alias": "fullname"}
] \ No newline at end of file
diff --git a/forum_modules/openidauth/store.py b/forum_modules/openidauth/store.py
index 93d38a5b..fa61ea6d 100755
--- a/forum_modules/openidauth/store.py
+++ b/forum_modules/openidauth/store.py
@@ -1,79 +1,79 @@
-import time, base64, md5
-
-from openid.store import nonce as oid_nonce
-from openid.store.interface import OpenIDStore
-from openid.association import Association as OIDAssociation
-from django.conf import settings
-
-from models import OpenIdNonce as Nonce, OpenIdAssociation as Association
-
-class OsqaOpenIDStore(OpenIDStore):
- def __init__(self):
- self.max_nonce_age = 6 * 60 * 60 # Six hours
-
- def storeAssociation(self, server_url, association):
- assoc = Association(
- server_url = server_url,
- handle = association.handle,
- secret = base64.encodestring(association.secret),
- issued = association.issued,
- lifetime = association.issued,
- assoc_type = association.assoc_type
- )
- assoc.save()
-
- def getAssociation(self, server_url, handle=None):
- assocs = []
- if handle is not None:
- assocs = Association.objects.filter(
- server_url = server_url, handle = handle
- )
- else:
- assocs = Association.objects.filter(
- server_url = server_url
- )
- if not assocs:
- return None
- associations = []
- for assoc in assocs:
- association = OIDAssociation(
- assoc.handle, base64.decodestring(assoc.secret), assoc.issued,
- assoc.lifetime, assoc.assoc_type
- )
- if association.getExpiresIn() == 0:
- self.removeAssociation(server_url, assoc.handle)
- else:
- associations.append((association.issued, association))
- if not associations:
- return None
- return associations[-1][1]
-
- def removeAssociation(self, server_url, handle):
- assocs = list(Association.objects.filter(
- server_url = server_url, handle = handle
- ))
- assocs_exist = len(assocs) > 0
- for assoc in assocs:
- assoc.delete()
- return assocs_exist
-
- def storeNonce(self, nonce):
- nonce, created = Nonce.objects.get_or_create(
- nonce = nonce, defaults={'expires': int(time.time())}
- )
-
- def useNonce(self, server_url, timestamp, salt):
- if abs(timestamp - time.time()) > oid_nonce.SKEW:
- return False
-
- try:
- nonce = Nonce( server_url=server_url, timestamp=timestamp, salt=salt)
- nonce.save()
- except:
- raise
- else:
- return 1
-
- def getAuthKey(self):
- # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY
- return md5.new(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN]
+import time, base64, md5
+
+from openid.store import nonce as oid_nonce
+from openid.store.interface import OpenIDStore
+from openid.association import Association as OIDAssociation
+from django.conf import settings
+
+from models import OpenIdNonce as Nonce, OpenIdAssociation as Association
+
+class OsqaOpenIDStore(OpenIDStore):
+ def __init__(self):
+ self.max_nonce_age = 6 * 60 * 60 # Six hours
+
+ def storeAssociation(self, server_url, association):
+ assoc = Association(
+ server_url = server_url,
+ handle = association.handle,
+ secret = base64.encodestring(association.secret),
+ issued = association.issued,
+ lifetime = association.issued,
+ assoc_type = association.assoc_type
+ )
+ assoc.save()
+
+ def getAssociation(self, server_url, handle=None):
+ assocs = []
+ if handle is not None:
+ assocs = Association.objects.filter(
+ server_url = server_url, handle = handle
+ )
+ else:
+ assocs = Association.objects.filter(
+ server_url = server_url
+ )
+ if not assocs:
+ return None
+ associations = []
+ for assoc in assocs:
+ association = OIDAssociation(
+ assoc.handle, base64.decodestring(assoc.secret), assoc.issued,
+ assoc.lifetime, assoc.assoc_type
+ )
+ if association.getExpiresIn() == 0:
+ self.removeAssociation(server_url, assoc.handle)
+ else:
+ associations.append((association.issued, association))
+ if not associations:
+ return None
+ return associations[-1][1]
+
+ def removeAssociation(self, server_url, handle):
+ assocs = list(Association.objects.filter(
+ server_url = server_url, handle = handle
+ ))
+ assocs_exist = len(assocs) > 0
+ for assoc in assocs:
+ assoc.delete()
+ return assocs_exist
+
+ def storeNonce(self, nonce):
+ nonce, created = Nonce.objects.get_or_create(
+ nonce = nonce, defaults={'expires': int(time.time())}
+ )
+
+ def useNonce(self, server_url, timestamp, salt):
+ if abs(timestamp - time.time()) > oid_nonce.SKEW:
+ return False
+
+ try:
+ nonce = Nonce( server_url=server_url, timestamp=timestamp, salt=salt)
+ nonce.save()
+ except:
+ raise
+ else:
+ return 1
+
+ def getAuthKey(self):
+ # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY
+ return md5.new(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN]
diff --git a/forum_modules/pgfulltext/__init__.py b/forum_modules/pgfulltext/__init__.py
index ec4892c7..8215e1a9 100755
--- a/forum_modules/pgfulltext/__init__.py
+++ b/forum_modules/pgfulltext/__init__.py
@@ -1,9 +1,9 @@
-NAME = 'Postgresql Full Text Search'
-DESCRIPTION = "Enables PostgreSql full text search functionality."
-
-try:
- import psycopg2
- CAN_ENABLE = True
-except:
- CAN_ENABLE = False
+NAME = 'Postgresql Full Text Search'
+DESCRIPTION = "Enables PostgreSql full text search functionality."
+
+try:
+ import psycopg2
+ CAN_ENABLE = True
+except:
+ CAN_ENABLE = False
\ No newline at end of file
diff --git a/forum_modules/pgfulltext/handlers.py b/forum_modules/pgfulltext/handlers.py
index 17fb1762..f4a7a3b2 100755
--- a/forum_modules/pgfulltext/handlers.py
+++ b/forum_modules/pgfulltext/handlers.py
@@ -1,11 +1,11 @@
-from forum.models import Question
-
-def question_search(keywords, orderby):
- return 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]
+from forum.models import Question
+
+def question_search(keywords, orderby):
+ return 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(orderby, '-ranking') \ No newline at end of file
diff --git a/forum_modules/pgfulltext/management.py b/forum_modules/pgfulltext/management.py
index 89eb1395..487580ff 100755
--- a/forum_modules/pgfulltext/management.py
+++ b/forum_modules/pgfulltext/management.py
@@ -1,29 +1,29 @@
-import os
-
-from django.db import connection, transaction
-from django.conf import settings
-
-import forum.models
-
-if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ):
- from django.db.models.signals import post_syncdb
-
- def setup_pgfulltext(sender, **kwargs):
- if sender == forum.models:
- install_pg_fts()
-
- post_syncdb.connect(setup_pgfulltext)
-
-def install_pg_fts():
- f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r')
-
- try:
- cursor = connection.cursor()
- cursor.execute(f.read())
- transaction.commit_unless_managed()
- except:
- pass
- finally:
- cursor.close()
-
- f.close()
+import os
+
+from django.db import connection, transaction
+from django.conf import settings
+
+import forum.models
+
+if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ):
+ from django.db.models.signals import post_syncdb
+
+ def setup_pgfulltext(sender, **kwargs):
+ if sender == forum.models:
+ install_pg_fts()
+
+ post_syncdb.connect(setup_pgfulltext)
+
+def install_pg_fts():
+ f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r')
+
+ try:
+ cursor = connection.cursor()
+ cursor.execute(f.read())
+ transaction.commit_unless_managed()
+ except:
+ pass
+ finally:
+ cursor.close()
+
+ f.close()
diff --git a/forum_modules/sphinxfulltext/dependencies.py b/forum_modules/sphinxfulltext/dependencies.py
index 5ddb91d2..046ebfc5 100755
--- a/forum_modules/sphinxfulltext/dependencies.py
+++ b/forum_modules/sphinxfulltext/dependencies.py
@@ -1,2 +1,2 @@
-DJANGO_APPS = ('djangosphinx', )
-
+DJANGO_APPS = ('djangosphinx', )
+
diff --git a/forum_modules/sphinxfulltext/handlers.py b/forum_modules/sphinxfulltext/handlers.py
index 665c9380..226acf72 100755
--- a/forum_modules/sphinxfulltext/handlers.py
+++ b/forum_modules/sphinxfulltext/handlers.py
@@ -1,4 +1,4 @@
-from forum.models import Question
-
-def question_search(keywords, orderby):
+from forum.models import Question
+
+def question_search(keywords, orderby):
return Question.search.query(keywords) \ No newline at end of file
diff --git a/forum_modules/sphinxfulltext/models.py b/forum_modules/sphinxfulltext/models.py
index 66b8ddf9..9db4aa86 100755
--- a/forum_modules/sphinxfulltext/models.py
+++ b/forum_modules/sphinxfulltext/models.py
@@ -1,10 +1,10 @@
-from forum.models import Question
-from django.conf import settings
-from djangosphinx.manager import SphinxSearch
-
-
-Question.add_to_class('search', SphinxSearch(
- index=' '.join(settings.SPHINX_SEARCH_INDICES),
- mode='SPH_MATCH_ALL',
- )
- )
+from forum.models import Question
+from django.conf import settings
+from djangosphinx.manager import SphinxSearch
+
+
+Question.add_to_class('search', SphinxSearch(
+ index=' '.join(settings.SPHINX_SEARCH_INDICES),
+ mode='SPH_MATCH_ALL',
+ )
+ )
diff --git a/forum_modules/sphinxfulltext/settings.py b/forum_modules/sphinxfulltext/settings.py
index c98de7b3..7c2da124 100755
--- a/forum_modules/sphinxfulltext/settings.py
+++ b/forum_modules/sphinxfulltext/settings.py
@@ -1,5 +1,5 @@
-SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
-SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
-#last item, especially if you have just one :)
-SPHINX_SERVER='localhost'
-SPHINX_PORT=3312
+SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
+#last item, especially if you have just one :)
+SPHINX_SERVER='localhost'
+SPHINX_PORT=3312
diff --git a/settings_local.py.dist b/settings_local.py.dist
index 3a71b389..52c54bbb 100755
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -1,110 +1,110 @@
-# encoding:utf-8
-import os.path
-from django.utils.translation import ugettext as _
-
-def check_local_setting(name, value):
- local_vars = locals()
- if name in local_vars and local_vars[name] == value:
- return True
- else:
- return False
-
-SITE_SRC_ROOT = os.path.dirname(__file__)
-LOG_FILENAME = 'django.osqa.log'
-
-#for logging
-import logging
-logging.basicConfig(
- filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME),
- level=logging.DEBUG,
- format='%(pathname)s TIME: %(asctime)s MSG: %(filename)s:%(funcName)s:%(lineno)d %(message)s',
-)
-
-#ADMINS and MANAGERS
-ADMINS = (('Forum Admin', 'forum@example.com'),)
-MANAGERS = ADMINS
-
-#DEBUG SETTINGS
-DEBUG = False
-TEMPLATE_DEBUG = DEBUG
-INTERNAL_IPS = ('127.0.0.1',)
-
-DATABASE_NAME = '' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_ENGINE = '' #mysql, etc
-DATABASE_HOST = ''
-DATABASE_PORT = ''
-
-#Moved from settings.py for better organization. (please check it up to clean up settings.py)
-
-#email server settings
-SERVER_EMAIL = ''
-DEFAULT_FROM_EMAIL = ''
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = ''
-EMAIL_SUBJECT_PREFIX = '[OSQA] '
-EMAIL_HOST='osqa.net'
-EMAIL_PORT='25'
-EMAIL_USE_TLS=False
-
-#LOCALIZATIONS
-TIME_ZONE = 'America/New_York'
-
-###########################
-#
-# this will allow running your forum with url like http://site.com/forum
-#
-# FORUM_SCRIPT_ALIAS = 'forum/'
-#
-FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
-
-
-#OTHER SETTINGS
-APP_TITLE = u'OSQA: Open Source Q&A Forum'
-APP_SHORT_NAME = u'OSQA'
-APP_KEYWORDS = u'OSQA,CNPROG,forum,community'
-APP_DESCRIPTION = u'Ask and answer questions.'
-APP_INTRO = u'<p>Ask and answer questions, make the world better!</p>'
-APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.'
-LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
-GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc
-
-USE_I18N = True
-LANGUAGE_CODE = 'en'
-EMAIL_VALIDATION = 'off' #string - on|off
-MIN_USERNAME_LENGTH = 1
-EMAIL_UNIQUE = False
-APP_URL = 'http://osqa.net' #used by email notif system and RSS
-GOOGLE_SITEMAP_CODE = ''
-GOOGLE_ANALYTICS_KEY = ''
-BOOKS_ON = False
-WIKI_ON = True
-USE_EXTERNAL_LEGACY_LOGIN = False
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
-EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = '<span class="orange">OSQA</span>'
-FEEDBACK_SITE_URL = None #None or url
-EDITABLE_SCREEN_NAME = False #True or False - can user change screen name?
-
-DJANGO_VERSION = 1.1
-RESOURCE_REVISION=4
-
-USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
-#also sphinx search engine and djangosphinxs app must be installed
-#sample sphinx configuration file is /sphinx/sphinx.conf
-SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
-SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
-#last item, especially if you have just one :)
-SPHINX_SERVER='localhost'
-SPHINX_PORT=3312
-
-#please get these at recaptcha.net
-RECAPTCHA_PRIVATE_KEY='...'
-RECAPTCHA_PUBLIC_KEY='...'
-OSQA_DEFAULT_SKIN = 'default'
-
-#Facebook settings
-USE_FB_CONNECT=False
-FB_API_KEY='' #your api key from facebook
-FB_SECRET='' #your application secret
+# encoding:utf-8
+import os.path
+from django.utils.translation import ugettext as _
+
+def check_local_setting(name, value):
+ local_vars = locals()
+ if name in local_vars and local_vars[name] == value:
+ return True
+ else:
+ return False
+
+SITE_SRC_ROOT = os.path.dirname(__file__)
+LOG_FILENAME = 'django.osqa.log'
+
+#for logging
+import logging
+logging.basicConfig(
+ filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME),
+ level=logging.DEBUG,
+ format='%(pathname)s TIME: %(asctime)s MSG: %(filename)s:%(funcName)s:%(lineno)d %(message)s',
+)
+
+#ADMINS and MANAGERS
+ADMINS = (('Forum Admin', 'forum@example.com'),)
+MANAGERS = ADMINS
+
+#DEBUG SETTINGS
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+INTERNAL_IPS = ('127.0.0.1',)
+
+DATABASE_NAME = '' # Or path to database file if using sqlite3.
+DATABASE_USER = '' # Not used with sqlite3.
+DATABASE_PASSWORD = '' # Not used with sqlite3.
+DATABASE_ENGINE = '' #mysql, etc
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+#Moved from settings.py for better organization. (please check it up to clean up settings.py)
+
+#email server settings
+SERVER_EMAIL = ''
+DEFAULT_FROM_EMAIL = ''
+EMAIL_HOST_USER = ''
+EMAIL_HOST_PASSWORD = ''
+EMAIL_SUBJECT_PREFIX = '[OSQA] '
+EMAIL_HOST='osqa.net'
+EMAIL_PORT='25'
+EMAIL_USE_TLS=False
+
+#LOCALIZATIONS
+TIME_ZONE = 'America/New_York'
+
+###########################
+#
+# this will allow running your forum with url like http://site.com/forum
+#
+# FORUM_SCRIPT_ALIAS = 'forum/'
+#
+FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
+
+
+#OTHER SETTINGS
+APP_TITLE = u'OSQA: Open Source Q&A Forum'
+APP_SHORT_NAME = u'OSQA'
+APP_KEYWORDS = u'OSQA,CNPROG,forum,community'
+APP_DESCRIPTION = u'Ask and answer questions.'
+APP_INTRO = u'<p>Ask and answer questions, make the world better!</p>'
+APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.'
+LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
+GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc
+
+USE_I18N = True
+LANGUAGE_CODE = 'en'
+EMAIL_VALIDATION = 'off' #string - on|off
+MIN_USERNAME_LENGTH = 1
+EMAIL_UNIQUE = False
+APP_URL = 'http://osqa.net' #used by email notif system and RSS
+GOOGLE_SITEMAP_CODE = ''
+GOOGLE_ANALYTICS_KEY = ''
+BOOKS_ON = False
+WIKI_ON = True
+USE_EXTERNAL_LEGACY_LOGIN = False
+EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
+EXTERNAL_LEGACY_LOGIN_PORT = 80
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = '<span class="orange">OSQA</span>'
+FEEDBACK_SITE_URL = None #None or url
+EDITABLE_SCREEN_NAME = False #True or False - can user change screen name?
+
+DJANGO_VERSION = 1.1
+RESOURCE_REVISION=4
+
+USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
+#also sphinx search engine and djangosphinxs app must be installed
+#sample sphinx configuration file is /sphinx/sphinx.conf
+SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
+#last item, especially if you have just one :)
+SPHINX_SERVER='localhost'
+SPHINX_PORT=3312
+
+#please get these at recaptcha.net
+RECAPTCHA_PRIVATE_KEY='...'
+RECAPTCHA_PUBLIC_KEY='...'
+OSQA_DEFAULT_SKIN = 'default'
+
+#Facebook settings
+USE_FB_CONNECT=False
+FB_API_KEY='' #your api key from facebook
+FB_SECRET='' #your application secret