# encoding: utf-8 import os import simplejson as json import sys from django.core.urlresolvers import reverse from django.contrib import messages from django.core.mail import send_mail from django.contrib.sites.models import Site, RequestSite from django.http import HttpResponse, HttpResponseBadRequest, Http404, \ HttpResponseRedirect from django.shortcuts import render_to_response from django.template import Context, loader, RequestContext from django.utils.translation import ugettext as _ from auth.decorators import login_required from pysearpc import SearpcError from seaserv import ccnet_threaded_rpc, seafserv_rpc, seafserv_threaded_rpc,\ get_orgs_by_user, get_org_repos, list_org_inner_pub_repos, \ get_org_by_url_prefix, create_org, get_user_current_org, add_org_user, \ remove_org_user, get_org_groups, is_valid_filename, org_user_exists, \ create_org_repo, get_org_id_by_group, get_org_groups_by_user, \ get_org_users_by_url_prefix, list_org_shared_repos, is_personal_repo, \ get_org_group_repoids, is_org_repo_owner, get_repo, get_commits, \ check_permission, get_org_repo_owner from decorators import org_staff_required from forms import OrgCreateForm from signals import org_user_added from utils import validate_org_repo_owner from notifications.models import UserNotification from profile.models import Profile from share.models import FileShare from share.forms import RepoShareForm from registration.models import RegistrationProfile from base.accounts import User from contacts import Contact from seahub.forms import RepoCreateForm, SharedRepoCreateForm import seahub.settings as seahub_settings from seahub.utils import render_error, render_permission_error, gen_token, \ validate_group_name, string2list, calculate_repo_last_modify, MAX_INT, \ EVENTS_ENABLED, get_starred_files, gen_shared_link from seahub.views import myhome from seahub.signals import repo_created @login_required def create_org(request): """ Create org account. """ if request.method == 'POST': form = OrgCreateForm(request.POST) if form.is_valid(): org_name = form.cleaned_data['org_name'] url_prefix = form.cleaned_data['url_prefix'] username = request.user.username try: # create_org(org_name, url_prefix, username) ccnet_threaded_rpc.create_org(org_name, url_prefix, username) return HttpResponseRedirect(\ reverse(org_personal, args=[url_prefix])) except SearpcError, e: return render_error(request, e.msg, extra_ctx={ 'base_template': 'myhome_base.html', }) else: form = OrgCreateForm() return render_to_response('organizations/create_org.html', { 'form': form, }, context_instance=RequestContext(request)) @login_required def org_root(request, url_prefix): return HttpResponseRedirect(reverse(org_personal, args=[url_prefix])) @login_required def org_public(request, url_prefix): """ Show org info page, list org inner pub repos. """ org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) repos = list_org_inner_pub_repos(org.org_id, request.user.username) return render_to_response('organizations/org_public.html', { 'org': org, 'repos': repos, 'create_shared_repo': True, }, context_instance=RequestContext(request)) @login_required def org_personal(request, url_prefix): """ Show org personal page. """ org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) user = request.user.username # Org repos that I own owned_repos = seafserv_threaded_rpc.list_org_repos_by_owner(org.org_id, user) calculate_repo_last_modify(owned_repos) owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify)) # Org groups user created groups = get_org_groups_by_user(org.org_id, user) # Org repos others shared to me in_repos = list_org_shared_repos(org.org_id, user,'to_email', -1, -1) # For each org group I joined... for grp in groups: # Get org group repos, and for each group repos... for r_id in get_org_group_repoids(org.org_id, grp.id): # No need to list my own repo if is_org_repo_owner(org.org_id, r_id, user): continue # Convert repo properties due to the different collumns in Repo # and SharedRepo r = get_repo(r_id) if not r: continue r.repo_id = r.id r.repo_name = r.name r.repo_desc = r.desc last_commit = get_commits(r_id, 0, 1)[0] r.last_modified = last_commit.ctime if last_commit else 0 r.share_type = 'group' r.user = get_org_repo_owner(r_id) r.user_perm = check_permission(r_id, user) in_repos.append(r) in_repos.sort(lambda x, y: cmp(y.last_modified, x.last_modified)) # All org groups used in auto complete. org_groups = get_org_groups(org.org_id, -1, -1) # Org members used in auto complete contacts = [] org_members = get_org_users_by_url_prefix(org.url_prefix, 0, MAX_INT) for m in org_members: if m.email == user: # shouldn' show my'email in auto complete continue m.contact_email = m.email contacts.append(m) # Get nickname if not Profile.objects.filter(user=request.user.username): nickname = '' else: profile = Profile.objects.filter(user=request.user.username)[0] nickname = profile.nickname # events if EVENTS_ENABLED: events = True else: events = False quota_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org.org_id, user) starred_files = get_starred_files(user, org_id=org.org_id) return render_to_response('organizations/personal.html', { 'owned_repos': owned_repos, "in_repos": in_repos, 'org': org, 'groups': groups, 'org_groups': org_groups, 'contacts': contacts, 'create_shared_repo': False, 'allow_public_share': True, 'nickname': nickname, 'events': events, 'quota_usage': quota_usage, 'starred_files': starred_files, }, context_instance=RequestContext(request)) @login_required def org_inner_pub_repo_create(request, url_prefix): """ Handle ajax post to create org inner pub repo, this repo is accessiable by all org members. """ if not request.is_ajax() or request.method != 'POST': return Http404 result = {} content_type = 'application/json; charset=utf-8' form = SharedRepoCreateForm(request.POST) if form.is_valid(): repo_name = form.cleaned_data['repo_name'] repo_desc = form.cleaned_data['repo_desc'] permission = form.cleaned_data['permission'] passwd = form.cleaned_data['passwd'] user = request.user.username org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponse(json.dumps(_(u'Failed to create the library: you has not joined this organizatioin.')), content_type=content_type) try: # create a org repo repo_id = create_org_repo(repo_name, repo_desc, user, passwd, org.org_id) # set org inner pub seafserv_threaded_rpc.set_org_inner_pub_repo(org.org_id, repo_id, permission) except: repo_id = None if not repo_id: result['error'] = _(u"Failed to create.") else: result['success'] = True repo_created.send(sender=None, org_id=org.org_id, creator=user, repo_id=repo_id, repo_name=repo_name) return HttpResponse(json.dumps(result), content_type=content_type) else: return HttpResponseBadRequest(json.dumps(form.errors), content_type=content_type) @login_required def unset_org_inner_pub_repo(request, url_prefix, repo_id): org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(org_public, args=[url_prefix])) try: seafserv_threaded_rpc.unset_org_inner_pub_repo(org.org_id, repo_id) except SearpcError: pass messages.add_message(request, messages.INFO, _('Operation Succeeded.')) return HttpResponseRedirect(reverse(org_shareadmin, args=[url_prefix])) @login_required def org_groups(request, url_prefix): """ List org groups and add org group. """ org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) if request.method == 'POST': result = {} content_type = 'application/json; charset=utf-8' group_name = request.POST.get('group_name') if not validate_group_name(group_name): result['error'] = _(u'Group name can only contain letters, digits and underscore') return HttpResponse(json.dumps(result), content_type=content_type) try: e_grpname = group_name.encode('utf-8') user = request.user.username group_id = ccnet_threaded_rpc.create_org_group(org.org_id, e_grpname, user) except SearpcError, e: result['error'] = _(e.msg) return HttpResponse(json.dumps(result), content_type=content_type) return HttpResponse(json.dumps({'success': True}), content_type=content_type) joined_groups = get_org_groups_by_user(org.org_id, request.user.username) groups = get_org_groups(org.org_id, -1, -1) org_members = get_org_users_by_url_prefix(url_prefix, 0, MAX_INT) return render_to_response('organizations/org_groups.html', { 'org': org, 'groups': groups, 'joined_groups': joined_groups, 'org_members': org_members, }, context_instance=RequestContext(request)) def send_org_user_add_mail(request, email, password, org_name): """ Send email when add new user. """ use_https = request.is_secure() domain = RequestSite(request).domain t = loader.get_template('organizations/org_user_add_email.html') c = { 'user': request.user.username, 'org_name': org_name, 'email': email, 'password': password, 'domain': domain, 'protocol': use_https and 'https' or 'http', 'site_name': seahub_settings.SITE_NAME, } try: send_mail(_(u'Seafile Login Information'), t.render(Context(c)), None, [email], fail_silently=False) messages.add_message(request, messages.INFO, _(u'Mail sent successfully')) except: messages.add_message(request, messages.ERROR, _(u'Failed to send the email')) @login_required @org_staff_required def org_admin(request, url_prefix): """ List and add org users. """ if request.method == 'POST': emails = request.POST.get('added-member-name') email_list = string2list(emails) for email in email_list: if not email or email.find('@') <= 0 : continue org_id = request.user.org['org_id'] try: User.objects.get(email=email) email = email.strip(' ') org_id = request.user.org['org_id'] add_org_user(org_id, email, 0) # send signal org_user_added.send(sender=None, org_id=org_id, from_email=request.user.username, to_email=email) except User.DoesNotExist: # User is not registered, just create account and # add that account to org password = gen_token(max_length=6) if Site._meta.installed: site = Site.objects.get_current() else: site = RequestSite(request) RegistrationProfile.objects.create_active_user(\ email, email, password, site, send_email=False) add_org_user(org_id, email, 0) if hasattr(seahub_settings, 'EMAIL_HOST'): org_name = request.user.org['org_name'] send_org_user_add_mail(request, email, password, org_name) # 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 users_plus_one = get_org_users_by_url_prefix( url_prefix, per_page * (current_page - 1), per_page + 1) if len(users_plus_one) == per_page + 1: page_next = True else: page_next = False org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) users = users_plus_one[:per_page] for user in users: if user.props.id == request.user.id: user.is_self = True try: user.quota_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org.org_id, user.email) except: user.quota_usage = -1 # My contacts contacts = Contact.objects.filter(user_email=request.user.username) org_quota_usage = seafserv_threaded_rpc.get_org_quota_usage(org.org_id) org_quota = seafserv_threaded_rpc.get_org_quota(org.org_id) return render_to_response( 'organizations/org_admin.html', { 'users': users, 'contacts': contacts, 'current_page': current_page, 'prev_page': current_page-1, 'next_page': current_page+1, 'per_page': per_page, 'page_next': page_next, 'org_quota_usage': org_quota_usage, 'org_quota': org_quota, }, context_instance=RequestContext(request)) @login_required @org_staff_required def org_user_remove(request, url_prefix, user): """ Remove org user. """ org_id = request.user.org['org_id'] url_prefix = request.user.org['url_prefix'] remove_org_user(org_id, user) messages.success(request, _(u"Successfully deleted.")) return HttpResponseRedirect(reverse('org_admin', args=[url_prefix])) def org_msg(request): """ Show organization user added messages. """ orgmsg_list = [] notes = UserNotification.objects.filter(to_user=request.user.username) for n in notes: if n.msg_type == 'org_join_msg': try: d = json.loads(n.detail) from_email = d['from_email'] org_name = d['org_name'] org_prefix = d['org_prefix'] org_url = reverse('org_public', args=[org_prefix]) msg = _(u'%(from_email)s added you to Organization %(org_name)s') % \ {'from_email':from_email, 'org_url': org_url, 'org_name': org_name} orgmsg_list.append(msg) except json.decoder.JSONDecodeError: # This message is not json format, just list to user. orgmsg_list.append(n.detail) # remove new org msg notification UserNotification.objects.filter(to_user=request.user.username, msg_type='org_join_msg').delete() return render_to_response('organizations/new_msg.html', { 'orgmsg_list': orgmsg_list, }, context_instance=RequestContext(request)) @login_required def org_repo_create(request, url_prefix): """ Handle ajax post to create org repo, this repo is only accessiable by owner. """ if not request.is_ajax() or request.method != 'POST': return Http404 result = {} content_type = 'application/json; charset=utf-8' form = RepoCreateForm(request.POST) if form.is_valid(): repo_name = form.cleaned_data['repo_name'] repo_desc = form.cleaned_data['repo_desc'] encrypted = form.cleaned_data['encryption'] passwd = form.cleaned_data['passwd'] passwd_again = form.cleaned_data['passwd_again'] user = request.user.username org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponse(json.dumps(_(u'Failed to create: you has not joined this organization')), content_type=content_type) repo_id = create_org_repo(repo_name, repo_desc, user, passwd, org.org_id) if not repo_id: result['error'] = _(u'Failed to create') else: result['success'] = True repo_created.send(sender=None, org_id=org.org_id, creator=user, repo_id=repo_id, repo_name=repo_name) return HttpResponse(json.dumps(result), content_type=content_type) else: return HttpResponseBadRequest(json.dumps(form.errors), content_type=content_type) @login_required @org_staff_required def org_seafadmin(request, url_prefix): # 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 = get_org_repos(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 return render_to_response( 'organizations/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 @org_staff_required def org_group_admin(request, url_prefix): # 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 = 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('organizations/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)) @login_required @org_staff_required def org_group_remove(request, url_prefix, group_id): # Request header may missing HTTP_REFERER, we need to handle that case. next = request.META.get('HTTP_REFERER', None) if not next: next = seahub_settings.SITE_ROOT try: group_id_int = int(group_id) except ValueError: return HttpResponseRedirect(next) # Check whether is the org group. org_id = get_org_id_by_group(group_id_int) if request.user.org['org_id'] != org_id: return render_permission_error(request, _(u"This group doesn't belong to current organazation"), extra_ctx={'org': request.user.org, 'base_template': 'org_base.html'}) try: ccnet_threaded_rpc.remove_group(group_id_int, request.user.username) seafserv_threaded_rpc.remove_repo_group(group_id_int, None) ccnet_threaded_rpc.remove_org_group(org_id, group_id_int) except SearpcError, e: return render_error(request, e.msg, extra_ctx={ 'org': request.user.org, 'base_template': 'org_base.html', }) return HttpResponseRedirect(next) @login_required def org_repo_share(request, url_prefix): """ Share org repo to members or groups in current org. """ if request.method != 'POST': raise Http404 org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) form = RepoShareForm(request.POST) if not form.is_valid(): # TODO: may display error msg on form raise Http404 email_or_group = form.cleaned_data['email_or_group'] repo_id = form.cleaned_data['repo_id'] permission = form.cleaned_data['permission'] from_email = request.user.username # Test whether user is the repo owner if not validate_org_repo_owner(org.org_id, repo_id, request.user.username): return render_permission_error(request, _(u'Only the owner of this library has permission to share it.'), extra_ctx={ 'org': org, 'base_template': 'org_base.html', }) share_to_list = string2list(email_or_group) for share_to in share_to_list: if share_to == 'all': ''' Share to public ''' try: seafserv_threaded_rpc.set_org_inner_pub_repo(org.org_id, repo_id, permission) except: msg = _(u'Failed to share to all members') messages.add_message(request, messages.ERROR, msg) continue msg = _(u'Shared to all members successfully, you can go check it at Share.') % \ (reverse('org_shareadmin', args=[org.url_prefix])) messages.add_message(request, messages.INFO, msg) elif (share_to.find('@') == -1): ''' Share repo to group ''' # TODO: if we know group id, then we can simplly call group_share_repo group_name = share_to # Get all org groups. groups = get_org_groups(org.org_id, -1, -1) find = False for group in groups: # for every group that user joined, if group name and # group creator matchs, then has finded the group if group.props.group_name == group_name: seafserv_threaded_rpc.add_org_group_repo(repo_id, org.org_id, group.id, from_email, permission) find = True msg = _(u'Shared to %(group)s successfully,you can go check it at Share.') % \ {'group':group_name, 'share':reverse('org_shareadmin', args=[org.url_prefix])} messages.add_message(request, messages.INFO, msg) break if not find: msg = _(u'Failed to share to %s.') % group_name messages.add_message(request, messages.ERROR, msg) else: ''' Share repo to user ''' # Test whether share_to is in this org if not org_user_exists(org.org_id, share_to): msg = _(u'Failed to share to %s: this user does not exist in the organization.') % share_to messages.add_message(request, messages.ERROR, msg) continue # Record share info to db. try: seafserv_threaded_rpc.add_share(repo_id, from_email, share_to, permission) msg = _(u'Shared to %(share_to)s successfully,you can go check it at Share.') % \ {"share_to":share_to, "share": reverse('org_shareadmin', args=[org.url_prefix])} messages.add_message(request, messages.INFO, msg) except SearpcError, e: msg = _(u'Failed to share to %s.') % share_to messages.add_message(request, messages.ERROR, msg) continue return HttpResponseRedirect(reverse(org_personal, args=[org.url_prefix])) @login_required def org_shareadmin(request, url_prefix): """ List org shared repos and org shared links. """ username = request.user.username org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) shared_repos = [] # org repos shared by this user shared_repos += seafserv_threaded_rpc.list_org_share_repos(org.org_id, username, 'from_email', -1, -1) # repos shared to groups group_repos = seafserv_threaded_rpc.get_org_group_repos_by_owner(org.org_id, username) for repo in group_repos: group = ccnet_threaded_rpc.get_group(int(repo.group_id)) if not group: repo.props.user = '' continue repo.props.user = group.props.group_name repo.props.user_info = repo.group_id shared_repos += group_repos # public repos shared by this user pub_repos = seafserv_threaded_rpc.list_org_inner_pub_repos_by_owner(org.org_id, username) for repo in pub_repos: repo.props.user = _(u'all members') repo.props.user_info = 'all' shared_repos += pub_repos for repo in shared_repos: if repo.props.permission == 'rw': repo.share_permission = _(u'Read-Write') elif repo.props.permission == 'r': repo.share_permission = _(u'Read-Only') else: repo.share_permission = '' if repo.props.share_type == 'personal': repo.props.user_info = repo.props.user shared_repos.sort(lambda x, y: cmp(x.repo_id, y.repo_id)) # shared links fileshares = FileShare.objects.filter(username=request.user.username) o_fileshares = [] # shared links in org repos for fs in fileshares: if not is_personal_repo(fs.repo_id): # only list links in org repos if fs.s_type == 'f': fs.filename = os.path.basename(fs.path) fs.shared_link = gen_shared_link(request, fs.token, 'f') else: fs.filename = os.path.basename(fs.path[:-1]) fs.shared_link = gen_shared_link(request, fs.token, 'd') r = get_repo(fs.repo_id) # get_repo may returns None if not r: continue fs.repo = r o_fileshares.append(fs) # use org base template request.base_template = 'org_base.html' return render_to_response('repo/share_admin.html', { "org": org, "shared_repos": shared_repos, "fileshares": o_fileshares, "protocol": request.is_secure() and 'https' or 'http', "domain": RequestSite(request).domain, }, context_instance=RequestContext(request)) @login_required def org_share_permission_admin(request, url_prefix): org_id = int(request.GET.get('org_id', '')) share_type = request.GET.get('share_type', '') content_type = 'application/json; charset=utf-8' form = RepoShareForm(request.POST) form.is_valid() email_or_group = form.cleaned_data['email_or_group'] repo_id = form.cleaned_data['repo_id'] permission = form.cleaned_data['permission'] from_email = request.user.username if share_type == 'personal': try: seafserv_threaded_rpc.set_share_permission(repo_id, from_email, email_or_group, permission) except: return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type) return HttpResponse(json.dumps({'success': True}), content_type=content_type) if share_type == 'group': group_id = int(email_or_group) try: seafserv_threaded_rpc.set_org_group_repo_permission(org_id, group_id, repo_id, permission) except: return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type) return HttpResponse(json.dumps({'success': True}), content_type=content_type) if share_type == 'public': try: seafserv_threaded_rpc.set_org_inner_pub_repo(org_id, repo_id, permission) except: return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type) return HttpResponse(json.dumps({'success': True}), content_type=content_type) @login_required def org_pubinfo(request, url_prefix): """ Show org public information. """ org = get_user_current_org(request.user.username, url_prefix) if not org: return HttpResponseRedirect(reverse(myhome)) groups = get_org_groups(org.org_id, -1, -1) org_members = get_org_users_by_url_prefix(url_prefix, 0, MAX_INT) return render_to_response('organizations/org_pubinfo.html', { 'org': org, 'groups': groups, 'org_members': org_members, }, context_instance=RequestContext(request))