diff --git a/media/css/seahub.css b/media/css/seahub.css index 1e1105ee7e..933d658c14 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -956,7 +956,8 @@ ul.with-bg li { font-size:12px; color:#333; } -#message { +#message, +#comment-input { font: 13px/1.5 Arial, Helvetica, sans-serif; word-wrap: break-word; width: 600px; @@ -985,7 +986,8 @@ ul.with-bg li { margin-bottom:2px; } .msg-hd .time, -.msg-hd .group { +.msg-hd .group, +.comment-hd .time { color: #808080; } .msg-hd .group { @@ -1164,5 +1166,57 @@ ul.with-bg li { } /* File comment */ #file-comment { - margin-top: 60px; + width:400px; + position:fixed; + right:10px; + bottom:40px; + border:1px solid #cbcbcb; + padding:0 15px; + box-shadow: 0 2px 4px rgba(0,0,0,.2); + -moz-box-shadow: -1px 1px 1px rgba(0,0,0,.2); + -webkit-box-shadow: 0 2px 4px rgba(0,0,0,.2); + background:#fff; + overflow:auto; +} +#comment-input { + width:315px; + padding-left:1px; + height:30px; + margin:0; +} +.comment { + width:380px; + padding:15px 0; + border-top:1px solid #e8e8e8; +} +.comment .txt { + width:318px; +} +#file-comment-form { + width:380px; + margin:15px 0; +} +.comment-bd { + word-wrap:break-word; +} +#comment-caret { + position:fixed; + right:27px; + bottom:26px; +} +#comment-outer-caret, +#comment-inner-caret { + height:0; + width:1px; + border:14px solid; + border-color:#CBCBCB transparent; + border-bottom-width:0; + z-index:100; + margin:0 auto; +} +#comment-inner-caret { + border-top-color:#fff; + position:relative; + top:-15px; + left:-14px; } diff --git a/templates/file_comments.html b/templates/file_comments.html new file mode 100644 index 0000000000..9e0a17cd7b --- /dev/null +++ b/templates/file_comments.html @@ -0,0 +1,16 @@ +{% load seahub_tags avatar_tags%} +{% load url from future %} + + {% for comment in comments %} +
  • + {% avatar comment.from_email 48 %} +
    +
    + {{ comment.from_email|email2nickname }} + {{ comment.timestamp|translate_commit_time }} +
    +

    {{ comment.message|seahub_urlize|find_at|linebreaksbr }}

    +
    +
  • + {% endfor %} + diff --git a/templates/repo_view_file.html b/templates/repo_view_file.html index 3b5501e79e..157d014d22 100644 --- a/templates/repo_view_file.html +++ b/templates/repo_view_file.html @@ -29,16 +29,17 @@ {% endfor %}

    + {% if not view_history %}
    - {% if not view_history %} - {% endif %}
    + {% endif %} + {% if not view_history %}

    {% avatar latest_contributor 20 %} {{ latest_contributor|email2nickname }} 做了最新修改

    @@ -48,16 +49,19 @@ {% endfor %}

    - + {% endif %} +
    {% if not view_history and request.user.is_authenticated %} - {% if filetype == 'Text' or filetype == 'Markdown' %} + {% if filetype == 'Text' or filetype == 'Markdown' %} - {% endif %} - {% endif %} + {% endif %} + {% endif %} + {% if not view_history %} + {% endif %} {% if filetype == 'Text' or filetype == 'Image' or filetype == 'SVG' or filetype == 'Markdown' %} {% endif %} @@ -74,7 +78,7 @@
    -{% if not view_history %} + {% if not view_history %} -{% endif %} - -{% if comments %} - - -{% endif %} - -
    - {% if current_page != 1 %} - 上一页 - {% endif %} - {% if page_next %} - 下一页 - {% endif %} -
    - + {% endif %} + {% endif %} {% endblock %} {% block extra_script %} @@ -275,6 +266,54 @@ $('#shared-link').click(function() { }); {% include "snippets/bottom_bar.html" %} +{% if request.user.is_authenticated %} +$('#bottom-bar').append(' '); +$('#file-comment').css('max-height', $(window).height() - parseInt($('#file-comment').css('bottom'))); +$('#comment').click(function() { + if ($('#file-comment').hasClass('hide')) { + $('#file-comment, #comment-caret').removeClass('hide'); + } else { + $('#file-comment, #comment-caret').addClass('hide'); + } +}); + +var comment_input_pre_text = $('#comment-input').val(); +$('#comment-input').css('color', '#999').click(function() { + $(this).val('').css('color', '#000'); + $('#file-comment-form .submit').removeClass('hide'); +}); +$('#file-comment-form .submit').click(function() { + if (!$.trim($('#comment-input').val())) { + $('#file-comment-form .error').html('请先输入您的评论').removeClass('hide'); + return false; + } + $.ajax({ + url: '{% url 'views.file_comment' %}' + '?p={{path}}', + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + beforeSend: prepareCSRFToken, + data: { + 'repo_id': '{{ repo.id }}', + 'file_path': '{{ path|urlencode }}', + 'message': $('#comment-input').val() + }, + success: function(data) { + $('#comment-input').val(comment_input_pre_text).css('color', '#999'); + $('#file-comment-form .submit, #file-comment-form .error').addClass('hide'); + $('#comment-list').html(data.html); + }, + error: function(data, textStatus, jqXHR) { + var errors = $.parseJSON(data.responseText); + $.each(errors, function(index, value) { + $('#file-comment-form .error').html(value[0]).removeClass('hide'); + }); + } + }); + return false; +}); +{% endif %} + {% endif %} function send_open_local_file_request(path) { diff --git a/urls.py b/urls.py index 5b796240ad..079571f08b 100644 --- a/urls.py +++ b/urls.py @@ -66,6 +66,7 @@ urlpatterns = patterns('', # (r'^repo/setap/(?P[^/]+)/$', repo_set_access_property), url(r'^repo/(?P[^/]+)/files/$', repo_view_file, name="repo_view_file"), (r'^repo/(?P[^/]+)/file/edit/$', repo_file_edit), + (r'^file_comment/$', file_comment), (r'^pdf_full_view/$', pdf_full_view), url(r'^repo/(?P[^/]+)/(?P[^/]+)/$', repo_access_file, name='repo_access_file'), diff --git a/views.py b/views.py index 54ca9aa113..b05858ad9b 100644 --- a/views.py +++ b/views.py @@ -972,42 +972,6 @@ 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', '/') u_filename = os.path.basename(path) @@ -1092,22 +1056,9 @@ def repo_view_file(request, repo_id): """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] + comments = FileComment.objects.filter(file_path_hash=file_path_hash, repo_id=repo_id) contributors = get_file_contributors(repo_id, path.encode('utf-8'), file_path_hash, obj_id) latest_contributor = contributors[0] @@ -1135,17 +1086,52 @@ def repo_view_file(request, repo_id): "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, 'document_swf_exists': document_swf_exists, 'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT, 'contributors': contributors, 'latest_contributor': latest_contributor, }, context_instance=RequestContext(request)) + +def file_comment(request): + if request.method == 'POST': + # handle post request to leave comment on a file + content_type = 'application/json; charset=utf-8' + path = request.GET.get('p', ''); + + 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() + + comments = FileComment.objects.filter(file_path_hash=file_path_hash, repo_id=repo_id) + html = render_to_string("file_comments.html", {'comments':comments}) + return HttpResponse(json.dumps({'html': html}), content_type=content_type) + def repo_file_get(raw_path): err = '' file_content = ''