mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-19 15:38:38 +00:00
new devices page
This commit is contained in:
parent
585b48e3c4
commit
a43b3f969e
@ -10,3 +10,4 @@ django-statici18n==1.1.2
|
|||||||
djangorestframework==3.3.1
|
djangorestframework==3.3.1
|
||||||
git+git://github.com/haiwen/django-constance.git@bde7f7cdfd0ed1631a6817fd4cd76f37bf54fe35#egg=django-constance[database]
|
git+git://github.com/haiwen/django-constance.git@bde7f7cdfd0ed1631a6817fd4cd76f37bf54fe35#egg=django-constance[database]
|
||||||
openpyxl==2.3.0
|
openpyxl==2.3.0
|
||||||
|
pytz==2015.7
|
||||||
|
@ -2,7 +2,6 @@ import json
|
|||||||
|
|
||||||
from django.core.paginator import EmptyPage, InvalidPage
|
from django.core.paginator import EmptyPage, InvalidPage
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.dateformat import DateFormat
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
@ -16,6 +15,7 @@ from seahub.api2.throttling import UserRateThrottle
|
|||||||
from seahub.api2.utils import api_error
|
from seahub.api2.utils import api_error
|
||||||
from seahub.group.models import GroupMessage
|
from seahub.group.models import GroupMessage
|
||||||
from seahub.utils.paginator import Paginator
|
from seahub.utils.paginator import Paginator
|
||||||
|
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||||
from .utils import api_check_group
|
from .utils import api_check_group
|
||||||
|
|
||||||
json_content_type = 'application/json; charset=utf-8'
|
json_content_type = 'application/json; charset=utf-8'
|
||||||
@ -55,12 +55,13 @@ class GroupDiscussions(APIView):
|
|||||||
|
|
||||||
msgs = []
|
msgs = []
|
||||||
for e in group_msgs:
|
for e in group_msgs:
|
||||||
|
isoformat_timestr = datetime_to_isoformat_timestr(e.timestamp)
|
||||||
msgs.append({
|
msgs.append({
|
||||||
"group_id": group_id,
|
"group_id": group_id,
|
||||||
"discussion_id": e.pk,
|
"discussion_id": e.pk,
|
||||||
"user": e.from_email,
|
"user": e.from_email,
|
||||||
"content": e.message,
|
"content": e.message,
|
||||||
"created_at": e.timestamp.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(e.timestamp).format('O'),
|
"created_at": isoformat_timestr,
|
||||||
})
|
})
|
||||||
|
|
||||||
return HttpResponse(json.dumps(msgs), status=200,
|
return HttpResponse(json.dumps(msgs), status=200,
|
||||||
@ -79,10 +80,11 @@ class GroupDiscussions(APIView):
|
|||||||
from_email=username,
|
from_email=username,
|
||||||
message=content)
|
message=content)
|
||||||
|
|
||||||
|
isoformat_timestr = datetime_to_isoformat_timestr(discuss.timestamp)
|
||||||
return Response({
|
return Response({
|
||||||
"group_id": group_id,
|
"group_id": group_id,
|
||||||
"discussion_id": discuss.pk,
|
"discussion_id": discuss.pk,
|
||||||
"user": username,
|
"user": username,
|
||||||
"content": discuss.message,
|
"content": discuss.message,
|
||||||
"created_at": discuss.timestamp.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(discuss.timestamp).format('O'),
|
"created_at": isoformat_timestr,
|
||||||
}, status=201)
|
}, status=201)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.utils.dateformat import DateFormat
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.template.defaultfilters import filesizeformat
|
from django.template.defaultfilters import filesizeformat
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE
|
|||||||
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \
|
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \
|
||||||
get_default_group_avatar_url
|
get_default_group_avatar_url
|
||||||
from seahub.utils import is_org_context, is_valid_username
|
from seahub.utils import is_org_context, is_valid_username
|
||||||
from seahub.utils.timeutils import dt, utc_to_local
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||||
from seahub.group.utils import validate_group_name, check_group_name_conflict, \
|
from seahub.group.utils import validate_group_name, check_group_name_conflict, \
|
||||||
is_group_member, is_group_admin, is_group_owner, is_group_admin_or_owner
|
is_group_member, is_group_admin, is_group_owner, is_group_admin_or_owner
|
||||||
from seahub.group.views import remove_group_common
|
from seahub.group.views import remove_group_common
|
||||||
@ -52,12 +51,12 @@ def get_group_info(request, group_id, avatar_size=GROUP_AVATAR_DEFAULT_SIZE):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
avatar_url = get_default_group_avatar_url()
|
avatar_url = get_default_group_avatar_url()
|
||||||
|
|
||||||
val = utc_to_local(dt(group.timestamp))
|
isoformat_timestr = timestamp_to_isoformat_timestr(group.timestamp)
|
||||||
group_info = {
|
group_info = {
|
||||||
"id": group.id,
|
"id": group.id,
|
||||||
"name": group.group_name,
|
"name": group.group_name,
|
||||||
"owner": group.creator_name,
|
"owner": group.creator_name,
|
||||||
"created_at": val.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(val).format('O'),
|
"created_at": isoformat_timestr,
|
||||||
"avatar_url": request.build_absolute_uri(avatar_url),
|
"avatar_url": request.build_absolute_uri(avatar_url),
|
||||||
"admins": get_group_admins(group.id),
|
"admins": get_group_admins(group.id),
|
||||||
"wiki_enabled": is_wiki_mod_enabled_for_group(group_id)
|
"wiki_enabled": is_wiki_mod_enabled_for_group(group_id)
|
||||||
|
@ -2,7 +2,6 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.dateformat import DateFormat
|
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@ -13,6 +12,7 @@ from seahub.api2.authentication import TokenAuthentication
|
|||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.share.models import UploadLinkShare
|
from seahub.share.models import UploadLinkShare
|
||||||
from seahub.utils import gen_shared_upload_link
|
from seahub.utils import gen_shared_upload_link
|
||||||
|
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||||
|
|
||||||
json_content_type = 'application/json; charset=utf-8'
|
json_content_type = 'application/json; charset=utf-8'
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class SharedUploadLinksView(APIView):
|
|||||||
link.repo = r
|
link.repo = r
|
||||||
|
|
||||||
if link.expire_date:
|
if link.expire_date:
|
||||||
expire_date = link.expire_date.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(link.expire_date).format('O')
|
expire_date = datetime_to_isoformat_timestr(link.expire_date)
|
||||||
else:
|
else:
|
||||||
expire_date = ""
|
expire_date = ""
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class SharedUploadLinksView(APIView):
|
|||||||
"repo_id": link.repo_id,
|
"repo_id": link.repo_id,
|
||||||
"path": link.path,
|
"path": link.path,
|
||||||
"token": link.token,
|
"token": link.token,
|
||||||
"ctime": link.ctime.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(link.ctime).format('O'),
|
"ctime": datetime_to_isoformat_timestr(link.ctime),
|
||||||
"view_cnt": link.view_cnt,
|
"view_cnt": link.view_cnt,
|
||||||
"expire_date": expire_date,
|
"expire_date": expire_date,
|
||||||
})
|
})
|
||||||
|
@ -61,6 +61,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/download/$', DirDownloadView.as_view(), name='api2-dir-download'),
|
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/download/$', DirDownloadView.as_view(), name='api2-dir-download'),
|
||||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/thumbnail/$', ThumbnailView.as_view(), name='api2-thumbnail'),
|
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/thumbnail/$', ThumbnailView.as_view(), name='api2-thumbnail'),
|
||||||
url(r'^starredfiles/', StarredFileView.as_view(), name='starredfiles'),
|
url(r'^starredfiles/', StarredFileView.as_view(), name='starredfiles'),
|
||||||
|
url(r'^devices/', DevicesView.as_view(), name='api2-devices'),
|
||||||
url(r'^shared-repos/$', SharedRepos.as_view(), name='sharedrepos'),
|
url(r'^shared-repos/$', SharedRepos.as_view(), name='sharedrepos'),
|
||||||
url(r'^shared-repos/(?P<repo_id>[-0-9-a-f]{36})/$', SharedRepo.as_view(), name='sharedrepo'),
|
url(r'^shared-repos/(?P<repo_id>[-0-9-a-f]{36})/$', SharedRepo.as_view(), name='sharedrepo'),
|
||||||
url(r'^beshared-repos/$', BeShared.as_view(), name='beshared'),
|
url(r'^beshared-repos/$', BeShared.as_view(), name='beshared'),
|
||||||
|
@ -29,7 +29,6 @@ from django.template.defaultfilters import filesizeformat
|
|||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.dateformat import DateFormat
|
|
||||||
|
|
||||||
from .throttling import ScopedRateThrottle, AnonRateThrottle, UserRateThrottle
|
from .throttling import ScopedRateThrottle, AnonRateThrottle, UserRateThrottle
|
||||||
from .authentication import TokenAuthentication
|
from .authentication import TokenAuthentication
|
||||||
@ -75,11 +74,12 @@ from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
|
|||||||
gen_file_share_link, gen_dir_share_link, is_org_context, gen_shared_link, \
|
gen_file_share_link, gen_dir_share_link, is_org_context, gen_shared_link, \
|
||||||
get_org_user_events, calculate_repos_last_modify, send_perm_audit_msg, \
|
get_org_user_events, calculate_repos_last_modify, send_perm_audit_msg, \
|
||||||
gen_shared_upload_link, convert_cmmt_desc_link, is_org_repo_creation_allowed
|
gen_shared_upload_link, convert_cmmt_desc_link, is_org_repo_creation_allowed
|
||||||
|
from seahub.utils.devices import get_user_devices, do_unlink_device
|
||||||
from seahub.utils.repo import get_sub_repo_abbrev_origin_path
|
from seahub.utils.repo import get_sub_repo_abbrev_origin_path
|
||||||
from seahub.utils.star import star_file, unstar_file
|
from seahub.utils.star import star_file, unstar_file
|
||||||
from seahub.utils.file_types import IMAGE, DOCUMENT
|
from seahub.utils.file_types import IMAGE, DOCUMENT
|
||||||
from seahub.utils.file_size import get_file_size_unit
|
from seahub.utils.file_size import get_file_size_unit
|
||||||
from seahub.utils.timeutils import utc_to_local
|
from seahub.utils.timeutils import utc_to_local, datetime_to_isoformat_timestr
|
||||||
from seahub.views import validate_owner, is_registered_user, check_file_lock, \
|
from seahub.views import validate_owner, is_registered_user, check_file_lock, \
|
||||||
group_events_data, get_diff, create_default_library, get_owned_repo_list, \
|
group_events_data, get_diff, create_default_library, get_owned_repo_list, \
|
||||||
list_inner_pub_repos, get_virtual_repos_by_owner, \
|
list_inner_pub_repos, get_virtual_repos_by_owner, \
|
||||||
@ -1861,6 +1861,38 @@ class OwaFileView(APIView):
|
|||||||
send_file_access_msg(request, repo, path, 'api')
|
send_file_access_msg(request, repo, path, 'api')
|
||||||
return Response(wopi_dict)
|
return Response(wopi_dict)
|
||||||
|
|
||||||
|
class DevicesView(APIView):
|
||||||
|
"""List user devices"""
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def get(self, request, format=None):
|
||||||
|
username = request.user.username
|
||||||
|
user_devices = get_user_devices(username)
|
||||||
|
return Response(user_devices)
|
||||||
|
|
||||||
|
def delete(self, request, format=None):
|
||||||
|
|
||||||
|
platform = request.data.get('platform', '')
|
||||||
|
device_id = request.data.get('device_id', '')
|
||||||
|
|
||||||
|
if not platform:
|
||||||
|
error_msg = 'platform invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if not device_id:
|
||||||
|
error_msg = 'device_id invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
do_unlink_device(request.user.username, platform, device_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})
|
||||||
|
|
||||||
class FileView(APIView):
|
class FileView(APIView):
|
||||||
"""
|
"""
|
||||||
@ -4327,8 +4359,7 @@ class RepoDownloadSharedLinks(APIView):
|
|||||||
|
|
||||||
shared_link['create_by'] = fs.username
|
shared_link['create_by'] = fs.username
|
||||||
shared_link['creator_name'] = email2nickname(fs.username)
|
shared_link['creator_name'] = email2nickname(fs.username)
|
||||||
# return time with time zone: 2016-01-18T15:03:10+0800
|
shared_link['create_time'] = datetime_to_isoformat_timestr(fs.ctime)
|
||||||
shared_link['create_time'] = fs.ctime.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(fs.ctime).format('O')
|
|
||||||
shared_link['token'] = fs.token
|
shared_link['token'] = fs.token
|
||||||
shared_link['path'] = path
|
shared_link['path'] = path
|
||||||
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
|
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
|
||||||
@ -4412,8 +4443,7 @@ class RepoUploadSharedLinks(APIView):
|
|||||||
|
|
||||||
shared_link['create_by'] = fs.username
|
shared_link['create_by'] = fs.username
|
||||||
shared_link['creator_name'] = email2nickname(fs.username)
|
shared_link['creator_name'] = email2nickname(fs.username)
|
||||||
# return time with time zone: 2016-01-18T15:03:10+0800
|
shared_link['create_time'] = datetime_to_isoformat_timestr(fs.ctime)
|
||||||
shared_link['create_time'] = fs.ctime.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(fs.ctime).format('O')
|
|
||||||
shared_link['token'] = fs.token
|
shared_link['token'] = fs.token
|
||||||
shared_link['path'] = path
|
shared_link['path'] = path
|
||||||
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
|
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
{% extends "home_base.html" %}
|
|
||||||
|
|
||||||
{% load seahub_tags avatar_tags i18n %}
|
|
||||||
|
|
||||||
{% block sub_title %}{% trans "Devices" %} - {% endblock %}
|
|
||||||
{% block cur_devices %}tab-cur{% endblock %}
|
|
||||||
|
|
||||||
{% block right_panel %}
|
|
||||||
<h3 class="hd">{% trans "Devices" %}</h3>
|
|
||||||
{% if devices %}
|
|
||||||
<table class="client-list">
|
|
||||||
<tr>
|
|
||||||
<th width="13%">{% trans "Platform" %}</th>
|
|
||||||
<th width="25%">{% trans "Device Name" %}</th>
|
|
||||||
<th width="20%">{% trans "IP" %}</th>
|
|
||||||
<th width="17%">{% trans "Last Access" %}</th>
|
|
||||||
<th width="15%">{% trans "# Libraries" %}</th>
|
|
||||||
<th width="10%"></th>
|
|
||||||
</tr>
|
|
||||||
{% for device in devices %}
|
|
||||||
<tr data-platform="{{ device.platform }}" data-name="{{ device.device_name }}" data-device-id="{{ device.device_id }}">
|
|
||||||
<td>{{ device.platform }}</td>
|
|
||||||
<td>{{ device.device_name }}</td>
|
|
||||||
<td>{{ device.last_login_ip }}</td>
|
|
||||||
<td>{{ device.last_accessed | translate_seahub_time }}</td>
|
|
||||||
<td>
|
|
||||||
<span class="lib-num">{{ device.synced_repos|length }}{% if device.synced_repos %} <span class="dir-icon icon-caret-down"></span>{% endif %}</span>
|
|
||||||
{% if device.synced_repos %}
|
|
||||||
<ul class="lib-list hide">
|
|
||||||
{% for repo in device.synced_repos %}
|
|
||||||
<li><a href="{% url 'view_common_lib_dir' repo.repo_id '' %}">{{ repo.repo_name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div>
|
|
||||||
<span class="unlink-device op-icon sf2-icon-delete vh" title="{% trans "Unlink" %}"></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<div class="op-confirm unsync-confirm hide" id="unsync-cfm-popup">
|
|
||||||
<p class="details">{% trans "Really want to unlink %s?" %}</p>
|
|
||||||
<p>{% trans "It will immediately stop syncing." %}</p>
|
|
||||||
<button class="yes">{% trans "Yes" %}</button>
|
|
||||||
<button class="no">{% trans "No" %}</button>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="empty-tips">
|
|
||||||
<h2 class="alc">{% trans "You do not have connected devices" %}</h2>
|
|
||||||
<p>{% trans "Your clients (Desktop/Android/iOS) will be listed here." %}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extra_script %}{{block.super}}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$('.lib-num').click(function(){
|
|
||||||
var lib_list = $(this).next('.lib-list');
|
|
||||||
var dir_icon = $(this).children('.dir-icon');
|
|
||||||
if (lib_list.length > 0) {
|
|
||||||
lib_list.toggleClass('hide');
|
|
||||||
if (lib_list.hasClass('hide')) {
|
|
||||||
dir_icon.removeClass('icon-caret-up').addClass('icon-caret-down');
|
|
||||||
} else {
|
|
||||||
dir_icon.removeClass('icon-caret-down').addClass('icon-caret-up');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
$('#main-panel').removeClass('ovhd');
|
|
||||||
$('.unlink-device').click(function() {
|
|
||||||
var op = $(this);
|
|
||||||
var cont = op.parent().css({'position': 'relative'}),
|
|
||||||
cfm;
|
|
||||||
var tr = op.parents('tr');
|
|
||||||
|
|
||||||
// only show 1 popup each time.
|
|
||||||
$('.unsync-confirm', op.parents('table')).addClass('hide');
|
|
||||||
|
|
||||||
if (cont.find('.unsync-confirm').length == 1) {
|
|
||||||
cfm = cont.find('.unsync-confirm');
|
|
||||||
} else {
|
|
||||||
cfm = $('#unsync-cfm-popup').clone().removeAttr('id');
|
|
||||||
cont.append(cfm);
|
|
||||||
cfm.css({'right':0, 'top': op.position().top + op.height() + 3, 'white-space':'nowrap'});
|
|
||||||
}
|
|
||||||
cfm.removeClass('hide');
|
|
||||||
|
|
||||||
var details = $('.details', cfm);
|
|
||||||
details.html(details.html().replace('%s', tr.attr('data-name')));
|
|
||||||
|
|
||||||
$('.no',cfm).unbind().click(function() {
|
|
||||||
cfm.addClass('hide');
|
|
||||||
});
|
|
||||||
$('.yes',cfm).unbind().click(function() {
|
|
||||||
cfm.addClass('hide');
|
|
||||||
$.ajax({
|
|
||||||
url: '{% url 'unlink_device' %}',
|
|
||||||
type: 'POST',
|
|
||||||
dataType: 'json',
|
|
||||||
beforeSend: prepareCSRFToken,
|
|
||||||
data: {
|
|
||||||
'platform': tr.data('platform'),
|
|
||||||
'device_id': tr.data('device-id')
|
|
||||||
},
|
|
||||||
success: function() {
|
|
||||||
tr.remove();
|
|
||||||
feedback("{% trans "Successfully unlinked." %}", 'success');
|
|
||||||
},
|
|
||||||
error: ajaxErrorHandler
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
@ -50,7 +50,7 @@
|
|||||||
<li class="tab {% block cur_personal_wiki %}{% endblock %}"><a href="{% url 'personal_wiki' %}"><span class="sf2-icon-wiki"></span>{% trans "Personal Wiki" %}</a></li>
|
<li class="tab {% block cur_personal_wiki %}{% endblock %}"><a href="{% url 'personal_wiki' %}"><span class="sf2-icon-wiki"></span>{% trans "Personal Wiki" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li class="tab {% block cur_devices %}{% endblock %}"><a href="{% url 'devices' %}"><span class="sf2-icon-monitor"></span>{% trans "Devices" %}</a></li>
|
<li class="tab {% block cur_devices %}{% endblock %}"><a href="{{ SITE_ROOT }}#devices/"><span class="sf2-icon-monitor"></span>{% trans "Devices" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 class="hd">{% trans "Share Admin" %}</h3>
|
<h3 class="hd">{% trans "Share Admin" %}</h3>
|
||||||
|
@ -666,7 +666,7 @@
|
|||||||
<li class="tab"><a href="{% url 'personal_wiki' %}"><span class="sf2-icon-wiki"></span>{% trans "Personal Wiki" %}</a></li>
|
<li class="tab"><a href="{% url 'personal_wiki' %}"><span class="sf2-icon-wiki"></span>{% trans "Personal Wiki" %}</a></li>
|
||||||
<% } %>
|
<% } %>
|
||||||
<% } %>
|
<% } %>
|
||||||
<li class="tab"><a href="{% url 'devices' %}"><span class="sf2-icon-monitor"></span>{% trans "Devices" %}</a></li>
|
<li class="tab<% if (cur_tab == 'devices') { %> tab-cur<% } %>"><a href="{{ SITE_ROOT }}#devices/"><span class="sf2-icon-monitor"></span>{% trans "Devices" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 class="hd">{% trans "Share Admin" %}</h3>
|
<h3 class="hd">{% trans "Share Admin" %}</h3>
|
||||||
@ -845,6 +845,27 @@
|
|||||||
<span class="sf2-icon-delete unstar op-icon vh" title="{% trans "Unstar" %}"></span>
|
<span class="sf2-icon-delete unstar op-icon vh" title="{% trans "Unstar" %}"></span>
|
||||||
</td>
|
</td>
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/template" id="device-item-tmpl">
|
||||||
|
<td><%- platform %></td>
|
||||||
|
<td><%- device_name %></td>
|
||||||
|
<td><%- last_login_ip %></td>
|
||||||
|
<td><time title='<%- time %>'><%- time_from_now %></time></td>
|
||||||
|
<td>
|
||||||
|
<span class="lib-num cspt"><%- synced_repos_length %><% if (synced_repos.length > 0) { %> <span class="dir-icon icon-caret-down"></span><% } %></span>
|
||||||
|
<% if (synced_repos.length > 0) { %>
|
||||||
|
<ul class="lib-list hide">
|
||||||
|
<% for (var i = 0, len = synced_repos_length; i < len; i++) { %>
|
||||||
|
<li><a href="#my-libs/lib/<%- synced_repos[i].repo_id %>"><%- synced_repos[i].repo_name %></a></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<span class="unlink-device op-icon sf2-icon-delete vh" title="{% trans "Unlink" %}"></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</script>
|
||||||
<script type="text/template" id="my-repos-hd-tmpl">
|
<script type="text/template" id="my-repos-hd-tmpl">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="4%"><!--icon--></th>
|
<th width="4%"><!--icon--></th>
|
||||||
|
@ -75,6 +75,28 @@
|
|||||||
<button id="activities-more" class="hide">{% trans 'More' %}</button>
|
<button id="activities-more" class="hide">{% trans 'More' %}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="devices" class="hide">
|
||||||
|
<h3 class="hd">{% trans "Devices" %}</h3>
|
||||||
|
<table class="hide">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="13%">{% trans "Platform" %}</th>
|
||||||
|
<th width="25%">{% trans "Device Name" %}</th>
|
||||||
|
<th width="20%">{% trans "IP" %}</th>
|
||||||
|
<th width="17%">{% trans "Last Access" %}</th>
|
||||||
|
<th width="15%">{% trans "# Libraries" %}</th>
|
||||||
|
<th width="10%"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
<span class="loading-icon loading-tip"></span>
|
||||||
|
<div class="empty-tips hide">
|
||||||
|
<h2 class="alc">{% trans "You do not have connected devices" %}</h2>
|
||||||
|
<p>{% trans "Your clients (Desktop/Android/iOS) will be listed here." %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="guide-for-new" class="hide">
|
<div id="guide-for-new" class="hide">
|
||||||
<span class="icon-lightbulb fleft"></span>
|
<span class="icon-lightbulb fleft"></span>
|
||||||
<div class="txt">
|
<div class="txt">
|
||||||
@ -232,6 +254,7 @@ app["pageOptions"] = {
|
|||||||
enable_thumbnail: {% if enable_thumbnail %} true {% else %} false {% endif %},
|
enable_thumbnail: {% if enable_thumbnail %} true {% else %} false {% endif %},
|
||||||
thumbnail_default_size: {{ thumbnail_default_size }},
|
thumbnail_default_size: {{ thumbnail_default_size }},
|
||||||
thumbnail_size_for_grid: {{ thumbnail_size_for_grid }},
|
thumbnail_size_for_grid: {{ thumbnail_size_for_grid }},
|
||||||
|
language_code: "{{ LANGUAGE_CODE }}",
|
||||||
enable_encrypted_library: {% if enable_encrypted_library %} true {% else %} false {% endif %},
|
enable_encrypted_library: {% if enable_encrypted_library %} true {% else %} false {% endif %},
|
||||||
enable_repo_history_setting: {% if enable_repo_history_setting %} true {% else %} false {% endif %},
|
enable_repo_history_setting: {% if enable_repo_history_setting %} true {% else %} false {% endif %},
|
||||||
max_upload_file_size: {% if max_upload_file_size %} {{ max_upload_file_size }} {% else %} '' {% endif %},
|
max_upload_file_size: {% if max_upload_file_size %} {{ max_upload_file_size }} {% else %} '' {% endif %},
|
||||||
|
@ -56,9 +56,6 @@ urlpatterns = patterns(
|
|||||||
url(r'^home/wiki_page_edit/(?P<page_name>[^/]+)$', personal_wiki_page_edit, name='personal_wiki_page_edit'),
|
url(r'^home/wiki_page_edit/(?P<page_name>[^/]+)$', personal_wiki_page_edit, name='personal_wiki_page_edit'),
|
||||||
url(r'^home/wiki_page_delete/(?P<page_name>[^/]+)$', personal_wiki_page_delete, name='personal_wiki_page_delete'),
|
url(r'^home/wiki_page_delete/(?P<page_name>[^/]+)$', personal_wiki_page_delete, name='personal_wiki_page_delete'),
|
||||||
|
|
||||||
url(r'^devices/$', devices, name='devices'),
|
|
||||||
url(r'^home/devices/unlink/$', unlink_device, name='unlink_device'),
|
|
||||||
|
|
||||||
# url(r'^home/public/reply/(?P<msg_id>[\d]+)/$', innerpub_msg_reply, name='innerpub_msg_reply'),
|
# url(r'^home/public/reply/(?P<msg_id>[\d]+)/$', innerpub_msg_reply, name='innerpub_msg_reply'),
|
||||||
# url(r'^home/owner/(?P<owner_name>[^/]+)/$', ownerhome, name='ownerhome'),
|
# url(r'^home/owner/(?P<owner_name>[^/]+)/$', ownerhome, name='ownerhome'),
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import datetime
|
|||||||
|
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
from seahub.api2.models import TokenV2, DESKTOP_PLATFORMS
|
from seahub.api2.models import TokenV2, DESKTOP_PLATFORMS
|
||||||
|
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -17,7 +18,6 @@ def _last_sync_time(repos):
|
|||||||
|
|
||||||
def get_user_devices(username):
|
def get_user_devices(username):
|
||||||
devices = TokenV2.objects.get_user_devices(username)
|
devices = TokenV2.objects.get_user_devices(username)
|
||||||
|
|
||||||
peer_repos_map = get_user_synced_repo_infos(username)
|
peer_repos_map = get_user_synced_repo_infos(username)
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
@ -29,6 +29,7 @@ def get_user_devices(username):
|
|||||||
device['last_accessed'] = max(device['last_accessed'],
|
device['last_accessed'] = max(device['last_accessed'],
|
||||||
_last_sync_time(repos))
|
_last_sync_time(repos))
|
||||||
|
|
||||||
|
device['last_accessed'] = datetime_to_isoformat_timestr(device['last_accessed'])
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
def get_user_synced_repo_infos(username):
|
def get_user_synced_repo_infos(username):
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import pytz
|
||||||
import datetime
|
import datetime
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
@ -11,7 +12,6 @@ def dt(value):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
# TODO: need a better way to handle 64 bits timestamp.
|
# TODO: need a better way to handle 64 bits timestamp.
|
||||||
return datetime.datetime.utcfromtimestamp(value/1000000)
|
return datetime.datetime.utcfromtimestamp(value/1000000)
|
||||||
|
|
||||||
|
|
||||||
def value_to_db_datetime(value):
|
def value_to_db_datetime(value):
|
||||||
if value is None:
|
if value is None:
|
||||||
@ -33,3 +33,16 @@ def utc_to_local(dt):
|
|||||||
utc = dt.replace(tzinfo=timezone.utc)
|
utc = dt.replace(tzinfo=timezone.utc)
|
||||||
local = timezone.make_naive(utc, tz)
|
local = timezone.make_naive(utc, tz)
|
||||||
return local
|
return local
|
||||||
|
|
||||||
|
def timestamp_to_isoformat_timestr(timestamp):
|
||||||
|
dt_obj = dt(timestamp)
|
||||||
|
dt_obj = dt_obj.replace(microsecond=0)
|
||||||
|
pytz_obj = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
isoformat_timestr = pytz_obj.localize(dt_obj).isoformat()
|
||||||
|
return isoformat_timestr
|
||||||
|
|
||||||
|
def datetime_to_isoformat_timestr(datetime):
|
||||||
|
datetime = datetime.replace(microsecond=0)
|
||||||
|
pytz_obj = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
isoformat_timestr = pytz_obj.localize(datetime).isoformat()
|
||||||
|
return isoformat_timestr
|
||||||
|
@ -60,7 +60,6 @@ from seahub.utils.star import get_dir_starred_files
|
|||||||
from seahub.utils.timeutils import utc_to_local
|
from seahub.utils.timeutils import utc_to_local
|
||||||
from seahub.views.modules import MOD_PERSONAL_WIKI, enable_mod_for_user, \
|
from seahub.views.modules import MOD_PERSONAL_WIKI, enable_mod_for_user, \
|
||||||
disable_mod_for_user
|
disable_mod_for_user
|
||||||
from seahub.utils.devices import get_user_devices, do_unlink_device
|
|
||||||
import seahub.settings as settings
|
import seahub.settings as settings
|
||||||
from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, \
|
from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, \
|
||||||
FILE_ENCODING_LIST, FILE_ENCODING_TRY_LIST, AVATAR_FILE_STORAGE, \
|
FILE_ENCODING_LIST, FILE_ENCODING_TRY_LIST, AVATAR_FILE_STORAGE, \
|
||||||
@ -886,37 +885,6 @@ def libraries(request):
|
|||||||
'can_add_pub_repo': can_add_pub_repo,
|
'can_add_pub_repo': can_add_pub_repo,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
@login_required
|
|
||||||
@user_mods_check
|
|
||||||
def devices(request):
|
|
||||||
"""List user devices"""
|
|
||||||
username = request.user.username
|
|
||||||
user_devices = get_user_devices(username)
|
|
||||||
|
|
||||||
return render_to_response('devices.html', {
|
|
||||||
"devices": user_devices,
|
|
||||||
}, context_instance=RequestContext(request))
|
|
||||||
|
|
||||||
@login_required_ajax
|
|
||||||
@require_POST
|
|
||||||
def unlink_device(request):
|
|
||||||
content_type = 'application/json; charset=utf-8'
|
|
||||||
|
|
||||||
platform = request.POST.get('platform', '')
|
|
||||||
device_id = request.POST.get('device_id', '')
|
|
||||||
|
|
||||||
if not platform or not device_id:
|
|
||||||
return HttpResponseBadRequest(json.dumps({'error': _(u'Argument missing')}),
|
|
||||||
content_type=content_type)
|
|
||||||
|
|
||||||
try:
|
|
||||||
do_unlink_device(request.user.username, platform, device_id)
|
|
||||||
except:
|
|
||||||
return HttpResponse(json.dumps({'error': _(u'Internal server error')}),
|
|
||||||
status=500, content_type=content_type)
|
|
||||||
|
|
||||||
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_POST
|
@require_POST
|
||||||
def unsetinnerpub(request, repo_id):
|
def unsetinnerpub(request, repo_id):
|
||||||
|
17
static/scripts/app/collections/devices.js
Normal file
17
static/scripts/app/collections/devices.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
define([
|
||||||
|
'underscore',
|
||||||
|
'backbone',
|
||||||
|
'common',
|
||||||
|
'app/models/device'
|
||||||
|
], function(_, Backbone, Common, Device) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var DevicesCollection = Backbone.Collection.extend({
|
||||||
|
model: Device,
|
||||||
|
url: function () {
|
||||||
|
return Common.getUrl({name: 'devices'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return DevicesCollection;
|
||||||
|
});
|
10
static/scripts/app/models/device.js
Normal file
10
static/scripts/app/models/device.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
define([
|
||||||
|
'underscore',
|
||||||
|
'backbone'
|
||||||
|
], function(_, Backbone) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Device = Backbone.Model.extend({});
|
||||||
|
|
||||||
|
return Device;
|
||||||
|
});
|
@ -29,6 +29,7 @@ define([
|
|||||||
'common/lib/:repo_id(/*path)': 'showCommonDir',
|
'common/lib/:repo_id(/*path)': 'showCommonDir',
|
||||||
'starred/': 'showStarredFile',
|
'starred/': 'showStarredFile',
|
||||||
'activities/': 'showActivities',
|
'activities/': 'showActivities',
|
||||||
|
'devices/': 'showDevices',
|
||||||
// Default
|
// Default
|
||||||
'*actions': 'showRepos'
|
'*actions': 'showRepos'
|
||||||
},
|
},
|
||||||
@ -96,6 +97,12 @@ define([
|
|||||||
this.sideNavView.setCurTab('starred');
|
this.sideNavView.setCurTab('starred');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showDevices: function() {
|
||||||
|
this.switchCurrentView(this.myHomeView);
|
||||||
|
this.myHomeView.showDevices();
|
||||||
|
this.sideNavView.setCurTab('devices');
|
||||||
|
},
|
||||||
|
|
||||||
showActivities: function() {
|
showActivities: function() {
|
||||||
this.switchCurrentView(this.myHomeView);
|
this.switchCurrentView(this.myHomeView);
|
||||||
this.myHomeView.showActivities();
|
this.myHomeView.showActivities();
|
||||||
|
104
static/scripts/app/views/device.js
Normal file
104
static/scripts/app/views/device.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'underscore',
|
||||||
|
'backbone',
|
||||||
|
'common',
|
||||||
|
], function($, _, Backbone, Common) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var DeviceView = Backbone.View.extend({
|
||||||
|
tagName: 'tr',
|
||||||
|
|
||||||
|
template: _.template($('#device-item-tmpl').html()),
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'mouseenter': 'showAction',
|
||||||
|
'mouseleave': 'hideAction',
|
||||||
|
'click .unlink-device': 'unlinkDevice',
|
||||||
|
'click .lib-num': 'showSyncedRepos'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
var data = this.model.toJSON();
|
||||||
|
|
||||||
|
if (typeof(data['synced_repos']) == 'undefined') {
|
||||||
|
data['synced_repos'] = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data['synced_repos']) {
|
||||||
|
data['synced_repos_length'] = data['synced_repos'].length;
|
||||||
|
} else {
|
||||||
|
data['synced_repos_length'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to human readable time
|
||||||
|
var now = new Date(),
|
||||||
|
last_accessed = Common.getMomentWithLocale(data['last_accessed']);
|
||||||
|
|
||||||
|
data['time'] = last_accessed.format('LLLL');
|
||||||
|
if (last_accessed - now > 0) {
|
||||||
|
data['time_from_now'] = gettext("Just now");
|
||||||
|
} else {
|
||||||
|
data['time_from_now'] = last_accessed.fromNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.html(this.template(data));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAction: function() {
|
||||||
|
this.$el.addClass('hl');
|
||||||
|
this.$el.find('.op-icon').removeClass('vh');
|
||||||
|
},
|
||||||
|
|
||||||
|
hideAction: function() {
|
||||||
|
this.$el.removeClass('hl');
|
||||||
|
this.$el.find('.op-icon').addClass('vh');
|
||||||
|
},
|
||||||
|
|
||||||
|
showSyncedRepos: function(e) {
|
||||||
|
var $lib_num = $(e.currentTarget);
|
||||||
|
var lib_list = $lib_num.next('.lib-list');
|
||||||
|
var dir_icon = $lib_num.children('.dir-icon');
|
||||||
|
|
||||||
|
if (lib_list.length > 0) {
|
||||||
|
lib_list.toggleClass('hide');
|
||||||
|
if (lib_list.hasClass('hide')) {
|
||||||
|
dir_icon.removeClass('icon-caret-up').addClass('icon-caret-down');
|
||||||
|
} else {
|
||||||
|
dir_icon.removeClass('icon-caret-down').addClass('icon-caret-up');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
unlinkDevice: function() {
|
||||||
|
var _this = this,
|
||||||
|
data = {
|
||||||
|
'platform': this.model.get('platform'),
|
||||||
|
'device_id': this.model.get('device_id')
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: Common.getUrl({name: 'devices'}),
|
||||||
|
type: 'DELETE',
|
||||||
|
dataType: 'json',
|
||||||
|
beforeSend: Common.prepareCSRFToken,
|
||||||
|
data: data,
|
||||||
|
success: function() {
|
||||||
|
_this.remove();
|
||||||
|
Common.feedback(gettext("Success"), 'success');
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
Common.ajaxErrorHandler(xhr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return DeviceView;
|
||||||
|
});
|
58
static/scripts/app/views/devices.js
Normal file
58
static/scripts/app/views/devices.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'underscore',
|
||||||
|
'backbone',
|
||||||
|
'common',
|
||||||
|
'app/views/device',
|
||||||
|
'app/collections/devices',
|
||||||
|
], function($, _, Backbone, Common, Device, DevicesCollection) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var DevicesView = Backbone.View.extend({
|
||||||
|
|
||||||
|
el: $('#devices'),
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
this.$table = this.$('table');
|
||||||
|
this.$tableBody = this.$('tbody');
|
||||||
|
this.$loadingTip = this.$('.loading-tip');
|
||||||
|
this.$emptyTip = this.$('.empty-tips');
|
||||||
|
|
||||||
|
this.devices = new DevicesCollection();
|
||||||
|
this.listenTo(this.devices, 'reset', this.reset);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
addOne: function(device) {
|
||||||
|
var view = new Device({model: device});
|
||||||
|
this.$tableBody.append(view.render().el);
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this.$tableBody.empty();
|
||||||
|
this.$loadingTip.hide();
|
||||||
|
this.devices.each(this.addOne, this);
|
||||||
|
if (this.devices.length) {
|
||||||
|
this.$emptyTip.hide();
|
||||||
|
this.$table.show();
|
||||||
|
} else {
|
||||||
|
this.$emptyTip.show();
|
||||||
|
this.$table.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.$el.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
this.$el.show();
|
||||||
|
this.$table.hide();
|
||||||
|
this.$loadingTip.show();
|
||||||
|
this.devices.fetch({reset: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return DevicesView;
|
||||||
|
});
|
@ -6,9 +6,10 @@ define([
|
|||||||
'app/views/myhome-repos',
|
'app/views/myhome-repos',
|
||||||
'app/views/myhome-shared-repos',
|
'app/views/myhome-shared-repos',
|
||||||
'app/views/starred-file',
|
'app/views/starred-file',
|
||||||
|
'app/views/devices',
|
||||||
'app/views/activities'
|
'app/views/activities'
|
||||||
], function($, _, Backbone, Common, ReposView,
|
], function($, _, Backbone, Common, ReposView,
|
||||||
SharedReposView, StarredFileView, ActivitiesView) {
|
SharedReposView, StarredFileView, DevicesView, ActivitiesView) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MyHomeView = Backbone.View.extend({
|
var MyHomeView = Backbone.View.extend({
|
||||||
@ -18,6 +19,7 @@ define([
|
|||||||
this.reposView = new ReposView();
|
this.reposView = new ReposView();
|
||||||
this.sharedReposView = new SharedReposView();
|
this.sharedReposView = new SharedReposView();
|
||||||
this.starredFileView = new StarredFileView();
|
this.starredFileView = new StarredFileView();
|
||||||
|
this.devicesView = new DevicesView();
|
||||||
this.activitiesView = new ActivitiesView();
|
this.activitiesView = new ActivitiesView();
|
||||||
|
|
||||||
this.dirView = options.dirView;
|
this.dirView = options.dirView;
|
||||||
@ -45,6 +47,12 @@ define([
|
|||||||
this.currentView = this.starredFileView;
|
this.currentView = this.starredFileView;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showDevices: function() {
|
||||||
|
this.currentView.hide();
|
||||||
|
this.devicesView.show();
|
||||||
|
this.currentView = this.devicesView;
|
||||||
|
},
|
||||||
|
|
||||||
showActivities: function() {
|
showActivities: function() {
|
||||||
this.currentView.hide();
|
this.currentView.hide();
|
||||||
this.activitiesView.show();
|
this.activitiesView.show();
|
||||||
|
@ -41,6 +41,7 @@ require.config({
|
|||||||
simplemodal: 'lib/jquery.simplemodal',
|
simplemodal: 'lib/jquery.simplemodal',
|
||||||
jstree: 'lib/jstree.1.0',
|
jstree: 'lib/jstree.1.0',
|
||||||
select2: 'lib/select2-3.5.2',
|
select2: 'lib/select2-3.5.2',
|
||||||
|
moment: 'lib/moment-with-locales',
|
||||||
|
|
||||||
underscore: 'lib/underscore',
|
underscore: 'lib/underscore',
|
||||||
backbone: 'lib/backbone',
|
backbone: 'lib/backbone',
|
||||||
@ -52,8 +53,9 @@ define([
|
|||||||
'jquery',
|
'jquery',
|
||||||
'underscore',
|
'underscore',
|
||||||
'text', // Workaround for r.js, otherwise text.js will not be included
|
'text', // Workaround for r.js, otherwise text.js will not be included
|
||||||
'pinyin-by-unicode'
|
'pinyin-by-unicode',
|
||||||
], function($, _, text, PinyinByUnicode) {
|
'moment',
|
||||||
|
], function($, _, text, PinyinByUnicode, Moment) {
|
||||||
return {
|
return {
|
||||||
INFO_TIMEOUT: 10000, // 10 secs for info msg
|
INFO_TIMEOUT: 10000, // 10 secs for info msg
|
||||||
SUCCESS_TIMEOUT: 3000, // 3 secs for success msg
|
SUCCESS_TIMEOUT: 3000, // 3 secs for success msg
|
||||||
@ -144,6 +146,7 @@ define([
|
|||||||
case 'set_notice_seen_by_id': return siteRoot + 'ajax/set_notice_seen_by_id/';
|
case 'set_notice_seen_by_id': return siteRoot + 'ajax/set_notice_seen_by_id/';
|
||||||
case 'toggle_personal_modules': return siteRoot + 'ajax/toggle-personal-modules/';
|
case 'toggle_personal_modules': return siteRoot + 'ajax/toggle-personal-modules/';
|
||||||
case 'starred_files': return siteRoot + 'api2/starredfiles/';
|
case 'starred_files': return siteRoot + 'api2/starredfiles/';
|
||||||
|
case 'devices': return siteRoot + 'api2/devices/';
|
||||||
case 'events': return siteRoot + 'api2/events/';
|
case 'events': return siteRoot + 'api2/events/';
|
||||||
case 'search_user': return siteRoot + 'api2/search-user/';
|
case 'search_user': return siteRoot + 'api2/search-user/';
|
||||||
case 'user_profile': return siteRoot + 'profile/' + options.username + '/';
|
case 'user_profile': return siteRoot + 'profile/' + options.username + '/';
|
||||||
@ -191,6 +194,19 @@ define([
|
|||||||
'default' : 'file.png'
|
'default' : 'file.png'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getMomentWithLocale: function(time) {
|
||||||
|
var language_code;
|
||||||
|
if (app.pageOptions.language_code == 'en') {
|
||||||
|
language_code = 'en-gb';
|
||||||
|
} else if (app.pageOptions.language_code == 'es-ar' || app.pageOptions.language_code == 'es-mx') {
|
||||||
|
language_code = 'es';
|
||||||
|
} else {
|
||||||
|
language_code = app.pageOptions.language_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Moment(time).locale(language_code);
|
||||||
|
},
|
||||||
|
|
||||||
getFileIconUrl: function(filename, size) {
|
getFileIconUrl: function(filename, size) {
|
||||||
if (size > 24) {
|
if (size > 24) {
|
||||||
size = 192;
|
size = 192;
|
||||||
|
5746
static/scripts/lib/moment-with-locales.js
Normal file
5746
static/scripts/lib/moment-with-locales.js
Normal file
File diff suppressed because it is too large
Load Diff
40
tests/api/test_devices.py
Normal file
40
tests/api/test_devices.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from seahub.api2.models import TokenV2
|
||||||
|
from seahub.test_utils import BaseTestCase, Fixtures
|
||||||
|
|
||||||
|
|
||||||
|
class DevicesTest(BaseTestCase, Fixtures):
|
||||||
|
def setUp(self):
|
||||||
|
self.platform = 'android'
|
||||||
|
self.device_id = '4a0d62c1f27b3b74'
|
||||||
|
TokenV2.objects.get_or_create_token(self.user.username, self.platform,
|
||||||
|
self.device_id, u'PLK-AL10', u'2.0.3', u'5.0.2', '192.168.1.208')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_repo()
|
||||||
|
|
||||||
|
def test_can_list(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
resp = self.client.get(reverse('api2-devices'))
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp[0]['platform'] == self.platform
|
||||||
|
assert json_resp[0]['device_id'] == self.device_id
|
||||||
|
|
||||||
|
def test_can_delete(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
data = 'platform=%s&device_id=%s' % (self.platform, self.device_id)
|
||||||
|
resp = self.client.delete(reverse('api2-devices'), data, 'application/x-www-form-urlencoded')
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual(0, len(TokenV2.objects.all()))
|
||||||
|
|
||||||
|
def test_can_not_delete_with_invalid_args(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
resp = self.client.delete(reverse('api2-devices'))
|
||||||
|
self.assertEqual(400, resp.status_code)
|
Loading…
Reference in New Issue
Block a user