1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 23:20:51 +00:00

Merge branch 'lj'

Conflicts:
	organizations/views.py
	templates/myhome.html
	templates/snippets/my_owned_repos.html
	views.py
This commit is contained in:
llj
2012-10-26 20:23:21 +08:00
11 changed files with 291 additions and 36 deletions

View File

@@ -149,3 +149,15 @@ def innerpubmsg_reply_added_cb(sender, instance, **kwargs):
detail=msg_id)
n.save()
class UserStarredFiles(models.Model):
"""Starred files are marked by users to get quick access to it on user
home page.
"""
email = models.EmailField()
org_id = models.IntegerField()
repo_id = models.CharField(max_length=36)
path = models.TextField()
is_dir = models.BooleanField()

View File

@@ -666,7 +666,8 @@ textarea:-moz-placeholder {
#repo-create-form {
padding:0 20px;
}
.empty-repo-tips {
.empty-repo-tips,
.no-starred-file-tips {
width: 60%;
margin: 25px auto;
}
@@ -1349,6 +1350,10 @@ textarea:-moz-placeholder {
#file-edit-cancel {
color:#900;
}
#star {
padding-left:18px;
background:#efefef url('../img/star.png') no-repeat scroll 3px 48%;
}
/* shareadmin */
.view-link-alert p {
display: inline-block;

BIN
media/img/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

View File

@@ -32,7 +32,7 @@
{% endblock %}
{% block right_panel %}
<!-- 我拥有的资料库 --><!-- 共享给我的资料库 -->
<!-- 我拥有的资料库 共享给我的资料库 星标文件 -->
{% include "snippets/my_owned_repos.html" %}
{% if events %}

View File

@@ -37,7 +37,7 @@ from seahub.forms import RepoCreateForm, SharedRepoCreateForm
import seahub.settings as seahub_settings
from seahub.utils import render_error, render_permission_error, gen_token, \
validate_group_name, string2list, calculate_repo_last_modify, MAX_INT, \
EVENTS_ENABLED, get_org_user_events
EVENTS_ENABLED, get_org_user_events, get_starred_files
from seahub.views import myhome
from seahub.signals import repo_created
@@ -141,6 +141,7 @@ def org_personal(request, url_prefix):
events = None
quota_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org.org_id, user)
starred_files = get_starred_files(user, org_id=org.org_id)
return render_to_response('organizations/personal.html', {
'owned_repos': owned_repos,
@@ -154,6 +155,7 @@ def org_personal(request, url_prefix):
'nickname': nickname,
'events': events,
'quota_usage': quota_usage,
'starred_files': starred_files,
}, context_instance=RequestContext(request))
@login_required

View File

@@ -19,30 +19,30 @@
{% if grpmsg_list or grpmsg_reply_list or orgmsg_list %}
<div class="info-item">
<h3 class="info-item-top">{% trans "Reminders..." %}</h3>
<ul class="info-item-bottom">
{% if grpmsg_list %}
<li>
{% trans "Group" %}
{% for grp in grpmsg_list %}
<a href="{% url 'group_info' grp.id %}" class="no-bold">{{ grp.group_name }}</a>
{% endfor %}
{% trans "have new message" %}
</li>
{% endif %}
{% if grpmsg_reply_list %}
<li><a href="{{ SITE_ROOT }}group/reply/new/" class="no-bold">{{ grpmsg_reply_list|length }}{% trans "group message has new reply" %}</a></li>
{% endif %}
{% if orgmsg_list %}
<li><a href="{% url 'org_msg' %}" class="no-bold">{{ orgmsg_list|length }}{% trans "org messages" %}</a></li>
{% endif %}
</ul>
<h3 class="info-item-top">{% trans "Reminding..." %}</h3>
<ul class="info-item-bottom">
{% if grpmsg_list %}
<li>
{% trans "Group" %}
{% for grp in grpmsg_list %}
<a href="{% url 'group_info' grp.id %}" class="no-bold">{{ grp.group_name }}</a>
{% endfor %}
{% trans "have new message" %}
</li>
{% endif %}
{% if grpmsg_reply_list %}
<li><a href="{{ SITE_ROOT }}group/reply/new/" class="no-bold">{{ grpmsg_reply_list|length }}{% trans "group message has new reply" %}</a></li>
{% endif %}
{% if orgmsg_list %}
<li><a href="{% url 'org_msg' %}" class="no-bold">{{ orgmsg_list|length }}{% trans "org messages" %}</a></li>
{% endif %}
</ul>
</div>
{% endif %}
<div class="info-item">
<h3 class="info-item-top">{% trans "Storage Used" %}</h3>
<p class="info-item-bottom">{{ quota_usage|filesizeformat }} {% if quota > 0 %}/ {{ quota|filesizeformat }} {% endif %}</p>
<h3 class="info-item-top">{% trans "Storage Used" %}</h3>
<p class="info-item-bottom">{{ quota_usage|filesizeformat }} {% if quota > 0 %}/ {{ quota|filesizeformat }} {% endif %}</p>
</div>
<!-- 我的群组 -->
@@ -52,7 +52,7 @@
{% endblock %}
{% block right_panel %}
<!-- 我拥有的资料库 --><!-- 共享给我的资料库 -->
<!-- 我拥有的资料库 共享给我的资料库 星标文件 -->
{% include "snippets/my_owned_repos.html" %}
{% if events %}

View File

@@ -25,14 +25,12 @@
<button class="fright" data="{% url 'file_revisions' repo.id %}?p={{ path|urlencode }}" id="back">返回文件版本列表</button>
</div>
{% endif %}
{% if page_from == 'recycle' %}
<div class="w100 ovhd">
<h2 class="fleft">
{{repo.props.name}} 的文件回收站
</h2>
<button class="fright" data="{% url 'repo' repo.id %}" id="back">返回同步目录</button>
</div>
{% endif %}
{% if page_from == 'recycle' %}
<div class="w100 ovhd">
<h2 class="fleft">{{repo.props.name}} 的文件回收站</h2>
<button class="fright" data="{% url 'repo' repo.id %}" id="back">返回同步目录</button>
</div>
{% endif %}
{% endif %}
<div class="w100 ovhd">
<p class="path fleft">
@@ -65,7 +63,6 @@
{{ name }}
{% endif %}
{% endfor %}
{% endif %} <!-- if page_from == 'recycle' -->
</p>
@@ -75,6 +72,11 @@
<button data="{{ SITE_ROOT }}sharedlink/get/?repo_id={{ repo.id }}&p={{ path|urlencode }}&file_name={{ file_name }}" id="get-shared-link">获取分享地址</button>
<button id="send-shared-link" class="hide">发送</button>
<button data="{{ SITE_ROOT }}sharedlink/remove/?t={{ fileshare.token }}" id="rm-shared-link" class="hide">删除</button>
{% if is_starred %}
<button id="star" data="starred">取消星标</button>
{% else %}
<button id="star" data="unstarred">添加星标</button>
{% endif %}
</div>
{% endif %}
</div>
@@ -465,6 +467,48 @@ $('#open-local').click(function () {
}
}, 2000);
});
//star
$('#star').click(function() {
var star_btn = $(this);
var state = star_btn.attr('data');
$.ajax({
url: '{{ SITE_ROOT }}repo/star_file/{{ repo.id }}/',
type: 'POST',
cache: false,
contentType: 'application/json; charset=utf-8',
beforeSend: prepareCSRFToken,
dataType: 'json',
data: {
path: '{{ path }}',
state: state,
org_id: {% if org %} {{ org.org_id }} {% else %} -1 {% endif %}
},
success:function(data) {
if (data['success']) {
if (state == 'starred') {
feedback('星标取消成功', 'success');
star_btn.attr('data', 'unstarred').text('添加星标');
} else {
feedback('星标添加成功', 'success');
star_btn.attr('data', 'starred').text('取消星标');
}
} else {
feedback('操作失败:' + data['err_msg'], 'error');
}
},
error:function(jqXHR, textStatus, errorThrown) {
feedback(textStatus + ',操作失败', 'error');
}
});
})
.hover(
function() {
$(this).css('background-color', '#fff');
},
function() {
$(this).css('background-color', '#efefef');
}
);
//'not view_history' ends here
{% endif %}

View File

@@ -1,10 +1,12 @@
{% load seahub_tags i18n %}
{% load url from future %}
<h3>{% trans "Libraries" %}</h3>
<div id="repos-tabs">
<div class="ovhd">
<ul class="fleft">
<li><a href="#my-own-repos" onfocus="this.blur()">{% trans "I owned" %}</a></li>
<li><a href="#repos-shared-to-me" onfocus="this.blur()">{% trans "Share to me" %}</a></li>
<li><a href="#repos-shared-to-me" onfocus="this.blur()">{% trans "Shared to me" %}</a></li>
<li><a href="#starred-files" onfocus="this.blur()">{% trans "starred" %}</a></li>
</ul>
<button id="repo-create" class="fright">{% trans "New Library" %}</button>
</div>
@@ -82,4 +84,28 @@
<p class="empty-repo-tips">您的朋友可以将他的资料库共享给您,这些资料库会显示在这里。</p>
{% endif %}
</div>
<div id="starred-files">
{% if starred_files %}
<table>
<tr>
<th width="5%"></th>
<th width="45%">文件名</th>
<th width="30%">所属资料库</th>
<th width="20%">更新时间</th>
</tr>
{% for sfile in starred_files %}
<tr>
<td class="icon-container"><img src="{{ MEDIA_URL }}img/file/{{ sfile.path|file_icon_filter }}" alt="文件" /></td>
<td>
<a href="{% url 'repo_view_file' sfile.repo.id %}?p={{ sfile.path|urlencode }}">{{ sfile.formatted_path }}</a>
</td>
<td>{{ sfile.repo.name }}</td>
<td>{{ sfile.last_modified|translate_commit_time }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p class="no-starred-file-tips">您可以把重要的文件加上星标,这样它们就能显示在这里</p>
{% endif %}
</div>
</div>

View File

@@ -53,6 +53,7 @@ urlpatterns = patterns('',
url(r'^repo/revert_dir/(?P<repo_id>[^/]+)/$', repo_revert_dir, name='repo_revert_dir'),
url(r'^repo/upload_file/(?P<repo_id>[^/]+)/$', repo_upload_file, name='repo_upload_file'),
url(r'^repo/update_file/(?P<repo_id>[^/]+)/$', repo_update_file, name='repo_update_file'),
url(r'^repo/star_file/(?P<repo_id>[^/]+)/$', repo_star_file, name='repo_star_file'),
(r'^repo/upload_error/(?P<repo_id>[^/]+)/$', upload_file_error),
(r'^repo/update_error/(?P<repo_id>[^/]+)/$', update_file_error),
url(r'^repo/file_revisions/(?P<repo_id>[^/]+)/$', file_revisions, name='file_revisions'),

127
utils.py
View File

@@ -4,12 +4,14 @@ import os
import re
import random
import stat
import urllib2
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.hashcompat import sha_constructor
from base.models import FileContributors
from base.models import FileContributors, UserStarredFiles
from django.utils.hashcompat import md5_constructor
from pysearpc import SearpcError
@@ -468,3 +470,126 @@ else:
pass
def get_org_user_events():
pass
class StarredFile(object):
def format_path(self):
if self.path == "/":
return self.path
# strip leading slash
path = self.path[1:]
if path[-1:] == '/':
path = path[:-1]
return path.replace('/', ' / ')
def __init__(self, org_id, repo, path, is_dir, last_modified):
# always 0 for non-org repo
self.org_id = org_id
self.repo = repo
self.path = path
self.formatted_path = self.format_path()
self.is_dir = is_dir
# always 0 for dir
self.last_modified = last_modified
# org_id > 0: get starred files in org repos
# org_id < 0: get starred files in personal repos
def get_starred_files(email, org_id=-1):
"""Return a list of starred files for some user, sorted descending by the
last modified time.
"""
starred_files = UserStarredFiles.objects.filter(email=email, org_id=org_id)
ret = []
for sfile in starred_files:
# repo still exists?
try:
repo = get_repo(sfile.repo_id)
except SearpcError:
continue
if not repo:
sfile.delete()
continue
# file still exists?
file_id = ''
if sfile.path != "/":
try:
file_id = seafserv_threaded_rpc.get_file_by_path(sfile.repo_id, sfile.path)
except SearpcError:
continue
if not file_id:
sfile.delete()
continue
last_modified = 0
if not sfile.is_dir:
# last modified
path_hash = md5_constructor(urllib2.quote(sfile.path.encode('utf-8'))).hexdigest()[:12]
last_modified = get_file_contributors(sfile.repo_id, sfile.path, path_hash, file_id)[1]
f = StarredFile(sfile.org_id, repo, sfile.path, sfile.is_dir, last_modified)
ret.append(f)
# First sort by repo
# Within the same repo:
# dir > file
# dirs are sorted by name ascending
# files are sorted by last_modified descending
def sort_func(fa, fb):
# Different repo?
if fa.repo.id != fb.repo.id:
ret = cmp(fa.repo.name, fb.repo.name)
if ret != 0:
return ret
else:
# two different repo has the same name, compare the id
return cmp(fa.repo.id, fb.repo.id)
# OK, same repo
if fa.is_dir and fb.is_dir:
return cmp(fa.path, fb.path)
elif fa.is_dir and not fb.is_dir:
return -1
elif fb.is_dir and not fa.is_dir:
return 1
else:
return cmp(fb.last_modified, fa.last_modified)
ret.sort(sort_func)
return ret
def star_file(email, repo_id, path, is_dir, org_id=-1):
if is_file_starred(email, repo_id, path, org_id):
return
f = UserStarredFiles(email=email,
org_id=org_id,
repo_id=repo_id,
path=path,
is_dir=is_dir)
f.save()
def unstar_file(email, repo_id, path):
try:
f = UserStarredFiles.objects.get(email=email,
repo_id=repo_id,
path=path)
except UserStarredFiles.DoesNotExist:
pass
else:
f.delete()
def is_file_starred(email, repo_id, path, org_id=-1):
try:
f = UserStarredFiles.objects.get(email=email,
repo_id=repo_id,
path=path,
org_id=org_id)
return True
except UserStarredFiles.DoesNotExist:
return False

View File

@@ -68,7 +68,8 @@ from utils import render_permission_error, render_error, list_to_string, \
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, \
get_file_contributors, EVENTS_ENABLED, get_user_events
get_file_contributors, EVENTS_ENABLED, get_user_events, \
get_starred_files, star_file, unstar_file, is_file_starred
try:
from settings import DOCUMENT_CONVERTOR_ROOT
if DOCUMENT_CONVERTOR_ROOT[-1:] != '/':
@@ -282,6 +283,14 @@ class RepoView(CtxSwitchRequiredMixin, RepoMixin, TemplateResponseMixin,
is_group_user(x.id, self.user.username)]
return groups
def is_starred_dir(self):
org_id = -1
if self.request.user.org:
org_id = self.request.user.org['org_id']
args = (self.request.user.username, self.repo.id, self.path.encode('utf-8'), org_id)
print args
return is_file_starred(*args)
def get_context_data(self, **kwargs):
kwargs['repo'] = self.repo
# kwargs['can_access'] = self.can_access
@@ -296,6 +305,7 @@ class RepoView(CtxSwitchRequiredMixin, RepoMixin, TemplateResponseMixin,
kwargs['accessible_repos'] = self.get_accessible_repos()
kwargs['applet_root'] = self.applet_root
kwargs['groups'] = self.get_repo_shared_groups()
kwargs['is_starred'] = self.is_starred_dir()
if len(kwargs['groups']) > 1:
ctx = {}
ctx['groups'] = kwargs['groups']
@@ -919,6 +929,8 @@ def myhome(request):
else:
events = None
starred_files = get_starred_files(request.user.username)
return render_to_response('myhome.html', {
"nickname": nickname,
"owned_repos": owned_repos,
@@ -935,6 +947,7 @@ def myhome(request):
"create_shared_repo": False,
"allow_public_share": allow_public_share,
"events": events,
"starred_files": starred_files,
}, context_instance=RequestContext(request))
@login_required
@@ -1308,6 +1321,13 @@ def repo_view_file(request, repo_id):
else:
repogrp_str = ''
is_starred = False
if page_from != 'recycle':
org_id = -1
if request.user.org:
org_id = request.user.org['org_id']
is_starred = is_file_starred(request.user.username, repo.id, path.encode('utf-8'), org_id)
return render_to_response('repo_view_file.html', {
'repo': repo,
'obj_id': obj_id,
@@ -1342,6 +1362,7 @@ def repo_view_file(request, repo_id):
'read_only': read_only,
'page_from': page_from,
'repo_group_str': repogrp_str,
'is_starred': is_starred,
}, context_instance=RequestContext(request))
def file_comment(request):
@@ -2752,3 +2773,22 @@ def i18n(request):
return res
@login_required
def repo_star_file(request, repo_id):
path = request.POST.get('path')
state = request.POST.get('state');
content_type = 'application/json; charset=utf-8'
if not (path and state):
return HttpResponse(json.dumps({'success':False, 'err_msg':u'参数错误'}),
content_type=content_type)
org_id = int(request.POST.get('org_id'))
path = urllib2.unquote(path.encode('utf-8'))
is_dir = False
if state == 'unstarred':
star_file(request.user.username, repo_id, path, is_dir, org_id=org_id)
else:
unstar_file(request.user.username, repo_id, path)
return HttpResponse(json.dumps({'success':True}), content_type=content_type)