mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 07:27:04 +00:00
add user events
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
from signals import repo_created, repo_deleted
|
||||||
|
from handlers import repo_created_cb, repo_deleted_cb
|
||||||
|
|
||||||
|
repo_created.connect(repo_created_cb)
|
||||||
|
repo_deleted.connect(repo_deleted_cb)
|
||||||
|
@@ -12,6 +12,12 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
BUSINESS_MODE = False
|
BUSINESS_MODE = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
from settings import EVENTS_CONFIG_FILE
|
||||||
|
SHOW_EVENTS = True
|
||||||
|
except ImportError:
|
||||||
|
SHOW_EVENTS = False
|
||||||
|
|
||||||
def base(request):
|
def base(request):
|
||||||
"""
|
"""
|
||||||
Add seahub base configure to the context.
|
Add seahub base configure to the context.
|
||||||
@@ -32,5 +38,6 @@ def base(request):
|
|||||||
'cloud_mode': request.cloud_mode,
|
'cloud_mode': request.cloud_mode,
|
||||||
'org': org,
|
'org': org,
|
||||||
'base_template': base_template,
|
'base_template': base_template,
|
||||||
|
'show_events': SHOW_EVENTS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
handlers.py
Normal file
42
handlers.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import seafevents
|
||||||
|
from utils import get_seafevents_session
|
||||||
|
|
||||||
|
def repo_created_cb(sender, **kwargs):
|
||||||
|
creator = kwargs['creator']
|
||||||
|
repo_id = kwargs['repo_id']
|
||||||
|
repo_name = kwargs['repo_name']
|
||||||
|
|
||||||
|
detail = {
|
||||||
|
'creator': creator,
|
||||||
|
'repo_id': repo_id,
|
||||||
|
'repo_name': repo_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
users = [creator]
|
||||||
|
|
||||||
|
session = get_seafevents_session()
|
||||||
|
seafevents.save_user_events (session, 'repo-create', detail, users, None)
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def repo_deleted_cb(sender, **kwargs):
|
||||||
|
"""When a repo is deleted, an event would be added to every user in all
|
||||||
|
groups to which this repo is shared.
|
||||||
|
|
||||||
|
"""
|
||||||
|
usernames = kwargs['usernames']
|
||||||
|
|
||||||
|
repo_owner = kwargs['repo_owner']
|
||||||
|
repo_id = kwargs['repo_id']
|
||||||
|
repo_name = kwargs['repo_name']
|
||||||
|
|
||||||
|
detail = {
|
||||||
|
'repo_owner': repo_owner,
|
||||||
|
'repo_id': repo_id,
|
||||||
|
'repo_name': repo_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
users = usernames
|
||||||
|
|
||||||
|
session = get_seafevents_session()
|
||||||
|
seafevents.save_user_events (session, 'repo-delete', detail, users, None)
|
||||||
|
session.close()
|
4
signals.py
Normal file
4
signals.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import django.dispatch
|
||||||
|
|
||||||
|
repo_created = django.dispatch.Signal(providing_args=["creator", "repo_id", "repo_name"])
|
||||||
|
repo_deleted = django.dispatch.Signal(providing_args=["usernames", "repo_owner", "repo_id", "repo_name"])
|
64
templates/events.html
Normal file
64
templates/events.html
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{% extends base_template %}
|
||||||
|
{% load seahub_tags avatar_tags%}
|
||||||
|
{% load url from future %}
|
||||||
|
|
||||||
|
{% block extra_style %}
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.event-item {
|
||||||
|
padding-top: 5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px dashed gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#events {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main_panel %}
|
||||||
|
<h2>最近事件</h2>
|
||||||
|
<ul id="events">
|
||||||
|
{% for ev in events %}
|
||||||
|
<li class="event-item">
|
||||||
|
{% if ev.etype == 'repo-update' %}
|
||||||
|
{% with author=ev.commit.creator_name commit=ev.commit repo=ev.repo %}
|
||||||
|
{% avatar author 24 %}<a href="{% url 'user_profile' author %}" class="name" title="{{ author|email2nickname}}">{{ author|email2nickname }}</a>
|
||||||
|
在 {{ commit.ctime|translate_commit_time }} 更新了目录
|
||||||
|
<a href="{{SITE_ROOT}}repo/{{repo.id}}">{{ repo.name }}</a>
|
||||||
|
<a class="lsch" href="{{ SITE_ROOT }}repo/history/changes/{{ repo.id }}/?commit_id={{ commit.id }}" data="{{ commit.props.ctime|tsstr_sec }}">详情</a>
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if ev.etype == 'repo-create' %}
|
||||||
|
{% with author=ev.creator repo_id=ev.repo_id repo_name=ev.repo_name %}
|
||||||
|
{% avatar author 24 %}<a href="{% url 'user_profile' author %}" class="name" title="{{ author|email2nickname}}">{{ author|email2nickname }}</a>
|
||||||
|
在 {{ ev.timestamp|translate_commit_time }} 创建了目录
|
||||||
|
<a href="{{SITE_ROOT}}repo/{{repo_id}}">{{ repo_name }}</a>
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if ev.etype == 'repo-delete' %}
|
||||||
|
{% with author=ev.repo_owner repo_name=ev.repo_name %}
|
||||||
|
{% avatar author 24 %}<a href="{% url 'user_profile' author %}" class="name" title="{{ author|email2nickname}}">{{ author|email2nickname }}</a>
|
||||||
|
在 {{ ev.timestamp|translate_commit_time }} 删除了目录
|
||||||
|
<span>{{ repo_name }}</span>
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<div id="ls-ch" class="hide"></div><!--list modification details of a commit-->
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
{% include 'snippets/list_commit_detail.html' %}
|
||||||
|
$('#back').click(function() {
|
||||||
|
location.href = $(this).attr('data');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@@ -29,5 +29,10 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="{{ SITE_ROOT }}contacts/" {% block nav_contacts_class %}{% endblock %}>通讯录</a>
|
<a href="{{ SITE_ROOT }}contacts/" {% block nav_contacts_class %}{% endblock %}>通讯录</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if show_events %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ SITE_ROOT }}events/">事件流</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -23,5 +23,7 @@ from service import create_org, get_orgs_by_user, get_org_by_url_prefix, \
|
|||||||
get_org_id_by_repo_id, is_org_staff, get_org_users_by_url_prefix, \
|
get_org_id_by_repo_id, is_org_staff, get_org_users_by_url_prefix, \
|
||||||
org_user_exists, list_org_repos_by_owner
|
org_user_exists, list_org_repos_by_owner
|
||||||
|
|
||||||
|
from service import get_related_users_by_repo
|
||||||
|
|
||||||
from service import CCNET_CONF_PATH, CCNET_SERVER_ADDR, CCNET_SERVER_PORT
|
from service import CCNET_CONF_PATH, CCNET_SERVER_ADDR, CCNET_SERVER_PORT
|
||||||
|
|
||||||
|
@@ -652,3 +652,27 @@ def get_file_size(file_id):
|
|||||||
except SearpcError, e:
|
except SearpcError, e:
|
||||||
fs = 0
|
fs = 0
|
||||||
return fs
|
return fs
|
||||||
|
|
||||||
|
def get_related_users_by_repo(repo_id):
|
||||||
|
"""Give a repo id, returns a list of users of:
|
||||||
|
- the repo owner
|
||||||
|
- members of groups to which the repo is shared
|
||||||
|
- users to which the repo is shared
|
||||||
|
"""
|
||||||
|
owner = seafserv_threaded_rpc.get_repo_owner(repo_id)
|
||||||
|
users = [owner]
|
||||||
|
|
||||||
|
groups = get_shared_groups_by_repo(repo_id)
|
||||||
|
for group in groups:
|
||||||
|
members = get_group_members(group.id)
|
||||||
|
for member in members:
|
||||||
|
if member.user_name not in users:
|
||||||
|
users.append(member.user_name)
|
||||||
|
|
||||||
|
share_repos = seafserv_threaded_rpc.list_share_repos(owner, 'from_email', -1, -1)
|
||||||
|
for repo in share_repos:
|
||||||
|
if repo.id == repo_id:
|
||||||
|
if repo.shared_email not in users:
|
||||||
|
users.append(repo.shared_email)
|
||||||
|
|
||||||
|
return users
|
||||||
|
1
urls.py
1
urls.py
@@ -100,6 +100,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
|
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
|
||||||
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
|
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
|
||||||
|
|
||||||
|
url(r'^events/$', show_events, name='show_events'),
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
16
utils.py
16
utils.py
@@ -13,6 +13,8 @@ from base.models import FileContributors
|
|||||||
|
|
||||||
from pysearpc import SearpcError
|
from pysearpc import SearpcError
|
||||||
|
|
||||||
|
import seafevents
|
||||||
|
|
||||||
from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \
|
from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \
|
||||||
get_repo, get_commits, get_group_repoids, CCNET_SERVER_ADDR, \
|
get_repo, get_commits, get_group_repoids, CCNET_SERVER_ADDR, \
|
||||||
CCNET_SERVER_PORT, get_org_id_by_repo_id, get_org_by_id, is_org_staff, \
|
CCNET_SERVER_PORT, get_org_id_by_repo_id, get_org_by_id, is_org_staff, \
|
||||||
@@ -189,7 +191,7 @@ def get_accessible_repos(request, repo):
|
|||||||
if stat.S_ISDIR(dirent.props.mode):
|
if stat.S_ISDIR(dirent.props.mode):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if repo.encrypted:
|
if repo and repo.encrypted:
|
||||||
repo.has_subdir = check_has_subdir(repo)
|
repo.has_subdir = check_has_subdir(repo)
|
||||||
accessible_repos = [repo]
|
accessible_repos = [repo]
|
||||||
return accessible_repos
|
return accessible_repos
|
||||||
@@ -404,3 +406,15 @@ def get_file_contributors(repo_id, file_path, file_path_hash, file_id):
|
|||||||
last_modified = fc.last_modified
|
last_modified = fc.last_modified
|
||||||
|
|
||||||
return contributors, last_modified
|
return contributors, last_modified
|
||||||
|
|
||||||
|
seafevents_session = None
|
||||||
|
def get_seafevents_session():
|
||||||
|
if not hasattr(settings, 'EVENTS_CONFIG_FILE'):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
global seafevents_session
|
||||||
|
|
||||||
|
if not seafevents_session:
|
||||||
|
seafevents_session = seafevents.init_db_session(settings.EVENTS_CONFIG_FILE)
|
||||||
|
|
||||||
|
return seafevents_session()
|
||||||
|
44
views.py
44
views.py
@@ -39,9 +39,11 @@ from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_repos, get_emailusers, \
|
|||||||
get_group, get_shared_groups_by_repo, is_group_user, check_permission, \
|
get_group, get_shared_groups_by_repo, is_group_user, check_permission, \
|
||||||
list_personal_shared_repos, is_org_group, get_org_id_by_group, is_org_repo,\
|
list_personal_shared_repos, is_org_group, get_org_id_by_group, is_org_repo,\
|
||||||
list_inner_pub_repos, get_org_groups_by_repo, is_org_repo_owner, \
|
list_inner_pub_repos, get_org_groups_by_repo, is_org_repo_owner, \
|
||||||
get_org_repo_owner, is_passwd_set, get_file_size
|
get_org_repo_owner, is_passwd_set, get_file_size, get_related_users_by_repo
|
||||||
from pysearpc import SearpcError
|
from pysearpc import SearpcError
|
||||||
|
|
||||||
|
from signals import repo_created, repo_deleted
|
||||||
|
|
||||||
from base.accounts import User
|
from base.accounts import User
|
||||||
from base.decorators import sys_staff_required, ctx_switch_required
|
from base.decorators import sys_staff_required, ctx_switch_required
|
||||||
from base.mixins import LoginRequiredMixin, CtxSwitchRequiredMixin
|
from base.mixins import LoginRequiredMixin, CtxSwitchRequiredMixin
|
||||||
@@ -72,6 +74,14 @@ except ImportError:
|
|||||||
DOCUMENT_CONVERTOR_ROOT = None
|
DOCUMENT_CONVERTOR_ROOT = None
|
||||||
from settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD
|
from settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD
|
||||||
|
|
||||||
|
try:
|
||||||
|
from settings import EVENTS_CONFIG_FILE
|
||||||
|
import seafevents
|
||||||
|
except ImportError:
|
||||||
|
EVENTS_CONFIG_FILE = None
|
||||||
|
else:
|
||||||
|
from utils import get_seafevents_session
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def root(request):
|
def root(request):
|
||||||
return HttpResponseRedirect(reverse(myhome))
|
return HttpResponseRedirect(reverse(myhome))
|
||||||
@@ -720,6 +730,10 @@ def modify_token(request, repo_id):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def remove_repo(request, repo_id):
|
def remove_repo(request, repo_id):
|
||||||
|
repo = get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
return render_error(request, u"该同步目录不存在")
|
||||||
|
|
||||||
user = request.user.username
|
user = request.user.username
|
||||||
org, base_template = check_and_get_org_by_repo(repo_id, user)
|
org, base_template = check_and_get_org_by_repo(repo_id, user)
|
||||||
if org:
|
if org:
|
||||||
@@ -735,7 +749,14 @@ def remove_repo(request, repo_id):
|
|||||||
# Remove repo in personal context, only repo owner or site staff can
|
# Remove repo in personal context, only repo owner or site staff can
|
||||||
# perform this operation.
|
# perform this operation.
|
||||||
if validate_owner(request, repo_id) or request.user.is_staff:
|
if validate_owner(request, repo_id) or request.user.is_staff:
|
||||||
|
usernames = get_related_users_by_repo(repo_id)
|
||||||
seafserv_threaded_rpc.remove_repo(repo_id)
|
seafserv_threaded_rpc.remove_repo(repo_id)
|
||||||
|
repo_deleted.send(sender=None,
|
||||||
|
usernames=usernames,
|
||||||
|
repo_owner=user,
|
||||||
|
repo_id=repo_id,
|
||||||
|
repo_name=repo.name,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
err_msg = u'删除同步目录失败, 只有管理员或目录创建者有权删除目录。'
|
err_msg = u'删除同步目录失败, 只有管理员或目录创建者有权删除目录。'
|
||||||
return render_permission_error(request, err_msg)
|
return render_permission_error(request, err_msg)
|
||||||
@@ -976,6 +997,11 @@ def public_repo_create(request):
|
|||||||
result['error'] = u"创建目录失败"
|
result['error'] = u"创建目录失败"
|
||||||
else:
|
else:
|
||||||
result['success'] = True
|
result['success'] = True
|
||||||
|
repo_created.send(sender=None,
|
||||||
|
creator=user,
|
||||||
|
repo_id=repo_id,
|
||||||
|
repo_name=repo_name)
|
||||||
|
|
||||||
return HttpResponse(json.dumps(result), content_type=content_type)
|
return HttpResponse(json.dumps(result), content_type=content_type)
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest(json.dumps(form.errors),
|
return HttpResponseBadRequest(json.dumps(form.errors),
|
||||||
@@ -2042,6 +2068,10 @@ def repo_create(request):
|
|||||||
result['error'] = u"创建目录失败"
|
result['error'] = u"创建目录失败"
|
||||||
else:
|
else:
|
||||||
result['success'] = True
|
result['success'] = True
|
||||||
|
repo_created.send(sender=None,
|
||||||
|
creator=user,
|
||||||
|
repo_id=repo_id,
|
||||||
|
repo_name=repo_name)
|
||||||
return HttpResponse(json.dumps(result), content_type=content_type)
|
return HttpResponse(json.dumps(result), content_type=content_type)
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest(json.dumps(form.errors),
|
return HttpResponseBadRequest(json.dumps(form.errors),
|
||||||
@@ -2395,4 +2425,16 @@ def use_flash_for_pdf(request):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def show_events(request):
|
||||||
|
ev_session = get_seafevents_session()
|
||||||
|
events = seafevents.get_user_events(ev_session, request.user.username, 0, 10)
|
||||||
|
ev_session.close()
|
||||||
|
for ev in events:
|
||||||
|
if ev.etype == 'repo-update':
|
||||||
|
ev.repo = get_repo(ev.repo_id)
|
||||||
|
ev.commit = seafserv_threaded_rpc.get_commit(ev.commit_id)
|
||||||
|
|
||||||
|
return render_to_response ('events.html', {
|
||||||
|
'events': events,
|
||||||
|
}, context_instance=RequestContext(request))
|
||||||
|
Reference in New Issue
Block a user