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:
@@ -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):
|
||||
|
||||
|
150
seahub/api2/endpoints/admin/libraries.py
Normal file
150
seahub/api2/endpoints/admin/libraries.py
Normal 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)
|
253
seahub/api2/endpoints/admin/library_dirents.py
Normal file
253
seahub/api2/endpoints/admin/library_dirents.py
Normal 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})
|
39
seahub/api2/endpoints/admin/system_library.py
Normal file
39
seahub/api2/endpoints/admin/system_library.py
Normal 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)
|
76
seahub/api2/endpoints/admin/trash_libraries.py
Normal file
76
seahub/api2/endpoints/admin/trash_libraries.py
Normal 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})
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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 %}
|
||||
};
|
||||
|
@@ -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')),
|
||||
|
@@ -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,
|
||||
|
@@ -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 + '/';
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -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'});
|
||||
|
24
static/scripts/sysadmin-app/collection/libraries.js
Normal file
24
static/scripts/sysadmin-app/collection/libraries.js
Normal 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;
|
||||
});
|
24
static/scripts/sysadmin-app/collection/library-dirents.js
Normal file
24
static/scripts/sysadmin-app/collection/library-dirents.js
Normal 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;
|
||||
});
|
17
static/scripts/sysadmin-app/collection/trash-libraries.js
Normal file
17
static/scripts/sysadmin-app/collection/trash-libraries.js
Normal 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;
|
||||
});
|
54
static/scripts/sysadmin-app/models/library-dirent.js
Normal file
54
static/scripts/sysadmin-app/models/library-dirent.js
Normal 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;
|
||||
});
|
11
static/scripts/sysadmin-app/models/library.js
Normal file
11
static/scripts/sysadmin-app/models/library.js
Normal file
@@ -0,0 +1,11 @@
|
||||
define([
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common',
|
||||
], function(_, Backbone, Common) {
|
||||
'use strict';
|
||||
|
||||
var LibraryModel = Backbone.Model.extend({});
|
||||
|
||||
return LibraryModel;
|
||||
});
|
11
static/scripts/sysadmin-app/models/system-library.js
Normal file
11
static/scripts/sysadmin-app/models/system-library.js
Normal file
@@ -0,0 +1,11 @@
|
||||
define([
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common'
|
||||
], function(_, Backbone, Common) {
|
||||
'use strict';
|
||||
|
||||
var SystemLibrary = Backbone.Model.extend({});
|
||||
|
||||
return SystemLibrary;
|
||||
});
|
11
static/scripts/sysadmin-app/models/trash-library.js
Normal file
11
static/scripts/sysadmin-app/models/trash-library.js
Normal file
@@ -0,0 +1,11 @@
|
||||
define([
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common'
|
||||
], function(_, Backbone, Common) {
|
||||
'use strict';
|
||||
|
||||
var TrashLibrary = Backbone.Model.extend({});
|
||||
|
||||
return TrashLibrary;
|
||||
});
|
@@ -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();
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -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;
|
||||
|
||||
});
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
@@ -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'
|
||||
|
152
static/scripts/sysadmin-app/views/libraries.js
Normal file
152
static/scripts/sysadmin-app/views/libraries.js
Normal 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;
|
||||
|
||||
});
|
208
static/scripts/sysadmin-app/views/library-dir.js
Normal file
208
static/scripts/sysadmin-app/views/library-dir.js
Normal 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;
|
||||
|
||||
});
|
80
static/scripts/sysadmin-app/views/library-dirent.js
Normal file
80
static/scripts/sysadmin-app/views/library-dirent.js
Normal 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;
|
||||
});
|
125
static/scripts/sysadmin-app/views/library.js
Normal file
125
static/scripts/sysadmin-app/views/library.js
Normal 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;
|
||||
});
|
@@ -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();
|
||||
|
81
static/scripts/sysadmin-app/views/system-library.js
Normal file
81
static/scripts/sysadmin-app/views/system-library.js
Normal 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;
|
||||
});
|
119
static/scripts/sysadmin-app/views/trash-libraries.js
Normal file
119
static/scripts/sysadmin-app/views/trash-libraries.js
Normal 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;
|
||||
|
||||
});
|
76
static/scripts/sysadmin-app/views/trash-library.js
Normal file
76
static/scripts/sysadmin-app/views/trash-library.js
Normal 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;
|
||||
});
|
Reference in New Issue
Block a user