mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 23:20:51 +00:00
Add group feature
This commit is contained in:
@@ -6,4 +6,8 @@ register = template.Library()
|
||||
@register.filter(name='tsstr_sec')
|
||||
def tsstr_sec(value):
|
||||
"""Turn a timestamp to string"""
|
||||
return datetime.fromtimestamp(value).strftime("%Y-%m-%d %H:%M:%S")
|
||||
try:
|
||||
return datetime.fromtimestamp(value).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
return datetime.fromtimestamp(value/1000000).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
@@ -1,33 +1,3 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class Group(models.Model):
|
||||
"""A group is identified uniquely by group_id."""
|
||||
|
||||
group_id = models.CharField(max_length=36, primary_key=True, db_column='uuid')
|
||||
name = models.CharField(max_length=20, editable=False, unique=True)
|
||||
creator = models.ForeignKey(User, verbose_name=_("creator"), related_name="%(class)s_created")
|
||||
admin = models.ForeignKey(User, verbose_name=_("admin"), related_name="%(class)s_admined")
|
||||
ctime = models.DateTimeField('create time', editable=False)
|
||||
description = models.TextField(_("description"))
|
||||
users = models.ManyToManyField(User)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class JoinRequest(models.Model):
|
||||
"""A request for joining a group."""
|
||||
group = models.ForeignKey(Group)
|
||||
user = models.ForeignKey(User)
|
||||
ctime = models.DateTimeField('create time', editable=False)
|
||||
|
||||
|
||||
class Invitation(models.Model):
|
||||
"""An invitation for joining a group."""
|
||||
|
||||
group = models.ForeignKey(Group)
|
||||
user = models.ForeignKey(User)
|
||||
ctime = models.DateTimeField('create time', editable=False)
|
||||
# Create your models here.
|
||||
|
118
group/templates/group/group_info.html
Normal file
118
group/templates/group/group_info.html
Normal file
@@ -0,0 +1,118 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
{% load seahub_tags %}
|
||||
|
||||
{% block nav_group_class %}class="cur"{% endblock %}
|
||||
{% block left_panel %}
|
||||
<ul>
|
||||
<li>管理员</li>
|
||||
{% for member in members %}
|
||||
{% if member.props.is_staff == 1 %}
|
||||
<p><a href="{{ SITE_ROOT }}useradmin/info/{{ member.props.user_name }}/">{{ member.props.user_name }}</a></p>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li>成员</li>
|
||||
{% for member in members %}
|
||||
{% if member.props.is_staff == 0 %}
|
||||
<p><a href="{{ SITE_ROOT }}useradmin/info/{{ member.props.user_name }}/">{{ member.props.user_name }}</a></p>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li>操作</li>
|
||||
{% if is_creator %}
|
||||
<p><a id="add-member" href="#">添加成员</a></p>
|
||||
<p><a id="remove-member" href="#">删除成员</a></p>
|
||||
<p><a id="remove-group" href="#">解散小组</a></p>
|
||||
{% else %}
|
||||
<p><a id="quit-group" href="#">退出小组</a></p>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block right_panel %}
|
||||
|
||||
<h3>小组里共享的同步目录</h3>
|
||||
{% if repos %}
|
||||
<table class="repo-list">
|
||||
<tr>
|
||||
<th width="30%">名字</th>
|
||||
<th width="30%">描述</th>
|
||||
<th width="20%">共享来源</th>
|
||||
<th width="20%">操作</th>
|
||||
</tr>
|
||||
|
||||
{% for repo in repos %}
|
||||
<tr>
|
||||
<td><a href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/">{{ repo.props.name }}</a></td>
|
||||
<td>{{ repo.props.desc }}</td>
|
||||
<td>{{ repo.share_from }}</td>
|
||||
<td>
|
||||
<button data="{{ SITE_ROOT }}download/repo/?repo_id={{ repo.props.id }}" class="download-btn">下载</button>
|
||||
{% if is_creator or repo.share_from_me %}
|
||||
<button data="{{ SITE_ROOT }}shareadmin/removeshare/?repo_id={{ repo.props.id }}&gid={{ group_id }}" class="repo-delete-btn">删除</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>暂无</p>
|
||||
{% endif %}
|
||||
|
||||
<form id="member-add-form" action="{{ SITE_ROOT }}group/memberadd/" method="post" name="member-add-form" class="hide">
|
||||
<label>邮箱</label><br />
|
||||
<input id="user_name" name="user_name" value="" />
|
||||
<input type="hidden" id="group_id" name="group_id" value="{{ group_id }}" />
|
||||
<input type="submit" value="提交" />
|
||||
</form>
|
||||
<form id="member-remove-form" action="{{ SITE_ROOT }}group/memberrm/" method="post" name="member-remove-form" class="hide">
|
||||
<label>邮箱</label><br />
|
||||
<input id="user_name" name="user_name" value="" />
|
||||
<input type="hidden" id="group_id" name="group_id" value="{{ group_id }}" />
|
||||
<input type="submit" value="提交" />
|
||||
</form>
|
||||
|
||||
<div id="dialog-remove-confirm" class="center hide">
|
||||
<p>确定要解散?</p>
|
||||
<button id="rm-yes-btn">确定</button>
|
||||
<button class="simplemodal-close">取消</button>
|
||||
</div>
|
||||
|
||||
<div id="dialog-quit-confirm" class="center hide">
|
||||
<p>确定要退出?</p>
|
||||
<button id="quit-yes-btn">确定</button>
|
||||
<button class="simplemodal-close">取消</button>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$("#add-member").click(function() {
|
||||
$("#member-add-form").modal({appendTo: "#main", containerCss:{padding:18}});
|
||||
});
|
||||
|
||||
$("#remove-member").click(function() {
|
||||
$("#member-remove-form").modal({appendTo: "#main", containerCss:{padding:18}});
|
||||
});
|
||||
|
||||
$("#remove-group").click(function() {
|
||||
$('#dialog-remove-confirm').modal({appendTo:'#main'});
|
||||
});
|
||||
|
||||
$("#quit-group").click(function() {
|
||||
$('#dialog-quit-confirm').modal({appendTo:'#main'});
|
||||
});
|
||||
|
||||
$('#rm-yes-btn').click(function() {
|
||||
location.href = "{{ SITE_ROOT }}group/rm/?gid={{ group_id }}";
|
||||
});
|
||||
|
||||
$('#quit-yes-btn').click(function() {
|
||||
location.href = "{{ SITE_ROOT }}group/quit/?gid={{ group_id }}";
|
||||
});
|
||||
|
||||
$('.repo-delete-btn').click(function() {
|
||||
location.href = $(this).attr('data');
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
49
group/templates/group/groups.html
Normal file
49
group/templates/group/groups.html
Normal file
@@ -0,0 +1,49 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
{% load seahub_tags %}
|
||||
|
||||
{% block nav_group_class %}class="cur"{% endblock %}
|
||||
{% block left_panel %}
|
||||
<ul>
|
||||
<li><a id="group-add" href="#">添加小组</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block right_panel %}
|
||||
|
||||
<h3>我参加的小组</h3>
|
||||
{% if groups %}
|
||||
<table class="group-list">
|
||||
<tr>
|
||||
<th width="30%">名字</th>
|
||||
<th width="40%">创建者</th>
|
||||
<th width="30%">创建时间</th>
|
||||
</tr>
|
||||
|
||||
{% for group in groups %}
|
||||
<tr>
|
||||
<td><a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">{{ group.props.group_name }}</a></td>
|
||||
<td>{{ group.props.creator_name }}</td>
|
||||
<td>{{ group.props.timestamp|tsstr_sec }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>暂无</p>
|
||||
{% endif %}
|
||||
|
||||
<form id="group-add-form" action="{{ SITE_ROOT }}group/add/" method="post" name="group-add-form" class="hide">
|
||||
<label>小组名称</label><br />
|
||||
<input id="group_name" name="group_name" value="" />
|
||||
<input type="submit" value="提交" />
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$("#group-add").click(function() {
|
||||
$("#group-add-form").modal({appendTo: "#main", containerCss:{padding:18}});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
23
group/tests.py
Normal file
23
group/tests.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
|
15
group/urls.py
Normal file
15
group/urls.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
from views import group_list, group_add, group_info, \
|
||||
group_add_member, group_remove_member, \
|
||||
group_quit, group_remove
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', group_list, name='group_list'),
|
||||
(r'^add/$', group_add),
|
||||
(r'^rm/$', group_remove),
|
||||
(r'^memberadd/$', group_add_member),
|
||||
(r'^memberrm/$', group_remove_member),
|
||||
(r'^quit/$', group_quit),
|
||||
url(r'^(?P<group_id>[^/]+)/$', group_info, name='group_info'),
|
||||
)
|
153
group/views.py
153
group/views.py
@@ -1,4 +1,153 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from django.shortcuts import render_to_response, redirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import RequestContext
|
||||
from auth.decorators import login_required
|
||||
|
||||
from seaserv import ccnet_rpc, seafserv_threaded_rpc, get_repo, \
|
||||
get_group_repoids
|
||||
|
||||
from seahub.views import validate_emailuser
|
||||
from seahub.utils import go_error, go_permission_error
|
||||
|
||||
from pysearpc import SearpcError
|
||||
|
||||
@login_required
|
||||
def group_list(request):
|
||||
groups = ccnet_rpc.get_groups(request.user.username);
|
||||
|
||||
return render_to_response("group/groups.html", {
|
||||
"groups": groups,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def group_add(request):
|
||||
error_msg = None
|
||||
if request.method == 'POST':
|
||||
group_name = request.POST.get('group_name')
|
||||
if group_name.find('@') >= 0:
|
||||
return go_error(request, u'小组名称不能包含@')
|
||||
|
||||
try:
|
||||
ccnet_rpc.create_group(group_name.encode('utf-8'), request.user.username)
|
||||
except SearpcError, e:
|
||||
error_msg = e.msg
|
||||
return go_error(request, error_msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('group_list', args=[]))
|
||||
|
||||
@login_required
|
||||
def group_remove(request):
|
||||
group_id = request.GET.get('gid')
|
||||
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except ValueError:
|
||||
group_id_int = -1
|
||||
|
||||
try:
|
||||
ccnet_rpc.remove_group(group_id_int, request.user.username)
|
||||
seafserv_threaded_rpc.remove_repo_group(group_id_int, None)
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('group_list', args=[]))
|
||||
|
||||
@login_required
|
||||
def group_info(request, group_id):
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except ValueError:
|
||||
return HttpResponseRedirect(reverse('group_list', args=[]))
|
||||
|
||||
group = ccnet_rpc.get_group(group_id_int)
|
||||
if not group:
|
||||
return HttpResponseRedirect(reverse('group_list', args=[]))
|
||||
|
||||
if group.props.creator_name == request.user.username:
|
||||
is_creator = True
|
||||
else:
|
||||
is_creator = False
|
||||
|
||||
members = ccnet_rpc.get_group_members(group_id_int)
|
||||
|
||||
repos = []
|
||||
repo_ids = get_group_repoids(group_id=group_id_int)
|
||||
for repo_id in repo_ids:
|
||||
if not repo_id:
|
||||
continue
|
||||
repo = get_repo(repo_id)
|
||||
repo.share_from = seafserv_threaded_rpc.get_group_repo_share_from(repo_id)
|
||||
if request.user.username == repo.share_from:
|
||||
repo.share_from_me = True
|
||||
else:
|
||||
repo.share_from_me = False
|
||||
repos.append(repo)
|
||||
|
||||
return render_to_response("group/group_info.html", {
|
||||
"members": members,
|
||||
"repos": repos,
|
||||
"group_id": group_id,
|
||||
"is_creator": is_creator,
|
||||
}, context_instance=RequestContext(request));
|
||||
|
||||
@login_required
|
||||
def group_add_member(request):
|
||||
if request.method == 'POST':
|
||||
group_id = request.POST.get('group_id')
|
||||
member_name = request.POST.get('user_name')
|
||||
if not validate_emailuser(member_name):
|
||||
err_msg = u'用户不存在'
|
||||
return go_error(request, err_msg)
|
||||
else:
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except ValueError:
|
||||
return go_error(request, u'group id 不是有效参数')
|
||||
try:
|
||||
ccnet_rpc.group_add_member(group_id_int, request.user.username,
|
||||
member_name)
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('group_info', args=[group_id]))
|
||||
|
||||
@login_required
|
||||
def group_remove_member(request):
|
||||
if request.method == 'POST':
|
||||
group_id = request.POST.get('group_id')
|
||||
member_name = request.POST.get('user_name')
|
||||
if not validate_emailuser(member_name):
|
||||
err_msg = u'用户不存在'
|
||||
return go_error(request, err_msg)
|
||||
else:
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except ValueError:
|
||||
raise Http404
|
||||
try:
|
||||
ccnet_rpc.group_remove_member(group_id_int, request.user.username,
|
||||
member_name)
|
||||
seafserv_threaded_rpc.remove_repo_group(group_id_int, member_name)
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('group_info', args=[group_id]))
|
||||
|
||||
@login_required
|
||||
def group_quit(request):
|
||||
group_id = request.GET.get('gid')
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except ValueError:
|
||||
raise Http404
|
||||
|
||||
try:
|
||||
ccnet_rpc.quit_group(group_id_int, request.user.username)
|
||||
seafserv_threaded_rpc.remove_repo_group(group_id_int, request.user.username)
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('group_list', args=[]))
|
||||
|
@@ -103,7 +103,7 @@ INSTALLED_APPS = (
|
||||
'seahub.base',
|
||||
'seahub.profile',
|
||||
'seahub.contacts',
|
||||
# 'seahub.group',
|
||||
'seahub.group',
|
||||
# 'seahub.share',
|
||||
)
|
||||
|
||||
|
8
templates/error.html
Normal file
8
templates/error.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
|
||||
{% block title %}错误{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
|
||||
<p>{{ error_msg }}</p>
|
||||
{% endblock %}
|
@@ -1,33 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Groups{% endblock %}
|
||||
|
||||
{% block left_panel %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block right_panel %}
|
||||
|
||||
<p>Use the following command to add a group:</p>
|
||||
<div><code>ccnet-tool group-follow <group-id> <rendevous-id></code></div>
|
||||
|
||||
<h3>Groups</h3>
|
||||
|
||||
<table class="group-list default">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>ID</th>
|
||||
<th>Rendezvous</th>
|
||||
<th>Maintainer</th>
|
||||
</tr>
|
||||
{% for group in groups %}
|
||||
<tr>
|
||||
<td>{{ group.props.name }}</td>
|
||||
<td>{{ group.props.id }}</td>
|
||||
<td>{{ group.props.rendezvous }}</td>
|
||||
<td>{{ group.props.maintainers }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
@@ -70,7 +70,7 @@
|
||||
{% endif %}
|
||||
|
||||
<form id="repo-share-form" action="{{ SITE_ROOT }}home/my/" method="post" name="repo-share-form" class="hide">
|
||||
<label>邮箱:</label><br />
|
||||
<label>邮箱或小组:</label><br />
|
||||
<textarea id="to_email" name="to_email"></textarea><br />
|
||||
<input id="share_repo_id" type="hidden" name="share_repo_id" value="" />
|
||||
<p class="error hide">输入不能为空。</p>
|
||||
@@ -88,6 +88,10 @@ $(function() {
|
||||
contact_list.push('{{ contact.contact_email }}');
|
||||
{% endfor %}
|
||||
|
||||
{% for group in groups %}
|
||||
contact_list.push('{{ group.props.group_name }} <{{ group.props.creator_name }}>');
|
||||
{% endfor %}
|
||||
|
||||
function split(val) {
|
||||
return val.split(/,\s*/);
|
||||
}
|
||||
|
@@ -10,7 +10,9 @@
|
||||
<li>
|
||||
<a href="{{ SITE_ROOT }}contacts/" {% block nav_contacts_class %}{% endblock %}>联系人</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ SITE_ROOT }}group/" {% block nav_group_class %}{% endblock %}>小组</a>
|
||||
</li>
|
||||
{% if request.user.is_staff %}
|
||||
<li>
|
||||
<a href="{{ SITE_ROOT }}seafadmin/" {% block nav_seafadmin_class %}{% endblock %}>目录管理</a>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
|
||||
{% block title %}Permission Error{% endblock %}
|
||||
{% block title %}权限错误{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
|
||||
<p>Permission Error.</p>
|
||||
<p>{{ error_msg }}</p>
|
||||
{% endblock %}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<td>{{ repo.props.shared_email }}</td>
|
||||
<td>{{ repo.props.desc }}</td>
|
||||
<td>
|
||||
<button data="{{ SITE_ROOT }}shareadmin/removeshare/?repo_id={{ repo.props.id }}&to_email={{ repo.props.shared_email }}" class="cancel-share-btn">取消共享</button>
|
||||
<button data="{{ SITE_ROOT }}shareadmin/removeshare/?repo_id={{ repo.props.id }}&to_email={{ repo.props.shared_email }}&gid={{ repo.gid }}" class="cancel-share-btn">取消共享</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@@ -12,6 +12,7 @@ from service import get_users, get_user, get_events, count_event
|
||||
from service import get_repos, get_repo, get_commits, get_branches
|
||||
from service import get_binding_peerids
|
||||
from service import get_ccnetuser
|
||||
from service import get_group_repoids
|
||||
|
||||
from service import CCNET_CONF_PATH
|
||||
|
||||
|
@@ -385,9 +385,31 @@ def get_binding_peerids(email):
|
||||
except SearpcError:
|
||||
return []
|
||||
|
||||
if not peer_ids:
|
||||
return []
|
||||
|
||||
peerid_list = []
|
||||
for peer_id in peer_ids.split("\n"):
|
||||
if peer_id == '':
|
||||
continue
|
||||
peerid_list.append(peer_id)
|
||||
return peerid_list
|
||||
|
||||
def get_group_repoids(group_id=None):
|
||||
"""Get repo ids of a given group id or username"""
|
||||
try:
|
||||
repo_ids = seafserv_threaded_rpc.get_group_repoids(group_id)
|
||||
except SearpcError:
|
||||
return []
|
||||
|
||||
if not repo_ids:
|
||||
return []
|
||||
|
||||
repoid_list = []
|
||||
for repo_id in repo_ids.split("\n"):
|
||||
if repo_id == '':
|
||||
continue
|
||||
repoid_list.append(repo_id)
|
||||
return repoid_list
|
||||
|
||||
|
||||
|
5
urls.py
5
urls.py
@@ -2,8 +2,8 @@ from django.conf.urls.defaults import *
|
||||
from django.conf import settings
|
||||
from django.views.generic.simple import direct_to_template
|
||||
|
||||
from seahub.views import root, peers, groups, myhome, \
|
||||
repo, repo_history, group, modify_token, remove_repo, seafadmin, useradmin, \
|
||||
from seahub.views import root, peers, myhome, \
|
||||
repo, repo_history, modify_token, remove_repo, seafadmin, useradmin, \
|
||||
role_add, role_remove, activate_user, user_add, user_remove, \
|
||||
ownerhome, remove_fetched_repo, \
|
||||
repo_list_dir, user_info, repo_set_access_property, repo_operation_file, \
|
||||
@@ -54,6 +54,7 @@ urlpatterns = patterns('',
|
||||
(r'^useradmin/(?P<user_id>[^/]+)/user/remove/$', user_remove),
|
||||
(r'^useradmin/activate/(?P<user_id>[^/]+)/$', activate_user),
|
||||
# (r'^avatar/', include('avatar.urls')),
|
||||
(r'^group/', include('seahub.group.urls')),
|
||||
(r'^profile/', include('seahub.profile.urls')),
|
||||
(r'^back/local/$', back_local),
|
||||
|
||||
|
73
utils.py
Normal file
73
utils.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
|
||||
from django.utils.hashcompat import sha_constructor
|
||||
|
||||
import time
|
||||
import settings
|
||||
|
||||
def go_permission_error(request, msg=None):
|
||||
"""
|
||||
return permisson error page
|
||||
|
||||
"""
|
||||
return render_to_response('permission_error.html', {
|
||||
'error_msg': msg or u'权限错误',
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def go_error(request, msg=None):
|
||||
"""
|
||||
return normal error page
|
||||
|
||||
"""
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': msg or u'内部错误',
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def list_to_string(l):
|
||||
"""
|
||||
return string of a list
|
||||
|
||||
"""
|
||||
tmp_str = ''
|
||||
for e in l[:-1]:
|
||||
tmp_str = tmp_str + e + ', '
|
||||
tmp_str = tmp_str + l[-1]
|
||||
|
||||
return tmp_str
|
||||
|
||||
def get_httpserver_root():
|
||||
"""
|
||||
Get seafile http server address and port from settings.py,
|
||||
and cut out last '/'
|
||||
|
||||
"""
|
||||
if settings.HTTP_SERVER_ROOT[-1] == '/':
|
||||
http_server_root = settings.HTTP_SERVER_ROOT[:-1]
|
||||
else:
|
||||
http_server_root = settings.HTTP_SERVER_ROOT
|
||||
return http_server_root
|
||||
|
||||
def get_ccnetapplet_root():
|
||||
"""
|
||||
Get ccnet applet address and port from settings.py,
|
||||
and cut out last '/'
|
||||
|
||||
"""
|
||||
if settings.CCNET_APPLET_ROOT[-1] == '/':
|
||||
ccnet_applet_root = settings.CCNET_APPLET_ROOT[:-1]
|
||||
else:
|
||||
ccnet_applet_root = settings.CCNET_APPLET_ROOT
|
||||
return ccnet_applet_root
|
||||
|
||||
def gen_token():
|
||||
"""
|
||||
Generate short token used for owner to access repo file
|
||||
|
||||
"""
|
||||
|
||||
token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[::8]
|
||||
return token
|
258
views.py
258
views.py
@@ -1,4 +1,5 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from django.shortcuts import render_to_response, redirect
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -6,13 +7,15 @@ from django.template import RequestContext
|
||||
from auth.decorators import login_required
|
||||
from django.db import IntegrityError
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, PasswordChangeForm
|
||||
from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, \
|
||||
PasswordChangeForm
|
||||
from auth.tokens import default_token_generator
|
||||
|
||||
from pysearpc import SearpcError
|
||||
from seaserv import ccnet_rpc, get_groups, get_users, get_repos, \
|
||||
get_repo, get_commits, get_branches, \
|
||||
seafserv_threaded_rpc, seafserv_rpc, get_binding_peerids, get_ccnetuser
|
||||
seafserv_threaded_rpc, seafserv_rpc, get_binding_peerids, get_ccnetuser, \
|
||||
get_group_repoids
|
||||
|
||||
from seahub.share.models import GroupShare, UserShare
|
||||
from seahub.share.forms import GroupAddRepoForm
|
||||
@@ -22,42 +25,61 @@ from urllib import quote
|
||||
|
||||
from seahub.contacts.models import Contact
|
||||
|
||||
from utils import go_permission_error, go_error, list_to_string, get_httpserver_root, \
|
||||
get_ccnetapplet_root, gen_token
|
||||
|
||||
import stat
|
||||
import time
|
||||
import settings
|
||||
|
||||
def list_to_string(l):
|
||||
tmp_str = ''
|
||||
for e in l[:-1]:
|
||||
tmp_str = tmp_str + e + ', '
|
||||
tmp_str = tmp_str + l[-1]
|
||||
|
||||
return tmp_str
|
||||
def group_share_repo(request, repo_id, group_id, from_email):
|
||||
"""
|
||||
share a repo to a group
|
||||
|
||||
def get_httpserver_root():
|
||||
# Get seafile http server address and port from settings.py,
|
||||
# and cut out last '/'
|
||||
if settings.HTTP_SERVER_ROOT[-1] == '/':
|
||||
http_server_root = settings.HTTP_SERVER_ROOT[:-1]
|
||||
else:
|
||||
http_server_root = settings.HTTP_SERVER_ROOT
|
||||
return http_server_root
|
||||
"""
|
||||
# check whether group exists
|
||||
group = ccnet_rpc.get_group(group_id)
|
||||
if not group:
|
||||
return go_error(request, u'共享失败:小组不存在')
|
||||
|
||||
def get_ccnetapplet_root():
|
||||
# Get ccnet applet address and port from settings.py,
|
||||
# and cut out last '/'
|
||||
if settings.CCNET_APPLET_ROOT[-1] == '/':
|
||||
ccnet_applet_root = settings.CCNET_APPLET_ROOT[:-1]
|
||||
else:
|
||||
ccnet_applet_root = settings.CCNET_APPLET_ROOT
|
||||
return ccnet_applet_root
|
||||
# check whether user belong to the group
|
||||
joined = False
|
||||
groups = ccnet_rpc.get_groups(request.user.username)
|
||||
for group in groups:
|
||||
if group.props.id == group_id:
|
||||
joined = True
|
||||
if not joined:
|
||||
return go_error(request, u'共享失败:未加入该小组')
|
||||
|
||||
if seafserv_threaded_rpc.group_share_repo(repo_id, group_id, from_email) != 0:
|
||||
return go_error(request, u'共享失败:内部错误')
|
||||
|
||||
def gen_token():
|
||||
# Generate short token used for owner to access repo file
|
||||
from django.utils.hashcompat import sha_constructor
|
||||
token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[::8]
|
||||
return token
|
||||
def group_unshare_repo(request, repo_id, group_id, from_email):
|
||||
"""
|
||||
unshare a repo to a group
|
||||
|
||||
"""
|
||||
# check whether group exists
|
||||
group = ccnet_rpc.get_group(group_id)
|
||||
if not group:
|
||||
return go_error(request, u'共享失败:小组不存在')
|
||||
|
||||
# check whether user belong to the group
|
||||
joined = False
|
||||
groups = ccnet_rpc.get_groups(from_email)
|
||||
for group in groups:
|
||||
if group.props.id == group_id:
|
||||
joined = True
|
||||
if not joined:
|
||||
return go_error(request, u'共享失败:未加入该小组')
|
||||
|
||||
# check whether user is group staff or the one share the repo
|
||||
if not ccnet_rpc.check_group_staff(group_id, from_email) and \
|
||||
seafserv_threaded_rpc.get_group_repo_share_from(repo_id) != from_email:
|
||||
return go_permission_error(request, u'取消共享失败:只有小组管理员或共享目录发布者有权取消共享')
|
||||
|
||||
if seafserv_threaded_rpc.group_unshare_repo(repo_id, group_id, from_email) != 0:
|
||||
return go_error(request, u'共享失败:内部错误')
|
||||
|
||||
@login_required
|
||||
def root(request):
|
||||
@@ -84,75 +106,27 @@ def peers(request):
|
||||
'users': users,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def groups(request):
|
||||
groups = get_groups()
|
||||
return render_to_response('groups.html', {
|
||||
'groups': groups,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def group(request, group_id):
|
||||
"""Show a group.
|
||||
|
||||
Login is not required, but permission check based on token should
|
||||
be added later.
|
||||
"""
|
||||
|
||||
group = get_group(group_id)
|
||||
shared_repos = GroupShare.objects.filter(group_id=group_id)
|
||||
return render_to_response('group.html', {
|
||||
'group': group, 'shared_repos': shared_repos,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def group_add_repo(request, group_id):
|
||||
"""Add a repo to a group"""
|
||||
|
||||
group = get_group(group_id)
|
||||
if not group:
|
||||
raise Http404
|
||||
|
||||
if request.method == 'POST':
|
||||
form = GroupAddRepoForm(request.POST)
|
||||
if form.is_valid():
|
||||
group_repo = GroupShare()
|
||||
group_repo.group_id = group_id
|
||||
group_repo.repo_id = form.cleaned_data['repo_id']
|
||||
try:
|
||||
group_repo.save()
|
||||
except IntegrityError:
|
||||
# catch the case repo added to group before
|
||||
pass
|
||||
return HttpResponseRedirect(reverse('view_group', args=[group_id]))
|
||||
else:
|
||||
form = GroupAddRepoForm()
|
||||
|
||||
return render_to_response("group_add_repo.html", {
|
||||
'form': form, 'group': group
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def validate_owner(request, repo_id):
|
||||
# check whether email in the request own the repo
|
||||
return seafserv_threaded_rpc.is_repo_owner(request.user.username, repo_id)
|
||||
|
||||
#def check_fetched_repo(request, repo_id):
|
||||
# # check whether user has fetched the repo
|
||||
# peerid_list = get_binding_peerids(request.user.username)
|
||||
# for peer_id in peerid_list:
|
||||
# repos = seafserv_threaded_rpc.list_fetched_repos(peer_id)
|
||||
# for repo in repos:
|
||||
# if cmp(repo.props.id, repo_id):
|
||||
# return True
|
||||
#
|
||||
# return False
|
||||
|
||||
def check_shared_repo(request, repo_id):
|
||||
# check whether user has been shared this repo
|
||||
repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1)
|
||||
"""
|
||||
check whether user has been shared this repo or
|
||||
the repo share to the groups user join
|
||||
|
||||
"""
|
||||
repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1)
|
||||
for repo in repos:
|
||||
if cmp(repo.props.id, repo_id) == 0:
|
||||
if repo.props.id == repo_id:
|
||||
return True
|
||||
|
||||
groups = ccnet_rpc.get_groups(request.user.username)
|
||||
# for every group that user joined...
|
||||
for group in groups:
|
||||
# ...get repo ids in that group, and check whether repo ids contains that repo id
|
||||
repo_ids = get_group_repoids(group.props.id)
|
||||
if repo_ids.__contains__(repo_id):
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -210,11 +184,13 @@ def repo(request, repo_id):
|
||||
pass
|
||||
|
||||
# used to determin whether show repo content in repo.html
|
||||
# if a repo is shared to me, then I can view repo content on the web
|
||||
# if a repo is shared to me, or repo shared to the group I joined,
|
||||
# then I can view repo content on the web
|
||||
if check_shared_repo(request, repo_id):
|
||||
share_to_me = True
|
||||
else:
|
||||
share_to_me = False
|
||||
|
||||
return render_to_response('repo.html', {
|
||||
"repo": repo,
|
||||
"latest_commit": latest_commit,
|
||||
@@ -256,15 +232,6 @@ def repo_history(request, repo_id):
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def repo_share(request, repo_id):
|
||||
return render_to_response('repo_share.html', {
|
||||
"repo": repo,
|
||||
"commits": commits,
|
||||
"branches": branches,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def modify_token(request, repo_id):
|
||||
if not validate_owner(request, repo_id):
|
||||
@@ -280,8 +247,7 @@ def modify_token(request, repo_id):
|
||||
@login_required
|
||||
def remove_repo(request, repo_id):
|
||||
if not validate_owner(request, repo_id) and not request.user.is_staff:
|
||||
return render_to_response('permission_error.html', {
|
||||
}, context_instance=RequestContext(request))
|
||||
return go_permission_error(request, u'权限不足:无法查看该用户信息')
|
||||
|
||||
seafserv_threaded_rpc.remove_repo(repo_id)
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
@@ -306,17 +272,23 @@ def myhome(request):
|
||||
# Repos that are share to me
|
||||
in_repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1)
|
||||
|
||||
# handle share repo request
|
||||
if request.method == 'POST':
|
||||
output_msg = repo_add_share(request)
|
||||
|
||||
# my contacts
|
||||
contacts = Contact.objects.filter(user_email=email)
|
||||
|
||||
# groups I join
|
||||
groups = ccnet_rpc.get_groups(email)
|
||||
|
||||
return render_to_response('myhome.html', {
|
||||
"owned_repos": owned_repos,
|
||||
"quota_usage": quota_usage,
|
||||
"in_repos": in_repos,
|
||||
"output_msg": output_msg,
|
||||
"contacts": contacts,
|
||||
"groups": groups,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
@@ -426,7 +398,13 @@ def repo_add_share(request):
|
||||
if request.method == 'POST':
|
||||
from_email = request.user.username
|
||||
repo_id = request.POST.get('share_repo_id', '')
|
||||
to_email_list = request.POST.get('to_email', '').split(',')
|
||||
|
||||
# Handle the diffent separator
|
||||
to_email_str = request.POST.get('to_email', '').replace(';',',')
|
||||
to_email_str = to_email_str.replace('\n',',')
|
||||
to_email_str = to_email_str.replace('\r',',')
|
||||
|
||||
to_email_list = to_email_str.split(',')
|
||||
info_emails = []
|
||||
err_emails = []
|
||||
for to_email in to_email_list:
|
||||
@@ -434,16 +412,36 @@ def repo_add_share(request):
|
||||
if not to_email:
|
||||
continue
|
||||
|
||||
if validate_emailuser(to_email) and validate_owner(request, repo_id):
|
||||
seafserv_threaded_rpc.add_share(repo_id, from_email, to_email, 'rw')
|
||||
info_emails.append(to_email)
|
||||
# if to_email is user name, the format is: 'example@mail.com';
|
||||
# if to_email is group, the format is 'group_name <creator@mail.com>'
|
||||
if (to_email.split(' ')[0].find('@') == -1):
|
||||
group_name = to_email.split(' ')[0]
|
||||
group_creator = to_email.split(' ')[1]
|
||||
if validate_owner(request, repo_id):
|
||||
groups = ccnet_rpc.get_groups(request.user.username)
|
||||
find = False
|
||||
for group in groups:
|
||||
if group.props.group_name == group_name and \
|
||||
group_creator.find(group.props.creator_name) >= 0:
|
||||
group_share_repo(request, repo_id, int(group.props.id), from_email)
|
||||
find = True
|
||||
info_emails.append(group_name)
|
||||
|
||||
if not find:
|
||||
err_emails.append(group_name)
|
||||
else:
|
||||
err_emails.append(group_name)
|
||||
else:
|
||||
err_emails.append(to_email)
|
||||
if validate_emailuser(to_email) and validate_owner(request, repo_id):
|
||||
seafserv_threaded_rpc.add_share(repo_id, from_email, to_email, 'rw')
|
||||
info_emails.append(to_email)
|
||||
else:
|
||||
err_emails.append(to_email)
|
||||
|
||||
if info_emails:
|
||||
output_msg['info_msg'] = u'共享给%s成功,' % list_to_string(info_emails)
|
||||
if err_emails:
|
||||
output_msg['err_msg'] = u'共享给%s失败:用户不存在' % list_to_string(err_emails)
|
||||
output_msg['err_msg'] = u'共享给%s失败:用户或组不存在' % list_to_string(err_emails)
|
||||
|
||||
return output_msg
|
||||
|
||||
@@ -451,8 +449,23 @@ def repo_add_share(request):
|
||||
def repo_list_share(request):
|
||||
username = request.user.username
|
||||
|
||||
# repos that are share to user
|
||||
out_repos = seafserv_threaded_rpc.list_share_repos(username, 'from_email', -1, -1)
|
||||
|
||||
# repos that are share to groups
|
||||
group_repos = seafserv_threaded_rpc.get_group_my_share_repos(request.user.username)
|
||||
for group_repo in group_repos:
|
||||
repo_id = group_repo.props.repo_id
|
||||
if not repo_id:
|
||||
continue
|
||||
repo = get_repo(repo_id)
|
||||
group_id = group_repo.props.group_id
|
||||
group = ccnet_rpc.get_group(int(group_id))
|
||||
repo.props.shared_email = group.props.group_name
|
||||
repo.gid = group_id
|
||||
|
||||
out_repos.append(repo)
|
||||
|
||||
return render_to_response('share_repos.html', {
|
||||
"out_repos": out_repos,
|
||||
}, context_instance=RequestContext(request))
|
||||
@@ -481,15 +494,29 @@ def repo_download(request):
|
||||
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
@login_required
|
||||
def repo_remove_share(request):
|
||||
repo_id = request.GET.get('repo_id', '')
|
||||
if not validate_owner(request, repo_id):
|
||||
raise Http404
|
||||
return go_permission_error(request, u'取消共享失败:不是目录拥有者')
|
||||
|
||||
to_email = request.GET.get('to_email', '')
|
||||
from_email = request.user.username
|
||||
seafserv_threaded_rpc.remove_share(repo_id, from_email, to_email)
|
||||
|
||||
group_id = request.GET.get('gid')
|
||||
|
||||
# if request params don't have 'gid', then remove repos that share to
|
||||
# to other person; else, remove repos that share to groups
|
||||
if not group_id:
|
||||
to_email = request.GET.get('to_email', '')
|
||||
seafserv_threaded_rpc.remove_share(repo_id, from_email, to_email)
|
||||
else:
|
||||
try:
|
||||
group_id_int = int(group_id)
|
||||
except:
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
|
||||
group_unshare_repo(request, repo_id, group_id_int, from_email)
|
||||
|
||||
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
||||
|
||||
@@ -553,8 +580,11 @@ def useradmin(request):
|
||||
|
||||
@login_required
|
||||
def user_info(request, email):
|
||||
if request.user.username == email:
|
||||
return HttpResponseRedirect(reverse(myhome))
|
||||
|
||||
if not request.user.is_staff:
|
||||
raise Http404
|
||||
return go_permission_error(request, u'权限不足:无法查看该用户信息')
|
||||
|
||||
user_dict = {}
|
||||
owned_repos = []
|
||||
|
Reference in New Issue
Block a user