summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PENDING1
-rw-r--r--forum/skins/default/media/js/com.cnprog.utils.js2
-rw-r--r--forum/skins/default/media/js/wmd/wmd.js2
-rwxr-xr-xforum/skins/default/templates/base.html4
-rw-r--r--forum/urls.py60
-rw-r--r--forum/views/README12
-rw-r--r--forum/views/__init__.py3
-rw-r--r--forum/views/commands.py56
-rw-r--r--forum/views/meta.py8
-rw-r--r--forum/views/readers.py (renamed from forum/views/content.py)558
-rw-r--r--forum/views/users.py1
-rw-r--r--forum/views/writers.py535
12 files changed, 648 insertions, 594 deletions
diff --git a/PENDING b/PENDING
index 0f053357..9d2f00ee 100644
--- a/PENDING
+++ b/PENDING
@@ -4,7 +4,6 @@ new features (go to law school, get a job, do something real)
Just a joke - pick yourself a task and work on it.
==Refactoring==
-* (see note 1) analyze and split /views/content.py - Evgeny
* analyze and split models.py --> models/
* create forum/modules directory
* make modules load into the forum app like they
diff --git a/forum/skins/default/media/js/com.cnprog.utils.js b/forum/skins/default/media/js/com.cnprog.utils.js
index 93083288..3f7720c1 100644
--- a/forum/skins/default/media/js/com.cnprog.utils.js
+++ b/forum/skins/default/media/js/com.cnprog.utils.js
@@ -41,7 +41,7 @@ var notify = function() {
function appendLoader(containerSelector) {
$(containerSelector).append('<img class="ajax-loader" ' +
- 'src="mediaUrl("media/images/indicator.gif")" title="' +
+ 'src="' + mediaUrl("media/images/indicator.gif") + '" title="' +
$.i18n._('loading...') +
'" alt="' +
$.i18n._('loading...') +
diff --git a/forum/skins/default/media/js/wmd/wmd.js b/forum/skins/default/media/js/wmd/wmd.js
index 9888cda0..b5f1c9e0 100644
--- a/forum/skins/default/media/js/wmd/wmd.js
+++ b/forum/skins/default/media/js/wmd/wmd.js
@@ -54,7 +54,7 @@ Attacklab.wmdBase = function(){
var uploadImageHTML ="<div>" + $.i18n._('upload image') + "</div>" +
'<input type="file" name="file-upload" id="file-upload" size="26" '+
'onchange="return ajaxFileUpload($("#image-url"));"/><br>' +
- '<img id="loading" src="mediaUrl("media/images/indicator.gif")" style="display:none;"/>';
+ '<img id="loading" src="' + mediaUrl("media/images/indicator.gif") + '" style="display:none;"/>';
// The default text that appears in the dialog input box when entering
// links.
diff --git a/forum/skins/default/templates/base.html b/forum/skins/default/templates/base.html
index 9033b0c9..3a1848ef 100755
--- a/forum/skins/default/templates/base.html
+++ b/forum/skins/default/templates/base.html
@@ -18,9 +18,11 @@
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">google.load("jquery", "1.2.6");</script>
<script type="text/javascript">
+ /* <![CDATA[ */
var i18nLang = '{{settings.LANGUAGE_CODE}}';
var scriptUrl = '/{{settings.FORUM_SCRIPT_ALIAS}}'
var osqaSkin = '{{settings.OSQA_SKIN}}';
+ /* ]] */
</script>
<script type='text/javascript' src='{% media "/media/js/com.cnprog.i18n.js" %}'></script>
<script type='text/javascript' src='{% media "/media/js/jquery.i18n.js" %}'></script>
@@ -30,10 +32,12 @@
body { margin-top:2.4em; }
</style>
<script type="text/javascript">
+ /* <![CDATA[ */
$(document).ready(function() {
$('#validate_email_alert').click(function(){notify.close(true)})
notify.show();
});
+ /* ]] */
</script>
{% endif %}
diff --git a/forum/urls.py b/forum/urls.py
index f5cc561f..a045b027 100644
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -17,47 +17,49 @@ sitemaps = {
APP_PATH = os.path.dirname(__file__)
urlpatterns = patterns('',
- url(r'^$', app.content.index, name='index'),
- url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
+ url(r'^$', app.readers.index, name='index'),
+ url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}, name='sitemap'),
#(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/media/images/favicon.ico'}),
#(r'^favicon\.gif$', 'django.views.generic.simple.redirect_to', {'url': '/media/images/favicon.gif'}),
- (r'^m/(?P<path>.*)$', 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH,'skins').replace('\\','/')}
+ url(r'^m/(?P<path>.*)$', 'django.views.static.serve',
+ {'document_root': os.path.join(APP_PATH,'skins').replace('\\','/')},
+ name='osqa_media',
),
- (r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')}
+ url(r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve',
+ {'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')},
+ name='uploaded_file',
),
- (r'^%s/$' % _('signin/'), 'django_authopenid.views.signin'),
+ url(r'^%s/$' % _('signin/'), 'django_authopenid.views.signin', name='signin'),
url(r'^%s$' % _('about/'), app.meta.about, name='about'),
url(r'^%s$' % _('faq/'), app.meta.faq, name='faq'),
url(r'^%s$' % _('privacy/'), app.meta.privacy, name='privacy'),
url(r'^%s$' % _('logout/'), app.meta.logout, name='logout'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')), app.content.answer_comments, name='answer_comments'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')), app.content.edit_answer, name='edit_answer'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), app.content.answer_revisions, name='answer_revisions'),
- url(r'^%s$' % _('questions/'), app.content.questions, name='questions'),
- url(r'^%s%s$' % (_('questions/'), _('ask/')), app.content.ask, name='ask'),
- url(r'^%s%s$' % (_('questions/'), _('unanswered/')), app.content.unanswered, name='unanswered'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')), app.content.edit_question, name='edit_question'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.content.close, name='close'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.content.reopen, name='reopen'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.content.answer, name='answer'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')), app.writers.answer_comments, name='answer_comments'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')), app.writers.edit_answer, name='edit_answer'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), app.readers.answer_revisions, name='answer_revisions'),
+ url(r'^%s$' % _('questions/'), app.readers.questions, name='questions'),
+ url(r'^%s%s$' % (_('questions/'), _('ask/')), app.writers.ask, name='ask'),
+ url(r'^%s%s$' % (_('questions/'), _('unanswered/')), app.readers.unanswered, name='unanswered'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')), app.writers.edit_question, name='edit_question'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.commands.close, name='close'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.commands.reopen, name='reopen'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.writers.answer, name='answer'),
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.commands.vote, name='vote'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.content.question_revisions, name='question_revisions'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.content.question_comments, name='question_comments'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.readers.question_revisions, name='question_revisions'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.writers.question_comments, name='question_comments'),
url(r'^%s$' % _('command/'), app.commands.ajax_command, name='call_ajax'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \
- app.content.delete_comment, kwargs={'commented_object_type':'question'},\
+ app.writers.delete_comment, kwargs={'commented_object_type':'question'},\
name='delete_question_comment'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('answers/'), _('comments/'),_('delete/')), \
- app.content.delete_comment, kwargs={'commented_object_type':'answer'}, \
+ app.writers.delete_comment, kwargs={'commented_object_type':'answer'}, \
name='delete_answer_comment'), \
#place general question item in the end of other operations
- url(r'^%s(?P<id>\d+)/' % _('question/'), app.content.question, name='question'),
- url(r'^%s$' % _('tags/'), app.content.tags, name='tags'),
- url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.content.tag, name='tag_questions'),
+ url(r'^%s(?P<id>\d+)/' % _('question/'), app.readers.question, name='question'),
+ url(r'^%s$' % _('tags/'), app.readers.tags, name='tags'),
+ url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.readers.tag, name='tag_questions'),
url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.commands.mark_tag, \
kwargs={'reason':'good','action':'add'}, \
@@ -77,15 +79,15 @@ urlpatterns = patterns('',
url(r'^%s(?P<id>\d+)//*' % _('users/'), app.users.user, name='user'),
url(r'^%s$' % _('badges/'),app.meta.badges, name='badges'),
url(r'^%s(?P<id>\d+)//*' % _('badges/'), app.meta.badge, name='badge'),
- url(r'^%s%s$' % (_('messages/'), _('markread/')),app.meta.read_message, name='read_message'),
+ url(r'^%s%s$' % (_('messages/'), _('markread/')),app.commands.read_message, name='read_message'),
# (r'^admin/doc/' % _('admin/doc'), include('django.contrib.admindocs.urls')),
- (r'^%s(.*)' % _('nimda/'), admin.site.root),
- url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
- (r'^%s$' % _('upload/'), app.content.upload),
+ url(r'^%s(.*)' % _('nimda/'), admin.site.root, name='osqa_admin'),
+ url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}, name='feeds'),
+ url(r'^%s$' % _('upload/'), app.writers.upload, name='upload'),
url(r'^%s$' % _('books/'), app.books.books, name='books'),
url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.books.ask_book, name='ask_book'),
url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.books.book, name='book'),
- url(r'^%s$' % _('search/'), app.content.search, name='search'),
+ url(r'^%s$' % _('search/'), app.readers.search, name='search'),
url(r'^%s$' % _('feedback/'), app.meta.feedback, name='feedback'),
(r'^%sfb/' % _('account/'), include('fbconnect.urls')),
(r'^%s' % _('account/'), include('django_authopenid.urls')),
diff --git a/forum/views/README b/forum/views/README
new file mode 100644
index 00000000..7b6201cd
--- /dev/null
+++ b/forum/views/README
@@ -0,0 +1,12 @@
+readers.py - views strictly reading main content: questions, answers, tags and comments
+
+writers.py - views that write main content, with possible reading
+ note: deletion counts as writing in this case
+
+commands.py - data status changing commands, votes, question close/reopen
+
+users.py - user views - user listing and profiles
+
+meta.py - privacy, about, faq, feedback, logout, badges
+
+books.py - book views - to be moved to a books extension
diff --git a/forum/views/__init__.py b/forum/views/__init__.py
index a8e179b4..8b82d161 100644
--- a/forum/views/__init__.py
+++ b/forum/views/__init__.py
@@ -1,4 +1,5 @@
-import content
+import readers
+import writers
import commands
import users
import meta
diff --git a/forum/views/commands.py b/forum/views/commands.py
index 9ad07a2c..38915728 100644
--- a/forum/views/commands.py
+++ b/forum/views/commands.py
@@ -1,11 +1,14 @@
import datetime
from django.conf import settings
from django.utils import simplejson
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
+from django.template import RequestContext
from forum.models import *
+from forum.forms import CloseForm
from forum import auth
+from django.contrib.auth.decorators import login_required
from utils.decorators import ajax_method, ajax_login_required
import logging
@@ -278,3 +281,54 @@ def ajax_command(request):#refactor? view processing ajax commands - note "vote"
if request.POST['command'] == 'toggle-ignored-questions':
return ajax_toggle_ignored_questions(request)
+@login_required
+def close(request, id):#close question
+ """view to initiate and process
+ question close
+ """
+ question = get_object_or_404(Question, id=id)
+ if not auth.can_close_question(request.user, question):
+ return HttpResponse('Permission denied.')
+ if request.method == 'POST':
+ form = CloseForm(request.POST)
+ if form.is_valid():
+ reason = form.cleaned_data['reason']
+ question.closed = True
+ question.closed_by = request.user
+ question.closed_at = datetime.datetime.now()
+ question.close_reason = reason
+ question.save()
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = CloseForm()
+ return render_to_response('close.html', {
+ 'form' : form,
+ 'question' : question,
+ }, context_instance=RequestContext(request))
+
+@login_required
+def reopen(request, id):#re-open question
+ """view to initiate and process
+ question close
+ """
+ question = get_object_or_404(Question, id=id)
+ # open question
+ if not auth.can_reopen_question(request.user, question):
+ return HttpResponse('Permission denied.')
+ if request.method == 'POST' :
+ Question.objects.filter(id=question.id).update(closed=False,
+ closed_by=None, closed_at=None, close_reason=None)
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ return render_to_response('reopen.html', {
+ 'question' : question,
+ }, context_instance=RequestContext(request))
+
+#osqa-user communication system
+def read_message(request):#marks message a read
+ 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('')
diff --git a/forum/views/meta.py b/forum/views/meta.py
index b7a1183c..07e222b0 100644
--- a/forum/views/meta.py
+++ b/forum/views/meta.py
@@ -88,11 +88,3 @@ def badge(request, id):
'badge' : badge,
}, context_instance=RequestContext(request))
-#osqa-user communication system
-def read_message(request):#marks message a read
- 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('')
diff --git a/forum/views/content.py b/forum/views/readers.py
index 5ac6164e..88b786aa 100644
--- a/forum/views/content.py
+++ b/forum/views/readers.py
@@ -1,28 +1,20 @@
# encoding:utf-8
-import os.path
-import time, datetime, calendar, random
+import datetime
import logging
-from urllib import quote, unquote
+from urllib import unquote
from django.conf import settings
-from django.core.files.storage import default_storage
from django.shortcuts import render_to_response, get_object_or_404
-from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
from django.core.paginator import Paginator, EmptyPage, InvalidPage
-from django.template import RequestContext, loader
+from django.template import RequestContext
from django.utils.html import *
from django.utils import simplejson
-from django.core import serializers
-from django.db import transaction
-from django.db.models import Count, Q
-from django.contrib.contenttypes.models import ContentType
+from django.db.models import Q
from django.utils.translation import ugettext as _
-from django.utils.datastructures import SortedDict
from django.template.defaultfilters import slugify
-from django.core.exceptions import PermissionDenied
+from django.utils.datastructures import SortedDict
from utils.html import sanitize_html
-from utils.decorators import ajax_method, ajax_login_required
from markdown2 import Markdown
#from lxml.html.diff import htmldiff
from forum.diff import textDiff as htmldiff
@@ -34,6 +26,7 @@ from forum import auth
from utils.forms import get_next_url
# used in index page
+#refactor - move these numbers somewhere?
INDEX_PAGE_SIZE = 20
INDEX_AWARD_SIZE = 15
INDEX_TAGS_SIZE = 100
@@ -581,543 +574,4 @@ def answer_revisions(request, id):
'post': post,
'revisions': revisions,
}, context_instance=RequestContext(request))
-#system to collect user actions and change content and store in the database
-def create_new_answer( question=None, author=None, #service subroutine - refactor
- added_at=None, wiki=False,\
- text='', email_notify=False):
- """refactor
- non-view subroutine
- initializes the answer and revision
- and updates stuff in the corresponding question
- probably there is more Django-ish way to do it
- """
-
- 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:
- 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
-
-def create_new_question(title=None,author=None,added_at=None, #service subroutine - refactor
- wiki=False,tagnames=None,summary=None,
- text=None):
- """refactor
- this is not a view saves new question and revision
- 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
-
-def upload(request):#ajax upload file to a question or answer
- class FileTypeNotAllow(Exception):
- pass
- class FileSizeNotAllow(Exception):
- pass
- class UploadPermissionNotAuthorized(Exception):
- pass
-
- #<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>
- xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>"
-
- try:
- f = request.FILES['file-upload']
- # check upload permission
- if not auth.can_upload_files(request.user):
- raise UploadPermissionNotAuthorized
-
- # check file type
- file_name_suffix = os.path.splitext(f.name)[1].lower()
- if not file_name_suffix in settings.ALLOW_FILE_TYPES:
- raise FileTypeNotAllow
-
- # generate new file name
- new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
- # use default storage to store file
- default_storage.save(new_file_name, f)
- # check file size
- # byte
- size = default_storage.size(new_file_name)
- if size > settings.ALLOW_MAX_FILE_SIZE:
- default_storage.delete(new_file_name)
- raise FileSizeNotAllow
-
- result = xml_template % ('Good', '', default_storage.url(new_file_name))
- except UploadPermissionNotAuthorized:
- result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '')
- except FileTypeNotAllow:
- result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '')
- except FileSizeNotAllow:
- result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '')
- except Exception:
- result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '')
-
- return HttpResponse(result, mimetype="application/xml")
-
-#@login_required #actually you can post anonymously, but then must register
-def ask(request):#view used to ask a new question
- """a view to ask a new question
- gives space for q title, body, tags and checkbox for to post as wiki
-
- user can start posting a question anonymously but then
- must login/register in order for the question go be shown
- """
- if request.method == "POST":
- form = AskForm(request.POST)
- if form.is_valid():
-
- added_at = datetime.datetime.now()
- title = strip_tags(form.cleaned_data['title'].strip())
- 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]
-
- 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(reverse('user_signin_new_question'))
- 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))
-
-@login_required
-def close(request, id):#close question
- """view to initiate and process
- question close
- """
- question = get_object_or_404(Question, id=id)
- if not auth.can_close_question(request.user, question):
- return HttpResponse('Permission denied.')
- if request.method == 'POST':
- form = CloseForm(request.POST)
- if form.is_valid():
- reason = form.cleaned_data['reason']
- question.closed = True
- question.closed_by = request.user
- question.closed_at = datetime.datetime.now()
- question.close_reason = reason
- question.save()
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = CloseForm()
- return render_to_response('close.html', {
- 'form' : form,
- 'question' : question,
- }, context_instance=RequestContext(request))
-
-@login_required
-def reopen(request, id):#re-open question
- """view to initiate and process
- question close
- """
- question = get_object_or_404(Question, id=id)
- # open question
- if not auth.can_reopen_question(request.user, question):
- return HttpResponse('Permission denied.')
- if request.method == 'POST' :
- Question.objects.filter(id=question.id).update(closed=False,
- closed_by=None, closed_at=None, close_reason=None)
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- return render_to_response('reopen.html', {
- 'question' : question,
- }, context_instance=RequestContext(request))
-
-@login_required
-def edit_question(request, id):#edit or retag a question
- """view to edit question
- """
- question = get_object_or_404(Question, id=id)
- if question.deleted and not auth.can_view_deleted_post(request.user, question):
- raise Http404
- if auth.can_edit_post(request.user, question):
- return _edit_question(request, question)
- elif auth.can_retag_questions(request.user):
- return _retag_question(request, question)
- else:
- raise Http404
-
-def _retag_question(request, question):#non-url subview of edit question - just retag
- """retag question sub-view used by
- view "edit_question"
- """
- if request.method == 'POST':
- form = RetagQuestionForm(question, request.POST)
- if form.is_valid():
- if form.has_changed():
- latest_revision = question.get_latest_revision()
- retagged_at = datetime.datetime.now()
- # Update the Question itself
- Question.objects.filter(id=question.id).update(
- tagnames = form.cleaned_data['tags'],
- last_edited_at = retagged_at,
- last_edited_by = request.user,
- last_activity_at = retagged_at,
- last_activity_by = request.user
- )
- # Update the Question's tag associations
- tags_updated = Question.objects.update_tags(question,
- form.cleaned_data['tags'], request.user)
- # Create a new revision
- QuestionRevision.objects.create(
- question = question,
- title = latest_revision.title,
- author = request.user,
- revised_at = retagged_at,
- tagnames = form.cleaned_data['tags'],
- summary = CONST['retagged'],
- text = latest_revision.text
- )
- # send tags updated singal
- tags_updated.send(sender=question.__class__, question=question)
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = RetagQuestionForm(question)
- return render_to_response('question_retag.html', {
- 'question': question,
- 'form' : form,
- 'tags' : _get_tags_cache_json(),
- }, context_instance=RequestContext(request))
-
-def _edit_question(request, question):#non-url subview of edit_question - just edit the body/title
- latest_revision = question.get_latest_revision()
- revision_form = None
- if request.method == 'POST':
- if 'select_revision' in request.POST:
- # user has changed revistion number
- revision_form = RevisionForm(question, latest_revision, request.POST)
- if revision_form.is_valid():
- # Replace with those from the selected revision
- form = EditQuestionForm(question,
- QuestionRevision.objects.get(question=question,
- revision=revision_form.cleaned_data['revision']))
- else:
- form = EditQuestionForm(question, latest_revision, request.POST)
- else:
- # Always check modifications against the latest revision
- form = EditQuestionForm(question, latest_revision, request.POST)
- if form.is_valid():
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- if form.has_changed():
- edited_at = datetime.datetime.now()
- tags_changed = (latest_revision.tagnames !=
- form.cleaned_data['tags'])
- tags_updated = False
- # Update the Question itself
- updated_fields = {
- 'title': form.cleaned_data['title'],
- 'last_edited_at': edited_at,
- 'last_edited_by': request.user,
- 'last_activity_at': edited_at,
- 'last_activity_by': request.user,
- 'tagnames': form.cleaned_data['tags'],
- 'summary': strip_tags(html)[:120],
- 'html': html,
- }
-
- # only save when it's checked
- # because wiki doesn't allow to be edited if last version has been enabled already
- # and we make sure this in forms.
- if ('wiki' in form.cleaned_data and
- form.cleaned_data['wiki']):
- updated_fields['wiki'] = True
- updated_fields['wikified_at'] = edited_at
-
- Question.objects.filter(
- id=question.id).update(**updated_fields)
- # Update the Question's tag associations
- if tags_changed:
- tags_updated = Question.objects.update_tags(
- question, form.cleaned_data['tags'], request.user)
- # Create a new revision
- revision = QuestionRevision(
- question = question,
- title = form.cleaned_data['title'],
- author = request.user,
- revised_at = edited_at,
- tagnames = form.cleaned_data['tags'],
- text = form.cleaned_data['text'],
- )
- if form.cleaned_data['summary']:
- revision.summary = form.cleaned_data['summary']
- else:
- revision.summary = 'No.%s Revision' % latest_revision.revision
- revision.save()
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
-
- revision_form = RevisionForm(question, latest_revision)
- form = EditQuestionForm(question, latest_revision)
- return render_to_response('question_edit.html', {
- 'question': question,
- 'revision_form': revision_form,
- 'form' : form,
- 'tags' : _get_tags_cache_json()
- }, context_instance=RequestContext(request))
-
-@login_required
-def edit_answer(request, id):
- answer = get_object_or_404(Answer, id=id)
- if answer.deleted and not auth.can_view_deleted_post(request.user, answer):
- raise Http404
- elif not auth.can_edit_post(request.user, answer):
- raise Http404
- else:
- latest_revision = answer.get_latest_revision()
- if request.method == "POST":
- if 'select_revision' in request.POST:
- # user has changed revistion number
- revision_form = RevisionForm(answer, latest_revision, request.POST)
- if revision_form.is_valid():
- # Replace with those from the selected revision
- form = EditAnswerForm(answer,
- AnswerRevision.objects.get(answer=answer,
- revision=revision_form.cleaned_data['revision']))
- else:
- form = EditAnswerForm(answer, latest_revision, request.POST)
- else:
- form = EditAnswerForm(answer, latest_revision, request.POST)
- if form.is_valid():
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- if form.has_changed():
- edited_at = datetime.datetime.now()
- updated_fields = {
- 'last_edited_at': edited_at,
- 'last_edited_by': request.user,
- 'html': html,
- }
- Answer.objects.filter(id=answer.id).update(**updated_fields)
-
- revision = AnswerRevision(
- answer=answer,
- author=request.user,
- revised_at=edited_at,
- text=form.cleaned_data['text']
- )
-
- if form.cleaned_data['summary']:
- revision.summary = form.cleaned_data['summary']
- else:
- revision.summary = 'No.%s Revision' % latest_revision.revision
- revision.save()
-
- answer.question.last_activity_at = edited_at
- answer.question.last_activity_by = request.user
- answer.question.save()
-
- return HttpResponseRedirect(answer.get_absolute_url())
- else:
- revision_form = RevisionForm(answer, latest_revision)
- form = EditAnswerForm(answer, latest_revision)
- return render_to_response('answer_edit.html', {
- 'answer': answer,
- 'revision_form': revision_form,
- 'form': form,
- }, context_instance=RequestContext(request))
-
-def answer(request, id):#process a new answer
- question = get_object_or_404(Question, id=id)
- if request.method == "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()
-
- 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(reverse('user_signin_new_answer'))
-
- return HttpResponseRedirect(question.get_absolute_url())
-
-def question_comments(request, id):#ajax handler for loading comments to question
- question = get_object_or_404(Question, id=id)
- user = request.user
- return __comments(request, question, 'question')
-
-def answer_comments(request, id):#ajax handler for loading comments on answer
- answer = get_object_or_404(Answer, id=id)
- user = request.user
- return __comments(request, answer, 'answer')
-
-def __comments(request, obj, type):#non-view generic ajax handler to load comments to an object
- # only support get post comments by ajax now
- user = request.user
- if request.is_ajax():
- if request.method == "GET":
- response = __generate_comments_json(obj, type, user)
- elif request.method == "POST":
- if auth.can_add_comments(user,obj):
- comment_data = request.POST.get('comment')
- comment = Comment(content_object=obj, comment=comment_data, user=request.user)
- comment.save()
- obj.comment_count = obj.comment_count + 1
- obj.save()
- response = __generate_comments_json(obj, type, user)
- else:
- response = HttpResponseForbidden(mimetype="application/json")
- return response
-
-def __generate_comments_json(obj, type, user):#non-view generates json data for the post comments
- comments = obj.comments.all().order_by('id')
- # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null}
- json_comments = []
- from forum.templatetags.extra_tags import diff_date
- for comment in comments:
- comment_user = comment.user
- delete_url = ""
- if user != None and auth.can_delete_comment(user, comment):
- #/posts/392845/comments/219852/delete
- #todo translate this url
- delete_url = reverse(index) + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id)
- json_comments.append({"id" : comment.id,
- "object_id" : obj.id,
- "comment_age" : diff_date(comment.added_at),
- "text" : comment.comment,
- "user_display_name" : comment_user.username,
- "user_url" : comment_user.get_profile_url(),
- "delete_url" : delete_url
- })
-
- data = simplejson.dumps(json_comments)
- return HttpResponse(data, mimetype="application/json")
-
-def delete_comment(request, object_id='', comment_id='', commented_object_type=None):#ajax handler to delete comment
- response = None
- commented_object = None
- if commented_object_type == 'question':
- commented_object = Question
- elif commented_object_type == 'answer':
- commented_object = Answer
- if request.is_ajax():
- comment = get_object_or_404(Comment, id=comment_id)
- if auth.can_delete_comment(request.user, comment):
- obj = get_object_or_404(commented_object, id=object_id)
- obj.comments.remove(comment)
- obj.comment_count = obj.comment_count - 1
- obj.save()
- user = request.user
- return __generate_comments_json(obj, commented_object_type, user)
- raise PermissionDenied()
diff --git a/forum/views/users.py b/forum/views/users.py
index 2c7f5c34..a7ff252d 100644
--- a/forum/views/users.py
+++ b/forum/views/users.py
@@ -938,3 +938,4 @@ def user(request, id):
from forum.views import users
func = user_view.view_func
return func(request, id, user_view)
+
diff --git a/forum/views/writers.py b/forum/views/writers.py
new file mode 100644
index 00000000..33b49423
--- /dev/null
+++ b/forum/views/writers.py
@@ -0,0 +1,535 @@
+# encoding:utf-8
+import os.path
+import time, datetime, random
+import logging
+from django.core.files.storage import default_storage
+from django.shortcuts import render_to_response, get_object_or_404
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
+from django.template import RequestContext
+from django.utils.html import *
+from django.utils import simplejson
+from django.utils.translation import ugettext as _
+from django.core.exceptions import PermissionDenied
+
+from utils.html import sanitize_html
+from markdown2 import Markdown
+from forum.forms import *
+from forum.models import *
+from forum.auth import *
+from forum.const import *
+from forum import auth
+from utils.forms import get_next_url
+from forum.views.readers import _get_tags_cache_json
+
+# used in index page
+INDEX_PAGE_SIZE = 20
+INDEX_AWARD_SIZE = 15
+INDEX_TAGS_SIZE = 100
+# used in tags list
+DEFAULT_PAGE_SIZE = 60
+# used in questions
+QUESTIONS_PAGE_SIZE = 10
+# used in answers
+ANSWERS_PAGE_SIZE = 10
+
+markdowner = Markdown(html4tags=True)
+
+#system to collect user actions and change content and store in the database
+def create_new_answer( question=None, author=None, #service subroutine - refactor
+ added_at=None, wiki=False,\
+ text='', email_notify=False):
+ """refactor
+ non-view subroutine
+ initializes the answer and revision
+ and updates stuff in the corresponding question
+ probably there is more Django-ish way to do it
+ """
+
+ 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:
+ 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
+
+def create_new_question(title=None,author=None,added_at=None, #service subroutine - refactor
+ wiki=False,tagnames=None,summary=None,
+ text=None):
+ """refactor
+ this is not a view saves new question and revision
+ 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
+
+def upload(request):#ajax upload file to a question or answer
+ class FileTypeNotAllow(Exception):
+ pass
+ class FileSizeNotAllow(Exception):
+ pass
+ class UploadPermissionNotAuthorized(Exception):
+ pass
+
+ #<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>
+ xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>"
+
+ try:
+ f = request.FILES['file-upload']
+ # check upload permission
+ if not auth.can_upload_files(request.user):
+ raise UploadPermissionNotAuthorized
+
+ # check file type
+ file_name_suffix = os.path.splitext(f.name)[1].lower()
+ if not file_name_suffix in settings.ALLOW_FILE_TYPES:
+ raise FileTypeNotAllow
+
+ # generate new file name
+ new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
+ # use default storage to store file
+ default_storage.save(new_file_name, f)
+ # check file size
+ # byte
+ size = default_storage.size(new_file_name)
+ if size > settings.ALLOW_MAX_FILE_SIZE:
+ default_storage.delete(new_file_name)
+ raise FileSizeNotAllow
+
+ result = xml_template % ('Good', '', default_storage.url(new_file_name))
+ except UploadPermissionNotAuthorized:
+ result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '')
+ except FileTypeNotAllow:
+ result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '')
+ except FileSizeNotAllow:
+ result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '')
+ except Exception:
+ result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '')
+
+ return HttpResponse(result, mimetype="application/xml")
+
+#@login_required #actually you can post anonymously, but then must register
+def ask(request):#view used to ask a new question
+ """a view to ask a new question
+ gives space for q title, body, tags and checkbox for to post as wiki
+
+ user can start posting a question anonymously but then
+ must login/register in order for the question go be shown
+ """
+ if request.method == "POST":
+ form = AskForm(request.POST)
+ if form.is_valid():
+
+ added_at = datetime.datetime.now()
+ title = strip_tags(form.cleaned_data['title'].strip())
+ 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]
+
+ 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(reverse('user_signin_new_question'))
+ 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))
+
+@login_required
+def edit_question(request, id):#edit or retag a question
+ """view to edit question
+ """
+ question = get_object_or_404(Question, id=id)
+ if question.deleted and not auth.can_view_deleted_post(request.user, question):
+ raise Http404
+ if auth.can_edit_post(request.user, question):
+ return _edit_question(request, question)
+ elif auth.can_retag_questions(request.user):
+ return _retag_question(request, question)
+ else:
+ raise Http404
+
+def _retag_question(request, question):#non-url subview of edit question - just retag
+ """retag question sub-view used by
+ view "edit_question"
+ """
+ if request.method == 'POST':
+ form = RetagQuestionForm(question, request.POST)
+ if form.is_valid():
+ if form.has_changed():
+ latest_revision = question.get_latest_revision()
+ retagged_at = datetime.datetime.now()
+ # Update the Question itself
+ Question.objects.filter(id=question.id).update(
+ tagnames = form.cleaned_data['tags'],
+ last_edited_at = retagged_at,
+ last_edited_by = request.user,
+ last_activity_at = retagged_at,
+ last_activity_by = request.user
+ )
+ # Update the Question's tag associations
+ tags_updated = Question.objects.update_tags(question,
+ form.cleaned_data['tags'], request.user)
+ # Create a new revision
+ QuestionRevision.objects.create(
+ question = question,
+ title = latest_revision.title,
+ author = request.user,
+ revised_at = retagged_at,
+ tagnames = form.cleaned_data['tags'],
+ summary = CONST['retagged'],
+ text = latest_revision.text
+ )
+ # send tags updated singal
+ tags_updated.send(sender=question.__class__, question=question)
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = RetagQuestionForm(question)
+ return render_to_response('question_retag.html', {
+ 'question': question,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json(),
+ }, context_instance=RequestContext(request))
+
+def _edit_question(request, question):#non-url subview of edit_question - just edit the body/title
+ latest_revision = question.get_latest_revision()
+ revision_form = None
+ if request.method == 'POST':
+ if 'select_revision' in request.POST:
+ # user has changed revistion number
+ revision_form = RevisionForm(question, latest_revision, request.POST)
+ if revision_form.is_valid():
+ # Replace with those from the selected revision
+ form = EditQuestionForm(question,
+ QuestionRevision.objects.get(question=question,
+ revision=revision_form.cleaned_data['revision']))
+ else:
+ form = EditQuestionForm(question, latest_revision, request.POST)
+ else:
+ # Always check modifications against the latest revision
+ form = EditQuestionForm(question, latest_revision, request.POST)
+ if form.is_valid():
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ if form.has_changed():
+ edited_at = datetime.datetime.now()
+ tags_changed = (latest_revision.tagnames !=
+ form.cleaned_data['tags'])
+ tags_updated = False
+ # Update the Question itself
+ updated_fields = {
+ 'title': form.cleaned_data['title'],
+ 'last_edited_at': edited_at,
+ 'last_edited_by': request.user,
+ 'last_activity_at': edited_at,
+ 'last_activity_by': request.user,
+ 'tagnames': form.cleaned_data['tags'],
+ 'summary': strip_tags(html)[:120],
+ 'html': html,
+ }
+
+ # only save when it's checked
+ # because wiki doesn't allow to be edited if last version has been enabled already
+ # and we make sure this in forms.
+ if ('wiki' in form.cleaned_data and
+ form.cleaned_data['wiki']):
+ updated_fields['wiki'] = True
+ updated_fields['wikified_at'] = edited_at
+
+ Question.objects.filter(
+ id=question.id).update(**updated_fields)
+ # Update the Question's tag associations
+ if tags_changed:
+ tags_updated = Question.objects.update_tags(
+ question, form.cleaned_data['tags'], request.user)
+ # Create a new revision
+ revision = QuestionRevision(
+ question = question,
+ title = form.cleaned_data['title'],
+ author = request.user,
+ revised_at = edited_at,
+ tagnames = form.cleaned_data['tags'],
+ text = form.cleaned_data['text'],
+ )
+ if form.cleaned_data['summary']:
+ revision.summary = form.cleaned_data['summary']
+ else:
+ revision.summary = 'No.%s Revision' % latest_revision.revision
+ revision.save()
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+
+ revision_form = RevisionForm(question, latest_revision)
+ form = EditQuestionForm(question, latest_revision)
+ return render_to_response('question_edit.html', {
+ 'question': question,
+ 'revision_form': revision_form,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json()
+ }, context_instance=RequestContext(request))
+
+@login_required
+def edit_answer(request, id):
+ answer = get_object_or_404(Answer, id=id)
+ if answer.deleted and not auth.can_view_deleted_post(request.user, answer):
+ raise Http404
+ elif not auth.can_edit_post(request.user, answer):
+ raise Http404
+ else:
+ latest_revision = answer.get_latest_revision()
+ if request.method == "POST":
+ if 'select_revision' in request.POST:
+ # user has changed revistion number
+ revision_form = RevisionForm(answer, latest_revision, request.POST)
+ if revision_form.is_valid():
+ # Replace with those from the selected revision
+ form = EditAnswerForm(answer,
+ AnswerRevision.objects.get(answer=answer,
+ revision=revision_form.cleaned_data['revision']))
+ else:
+ form = EditAnswerForm(answer, latest_revision, request.POST)
+ else:
+ form = EditAnswerForm(answer, latest_revision, request.POST)
+ if form.is_valid():
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ if form.has_changed():
+ edited_at = datetime.datetime.now()
+ updated_fields = {
+ 'last_edited_at': edited_at,
+ 'last_edited_by': request.user,
+ 'html': html,
+ }
+ Answer.objects.filter(id=answer.id).update(**updated_fields)
+
+ revision = AnswerRevision(
+ answer=answer,
+ author=request.user,
+ revised_at=edited_at,
+ text=form.cleaned_data['text']
+ )
+
+ if form.cleaned_data['summary']:
+ revision.summary = form.cleaned_data['summary']
+ else:
+ revision.summary = 'No.%s Revision' % latest_revision.revision
+ revision.save()
+
+ answer.question.last_activity_at = edited_at
+ answer.question.last_activity_by = request.user
+ answer.question.save()
+
+ return HttpResponseRedirect(answer.get_absolute_url())
+ else:
+ revision_form = RevisionForm(answer, latest_revision)
+ form = EditAnswerForm(answer, latest_revision)
+ return render_to_response('answer_edit.html', {
+ 'answer': answer,
+ 'revision_form': revision_form,
+ 'form': form,
+ }, context_instance=RequestContext(request))
+
+def answer(request, id):#process a new answer
+ question = get_object_or_404(Question, id=id)
+ if request.method == "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()
+
+ 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(reverse('user_signin_new_answer'))
+
+ return HttpResponseRedirect(question.get_absolute_url())
+
+def __generate_comments_json(obj, type, user):#non-view generates json data for the post comments
+ comments = obj.comments.all().order_by('id')
+ # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null}
+ json_comments = []
+ from forum.templatetags.extra_tags import diff_date
+ for comment in comments:
+ comment_user = comment.user
+ delete_url = ""
+ if user != None and auth.can_delete_comment(user, comment):
+ #/posts/392845/comments/219852/delete
+ #todo translate this url
+ delete_url = reverse(index) + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id)
+ json_comments.append({"id" : comment.id,
+ "object_id" : obj.id,
+ "comment_age" : diff_date(comment.added_at),
+ "text" : comment.comment,
+ "user_display_name" : comment_user.username,
+ "user_url" : comment_user.get_profile_url(),
+ "delete_url" : delete_url
+ })
+
+ data = simplejson.dumps(json_comments)
+ return HttpResponse(data, mimetype="application/json")
+
+
+def question_comments(request, id):#ajax handler for loading comments to question
+ question = get_object_or_404(Question, id=id)
+ user = request.user
+ return __comments(request, question, 'question')
+
+def answer_comments(request, id):#ajax handler for loading comments on answer
+ answer = get_object_or_404(Answer, id=id)
+ user = request.user
+ return __comments(request, answer, 'answer')
+
+def __comments(request, obj, type):#non-view generic ajax handler to load comments to an object
+ # only support get post comments by ajax now
+ user = request.user
+ if request.is_ajax():
+ if request.method == "GET":
+ response = __generate_comments_json(obj, type, user)
+ elif request.method == "POST":
+ if auth.can_add_comments(user,obj):
+ comment_data = request.POST.get('comment')
+ comment = Comment(content_object=obj, comment=comment_data, user=request.user)
+ comment.save()
+ obj.comment_count = obj.comment_count + 1
+ obj.save()
+ response = __generate_comments_json(obj, type, user)
+ else:
+ response = HttpResponseForbidden(mimetype="application/json")
+ return response
+
+def delete_comment(request, object_id='', comment_id='', commented_object_type=None):#ajax handler to delete comment
+ response = None
+ commented_object = None
+ if commented_object_type == 'question':
+ commented_object = Question
+ elif commented_object_type == 'answer':
+ commented_object = Answer
+
+ if request.is_ajax():
+ comment = get_object_or_404(Comment, id=comment_id)
+ if auth.can_delete_comment(request.user, comment):
+ obj = get_object_or_404(commented_object, id=object_id)
+ obj.comments.remove(comment)
+ obj.comment_count = obj.comment_count - 1
+ obj.save()
+ user = request.user
+ return __generate_comments_json(obj, commented_object_type, user)
+ raise PermissionDenied()