def save(self, *args, **kwargs): # Sanitize the post body. self.html = html_util.parse_html(self.content) # Must add tags with instance method. This is just for safety. self.tag_val = html_util.strip_tags(self.tag_val) # Posts other than a question also carry the same tag if self.is_toplevel and self.type != Post.QUESTION: required_tag = self.get_type_display() if required_tag not in self.tag_val: self.tag_val += "," + required_tag if not self.id: # Set the titles if self.parent and not self.title: self.title = self.parent.title if self.parent and self.parent.type in (Post.ANSWER, Post.COMMENT): # Only comments may be added to a parent that is answer or comment. self.type = Post.COMMENT if self.type is None: # Set post type if it was left empty. self.type = self.COMMENT if self.parent else self.FORUM # This runs only once upon object creation. self.title = self.parent.title if self.parent else self.title self.lastedit_user = self.author self.status = self.status or Post.PENDING self.creation_date = self.creation_date or general_util.now() self.lastedit_date = self.creation_date # Set the timestamps on the parent if self.type == Post.ANSWER and self.parent: self.parent.lastedit_date = self.lastedit_date self.parent.lastedit_user = self.lastedit_user self.parent.save() # Recompute post reply count self.update_reply_count() super(Post, self).save(*args, **kwargs)
def email_handler(request): key = request.POST.get("key") if key != settings.EMAIL_REPLY_SECRET_KEY: data = dict(status="error", msg="key does not match") else: body = request.POST.get("body") body = smart_text(body, errors="ignore") # This is for debug only #fname = "%s/email-debug.txt" % settings.LIVE_DIR #fp = file(fname, "wt") #fp.write(body.encode("utf-8")) #fp.close() try: # Parse the incoming email. # Emails can be malformed in which case we will force utf8 on them before parsing try: msg = pyzmail.PyzMessage.factory(body) except Exception, exc: body = body.encode('utf8', errors='ignore') msg = pyzmail.PyzMessage.factory(body) # Extract the address from the address tuples. address = msg.get_addresses('to')[0][1] # Parse the token from the address. start, token, rest = address.split('+') # Verify that the token exists. token = ReplyToken.objects.get(token=token) # Find the post that the reply targets post, author = token.post, token.user # Extract the body of the email. part = msg.text_part or msg.html_part text = part.get_payload() # Remove the reply related content if settings.EMAIL_REPLY_REMOVE_QUOTED_TEXT: text = EmailReplyParser.parse_reply(text) else: text = text.decode("utf8", errors='replace') text = u"<div class='preformatted'>%s</div>" % text # Apply server specific formatting text = html_util.parse_html(text) # Apply the markdown on the text text = markdown.markdown(text) # Rate-limit sanity check, potentially a runaway process since = html_util.now() - timedelta(days=1) if Post.objects.filter(author=author, creation_date__gt=since).count() > settings.MAX_POSTS_TRUSTED_USER: raise Exception("too many posts created %s" % author.id) # Create the new post. post_type = Post.ANSWER if post.is_toplevel else Post.COMMENT obj = Post.objects.create(type=post_type, parent=post, content=text, author=author) # Delete the token. Disabled for now. # Old token should be deleted in the data pruning #token.delete() # Form the return message. data = dict(status="ok", id=obj.id) except Exception, exc: output = StringIO.StringIO() traceback.print_exc(file=output) data = dict(status="error", msg=str(output.getvalue()))
class NewPost(FormView): form_class = LongForm template_name = "post_edit.html" def get_context_data(self, **kwargs): context = super(NewPost, self).get_context_data(**kwargs) context['nodes_list'] = [n["node_name"] for n in ln.get_nodes_list()] return context def get(self, request, *args, **kwargs): initial = dict() if "memo" in kwargs: memo = json_util.deserialize_memo(kwargs["memo"]) if "parent_post_id" in memo: expected_memo_keys = ["parent_post_id", "post_type", "content"] self.form_class = ShortForm else: expected_memo_keys = [ "title", "post_type", "tag_val", "content" ] for key in expected_memo_keys: initial[key] = memo[key] else: # Attempt to prefill from GET parameters for key in "title post_type tag_val content".split(): value = request.GET.get(key) if value: initial[key] = value try: context = self.get_context_data(**kwargs) except Exception as e: logger.exception(e) raise context['form'] = self.form_class(initial=initial) context['errors_detected'] = False return render(request, self.template_name, context) def post(self, request, *args, **kwargs): if "memo" in kwargs: post_preview = PostPreview() # Some data comes from memo memo = json_util.deserialize_memo(kwargs["memo"]) if "parent_post_id" in memo: self.form_class = ShortForm parent_post_id = memo["parent_post_id"] # Find the parent. try: parent = Post.objects.get(pk=parent_post_id) except ObjectDoesNotExist, e: msg = "The post does not exist. Perhaps it was deleted request (Request: %s)".format( request) logger.error(msg) raise PostViewException(msg) post_preview.parent_post_id = parent_post_id post_preview.title = parent.title post_preview.tag_val = parent.tag_val post_preview.tag_value = html_util.split_tags(parent.tag_val) else: post_preview.title = memo["title"] post_preview.tag_val = memo["tag_val"] post_preview.tag_value = html_util.split_tags(memo["tag_val"]) post_preview.status = Post.OPEN post_preview.type = memo["post_type"] post_preview.date = datetime.utcfromtimestamp( memo["unixtime"]).replace(tzinfo=utc) # Validating the form. form = self.form_class(request.POST) if not form.is_valid(): try: context = self.get_context_data(**kwargs) except Exception as e: logger.exception(e) raise context['form'] = form context['errors_detected'] = True return render(request, self.template_name, context) if "memo" in kwargs: # Only new data comes from the HTML form post_preview.content = form.cleaned_data.get("content") post_preview.html = html_util.parse_html(post_preview.content) else: # Valid forms start here. data = form.cleaned_data # All data comes from the HTML form post_preview = PostPreview(title=data.get('title'), content=data.get('content'), tag_val=data.get('tag_val'), type=int(data.get('post_type')), date=general_util.now()) return HttpResponseRedirect( post_preview.get_absolute_url(memo=post_preview.serialize_memo()))
class NewAnswer(FormView): """ Creates a new post. """ form_class = ShortForm template_name = "post_edit.html" type_map = dict(answer=Post.ANSWER, comment=Post.COMMENT) post_type = None def get_context_data(self, **kwargs): context = super(NewAnswer, self).get_context_data(**kwargs) context['nodes_list'] = [n["node_name"] for n in ln.get_nodes_list()] return context def post(self, request, *args, **kwargs): """ This gets the initial "new answer" request, before the get method if everything looks good then we generate the memo (with parent post id) and re-direct to preview. If there are errors, we also render this back to the user directly from here. See `if not form.is_valid()` """ parent_post_id = int(self.kwargs['pid']) # URL sets the type for this new post post_type = self.type_map.get(self.post_type) assert post_type in [ Post.ANSWER, Post.COMMENT ], "I only support Answers and Comment types, got: {}".format( post_type) # Find the parent. try: parent = Post.objects.get(pk=parent_post_id) except ObjectDoesNotExist, exc: logger.error( "The post does not exist. Perhaps it was deleted request (Request: %s)", request) raise # Validating the form. form = self.form_class(request.POST) if not form.is_valid(): try: context = self.get_context_data(**kwargs) except Exception as e: logger.exception(e) raise context["form"] = form context["errors_detected"] = True return render(request, self.template_name, context) # Valid forms start here content = form.cleaned_data.get("content") post_preview = PostPreview() post_preview.parent_post_id = parent_post_id post_preview.title = parent.title post_preview.tag_val = parent.tag_val post_preview.tag_value = html_util.split_tags(parent.tag_val) post_preview.status = Post.OPEN post_preview.type = post_type post_preview.content = content post_preview.html = html_util.parse_html(content) post_preview.date = general_util.now() post_preview.memo = post_preview.serialize_memo() post_preview.clean_fields() return HttpResponseRedirect( post_preview.get_absolute_url(memo=post_preview.memo))
raise post_preview.parent_post_id = parent_post_id post_preview.title = parent.title post_preview.tag_val = parent.tag_val post_preview.tag_value = html_util.split_tags(parent.tag_val) else: post_preview.title = memo["title"] post_preview.tag_val = memo["tag_val"] post_preview.tag_value = html_util.split_tags(memo["tag_val"]) post_preview.status = Post.OPEN post_preview.type = memo["post_type"] post_preview.content = memo["content"] post_preview.html = html_util.parse_html(memo["content"]) post_preview.date = datetime.utcfromtimestamp( memo["unixtime"]).replace(tzinfo=utc) post_preview.memo = post_preview.serialize_memo() post_preview.clean_fields() return post_preview def get_context_data(self, **kwargs): context = super(PostPreviewView, self).get_context_data(**kwargs) view_obj = context.get("view") memo_serialized = view_obj.kwargs["memo"]