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

new admin libraries page

This commit is contained in:
lian
2016-05-25 10:45:22 +08:00
parent 37f6648a12
commit 72f4297934
32 changed files with 1880 additions and 65 deletions

View File

@@ -57,7 +57,11 @@ class AdminDevices(APIView):
result['platform'] = device.platform
return_results.append(result)
return Response(({'has_next_page': has_next_page}, return_results))
page_info = {
'has_next_page': has_next_page,
'current_page': current_page
}
return Response((page_info, return_results))
def delete(self, request, format=None):

View File

@@ -0,0 +1,150 @@
import logging
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django.template.defaultfilters import filesizeformat
from seaserv import ccnet_api, seafile_api, seafserv_threaded_rpc
from seahub.views.ajax import get_related_users_by_org_repo, \
get_related_users_by_repo
from seahub.signals import repo_deleted
from seahub.views import get_system_default_repo_id
from seahub.utils import is_org_context
from seahub.base.accounts import User
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
logger = logging.getLogger(__name__)
def get_repo_info(repo):
result = {}
result['id'] = repo.repo_id
result['name'] = repo.repo_name
result['owner'] = seafile_api.get_repo_owner(repo.repo_id)
result['size'] = repo.size
result['size_formatted'] = filesizeformat(repo.size)
result['encrypted'] = repo.encrypted
result['file_count'] = repo.file_count
return result
class AdminLibraries(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def get(self, request, format=None):
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '100'))
except ValueError:
current_page = 1
per_page = 100
start = (current_page - 1) * per_page
limit = per_page + 1
repos_all = seafile_api.get_repo_list(start, limit)
if len(repos_all) > per_page:
repos_all = repos_all[:per_page]
has_next_page = True
else:
has_next_page = False
default_repo_id = get_system_default_repo_id()
repos_all = filter(lambda r: not r.is_virtual, repos_all)
repos_all = filter(lambda r: r.repo_id != default_repo_id, repos_all)
return_results = []
for repo in repos_all:
repo_info = get_repo_info(repo)
return_results.append(repo_info)
page_info = {
'has_next_page': has_next_page,
'current_page': current_page
}
return Response((page_info, return_results))
class AdminLibrary(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def delete(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
return Response({'success': True})
if get_system_default_repo_id() == repo_id:
error_msg = ('System library can not be deleted.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if is_org_context(request):
org_id = seafserv_threaded_rpc.get_org_id_by_repo_id(repo_id)
usernames = get_related_users_by_org_repo(org_id, repo_id)
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
org_id = -1
usernames = get_related_users_by_repo(repo_id)
repo_owner = seafile_api.get_repo_owner(repo_id)
try:
seafile_api.remove_repo(repo_id)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
repo_deleted.send(sender=None, org_id=org_id, usernames=usernames,
repo_owner=repo_owner, repo_id=repo_id,
repo_name=repo.repo_name)
return Response({'success': True})
def put(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
new_owner = request.data.get('owner', None)
if not new_owner:
error_msg = 'owner invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
User.objects.get(email=new_owner)
except User.DoesNotExist:
error_msg = 'User %s not found.' % new_owner
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if is_org_context(request):
try:
if seafserv_threaded_rpc.get_org_id_by_repo_id(repo_id) > 0:
error_msg = 'Can not transfer organization library.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if ccnet_api.get_orgs_by_user(new_owner):
error_msg = 'Can not transfer library to organization user %s' % new_owner
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
seafile_api.set_repo_owner(repo_id, new_owner)
repo = seafile_api.get_repo(repo_id)
repo_info = get_repo_info(repo)
return Response(repo_info)

View File

@@ -0,0 +1,253 @@
import os
import stat
import logging
import time
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django.template.defaultfilters import filesizeformat
from seaserv import seafile_api, seafserv_threaded_rpc
from pysearpc import SearpcError
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.views.sysadmin import can_view_sys_admin_repo
from seahub.views.file import send_file_access_msg
from seahub.utils import is_org_context, gen_file_get_url, \
check_filename_with_rename
from seahub.views import get_system_default_repo_id
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
logger = logging.getLogger(__name__)
class AdminLibraryDirents(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def get(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not can_view_sys_admin_repo(repo):
error_msg = 'Feature disabled.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
parent_dir = request.GET.get('parent_dir', '/')
if not parent_dir:
error_msg = 'parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if parent_dir[-1] != '/':
parent_dir = parent_dir + '/'
dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir)
if not dir_id:
error_msg = 'Folder %s not found.' % parent_dir
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# TODO org?
if is_org_context(request):
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
try:
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo_id,
parent_dir, dir_id, repo_owner, -1, -1)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return_results = {}
return_results['repo_name'] = repo.repo_name
return_results['repo_id'] = repo.repo_id
return_results['is_system_library'] = True if \
repo.id == get_system_default_repo_id() else False
return_results['dirent_list'] = []
for dirent in dirs:
result = {}
if stat.S_ISDIR(dirent.mode):
result['is_file'] = False
result['obj_name'] = dirent.obj_name
result['file_size'] = ''
result['last_update'] = timestamp_to_isoformat_timestr(dirent.mtime)
else:
if repo.version == 0:
size = seafile_api.get_file_size(repo.store_id,
repo.version, dirent.obj_id)
else:
size = dirent.size
result['is_file'] = True
result['obj_name'] = dirent.obj_name
result['file_size'] = filesizeformat(size)
result['last_update'] = timestamp_to_isoformat_timestr(dirent.mtime)
return_results['dirent_list'].append(result)
return Response(return_results)
def post(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not can_view_sys_admin_repo(repo):
error_msg = 'Feature disabled.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
parent_dir = request.GET.get('parent_dir', '/')
if not parent_dir:
error_msg = 'parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if parent_dir[-1] != '/':
parent_dir = parent_dir + '/'
dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir)
if not dir_id:
error_msg = 'Folder %s not found.' % parent_dir
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
obj_name = request.data.get('obj_name', None)
if not obj_name or not seafile_api.is_valid_filename(repo_id, obj_name):
error_msg = 'obj_name invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
obj_name = check_filename_with_rename(repo_id, parent_dir, obj_name)
username = request.user.username
try:
seafile_api.post_dir(repo_id, parent_dir, obj_name, username)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
result = {}
result['is_file'] = False
result['obj_name'] = obj_name
result['file_size'] = filesizeformat(0)
result['last_update'] = timestamp_to_isoformat_timestr(time.time())
return Response(result)
class AdminLibraryDirent(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def get(self, request, repo_id):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not can_view_sys_admin_repo(repo):
error_msg = 'Feature disabled.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
path = request.GET.get('path', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if path[0] != '/':
path = '/' + path
file_id = None
dir_id = None
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if file_id:
is_file = True
elif dir_id:
is_file = False
else:
error_msg = 'path %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
username = request.user.username
obj_name = os.path.basename(path.rstrip('/'))
if is_file and request.GET.get('dl', '0') == '1':
token = seafile_api.get_fileserver_access_token(repo_id,
file_id, 'download', username, use_onetime=True)
dl_url = gen_file_get_url(token, obj_name)
send_file_access_msg(request, repo, path, 'web')
return Response({'download_url': dl_url})
size = seafile_api.get_file_size(repo.store_id,
repo.version, file_id or dir_id)
result = {}
result['is_file'] = is_file
result['obj_name'] = obj_name
result['file_size'] = filesizeformat(size)
# TODO
# result['last_update'] = timestamp_to_isoformat_timestr(dirent.mtime)
return Response(result)
def delete(self, request, repo_id):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not can_view_sys_admin_repo(repo):
error_msg = 'Feature disabled.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
path = request.GET.get('path', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if path[0] != '/':
path = '/' + path
file_id = None
dir_id = None
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if not file_id and not dir_id:
return Response({'success': True})
parent_dir = os.path.dirname(path)
file_name = os.path.basename(path)
try:
seafile_api.del_file(repo_id,
parent_dir, file_name, request.user.username)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})

View File

@@ -0,0 +1,39 @@
import logging
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from seaserv import seafile_api
from seahub.views import get_system_default_repo_id
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
logger = logging.getLogger(__name__)
class AdminSystemLibrary(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def get(self, request, format=None):
try:
repo = seafile_api.get_repo(get_system_default_repo_id())
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
result = {}
result['name'] = repo.repo_name
result['id'] = repo.repo_id
result['description'] = repo.desc
return Response(result)

View File

@@ -0,0 +1,76 @@
import logging
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from seaserv import seafile_api
from pysearpc import SearpcError
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
logger = logging.getLogger(__name__)
class AdminTrashLibraries(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def get(self, request, format=None):
repos_all = seafile_api.get_trash_repo_list(-1, -1)
return_results = []
for repo in repos_all:
result = {}
result['name'] = repo.repo_name
result['id'] = repo.repo_id
result['owner'] = repo.owner_id
result['delete_time'] = timestamp_to_isoformat_timestr(repo.del_time)
return_results.append(result)
return Response(return_results)
def delete(self, request, format=None):
try:
seafile_api.empty_repo_trash()
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})
class AdminTrashLibrary(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
throttle_classes = (UserRateThrottle,)
permission_classes = (IsAdminUser,)
def put(self, request, repo_id, format=None):
try:
seafile_api.restore_repo_from_trash(repo_id)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})
def delete(self, request, repo_id, format=None):
try:
seafile_api.del_repo_from_trash(repo_id)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})

View File

@@ -14,8 +14,8 @@
<a href="{% url "sys_settings" %}"><span class="sf2-icon-cog2"></span>{% trans "Settings" %}</a>
</li>
{% endif %}
<li class="tab">
<a href="{{ SITE_ROOT }}sys/seafadmin/"><span class="sf2-icon-library"></span>{% trans "Libraries" %}</a>
<li class="tab <% if (cur_tab == 'libraries') { %> tab-cur<% } %>">
<a href="{{ SITE_ROOT }}sysadmin/#libraries/"><span class="sf2-icon-library"></span>{% trans "Libraries" %}</a>
</li>
<li class="tab">
<a href="{{ SITE_ROOT }}sys/useradmin/"><span class="sf2-icon-user"></span>{% trans "Users" %}</a>
@@ -117,8 +117,8 @@
</dl>
</script>
<script type="text/template" id="admin-devices-tmpl">
<div class="hd ovhd">
<script type="text/template" id="devices-tmpl">
<div class="hd">
<ul class="tab-tabs-nav fleft">
<li class="tab <% if (cur_tab == 'desktop') { %> ui-state-active <% } %>">
<a href="#desktop-devices/" class="a">{% trans "Desktop" %}</a>
@@ -157,7 +157,7 @@
</div>
</script>
<script type="text/template" id="admin-device-item-tmpl">
<script type="text/template" id="device-item-tmpl">
<td><%- user %></td>
<td><%- platform %> / <%- client_version %></td>
<td><%- device_name %></td>
@@ -170,8 +170,8 @@
</td>
</script>
<script type="text/template" id="admin-device-errors-tmpl">
<div class="hd ovhd">
<script type="text/template" id="device-errors-tmpl">
<div class="hd">
<ul class="tab-tabs-nav fleft">
<li class="tab <% if (cur_tab == 'desktop') { %> ui-state-active <% } %>">
<a href="#desktop-devices/" class="a">{% trans "Desktop" %}</a>
@@ -205,7 +205,7 @@
</div>
</script>
<script type="text/template" id="admin-device-error-tmpl">
<script type="text/template" id="device-error-item-tmpl">
<td>
<a href="{{ SITE_ROOT }}useradmin/info/<%- email %>/"><%- email %></a>
</td>
@@ -249,3 +249,226 @@
</p>
<% } %>
</script>
<script type="text/template" id="libraries-tmpl">
<div class="hd">
<ul class="tab-tabs-nav fleft">
<li class="tab <% if (cur_tab == 'all') { %> ui-state-active <% } %>">
<a href="#libraries/" class="a">{% trans "All" %}</a>
</li>
<li class="tab <% if (cur_tab == 'system') { %> ui-state-active <% } %>">
<a href="#libraries/system/" class="a">{% trans "System" %}</a>
</li>
<li class="tab <% if (cur_tab == 'trash') { %> ui-state-active <% } %>">
<a href="#libraries/trash/" class="a">{% trans "Trash" %}</a>
</li>
</ul>
</div>
<table class="hide">
<thead>
<tr>
<th width="4%"><!--icon--></th>
<th width="27%">{% trans "Name" %}</th>
<th width="10%"></th>
<th width="17%">{% trans "Files / Size" %}</th>
<th width="21%">ID</th>
<th width="21%">{% trans "Owner" %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<span class="loading-icon loading-tip"></span>
<div class="empty-tips hide">
<h2 class="alc">{% trans "No libraries" %}</h2>
</div>
<div id="paginator">
<a class="js-previous hide" href="#">{% trans "Previous" %}</a>
<a class="js-next hide" href="#">{% trans "Next" %}</a>
</div>
</script>
<script type="text/template" id="library-item-tmpl">
<% if (encrypted) { %>
<td><img src="{{MEDIA_URL}}img/sync-folder-encrypt-20.png" title="{% trans "Encrypted"%}" alt="{% trans "directory icon" %}" /></td>
<% } else { %>
<td><img src="{{MEDIA_URL}}img/sync-folder-20.png?" title="{% trans "Read-Write"%}" alt="{% trans "directory icon" %}" /></td>
<% } %>
<% if (name) { %>
<% if (enable_sys_admin_view_repo && is_pro && !encrypted) { %>
<td><a href="#libraries/<%- id %>/dirents/"><%- name %></a></td>
<% } else { %>
<td><%- name %></td>
<% } %>
<% } else { %>
<td>--</td>
<% } %>
<td>
<a href="#" class="sf2-icon-delete sf2-x repo-delete-btn op-icon vh" title="{% trans "Delete" %}" aria-label="{% trans "Delete" %}"></a>
<a href="#" class="sf2-icon-move sf2-x repo-transfer-btn op-icon vh" title="{% trans "Transfer" %}" aria-label="{% trans "Transfer" %}"></a>
</td>
<td><%- file_count %> / <%- size_formatted %></td>
<td style="font-size:11px;"><%- id %></td>
<td>
<% if (owner) { %>
<a href="{{ SITE_ROOT }}useradmin/info/<%- owner %>/"><%- owner %></a>
<% } else { %>
--
<% } %>
</td>
</script>
<script type="text/template" id="system-library-tmpl">
<div class="hd">
<ul class="tab-tabs-nav fleft">
<li class="tab <% if (cur_tab == 'all') { %> ui-state-active <% } %>">
<a href="#libraries/" class="a">{% trans "All" %}</a>
</li>
<li class="tab <% if (cur_tab == 'system') { %> ui-state-active <% } %>">
<a href="#libraries/system/" class="a">{% trans "System" %}</a>
</li>
<li class="tab <% if (cur_tab == 'trash') { %> ui-state-active <% } %>">
<a href="#libraries/trash/" class="a">{% trans "Trash" %}</a>
</li>
</ul>
</div>
<table class="hide">
<thead>
<tr>
<th width="25%">{% trans "Name" %}</th>
<th width="35%">ID</th>
<th width="40%">{% trans "Description" %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<span class="loading-icon loading-tip"></span>
</script>
<script type="text/template" id="system-library-item-tmpl">
<td><a href="#libraries/<%- id %>/dirents/"><%- name %></a></td>
<td style="font-size:11px;"><%- id %></td>
<td><%- description %></td>
</script>
<script type="text/template" id="trash-libraries-tmpl">
<div class="hd">
<ul class="tab-tabs-nav fleft">
<li class="tab <% if (cur_tab == 'all') { %> ui-state-active <% } %>">
<a href="#libraries/" class="a">{% trans "All" %}</a>
</li>
<li class="tab <% if (cur_tab == 'system') { %> ui-state-active <% } %>">
<a href="#libraries/system/" class="a">{% trans "System" %}</a>
</li>
<li class="tab <% if (cur_tab == 'trash') { %> ui-state-active <% } %>">
<a href="#libraries/trash/" class="a">{% trans "Trash" %}</a>
</li>
</ul>
<button id="clean-trash-libraries" class="fright"><span class="vam">{% trans "Clean" %}</span></button>
</div>
<p class="tip">{% trans "Tip: libraries deleted 30 days ago will be cleaned automatically."%}</p>
<table class="hide">
<thead>
<tr>
<th width="4%"><!--icon--></th>
<th width="30%">{% trans "Name" %}</th>
<th width="30%">{% trans "Owner" %}</th>
<th width="20%">{% trans "Deleted Time" %}</th>
<th width="16%">{% trans "Operations" %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<span class="loading-icon loading-tip"></span>
<div class="empty-tips hide">
<h2 class="alc">{% trans "No libraries" %}</h2>
</div>
</script>
<script type="text/template" id="trash-library-item-tmpl">
<td><img src="{{MEDIA_URL}}img/sync-folder-20.png?t=1387267140" title="{% trans "Read-Write"%}" alt="{% trans "directory icon" %}" /></td>
<td><%- name %></td>
<td><a href="{{ SITE_ROOT }}useradmin/info/<%- owner %>/"><%- owner %></a></td>
<td><time title='<%- time %>'><%- time_from_now %></time></td>
<td>
<a href="#" class="sf2-icon-delete sf2-x repo-delete-btn op-icon vh" title="{% trans "Delete" %}" aria-label="{% trans "Delete" %}"></a>
<a href="#" class="sf2-icon-reply sf2-x repo-restore-btn op-icon vh" title="{% trans "Restore" %}" aria-label="{% trans "Restore" %}"></a>
</td>
</script>
<script type="text/template" id="dir-view-tmpl">
<div class="repo-file-list-topbar">
<ol class="path-bar breadcrumb"></ol>
<div class="repo-op"></div>
</div>
<div class="js-dir-content">
<table class="repo-file-list">
<thead>
<tr>
<th width="4%"><!--icon--></th>
<th width="61%">{% trans "Name" %}</th>
<th width="10%"></th>
<th width="10%">{% trans "Size" %}</th>
<th width="15%">{% trans "Last Update" %}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<span class="loading-icon loading-tip"></span>
<p class="error hide"></p>
</script>
<script type="text/template" id="dir-path-bar-tmpl">
<li><a href="#libraries/" class="path-link normal">{% trans "Libraries" %}</a></li>
<% if (path == '/') { %>
<li><%- repo_name %></li>
<% } else { %>
<li><a href="#libraries/<%= repo_id %>/dirents" class="path-link normal"><%- repo_name %></a></li>
<% for (var i = 0, len = path_list.length - 1; i < len; i++) { %>
<li><a href="#libraries/<%= repo_id %>/dirents/<% print(path_list_encoded.slice(0, i+1).join('/')); %>" class="path-link normal"><%- path_list[i] %></a></li>
<% } %>
<li><%- path_list[i] %></li>
<% } %>
</script>
<script type="text/template" id="dir-op-bar-tmpl">
<% if (is_system_library) { %>
<div id="basic-upload" class="inline-block">
<button class="op-btn basic-upload-btn" >{% trans "Upload" %}</button>
<input id="basic-upload-input" class="hide" type="file" name="file" multiple />
</div>
<button id="add-new-dir" class="op-btn">{% trans "New Folder" %}</button>
<% } %>
</script>
<script type="text/template" id="dirent-item-tmpl">
<td><img src="<%= icon_url %>" width="24" alt="" /></td>
<% if (is_file) { %>
<td><%- obj_name %></td>
<% } else { %>
<td><span><a href="<%- url %>"><%- obj_name %></a></span></td>
<% } %>
<td>
<% if (is_system_library) { %>
<a href="#" class="sf2-icon-delete sf2-x dirent-delete-btn op-icon vh" title="{% trans "Delete" %}" aria-label="{% trans "Delete" %}"></a>
<% } %>
<% if (is_file) { %>
<a href="#" class="sf2-icon-download sf2-x dirent-download-btn op-icon vh" title="{% trans "Download" %}" aria-label="{% trans "Download" %}"></a>
<% } %>
</td>
<td><%- file_size %></td>
<td><time title="<%- time %>"><%- time_from_now %></time></td>
</script>
<script type="text/template" id="library-transfer-form-tmpl">
<form method="" action="" id="library-transfer-form">
<h3><%= title %></h3>
<input type="hidden" name="email" /><br />
<p class="error hide"></p>
<input type="submit" value="{% trans "Submit" %}" />
</form>
</script>

View File

@@ -23,7 +23,7 @@
</li>
{% endif %}
<li class="tab {% block cur_repo %}{% endblock %}">
<a href="{{ SITE_ROOT }}sys/seafadmin/"><span class="sf2-icon-library"></span>{% trans "Libraries" %}</a>
<a href="{{ SITE_ROOT }}sysadmin/#libraries/"><span class="sf2-icon-library"></span>{% trans "Libraries" %}</a>
</li>
<li class="tab {% block cur_users %}{% endblock %}">
<a href="{{ SITE_ROOT }}sys/useradmin/"><span class="sf2-icon-user"></span>{% trans "Users" %}</a>

View File

@@ -79,6 +79,8 @@
{% include 'js/sysadmin-templates.html' %}
</div><!-- wrapper -->
{% include "js/dir-view.html" %}
{% include "js/lib-op-popups.html" %}
<script type="text/javascript">
var app = {
config: {
@@ -103,6 +105,7 @@ app["pageOptions"] = {
max_upload_file_size: {% if max_upload_file_size %} {{ max_upload_file_size }} {% else %} '' {% endif %},
folder_perm_enabled: {% if folder_perm_enabled %} true {% else %} false {% endif %},
is_pro: {% if is_pro %} true {% else %} false {% endif %},
enable_sys_admin_view_repo: {% if enable_sys_admin_view_repo %} true {% else %} false {% endif %},
file_audit_enabled: {% if file_audit_enabled %} true {% else %} false {% endif %},
cur_note: {% if request.cur_note %} {'id': '{{ request.cur_note.id }}'} {% else %} null {% endif %}
};

View File

@@ -32,6 +32,10 @@ from seahub.api2.endpoints.admin.perm_audit import PermAudit
from seahub.api2.endpoints.admin.sysinfo import SysInfo
from seahub.api2.endpoints.admin.devices import AdminDevices
from seahub.api2.endpoints.admin.device_errors import AdminDeviceErrors
from seahub.api2.endpoints.admin.libraries import AdminLibraries, AdminLibrary
from seahub.api2.endpoints.admin.library_dirents import AdminLibraryDirents, AdminLibraryDirent
from seahub.api2.endpoints.admin.system_library import AdminSystemLibrary
from seahub.api2.endpoints.admin.trash_libraries import AdminTrashLibraries, AdminTrashLibrary
# Uncomment the next two lines to enable the admin:
#from django.contrib import admin
@@ -196,6 +200,14 @@ urlpatterns = patterns(
url(r'^api/v2.1/admin/devices/$', AdminDevices.as_view(), name='api-v2.1-admin-devices'),
url(r'^api/v2.1/admin/device-errors/$', AdminDeviceErrors.as_view(), name='api-v2.1-admin-device-errors'),
url(r'^api/v2.1/admin/libraries/$', AdminLibraries.as_view(), name='api-v2.1-admin-libraries'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminLibrary.as_view(), name='api-v2.1-admin-library'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirents/$', AdminLibraryDirents.as_view(), name='api-v2.1-admin-library-dirents'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirent/$', AdminLibraryDirent.as_view(), name='api-v2.1-admin-library-dirent'),
url(r'^api/v2.1/admin/system-library/$', AdminSystemLibrary.as_view(), name='api-v2.1-admin-system-library'),
url(r'^api/v2.1/admin/trash-libraries/$', AdminTrashLibraries.as_view(), name='api-v2.1-admin-trash-libraries'),
url(r'^api/v2.1/admin/trash-libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminTrashLibrary.as_view(), name='api-v2.1-admin-trash-library'),
(r'^avatar/', include('seahub.avatar.urls')),
(r'^notification/', include('seahub.notifications.urls')),
(r'^contacts/', include('seahub.contacts.urls')),

View File

@@ -78,6 +78,7 @@ def sysadmin(request):
folder_perm_enabled = True if is_pro_version() and settings.ENABLE_FOLDER_PERM else False
return render_to_response('sysadmin/sysadmin_backbone.html', {
'enable_sys_admin_view_repo': ENABLE_SYS_ADMIN_VIEW_REPO,
'enable_upload_folder': settings.ENABLE_UPLOAD_FOLDER,
'enable_resumable_fileupload': settings.ENABLE_RESUMABLE_FILEUPLOAD,
'enable_thumbnail': settings.ENABLE_THUMBNAIL,

View File

@@ -166,6 +166,12 @@ define([
case 'sysinfo': return siteRoot + 'api/v2.1/admin/sysinfo/';
case 'admin-devices': return siteRoot + 'api/v2.1/admin/devices/';
case 'admin-device-errors': return siteRoot + 'api/v2.1/admin/device-errors/';
case 'admin-libraries': return siteRoot + 'api/v2.1/admin/libraries/';
case 'admin-library': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/';
case 'admin-library-dirents': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/dirents/';
case 'admin-system-library': return siteRoot + 'api/v2.1/admin/system-library/';
case 'admin-trash-libraries': return siteRoot + 'api/v2.1/admin/trash-libraries/';
case 'admin-trash-library': return siteRoot + 'api/v2.1/admin/trash-libraries/' + options.repo_id + '/';
}
},

View File

@@ -1,17 +1,16 @@
define([
'underscore',
'backbone',
'backbone.paginator',
'common',
'sysadmin-app/models/device'
], function(_, Backbone, BackbonePaginator, Common, Device) {
], function(_, BackbonePaginator, Common, DeviceModel) {
'use strict';
var DeviceCollection = Backbone.PageableCollection.extend({
model: Device,
state: {pageSize: 50,},
model: DeviceModel,
state: {pageSize: 50},
parseState: function(data) {
return {hasNextPage: data[0].has_next_page};
return {hasNextPage: data[0].has_next_page, current_page: data[0].current_page};
},
url: function () {
return Common.getUrl({name: 'admin-devices'});

View File

@@ -0,0 +1,24 @@
define([
'underscore',
'backbone.paginator',
'common',
'sysadmin-app/models/library'
], function(_, BackbonePaginator, Common, LibraryModel) {
'use strict';
var LibraryCollection = Backbone.PageableCollection.extend({
model: LibraryModel,
state: {pageSize: 100},
parseState: function(data) {
return {hasNextPage: data[0].has_next_page, current_page: data[0].current_page};
},
parseRecords: function(data) {
return data[1];
},
url: function () {
return Common.getUrl({name: 'admin-libraries'});
}
});
return LibraryCollection;
});

View File

@@ -0,0 +1,24 @@
define([
'underscore',
'backbone',
'common',
'sysadmin-app/models/library-dirent'
], function(_, Backbone, Common, LibraryDirentModel) {
'use strict';
var LibraryDirentCollection = Backbone.Collection.extend({
model: LibraryDirentModel,
parse: function (data) {
this.repo_name = data.repo_name;
this.repo_id = data.repo_id;
this.is_system_library = data.is_system_library;
return data.dirent_list; // return the array
},
setPath: function(repo_id, path) {
this.repo_id = repo_id;
this.path = path;
}
});
return LibraryDirentCollection;
});

View File

@@ -0,0 +1,17 @@
define([
'underscore',
'backbone',
'common',
'sysadmin-app/models/trash-library'
], function(_, Backbone, Common, TrashLibraryModel) {
'use strict';
var TrashLibraryCollection = Backbone.Collection.extend({
model: TrashLibraryModel,
url: function () {
return Common.getUrl({name: 'admin-trash-libraries'});
}
});
return TrashLibraryCollection;
});

View File

@@ -0,0 +1,54 @@
define([
'underscore',
'backbone',
'common',
], function(_, Backbone, Common) {
'use strict';
var LibraryDirentModel = Backbone.Model.extend({
// get the absolute path within the library
getPath: function() {
return Common.pathJoin([this.collection.path, this.get('obj_name')]);
},
// return the URL to visit the folder or file
getWebUrl: function() {
var dir = this.collection;
var dirent_path = this.getPath();
if (this.get('is_file')) {
return '#';
} else {
return '#libraries/' + dir.repo_id + '/dirents' + Common.encodePath(dirent_path);
}
},
getDownloadUrl: function() {
var dir = this.collection;
var dirent_path = this.getPath();
return app.config.siteRoot + "api/v2.1/admin/libraries/" + dir.repo_id
+ "/dirent/?path=" + Common.encodePath(dirent_path) + "&dl=1";
},
getDeleteUrl: function() {
var dir = this.collection;
var dirent_path = this.getPath();
return app.config.siteRoot + "api/v2.1/admin/libraries/" + dir.repo_id
+ "/dirent/?path=" + Common.encodePath(dirent_path);
},
getIconUrl: function(size) {
if (this.get('is_file')) {
return Common.getFileIconUrl(this.get('obj_name'), size);
} else {
var is_readonly = this.get('perm') == 'r';
return Common.getDirIconUrl(is_readonly, size);
}
}
});
return LibraryDirentModel;
});

View File

@@ -0,0 +1,11 @@
define([
'underscore',
'backbone',
'common',
], function(_, Backbone, Common) {
'use strict';
var LibraryModel = Backbone.Model.extend({});
return LibraryModel;
});

View File

@@ -0,0 +1,11 @@
define([
'underscore',
'backbone',
'common'
], function(_, Backbone, Common) {
'use strict';
var SystemLibrary = Backbone.Model.extend({});
return SystemLibrary;
});

View File

@@ -0,0 +1,11 @@
define([
'underscore',
'backbone',
'common'
], function(_, Backbone, Common) {
'use strict';
var TrashLibrary = Backbone.Model.extend({});
return TrashLibrary;
});

View File

@@ -8,10 +8,16 @@ define([
'sysadmin-app/views/desktop-devices',
'sysadmin-app/views/mobile-devices',
'sysadmin-app/views/device-errors',
'sysadmin-app/views/libraries',
'sysadmin-app/views/library-dir',
'sysadmin-app/views/system-library',
'sysadmin-app/views/trash-libraries',
'app/views/account'
], function($, Backbone, Common, SideNavView, DashboardView,
DesktopDevicesView, MobileDevicesView, DeviceErrorsView,
AccountView) {
LibrariesView, LibraryDirView, SystemLibrariesView,
TrashLibrariesView, AccountView) {
"use strict";
var Router = Backbone.Router.extend({
@@ -21,6 +27,10 @@ define([
'desktop-devices/': 'showDesktopDevices',
'mobile-devices/': 'showMobileDevices',
'device-errors/': 'showDeviceErrors',
'libraries/': 'showLibraries',
'libraries/:repo_id/dirents(/*path)': 'showLibraryDir',
'libraries/system/': 'showSystemLibrary',
'libraries/trash/': 'showTrashLibraries',
// Default
'*actions': 'showDashboard'
},
@@ -40,6 +50,10 @@ define([
this.desktopDevicesView = new DesktopDevicesView();
this.mobileDevicesView = new MobileDevicesView();
this.deviceErrorsView = new DeviceErrorsView();
this.librariesView = new LibrariesView();
this.systemLibrariesView = new SystemLibrariesView();
this.trashLibrariesView = new TrashLibrariesView();
this.libraryDirView = new LibraryDirView();
app.ui.accountView = this.accountView = new AccountView();
@@ -65,9 +79,9 @@ define([
var url = window.location.href;
var page = url.match(/.*?page=(\d+)/);
if (page) {
current_page = page[1];
var current_page = page[1];
} else {
current_page = null;
var current_page = null;
}
this.switchCurrentView(this.desktopDevicesView);
this.sideNavView.setCurTab('devices');
@@ -91,6 +105,42 @@ define([
this.switchCurrentView(this.deviceErrorsView);
this.sideNavView.setCurTab('devices');
this.deviceErrorsView.show();
},
showLibraries: function() {
var url = window.location.href;
var page = url.match(/.*?page=(\d+)/);
if (page) {
var current_page = page[1];
} else {
var current_page = null;
}
this.switchCurrentView(this.librariesView);
this.sideNavView.setCurTab('libraries');
this.librariesView.show({'current_page': current_page});
},
showLibraryDir: function(repo_id, path) {
if (path) {
path = '/' + path;
} else {
path = '/';
}
this.switchCurrentView(this.libraryDirView);
this.libraryDirView.show(repo_id, path);
this.sideNavView.setCurTab('libraries');
},
showSystemLibrary: function() {
this.switchCurrentView(this.systemLibrariesView);
this.sideNavView.setCurTab('libraries');
this.systemLibrariesView.show();
},
showTrashLibraries: function() {
this.switchCurrentView(this.trashLibrariesView);
this.sideNavView.setCurTab('libraries');
this.trashLibrariesView.show();
}
});

View File

@@ -6,19 +6,20 @@ define([
'moment',
'sysadmin-app/views/device',
'sysadmin-app/collection/devices'
], function($, _, Backbone, Common, Moment, Device, DevicesCollection) {
], function($, _, Backbone, Common, Moment, Device, DeviceCollection) {
'use strict';
var DevicesView = Backbone.View.extend({
id: 'admin-devices',
template: _.template($("#admin-devices-tmpl").html()),
template: _.template($("#devices-tmpl").html()),
initialize: function() {
this.deviceCollection = new DevicesCollection();
this.deviceCollection = new DeviceCollection();
this.listenTo(this.deviceCollection, 'add', this.addOne);
this.listenTo(this.deviceCollection, 'reset', this.reset);
this.render();
},
render: function() {
@@ -47,23 +48,22 @@ define([
getNextPage: function() {
this.initPage();
var current_page = this.deviceCollection.state.current_page;
if (this.deviceCollection.state.hasNextPage) {
var _this = this;
this.deviceCollection.getNextPage({
reset:true,
this.deviceCollection.getPage(current_page + 1, {
reset: true,
data: {'platform': 'desktop'},
});
}
return false;
},
getPreviousPage: function() {
this.initPage();
if (this.deviceCollection.state.currentPage > 1) {
var _this = this;
this.deviceCollection.getPreviousPage({
reset:true,
var current_page = this.deviceCollection.state.current_page;
if (current_page > 1) {
this.deviceCollection.getPage(current_page - 1, {
reset: true,
data: {'platform': 'desktop'},
});
}
@@ -72,19 +72,22 @@ define([
hide: function() {
this.$el.detach();
this.attached = false;
},
show: function(option) {
this.option = option;
this.render();
if (!this.attached) {
this.attached = true;
$("#right-panel").html(this.$el);
}
this.showDesktopDevices();
},
showDesktopDevices: function() {
this.initPage();
var _this = this,
current_page = this.option.current_page || this.deviceCollection.state.currentPage;
current_page = this.option.current_page || 1;
this.deviceCollection.fetch({
data: {'platform': 'desktop', 'page': current_page},
@@ -108,18 +111,18 @@ define([
reset: function() {
var length = this.deviceCollection.length,
current_page = this.option.current_page || this.deviceCollection.state.currentPage;
current_page = this.deviceCollection.state.current_page;
this.$loadingTip.hide();
if (length > 0) {
this.deviceCollection.each(this.addOne, this);
this.$table.show();
this.renderPaginator();
} else {
this.$emptyTip.show();
}
this.renderPaginator();
app.router.navigate('desktop-devices/?page=' + current_page);
},
@@ -135,8 +138,8 @@ define([
this.$jsNext.hide();
}
var current_page = this.option.current_page || this.deviceCollection.state.currentPage;
if (current_page > 1 && this.deviceCollection.length > 0) {
var current_page = this.deviceCollection.state.current_page;
if (current_page > 1) {
this.$jsPrevious.show();
} else {
this.$jsPrevious.hide();
@@ -146,5 +149,4 @@ define([
});
return DevicesView;
});

View File

@@ -11,7 +11,7 @@ define([
var DeviceErrorView = HLItemView.extend({
tagName: 'tr',
template: _.template($('#admin-device-error-tmpl').html()),
template: _.template($('#device-error-item-tmpl').html()),
initialize: function() {
HLItemView.prototype.initialize.call(this);

View File

@@ -5,19 +5,19 @@ define([
'common',
'sysadmin-app/views/device-error',
'sysadmin-app/collection/device-errors'
], function($, _, Backbone, Common, DeviceError, DeviceErrorsCollection) {
], function($, _, Backbone, Common, DeviceError, DeviceErrorCollection) {
'use strict';
var DeviceErrorsView = Backbone.View.extend({
id: 'admin-device-errors',
template: _.template($("#admin-device-errors-tmpl").html()),
template: _.template($("#device-errors-tmpl").html()),
initialize: function() {
this.deviceErrorsCollection = new DeviceErrorsCollection();
this.listenTo(this.deviceErrorsCollection, 'add', this.addOne);
this.listenTo(this.deviceErrorsCollection, 'reset', this.reset);
this.deviceErrorCollection = new DeviceErrorCollection();
this.listenTo(this.deviceErrorCollection, 'add', this.addOne);
this.listenTo(this.deviceErrorCollection, 'reset', this.reset);
this.render();
},
@@ -35,7 +35,7 @@ define([
_this.$table.hide();
_this.$cleanBtn.hide();
_this.$emptyTip.show();
_this.deviceErrorsCollection.reset();
_this.deviceErrorCollection.reset();
var msg = gettext("Successfully clean all errors.");
Common.feedback(msg, 'success');
},
@@ -75,7 +75,7 @@ define([
this.initPage();
var _this = this;
this.deviceErrorsCollection.fetch({
this.deviceErrorCollection.fetch({
cache: false, // for IE
reset: true,
success: function (collection, response, opts) {
@@ -98,8 +98,8 @@ define([
reset: function() {
this.$loadingTip.hide();
if (this.deviceErrorsCollection.length) {
this.deviceErrorsCollection.each(this.addOne, this);
if (this.deviceErrorCollection.length) {
this.deviceErrorCollection.each(this.addOne, this);
this.$table.show();
this.$cleanBtn.show();
} else {

View File

@@ -11,7 +11,7 @@ define([
var DeviceView = HLItemView.extend({
tagName: 'tr',
template: _.template($('#admin-device-item-tmpl').html()),
template: _.template($('#device-item-tmpl').html()),
events: {
'click .unlink-device': 'unlinkDevice'

View File

@@ -0,0 +1,152 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'sysadmin-app/views/library',
'sysadmin-app/collection/libraries'
], function($, _, Backbone, Common, Moment, LibraryView, LibraryCollection) {
'use strict';
var LibrariesView = Backbone.View.extend({
id: 'admin-libraries',
template: _.template($("#libraries-tmpl").html()),
initialize: function() {
this.libraryCollection = new LibraryCollection();
this.listenTo(this.libraryCollection, 'add', this.addOne);
this.listenTo(this.libraryCollection, 'reset', this.reset);
this.render();
},
render: function() {
var data = {'cur_tab': 'all',};
this.$el.html(this.template(data));
this.$table = this.$('table');
this.$tableBody = $('tbody', this.$table);
this.$loadingTip = this.$('.loading-tip');
this.$emptyTip = this.$('.empty-tips');
this.$jsPrevious = this.$('.js-previous');
this.$jsNext = this.$('.js-next');
},
events: {
'click #paginator .js-next': 'getNextPage',
'click #paginator .js-previous': 'getPreviousPage'
},
initPage: function() {
this.$table.hide();
this.$tableBody.empty();
this.$loadingTip.show();
this.$emptyTip.hide();
this.$jsNext.hide();
this.$jsPrevious.hide();
},
getNextPage: function() {
this.initPage();
var current_page = this.libraryCollection.state.current_page;
if (this.libraryCollection.state.hasNextPage) {
this.libraryCollection.getPage(current_page + 1, {
reset: true
});
}
return false;
},
getPreviousPage: function() {
this.initPage();
var current_page = this.libraryCollection.state.current_page;
if ( current_page > 1) {
this.libraryCollection.getPage(current_page - 1, {
reset: true
});
}
return false;
},
hide: function() {
this.$el.detach();
this.attached = false;
},
show: function(option) {
this.option = option;
if (!this.attached) {
this.attached = true;
$("#right-panel").html(this.$el);
}
this.showLibraries();
},
showLibraries: function() {
this.initPage();
var _this = this,
current_page = this.option.current_page || 1;
this.libraryCollection.fetch({
data: {'page': current_page},
cache: false, // for IE
reset: true,
error: function (collection, response, opts) {
var err_msg;
if (response.responseText) {
if (response['status'] == 401 || response['status'] == 403) {
err_msg = gettext("Permission error");
} else {
err_msg = $.parseJSON(response.responseText).error_msg;
}
} else {
err_msg = gettext("Failed. Please check the network.");
}
Common.feedback(err_msg, 'error');
}
});
},
reset: function() {
var length = this.libraryCollection.length,
current_page = this.libraryCollection.state.current_page;
this.$loadingTip.hide();
if (length > 0) {
this.libraryCollection.each(this.addOne, this);
this.$table.show();
this.renderPaginator();
} else {
this.$emptyTip.show();
}
app.router.navigate('libraries/?page=' + current_page);
},
renderPaginator: function() {
if (this.libraryCollection.state.hasNextPage) {
this.$jsNext.show();
} else {
this.$jsNext.hide();
}
var current_page = this.libraryCollection.state.current_page;
if (current_page > 1) {
this.$jsPrevious.show();
} else {
this.$jsPrevious.hide();
}
},
addOne: function(library) {
var view = new LibraryView({model: library});
this.$tableBody.append(view.render().el);
}
});
return LibrariesView;
});

View File

@@ -0,0 +1,208 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'app/views/fileupload',
'sysadmin-app/views/library-dirent',
'sysadmin-app/collection/library-dirents'
], function($, _, Backbone, Common, Moment, FileUploadView,
DirentView, DirentCollection) {
'use strict';
var LibraryDirView = Backbone.View.extend({
id: 'dir-view',
template: _.template($('#dir-view-tmpl').html()),
pathBarTemplate: _.template($('#dir-path-bar-tmpl').html()),
newDirTemplate: _.template($("#add-new-dir-form-template").html()),
dir_op_bar_template: _.template($('#dir-op-bar-tmpl').html()),
initialize: function() {
this.dir = new DirentCollection();
this.listenTo(this.dir, 'add', this.addOne);
this.listenTo(this.dir, 'reset', this.reset);
this.fileUploadView = new FileUploadView({dirView: this});
this.render();
},
render: function() {
this.$el.html(this.template());
this.$table = this.$('table');
this.$tableBody = $('tbody', this.$table);
this.$loadingTip = this.$('.loading-tip');
this.$emptyTip = this.$('.empty-tips');
this.$path_bar = this.$('.path-bar');
this.$dir_op_bar = this.$('.repo-op');
},
events: {
'click .basic-upload-btn': 'uploadFile',
'click #add-new-dir': 'newDir',
},
uploadFile: function() {
this.$('#basic-upload-input').trigger('click');
},
addNewFile: function(new_dirent) {
new_dirent.set('last_update', new Date().getTime());
var dirView = this;
var view = new DirentView({model: new_dirent, dirView: dirView});
dirView.$tableBody.prepend(view.render().el);
},
newDir: function() {
var form = $(this.newDirTemplate()),
form_id = form.attr('id'),
dir = this.dir,
dirView = this;
form.modal({appendTo:'#main'});
$('#simplemodal-container').css({'height':'auto'});
form.submit(function() {
var obj_name = $.trim($('input[name="name"]', form).val());
if (!obj_name) {
Common.showFormError(form_id, gettext("It is required."));
return false;
};
var post_data = {'obj_name': obj_name},
post_url = Common.getUrl({name: 'admin-library-dirents', repo_id: dir.repo_id}) + '?parent_dir=' + Common.encodePath(dir.path);
var after_op_success = function(data) {
var new_dirent = dir.add(data, {silent:true});
$.modal.close();
dirView.addNewDir(new_dirent);
};
Common.ajaxPost({
'form': form,
'post_url': post_url,
'post_data': post_data,
'after_op_success': after_op_success,
'form_id': form_id
});
return false;
});
},
addNewDir: function(new_dirent) {
var dirView = this;
var view = new DirentView({model: new_dirent, dirView: dirView});
dirView.$tableBody.prepend(view.render().el);
},
hide: function() {
this.$el.detach();
this.attached = false;
},
show: function(repo_id, path) {
if (!this.attached) {
this.attached = true;
$("#right-panel").html(this.$el);
}
// init collection
this.dir.setPath(repo_id, path);
this.fetchLibraryDirents();
},
initPage: function() {
this.renderPath();
this.renderDirOpBar();
this.setFileInput();
this.$tableBody.empty();
this.$loadingTip.show();
this.$emptyTip.hide();
},
renderPath: function() {
var dir = this.dir;
var path = dir.path;
var obj = {
path: path,
repo_name: dir.repo_name,
};
var path_list = path.substr(1).split('/');
var path_list_encoded = Common.encodePath(path.substr(1)).split('/');
if (path != '/') {
$.extend(obj, {
path_list: path_list,
path_list_encoded: path_list_encoded,
repo_id: dir.repo_id
});
}
this.$path_bar.html(this.pathBarTemplate(obj));
},
renderDirOpBar: function() {
var data = {
'is_system_library': this.dir.is_system_library,
'encrypted': this.dir.encrypted,
};
this.$dir_op_bar.html(this.dir_op_bar_template(data));
},
setFileInput: function () {
var $popup = this.fileUploadView.$el;
$popup.fileupload(
'option',
'fileInput',
this.$('#basic-upload-input'));
},
fetchLibraryDirents: function() {
var dir = this.dir;
dir.fetch({
url: Common.getUrl({name: 'admin-library-dirents', repo_id: dir.repo_id}) + '?parent_dir=' + Common.encodePath(dir.path),
data: {},
cache: false, // for IE
reset: true,
error: function (collection, response, opts) {
var err_msg;
if (response.responseText) {
if (response['status'] == 401 || response['status'] == 403) {
err_msg = gettext("Permission error");
} else {
err_msg = $.parseJSON(response.responseText).error_msg;
}
} else {
err_msg = gettext("Failed. Please check the network.");
}
Common.feedback(err_msg, 'error');
}
});
},
reset: function() {
this.initPage();
this.$loadingTip.hide();
var length = this.dir.length;
if (length > 0) {
this.dir.each(this.addOne, this);
this.$table.show();
} else {
this.$emptyTip.show();
}
},
addOne: function(dirent) {
var view = new DirentView({model: dirent, dirView: this});
this.$tableBody.append(view.render().el);
}
});
return LibraryDirView;
});

View File

@@ -0,0 +1,80 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'app/views/widgets/hl-item-view'
], function($, _, Backbone, Common, Moment, HLItemView) {
'use strict';
var LibraryDirentView = HLItemView.extend({
tagName: 'tr',
template: _.template($('#dirent-item-tmpl').html()),
events: {
'click .dirent-download-btn': 'downloadDirent',
'click .dirent-delete-btn': 'deleteDirent',
},
initialize: function(options) {
HLItemView.prototype.initialize.call(this);
this.dir = options.dirView.dir;
},
downloadDirent: function() {
var _this = this;
$.ajax({
url: _this.model.getDownloadUrl(),
dataType: 'json',
success: function(data) {
location.href = data['download_url']
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
return false;
},
deleteDirent: function() {
var _this = this;
$.ajax({
url: _this.model.getDeleteUrl(),
type: 'DELETE',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
success: function() {
_this.$el.remove();
Common.feedback(gettext("Success"), 'success');
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
return false;
},
render: function() {
var file_icon_size = Common.isHiDPI() ? 48 : 24;
var data = this.model.toJSON(),
last_update = Moment(data['last_update']);
data['time'] = last_update.format('LLLL');
data['time_from_now'] = Common.getRelativeTimeStr(last_update);
data['icon_url'] = this.model.getIconUrl(file_icon_size);
data['download_url'] = this.model.getDownloadUrl(),
data['repo_id'] = this.dir.repo_id;
data['is_system_library'] = this.dir.is_system_library;
data['url'] = this.model.getWebUrl(),
this.$el.html(this.template(data));
return this;
}
});
return LibraryDirentView;
});

View File

@@ -0,0 +1,125 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'simplemodal',
'select2',
'app/views/widgets/hl-item-view'
], function($, _, Backbone, Common, Moment, Simplemodal,
Select2, HLItemView) {
'use strict';
var LibraryView = HLItemView.extend({
tagName: 'tr',
template: _.template($('#library-item-tmpl').html()),
transferTemplate: _.template($('#library-transfer-form-tmpl').html()),
events: {
'click .repo-delete-btn': 'deleteLibrary',
'click .repo-transfer-btn': 'transferLibrary',
},
initialize: function() {
HLItemView.prototype.initialize.call(this);
this.listenTo(this.model, "change", this.render);
},
deleteLibrary: function() {
var _this = this;
$.ajax({
url: Common.getUrl({'name':'admin-library', 'repo_id': _this.model.get('id')}),
type: 'DELETE',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
success: function() {
_this.$el.remove();
Common.feedback(gettext("Success"), 'success');
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
return false;
},
transferLibrary: function() {
var _this = this;
var repo_name = this.model.get('name');
var $form = $(this.transferTemplate({
title: gettext("Transfer Library {library_name} To").replace('{library_name}',
'<span class="op-target ellipsis ellipsis-op-target" title="' + Common.HTMLescape(repo_name) + '">' + Common.HTMLescape(repo_name) + '</span>')
}));
$form.modal({focus:false});
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
$('[name="email"]', $form).select2($.extend(
Common.contactInputOptionsForSelect2(), {
width: '300px',
maximumSelectionSize: 1,
placeholder: gettext("Search user or enter email and press Enter"), // to override 'placeholder' returned by `Common.conta...`
formatSelectionTooBig: gettext("You cannot select any more choices")
}));
$form.submit(function() {
var email = $.trim($('[name="email"]', $(this)).val());
if (!email) {
return false;
}
if (email == _this.model.get('owner')) {
return false;
}
var url = Common.getUrl({'name': 'admin-library','repo_id': _this.model.get('id')});
var $submitBtn = $('[type="submit"]', $(this));
Common.disableButton($submitBtn);
$.ajax({
url: url,
type: 'put',
dataType: 'json',
beforeSend: Common.prepareCSRFToken,
data: {
'owner': email
},
success: function() {
$.modal.close();
Common.feedback(gettext("Successfully transferred the library."), 'success');
_this.model.set({'owner': email}); // it will trigger 'change' event
},
error: function(xhr) {
var error_msg;
if (xhr.responseText) {
error_msg = $.parseJSON(xhr.responseText).error_msg;
} else {
error_msg = gettext("Failed. Please check the network.");
}
$('.error', $form).html(error_msg).show();
Common.enableButton($submitBtn);
}
});
return false;
});
return false;
},
render: function() {
var data = this.model.toJSON(),
last_accessed = Moment(data['last_accessed']);
data['enable_sys_admin_view_repo'] = app.pageOptions.enable_sys_admin_view_repo;
data['is_pro'] = app.pageOptions.is_pro;
data['time'] = last_accessed.format('LLLL');
data['time_from_now'] = Common.getRelativeTimeStr(last_accessed);
this.$el.html(this.template(data));
return this;
}
});
return LibraryView;
});

View File

@@ -6,19 +6,20 @@ define([
'moment',
'sysadmin-app/views/device',
'sysadmin-app/collection/devices'
], function($, _, Backbone, Common, Moment, Device, DevicesCollection) {
], function($, _, Backbone, Common, Moment, Device, DeviceCollection) {
'use strict';
var DevicesView = Backbone.View.extend({
id: 'admin-devices',
template: _.template($("#admin-devices-tmpl").html()),
template: _.template($("#devices-tmpl").html()),
initialize: function() {
this.deviceCollection = new DevicesCollection();
this.deviceCollection = new DeviceCollection();
this.listenTo(this.deviceCollection, 'add', this.addOne);
this.listenTo(this.deviceCollection, 'reset', this.reset);
this.render();
},
render: function() {
@@ -47,10 +48,10 @@ define([
getNextPage: function() {
this.initPage();
var current_page = this.deviceCollection.state.current_page;
if (this.deviceCollection.state.hasNextPage) {
var _this = this;
this.deviceCollection.getNextPage({
reset:true,
this.deviceCollection.getPage(current_page + 1, {
reset: true,
data: {'platform': 'mobile'},
});
}
@@ -60,10 +61,10 @@ define([
getPreviousPage: function() {
this.initPage();
if (this.deviceCollection.state.currentPage > 1) {
var _this = this;
this.deviceCollection.getPreviousPage({
reset:true,
var current_page = this.deviceCollection.state.current_page;
if (current_page > 1) {
this.deviceCollection.getPage(current_page - 1, {
reset: true,
data: {'platform': 'mobile'},
});
}
@@ -72,19 +73,22 @@ define([
hide: function() {
this.$el.detach();
this.attached = false;
},
show: function(option) {
this.option = option;
this.render();
if (!this.attached) {
this.attached = true;
$("#right-panel").html(this.$el);
}
this.showMobileDevices();
},
showMobileDevices: function() {
this.initPage();
var _this = this,
current_page = this.option.current_page || this.deviceCollection.state.currentPage;
current_page = this.option.current_page || 1;
this.deviceCollection.fetch({
data: {'platform': 'mobile', 'page': current_page},
@@ -108,18 +112,18 @@ define([
reset: function() {
var length = this.deviceCollection.length,
current_page = this.option.current_page || this.deviceCollection.state.currentPage;
current_page = this.deviceCollection.state.current_page;
this.$loadingTip.hide();
if (length > 0) {
this.deviceCollection.each(this.addOne, this);
this.$table.show();
this.renderPaginator();
} else {
this.$emptyTip.show();
}
this.renderPaginator();
app.router.navigate('mobile-devices/?page=' + current_page);
},
@@ -135,8 +139,8 @@ define([
this.$jsNext.hide();
}
var current_page = this.option.current_page || this.deviceCollection.state.currentPage;
if (current_page > 1 && this.deviceCollection.length > 0) {
var current_page = this.deviceCollection.state.current_page;
if (current_page > 1) {
this.$jsPrevious.show();
} else {
this.$jsPrevious.hide();

View File

@@ -0,0 +1,81 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'sysadmin-app/models/system-library'
], function($, _, Backbone, Common, SystemLibrary) {
'use strict';
var SystemLibraryView = Backbone.View.extend({
id: "admin-system-library",
template: _.template($("#system-library-tmpl").html()),
itemTemplate: _.template($("#system-library-item-tmpl").html()),
initialize: function() {
this.systemLibrary = new SystemLibrary();
this.render();
},
render: function() {
var data = {'cur_tab': 'system',};
this.$el.html(this.template(data));
this.$table = this.$('table');
this.$tableBody = $('tbody', this.$table);
this.$loadingTip = this.$('.loading-tip');
},
initPage: function() {
this.$table.hide();
this.$tableBody.empty();
this.$loadingTip.show();
},
showSystemLibrary: function() {
this.initPage();
var _this = this;
this.systemLibrary.fetch({
url: Common.getUrl({name: 'admin-system-library'}),
cache: false, // for IE
reset: true,
success: function(model, response, options) {
_this.reset();
},
error: function(model, response, options) {
var err_msg;
if (response.responseText) {
if (response['status'] == 401 || response['status'] == 403) {
err_msg = gettext("Permission error");
} else {
err_msg = $.parseJSON(response.responseText).error_msg;
}
} else {
err_msg = gettext("Failed. Please check the network.");
}
Common.feedback(err_msg, 'error');
}
});
},
hide: function() {
this.$el.detach();
},
show: function() {
$("#right-panel").html(this.$el);
this.showSystemLibrary();
},
reset: function() {
this.$loadingTip.hide();
this.$tableBody.html(this.itemTemplate(this.systemLibrary.toJSON()));
this.$table.show();
}
});
return SystemLibraryView;
});

View File

@@ -0,0 +1,119 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'sysadmin-app/views/trash-library',
'sysadmin-app/collection/trash-libraries'
], function($, _, Backbone, Common, Moment, TrashLibraryView,
TrashLibraryCollection) {
'use strict';
var TrashLibrariesView = Backbone.View.extend({
id: 'admin-trash-libraries',
template: _.template($("#trash-libraries-tmpl").html()),
initialize: function() {
this.trashLibraryCollection = new TrashLibraryCollection();
this.listenTo(this.trashLibraryCollection, 'add', this.addOne);
this.listenTo(this.trashLibraryCollection, 'reset', this.reset);
},
render: function() {
var data = {'cur_tab': 'trash',};
this.$el.html(this.template(data));
this.$table = this.$('table');
this.$tableBody = $('tbody', this.$table);
this.$loadingTip = this.$('.loading-tip');
this.$emptyTip = this.$('.empty-tips');
},
events: {
'click #clean-trash-libraries': 'cleanTrashLibraries',
},
cleanTrashLibraries: function() {
var _this = this;
$.ajax({
url: Common.getUrl({'name':'admin-trash-libraries'}),
type: 'DELETE',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
success: function() {
_this.$table.hide();
_this.$emptyTip.show();
Common.feedback(gettext("Success"), 'success');
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
},
initPage: function() {
this.$table.hide();
this.$tableBody.empty();
this.$loadingTip.show();
this.$emptyTip.hide();
},
hide: function() {
this.$el.detach();
},
show: function(option) {
this.option = option;
this.render();
$("#right-panel").html(this.$el);
this.showTrashLibraries();
},
showTrashLibraries: function() {
this.initPage();
var _this = this;
this.trashLibraryCollection.fetch({
data: {},
cache: false, // for IE
reset: true,
error: function (collection, response, opts) {
var err_msg;
if (response.responseText) {
if (response['status'] == 401 || response['status'] == 403) {
err_msg = gettext("Permission error");
} else {
err_msg = $.parseJSON(response.responseText).error_msg;
}
} else {
err_msg = gettext("Failed. Please check the network.");
}
Common.feedback(err_msg, 'error');
}
});
},
reset: function() {
var length = this.trashLibraryCollection.length;
this.$loadingTip.hide();
if (length > 0) {
this.trashLibraryCollection.each(this.addOne, this);
this.$table.show();
} else {
this.$emptyTip.show();
}
},
addOne: function(library) {
var view = new TrashLibraryView({model: library});
this.$tableBody.append(view.render().el);
}
});
return TrashLibrariesView;
});

View File

@@ -0,0 +1,76 @@
define([
'jquery',
'underscore',
'backbone',
'common',
'moment',
'app/views/widgets/hl-item-view'
], function($, _, Backbone, Common, Moment, HLItemView) {
'use strict';
var LibraryView = HLItemView.extend({
tagName: 'tr',
template: _.template($('#trash-library-item-tmpl').html()),
events: {
'click .repo-delete-btn': 'deleteTrashLibrary',
'click .repo-restore-btn': 'restoreTrashLibrary',
},
initialize: function() {
HLItemView.prototype.initialize.call(this);
},
deleteTrashLibrary: function() {
var _this = this;
$.ajax({
url: Common.getUrl({'name':'admin-trash-library', 'repo_id': _this.model.get('id')}),
type: 'DELETE',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
success: function() {
_this.$el.remove();
Common.feedback(gettext("Success"), 'success');
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
return false;
},
restoreTrashLibrary: function() {
var _this = this;
$.ajax({
url: Common.getUrl({'name':'admin-trash-library', 'repo_id': _this.model.get('id')}),
type: 'PUT',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
success: function() {
_this.$el.remove();
Common.feedback(gettext("Success"), 'success');
},
error: function(xhr, textStatus, errorThrown) {
Common.ajaxErrorHandler(xhr, textStatus, errorThrown);
}
})
return false;
},
render: function() {
var data = this.model.toJSON(),
delete_time = Moment(data['delete_time']);
data['time'] = delete_time.format('LLLL');
data['time_from_now'] = Common.getRelativeTimeStr(delete_time);
this.$el.html(this.template(data));
return this;
}
});
return LibraryView;
});