Modified:
/trunk/pirate-politics/new_templates/base.html
/trunk/pirate-politics/new_templates/issue_detail.html
/trunk/pirate-politics/pirate_comments/models.py
/trunk/pirate-politics/pirate_comments/templatetags/commenttags.py
/trunk/pirate-politics/pirate_issues/templatetags/issuetags.py
/trunk/pirate-politics/static/style.css
=======================================
--- /trunk/pirate-politics/new_templates/base.html Tue Jan 25 15:15:31 2011
+++ /trunk/pirate-politics/new_templates/base.html Wed Jan 26 15:29:32 2011
@@ -28,8 +28,8 @@
<script type="text/javascript">
- $(document).ready(function(){
- $('#comments').collapsible({xoffset:'-20',yoffset:'50',
defaulthide: false});
+ jQuery(document).ready(function(){
+ $('#collapsible_comments').collapsible();
});
function changeText(text,div_id)
=======================================
--- /trunk/pirate-politics/new_templates/issue_detail.html Tue Jan 25
15:15:31 2011
+++ /trunk/pirate-politics/new_templates/issue_detail.html Wed Jan 26
15:29:32 2011
@@ -83,15 +83,16 @@
{% include '_commentform.html' %}
- {% pp_load_comments object=pp_issue.issue %}
-
- {% for node, count in pp_comment.nodes %}
-
- {% include '_commentleaf.html' %}
-
- {% endfor %}
-
- {% endpp_load_comments%}
+ <div class="commentstructure">
+
+ {% pp_comment_list_get user=request.user
object=pp_issue.issue.id request=request %}
+
+ {% comment %} {{ pp_comment.debug_comments }} {%
endcomment %}
+ {{ pp_comment.comments|safe }}
+
+ {% endpp_comment_list_get%}
+
+ </div>
{% endpp_get_issue %}
=======================================
--- /trunk/pirate-politics/pirate_comments/models.py Tue Jan 25 15:15:31
2011
+++ /trunk/pirate-politics/pirate_comments/models.py Wed Jan 26 15:29:32
2011
@@ -12,38 +12,24 @@
from pirate_core.views import template_for_model
-class PirateComment(models.Model):
+class Comment(models.Model):
user = models.ForeignKey(User)
- submit_date = models.DateTimeField('date_published',auto_now_add=True)
+ submit_date = models.DateTimeField('date_published')
text = models.TextField(max_length=1200)
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_pirate_%(class)s")
object_pk = models.IntegerField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type",
fk_field="object_pk")
+ reply_to = models.ForeignKey('self',
related_name="comment_parent",null=True)
is_leaf = models.BooleanField()
+ is_root = models.BooleanField()
class Meta:
verbose_name = _('comment')
def __unicode__(self):
return self.user.username + ":" + str(self.submit_date)
-
-
-class NSComment(NS_Node):
- user = models.ForeignKey(User)
- text = models.TextField()
-
- #created = models.DateTimeField(auto_now_add=True)
- # Exception Value: Cannot use None as a query value
- created = models.DateTimeField(editable=False)
-
- content_type = models.ForeignKey(ContentType,
- verbose_name=_('content type'),
-
related_name="content_type_set_for_%(class)s")
- object_pk = models.IntegerField(_('object ID'))
- content_object = generic.GenericForeignKey(ct_field="content_type",
fk_field="object_pk")
-
@models.permalink
#This must be added to all classes that can be tagged
@@ -51,16 +37,15 @@
content_type = self.content_type
path = template_for_model(str(content_type)) + "?_t=" +
str(content_type.pk) + "&_o=" + str(self.object_pk)
return path
-
- def __unicode__(self):
- return u'NS_Comment %d: %s' % (self.id, self.text)
-
- class Meta:
- verbose_name = _('nscomment')
-
- # when adding a custom Meta class to a NS model, the ordering must
be
- # set again
-
-
-admin.site.register(NSComment)
-
+
+ def save(self, *args, **kwargs):
+ new_comment = super(Comment, self).save(*args, **kwargs)
+ try:
+ new_comment.reply_to.is_leaf = False
+ new_comment.reply_to.save()
+ except: pass
+ return new_comment
+
+
+admin.site.register(Comment)
+
=======================================
--- /trunk/pirate-politics/pirate_comments/templatetags/commenttags.py Tue
Jan 25 15:15:31 2011
+++ /trunk/pirate-politics/pirate_comments/templatetags/commenttags.py Wed
Jan 26 15:29:32 2011
@@ -2,9 +2,11 @@
from django import forms
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import simplejson
-from pirate_comments.models import PirateComment, NSComment
+from pirate_comments.models import Comment
from django.db import transaction
+from django.middleware import csrf
from django.contrib.contenttypes.models import ContentType
+from pirate_profile.models import Profile
import datetime
@@ -20,77 +22,100 @@
get_namespace = namespace_get('pp_comment')
@block
-def pp_load_comments(context, nodelist, *args, **kwargs):
-
+def pp_comment_get(context, nodelist, *args, **kwargs):
+ pass
+
+@block
+def pp_comment_list_get(context, nodelist, *args, **kwargs):
+
+ """we have to render the tree html here, because recursive includes
are not allowed in django templates
+ this could be more efficient with pre/post order tree traversal, but
for now this suffices.
+ mptt and treebeard both are not designed for GAE, need a tree
traversal library for non-rel"""
+
context.push()
namespace = get_namespace(context)
- obj = kwargs.pop('object', None)
- node_id = kwargs.pop('node_id',None)
-
- if obj is None:
+ object_pk = kwargs.get('object', None)
+ user = kwargs.get('user',None) #needs request.user for reply submission
+ if object_pk is None:
raise ValueError("pp_consensus_get tag requires that a consensus
object be passed "
"to it assigned to the 'object=' argument,
and that the str "
"be assigned the string value 'consensus.")
- user = kwargs.pop('user', None)
-
- if node_id:
- root = get_object_or_404(NSComment, id=node_id)
- if root.get_depth() != 1:
- # meh not really a root node, redirecting...
- raise
HttpRedirectException(HttpResponseRedirect(obj.get_absolute_url()))
- else:
- root = None
-
- if node_id:
- descendants = root.get_descendants()
- nodes = [(root, len(descendants))] + [
- (node, node.get_children_count())
- for node in descendants
- ]
- namespace['mainpage'] = False
- namespace['nodes'] = nodes
-
- else:
- namespace['mainpage'] = True
- nodes = NSComment.objects.all()
- nodes = nodes.filter(object_pk=obj.pk)
- namespace['nodes'] = [(node, node.get_descendant_count())
- for node in nodes if node.get_depth() == 1]
- namespace['total_comments'] = len(namespace['nodes']) + \
- sum([count for _, count in
namespace['nodes']])
- namespace['treetype'] = 'ns'
-
+
+ request = kwargs.get('request',None)
+
+ comment_tree = []
+
+ comments = Comment.objects.all()
+ comments = comments.filter(object_pk=object_pk,is_root=True)
+ comments = comments.order_by('-submit_date')
+
+ for c in comments:
+ if c.is_leaf:
+ comment_tree.append((c,0))
+ else:
+ comment_tree.append(get_children(object_pk,c))
+
+ tree_html = render_to_comment_tree_html(comment_tree, user, request)
+ tree_html = "<ul id='collapsible_comments'>" + tree_html + "</ul>"
+ namespace['comments'] = tree_html
+ namespace['debug_comments'] = comment_tree
output = nodelist.render(context)
context.pop()
return output
-@block
-def pp_comment_list_get(context, nodelist, *args, **kwargs):
-
- #load nested comments from treebeard
- context.push()
- namespace = get_namespace(context)
-
- object_pk = kwargs.pop('object', None)
- if object_pk is None:
- raise ValueError("pp_consensus_get tag requires that a consensus
object be passed "
- "to it assigned to the 'object=' argument,
and that the str "
- "be assigned the string value 'consensus.")
- user = kwargs.pop('user', None)
-
- comments = Comment.objects.all()
- comments = comments.filter(object_pk=object_pk)
-
-
- namespace['comments'] = comments
-
- output = nodelist.render(context)
- context.pop()
-
- return output
+def render_to_comment_tree_html(comment_tree, user, request):
+ """Comment tree in form:
+ c_tree = [[Comment1, [Comment1_2, Comment1_3, Comment1_4]],
Comment2, Comment 3]
+ must be rendered as a <ul>...<li><ul> ...
<li>render_comment()</li> ... </ul> </li> </ul>"""
+
+ ret_html = ""
+ for comment in comment_tree:
+ if isinstance(comment, tuple):
+ ret_html+="<li> "+ render_comment(comment[0], comment[1],
user, request) + "</li>"
+ elif isinstance(comment, list):
+ ret_html+= "<li> "+ render_comment(comment[0][0],
comment[0][1], user, request) + "<ul>" +
render_to_comment_tree_html(comment[1], user, request) + "</ul></li>"
+
+ return ret_html
+
+def generate_time_string(then, now):
+ time_to = abs(now - then)
+ hours = time_to.seconds / 360
+
+ if hours > 24:
+ ret = str(time_to.days) + " days ago"
+ elif hours == 0:
+ if time_to.seconds / 60 == 0:
+ ret = str(time_to.seconds) + " seconds ago"
+ else:
+ ret = str(time_to.seconds / 60) + " minutes ago"
+
+ else:
+ ret = str(time_to.seconds / 360 ) + " hours ago"
+ return " said " + ret
+
+
+#ok this is as ugly as it gets, but there's little other ways to generate
this html that I am aware of
+def render_comment(comment_obj, count, user, request):
+ content_type = ContentType.objects.get_for_model(comment_obj.user)
+ form =
ReplyForm(initial={'is_root':False,'is_leaf':True,'content_type':comment_obj.content_type,'object_pk':comment_obj.object_pk,'reply_to':comment_obj, 'submit_date':datetime.datetime.now(),'user':user})
+ if count == 0 or count > 1: plural = "s"
+ else: plural = ""
+ """returns the relevant html for a single atomic comment given the
comment object"""
+ return " <div class='comment_user'> <a href='/user_profile.html"
+ "?_t=" + str(content_type.pk) + "&_o=" + str(comment_obj.user.pk) + "'>"
+ str(comment_obj.user.username)+ "</a>" +
generate_time_string(comment_obj.submit_date, datetime.datetime.now())
+ ":</div><div>" + str(comment_obj.text) +"</div><div
class='comment_reply'>" + " <a href='javascript:;' onmousedown="
+ "'toggleSlide(" + '"add_reply' + str(comment_obj.id) + '"'
+ ");'>reply</a><div id='add_reply" + str(comment_obj.id) + "'
style='display:none; overflow:hidden; height:250px;'><form method='post'
action=''><div style='display:none'><input type='hidden'
name='csrfmiddlewaretoken' value='" + str(csrf.get_token(request)) + "'
/><input id='reply_to_object' type='hidden' name='reply_to_object' value='"
+ str(comment_obj.id)+ "'/></div>" + str(form.as_p()) + "<input
type='submit' class='button green' value='Submit
Comment'></form></div></div>"
+
+def get_children(object_pk,cur_comment):
+ get_list = []
+ comments = Comment.objects.all()
+ comments =
comments.filter(object_pk=object_pk,is_root=False,reply_to=cur_comment)
+ for c in comments:
+ if c.is_leaf:
+ get_list.append((c,0))
+ else:
+ get_list.append(get_children(object_pk,c))
+ return [(cur_comment, len(comments)), get_list]
@block
def pp_comment_form2(context, nodelist, *args, **kwargs):
@@ -103,14 +128,13 @@
obj = kwargs.get('object',None)
comment = kwargs.get('edit',None)
user = kwargs.get('user', None)
- tbmodel = NSComment
if obj == None:
raise ValueException("The designer must specify 'object' to the
form of type models.Model ")
if user == None:
raise ValueException("The designer must specify one of 'user',
generally request.user ")
- if isinstance(obj, NSComment):
+ if isinstance(obj, Comment):
root = obj
else:
root = None
@@ -168,24 +192,49 @@
POST = kwargs.get('POST', None)
path = kwargs.get('path', None)
- obj = kwargs.get('object',None)
+ reply_to = kwargs.get('object',None)
user = kwargs.get('user', None)
-
- if isinstance(obj, Comment):
- comment = obj
+ comment = kwargs.get('edit',None)
+
+ if isinstance(reply_to, Comment):
+ c_type = reply_to.content_type
+ object_pk = reply_to.object_pk
else:
- comment = None
+ c_type = ContentType.objects.get_for_model(reply_to.__class__)
+ object_pk = reply_to.id
+ reply_to = None
+
if POST and POST.get("form_id") == "pp_comment_form":
form = CommentForm(POST) if comment is None else CommentForm(POST,
instance=comment)
- comment = form.save(commit=False)
- comment.user = user
- c_type = ContentType.objects.get_for_model(obj.__class__)
- comment.content_type = c_type
- comment.object_pk = obj.pk
- comment.is_leaf = False
- comment.save()
-
- raise
HttpRedirectException(HttpResponseRedirect(obj.get_absolute_url()))
+ if form.is_valid():
+ newcomment = form.save(commit=False)
+ newcomment.user = user
+ newcomment.content_type = c_type
+ newcomment.object_pk = object_pk
+ newcomment.reply_to = reply_to
+ newcomment.is_leaf = True
+ newcomment.submit_date = datetime.datetime.now()
+ if reply_to == None: newcomment.is_root = True #non-root
comments will use auto-generated reply form
+ else: newcomment.is_root = False
+ newcomment.save()
+ raise
HttpRedirectException(HttpResponseRedirect(newcomment.content_object.get_absolute_url()))
+
+ if POST and POST.get("form_id") == "pp_reply_form":
+ form = ReplyForm(POST) if comment is None else ReplyForm(POST,
instance=comment)
+ if form.is_valid():
+ com_rep = int(POST.get("reply_to_object"))
+ newcomment = form.save(commit=False)
+ newcomment.user = user
+ newcomment.content_type = c_type
+ newcomment.object_pk = object_pk
+ newcomment.reply_to = Comment.objects.get(pk=com_rep)
+ newcomment.reply_to.is_leaf = False
+ newcomment.reply_to.save()
+ newcomment.is_leaf = True
+ newcomment.is_root = False
+ newcomment.submit_date = datetime.datetime.now()
+ newcomment.save()
+ raise
HttpRedirectException(HttpResponseRedirect(newcomment.content_object.get_absolute_url()))
else: form = CommentForm() if comment is None else
CommentForm(instance=comment)
@@ -196,7 +245,7 @@
return output
-class CommentForm(forms.Form):
+class CommentForm(forms.ModelForm):
'''
This form is used to allow creation and modification of comment
objects.
It extends FormMixin in order to provide a create() class method, which
@@ -208,11 +257,30 @@
new_comment = super(CommentForm, self).save(commit=commit)
return new_comment
+ class Meta:
+ model = Comment
+ exclude =
('user','object_pk','content_type','reply_to','submit_date', 'is_leaf','is_root', 'content_object')
#need to grab user from authenticatio
form_id = forms.CharField(widget=forms.HiddenInput(),
initial="pp_comment_form")
text = forms.CharField(widget=forms.Textarea)
- parent = forms.IntegerField(label='Parent',
- required=False,
- widget=forms.HiddenInput)
+
+class ReplyForm(forms.ModelForm):
+ '''
+ This form is used to allow creation and modification of comment
objects.
+ It extends FormMixin in order to provide a create() class method, which
+ is used to process POST, path, and object variables in a consistant
way,
+ and in order to automatically provide the form with a form_id.
+ '''
+
+ def save(self, commit=True):
+ new_comment = super(ReplyForm, self).save(commit=commit)
+ return new_comment
+
+ class Meta:
+ model = Comment
+ exclude =
('user','object_pk','content_type','reply_to','submit_date', 'is_leaf','is_root', 'content_object')
+ #need to grab user from authenticatio
+ form_id = forms.CharField(widget=forms.HiddenInput(),
initial="pp_reply_form")
+ text = forms.CharField(widget=forms.Textarea,label="")
=======================================
--- /trunk/pirate-politics/pirate_issues/templatetags/issuetags.py Tue Jan
25 15:15:31 2011
+++ /trunk/pirate-politics/pirate_issues/templatetags/issuetags.py Wed Jan
26 15:29:32 2011
@@ -155,10 +155,7 @@
#These don't work
###>>> issue1.delete(); issue2.delete(); issue3.delete();
###>>> topic1.delete(); topic2.delete()
-
-
"""
-
context.push()
namespace = get_namespace(context)
=======================================
--- /trunk/pirate-politics/static/style.css Tue Jan 25 19:05:51 2011
+++ /trunk/pirate-politics/static/style.css Wed Jan 26 15:29:32 2011
@@ -1,6 +1,6 @@
-html, body { margin: 0; padding: 0; }
+ohtml, body { margin: 0; padding: 0; }
body{
- font-family: Helvetica;
+ font-family: sans-serif;
background:#eee;
}
a { text-decoration: none; color: #91534D; }
@@ -92,7 +92,6 @@
border-right: 5px solid #ccc;
position:relative;
width:25%;
-
float:left;
}
@@ -383,6 +382,20 @@
left: 10px;
font-size: small;
}
+
+.comment_user, .comment_reply{
+ font-size:80%;
+
+}
+
+
+
+.commentstructure li {
+ padding:5px;
+}
+
+.collapsible_comments { padding: 0; margin: 0; }
+
.bullet h1, .bullet p {
color:white;
@@ -390,7 +403,7 @@
text-align:center;
border:0;
position:relative;
- left:0px;
+ left:-2px;
bottom:+2px;
overflow:visible;
}
@@ -608,8 +621,3 @@
.button.large { font-size: 125%; padding: 7px 12px; }
.button.large:active { padding: 8px 12px 6px; background-position: 0 top; }
-#comments { padding: 0; margin: 0; }
-#comments li { float: left; padding: 10px 0 0; }
-.comment { background: #fff; padding: 10px; display: block; overflow:
hidden; }
-.comment-author { float: left; width: 50px; font-size: 10px; text-align:
center; margin: 0 10px 10px 0; }
-.comment-body, .comment-body p { font-size: 12px; line-height: 18px;
margin: 0; }