Added comment support, but don't enable it
authorSverre Rabbelier <srabbelier@gmail.com>
Thu, 05 Mar 2009 19:21:43 +0000
changeset 1678 80411f57f31a
parent 1677 b2cf6ad50a2a
child 1679 2b28763da7a4
Added comment support, but don't enable it Patch by: Sverre Rabbelier
app/soc/content/css/soc-090120.css
app/soc/logic/models/comment.py
app/soc/models/comment.py
app/soc/models/work.py
app/soc/templates/soc/comment/edit.html
app/soc/templates/soc/templatetags/_as_comment.html
app/soc/templates/soc/templatetags/_as_comments.html
app/soc/views/helper/templatetags/forms_helpers.py
app/soc/views/models/comment.py
--- a/app/soc/content/css/soc-090120.css	Thu Mar 05 19:20:02 2009 +0000
+++ b/app/soc/content/css/soc-090120.css	Thu Mar 05 19:21:43 2009 +0000
@@ -360,11 +360,16 @@
   margin-bottom: 1em;
 }
 
+/*  */
+
 span.unread {
   font-weight: bold;
   color: #FF0000;
 }
 
+span.edited {
+  color: #808080;
+}
  
 /* SEARCH FIELD */ 
 #search {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/models/comment.py	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,43 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Comment query functions.
+"""
+
+__authors__ = [
+  '"Matthew Wilkes" <matthew@matthewwilkes.co.uk>',
+  ]
+
+
+from soc.logic.models import base
+from soc.logic.models import linkable as linkable_logic
+
+import soc.models.comment
+
+
+class Logic(base.Logic):
+  """Logic methods for the comment model
+  """
+
+  def __init__(self, model=soc.models.comment.Comment,
+               base_model=None, scope_logic=linkable_logic):
+    """Defines the name, key_name and model for this entity.
+    """
+
+    super(Logic, self).__init__(model=model, base_model=base_model,
+                                scope_logic=scope_logic)
+
+logic = Logic()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/comment.py	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,74 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This module contains the comment Model."""
+
+__authors__ = [
+  '"Matthew Wilkes" <matthew@matthewwilkes.co.uk>',
+]
+
+from google.appengine.ext import db
+
+import soc.models.work
+import soc.models.user
+from soc.models import base
+import soc.models.linkable
+
+
+from django.utils.translation import ugettext as _
+
+
+class Comment(soc.models.linkable.Linkable):
+  """Model of a comment on a work.
+
+  A comment is associated with a Work, for example a Document or a Proposal,
+  and with a user, the author.  There are two types of comment, public (i.e.
+  visible to the student), or private (i.e. visible to programme/club staff).
+  Neither type are visible to people who are not connected to the work being
+  commented on.
+  """
+
+  #: A required many:1 relationship with a Work, where the comment entity
+  #: provides additional textual information about the commented work.
+  #: There is a backreference in Work called comments, which is a db.Query
+  #: instance
+  commented = db.ReferenceProperty(reference_class=soc.models.work.Work,
+                                  required=False, collection_name="comments")
+
+  #: A required many:1 relationship with a comment entity indicating
+  #: the user who provided that comment.  There is a backreference in Work
+  #: called comments, which is a db.Query instance.
+  author = db.ReferenceProperty(reference_class=soc.models.user.User,
+                                  required=True, collection_name="commented")
+
+  #: The rich textual content of this comment
+  content = db.TextProperty(verbose_name=_('Content'))
+
+  #: Indicated if the comment should be visible to the appropriate student
+  is_public = db.BooleanProperty(
+    verbose_name=_('Public comment'))
+
+  #: Date when the comment was added
+  created = db.DateTimeProperty(auto_now_add=True)
+
+  #: date when the work was last modified
+  modified = db.DateTimeProperty(auto_now=True)
+
+  # indicating wich user last modified the work. Used in displaying Work
+  modified_by = db.ReferenceProperty(reference_class=soc.models.user.User,
+                                     required=False,
+                                     collection_name="modified_comments",
+                                     verbose_name=_('Modified by'))
--- a/app/soc/models/work.py	Thu Mar 05 19:20:02 2009 +0000
+++ b/app/soc/models/work.py	Thu Mar 05 19:21:43 2009 +0000
@@ -39,6 +39,10 @@
     reviews)  a 1:many relationship between a Work and the zero or more
       Reviews of that Work.  This relation is implemented as the 'reviews'
       back-reference Query of the Review model 'reviewed' reference.
+
+    comments)  a 1:many relationship between a work and zero or more comments
+      about that work.  This is implemented as the 'comments' back-reference
+      of the Comment.commented reference property.
   """
 
   #: Required 1:1 relationship indicating the User who initially authored the
@@ -84,4 +88,3 @@
     """Alias 'title' Property as 'name' for use in common templates.
     """
     return self.title
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/comment/edit.html	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,21 @@
+{% extends "soc/models/edit.html" %}
+{% comment %}
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+{% block header_title %}
+{{ page_name }}
+ {% if work_link %}
+   <a href="{{ work_link }}">(original {{ comment_on_name }})</a>
+ {% endif %}
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/templatetags/_as_comment.html	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,26 @@
+{% comment %}
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+<li class="{{ comment_class }}">
+On {{ created|date:"jS F Y H:i" }}
+{% if edit_link %}
+you <a href="{{ edit_link }}">wrote</a>:
+{% else %}
+{{ author }} wrote:
+{% endif %}
+<p>{{ content|safe }}</p>
+{% if modified_by %}
+<p><span class="edited">Last edited by {{ modified_by }} on {{ modified_on|date:"jS F Y H:i" }}</span></p>
+{% endif %}
+</li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/templatetags/_as_comments.html	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,21 @@
+{% comment %}
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+{% load forms_helpers %}
+
+<ul>
+{% for comment in comments %}
+  {% as_comment comment %}
+{% endfor %}
+</ul>
--- a/app/soc/views/helper/templatetags/forms_helpers.py	Thu Mar 05 19:20:02 2009 +0000
+++ b/app/soc/views/helper/templatetags/forms_helpers.py	Thu Mar 05 19:21:43 2009 +0000
@@ -32,12 +32,48 @@
 from django.utils.html import escape
 
 from soc.logic import dicts
+from soc.logic.models import user as user_logic
+from soc.views.helper import redirects
 from soc.views.helper import widgets
 
 
 register = template.Library()
 
 
+@register.inclusion_tag('soc/templatetags/_as_comments.html',
+                        takes_context=True)
+def as_comments(context, work):
+  """Returns a HTML representation of a work's comments.
+  """
+
+  context['comments'] =  work.comments
+  return context
+
+@register.inclusion_tag('soc/templatetags/_as_comment.html',
+                        takes_context=True)
+def as_comment(context, comment):
+  """Returns a HTML representation of a comment.
+  """
+
+  edit_link = ''
+  current_user = user_logic.logic.getForCurrentAccount()
+
+  if current_user and comment.author.key() == current_user.key():
+    params = {'url_name': context['comment_on_url_name']}
+    edit_link = redirects.getEditRedirect(comment, params)
+
+  context.update({
+      'author': comment.author.name,
+      'content': comment.content,
+      'created': comment.created,
+      'edit_link': edit_link,
+      'modified_on': comment.modified,
+      'modified_by': comment.modified_by.name if comment.modified_by else '',
+      'comment_class': "public" if comment.is_public else "private",
+      })
+
+  return context
+
 @register.inclusion_tag('soc/templatetags/_field_as_table_row.html')
 def field_as_table_row(field):
   """Prints a newforms field as a table row.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/views/models/comment.py	Thu Mar 05 19:21:43 2009 +0000
@@ -0,0 +1,182 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Views for comments.
+"""
+
+__authors__ = [
+    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+    '"Matthew Wilkes" <matthew@matthewwilkes.co.uk>',
+  ]
+
+import time
+
+from google.appengine.api import users
+from google.appengine.ext.db import Key
+
+from django import forms
+
+from soc.logic import cleaning
+from soc.logic import dicts
+from soc.logic import validate
+from soc.logic.models.user import logic as user_logic
+from soc.logic.models.comment import logic as comment_logic
+from soc.logic.models.document import logic as document_logic
+from soc.logic.models.linkable import logic as link_logic
+from soc.models import linkable
+from soc.views import helper
+from soc.views.helper import access
+from soc.views.helper import redirects
+from soc.views.helper import params as params_helper
+from soc.views.models import base
+
+import soc.models.comment
+import soc.logic.models.comment
+import soc.logic.dicts
+import soc.views.helper
+import soc.views.helper.widgets
+
+
+class View(base.View):
+  """View methods for the comment model.
+  """
+
+  def __init__(self, params=None):
+    """Defines the fields and methods required for the base View class
+    to provide the user with list, public, create, edit and delete views.
+
+    Params:
+      params: a dict with params for this View
+      comment_on_name: e.g. 'Document'
+      comment_on_url_name: e.g. 'document'
+    """
+
+    rights = access.Checker(params)
+    rights['create'] = [('checkSeeded', ['checkIsDocumentReadable','scope_path'])]
+    rights['edit'] = [('checkIsMyEntity', [comment_logic,'author', True])]
+    rights['delete'] = [('checkIsMyEntity', [comment_logic,'author', True])]
+
+    new_params = {}
+    new_params['logic'] = comment_logic
+    new_params['rights'] = rights
+
+    new_params['name'] = "Comment"
+
+    new_params['create_template'] = 'soc/comment/edit.html'
+    new_params['edit_template'] = 'soc/comment/edit.html'
+
+    new_params['no_show'] = True
+    new_params['no_admin'] = True
+    new_params['no_create_raw'] = True
+    new_params['no_create_with_key_fields'] = True
+    new_params['no_list_raw'] = True
+
+    new_params['create_extra_dynaproperties'] = {
+        'on': forms.fields.CharField(widget=helper.widgets.ReadOnlyInput(),
+                                             required=False),
+        'content': forms.fields.CharField(
+            widget=helper.widgets.TinyMCE(attrs={'rows':10, 'cols':40})),
+        'scope_path': forms.CharField(widget=forms.HiddenInput, required=True),
+        }
+    new_params['extra_dynaexclude'] = ['author', 'commented', 'link_id', 'modified_by']
+
+    new_params['edit_extra_dynaproperties'] = {
+        'link_id': forms.CharField(widget=forms.HiddenInput, required=True),
+        'created_by': forms.fields.CharField(widget=helper.widgets.ReadOnlyInput(),
+                                             required=False),
+        }
+
+    params = dicts.merge(params, new_params)
+    super(View, self).__init__(params=params)
+
+  def _editContext(self, request, context):
+    """see base.View._editContext.
+    """
+
+    entity = context['entity']
+
+    if entity:
+      on = entity.commented
+    else:
+      seed = context['seed']
+      on =  seed['commented']
+
+    params = {'url_name': self._params['comment_on_url_name']}
+    redirect = redirects.getPublicRedirect(on, params)
+
+    context['comment_on_url_name'] = self._params['comment_on_url_name']
+    context['comment_on_name'] = self._params['comment_on_name']
+    context['work_link'] = redirect
+
+  def _editPost(self, request, entity, fields, params=None):
+    """See base.View._editPost().
+    """
+
+    user = user_logic.getForCurrentAccount()
+    scope_path = fields['scope_path']
+
+    if not entity:
+      fields['author'] = user
+      fields['link_id'] = 't%i' % (time.time())
+    else:
+      fields['author'] = entity.author
+      fields['link_id'] = entity.link_id
+      fields['modified_by'] = user
+
+    fields['commented'] = self._getWorkByKeyName(scope_path).key()
+
+    super(View, self)._editPost(request, entity, fields)
+
+  def _editGet(self, request, entity, form):
+    """See base.View._editGet().
+    """
+
+    form.fields['created_by'].initial = entity.author.name
+    form.fields['on'].initial = entity.commented.name
+    form.fields['link_id'].initial = entity.link_id
+    form.fields['scope_path'].initial = entity.scope_path
+
+    super(View, self)._editGet(request, entity, form)
+
+  def _getWorkByKeyName(self, keyname):
+    """Returns the work for the specified key name.
+    """
+
+    logic = self._params['comment_on_logic']
+    return logic.getFromKeyName(keyname)
+
+  def _editSeed(self, request, seed):
+    """Checks if scope_path is seeded and puts it into to_user.
+
+    For parameters see base._editSeed()
+    """
+
+    scope_path = seed['scope_path']
+    work = self._getWorkByKeyName(scope_path)
+    seed['on'] = work.title
+    seed['commented'] = work
+
+  def getMenusForScope(self, entity, params):
+    """Returns the featured menu items for one specifc entity.
+
+    A link to the home page of the specified entity is also included.
+
+    Args:
+      entity: the entity for which the entry should be constructed
+      params: a dict with params for this View.
+    """
+
+    return []