diff --git a/base/models.py b/base/models.py index ad1f2236fd..11f0ccffd0 100644 --- a/base/models.py +++ b/base/models.py @@ -24,3 +24,15 @@ class FileComment(models.Model): class Meta: ordering = ['-timestamp'] + +class FileContributors(models.Model): + """(repo_id, file path, file_id, contributors)""" + + repo_id = models.CharField(max_length=36, db_index=True) + file_id = models.CharField(max_length=40) + + file_path = models.TextField() + file_path_hash = models.CharField(max_length=12) + + # email addresses seperated by comma + emails = models.TextField() diff --git a/group/views.py b/group/views.py index c5ff68031b..1550cbe832 100644 --- a/group/views.py +++ b/group/views.py @@ -495,7 +495,7 @@ def group_share_repo(request, repo_id, group_id, from_email): return render_error(request, u'共享失败:小组不存在') # Check whether user belongs to the group. - joined = is_group_user(group_id_int, request.user.username) + joined = is_group_user(int(group.id), request.user.username) if not joined: return render_error(request, u'共享失败:未加入该小组') diff --git a/settings.py b/settings.py index fff615875e..7e63d5c7f3 100644 --- a/settings.py +++ b/settings.py @@ -167,20 +167,6 @@ AVATAR_CACHE_TIMEOUT = 24 * 60 * 60 AVATAR_ALLOWED_FILE_EXTS = ('.jpg', '.png', '.jpeg', '.gif') AUTO_GENERATE_AVATAR_SIZES = (16, 28, 48, 60, 80) -# File upload -# FILE_UPLOAD_MAX_MEMORY_SIZE = 0 - -# FILE_UPLOAD_TEMP_DIR = "/tmp/seafile-upload" - -# if not os.access(FILE_UPLOAD_TEMP_DIR, os.F_OK): -# os.mkdir(FILE_UPLOAD_TEMP_DIR) - -# FILE_UPLOAD_HANDLERS = ( -# "seahub.utils.UploadProgressCachedHandler", -# "django.core.files.uploadhandler.MemoryFileUploadHandler", -# "django.core.files.uploadhandler.TemporaryFileUploadHandler", -# ) - CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', diff --git a/templates/repo_view_file.html b/templates/repo_view_file.html index b8e1ad3fa6..849eeab214 100644 --- a/templates/repo_view_file.html +++ b/templates/repo_view_file.html @@ -41,6 +41,14 @@
+
+ {{ contributors|length }} 个贡献者 + {% for user in contributors %} + + {{ user|email2nickname}}{% avatar user 24 %} + + {% endfor %} +
{% if filetype == 'Text' or filetype == 'Image' or filetype == 'SVG' or filetype == 'Markdown' %} diff --git a/utils.py b/utils.py index 664b48c5ac..05f5d6cf83 100644 --- a/utils.py +++ b/utils.py @@ -2,7 +2,6 @@ # encoding: utf-8 import os import re -import time import random import stat @@ -10,9 +9,9 @@ from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.hashcompat import sha_constructor -from django.core.files.uploadhandler import FileUploadHandler, StopUpload, \ - StopFutureHandlers -from django.core.cache import cache +from base.models import FileContributors + +from pysearpc import SearpcError from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \ get_repo, get_commits, get_group_repoids, CCNET_SERVER_ADDR, \ @@ -141,50 +140,6 @@ def calculate_repo_last_modify(repo_list): except: repo.latest_modify = None -class UploadProgressCachedHandler(FileUploadHandler): - """Tracks progress for file uploads. The http post request must contain a - header or query parameter, 'X-Progress-ID', which should contain a unique - string to identify the upload to be tracked. - - """ - - def __init__(self, request=None): - super(UploadProgressCachedHandler, self).__init__(request) - self.progress_id = None - self.cache_key = None - - def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): - self.content_length = content_length - if 'X-Progress-ID' in self.request.GET : - self.progress_id = self.request.GET['X-Progress-ID'] - elif 'X-Progress-ID' in self.request.META: - self.progress_id = self.request.META['X-Progress-ID'] - # print "handle_raw_input: ", self.progress_id - if self.progress_id: - self.cache_key = "%s_%s" % (self.request.user.username, self.progress_id ) - # make cache expiring in 30 seconds - cache.set(self.cache_key, { - 'length': self.content_length, - 'uploaded' : 0 - }, 30) - - def new_file(self, field_name, file_name, content_type, content_length, charset=None): - pass - - def receive_data_chunk(self, raw_data, start): - if self.cache_key: - data = cache.get(self.cache_key) - data['uploaded'] += self.chunk_size - cache.set(self.cache_key, data, 30) - return raw_data - - def file_complete(self, file_size): - pass - - def upload_complete(self): - if self.cache_key: - cache.delete(self.cache_key) - def check_filename_with_rename(repo_id, parent_dir, filename): latest_commit = get_commits(repo_id, 0, 1)[0] dirents = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, @@ -378,3 +333,70 @@ def check_and_get_org_by_group(group_id, user): base_template = 'myhome_base.html' return org, base_template + +def get_file_contributors_from_revisions(repo_id, file_path): + """Inspect the file history and get a list of users who have modified the + it. + + """ + commits = [] + try: + commits = seafserv_threaded_rpc.list_file_revisions(repo_id, file_path, 100) + except SearpcError: + return [] + + # Commits are already sorted by date, so the user list is also sorted. + users = [ commit.creator_name for commit in commits ] + + # Remove duplicate elements in a list + ret = [] + for user in users: + if user not in ret: + ret.append(user) + + return ret + +def get_file_contributors(repo_id, file_path, file_path_hash, file_id): + """Get file contributors list from database cache. If not found in cache, + try to get it from seaf-server. + + """ + contributors = [] + try: + fc = FileContributors.objects.get(repo_id=repo_id, + file_path_hash=file_path_hash) + except FileContributors.DoesNotExist: + # has no cache yet + contributors = get_file_contributors_from_revisions (repo_id, file_path) + if not contributors: + return [] + emails = ','.join(contributors) + file_contributors = FileContributors(repo_id=repo_id, + file_id=file_id, + file_path=file_path, + file_path_hash=file_path_hash, + emails=emails) + file_contributors.save() + else: + # cache found + if fc.file_id != file_id: + # but cache is outdated + fc.delete() + contributors = get_file_contributors_from_revisions (repo_id, file_path) + if not contributors: + return [] + emails = ','.join(contributors) + file_contributors = FileContributors(repo_id=repo_id, + file_id=file_id, + file_path=file_path, + file_path_hash=file_path_hash, + emails=emails) + file_contributors.save() + else: + # cache is valid + if fc.emails: + contributors = fc.emails.split(',') + else: + contributors = [] + + return contributors diff --git a/views.py b/views.py index 852bad3a21..f94d0a04e8 100644 --- a/views.py +++ b/views.py @@ -58,7 +58,8 @@ from utils import render_permission_error, render_error, list_to_string, \ check_filename_with_rename, get_accessible_repos, EMPTY_SHA1, \ get_file_revision_id_size, get_ccnet_server_addr_port, \ gen_file_get_url, string2list, MAX_INT, \ - gen_file_upload_url, check_and_get_org_by_repo + gen_file_upload_url, check_and_get_org_by_repo, \ + get_file_contributors from seahub.profile.models import Profile try: from settings import DOCUMENT_CONVERTOR_ROOT @@ -968,6 +969,8 @@ def repo_view_file(request, repo_id): else: page_next = False comments = comments_plus_one[:per_page] + + contributors = get_file_contributors(repo_id, path.encode('utf-8'), file_path_hash, obj_id) return render_to_response('repo_view_file.html', { 'repo': repo, @@ -999,6 +1002,7 @@ def repo_view_file(request, repo_id): 'page_next': page_next, 'document_swf_exists': document_swf_exists, 'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT, + 'contributors': contributors, }, context_instance=RequestContext(request)) def repo_file_get(raw_path): @@ -1828,7 +1832,7 @@ def render_file_revisions (request, repo_id): return render_error(request, error_msg) try: - commits = seafserv_threaded_rpc.list_file_revisions(repo_id, path) + commits = seafserv_threaded_rpc.list_file_revisions(repo_id, path, 0) except SearpcError, e: return render_error(request, e.msg)