1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-31 06:40:39 +00:00

Add org account feature

This commit is contained in:
xiez 2012-06-20 19:39:21 +08:00
parent 5b96dc2ab2
commit 9c4a54931f
36 changed files with 1039 additions and 85 deletions

View File

@ -9,7 +9,7 @@ from django.contrib.sites.models import Site
from auth.models import get_hexdigest, check_password
from auth import authenticate, login
from registration import signals
from registration.forms import RegistrationForm
#from registration.forms import RegistrationForm
from registration.models import RegistrationProfile
from seaserv import ccnet_rpc, get_ccnetuser
@ -29,13 +29,15 @@ def convert_to_ccnetuser(emailuser):
ccnetuser.is_staff = emailuser.props.is_staff
ccnetuser.is_active = emailuser.props.is_active
ccnetuser.ctime = emailuser.props.ctime
ccnetuser.org = emailuser.org
return ccnetuser
class CcnetUser(object):
is_staff = False
is_active = False
objects = UserManager()
org = None
def __init__(self, username, raw_password):
self.username = username
@ -225,12 +227,6 @@ class RegistrationBackend(object):
# login the user
activated.backend='auth.backends.ModelBackend'
login(request, activated)
# TODO: user.user_id should be change
try:
if request.user.user_id:
ccnet_rpc.add_client(ccnet_user_id)
except:
pass
return activated
@ -323,3 +319,146 @@ class RegistrationForm(forms.Form):
raise forms.ValidationError(_("The two password fields didn't match."))
return self.cleaned_data
class OrgRegistrationForm(RegistrationForm):
"""
Form for registering a business user account.
Validates that the requested email is not already in use, and
requires the password to be entered twice to catch typos.
"""
org_name = forms.CharField(max_length=256,
widget=forms.TextInput(),
label=_("Organization Name"))
url_prefix = forms.RegexField(label=_("Url Prefix"), max_length=20,
regex=r'^[a-z0-9]+$',
error_message=_("This value must contain only letters or numbers."))
def clean_url_prefix(self):
url_prefix = self.cleaned_data['url_prefix']
org = ccnet_rpc.get_org_by_url_prefix(url_prefix)
if not org:
return url_prefix
else:
raise forms.ValidationError(_("A organization with this url prefix already"))
class OrgRegistrationBackend(object):
def register(self, request, **kwargs):
"""
Given a username, email address and password, register a new
user account, which will initially be inactive.
Along with the new ``User`` object, a new
``registration.models.RegistrationProfile`` will be created,
tied to that ``User``, containing the activation key which
will be used for this account.
An email will be sent to the supplied email address; this
email should contain an activation link. The email will be
rendered using two templates. See the documentation for
``RegistrationProfile.send_activation_email()`` for
information about these templates and the contexts provided to
them.
After the ``User`` and ``RegistrationProfile`` are created and
the activation email is sent, the signal
``registration.signals.user_registered`` will be sent, with
the new ``User`` as the keyword argument ``user`` and the
class of this backend as the sender.
"""
email, password = kwargs['email'], kwargs['password1']
username = email
org_name, url_prefix = kwargs['org_name'], kwargs['url_prefix']
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
if settings.ACTIVATE_AFTER_REGISTRATION:
# since user will be activated after registration,
# so we will not use email sending, just create acitvated user
new_user = RegistrationProfile.objects.create_active_user(username, email,
password, site,
send_email=False)
# create orgnization account
try:
ccnet_rpc.create_org(org_name, url_prefix, username)
except SearpcError, e:
pass
else:
# login the user
new_user.backend='auth.backends.ModelBackend'
login(request, new_user)
else:
# create inactive user, user can be activated by admin, or through activated email
new_user = RegistrationProfile.objects.create_inactive_user(username, email,
password, site,
send_email=settings.REGISTRATION_SEND_MAIL)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=request)
return new_user
def activate(self, request, activation_key):
"""
Given an an activation key, look up and activate the user
account corresponding to that key (if possible).
After successful activation, the signal
``registration.signals.user_activated`` will be sent, with the
newly activated ``User`` as the keyword argument ``user`` and
the class of this backend as the sender.
"""
activated = RegistrationProfile.objects.activate_user(activation_key)
if activated:
signals.user_activated.send(sender=self.__class__,
user=activated,
request=request)
# login the user
activated.backend='auth.backends.ModelBackend'
login(request, activated)
return activated
def registration_allowed(self, request):
"""
Indicate whether account registration is currently permitted,
based on the value of the setting ``REGISTRATION_OPEN``. This
is determined as follows:
* If ``REGISTRATION_OPEN`` is not specified in settings, or is
set to ``True``, registration is permitted.
* If ``REGISTRATION_OPEN`` is both specified and set to
``False``, registration is not permitted.
"""
return getattr(settings, 'REGISTRATION_OPEN', True)
def get_form_class(self, request):
"""
Return the default form class used for user registration.
"""
return RegistrationForm
def post_registration_redirect(self, request, user):
"""
Return the name of the URL to redirect to after successful
user registration.
"""
return ('registration_complete', (), {})
def post_activation_redirect(self, request, user):
"""
Return the name of the URL to redirect to after successful
account activation.
"""
return ('myhome', (), {})

View File

@ -15,5 +15,6 @@ def base(request):
"""
return {
'seafile_version': settings.SEAFILE_VERSION,
'seahub_title': settings.SEAHUB_TITLE,
'seahub_title': settings.SEAHUB_TITLE,
'account_type': settings.ACCOUNT_TYPE,
}

View File

@ -47,4 +47,3 @@ class InfobarMiddleware(object):
def process_response(self, request, response):
return response

View File

@ -6,13 +6,20 @@ from registration.views import activate
from registration.views import register
from seahub.base.accounts import RegistrationForm
from seahub.base.accounts import OrgRegistrationForm
reg_dict = { 'backend': 'seahub.base.accounts.RegistrationBackend',
'form_class': RegistrationForm,
}
org_reg_dict = { 'backend': 'seahub.base.accounts.OrgRegistrationBackend',
'form_class': OrgRegistrationForm,
'template_name': 'registration/org_registration_form.html',
}
if settings.ACTIVATE_AFTER_REGISTRATION == True:
reg_dict['success_url'] = settings.SITE_ROOT
org_reg_dict['success_url'] = settings.SITE_ROOT
urlpatterns = patterns('',
url(r'^activate/complete/$',
@ -40,5 +47,11 @@ urlpatterns = patterns('',
direct_to_template,
{ 'template': 'registration/registration_closed.html' },
name='registration_disallowed'),
url(r'^business/register/$',
register,
org_reg_dict,
name='registration_register'),
(r'', include('registration.auth_urls')),
)

View File

@ -8,7 +8,7 @@
<form action="" method="post">
{{ form.user_email.as_hidden }}
<label>邮箱:</label>
{{ form.contact_email }}
<input id="id_contact_email" type="text" maxlength="255" value="{{ old_contact_email }}" name="contact_email" readonly="readonly">
<label>名字(可选)</label>
{{ form.contact_name }}
<label>备注(可选)</label>

View File

@ -35,8 +35,12 @@ def contact_add(request):
elif contact_email == request.user.username:
error_msg = u"不能添加自己为联系人"
elif Contact.objects.filter(user_email=request.user.username,
contact_email=contact_email).count() > 0:
contact_email=contact_email).count() > 0:
error_msg = u"联系人列表中已有该用户"
elif request.user.org and \
not ccnet_rpc.org_user_exists(request.user.org.org_id,
contact_email):
error_msg = u"当前企业不存在该用户"
else:
contact = Contact()
contact.user_email = request.user.username
@ -69,9 +73,9 @@ def contact_edit(request):
emailuser = ccnet_rpc.get_emailuser(contact_email)
if not emailuser:
error_msg = u"用户不存在"
elif cmp(contact_email, request.user.username) == 0:
elif contact_email == request.user.username:
error_msg = u"不能添加自己为联系人"
elif cmp(old_contact_email, contact_email) != 0 and \
elif old_contact_email != contact_email and \
Contact.objects.filter(user_email=request.user.username,
contact_email=contact_email).count() > 0:
error_msg = u"联系人列表中已有该用户"

View File

@ -25,8 +25,12 @@ def group_list(request):
return go_error(request, u'小组名称只能包含中英文字符,数字及下划线')
try:
ccnet_rpc.create_group(group_name.encode('utf-8'),
group_id = ccnet_rpc.create_group(group_name.encode('utf-8'),
request.user.username)
# TODO: transaction?
if request.user.org and group_id > 0:
ccnet_rpc.add_org_group(request.user.org.org_id,
group_id)
except SearpcError, e:
error_msg = e.msg
return go_error(request, error_msg)
@ -62,11 +66,17 @@ def group_remove(request, group_id):
try:
ccnet_rpc.remove_group(group_id_int, request.user.username)
seafserv_threaded_rpc.remove_repo_group(group_id_int, None)
if request.user.org:
ccnet_rpc.remove_org_group(request.user.org.org_id,
group_id_int)
except SearpcError, e:
return go_error(request, e.msg)
if request.GET.get('src', '') == 'groupadmin':
return HttpResponseRedirect(reverse('group_admin'))
if request.GET.get('src', '') == 'orggroupadmin':
return HttpResponseRedirect(reverse('org_group_admin'))
elif request.GET.get('src', '') == 'sysgroupadmin':
return HttpResponseRedirect(reverse('sys_group_admin'))
else:
return HttpResponseRedirect(reverse('group_list', args=[]))
@ -178,17 +188,31 @@ def group_members(request, group_id):
continue
member_name_dict[member_name] = member_name
for member_name in member_name_dict.keys():
if not validate_emailuser(member_name):
err_msg = u'用户 %s 不存在' % member_name
return go_error(request, err_msg)
else:
try:
ccnet_rpc.group_add_member(group_id_int,
request.user.username,
member_name)
except SearpcError, e:
return go_error(request, e.msg)
if request.user.org:
for member_name in member_name_dict.keys():
if not ccnet_rpc.org_user_exists(request.user.org.org_id,
member_name):
err_msg = u'当前企业不存在 %s 用户' % member_name
return go_error(request, err_msg)
else:
try:
ccnet_rpc.group_add_member(group_id_int,
request.user.username,
member_name)
except SearpcError, e:
return go_error(request, e.msg)
else:
for member_name in member_name_dict.keys():
if not validate_emailuser(member_name):
err_msg = u'用户 %s 不存在' % member_name
return go_error(request, err_msg)
else:
try:
ccnet_rpc.group_add_member(group_id_int,
request.user.username,
member_name)
except SearpcError, e:
return go_error(request, e.msg)
members = ccnet_rpc.get_group_members(group_id_int)
contacts = Contact.objects.filter(user_email=request.user.username)

View File

@ -365,3 +365,14 @@ h2.repo-history {
/* notification admin */
.cur-note { color: red; font-size: 75%; }
/* org */
.org-member .avatar {
border-radius: 2px;
-moz-border-radius: 2px;
margin-right: 5px;
}
.org-member .avatar,
.org-member-name {
vertical-align:middle;
}

View File

@ -68,6 +68,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'auth.middleware.AuthenticationMiddleware',
'seahub.base.middleware.InfobarMiddleware',
'seahub.subdomain.middleware.SubdomainMiddleware',
# 'seahub.base.middleware.UseridMiddleware',
)
@ -110,6 +111,7 @@ INSTALLED_APPS = (
'seahub.contacts',
'seahub.group',
'seahub.share',
'seahub.subdomain',
)
AUTHENTICATION_BACKENDS = (
@ -160,6 +162,13 @@ FILEEXT_ICON_MAP = {
'default' : 'file-icon-24.png',
}
SITE_SUBDOMAIN = 'cloud'
SITE_BASE_NAME = 'seafile.com.cn' if not DEBUG else 'localhost.localdomain'
SESSION_COOKIE_DOMAIN = '.' + SITE_BASE_NAME
# account type is `personal` or `business`
ACCOUNT_TYPE = 'personal'
try:
import local_settings
except ImportError:

View File

@ -1,5 +1,5 @@
{% autoescape off %}
亲爱的{{ anon_email }} 您好!
亲爱的 {{ anon_email }}
{{ email }} 在SeaCloud上共享了一个同步目录给你请点击以下链接查看

0
subdomain/__init__.py Normal file
View File

37
subdomain/middleware.py Normal file
View File

@ -0,0 +1,37 @@
from django.http import HttpResponseRedirect, Http404
from seahub.settings import SITE_BASE_NAME, SITE_SUBDOMAIN
class SubdomainMiddleware(object):
def process_request(self, request):
if not request.user.is_authenticated():
return None
host = request.META.get('HTTP_HOST', '')
http_or_https = request.is_secure() and 'https://' or 'http://'
has_subdomain = True if host.replace(SITE_BASE_NAME, '', 1).find('.') >= 0 else False
full_path = request.get_full_path()
if request.user.org:
# business account
url_prefix = request.user.org.url_prefix
if not has_subdomain:
host = request.user.org.url_prefix + '.' + host
return HttpResponseRedirect(http_or_https + host + full_path)
elif host.split('.')[0] != url_prefix:
host = url_prefix + '.' + '.'.join(host.split('.')[1:])
return HttpResponseRedirect(http_or_https + host + full_path)
else:
# personal account
if not has_subdomain:
host = SITE_SUBDOMAIN + '.' + host
return HttpResponseRedirect(http_or_https + host + full_path)
elif host.split('.')[0] != SITE_SUBDOMAIN:
host = SITE_SUBDOMAIN + '.' + '.'.join(host.split('.')[1:])
return HttpResponseRedirect(http_or_https + host + full_path)
return None
def process_response(self, request, response):
return response

3
subdomain/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

23
subdomain/tests.py Normal file
View 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
"""}

1
subdomain/views.py Normal file
View File

@ -0,0 +1 @@
# Create your views here.

View File

@ -1,4 +1,4 @@
{% extends "admin_base.html" %}
{% extends base_template %}
{% block title %}添加用户{% endblock %}
{% block nav_useradmin_class %}class="cur"{% endblock %}
{% block main_panel %}
@ -15,9 +15,9 @@
{{ form.email.errors }}
{{ form.password1.errors }}
{{ form.password2.errors }}
<p class="error hide" id="error"></p>
<p class="error hide" id="error"></p>
<input type="submit" value="提交" class="submit" />
<input type="submit" value="添加" class="submit" />
</form>
</div>
{% endblock %}

View File

@ -4,16 +4,19 @@
<ul class="nav w100 ovhd">
{% if request.user.is_staff %}
<li>
<a href="{{ SITE_ROOT }}seafadmin/" {% block nav_seafadmin_class %}{% endblock %}>目录管理</a>
<a href="{{ SITE_ROOT }}sys/seafadmin/" {% block nav_seafadmin_class %}{% endblock %}>目录管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}useradmin/" {% block nav_useradmin_class %}{% endblock %}>用户管理</a>
<a href="{{ SITE_ROOT }}sys/useradmin/" {% block nav_useradmin_class %}{% endblock %}>用户管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}groupadmin/" {% block nav_groupadmin_class %}{% endblock %}>小组管理</a>
<a href="{{ SITE_ROOT }}sys/groupadmin/" {% block nav_groupadmin_class %}{% endblock %}>小组管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}notificationadmin/" {% block nav_notificationadmin_class %}{% endblock %}>通知管理</a>
<a href="{{ SITE_ROOT }}sys/orgadmin/" {% block nav_orgadmin_class %}{% endblock %}>企业管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}sys/notificationadmin/" {% block nav_notificationadmin_class %}{% endblock %}>通知管理</a>
</li>
{% endif %}
</ul>

View File

@ -25,10 +25,15 @@
<div id="top-bar">
<div class="top-bar">
<div class="top-bar-in w100 ovhd">
{% if request.user.is_staff %}
{% if request.user.is_staff or request.user.org.is_staff %}
<div class="fleft">
<a href="{{ SITE_ROOT }}seafadmin/"{% block top_bar_manager_class %}{% endblock %}>管理员控制台</a>
<a href="{{ SITE_ROOT }}home/my/"{% block top_bar_myaccount_class %}{% endblock %}>我的帐号</a>
{% if request.user.is_staff %}
<a href="{{ SITE_ROOT }}sys/seafadmin/"
{% else %}
<a href="{{ SITE_ROOT }}seafadmin/"
{% endif %}
{% block top_bar_manager_class %}{% endblock %}>管理员控制台</a>
<a href="{{ SITE_ROOT }}home/my/"{% block top_bar_myaccount_class %}{% endblock %}>我的帐号</a>
</div>
{% endif %}
@ -43,9 +48,13 @@
-->
<a href="{{ SITE_ROOT }}accounts/logout/">退出</a>
{% else %}
<a href="{{ SITE_ROOT }}accounts/login/">登录</a>
<a href="{{ SITE_ROOT }}accounts/login/">登录</a>
{% if account_type == 'business' %}
<a href="{{ SITE_ROOT }}accounts/business/register/">注册</a>
{% else %}
<a href="{{ SITE_ROOT }}accounts/register/">注册</a>
{% endif %}
{% endif %}
</div>
</div>
</div>

View File

@ -4,6 +4,11 @@
{% block nav_myhome_class %}class="cur"{% endblock %}
{% block left_panel %}
{% if request.user.org %}
<h3>所属企业</h3>
<p>{{ request.user.org.org_name }}</p>
{% endif %}
<h3>已用空间</h3>
<p>{{ quota_usage|filesizeformat }} / 2 GB</p>

View File

@ -5,6 +5,11 @@
<li>
<a href="{{ SITE_ROOT }}home/my/" {% block nav_myhome_class %}{% endblock %}>我的页面</a>
</li>
{% if request.user.org %}
<li>
<a href="{{ SITE_ROOT }}org/" {% block nav_org_class %}{% endblock %}>企业</a>
</li>
{% endif %}
<li>
<a href="{{ SITE_ROOT }}group/" {% block nav_group_class %}{% endblock %}>小组</a>
</li>

View File

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% block top_bar_manager_class %} class="cur"{% endblock %}
{% block nav %}
<ul class="nav w100 ovhd">
{% if request.user.org.is_staff %}
<li>
<a href="{{ SITE_ROOT }}seafadmin/" {% block nav_seafadmin_class %}{% endblock %}>目录管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}useradmin/" {% block nav_useradmin_class %}{% endblock %}>用户管理</a>
</li>
<li>
<a href="{{ SITE_ROOT }}groupadmin/" {% block nav_groupadmin_class %}{% endblock %}>小组管理</a>
</li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,60 @@
{% extends "org_admin_base.html" %}
{% load seahub_tags %}
{% block nav_groupadmin_class %}class="cur"{% endblock %}
{% block right_panel %}
<h3>所有小组</h3>
{% if groups %}
<table>
<tr>
<th width="30%">名字</th>
<th width="40%">创建者</th>
<th width="23%">创建时间</th>
<th width="7%">操作</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>
<td><button data="{{ SITE_ROOT}}group/{{ group.id }}/?op=delete&src=orggroupadmin" class="group-remove-btn">删除</button></td>
</tr>
{% endfor %}
</table>
<div id="paginator">
{% if current_page != 1 %}
<a href="{{ SITE_ROOT }}groupadmin/?page={{ prev_page }}&per_page={{ per_page }}">上一页</a>
{% endif %}
{% if page_next %}
<a href="{{ SITE_ROOT }}groupadmin/?page={{ next_page }}&per_page={{ per_page }}">下一页</a>
{% endif %}
<span>每页:</span>
{% if per_page == 25 %}
<span> 25 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=25" class="per-page">25</a>
{% endif %}
{% if per_page == 50 %}
<span> 50 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=50" class="per-page">50</a>
{% endif %}
{% if per_page == 100 %}
<span> 100 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=100" class="per-page">100</a>
{% endif %}
</div>
{% else %}
<p>暂无</p>
{% endif %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.group-remove-btn'), '确定要删除小组?')
</script>
{% endblock %}

103
templates/org_info.html Normal file
View File

@ -0,0 +1,103 @@
{% extends "myhome_base.html" %}
{% load seahub_tags avatar_tags %}
{% block nav_org_class %}class="cur"{% endblock %}
{% block main_panel %}
<h2>{{ org.org_name }}</h2>
<div class="side fright">
<h3>企业成员</h3>
{% if org_users %}
<ul>
{% for member in org_users %}
<li class="org-member">{% avatar member.user_name 16 %}<span class="org-member-name">{{ member.short_username }}</span></li>
{% endfor %}
</ul>
{% else %}
<p>暂无</p>
{% endif %}
{% if is_join %}
<h3>操作</h3>
<ul class="with-bg">
{% if is_staff %}
<li><a id="group-mgr" href="{{ SITE_ROOT }}group/{{ group.id }}/members/">小组管理</a></li>
{% else %}
<li><a id="quit-group" href="#" data="{{ SITE_ROOT }}group/{{ group.id }}/?op=quit">退出小组</a></li>
{% endif %}
</ul>
{% endif %}
</div>
<div class="main fleft">
<h3>企业里公开的同步目录</h3>
{% if repos %}
<table>
<tr>
<th width="20%">名字</th>
<th width="48%">描述</th>
<th width="22%">共享来源</th>
<th width="10%">操作</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>
<img src="{{ MEDIA_URL }}img/download-20.png" class="download vh" data="{{ SITE_ROOT }}seafile_access_check/?repo_id={{ repo.props.id }}" alt="下载" title="下载" />
{% if is_staff or repo.share_from_me %}
<img src="{{ MEDIA_URL }}img/delete-20.png" class="cancel-share vh" data="{{ SITE_ROOT }}shareadmin/removeshare/?repo_id={{ repo.props.id }}&from={{ repo.share_from }}&gid={{ group.id }}" title="取消共享" alt="取消共享" />
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>暂无</p>
{% endif %}
<h3>企业里的小组</h3>
{% if groups %}
<table>
<tr>
<th width="30%">名字</th>
<th widht="40%">创建者</th>
<th width="30%">创建时间</th>
</tr>
{% for group in groups %}
<tr>
<td>{{ group.group_name }}</td>
<td>{{ group.creator_name }}</td>
<td>{{ group.timestamp|tsstr_sec }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>暂无</p>
{% endif %}
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('#quit-group'), '确定要退出?');
addConfirmTo($('.cancel-share'), '确定要取消共享该目录?');
$("table tr:gt(0)").hover(
function() {
$(this).find('img').css('cursor', 'pointer').removeClass('vh');
},
function() {
$(this).find('img').addClass('vh');
}
);
$('.download').click(function() {
window.open($(this).attr('data'));
});
</script>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends "org_admin_base.html" %}
{% block nav_seafadmin_class %}class="cur"{% endblock %}
{% block right_panel %}
<h3>所有同步目录</h3>
{% if repos %}
<table>
<tr>
<th width="25%">名字</th>
<th width="28%">拥有者</th>
<th width="40%">描述</th>
<th width="7%">操作</th>
</tr>
{% for repo in repos %}
<tr>
<td><a href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/">{{ repo.props.name }}</a></td>
<td><a href="{{ SITE_ROOT }}useradmin/info/{{ repo.owner }}/">{{ repo.owner}}</a></td>
<td>{{ repo.props.desc }}</td>
<td><button data="{{ SITE_ROOT }}repo/remove/{{ repo.props.id }}/?next={{ request.path }}" class="repo-delete-btn">删除</button></td>
</tr>
{% endfor %}
</table>
<div id="paginator">
{% if current_page != 1 %}
<a href="{{ SITE_ROOT }}seafadmin/?page={{ prev_page }}&per_page={{ per_page }}">上一页</a>
{% endif %}
{% if page_next %}
<a href="{{ SITE_ROOT }}seafadmin/?page={{ next_page }}&per_page={{ per_page }}">下一页</a>
{% endif %}
<span>每页:</span>
{% if per_page == 25 %}
<span> 25 </span>
{% else %}
<a href="{{ SITE_ROOT }}seafadmin/?per_page=25" class="per-page">25</a>
{% endif %}
{% if per_page == 50 %}
<span> 50 </span>
{% else %}
<a href="{{ SITE_ROOT }}seafadmin/?per_page=50" class="per-page">50</a>
{% endif %}
{% if per_page == 100 %}
<span> 100 </span>
{% else %}
<a href="{{ SITE_ROOT }}seafadmin/?per_page=100" class="per-page">100</a>
{% endif %}
</div>
{% else %}
<p>暂无</p>
{% endif %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.repo-delete-btn'));
</script>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "admin_base.html" %}
{% extends "org_admin_base.html" %}
{% block nav_useradmin_class %}class="cur"{% endblock %}
{% block left_panel %}
@ -10,6 +10,18 @@
{% block right_panel %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
{% if message.tags == 'info' %}
<li class="notification">邮件发送成功。</li>
{% endif %}
{% if message.tags == 'error' %}
<li class="error">邮件发送失败。</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
<h3>所有用户</h3>
<table>
@ -32,7 +44,7 @@
<!-- <button class="add-role-btn" userid="{{ user.profile.ccnet_user_id }}" email="{{ user.email }}">添加角色</button> -->
{% endif %}
{% if not user.is_self %}
<button class="remove-user-btn" data="{{ SITE_ROOT }}useradmin/{{ user.props.id }}/user/remove/">删除</button>
<button class="remove-user-btn" data="{{ SITE_ROOT }}useradmin/remove/{{ user.props.id }}/">删除</button>
{% endif %}
</td>
</tr>

View File

@ -0,0 +1,52 @@
{% extends "myhome_base.html" %}
{% block title %}注册{% endblock %}
{% block main_panel %}
<div class="narrow-panel">
{% if request.user.is_authenticated %}
<h2>欢迎回来,您已登录。</h2>
{% else %}
<h2>注册企业帐号</h2>
<form action="" method="post">
<label for="id_email">邮箱:</label>
{{ form.email }}
<label for="id_password1">密码:</label>
{{ form.password1 }}
<label for="id_password2">确认密码:</label>
{{ form.password2 }}
<label for="id_org_name">企业名称:</label>
{{ form.org_name}}
<lable for="id_url_prefix">域名前缀:</label>
{{ form.url_prefix }}
<p class="error hide"></p>
{{ form.email.errors }}
{{ form.password1.errors }}
{{ form.password2.errors }}
{{ form.url_prefix.errors }}
<input type="submit" value="提交" class="submit" />
</form>
{% endif %}
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
$('input[type="submit"]').click(function(){
if (!$.trim($('input[name="email"]').attr('value'))) {
$('.error').removeClass('hide').html('请输入邮箱。');
return false;
}
if (!$.trim($('input[name="password1"]').attr('value'))) {
$('.error').removeClass('hide').html('请输入密码。');
return false;
}
if (!$.trim($('input[name="password2"]').attr('value'))) {
$('.error').removeClass('hide').html('请确认密码。');
return false;
}
if ($.trim($('input[name="password1"]').attr('value')) != $.trim($('input[name="password2"]').attr('value'))) {
$('.error').removeClass('hide').html('两次输入的密码不一致。');
return false;
}
});
</script>
{% endblock %}

View File

@ -18,7 +18,7 @@
<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>
<td><button data="{{ SITE_ROOT}}group/{{ group.id }}/?op=delete&src=groupadmin" class="group-remove-btn">删除</button></td>
<td><button data="{{ SITE_ROOT}}group/{{ group.id }}/?op=delete&src=sysgroupadmin" class="group-remove-btn">删除</button></td>
</tr>
{% endfor %}
</table>

View File

@ -0,0 +1,64 @@
{% extends "admin_base.html" %}
{% load seahub_tags %}
{% block nav_orgadmin_class %}class="cur"{% endblock %}
{% block right_panel %}
<h3>所有企业</h3>
{% if orgs %}
<table>
<tr>
<th width="20%">名字</th>
<th width="30%">域名前缀</th>
<th width="20%">创建者</th>
<th width="23%">创建时间</th>
<th width="7%">操作</th>
</tr>
{% for org in orgs %}
<tr>
<td>{{ org.org_name }}</td>
<td>{{ org.url_prefix }}</td>
<td>{{ org.creator }}</td>
<td>{{ org.ctime|tsstr_sec }}</td>
<td><button data="{{ SITE_ROOT}}org/remove/{{ org.org_id }}/" class="org-remove-btn">删除</button></td>
</tr>
{% endfor %}
</table>
<!--
<div id="paginator">
{% if current_page != 1 %}
<a href="{{ SITE_ROOT }}groupadmin/?page={{ prev_page }}&per_page={{ per_page }}">上一页</a>
{% endif %}
{% if page_next %}
<a href="{{ SITE_ROOT }}groupadmin/?page={{ next_page }}&per_page={{ per_page }}">下一页</a>
{% endif %}
<span>每页:</span>
{% if per_page == 25 %}
<span> 25 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=25" class="per-page">25</a>
{% endif %}
{% if per_page == 50 %}
<span> 50 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=50" class="per-page">50</a>
{% endif %}
{% if per_page == 100 %}
<span> 100 </span>
{% else %}
<a href="{{ SITE_ROOT }}groupadmin/?per_page=100" class="per-page">100</a>
{% endif %}
</div>
-->
{% else %}
<p>暂无</p>
{% endif %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.org-remove-btn'), '确定要删除该企业?');
</script>
{% endblock %}

View File

@ -0,0 +1,87 @@
{% extends "admin_base.html" %}
{% block nav_useradmin_class %}class="cur"{% endblock %}
{% block left_panel %}
<h3>操作</h3>
<ul class="with-bg">
<li><a href="{{ SITE_ROOT }}useradmin/add/">添加用户</a></li>
</ul>
{% endblock %}
{% block right_panel %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
{% if message.tags == 'info' %}
<li class="notification">邮件发送成功。</li>
{% endif %}
{% if message.tags == 'error' %}
<li class="error">邮件发送失败。</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
<h3>所有用户</h3>
<table>
<tr>
<th width="50%">邮箱</th>
<th width="20%">是否激活</th>
<th width="20%">是否企业帐号</th>
<th width="10%">操作</th>
</tr>
{% for user in users %}
<tr>
<td><a href="{{ SITE_ROOT }}useradmin/info/{{ user.props.email }}/">{{ user.props.email }}</a></td>
{% if user.props.is_active %}
<td>已激活</td>
{% else %}
<td><button data="{{ SITE_ROOT }}useradmin/activate/{{ user.props.id }}/" class="activate">激活</button></td>
{% endif %}
{% if user.is_org_user %}
<td></td>
{% else %}
<td></td>
{% endif %}
<td>
{% if user.profile %}
<!-- <button class="add-role-btn" userid="{{ user.profile.ccnet_user_id }}" email="{{ user.email }}">添加角色</button> -->
{% endif %}
{% if not user.is_self %}
<button class="remove-user-btn" data="{{ SITE_ROOT }}useradmin/remove/{{ user.props.id }}/">删除</button>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<form id="add-role-form" action="" method="post" class="hide">
<p><span id="user_email"></span> 的新角色 (即 MyClient 等)</p>
<input id="id_role" type="text" name="role" /><br/>
<input id="id_summit" type="submit" value="提交" />
</form>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
$('.activate').each(function(){
$(this).click(function(){
location.href = $(this).attr('data');
});
});
$(".add-role-btn").each(function() {
$(this).click(function() {
var url = "{{ SITE_ROOT }}useradmin/" + $(this).attr("userid") + "/role/add/";
$("#add-role-form").attr('action', url);
$("#user_email").html($(this).attr("email"));
$("#add-role-form").modal({appendTo: "#main"});
});
});
//delete confirm
addConfirmTo($('.remove-user-btn'));
addConfirmTo($('.role-delete-btn'));
</script>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% autoescape off %}
亲爱的 {{ email }}
{% if org %}
{{ user }} 在 SeaCloud 云存储上将您加入到企业 {{ org.org_name }}
{% else %}
{{ user }} 将您加入 SeaCloud 云存储!
{% endif %}
以下是您的登录信息:
用户名: {{ email }}
密码: {{ password }}
请点击以下链接登录:
{{ protocol }}://{{ domain }}{% url auth_login %}
感谢使用我们的网站!
Seafile团队
{% endautoescape %}

View File

@ -50,7 +50,6 @@ def login(request, template_name='registration/login.html',
# Okay, security checks complete. Log the user in.
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
request.session.delete_test_cookie()

View File

@ -304,6 +304,10 @@ def get_ccnetuser(username=None, userid=None):
emailuser = ccnet_rpc.get_emailuser_by_id(userid)
if not emailuser:
return None
# Check whether is business account
org = ccnet_rpc.get_org_by_user(emailuser.email)
emailuser.org = org
# And convert to ccnetuser
from seahub.base.accounts import convert_to_ccnetuser

33
urls.py
View File

@ -3,12 +3,13 @@ from django.conf import settings
from django.views.generic.simple import direct_to_template
from seahub.views import root, peers, myhome, \
repo, repo_history, modify_token, remove_repo, seafadmin, useradmin, \
activate_user, user_add, user_remove, \
repo, repo_history, modify_token, remove_repo, sys_seafadmin, sys_useradmin, \
org_seafadmin, org_useradmin, org_group_admin, org_remove, \
activate_user, user_add, user_remove, sys_group_admin, sys_org_admin, \
ownerhome, repo_history_dir, repo_history_revert, \
user_info, repo_set_access_property, repo_access_file, \
repo_remove_share, repo_download, \
seafile_access_check, back_local, group_admin, repo_history_changes
repo_remove_share, repo_download, org_info, \
seafile_access_check, back_local, repo_history_changes
from seahub.notifications.views import notification_list
from seahub.share.views import share_admin
@ -50,25 +51,33 @@ urlpatterns = patterns('',
(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file),
(r'^download/repo/$', repo_download),
(r'^seafile_access_check/$', seafile_access_check),
url(r'^org/remove/(?P<org_id>[\d]+)/$', org_remove, name="org_remove"),
(r'^org/$', org_info),
(r'^back/local/$', back_local),
(r'^seafadmin/$', seafadmin),
url(r'^useradmin/$', useradmin, name='useradmin'),
(r'^useradmin/add/$', user_add),
(r'^useradmin/remove/(?P<user_id>[^/]+)/$', user_remove),
(r'^useradmin/info/(?P<email>[^/]+)/$', user_info),
# (r'^useradmin/(?P<user_id>[^/]+)/role/add/$', role_add),
# (r'^useradmin/(?P<user_id>[^/]+)/role/remove/$', role_remove),
(r'^useradmin/(?P<user_id>[^/]+)/user/remove/$', user_remove),
(r'^useradmin/activate/(?P<user_id>[^/]+)/$', activate_user),
### Apps ###
(r'^avatar/', include('avatar.urls')),
(r'^notification/', include('notifications.urls')),
url(r'^notificationadmin/', notification_list, name='notification_list'),
url(r'^sys/notificationadmin/', notification_list, name='notification_list'),
(r'^contacts/', include('contacts.urls')),
(r'^group/', include('seahub.group.urls')),
url(r'^groupadmin/$', group_admin, name='group_admin'),
(r'^profile/', include('seahub.profile.urls')),
(r'^back/local/$', back_local),
### SeaHub admin ###
(r'^sys/seafadmin/$', sys_seafadmin),
url(r'^sys/useradmin/$', sys_useradmin, name='sys_useradmin'),
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
### Org admin ###
(r'^seafadmin/$', org_seafadmin),
url(r'^useradmin/$', org_useradmin, name='org_useradmin'),
url(r'^groupadmin/$', org_group_admin, name='org_group_admin'),
)
if settings.DEBUG:

View File

@ -9,7 +9,7 @@ from django.utils.hashcompat import sha_constructor
def go_permission_error(request, msg=None):
"""
return permisson error page
Return permisson error page.
"""
return render_to_response('permission_error.html', {
@ -18,7 +18,7 @@ def go_permission_error(request, msg=None):
def go_error(request, msg=None):
"""
return normal error page
Return normal error page.
"""
return render_to_response('error.html', {
@ -27,20 +27,15 @@ def go_error(request, msg=None):
def list_to_string(l):
"""
return string of a list
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
return ','.join(l)
def get_httpserver_root():
"""
Get seafile http server address and port from settings.py,
and cut out last '/'
and cut out last '/'.
"""
if settings.HTTP_SERVER_ROOT[-1] == '/':
@ -52,7 +47,7 @@ def get_httpserver_root():
def get_ccnetapplet_root():
"""
Get ccnet applet address and port from settings.py,
and cut out last '/'
and cut out last '/'.
"""
if settings.CCNET_APPLET_ROOT[-1] == '/':
@ -63,17 +58,17 @@ def get_ccnetapplet_root():
def gen_token():
"""
Generate short token used for owner to access repo file
Generate short token used for owner to access repo file.
"""
token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[::8]
token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[:5]
return token
def validate_group_name(group_name):
"""
Check whether group name is valid.
A valid group name only contains alphanumeric character
A valid group name only contains alphanumeric character.
"""
return re.match('^\w+$', group_name, re.U)

218
views.py
View File

@ -2,13 +2,16 @@
import settings
import stat
import simplejson as json
import sys
from urllib import quote
from django.core.urlresolvers import reverse
from django.core.mail import send_mail
from django.contrib import messages
from django.contrib.sites.models import Site, RequestSite
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.template import Context, loader, RequestContext
from django.views.decorators.csrf import csrf_protect
from auth.decorators import login_required
@ -713,7 +716,7 @@ def mypeers(request):
cid = get_user_cid(request.user)
@login_required
def seafadmin(request):
def sys_seafadmin(request):
if not request.user.is_staff:
raise Http404
@ -728,6 +731,7 @@ def seafadmin(request):
repos_all = seafserv_threaded_rpc.get_repo_list(per_page *
(current_page -1),
per_page + 1)
repos = repos_all[:per_page]
if len(repos_all) == per_page + 1:
@ -742,7 +746,7 @@ def seafadmin(request):
repo.owner = None
return render_to_response(
'repos.html', {
'sys_seafadmin.html', {
'repos': repos,
'current_page': current_page,
'prev_page': current_page-1,
@ -753,17 +757,80 @@ def seafadmin(request):
context_instance=RequestContext(request))
@login_required
def useradmin(request):
def org_seafadmin(request):
if not request.user.org:
raise Http404
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
repos_all = seafserv_threaded_rpc.get_org_repo_list(request.user.org.org_id,
per_page * (current_page -1),
per_page + 1)
repos = repos_all[:per_page]
if len(repos_all) == per_page + 1:
page_next = True
else:
page_next = False
for repo in repos:
try:
repo.owner = seafserv_threaded_rpc.get_repo_owner(repo.props.id)
except:
repo.owner = None
return render_to_response(
'org_seafadmin.html', {
'repos': repos,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
},
context_instance=RequestContext(request))
@login_required
def sys_useradmin(request):
if not request.user.is_staff:
raise Http404
users = ccnet_rpc.get_emailusers(-1,-1)
for user in users:
if user.props.id == request.user.id:
user.is_self = True
# TODO: may add new is_org_user rpc
user.is_org_user = True if ccnet_rpc.get_org_by_user(user.email) else False
return render_to_response(
'useradmin.html', {
'sys_useradmin.html', {
'users': users,
},
context_instance=RequestContext(request))
@login_required
def org_useradmin(request):
if not request.user.org.is_staff:
raise Http404
users = ccnet_rpc.get_org_emailusers(request.user.org.url_prefix,
0, sys.maxint)
for user in users:
if user.props.id == request.user.id:
user.is_self = True
user.is_org_user = True
return render_to_response(
'org_useradmin.html', {
'users': users,
},
context_instance=RequestContext(request))
@ -834,13 +901,18 @@ def user_info(request, email):
def user_remove(request, user_id):
"""The user id is emailuser id."""
if not request.user.is_staff:
if not request.user.is_staff and not request.user.org.is_staff:
raise Http404
ccnetuser = get_ccnetuser(userid=int(user_id))
if ccnetuser.org:
ccnet_rpc.remove_org_user(ccnetuser.org.org_id, ccnetuser.username)
ccnetuser.delete()
return HttpResponseRedirect(reverse('useradmin'))
if request.user.is_staff:
return HttpResponseRedirect(reverse('sys_useradmin'))
else:
return HttpResponseRedirect(reverse('org_useradmin'))
@login_required
def activate_user(request, user_id):
@ -855,13 +927,37 @@ def activate_user(request, user_id):
return HttpResponseRedirect(reverse('useradmin'))
def send_user_add_mail(request, email, password):
""" Send email when add new user """
use_https = request.is_secure()
domain = RequestSite(request).domain
t = loader.get_template('user_add_email.html')
c = {
'user': request.user.username,
'org': request.user.org,
'email': email,
'password': password,
'domain': domain,
'protocol': use_https and 'https' or 'http',
}
try:
send_mail(u'SeaCloud注册信息', t.render(Context(c)),
None, [email], fail_silently=False)
messages.add_message(request, messages.INFO, email)
except:
messages.add_message(request, messages.ERROR, email)
@login_required
def user_add(request):
"""Add a user"""
if not request.user.is_staff:
if not request.user.is_staff and not request.user.org.is_staff:
raise Http404
base_template = 'org_admin_base.html' if request.user.org else 'admin_base.html'
if request.method == 'POST':
form = AddUserForm(request.POST)
if form.is_valid():
@ -872,12 +968,24 @@ def user_add(request):
ccnetuser.is_active = True
ccnetuser.save()
return HttpResponseRedirect(reverse('useradmin', args=[]))
if request.user.org:
org_id = request.user.org.org_id
ccnet_rpc.add_org_user(org_id, email, 0)
if hasattr(settings, 'EMAIL_HOST'):
send_user_add_mail(request, email, password)
return HttpResponseRedirect(reverse('org_useradmin'))
else:
if hasattr(settings, 'EMAIL_HOST'):
send_user_add_mail(request, email, password)
return HttpResponseRedirect(reverse('sys_useradmin', args=[]))
else:
form = AddUserForm()
return render_to_response("add_user_form.html", {
'form': form,
'form': form,
'base_template': base_template,
}, context_instance=RequestContext(request))
def back_local(request):
@ -887,7 +995,7 @@ def back_local(request):
return HttpResponseRedirect(redirect_url)
def group_admin(request):
def sys_group_admin(request):
if not request.user.is_staff:
raise Http404
@ -898,9 +1006,10 @@ def group_admin(request):
except ValueError:
current_page = 1
per_page = 25
groups_plus_one = ccnet_rpc.get_all_groups(per_page * (current_page -1),
per_page +1)
groups = groups_plus_one[:per_page]
if len(groups_plus_one) == per_page + 1:
@ -908,11 +1017,90 @@ def group_admin(request):
else:
page_next = False
return render_to_response("group_admin.html", {
"groups": groups,
return render_to_response('sys_group_admin.html', {
'groups': groups,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
}, context_instance=RequestContext(request))
def sys_org_admin(request):
if not request.user.is_staff:
raise Http404
orgs = ccnet_rpc.get_all_orgs(0, sys.maxint)
return render_to_response('sys_org_admin.html', {
'orgs': orgs,
}, context_instance=RequestContext(request))
def org_group_admin(request):
if not request.user.is_staff and not request.user.org.is_staff:
raise Http404
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
groups_plus_one = ccnet_rpc.get_org_groups (request.user.org.org_id,
per_page * (current_page -1),
per_page +1)
groups = groups_plus_one[:per_page]
if len(groups_plus_one) == per_page + 1:
page_next = True
else:
page_next = False
return render_to_response('org_group_admin.html', {
'groups': groups,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
}, context_instance=RequestContext(request))
def org_remove(request, org_id):
if not request.user.is_staff:
raise Http404
try:
org_id_int = int(org_id)
except ValueError:
return HttpResponseRedirect(reverse('sys_org_admin'))
# Remove repos in that org
seafserv_threaded_rpc.remove_org_repo_by_org_id(org_id_int)
# TODO: Remove repos in org's groups
ccnet_rpc.remove_org(org_id_int)
return HttpResponseRedirect(reverse('sys_org_admin'))
@login_required
def org_info(request):
if not request.user.org:
raise Http404
org = request.user.org
org_members = ccnet_rpc.get_org_emailusers(org.url_prefix, 0, sys.maxint)
for member in org_members:
member.short_username = member.email.split('@')[0]
groups = ccnet_rpc.get_org_groups(org.org_id, 0, sys.maxint)
return render_to_response('org_info.html', {
'org': org,
'org_users': org_members,
'groups': groups,
}, context_instance=RequestContext(request))