Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added anonymous posting, per-question subscription and fixes by Pothe…

…rs and some more, see development.log
  • Loading branch information...
commit 56f02677940a00f04780cfb361fc65950e910bf8 1 parent b1e66ad
@evgenyfadeev evgenyfadeev authored
Showing with 3,134 additions and 1,958 deletions.
  1. +33 −2 context.py
  2. +34 −1 development.log
  3. +45 −37 django_authopenid/forms.py
  4. +1 −1  django_authopenid/middleware.py
  5. +4 −1 django_authopenid/urls.py
  6. +185 −23 django_authopenid/views.py
  7. +4 −1 forum/admin.py
  8. +7 −7 forum/feed.py
  9. +29 −13 forum/forms.py
  10. +1 −0  forum/management/commands/once_award_badges.py
  11. +154 −5 forum/models.py
  12. +2 −2 forum/templatetags/extra_tags.py
  13. +0 −1  forum/user.py
  14. +194 −67 forum/views.py
  15. BIN  locale/en/LC_MESSAGES/django.mo
  16. +913 −862 locale/en/LC_MESSAGES/django.po
  17. BIN  locale/es/LC_MESSAGES/django.mo
  18. +807 −522 locale/es/LC_MESSAGES/django.po
  19. BIN  locale/zh_CN/LC_MESSAGES/django.mo
  20. +26 −7 locale/zh_CN/LC_MESSAGES/django.po
  21. +11 −4 settings.py
  22. +3 −3 settings_local.py
  23. +1 −1  templates/404.html
  24. +2 −2 templates/500.html
  25. +1 −1  templates/answer_edit.html
  26. +1 −1  templates/answer_edit_tips.html
  27. +21 −31 templates/ask.html
  28. +73 −27 templates/authopenid/changeemail.html
  29. +2 −0  templates/authopenid/changeopenid.html
  30. +2 −0  templates/authopenid/changepw.html
  31. +9 −8 templates/authopenid/complete.html
  32. +9 −8 templates/authopenid/confirm_email.txt
  33. +2 −0  templates/authopenid/delete.html
  34. +2 −1  templates/authopenid/failure.html
  35. +2 −0  templates/authopenid/sendpw.html
  36. +9 −9 templates/authopenid/sendpw_email.txt
  37. +2 −0  templates/authopenid/settings.html
  38. +102 −22 templates/authopenid/signin.html
  39. +2 −0  templates/authopenid/signup.html
  40. +1 −1  templates/badge.html
  41. +8 −7 templates/badges.html
  42. +15 −14 templates/base.html
  43. +16 −11 templates/base_content.html
  44. +1 −1  templates/book.html
  45. +1 −1  templates/close.html
  46. +5 −3 templates/content/js/com.cnprog.i18n.js
  47. +19 −4 templates/content/js/com.cnprog.post.js
  48. +1 −1  templates/content/js/com.cnprog.utils.js
  49. +1 −1  templates/content/style/openid.css
  50. +143 −69 templates/content/style/style.css
  51. +129 −94 templates/faq.html
  52. +1 −1  templates/footer.html
  53. +18 −19 templates/header.html
  54. +6 −4 templates/index.html
  55. +2 −2 templates/logout.html
  56. +2 −0  templates/pagesize.html
  57. +14 −7 templates/question.html
  58. +1 −1  templates/question_edit.html
  59. +11 −14 templates/question_retag.html
  60. +18 −10 templates/questions.html
  61. +1 −1  templates/revisions_answer.html
  62. +1 −2  templates/revisions_question.html
  63. +1 −1  templates/unanswered.html
  64. +2 −4 templates/user.html
  65. +1 −1  templates/user_edit.html
  66. +1 −1  templates/user_favorites.html
  67. +2 −0  templates/user_info.html
  68. +9 −6 templates/user_preferences.html
  69. +1 −1  templates/user_recent.html
  70. +1 −1  templates/user_reputation.html
  71. +1 −1  templates/user_responses.html
  72. +1 −1  templates/user_stats.html
  73. +0 −2  templates/user_tabs.html
  74. +1 −1  templates/user_votes.html
  75. +3 −0  urls.py
View
35 context.py
@@ -1,9 +1,40 @@
from django.conf import settings
def application_settings(context):
- return {
+ my_settings = {
'APP_TITLE' : settings.APP_TITLE,
'APP_URL' : settings.APP_URL,
'APP_KEYWORDS' : settings.APP_KEYWORDS,
'APP_DESCRIPTION' : settings.APP_DESCRIPTION,
- 'APP_INTRO' : settings.APP_INTRO
+ 'APP_INTRO' : settings.APP_INTRO,
+ 'EMAIL_VALIDATION': settings.EMAIL_VALIDATION,
+ 'LANGUAGE_CODE': settings.LANGUAGE_CODE,
+ 'GOOGLE_SITEMAP_CODE':settings.GOOGLE_SITEMAP_CODE,
+ 'GOOGLE_ANALYTICS_KEY':settings.GOOGLE_ANALYTICS_KEY,
}
+ return {'settings':my_settings}
+
+def auth_processor(request):
+ """
+ Returns context variables required by apps that use Django's authentication
+ system.
+
+ If there is no 'user' attribute in the request, uses AnonymousUser (from
+ django.contrib.auth).
+ """
+ if hasattr(request, 'user'):
+ user = request.user
+ if user.is_authenticated():
+ messages = user.message_set.all()
+ else:
+ messages = None
+ else:
+ from django.contrib.auth.models import AnonymousUser
+ user = AnonymousUser()
+ messages = None
+
+ from django.core.context_processors import PermWrapper
+ return {
+ 'user': user,
+ 'messages': messages,
+ 'perms': PermWrapper(user),
+ }
View
35 development.log
@@ -1,4 +1,37 @@
-# development
+==Aug 5, 2009 Evgeny==
+===Interface changes===
+Merged in my code that:
+* allows anonymous posting of Q&A and then login
+* per-question email notifications via 'send_email_alerts' command
+* allows space character in username
+* improves openid login
+* makes notification messages sticky - have to click "x" to dismiss
+* unanswered questions are now those with no accepted answer
+* added following setting parameters:
+
+settings.MIN_USERNAME_LENGTH = 1
+settings.EMAIL_UNIQUE = True|False
+settings.EMAIL_VALIDATION = 'on'|'off' #thought of maybe adding other options so type is string
+settings.GOOGLE_SITEMAP_CODE = <string>
+settings.GOOGLE_ANALYTICS_KEY = <string>
+
+===Fixes===
+* fixed incorrect answer count issue in question.html
+* translated Twittwer stuff in user_preferences.html
+* translated question_retag.html, except one phrase
+* added Adolfo's python2.4 fix
+* fixed template debugging comments so that they don't break page layout
+* reorganized header template so that it takes less vertical space
+
+===Code changes===
+* wrapped template context settings into a single settings dictionary
+* on login anonymous session is recorded so that anonymously posted questions (if any) could be found later
+* added models: AnonymousQuestion, AnonymousAnswer, EmailFeed (for notifications)
+* User model has two new fields email_key - 32 byte hex hash and email_isvalid - boolean
+ file sql_scripts/update_2009_07_05_EF.sql will make upgrade to the live database
+* added auth_processor to context.py which loads notification messages without deleting them
+ NOTE: default django auth processor must be removed!
+* added ajax actions questionSubscribeUpdates/questionUnsubscribeUpdates
==Aug 4 2009, Evgeny==
===Changes===
View
82 django_authopenid/forms.py
@@ -158,7 +158,7 @@ def clean_username(self):
raise forms.ValidationError(_('invalid user name'))
if self.cleaned_data['username'] in RESERVED_NAMES:
raise forms.ValidationError(_('sorry, this name can not be used, please try another'))
- if len(self.cleaned_data['username']) < 3:
+ if len(self.cleaned_data['username']) < settings.MIN_USERNAME_LENGTH:
raise forms.ValidationError(_('username too short'))
try:
user = User.objects.get(
@@ -171,19 +171,22 @@ def clean_username(self):
raise forms.ValidationError(_('this name is already in use - please try anoter'))
def clean_email(self):
- """For security reason one unique email in database"""
+ """Optionally, for security reason one unique email in database"""
if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
+ if settings.EMAIL_UNIQUE == True:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(u'There is already more than one \
+ account registered with that e-mail address. Please try \
+ another.')
+ raise forms.ValidationError(_("This email is already \
+ registered in our database. Please choose another."))
+ else:
return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(u'There is already more than one \
- account registered with that e-mail address. Please try \
- another.')
- raise forms.ValidationError(_("This email is already \
- registered in our database. Please choose another."))
-
+ #what if not???
class OpenidVerifyForm(forms.Form):
""" openid verify form (associate an openid with an account) """
@@ -204,8 +207,7 @@ def clean_username(self):
""" validate username """
if 'username' in self.cleaned_data:
if not username_re.search(self.cleaned_data['username']):
- raise forms.ValidationError(_("Usernames can only contain \
- letters, numbers and underscores"))
+ raise forms.ValidationError(_('invalid user name'))
try:
user = User.objects.get(
username__exact = self.cleaned_data['username']
@@ -241,7 +243,7 @@ def get_user(self):
attrs_dict = { 'class': 'required' }
-username_re = re.compile(r'^\w+$')
+username_re = re.compile(r'^[\w ]+$')
class RegistrationForm(forms.Form):
""" legacy registration form """
@@ -286,17 +288,20 @@ def clean_email(self):
""" validate if email exist in database
:return: raise error if it exist """
if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
+ if settings.EMAIL_UNIQUE == True:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(u'There is already more than one \
+ account registered with that e-mail address. Please try \
+ another.')
+ raise forms.ValidationError(u'This email is already registered \
+ in our database. Please choose another.')
+ else:
return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(u'There is already more than one \
- account registered with that e-mail address. Please try \
- another.')
- raise forms.ValidationError(u'This email is already registered \
- in our database. Please choose another.')
- return self.cleaned_data['email']
+ #what if not?
def clean_password2(self):
"""
@@ -361,18 +366,21 @@ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, \
def clean_email(self):
""" check if email don't exist """
if 'email' in self.cleaned_data:
- if self.user.email != self.cleaned_data['email']:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(u'There is already more than one \
- account registered with that e-mail address. Please try \
- another.')
- raise forms.ValidationError(u'This email is already registered \
- in our database. Please choose another.')
- return self.cleaned_data['email']
+ if settings.EMAIL_UNIQUE == True:
+ if self.user.email != self.cleaned_data['email']:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(u'There is already more than one \
+ account registered with that e-mail address. Please try \
+ another.')
+ raise forms.ValidationError(u'This email is already registered \
+ in our database. Please choose another.')
+ else:
+ return self.cleaned_data['email']
+ #what if not?
def clean_password(self):
View
2  django_authopenid/middleware.py
@@ -21,4 +21,4 @@ def process_response(self, request, response):
mimeparse.best_match(['text/html', 'application/xrds+xml'],
request.META['HTTP_ACCEPT']) == 'application/xrds+xml':
return HttpResponseRedirect(reverse('yadis_xrdf'))
- return response
+ return response
View
5 django_authopenid/urls.py
@@ -7,6 +7,8 @@
url(r'^yadis.xrdf$', 'xrdf', name='yadis_xrdf'),
# manage account registration
url(r'^%s$' % _('signin/'), 'signin', name='user_signin'),
+ url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}),
+ url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}),
url(r'^%s$' % _('signout/'), 'signout', name='user_signout'),
url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin',
name='user_complete_signin'),
@@ -21,7 +23,8 @@
# manage account settings
#url(r'^$', 'account_settings', name='user_account_settings'),
#url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'),
- #url(r'^%s$' % _('email/'), 'changeemail', name='user_changeemail'),
+ url(r'^%s$' % 'email/', 'changeemail', name='user_changeemail',kwargs = {'action':'change'}),
+ url(r'^%s%s$' % ('email/','validate/'), 'changeemail', name='user_changeemail',kwargs = {'action':'validate'}),
#url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'),
url(r'^%s$' % _('delete/'), 'delete', name='user_delete'),
)
View
208 django_authopenid/views.py
@@ -30,12 +30,12 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-from django.http import HttpResponseRedirect, get_host
+from django.http import HttpResponseRedirect, get_host, Http404
from django.shortcuts import render_to_response as render
from django.template import RequestContext, loader, Context
from django.conf import settings
from django.contrib.auth.models import User
-from django.contrib.auth import login, logout
+from django.contrib.auth import logout #for login I've added wrapper below - called login
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.utils.encoding import smart_unicode
@@ -44,6 +44,7 @@
from django.contrib.sites.models import Site
from django.utils.http import urlquote_plus
from django.core.mail import send_mail
+from django.views.defaults import server_error
from openid.consumer.consumer import Consumer, \
SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
@@ -65,6 +66,16 @@
OpenidVerifyForm, RegistrationForm, ChangepwForm, ChangeemailForm, \
ChangeopenidForm, DeleteForm, EmailPasswordForm
+def login(request,user):
+ from django.contrib.auth import login as _login
+ from forum.models import user_logged_in #custom signal
+ #1) get old session key
+ session_key = request.session.session_key
+ #2) login and get new session key
+ _login(request,user)
+ #3) send signal with old session key as argument
+ user_logged_in.send(user=user,session_key=session_key,sender=None)
+
def get_url_host(request):
if request.is_secure():
protocol = 'https'
@@ -76,8 +87,6 @@ def get_url_host(request):
def get_full_url(request):
return get_url_host(request) + request.get_full_path()
-
-
def ask_openid(request, openid_url, redirect_to, on_failure=None,
sreg_request=None):
""" basic function to ask openid and return response """
@@ -96,7 +105,7 @@ def ask_openid(request, openid_url, redirect_to, on_failure=None,
try:
auth_request = consumer.begin(openid_url)
except DiscoveryFailure:
- msg = _(u"非法OpenID地址: %s" % openid_url)
+ msg = _(u"OpenID %(openid_url)s is invalid" % {'openid_url':openid_url})
return on_failure(request, msg)
if sreg_request:
@@ -113,7 +122,6 @@ def complete(request, on_success=None, on_failure=None, return_to=None):
# make sure params are encoded in utf8
params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
openid_response = consumer.complete(params, return_to)
-
if openid_response.status == SUCCESS:
return on_success(request, openid_response.identity_url,
@@ -150,7 +158,7 @@ def decorated(request, *args, **kwargs):
return decorated
@not_authenticated
-def signin(request):
+def signin(request,newquestion=False,newanswer=False):
"""
signin page. It manage the legacy authentification (user/password)
and authentification with openid.
@@ -168,7 +176,7 @@ def signin(request):
if request.POST:
- if 'bsignin' in request.POST.keys():
+ if 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys():
form_signin = OpenidSigninForm(request.POST)
if form_signin.is_valid():
@@ -179,7 +187,6 @@ def signin(request):
reverse('user_complete_signin'),
urllib.urlencode({'next':next})
)
-
return ask_openid(request,
form_signin.cleaned_data['openid_url'],
redirect_to,
@@ -195,8 +202,24 @@ def signin(request):
next = clean_next(form_auth.cleaned_data.get('next'))
return HttpResponseRedirect(next)
+ question = None
+ if newquestion == True:
+ from forum.models import AnonymousQuestion as AQ
+ session_key = request.session.session_key
+ qlist = AQ.objects.filter(session_key=session_key).order_by('-added_at')
+ if len(qlist) > 0:
+ question = qlist[0]
+ answer = None
+ if newanswer == True:
+ from forum.models import AnonymousAnswer as AA
+ session_key = request.session.session_key
+ alist = AA.objects.filter(session_key=session_key).order_by('-added_at')
+ if len(alist) > 0:
+ answer = alist[0]
return render('authopenid/signin.html', {
+ 'question':question,
+ 'answer':answer,
'form1': form_auth,
'form2': form_signin,
'msg': request.GET.get('msg',''),
@@ -220,7 +243,7 @@ def signin_success(request, identity_url, openid_response):
if openid isn't registered user is redirected to register page.
"""
- openid_ = from_openid_response(openid_response)
+ openid_ = from_openid_response(openid_response) #create janrain OpenID object
request.session['openid'] = openid_
try:
rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
@@ -278,7 +301,8 @@ def register(request):
'next': next,
'username': nickname,
})
-
+
+ user_ = None
if request.POST:
just_completed = False
if 'bnewaccount' in request.POST.keys():
@@ -309,14 +333,41 @@ def register(request):
user_id=user_.id)
uassoc.save()
login(request, user_)
+
+ #check if we need to post a question that was added anonymously
+ #this needs to be a function call becase this is also done
+ #if user just logged in and did not need to create the new account
- # redirect, can redirect only if forms are valid.
- if is_redirect:
- return HttpResponseRedirect(next)
+ if user_ != None and settings.EMAIL_VALIDATION == 'on':
+ send_new_email_key(user_,nomessage=True)
+ output = validation_email_sent(request)
+ set_email_validation_message(user_) #message set after generating view
+ return output
+ elif user_.is_authenticated():
+ return HttpResponseRedirect('/')
+ else:
+ raise server_error('')
+ openid_str = str(openid_)
+ bits = openid_str.split('/')
+ base_url = bits[2] #assume this is base url
+ url_bits = base_url.split('.')
+ provider_name = url_bits[-2].lower()
+
+ providers = {'yahoo':'<font color="purple">Yahoo!</font>',
+ 'flickr':'<font color="#0063dc">flick</font><font color="#ff0084">r</font>&trade;',
+ 'google':'Google&trade;',
+ 'aol':'<font color="#31658e">AOL</font>',
+ }
+ if provider_name not in providers:
+ provider_logo = provider_name
+ else:
+ provider_logo = providers[provider_name]
+
return render('authopenid/complete.html', {
'form1': form1,
'form2': form2,
+ 'provider':providers[provider_name],
'nickname': nickname,
'email': email
}, context_instance=RequestContext(request))
@@ -464,47 +515,158 @@ def changepw(request):
return render('authopenid/changepw.html', {'form': form },
context_instance=RequestContext(request))
+def find_email_validation_messages(user):
+ msg_text = _('your email needs to be validated')
+ return user.message_set.filter(message__exact=msg_text)
+
+def set_email_validation_message(user):
+ messages = find_email_validation_messages(user)
+ msg_text = _('your email needs to be validated')
+ if len(messages) == 0:
+ user.message_set.create(message=msg_text)
+
+def clear_email_validation_message(user):
+ messages = find_email_validation_messages(user)
+ messages.delete()
+
+def set_new_email(user, new_email, nomessage=False):
+ if new_email != user.email:
+ user.email = new_email
+ user.email_isvalid = False
+ user.save()
+ if settings.EMAIL_VALIDATION == 'on':
+ send_new_email_key(user,nomessage=nomessage)
+
+def _send_email_key(user):
+ """private function. sends email containing validation key
+ to user's email address
+ """
+ subject = _("Welcome")
+ message_template = loader.get_template('authopenid/email_validation.txt')
+ import settings
+ message_context = Context({
+ 'validation_link': '%s/email/verify/%d/%s/' % (settings.APP_URL ,user.id,user.email_key)
+ })
+ message = message_template.render(message_context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
+
+def send_new_email_key(user,nomessage=False):
+ import random
+ random.seed()
+ user.email_key = '%032x' % random.getrandbits(128)
+ user.save()
+ _send_email_key(user)
+ if nomessage==False:
+ set_email_validation_message(user)
+
+@login_required
+def send_email_key(request):
+ """
+ url = /email/sendkey/
+
+ view that is shown right after sending email key
+ email sending is called internally
+
+ raises 404 if email validation is off
+ if current email is valid shows 'key_not_sent' view of
+ authopenid/changeemail.html template
+ """
+
+ if settings.EMAIL_VALIDATION != 'off':
+ if request.user.email_isvalid:
+ return render('authopenid/changeemail.html',
+ { 'email': request.user.email,
+ 'action_type': 'key_not_sent',
+ 'change_link': reverse('user_changeemail')},
+ context_instance=RequestContext(request)
+ )
+ else:
+ _send_email_key(request.user)
+ return validation_email_sent(request)
+ else:
+ raise Http404
+
+
+#internal server view used as return value by other views
+def validation_email_sent(request):
+ return render('authopenid/changeemail.html',
+ { 'email': request.user.email, 'action_type': 'validate', },
+ context_instance=RequestContext(request))
+
+
+def verifyemail(request,id=None,key=None):
+ """
+ view that is shown when user clicks email validation link
+ url = /email/verify/{{user.id}}/{{user.email_key}}/
+ """
+ if settings.EMAIL_VALIDATION != 'off':
+ user = User.objects.get(id=id)
+ if user:
+ if user.email_key == key:
+ user.email_isvalid = True
+ clear_email_validation_message(user)
+ user.save()
+ return render('authopenid/changeemail.html', {
+ 'action_type': 'validation_complete',
+ }, context_instance=RequestContext(request))
+ raise Http404
+
@login_required
def changeemail(request):
"""
changeemail view. It require password or openid to allow change.
- url: /changeemail/
+ url: /email/*
template : authopenid/changeemail.html
"""
msg = request.GET.get('msg', '')
extension_args = {}
user_ = request.user
-
+
redirect_to = get_url_host(request) + reverse('user_changeemail')
+ action = 'change'
if request.POST:
form = ChangeemailForm(request.POST, user=user_)
if form.is_valid():
if not form.test_openid:
- user_.email = form.cleaned_data['email']
- user_.save()
- msg = _("Email changed.")
- redirect = "%s?msg=%s" % (reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
+ new_email = form.cleaned_data['email']
+ if new_email != user_.email:
+ if settings.EMAIL_VALIDATION == 'on':
+ action = 'validate'
+ else:
+ action = 'done_novalidate'
+ set_new_email(user_, new_email,nomessage=True)
+ else:
+ action = 'keep'
else:
+ #what does this branch do?
+ return server_error('')
request.session['new_email'] = form.cleaned_data['email']
return ask_openid(request, form.cleaned_data['password'],
redirect_to, on_failure=emailopenid_failure)
+
elif not request.POST and 'openid.mode' in request.GET:
return complete(request, emailopenid_success,
emailopenid_failure, redirect_to)
else:
form = ChangeemailForm(initial={'email': user_.email},
user=user_)
+
- return render('authopenid/changeemail.html', {
+ output = render('authopenid/changeemail.html', {
'form': form,
+ 'email': user_.email,
+ 'action_type': action,
'msg': msg
}, context_instance=RequestContext(request))
+ if action == 'validate':
+ set_email_validation_message(user_)
+
+ return output
+
def emailopenid_success(request, identity_url, openid_response):
openid_ = from_openid_response(openid_response)
View
5 forum/admin.py
@@ -4,6 +4,9 @@
from models import *
+class AnonymousQuestionAdmin(admin.ModelAdmin):
+ """AnonymousQuestion admin class"""
+
class QuestionAdmin(admin.ModelAdmin):
"""Question admin class"""
@@ -68,4 +71,4 @@ class BookAuthorRssAdmin(admin.ModelAdmin):
admin.site.register(Activity, ActivityAdmin)
admin.site.register(Book, BookAdmin)
admin.site.register(BookAuthorInfo, BookAuthorInfoAdmin)
-admin.site.register(BookAuthorRss, BookAuthorRssAdmin)
+admin.site.register(BookAuthorRss, BookAuthorRssAdmin)
View
14 forum/feed.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python
#encoding:utf-8
#-------------------------------------------------------------------------------
# Name: Syndication feed class for subsribtion
@@ -13,16 +13,16 @@
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.translation import ugettext as _
from models import Question
+import settings
class RssLastestQuestionsFeed(Feed):
- title = _('site title') + _(' - ') + _('site slogan') + _(' - ')+ _('latest questions')
- #EDIT!!!
- link = 'http://where.com/questions/'
- description = _('meta site content')
+ title = settings.APP_TITLE + _(' - ')+ _('latest questions')
+ link = settings.APP_URL + '/' + _('questions/')
+ description = settings.APP_DESCRIPTION
#ttl = 10
- copyright = _('copyright message')
+ copyright = settings.APP_COPYRIGHT
def item_link(self, item):
- return '/questions/%s/' % item.id
+ return self.link + '%s/' % item.id
def item_author_name(self, item):
return item.author.username
View
42 forum/forms.py
@@ -43,7 +43,8 @@ def __init__(self, *args, **kwargs):
self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
self.max_length = 255
self.label = _('tags')
- self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
+ #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
+ self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
self.initial = ''
def clean(self, value):
@@ -74,6 +75,10 @@ def __init__(self, *args, **kwargs):
self.label = _('community wiki')
self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
+class EmailNotifyField(forms.BooleanField):
+ def __init__(self, *args, **kwargs):
+ super(EmailNotifyField, self).__init__(*args, **kwargs)
+ self.required = False
class SummaryField(forms.CharField):
def __init__(self, *args, **kwargs):
@@ -94,18 +99,28 @@ class AskForm(forms.Form):
user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
-
-
class AnswerForm(forms.Form):
text = EditorField()
wiki = WikiField()
openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- def __init__(self, question, *args, **kwargs):
+ email_notify = EmailNotifyField()
+ def __init__(self, question, user, *args, **kwargs):
super(AnswerForm, self).__init__(*args, **kwargs)
+ self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
if question.wiki:
self.fields['wiki'].initial = True
+ if user.is_authenticated():
+ try:
+ feed = EmailFeed.objects.get(feed_id=question.id, subscriber_id=user.id)
+ if feed.subscriber == user and feed.content == question:
+ self.fields['email_notify'].initial = True
+ return
+ except EmailFeed.DoesNotExist:
+ pass
+ self.fields['email_notify'].initial = False
+
class CloseForm(forms.Form):
reason = forms.ChoiceField(choices=CLOSE_REASONS)
@@ -181,13 +196,14 @@ def __init__(self, user, *args, **kwargs):
def clean_email(self):
"""For security reason one unique email in database"""
if self.user.email != self.cleaned_data['email']:
- if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
+ #todo dry it, there is a similar thing in openidauth
+ if settings.EMAIL_UNIQUE == True:
+ if 'email' in self.cleaned_data:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(_('this email has already been registered, please use another one'))
raise forms.ValidationError(_('this email has already been registered, please use another one'))
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- else:
- return self.cleaned_data['email']
+ return self.cleaned_data['email']
View
1  forum/management/commands/once_award_badges.py
@@ -12,6 +12,7 @@
# Licence: GPL V2
#-------------------------------------------------------------------------------
+from datetime import datetime, date
from django.db import connection
from django.shortcuts import get_object_or_404
from django.contrib.contenttypes.models import ContentType
View
159 forum/models.py
@@ -12,16 +12,40 @@
from django.db.models.signals import post_delete, post_save, pre_save
from django.utils.translation import ugettext as _
import django.dispatch
+import settings
from forum.managers import *
from const import *
+class EmailFeed(models.Model):
+ #subscription key for unsubscribe by visiting emailed link
+ key = models.CharField(max_length=32)
+ #generic relation with feed content (i.e. question or tags)
+ feed_content_type = models.ForeignKey(ContentType,related_name='content_emailfeed')
+ feed_id = models.PositiveIntegerField()
+ content = generic.GenericForeignKey('feed_content_type','feed_id')
+ #generic relation with owner - either nameless email or User
+ subscriber_content_type = models.ForeignKey(ContentType,related_name='subscriber_emailfeed')
+ subscriber_id = models.PositiveIntegerField()
+ subscriber = generic.GenericForeignKey('subscriber_content_type','subscriber_id')
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+ reported_at = models.DateTimeField(default=datetime.datetime.now)
+
+ #getter functions rely on implementations of similar functions in content
+ #of subscriber objects
+ def get_update_summary(self):
+ return self.content.get_update_summary(last_reported_at = self.reported_at,recipient_email = self.get_email())
+
+ def get_email(self):
+ return self.subscriber.email
+
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
created_by = models.ForeignKey(User, related_name='created_tags')
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_tags')
+ email_feeds = generic.GenericRelation(EmailFeed)
# Denormalised data
used_count = models.PositiveIntegerField(default=0)
@@ -131,6 +155,7 @@ class Question(models.Model):
comments = generic.GenericRelation(Comment)
votes = generic.GenericRelation(Vote)
flagged_items = generic.GenericRelation(FlaggedItem)
+ email_feeds = generic.GenericRelation(EmailFeed)
objects = QuestionManager()
@@ -173,7 +198,10 @@ def get_question_title(self):
attr = CONST['deleted']
else:
attr = None
- return u'%s %s' % (self.title, attr) if attr is not None else self.title
+ 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])
@@ -181,6 +209,57 @@ def get_revision_url(self):
def get_latest_revision(self):
return self.revisions.all()[0]
+ 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 = []
+ for answer in self.answers.all():
+ if (answer.added_at > last_reported_at):
+ 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:
+ answer_comments.append(comment)
+ if edited or comments or new_answers or modified_answers or answer_comments:
+ import sets
+ 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(answer_comments) > 1:
+ out.append(_('%(people)s commented answers') % {'people':people})
+ else:
+ out.append(_('%(people)s commented the 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
@@ -219,6 +298,44 @@ def save(self, **kwargs):
def __unicode__(self):
return u'revision %s of %s' % (self.revision, self.title)
+class AnonymousAnswer(models.Model):
+ question = models.ForeignKey(Question, related_name='anonymous_answers')
+ 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)
+
+ def publish(self,user):
+ from forum.views import create_new_answer
+ added_at = datetime.datetime.now()
+ print user.id
+ create_new_answer(question=self.question,wiki=self.wiki,
+ added_at=added_at,text=self.text,
+ author=user)
+ self.delete()
+
+class AnonymousQuestion(models.Model):
+ title = models.CharField(max_length=300)
+ session_key = models.CharField(max_length=40) #session id for anonymous questions
+ text = models.TextField()
+ summary = models.CharField(max_length=180)
+ tagnames = models.CharField(max_length=125)
+ 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)
+
+ def publish(self,user):
+ from forum.views import create_new_question
+ added_at = datetime.datetime.now()
+ create_new_question(title=self.title, author=user, added_at=added_at,
+ wiki=self.wiki, tagnames=self.tagnames,
+ summary=self.summary, text=self.text)
+ self.delete()
+
class Answer(models.Model):
question = models.ForeignKey(Question, related_name='answers')
author = models.ForeignKey(User, related_name='answers')
@@ -447,6 +564,13 @@ class BookAuthorRss(models.Model):
class Meta:
db_table = u'book_author_rss'
+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)
+ feeds = generic.GenericRelation(EmailFeed)
+
# User extend properties
QUESTIONS_PER_PAGE_CHOICES = (
(10, u'10'),
@@ -454,8 +578,11 @@ class Meta:
(50, u'50'),
)
+User.add_to_class('email_isvalid', models.BooleanField(default=False))
+User.add_to_class('email_key', models.CharField(max_length=16, 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('email_feeds', generic.GenericRelation(EmailFeed))
User.add_to_class('favorite_questions',
models.ManyToManyField(Question, through=FavoriteQuestion,
related_name='favorited_by'))
@@ -480,11 +607,14 @@ class Meta:
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
+ messages = []
+ for m in self.message_set.all():
+ messages.append(m.message)
+ return messages
def delete_messages(self):
self.message_set.all().delete()
@@ -632,6 +762,24 @@ 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)
@@ -652,3 +800,4 @@ def record_user_full_updated(instance, **kwargs):
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)
View
4 forum/templatetags/extra_tags.py
@@ -1,4 +1,4 @@
-import time
+import time
import datetime
import math
import re
@@ -237,4 +237,4 @@ def get_latest_changed_timestamp():
timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
except:
timestr = ''
- return timestr
+ return timestr
View
1  forum/user.py
@@ -1,4 +1,3 @@
-# coding=utf-8
from django.utils.translation import ugettext as _
class UserView:
def __init__(self, id, tab_title, tab_description, page_title, view_name, template_file, data_size=0):
View
261 forum/views.py
@@ -168,46 +168,140 @@ def questions(request, tagname=None, unanswered=False):
'pagesize' : pagesize
}}, context_instance=RequestContext(request))
+def create_new_answer( question=None, author=None,\
+ added_at=None, wiki=False,\
+ text='', email_notify=False):
+
+ html = sanitize_html(markdowner.convert(text))
+
+ #create answer
+ answer = Answer(
+ question = question,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ html = html
+ )
+ 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)
+
+ #update revision
+ 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:
+ try:
+ EmailFeed.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type)
+ except EmailFeed.DoesNotExist:
+ feed = EmailFeed(content = question, subscriber = author)
+ feed.save()
+ else:
+ #not sure if this is necessary. ajax should take care of this...
+ try:
+ feed = Email.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type)
+ feed.delete()
+ except:
+ pass
+
+def create_new_question(title=None,author=None,added_at=None,
+ wiki=False,tagnames=None,summary=None,
+ text=None):
+ """this is not a view
+ and maybe should become one of the methods on Question object?
+ """
+ html = sanitize_html(markdowner.convert(text))
+ question = Question(
+ title = title,
+ author = author,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = author,
+ wiki = wiki,
+ tagnames = tagnames,
+ html = html,
+ 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
+
#TODO: allow anynomus user to ask question by providing email and username.
-@login_required
+#@login_required
def ask(request):
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']
- )
+ added_at = datetime.datetime.now()
+ title = strip_tags(form.cleaned_data['title'])
+ wiki = form.cleaned_data['wiki']
+ tagnames = form.cleaned_data['tags'].strip()
+ text = form.cleaned_data['text']
+ html = sanitize_html(markdowner.convert(text))
+ summary = strip_tags(html)[:120]
- return HttpResponseRedirect(question.get_absolute_url())
+ if request.user.is_authenticated():
+ author = request.user
+
+ question = create_new_question(
+ title = title,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ tagnames = tagnames,
+ summary = summary,
+ text = text
+ )
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ request.session.flush()
+ session_key = request.session.session_key
+ question = AnonymousQuestion(
+ session_key = session_key,
+ title = title,
+ tagnames = tagnames,
+ wiki = wiki,
+ text = text,
+ summary = summary,
+ added_at = added_at,
+ ip_addr = request.META['REMOTE_ADDR'],
+ )
+ question.save()
+ return HttpResponseRedirect('%s%s%s' % ( _('/account/'),_('signin/'),('newquestion/')))
else:
form = AskForm()
@@ -233,7 +327,7 @@ def question(request, id):
question = get_object_or_404(Question, id=id)
if question.deleted and not can_view_deleted_post(request.user, question):
raise Http404
- answer_form = AnswerForm(question)
+ answer_form = AnswerForm(question,request.user)
answers = Answer.objects.get_answers_from_question(question, request.user)
answers = answers.select_related(depth=1)
@@ -254,7 +348,16 @@ def question(request, id):
if answers is not None:
answers = answers.order_by("-accepted", orderby)
- objects_list = Paginator(answers, ANSWERS_PAGE_SIZE)
+
+ filtered_answers = []
+ for answer in answers:
+ if answer.deleted == True:
+ if answer.author_id == request.user.id:
+ filtered_answers.append(answer)
+ else:
+ filtered_answers.append(answer)
+
+ objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE)
page_objects = objects_list.page(page)
# update view count
Question.objects.update_view_count(question)
@@ -558,42 +661,38 @@ def answer_revisions(request, id):
'revisions': revisions,
}, context_instance=RequestContext(request))
-#TODO: allow anynomus
-@login_required
def answer(request, id):
question = get_object_or_404(Question, id=id)
if request.method == "POST":
- form = AnswerForm(question, request.POST)
+ form = AnswerForm(question, request.user, request.POST)
if form.is_valid():
+ wiki = form.cleaned_data['wiki']
+ text = form.cleaned_data['text']
update_time = datetime.datetime.now()
- answer = Answer(
- question = question,
- author = request.user,
- added_at = update_time,
- wiki = form.cleaned_data['wiki'],
- html = sanitize_html(markdowner.convert(form.cleaned_data['text'])),
- )
- if answer.wiki:
- answer.last_edited_by = answer.author
- answer.last_edited_at = update_time
- answer.wikified_at = update_time
- answer.save()
- Question.objects.update_answer_count(question)
-
- question = get_object_or_404(Question, id=id)
- question.last_activity_at = update_time
- question.last_activity_by = request.user
- question.save()
-
- AnswerRevision.objects.create(
- answer = answer,
- revision = 1,
- author = request.user,
- revised_at = update_time,
- summary = CONST['default_version'],
- text = form.cleaned_data['text']
- )
+ if request.user.is_authenticated():
+ create_new_answer(
+ question=question,
+ author=request.user,
+ added_at=update_time,
+ wiki=wiki,
+ text=text,
+ email_notify=form.cleaned_data['email_notify']
+ )
+ else:
+ request.session.flush()
+ html = sanitize_html(markdowner.convert(text))
+ summary = strip_tags(html)[:120]
+ anon = AnonymousAnswer(
+ question = question,
+ wiki = wiki,
+ text = text,
+ summary = summary,
+ session_key = request.session.session_key,
+ ip_addr = request.META['REMOTE_ADDR'],
+ )
+ anon.save()
+ return HttpResponseRedirect('/account/signin/newanswer')
return HttpResponseRedirect(question.get_absolute_url())
@@ -655,6 +754,7 @@ def vote(request, id):
offensiveAnswer:8,
removeQuestion: 9,
removeAnswer:10
+ questionSubscribeUpdates:11
accept answer code:
response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default
@@ -831,6 +931,31 @@ def can_vote(vote_score, user):
else:
onDeleted(post, request.user)
delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user)
+ elif vote_type == '11':#subscribe q updates
+ user = request.user
+ if user.is_authenticated():
+ try:
+ EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id,feed_content_type=question_type)
+ except EmailFeed.DoesNotExist:
+ feed = EmailFeed(subscriber=user,content=question)
+ feed.save()
+ if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False:
+ response_data['message'] = _('subscription saved, %(email)s needs validation') % {'email':user.email}
+ #response_data['status'] = 1
+ #responst_data['allowed'] = 1
+ else:
+ pass
+ #response_data['status'] = 0
+ #response_data['allowed'] = 0
+ elif vote_type == '12':#unsubscribe q updates
+ user = request.user
+ if user.is_authenticated():
+ try:
+ feed = EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id)
+ feed.delete()
+ except EmailFeed.DoesNotExist:
+ pass
+
else:
response_data['success'] = 0
response_data['message'] = u'Request mode is not supported. Please try again.'
@@ -905,7 +1030,11 @@ def edit_user(request, id):
if request.method == "POST":
form = EditUserForm(user, request.POST)
if form.is_valid():
- user.email = sanitize_html(form.cleaned_data['email'])
+ new_email = sanitize_html(form.cleaned_data['email'])
+
+ from django_authopenid.views import set_new_email
+ set_new_email(user, new_email)
+
user.real_name = sanitize_html(form.cleaned_data['realname'])
user.website = sanitize_html(form.cleaned_data['website'])
user.location = sanitize_html(form.cleaned_data['city'])
@@ -1498,7 +1627,6 @@ def user_reputation(request, user_id, user_view):
reputation.query.group_by = ['question_id']
-
rep_list = []
for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)
@@ -1683,7 +1811,6 @@ def read_message(request):
if request.method == "POST":
if request.POST['formdata'] == 'required':
request.session['message_silent'] = 1
-
if request.user.is_authenticated():
request.user.delete_messages()
return HttpResponse('')
View
BIN  locale/en/LC_MESSAGES/django.mo
Binary file not shown
View
1,775 locale/en/LC_MESSAGES/django.po
913 additions, 862 deletions not shown
View
BIN  locale/es/LC_MESSAGES/django.mo
Binary file not shown
View
1,329 locale/es/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-07-23 01:11-0300\n"
+"POT-Creation-Date: 2009-08-05 22:28-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -15,19 +15,21 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: settings.py:27
+#: settings.py:12
msgid "account/"
msgstr "cuenta/"
-#: settings.py:27 django_authopenid/urls.py:9 django_authopenid/urls.py:11
+#: settings.py:12 django_authopenid/urls.py:9 django_authopenid/urls.py:10
+#: django_authopenid/urls.py:11 django_authopenid/urls.py:13
+#: forum/views.py:304 templates/authopenid/confirm_email.txt:10
msgid "signin/"
msgstr "ingresar/"
-#: django_authopenid/forms.py:67 django_authopenid/views.py:93
+#: django_authopenid/forms.py:67 django_authopenid/views.py:102
msgid "i-names are not supported"
msgstr "i-names no son soportados"
-#: django_authopenid/forms.py:102 django_authopenid/forms.py:207
+#: django_authopenid/forms.py:102
msgid ""
"Usernames can only contain letters, numbers and "
"underscores"
@@ -42,7 +44,7 @@ msgstr ""
"Este nombre de usuario no existe en nuestra base de datos. Por favor elija "
"otro."
-#: django_authopenid/forms.py:126 django_authopenid/forms.py:231
+#: django_authopenid/forms.py:126 django_authopenid/forms.py:233
msgid ""
"Please enter a valid username and password. Note that "
"both fields are case-sensitive."
@@ -50,11 +52,11 @@ msgstr ""
"Por favor ingrese un usuario y contraseña validos. Ambos campos son "
"sensibles a mayúsculas y minúsculas."
-#: django_authopenid/forms.py:130 django_authopenid/forms.py:235
+#: django_authopenid/forms.py:130 django_authopenid/forms.py:237
msgid "This account is inactive."
msgstr "Esta cuenta esta inactiva."
-#: django_authopenid/forms.py:158
+#: django_authopenid/forms.py:158 django_authopenid/forms.py:210
msgid "invalid user name"
msgstr "nombre de usuario no valido"
@@ -71,126 +73,136 @@ msgstr "nombre de usuario muy corto"
msgid "this name is already in use - please try anoter"
msgstr "este nombre ya está tomado - por favor intente con otro"
-#: django_authopenid/forms.py:184
+#: django_authopenid/forms.py:185
msgid ""
-"This email is already registered in our database. Please "
-"choose another."
+"This email is already registered in our database. "
+"Please choose another."
msgstr ""
"Este email ya está registrado en nuestra base de datos. Por favor, intente "
"con otro."
-#: django_authopenid/forms.py:214
+#: django_authopenid/forms.py:216
msgid ""
"This username don't exist. Please choose another."
msgstr "Este nombre de usuario no existe, por favor ingrese otro."
-#: django_authopenid/forms.py:253
+#: django_authopenid/forms.py:255
msgid "choose a username"
msgstr "elija un nombre de usuario"
-#: django_authopenid/forms.py:255 templates/authopenid/signup.html:36
+#: django_authopenid/forms.py:257 templates/authopenid/signup.html:38
msgid "your email address"
msgstr "su email (correo electrónico)"
-#: django_authopenid/forms.py:257 templates/authopenid/signup.html:37
+#: django_authopenid/forms.py:259 templates/authopenid/signup.html:39
msgid "choose password"
msgstr "elija una contraseña"
-#: django_authopenid/forms.py:259 templates/authopenid/signup.html:38
+#: django_authopenid/forms.py:261 templates/authopenid/signup.html:40
msgid "retype password"
msgstr "re-ingrese la contraseña"
-#: django_authopenid/forms.py:330
+#: django_authopenid/forms.py:335
msgid ""
"Old password is incorrect. Please enter the correct "
"password."
msgstr "La antigua contraseña es incorrecta. Por favor ingrese la correcta"
-#: django_authopenid/forms.py:342
+#: django_authopenid/forms.py:347
msgid "new passwords do not match"
msgstr "la nueva contraseña no coincide"
-#: django_authopenid/forms.py:434
+#: django_authopenid/forms.py:442
msgid "Incorrect username."
msgstr "Nombre de usuario incorrecto"
#: django_authopenid/urls.py:10
+#, fuzzy
+msgid "newquestion/"
+msgstr "pregunta"
+
+#: django_authopenid/urls.py:11
+#, fuzzy
+msgid "newanswer/"
+msgstr "resputa"
+
+#: django_authopenid/urls.py:12
msgid "signout/"
msgstr "salir/"
-#: django_authopenid/urls.py:11
+#: django_authopenid/urls.py:13
msgid "complete/"
msgstr "completado/"
-#: django_authopenid/urls.py:13
+#: django_authopenid/urls.py:15
msgid "register/"
msgstr "registrarse/"
-#: django_authopenid/urls.py:14
+#: django_authopenid/urls.py:16
msgid "signup/"
msgstr "registrarse/"
-#: django_authopenid/urls.py:16
+#: django_authopenid/urls.py:18
msgid "sendpw/"
msgstr "enviarcontrasena/"
-#: django_authopenid/urls.py:26
+#: django_authopenid/urls.py:29
msgid "delete/"
msgstr "borrar/"
-#: django_authopenid/views.py:99
+#: django_authopenid/views.py:108
#, python-format
-msgid "非法OpenID地址: %s"
+msgid "OpenID %(openid_url)s is invalid"
msgstr ""
-#: django_authopenid/views.py:366
+#: django_authopenid/views.py:417 django_authopenid/views.py:544
msgid "Welcome"
msgstr "Bienvenido"
-#: django_authopenid/views.py:456
+#: django_authopenid/views.py:507
msgid "Password changed."
msgstr "Contraseña modificada"
-#: django_authopenid/views.py:488
-msgid "Email changed."
-msgstr "Email modificado."
+#: django_authopenid/views.py:519 django_authopenid/views.py:524
+msgid "your email needs to be validated"
+msgstr ""
-#: django_authopenid/views.py:519 django_authopenid/views.py:671
+#: django_authopenid/views.py:681 django_authopenid/views.py:833
#, python-format
msgid "No OpenID %s found associated in our database"
msgstr "La OpenID %s no esta asociada en nuestra base de datos"
-#: django_authopenid/views.py:523 django_authopenid/views.py:678
+#: django_authopenid/views.py:685 django_authopenid/views.py:840
#, python-format
msgid "The OpenID %s isn't associated to current user logged in"
msgstr "La OpenID %s no esta asociada al usuario actualmente autenticado"
-#: django_authopenid/views.py:531
+#: django_authopenid/views.py:693
msgid "Email Changed."
msgstr "Email modificado"
-#: django_authopenid/views.py:606
+#: django_authopenid/views.py:768
msgid "This OpenID is already associated with another account."
msgstr "Esta OpenID ya está asociada a otra cuenta."
-#: django_authopenid/views.py:611
+#: django_authopenid/views.py:773
#, python-format
msgid "OpenID %s is now associated with your account."
msgstr "La OpenID %s está ahora asociada con tu cuenta."
-#: django_authopenid/views.py:681
+#: django_authopenid/views.py:843
msgid "Account deleted."
msgstr "Cuenta borrada."
-#: django_authopenid/views.py:721
+#: django_authopenid/views.py:883
msgid "Request for new password"
msgstr "Pedir nueva contraseña"
-#: django_authopenid/views.py:734
+#: django_authopenid/views.py:896
msgid "A new password has been sent to your email address."
msgstr "Una nueva contraseña ha sido enviada a tu cuenta de Email."
-#: django_authopenid/views.py:764
+#: django_authopenid/views.py:926
#, python-format
msgid ""
"Could not change password. Confirmation key '%s' is not "
@@ -199,7 +211,7 @@ msgstr ""
"No se ha podido modificar la contraseña. La clave de confirmación '%s' no "
"está registrada"
-#: django_authopenid/views.py:773
+#: django_authopenid/views.py:935
msgid ""
"Can not change password. User don't exist anymore in our "
"database."
@@ -207,7 +219,7 @@ msgstr ""
"No se puede cambiar la contraseña. El usuario no existe más en nuestra base "
"de datos."
-#: django_authopenid/views.py:782
+#: django_authopenid/views.py:944
#, python-format
msgid "Password changed for %s. You may now sign in."
msgstr "Contraseña cambiada por %s. Ahora puedes ingresar."
@@ -328,34 +340,21 @@ msgstr "versión inicial"
msgid "retagged"
msgstr "re-etiquetada"
-#: forum/feed.py:17 templates/base.html:7 templates/base_content.html:6
-#: templates/faq.html:25 templates/faq.html.py:108 templates/tough/faq.html:23
-#: templates/tough/faq.html.py:106 templates/tough/question_retag.html:89
-msgid "site title"
-msgstr "titulo del sitio"
-
-#: forum/feed.py:17
+#: forum/feed.py:18
msgid " - "
msgstr " - "
-#: forum/feed.py:17 templates/base.html:7 templates/base_content.html:6
-msgid "site slogan"
-msgstr " slogan del sitio"
-
-#: forum/feed.py:17
+#: forum/feed.py:18
msgid "latest questions"
msgstr "últimas preguntas"
-#: forum/feed.py:20 templates/index.html:8
-msgid "meta site content"
-msgstr "meta descripción"
-
-#: forum/feed.py:22
-msgid "copyright message"
-msgstr "mensaje de copyright"
+#: forum/feed.py:19
+#, fuzzy
+msgid "questions/"
+msgstr "preguntas"
#: forum/forms.py:14 templates/answer_edit_tips.html:34
-#: templates/answer_edit_tips.html.py:39 templates/question_edit_tips.html:31
+#: templates/answer_edit_tips.html.py:38 templates/question_edit_tips.html:31
#: templates/question_edit_tips.html:36
msgid "title"
msgstr "titulo"
@@ -380,25 +379,24 @@ msgstr "el contenido de la pregunta debe ser al menos de 10 caracteres"
msgid "tags"
msgstr "etiquetas"
-#: forum/forms.py:46
-msgid "please use space to separate tags (this enables autocomplete feature)"
+#: forum/forms.py:47
+msgid ""
+"Tags are short keywords, with no spaces within. Up to five tags can be used."
msgstr ""
-"por favor utilice espacio para separar las etiquetas (esto habilitael auto-"
-"completado)"
-#: forum/forms.py:53
+#: forum/forms.py:54 templates/question_retag.html:38
msgid "tags are required"
msgstr "las etiquetas son requeridas"
-#: forum/forms.py:57
+#: forum/forms.py:58
msgid "please use 5 tags or less"
msgstr "por favor use 5 o menos etiquetas"
-#: forum/forms.py:60
+#: forum/forms.py:61
msgid "tags must be shorter than 20 characters"
msgstr "las etiquetas deben ser menores a 20 caracteres"
-#: forum/forms.py:64
+#: forum/forms.py:65
msgid ""
"please use following characters in tags: letters 'a-z', numbers, and "
"characters '.-_#'"
@@ -406,15 +404,14 @@ msgstr ""
"por favor use solo los siguientes caracteres en los nombres de etiquetas: "
"letras 'a-z', números y caracteres '.-_#'"
-#: forum/forms.py:74 templates/index.html:56 templates/question.html:196
-#: templates/question.html.py:377 templates/questions.html:58
+#: forum/forms.py:75 templates/index.html:57 templates/question.html:199
+#: templates/question.html.py:380 templates/questions.html:58
#: templates/questions.html.py:70 templates/unanswered.html:48
-#: templates/unanswered.html.py:60 templates/tough/unanswered.html:46
-#: templates/tough/unanswered.html.py:58
+#: templates/unanswered.html.py:60
msgid "community wiki"
msgstr "wiki de comunidad"
-#: forum/forms.py:75
+#: forum/forms.py:76
msgid ""
"if you choose community wiki option, the question and answer do not generate "
"points and name of author will not be shown"
@@ -422,11 +419,11 @@ msgstr ""
"si marca la opción 'wiki de comunidad', la pregunta y respuestas no generan "
"puntos y el nombre del autor no será mostrado"
-#: forum/forms.py:84
+#: forum/forms.py:89
msgid "update summary:"
msgstr "resumen de modificación"
-#: forum/forms.py:85
+#: forum/forms.py:90
msgid ""
"enter a brief summary of your revision (e.g. fixed spelling, grammar, "
"improved style, this field is optional)"
@@ -434,48 +431,72 @@ msgstr ""
"ingresa un breve resumen de tu revisión (ej. error ortográfico, gramática, "
"mejoras de estilo. Este campo es opcional."
-#: forum/forms.py:160
+#: forum/forms.py:175
msgid "this email does not have to be linked to gravatar"
msgstr "este email no tiene porque estar asociado a un Gravatar"
-#: forum/forms.py:161
+#: forum/forms.py:176
msgid "Real name"
msgstr "Nombre real"
-#: forum/forms.py:162
+#: forum/forms.py:177
msgid "Website"
msgstr "Sitio Web"
-#: forum/forms.py:163
+#: forum/forms.py:178
msgid "Location"
msgstr "Ubicación"
-#: forum/forms.py:164
+#: forum/forms.py:179
msgid "Date of birth"
msgstr "Fecha de nacimiento"
-#: forum/forms.py:164
+#: forum/forms.py:179
msgid "will not be shown, used to calculate age, format: YYYY-MM-DD"
msgstr "no será mostrado, usado para calcular la edad. Formato: YYY-MM-DD"
-#: forum/forms.py:165 templates/base.html:64
-#: templates/authopenid/settings.html:20
+#: forum/forms.py:180 templates/authopenid/settings.html:21
msgid "Profile"
msgstr "Perfil"
-#: forum/forms.py:190 forum/forms.py:191
+#: forum/forms.py:207 forum/forms.py:208
msgid "this email has already been registered, please use another one"
msgstr "este email ya ha sido registrado, por favor use otro"
-#: forum/models.py:316 templates/badges.html:51
+#: forum/models.py:238
+#, fuzzy, python-format
+msgid "%(author)s modified the question"
+msgstr "Cerrar la pregunta"
+
+#: forum/models.py:242
+#, python-format
+msgid "%(people)s posted %(new_answer_count)s new answers"
+msgstr ""
+
+#: forum/models.py:247
+#, fuzzy, python-format
+msgid "%(people)s commented the question"
+msgstr "pregunta comentada"
+
+#: forum/models.py:252
+#, fuzzy, python-format
+msgid "%(people)s commented answers"
+msgstr "respuesta comentada"
+
+#: forum/models.py:254
+#, fuzzy, python-format
+msgid "%(people)s commented the answer"
+msgstr "respuesta comentada"
+
+#: forum/models.py:433 templates/badges.html:52
msgid "gold"
msgstr "oro"
-#: forum/models.py:317 templates/badges.html:59
+#: forum/models.py:434 templates/badges.html:60
msgid "silver"
msgstr "plata"
-#: forum/models.py:318 templates/badges.html:66
+#: forum/models.py:435 templates/badges.html:67
msgid "bronze"
msgstr "bronze"
@@ -555,7 +576,7 @@ msgstr "perfil - votos"
msgid "preferences"
msgstr "preferencias"
-#: forum/user.py:69 templates/user_tabs.html:28
+#: forum/user.py:69 templates/user_tabs.html:27
msgid "user preference settings"
msgstr "preferencias del usuario"
@@ -563,28 +584,43 @@ msgstr "preferencias del usuario"
msgid "profile - user preferences"
msgstr "perfil - preferencia de "
-#: forum/views.py:1730
+#: forum/views.py:304
+#, fuzzy
+msgid "/account/"
+msgstr "cuenta/"
+
+#: forum/views.py:943
+#, python-format
+msgid "subscription saved, %(email)s needs validation"
+msgstr ""
+
+#: forum/views.py:1853
msgid "uploading images is limited to users with >60 reputation points"
msgstr "para subir imagenes debes tener más de 60 puntos de reputación"
-#: forum/views.py:1732
+#: forum/views.py:1855
msgid "allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"
msgstr ""
"los tipos de archivos permitidos son 'jpg', 'jpeg', 'gif', 'bmp', 'png', "
"'tiff'"
-#: forum/views.py:1734
+#: forum/views.py:1857
#, python-format
msgid "maximum upload file size is %sK"
msgstr "tamaño máximo permitido es archivo %sK"
-#: forum/views.py:1736
+#: forum/views.py:1859
#, python-format
msgid ""
"Error uploading file. Please contact the site administrator. Thank you. %s"
msgstr ""
"Error al subir el archivo. Por favor, contacte al administrador. Gracias. %s"
+#: forum/management/commands/send_email_alerts.py:35
+#, fuzzy
+msgid "updates from website"
+msgstr "sitio web del usuario"
+
#: forum/templatetags/extra_tags.py:139 forum/templatetags/extra_tags.py:168
#: templates/header.html:33
msgid "badges"
@@ -642,6 +678,10 @@ msgstr "ver todas las preguntas"
msgid "see all tags"
msgstr "ver todas las tags"
+#: templates/500.html:22
+msgid "sorry, system error"
+msgstr ""
+
#: templates/500.html:24
msgid "system error log is recorded, error will be fixed as soon as possible"
msgstr ""
@@ -668,19 +708,19 @@ msgid "Edit answer"
msgstr "Editar respuesta"
#: templates/answer_edit.html:24 templates/answer_edit.html.py:27
-#: templates/ask.html:25 templates/ask.html.py:28 templates/question.html:37
-#: templates/question.html.py:40 templates/question_edit.html:27
+#: templates/ask.html:25 templates/ask.html.py:28 templates/question.html:40
+#: templates/question.html.py:43 templates/question_edit.html:27
msgid "hide preview"
msgstr "ocultar previsualización"
#: templates/answer_edit.html:27 templates/ask.html:28
-#: templates/question.html:40 templates/question_edit.html:27
+#: templates/question.html:43 templates/question_edit.html:27
msgid "show preview"
msgstr "ver previsualización"
#: templates/answer_edit.html:47 templates/question_edit.html:65
-#: templates/revisions_answer.html:36 templates/revisions_question.html:36
-#: templates/tough/question_retag.html:51
+#: templates/question_retag.html:52 templates/revisions_answer.html:36
+#: templates/revisions_question.html:36
msgid "back"
msgstr "volver"
@@ -693,23 +733,25 @@ msgstr "revisión"
msgid "select revision"
msgstr "seleccionar revisión"
-#: templates/answer_edit.html:62 templates/ask.html:81
-#: templates/question.html:447 templates/question_edit.html:91
+#: templates/answer_edit.html:62 templates/ask.html:94
+#: templates/question.html:452 templates/question_edit.html:91
msgid "Toggle the real time Markdown editor preview"
msgstr "Activar la visualización en tiempo real de Markdown"
-#: templates/answer_edit.html:62 templates/ask.html:81
-#: templates/question.html:447 templates/question_edit.html:91
+#: templates/answer_edit.html:62 templates/ask.html:94
+#: templates/question.html:452 templates/question_edit.html:91
msgid "toggle preview"
msgstr "Activar previsualización"
#: templates/answer_edit.html:73 templates/question_edit.html:119
+#: templates/question_retag.html:75
msgid "Save edit"
msgstr "Guardar la edición"
#: templates/answer_edit.html:74 templates/close.html:29
-#: templates/question_edit.html:120 templates/reopen.html:30
-#: templates/user_edit.html:83 templates/tough/question_retag.html:75
+#: templates/question_edit.html:120 templates/question_retag.html:76
+#: templates/reopen.html:30 templates/user_edit.html:83
+#: templates/authopenid/changeemail.html:34
msgid "Cancel"
msgstr "Cancelar"
@@ -753,24 +795,24 @@ msgstr "**negrita** o __negrita__"
msgid "link"
msgstr "enlace"
-#: templates/answer_edit_tips.html:34 templates/answer_edit_tips.html.py:39
+#: templates/answer_edit_tips.html:34 templates/answer_edit_tips.html.py:38
#: templates/question_edit_tips.html:31 templates/question_edit_tips.html:36
msgid "text"
msgstr "texto"
-#: templates/answer_edit_tips.html:39 templates/question_edit_tips.html:36
+#: templates/answer_edit_tips.html:38 templates/question_edit_tips.html:36
msgid "image"
msgstr "imagen"
-#: templates/answer_edit_tips.html:43 templates/question_edit_tips.html:40
+#: templates/answer_edit_tips.html:42 templates/question_edit_tips.html:40
msgid "numbered list:"
msgstr "lista numerada"
-#: templates/answer_edit_tips.html:48 templates/question_edit_tips.html:45
+#: templates/answer_edit_tips.html:47 templates/question_edit_tips.html:45
msgid "basic HTML tags are also supported"
msgstr "etiquetas básicas de HTML permitidas"
-#: templates/answer_edit_tips.html:51 templates/question_edit_tips.html:48
+#: templates/answer_edit_tips.html:50 templates/question_edit_tips.html:48
msgid "learn more about Markdown"
msgstr "aprender mas sobre Markdown"
@@ -778,32 +820,25 @@ msgstr "aprender mas sobre Markdown"
msgid "Ask a question"
msgstr "Hacer una pregunta"
-#: templates/ask.html:106
-msgid "Use"
-msgstr "Usar"
-
-#: templates/ask.html:106
-msgid "learn more about OpenID"
-msgstr "aprender mas sobre OpenID"
-
-#: templates/ask.html:106 templates/authopenid/signin.html:35
-#: templates/authopenid/signin.html:61
-msgid "Login"
-msgstr "Ingresar"
+#: templates/ask.html:67
+#, fuzzy
+msgid "login to post question info"
+msgstr "<strong>Más recientes</strong> preguntas son mostradas primero."
-#: templates/ask.html:109
-msgid "Get your own "
-msgstr "Obtiene tu propio "
+#: templates/ask.html:73
+#, python-format
+msgid "must have valid %(email)s to post"
+msgstr ""
-#: templates/ask.html:117 templates/authopenid/sendpw.html:27
-msgid "User name"
-msgstr "Nombre de usuario"
+#: templates/ask.html:108
+msgid "(required)"
+msgstr ""
-#: templates/ask.html:120
-msgid "Email: (won't be shown to anyone)"
-msgstr "Email: (no será mostrado a nadie)"
+#: templates/ask.html:115
+msgid "Login/signup to post your question"
+msgstr ""
-#: templates/ask.html:127
+#: templates/ask.html:117
msgid "Ask your question"
msgstr "Haz tu pregunta"
@@ -824,72 +859,47 @@ msgid "Badges"
msgstr "Distinciones"
#: templates/badges.html:21
+msgid "Community gives you awards for your questions, answers and votes."
+msgstr "La comunidad te da distinciones por tus preguntas, respuestas y votos."
+
+#: templates/badges.html:22
+#, fuzzy
msgid ""
-"Community gives you awards for your questions, answers and votes. Below is "
-"the list of available badges and number of times each type of badge has been "
-"awarded."
+"Below is the list of available badges and number of times each type of badge "
+"has been awarded."
msgstr ""
"La comunidad te condecora con distinciones por tus preguntas, respuestas y "
"votos. Debajo esta la lista de las distinciones disponibles y la cantidad de "
"veces que han sido asignadas."
-#: templates/badges.html:48
+#: templates/badges.html:49
msgid "Community badges"
msgstr "Distinciones de la comunidad"
-#: templates/badges.html:54
-msgid ""
-"Gold badge is very rare. To obtain it you have to show profound knowledge "
-"and ability in addition to actively participating in the community. Gold "
-"badge is the highest award in this community."
+#: templates/badges.html:55
+msgid "gold badge description"
msgstr ""
"Las distinciones de Oro son excepcionales. Para obtenerla debes demostrar un "
"profundo conocimiento y habilidad además de participar activamente en la "
"comunidad. La distinción de Oro es la condecoración máxima en esta comunidad"
-#: templates/badges.html:62
-msgid ""
-"Obtaining silver badge requires significant patience. If you got one, you've "
-"very significantly contributed to this community"
+#: templates/badges.html:63
+msgid "silver badge description"
msgstr ""
"Obtener una distinción de Plata requiere de paciencia. Si has logrado una, "
"quiere decir que haz significativamente aportado a esta comunidad."
-#: templates/badges.html:65
+#: templates/badges.html:66
msgid "bronze badge: often given as a special honor"
msgstr ""
"distinción de bronce: con frecuencia entregada como reconocimiento especial."
-#: templates/badges.html:69
-msgid ""
-"If you are active in this community, you will get this medal - still it is a "
-"special honor."
+#: templates/badges.html:70
+msgid "bronze badge description"
msgstr ""
"Si eres un usuario activo de esta comunidad, recibirás esta distinción - de "
"todas maneras es un honor especial."
-#: templates/base.html:62
-#, fuzzy
-msgid "Congratulations! You have new badges: "
-msgstr "felicitaciones, la comunidad te ha otorgado una distinción"
-
-#: templates/base.html:63
-#, fuzzy
-msgid "go to see"
-msgstr "OK para cerrar"
-
-#: templates/base_content.html:61
-msgid "congratulations, community gave you a badge"
-msgstr "felicitaciones, la comunidad te ha otorgado una distinción"
-
-#: templates/base_content.html:62
-msgid "see"
-msgstr "ver"
-
-#: templates/base_content.html:63
-msgid "profile"
-msgstr "perfil"
-
#: templates/book.html:7
msgid "reading channel"
msgstr "canal de lectura"
@@ -956,9 +966,8 @@ msgstr "esta pregunta ha sido seleccionada como favorita"
msgid "number of times"
msgstr "numero de veces"
-#: templates/book.html:105 templates/index.html:47 templates/questions.html:46
+#: templates/book.html:105 templates/index.html:48 templates/questions.html:46
#: templates/unanswered.html:37 templates/users_questions.html:30
-#: templates/tough/unanswered.html:35
msgid "votes"
msgstr "votos"
@@ -966,18 +975,16 @@ msgstr "votos"
msgid "the answer has been accepted to be correct"
msgstr "la respuesta ha sido aceptada como correcta"
-#: templates/book.html:115 templates/index.html:48 templates/questions.html:47
+#: templates/book.html:115 templates/index.html:49 templates/questions.html:47
#: templates/unanswered.html:38 templates/users_questions.html:40
-#: templates/tough/unanswered.html:36
msgid "views"
msgstr "vistas"
-#: templates/book.html:125 templates/index.html:68 templates/question.html:112
-#: templates/question.html.py:479 templates/questions.html:84
-#: templates/questions.html.py:149 templates/tags.html:47
+#: templates/book.html:125 templates/index.html:69 templates/question.html:115
+#: templates/question.html.py:486 templates/questions.html:84
+#: templates/questions.html.py:156 templates/tags.html:47
#: templates/unanswered.html:75 templates/unanswered.html.py:109
-#: templates/users_questions.html:52 templates/tough/unanswered.html:72
-#: templates/tough/unanswered.html:105
+#: templates/users_questions.html:52
msgid "using tags"
msgstr "usando etiquetas"
@@ -1005,15 +1012,15 @@ msgstr "Razón"
msgid "OK to close"
msgstr "OK para cerrar"
-#: templates/faq.html:11 templates/tough/faq.html:9
+#: templates/faq.html:11
msgid "Frequently Asked Questions "
msgstr "Preguntas Frecuentes"
-#: templates/faq.html:15 templates/tough/faq.html:13