diff --git a/base/models.py b/base/models.py
index 5eac114b0c..ad1f2236fd 100644
--- a/base/models.py
+++ b/base/models.py
@@ -1,3 +1,4 @@
+import datetime
from django.db import models
@@ -8,4 +9,18 @@ class UuidObjidMap(models.Model):
uuid = models.CharField(max_length=40)
obj_id = models.CharField(max_length=40, unique=True)
-
+class FileComment(models.Model):
+ """
+ Model used for leave comment on file.
+ NOTE:
+ Need manually create index for (file_path_hash, repo_id).
+ """
+ repo_id = models.CharField(max_length=36, db_index=True)
+ file_path = models.TextField()
+ file_path_hash = models.CharField(max_length=12)
+ from_email = models.EmailField()
+ message = models.TextField()
+ timestamp = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta:
+ ordering = ['-timestamp']
diff --git a/forms.py b/forms.py
index 5a4141c27d..04f2024a7f 100644
--- a/forms.py
+++ b/forms.py
@@ -47,6 +47,14 @@ class FileLinkShareForm(forms.Form):
email = forms.CharField(max_length=512)
file_shared_link = forms.CharField(max_length=40)
+class FileCommentForm(forms.Form):
+ """
+ Form for leave comments on file.
+ """
+ repo_id = forms.CharField(max_length=36)
+ file_path = forms.CharField()
+ message = forms.CharField()
+
class RepoCreateForm(forms.Form):
"""
Form for creating repo and org repo.
diff --git a/group/models.py b/group/models.py
index 45950daa2d..f8a033346e 100644
--- a/group/models.py
+++ b/group/models.py
@@ -25,13 +25,14 @@ class MessageReply(models.Model):
class MessageAttachment(models.Model):
"""
- A model used to represents a message attachment related to a group message.
+ Model used to represents a message attachment related to a group message.
"""
group_message = models.ForeignKey(GroupMessage)
repo_id = models.CharField(max_length=40)
- attach_type = models.CharField(max_length=5)
+ attach_type = models.CharField(max_length=5) # `file` or `dir`
path = models.TextField()
-
+ src = models.TextField(max_length=20) # `recommend` or `filecomment`
+
at_pattern = re.compile(r'(\s|^)(@\w+)', flags=re.U)
@receiver(post_save, sender=MessageReply)
diff --git a/group/templates/group/group_info.html b/group/templates/group/group_info.html
index a35cd0b5f9..0eff662be7 100644
--- a/group/templates/group/group_info.html
+++ b/group/templates/group/group_info.html
@@ -107,7 +107,8 @@
- {% if msg.attachment %}
+ {% if msg.attachment.src == 'recommend' %}
+
推荐
{% if msg.attachment.attach_type == 'file' %}
@@ -117,6 +118,12 @@
{{ msg.attachment.name }} :
{% endif %}
+
+ {% if msg.attachment.src == 'filecomment' %}
+
+ 文件 {{ msg.attachment.name }} 有新的评注:
+ {% endif %}
+
{{ msg.message|seahub_urlize|find_at|linebreaksbr }}
diff --git a/group/views.py b/group/views.py
index a2eb2b3e3f..ec5f0c74c6 100644
--- a/group/views.py
+++ b/group/views.py
@@ -207,7 +207,7 @@ def render_group_info(request, group_id, form):
group_id=group_id).order_by(\
'-timestamp')[per_page*(current_page-1) : per_page*current_page+1]
- if len(msgs_plus_one) == per_page + 1:
+ if msgs_plus_one.count() == per_page + 1:
page_next = True
else:
page_next = False
@@ -582,7 +582,8 @@ def group_recommend(request):
# save attachment
ma = MessageAttachment(group_message=gm, repo_id=repo_id,
- attach_type=attach_type, path=path)
+ attach_type=attach_type, path=path,
+ src='recommend')
ma.save()
group_url = reverse('group_info', args=[group.id])
diff --git a/media/css/seahub.css b/media/css/seahub.css
index 0182273a3a..687b7b5c6a 100644
--- a/media/css/seahub.css
+++ b/media/css/seahub.css
@@ -1023,3 +1023,7 @@ ul.with-bg li {
/* add padding to account for vertical scrollbar */
padding-right: 20px;
}
+
+#shared-link {
+ display: inline-block;
+}
diff --git a/templates/repo_view_file.html b/templates/repo_view_file.html
index 4c63c6f2ab..e6d4e15438 100644
--- a/templates/repo_view_file.html
+++ b/templates/repo_view_file.html
@@ -1,5 +1,5 @@
{% extends base_template %}
-{% load seahub_tags %}
+{% load seahub_tags avatar_tags%}
{% load url from future %}
{% block main_panel %}
@@ -37,7 +37,7 @@
{% if not view_history %}
-
{{ file_shared_link }}
+
{{ file_shared_link }}
@@ -89,6 +89,52 @@
+{% if request.user.is_authenticated %}
+
+{% endif %}
+
+{% if comments %}
+
+
+ {% for comment in comments %}
+ -
+
+
+
+ {% endfor %}
+
+{% endif %}
+
+ {% if current_page != 1 %}
+
上一页
+ {% endif %}
+ {% if page_next %}
+
下一页
+ {% endif %}
+
+
{% endblock %}
{% block extra_script %}
diff --git a/urls.py b/urls.py
index 7d4dcde689..214bd1e569 100644
--- a/urls.py
+++ b/urls.py
@@ -11,7 +11,8 @@ from seahub.views import root, myhome, \
seafile_access_check, repo_history_changes, \
repo_upload_file, file_upload_progress_page, \
upload_file_error, update_file_error, \
- get_subdir, file_move, repo_new_dir, repo_new_file, repo_rename_file, validate_filename, \
+ get_subdir, file_move, repo_new_dir, repo_new_file, repo_rename_file, \
+ validate_filename, \
repo_create, repo_update_file, repo_revert_file, file_revisions, \
get_shared_link, view_shared_file, remove_shared_link, send_shared_link, \
crocodoc_upload, crocodoc_status, crocodoc_session
@@ -77,7 +78,7 @@ urlpatterns = patterns('',
(r'^download/repo/$', repo_download),
(r'^file/move/get_subdir/$', get_subdir),
- (r'^file/move/$', file_move),
+ (r'^file/move/$', file_move),
(r'^seafile_access_check/$', seafile_access_check),
url(r'^org/remove/(?P
[\d]+)/$', org_remove, name="org_remove"),
(r'^org/$', org_info),
diff --git a/utils.py b/utils.py
index 17f3b893cb..0e9e3b276c 100644
--- a/utils.py
+++ b/utils.py
@@ -3,6 +3,7 @@
import os
import re
import time
+import random
import stat
from django.shortcuts import render_to_response
@@ -101,7 +102,9 @@ def gen_token(max_length=5):
"""
- token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[:max_length]
+ secret_key = settings.SECRET_KEY
+ rstr = str(random.random())
+ token = sha_constructor(secret_key + rstr).hexdigest()[:max_length]
return token
def validate_group_name(group_name):
diff --git a/views.py b/views.py
index 3101096feb..8f169181d0 100644
--- a/views.py
+++ b/views.py
@@ -20,6 +20,7 @@ from django.http import HttpResponse, HttpResponseBadRequest, Http404, \
HttpResponseRedirect
from django.shortcuts import render_to_response, redirect
from django.template import Context, loader, RequestContext
+from django.utils.hashcompat import md5_constructor
from django.views.decorators.csrf import csrf_protect
from auth.decorators import login_required
@@ -36,13 +37,15 @@ from pysearpc import SearpcError
from base.accounts import User
from base.decorators import sys_staff_required
-from seahub.base.models import UuidObjidMap
+from seahub.base.models import UuidObjidMap, FileComment
from seahub.contacts.models import Contact
from seahub.contacts.signals import mail_sended
+from group.models import GroupMessage, MessageAttachment
+from group.signals import grpmsg_added
from seahub.notifications.models import UserNotification
from seahub.organizations.utils import access_org_repo
from forms import AddUserForm, FileLinkShareForm, RepoCreateForm, \
- RepoNewDirForm, RepoNewFileForm
+ RepoNewDirForm, RepoNewFileForm, FileCommentForm
from utils import render_permission_error, render_error, list_to_string, \
get_httpserver_root, get_ccnetapplet_root, gen_token, \
calculate_repo_last_modify, valid_previewed_file, \
@@ -724,10 +727,44 @@ def repo_view_file(request, repo_id):
"""
Preview file on web, including files in current worktree and history.
"""
+ if request.method == 'POST':
+ # handle post request to leave comment on file
+ path = request.GET.get('p', '/')
+ next = reverse('repo_view_file', args=[repo_id]) + '?p=' + \
+ urllib2.quote(path.encode('utf-8'))
+
+ f = FileCommentForm(request.POST)
+ if f.is_valid():
+ repo_id = f.cleaned_data['repo_id']
+ file_path = f.cleaned_data['file_path']
+ file_path_hash = md5_constructor(file_path).hexdigest()[:12]
+ message = f.cleaned_data['message']
+ fc = FileComment(repo_id=repo_id, file_path=file_path,
+ file_path_hash=file_path_hash,
+ from_email=request.user.username, message=message)
+ fc.save()
+ # send a group message if the repo shared to any groups
+ repo_shared_groups = get_shared_groups_by_repo(repo_id)
+
+ for group in repo_shared_groups:
+ # save group message, and length should be less than 500
+ gm = GroupMessage(group_id=group.id,
+ from_email=request.user.username,
+ message=message[:500])
+ gm.save()
+ # send signal
+ grpmsg_added.send(sender=GroupMessage, group_id=group.id,
+ from_email=request.user.username)
+
+ # save attachment
+ ma = MessageAttachment(group_message=gm, repo_id=repo_id,
+ attach_type='file', path=path,
+ src='filecomment')
+ ma.save()
+ return HttpResponseRedirect(next)
+
http_server_root = get_httpserver_root()
path = request.GET.get('p', '/')
- if path[-1] == '/':
- path = path[:-1]
u_filename = os.path.basename(path)
filename = urllib2.quote(u_filename.encode('utf-8'))
@@ -800,6 +837,25 @@ def repo_view_file(request, repo_id):
# check whether user joined this group
if is_group_user(group.id, request.user.username):
groups.append(group)
+
+ """file comments"""
+ # Make sure page request is an int. If not, deliver first page.
+ try:
+ current_page = int(request.GET.get('page', '1'))
+ per_page= int(request.GET.get('per_page', '15'))
+ except ValueError:
+ current_page = 1
+ per_page = 15
+
+ file_path_hash = md5_constructor(urllib2.quote(path.encode('utf-8'))).hexdigest()[:12]
+ comments_plus_one = FileComment.objects.filter(
+ file_path_hash=file_path_hash,
+ repo_id=repo_id)[per_page*(current_page-1) : per_page*current_page+1]
+ if comments_plus_one.count() == per_page + 1:
+ page_next = True
+ else:
+ page_next = False
+ comments = comments_plus_one[:per_page]
return render_to_response('repo_view_file.html', {
'repo': repo,
@@ -823,8 +879,14 @@ def repo_view_file(request, repo_id):
'file_content': file_content,
"applet_root": get_ccnetapplet_root(),
'groups': groups,
+ 'comments': comments,
+ 'current_page': current_page,
+ 'prev_page': current_page-1,
+ 'next_page': current_page+1,
+ 'per_page': per_page,
+ 'page_next': page_next,
}, context_instance=RequestContext(request))
-
+
def repo_file_get(raw_path):
err = ''
file_content = ''