1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-18 16:36:15 +00:00

Merge pull request #818 from haiwen/org

Org system admin
This commit is contained in:
xiez
2015-09-18 13:21:12 +08:00
8 changed files with 266 additions and 37 deletions

View File

@@ -0,0 +1,28 @@
{% load seahub_tags i18n %}
<table>
<tr>
<th width="26%">{% trans "Name" %}</th>
<th width="25%">{% trans "Creator" %}</th>
<th width="17%">{% trans "Space Used" %}</th>
<th width="20%">{% trans "Created At / Expiration" %}</th>
<th width="12%">{% trans "Operations" %}</th>
</tr>
{% for org in orgs %}
<tr>
<td>
<a href="{% url 'sys_org_info_user' org.org_id %}">{{ org.org_name }}</a>
{% if org.trial_info %}
<p style="font-size:11px;">(Trial &nbsp;<a href="#" class="unset-trial" data-target="{{ org.org_name }}" data-url="{% url 'remove_trial' org.org_id %}">X</a>)</p>
{% endif %}
</td>
<td><a href="{% url 'user_info' org.creator %}">{{ org.creator }}</a></td>
<td>
{{ org.quota_usage|filesizeformat }} {% if org.total_quota > 0 %} / {{ org.total_quota|filesizeformat }} {% endif %}
</td>
<td>{{ org.ctime|tsstr_sec }} <br/>
<span class="{% if org.is_expired %}error{% endif %}">{% if org.expiration %}{{ org.expiration|date:'Y-m-d H:i:s' }}{% else %}--{% endif %}</span>
</td>
<td><a href="#" data-url="{% url "sys_org_remove" org.org_id %}" data-target="{{ org.org_name }}" class="remove-btn op vh">{% trans "Delete" %}</a></td>
</tr>
{% endfor %}
</table>

View File

@@ -1,11 +1,21 @@
{% extends "sysadmin/base.html" %}
{% load seahub_tags i18n %}
{% block cur_org %}tab-cur{% endblock %}
{% block left_panel %}{{block.super}}
<form action="{% url 'sys_org_search' %}" method="get" class="side-search-form">
<input type="text" name="name" class="input" value="" placeholder="{% trans "Search orgs by name..." %}" />
</form>
{% endblock %}
{% block right_panel %}
<div class="hd ovhd">
<h3 class="fleft">{% trans "All Organizations" %}</h3>
<div class="tabnav">
<ul class="tabnav-tabs">
<li class="tabnav-tab {% if all_page %}tabnav-tab-cur{% endif %}"><a href="{% url 'sys_org_admin' %}">{% trans "All" %}</a></li>
{% if enable_org_plan %}
<li class="tabnav-tab {% if paid_page %}tabnav-tab-cur{% endif %}"><a href="{% url 'sys_org_admin' %}?filter=paid">{% trans "Paid" %}</a></li>
{% endif %}
</ul>
<button id="add-btn" class="fright">{% trans "Add organization" %}</button>
</div>
@@ -29,38 +39,16 @@
</form>
{% if orgs %}
<table>
<tr>
<th width="20%">{% trans "Name" %}</th>
<th width="18%">{% trans "Url Prefix" %}</th>
<th width="25%">{% trans "Creator" %}</th>
<th width="17%">{% trans "Space Used" %}</th>
<th width="20%">{% trans "Created At" %}</th>
</tr>
{% for org in orgs %}
<tr>
<td>
<a href="{% url 'sys_org_info_user' org.org_id %}">{{ org.org_name }}</a>
{% if org.trial_info %}
<p style="font-size:11px;">(Trial &nbsp;<a href="#" class="unset-trial" data-target="{{ org.org_name }}" data-url="{% url 'remove_trial' org.org_id %}">X</a>)</p>
{% endif %}
</td>
<td>{{ org.url_prefix }}</td>
<td><a href="{% url 'user_info' org.creator %}">{{ org.creator }}</a></td>
<td>
{{ org.quota_usage|filesizeformat }} {% if org.total_quota > 0 %} / {{ org.total_quota|filesizeformat }} {% endif %}
</td>
<td>{{ org.ctime|tsstr_sec }}</td>
<!-- <td><a href="#" data-url="{{ SITE_ROOT}}org/remove/{{ org.org_id }}/" data-target="{{ org.org_name }}" class="remove-btn op vh">{% trans "Delete" %}</a></td> -->
</tr>
{% endfor %}
</table>
{% include "snippets/admin_paginator.html" %}
{% include "sysadmin/org_admin_table.html" %}
{% if not hide_paginator %}
{% include "snippets/admin_paginator.html" %}
{% endif %}
{% else %}
<p>{% trans "None." %}</p>
{% endif %}
{% endblock %}
{% block extra_script %}
@@ -70,6 +58,12 @@ $('#add-btn').click(function() {
$('#simplemodal-container').css({'width':'auto'});
});
addConfirmTo($('.remove-btn'), {
'title':"{% trans "Delete org" %}",
'con':"{% trans "Are you sure you want to delete %s ?" %}",
'post': true // post request
});
$('#add-org-form').submit(function() {
var form = $(this),
form_id = form.attr('id'),

View File

@@ -0,0 +1,33 @@
{% extends "sysadmin/base.html" %}
{% load seahub_tags i18n staticfiles %}
{% block cur_org %}tab-cur{% endblock %}
{% block right_panel %}
<h3>{% trans "Search Org"%}</h3>
<form id="search-repo-form" method="get" action="{% url "sys_org_search" %}">
<p class="tip">{% trans "Tip: you can search by keyword in name or creator or both." %}</p>
<label>{% trans "Name" %}</label><input type="text" name="name" class="input" value="{{name}}"/><br />
<label>{% trans "Creator" %}</label><input type="text" name="creator" class="input" value="{{creator}}"/><br />
<input type="submit" value="{% trans "Submit" %}" class="submit" />
</form>
<h3>{% trans "Result"%}</h3>
{% if orgs %}
{% include "sysadmin/org_admin_table.html" %}
{% else %}
<p>{% trans "No result" %}</p>
{% endif %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.remove-btn'), {
'title':"{% trans "Delete org" %}",
'con':"{% trans "Are you sure you want to delete %s ?" %}",
'post': true // post request
});
</script>
{% endblock %}

View File

@@ -17,6 +17,9 @@
<li class="tabnav-tab"><a href="{% url 'sys_useradmin_ldap_imported' %}">{% trans "LDAP(imported)" %}</a></li>
{% endif %}
<li class="tabnav-tab"><a href="{% url 'sys_useradmin_admins' %}">{% trans "Admins" %}</a></li>
{% if enable_user_plan %}
<li class="tabnav-tab"><a href="{% url 'sys_useradmin' %}?filter=paid">{% trans "Paid" %}</a></li>
{% endif %}
</ul>
<div class="fright">
<button id="import-users-btn"><span class="icon-upload-alt"></span>{% trans "Import users" %}</button>

View File

@@ -0,0 +1,39 @@
{% extends "sysadmin/base.html" %}
{% load seahub_tags i18n %}
{% block cur_users %}tab-cur{% endblock %}
{% block left_panel %}{{block.super}}
<form action="{% url 'user_search' %}" method="get" class="side-search-form">
<input type="text" name="email" class="input" value="" placeholder="{% trans "Search users..." %}" />
</form>
{% endblock %}
{% block right_panel %}
<div class="tabnav ovhd">
<ul class="tabnav-tabs fleft">
<li class="tabnav-tab"><a href="{% url 'sys_useradmin' %}">{% trans "Database" %}</a></li>
{% if have_ldap %}
<li class="tabnav-tab"><a href="{% url 'sys_useradmin_ldap' %}">{% trans "LDAP" %}</a></li>
<li class="tabnav-tab"><a href="{% url 'sys_useradmin_ldap_imported' %}">{% trans "LDAP(imported)" %}</a></li>
{% endif %}
<li class="tabnav-tab"><a href="{% url 'sys_useradmin_admins' %}">{% trans "Admins" %}</a></li>
{% if enable_user_plan %}
<li class="tabnav-tab tabnav-tab-cur"><a href="{% url 'sys_useradmin' %}?filter=paid">{% trans "Paid" %}</a></li>
{% endif %}
</ul>
</div>
{% with is_admin_page=False%}
{% include "sysadmin/useradmin_table.html" %}
{% endwith %}
<div id="activate-msg" class="hide">
<p>{% trans "Activating..., please wait" %}</p>
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
{% include "sysadmin/useradmin_js.html" %}
</script>
{% endblock %}

View File

@@ -79,12 +79,12 @@
</td>
<td>
{% if not user.is_self %}
<a href="#" class="remove-user-btn op vh" data-url="{% url 'user_remove' user.email %}" data-target="{{ user.props.email }}">{% trans "Delete" %}</a>
<a href="#" class="remove-user-btn op vh" data-url="{% url 'user_remove' user.email %}" data-target="{{ user.email }}">{% trans "Delete" %}</a>
{% if user.source == "DB" %}
<a href="#" class="reset-user-btn op vh" data-url="{% url 'user_reset' user.email %}" data-target="{{ user.props.email }}">{% trans "ResetPwd" %}</a>
<a href="#" class="reset-user-btn op vh" data-url="{% url 'user_reset' user.email %}" data-target="{{ user.email }}">{% trans "ResetPwd" %}</a>
{% endif %}
{% if is_admin_page %}
<a href="#" data-url="{% url 'user_remove_admin' user.email %}" data-target="{{ user.props.email }}" class="revoke-admin-btn op vh">{% trans "Revoke Admin" %}</a>
<a href="#" data-url="{% url 'user_remove_admin' user.email %}" data-target="{{ user.email }}" class="revoke-admin-btn op vh">{% trans "Revoke Admin" %}</a>
{% endif %}
{% endif %}
</td>

View File

@@ -220,8 +220,10 @@ urlpatterns = patterns(
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
url(r'^sys/groupadmin/(?P<group_id>\d+)/$', sys_admin_group_info, name='sys_admin_group_info'),
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
url(r'^sys/orgadmin/search/$', sys_org_search, name='sys_org_search'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/set_quota/$', sys_org_set_quota, name='sys_org_set_quota'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/rename/$', sys_org_rename, name='sys_org_rename'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/remove/$', sys_org_remove, name='sys_org_remove'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/set_member_quota/$', sys_org_set_member_quota, name='sys_org_set_member_quota'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/user/$', sys_org_info_user, name='sys_org_info_user'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/group/$', sys_org_info_group, name='sys_org_info_group'),

View File

@@ -13,6 +13,7 @@ from django.contrib import messages
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseNotAllowed
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils import timezone
from django.utils.translation import ugettext as _
from seaserv import ccnet_threaded_rpc, seafserv_threaded_rpc, get_emailusers, \
@@ -361,6 +362,33 @@ def _populate_user_quota_usage(user):
def sys_user_admin(request):
"""List all users from database.
"""
try:
from seahub_extra.plan.models import UserPlan
enable_user_plan = True
except ImportError:
enable_user_plan = False
if enable_user_plan and request.GET.get('filter', '') == 'paid':
# show paid users
users = []
ups = UserPlan.objects.all()
for up in ups:
u = User.objects.get(up.username)
_populate_user_quota_usage(u)
users.append(u)
last_logins = UserLastLogin.objects.filter(username__in=[x.username for x in users])
for u in users:
for e in last_logins:
if e.username == u.username:
u.last_login = e.last_login
return render_to_response('sysadmin/sys_useradmin_paid.html', {
'users': users,
'enable_user_plan': enable_user_plan,
}, context_instance=RequestContext(request))
### List all users
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
@@ -425,6 +453,7 @@ def sys_user_admin(request):
'guest_user': GUEST_USER,
'is_pro': is_pro_version(),
'pro_server': pro_server,
'enable_user_plan': enable_user_plan,
}, context_instance=RequestContext(request))
@login_required
@@ -1147,27 +1176,64 @@ def sys_org_admin(request):
current_page = 1
per_page = 25
try:
from seahub_extra.plan.models import OrgPlan
enable_org_plan = True
except ImportError:
enable_org_plan = False
if enable_org_plan and request.GET.get('filter', '') == 'paid':
orgs = []
ops = OrgPlan.objects.all()
for e in ops:
o = ccnet_threaded_rpc.get_org_by_id(e.org_id)
o.quota_usage = seafserv_threaded_rpc.get_org_quota_usage(o.org_id)
o.total_quota = seafserv_threaded_rpc.get_org_quota(o.org_id)
o.expiration = e.expire_date
o.is_expired = True if e.expire_date < timezone.now() else False
orgs.append(o)
return render_to_response('sysadmin/sys_org_admin.html', {
'orgs': orgs,
'enable_org_plan': enable_org_plan,
'hide_paginator': True,
'paid_page': True,
}, context_instance=RequestContext(request))
orgs_plus_one = ccnet_threaded_rpc.get_all_orgs(per_page * (current_page - 1),
per_page + 1)
if len(orgs_plus_one) == per_page + 1:
page_next = True
else:
page_next = False
orgs = orgs_plus_one[:per_page]
if ENABLE_TRIAL_ACCOUNT:
trial_orgs = TrialAccount.objects.filter(user_or_org__in=[x.org_id for x in orgs])
else:
trial_orgs = []
for org in orgs:
org.quota_usage = seafserv_threaded_rpc.get_org_quota_usage(org.org_id)
org.total_quota = seafserv_threaded_rpc.get_org_quota(org.org_id)
from seahub_extra.organizations.settings import ORG_TRIAL_DAYS
if ORG_TRIAL_DAYS > 0:
from datetime import timedelta
org.expiration = datetime.datetime.fromtimestamp(org.ctime / 1e6) + timedelta(days=ORG_TRIAL_DAYS)
org.trial_info = None
for trial_org in trial_orgs:
if trial_org.user_or_org == str(org.org_id):
org.trial_info = {'expire_date': trial_org.expire_date}
if trial_org.expire_date:
org.expiration = trial_org.expire_date
if len(orgs_plus_one) == per_page + 1:
page_next = True
else:
page_next = False
if org.expiration:
org.is_expired = True if org.expiration < timezone.now() else False
else:
org.is_expired = False
return render_to_response('sysadmin/sys_org_admin.html', {
'orgs': orgs,
@@ -1176,8 +1242,43 @@ def sys_org_admin(request):
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
'enable_org_plan': enable_org_plan,
'all_page': True,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_org_search(request):
org_name = request.GET.get('name', '').lower()
creator = request.GET.get('creator', '').lower()
if not org_name and not creator:
return HttpResponseRedirect(reverse('sys_org_admin'))
orgs = []
orgs_all = ccnet_threaded_rpc.get_all_orgs(-1, -1)
if org_name and creator:
for o in orgs_all:
if org_name in o.org_name.lower() and creator in o.creator.lower():
orgs.append(o)
else:
if org_name:
for o in orgs_all:
if org_name in o.org_name.lower():
orgs.append(o)
if creator:
for o in orgs_all:
if creator in o.creator.lower():
orgs.append(o)
return render_to_response(
'sysadmin/sys_org_search.html', {
'orgs': orgs,
'name': org_name,
'creator': creator,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_org_rename(request, org_id):
@@ -1199,6 +1300,35 @@ def sys_org_rename(request, org_id):
return HttpResponseRedirect(next)
@login_required
@require_POST
@sys_staff_required
def sys_org_remove(request, org_id):
"""Remove an org and all members/repos/groups.
Arguments:
- `request`:
- `org_id`:
"""
org_id = int(org_id)
org = ccnet_threaded_rpc.get_org_by_id(org_id)
users = ccnet_threaded_rpc.get_org_emailusers(org.url_prefix, -1, -1)
for u in users:
ccnet_threaded_rpc.remove_org_user(org_id, u.email)
groups = ccnet_threaded_rpc.get_org_groups(org.org_id, -1, -1)
for g in groups:
ccnet_threaded_rpc.remove_org_group(org_id, g.gid)
# remove org repos
seafserv_threaded_rpc.remove_org_repo_by_org_id(org_id)
# remove org
ccnet_threaded_rpc.remove_org(org_id)
messages.success(request, _(u'Successfully deleted.'))
return HttpResponseRedirect(reverse('sys_org_admin'))
@login_required_ajax
@sys_staff_required
def sys_org_set_member_quota(request, org_id):