mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-01 15:23:05 +00:00
Merge branch '5.0'
Conflicts: seahub/api2/urls.py seahub/api2/views.py seahub/group/templates/group/group_discuss.html seahub/options/models.py seahub/templates/repo_folder_perm.html seahub/templates/repo_share_manage.html seahub/templates/snippets/shared_link_js.html seahub/test_utils.py
This commit is contained in:
commit
ec9be6975d
@ -300,6 +300,9 @@ td, th { padding:5px 3px; word-wrap:break-word; border-bottom:1px solid #eee; }
|
||||
td { color: #333; font-size:14px; }
|
||||
table img { vertical-align:middle; }
|
||||
p { margin:0.5em 0; }
|
||||
:focus {/* to overwrite user agent stylesheet */
|
||||
outline:none;
|
||||
}
|
||||
|
||||
/********** common class ***********/
|
||||
.hl { background-color: #f8f8f8; }/*highlight*/
|
||||
|
@ -218,17 +218,6 @@ $('#logout').click(function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
if ($.browser.mozilla || $.browser.msie) {
|
||||
$('a').focus(function() {
|
||||
$(this).blur();
|
||||
});
|
||||
}
|
||||
if ($.browser.msie) {
|
||||
$('button, input[type="checkbox"], input[type="radio"], input[type="submit"]').focus(function() {
|
||||
$(this).blur();
|
||||
});
|
||||
$('.search-input').css({'line-height':$('.search-input').css('height')});
|
||||
}
|
||||
|
||||
/*
|
||||
* add confirm to an operation, using a popup
|
||||
|
169
media/js/jq.min.js
vendored
169
media/js/jq.min.js
vendored
File diff suppressed because one or more lines are too long
5
media/js/jquery-1.12.1.min.js
vendored
Normal file
5
media/js/jquery-1.12.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -76,7 +76,6 @@ class Account(APIView):
|
||||
profile.intro = note
|
||||
|
||||
profile.save()
|
||||
refresh_profile_cache(email)
|
||||
|
||||
def _update_account_quota(self, request, email):
|
||||
storage = request.data.get("storage", None)
|
||||
|
@ -52,13 +52,14 @@ urlpatterns = patterns('',
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/detail/$', FileDetailView.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/history/$', FileHistory.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/revision/$', FileRevision.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/revert/$', FileRevert.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/revert/$', FileRevert.as_view(), name='api2-file-revert'),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/file/shared-link/$', FileSharedLinkView.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='DirView'),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/sub_repo/$', DirSubRepoView.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/share/$', DirShareView.as_view()),
|
||||
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/dir/shared_items/$', DirSharedItemsEndpoint.as_view(), name="api2-dir-shared-items"),
|
||||
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/revert/$', DirRevert.as_view(), name='api2-dir-revert'),
|
||||
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'^devices/', DevicesView.as_view(), name='api2-devices'),
|
||||
|
@ -1861,6 +1861,7 @@ class OwaFileView(APIView):
|
||||
send_file_access_msg(request, repo, path, 'api')
|
||||
return Response(wopi_dict)
|
||||
|
||||
|
||||
class DevicesView(APIView):
|
||||
"""List user devices"""
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
@ -1894,6 +1895,7 @@ class DevicesView(APIView):
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
|
||||
class FileView(APIView):
|
||||
"""
|
||||
Support uniform interface for file related operations,
|
||||
@ -2268,38 +2270,44 @@ class FileDetailView(APIView):
|
||||
content_type=json_content_type)
|
||||
|
||||
class FileRevert(APIView):
|
||||
authentication_classes = (TokenAuthentication, )
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def put(self, request, repo_id, format=None):
|
||||
path = request.data.get('p', '')
|
||||
path = request.data.get('p', None)
|
||||
commit_id = request.data.get('commit_id', None)
|
||||
|
||||
if not path:
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
|
||||
error_msg = 'path invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not commit_id:
|
||||
error_msg = 'commit_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not seafile_api.get_repo(repo_id):
|
||||
error_msg = 'library %s not found.' % repo_id
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if not seafile_api.get_file_id_by_commit_and_path(repo_id, commit_id, path):
|
||||
error_msg = 'file %s not found.' % path
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if check_folder_permission(request, repo_id, '/') != 'rw':
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
username = request.user.username
|
||||
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
|
||||
if (is_locked, locked_by_me) == (None, None):
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Check file lock error')
|
||||
|
||||
if is_locked and not locked_by_me:
|
||||
return api_error(status.HTTP_403_FORBIDDEN, 'File is locked')
|
||||
|
||||
parent_dir = os.path.dirname(path)
|
||||
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
|
||||
return api_error(status.HTTP_403_FORBIDDEN,
|
||||
'You do not have permission to access this folder.')
|
||||
|
||||
path = unquote(path.encode('utf-8'))
|
||||
commit_id = unquote(request.data.get('commit_id', '').encode('utf-8'))
|
||||
try:
|
||||
ret = seafserv_threaded_rpc.revert_file(repo_id, commit_id,
|
||||
path, username)
|
||||
seafile_api.revert_file(repo_id, commit_id, path, username)
|
||||
except SearpcError as e:
|
||||
logger.error(e)
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, "Internal error")
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
return HttpResponse(json.dumps({"ret": ret}), status=200, content_type=json_content_type)
|
||||
|
||||
class FileRevision(APIView):
|
||||
authentication_classes = (TokenAuthentication, )
|
||||
@ -2698,6 +2706,45 @@ class DirDownloadView(APIView):
|
||||
return HttpResponse(json.dumps(redirect_url), status=200,
|
||||
content_type=json_content_type)
|
||||
|
||||
class DirRevert(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def put(self, request, repo_id):
|
||||
path = request.data.get('p', None)
|
||||
commit_id = request.data.get('commit_id', None)
|
||||
|
||||
if not path:
|
||||
error_msg = 'path invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not commit_id:
|
||||
error_msg = 'commit_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not seafile_api.get_repo(repo_id):
|
||||
error_msg = 'library %s not found.' % repo_id
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if not seafile_api.get_dir_id_by_commit_and_path(repo_id, commit_id, path):
|
||||
error_msg = 'folder %s not found.' % path
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if check_folder_permission(request, repo_id, '/') != 'rw':
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
username = request.user.username
|
||||
try:
|
||||
seafile_api.revert_dir(repo_id, commit_id, path, 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})
|
||||
|
||||
class DirShareView(APIView):
|
||||
authentication_classes = (TokenAuthentication, )
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
@ -71,7 +71,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
||||
if autoescape and not safe_input:
|
||||
lead, trail = escape(lead), escape(trail)
|
||||
url, trimmed = escape(url), escape(trimmed)
|
||||
middle = '<a target="_blank" href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
|
||||
middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
|
||||
words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
|
||||
else:
|
||||
if safe_input:
|
||||
|
@ -187,12 +187,18 @@ class UserOptionsManager(models.Manager):
|
||||
- `self`:
|
||||
- `username`:
|
||||
"""
|
||||
try:
|
||||
user_option = super(UserOptionsManager, self).get(
|
||||
email=username, option_key=KEY_DEFAULT_REPO)
|
||||
return user_option.option_val
|
||||
except UserOptions.DoesNotExist:
|
||||
user_options = super(UserOptionsManager, self).filter(
|
||||
email=username, option_key=KEY_DEFAULT_REPO)
|
||||
|
||||
if len(user_options) == 0:
|
||||
return None
|
||||
elif len(user_options) == 1:
|
||||
return user_options[0].option_val
|
||||
else:
|
||||
for o in user_options[1: len(user_options)]:
|
||||
o.delete()
|
||||
|
||||
return user_options[0].option_val
|
||||
|
||||
def passwd_change_required(self, username):
|
||||
"""Check whether user need to change password.
|
||||
@ -211,10 +217,10 @@ class UserOptionsManager(models.Manager):
|
||||
def unset_force_passwd_change(self, username):
|
||||
return self.unset_user_option(username, KEY_FORCE_PASSWD_CHANGE)
|
||||
|
||||
|
||||
class UserOptions(models.Model):
|
||||
email = LowerCaseCharField(max_length=255, db_index=True)
|
||||
option_key = models.CharField(max_length=50)
|
||||
option_val = models.CharField(max_length=50)
|
||||
|
||||
objects = UserOptionsManager()
|
||||
|
||||
|
@ -129,11 +129,19 @@ class DetailedProfile(models.Model):
|
||||
telephone = models.CharField(max_length=100)
|
||||
objects = DetailedProfileManager()
|
||||
|
||||
########## signal handler
|
||||
|
||||
########## signal handlers
|
||||
from django.db.models.signals import post_save
|
||||
from .utils import refresh_cache
|
||||
|
||||
@receiver(user_registered)
|
||||
def clean_email_id_cache(sender, **kwargs):
|
||||
from seahub.utils import normalize_cache_key
|
||||
|
||||
|
||||
user = kwargs['user']
|
||||
key = normalize_cache_key(user.email, EMAIL_ID_CACHE_PREFIX)
|
||||
cache.set(key, user.id, EMAIL_ID_CACHE_TIMEOUT)
|
||||
|
||||
@receiver(post_save, sender=Profile, dispatch_uid="update_nickname_cache")
|
||||
def update_nickname_cache(sender, instance, **kwargs):
|
||||
refresh_cache(instance.user)
|
||||
|
@ -36,9 +36,7 @@ def edit_profile(request):
|
||||
if form.is_valid():
|
||||
form.save(username=username)
|
||||
messages.success(request, _(u'Successfully edited profile.'))
|
||||
# refresh nickname cache
|
||||
refresh_cache(request.user.username)
|
||||
|
||||
|
||||
return HttpResponseRedirect(reverse('edit_profile'))
|
||||
else:
|
||||
messages.error(request, _(u'Failed to edit profile'))
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% load seahub_tags avatar_tags group_avatar_tags i18n %}
|
||||
{% load seahub_tags avatar_tags group_avatar_tags i18n staticfiles %}
|
||||
{% load url from future %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
@ -21,7 +21,7 @@
|
||||
{% block info_bar_message %}
|
||||
{% if request.user.is_authenticated and request.cur_note %}
|
||||
<div id="info-bar">
|
||||
<p id="info-bar-info">{{ request.cur_note.message|urlize|url_target_blank }}</p>
|
||||
<p id="info-bar-info">{{ request.cur_note.message|urlize }}</p>
|
||||
<span class="close sf2-icon-x1 op-icon" data="{{ request.cur_note.id }}" title="{% trans "Close" %}"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -149,7 +149,10 @@
|
||||
{% include 'footer.html' %}
|
||||
</div><!-- wrapper -->
|
||||
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/jq.min.js?t=1398068110"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery-1.12.1.min.js"></script>
|
||||
<script type="text/javascript" src="{% static "scripts/lib/jquery.simplemodal.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "scripts/lib/jquery.ui.tabs.js" %}"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/jq.min.js"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/base.js?t=1404370380"></script>
|
||||
<script type="text/javascript">
|
||||
$.jstree._themes = '{{ MEDIA_URL }}js/themes/';
|
||||
|
@ -31,7 +31,7 @@
|
||||
{% block info_bar_message %}
|
||||
{% if request.user.is_authenticated and request.cur_note %}
|
||||
<div id="info-bar">
|
||||
<p id="info-bar-info">{{ request.cur_note.message|urlize|url_target_blank }}</p>
|
||||
<p id="info-bar-info">{{ request.cur_note.message|urlize }}</p>
|
||||
<span class="close sf2-icon-x1 op-icon" title="{% trans "Close" %}"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -125,12 +125,31 @@ var get_more_trash = function(current_scan_stat) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$('table').on("click", ".restore-file, .restore-dir", function() {
|
||||
$('<form>', {
|
||||
"method": 'POST',
|
||||
"action": $(this).data('url'),
|
||||
"html": '<input name="csrfmiddlewaretoken" value="' + getCookie('csrftoken') + '" type="hidden">'
|
||||
}).appendTo(document.body).submit();
|
||||
var _this = $(this),
|
||||
commit_id = _this.data('commit_id'),
|
||||
path = _this.data('path');
|
||||
|
||||
$.ajax({
|
||||
url: _this.data('url'),
|
||||
type: 'PUT',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: {'commit_id': commit_id, 'p': path},
|
||||
success: function(data) {
|
||||
_this.closest('tr').remove();
|
||||
feedback("{% trans "Success" %}", 'success');
|
||||
},
|
||||
error: function ajaxErrorHandler(xhr, textStatus, errorThrown) {
|
||||
if (xhr.responseText) {
|
||||
feedback($.parseJSON(xhr.responseText).error_msg, 'error');
|
||||
} else {
|
||||
feedback("{% trans "Failed. Please check the network." %}", 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -38,7 +38,7 @@ $('#add-file-popup .submit').click(function() {
|
||||
$.modal.close();
|
||||
var files = '';
|
||||
for (var i = 0, len = selected.length; i < len; i++) {
|
||||
files += '<li class="item">' + '<img src="{{MEDIA_URL}}img/del.png" class="rm vam" data-index="' + i + '" /><span class="vam">' + selected[i].substr(selected[i].lastIndexOf('/') + 1) + '</span></li>';
|
||||
files += '<li class="item">' + '<img src="{{MEDIA_URL}}img/del.png" class="rm vam" data-index="' + i + '" /><span class="vam">' + HTMLescape(selected[i].substr(selected[i].lastIndexOf('/') + 1)) + '</span></li>';
|
||||
}
|
||||
files_ct.data('files', selected).html(files).removeClass('hide');
|
||||
$('.rm', files_ct).click(function() {
|
||||
|
@ -1,15 +1,9 @@
|
||||
{% load i18n %} {# for file/dir share #}
|
||||
{% load i18n %}
|
||||
<div id="file-share" class="hide">
|
||||
<h3 class="hd">{% trans 'Share %(name)s' %}</h3>
|
||||
<div id="file-share-tabs" class="left-right-tabs ovhd">
|
||||
<ul class="left-right-tabs-nav fleft">
|
||||
<li class="tab"><a href="#link-share" class="a">{% trans "Download Link" %}</a></li>
|
||||
{% if user_perm == 'rw' %}
|
||||
<li class="tab" id="upload-link-share-tab"><a href="#upload-link-share" class="a">{% trans "Upload Link" %}</a></li>{# for dir #}
|
||||
{% endif %}
|
||||
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual and is_repo_owner %}
|
||||
<li class="tab" id="syncable-share-tab"><a href="#syncable-share" class="a">{% trans "Private Share" %}</a></li> {# for dir #}
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<div class="fright">
|
||||
@ -58,51 +52,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user_perm == 'rw' %}
|
||||
<div id="upload-link-share" class="tabs-panel">
|
||||
<p class="tip">{% trans "You can share the generated link to others and then they can upload files to this directory via the link." %}</p>
|
||||
<div id="upload-link-options">
|
||||
<label class="checkbox-label">
|
||||
<span class="checkbox"><input type="checkbox" name="use-passwd" id="upload-link-passwd-switch" class="checkbox-orig" /></span>
|
||||
<span class="checkbox-option">{% trans "Add password protection"%}</span>
|
||||
</label>
|
||||
<div id="upload-link-passwd" class="hide">
|
||||
<label>{% trans "Password"%}</label><span class="tip">{% blocktrans %}(at least {{share_link_password_min_length}} characters){% endblocktrans %}</span><br />
|
||||
<input type="password" name="passwd" disabled="disabled" class="input input-disabled" /><br />
|
||||
<label>{% trans "Password again"%}</label><br />
|
||||
<input type="password" name="passwd_again" disabled="disabled" class="input input-disabled" />
|
||||
</div>
|
||||
<p class="error hide"></p>
|
||||
</div>
|
||||
<button id="gen-upload-link-btn" class="hide">{% trans "Generate"%}</button>
|
||||
<div id="share-upload-link-body" class="hide">
|
||||
<p><span class="vam">{% trans 'Upload Link: ' %}</span><input type="text" readonly="readonly" id="shared-upload-link-text" class="vam" /></p>
|
||||
<button id="send-upload-link">{% trans 'Send' %}</button>
|
||||
<button id="rm-shared-upload-link">{% trans 'Delete' %}</button>
|
||||
|
||||
<form id="upload-link-send-form" action="" method="post" class="hide">{% csrf_token %}
|
||||
<label>{% trans "Send to:"%}</label><br />
|
||||
<input type="text" class="input" id="upload-link-send-input" name="email" placeholder="{% trans "Emails, Seperated by ','"%}" /><br />
|
||||
<input type="hidden" name="shared_upload_link" value="{{ dir_shared_upload_link }}" />
|
||||
<label>{% trans "Message (optional):"%}</label><br />
|
||||
<textarea class="textarea" name="extra_msg" id="upload-extra-msg-text"></textarea><br />
|
||||
<p class="error hide"></p>
|
||||
<input type="submit" value="{% trans "Submit"%}" class="submit" />
|
||||
<input type="button" value="{% trans "Cancel"%}" class="cancel" />
|
||||
<p id="upload-sending" class="hide">{% trans "Sending..."%}</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual and is_repo_owner %}
|
||||
<div id="syncable-share" class="tabs-panel">
|
||||
{% url 'share_repo' as repo_share_url %}
|
||||
{% with post_url=repo_share_url %}
|
||||
{% include "snippets/repo_share_form.html" %} {# for sub-lib share #}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<td><a href="?commit_id={{ dirent.commit_id }}&base={{ dirent.basedir|urlencode }}&p=/{{ dirent.obj_name|urlencode }}&dir_path={{dir_path|urlencode}}">{{ dirent.obj_name }}</a></td>
|
||||
<td>{{ dirent.delete_time|translate_seahub_time }}</td>
|
||||
<td></td>
|
||||
<td><a class="op restore-dir hide" href="#" data-url="{% url 'repo_revert_dir' repo.id %}?commit={{ dirent.commit_id }}&p={{ dirent.basedir|urlencode }}{{dirent.obj_name|urlencode}}">{% trans "Restore" %}</a></td>
|
||||
<td><a class="op restore-dir hide" href="#" data-commit_id="{{dirent.commit_id}}" data-path="{{dirent.basedir}}{{dirent.obj_name}}" data-url="{% url 'api2-dir-revert' repo.id %}">{% trans "Restore" %}</a></td>
|
||||
{% else %}
|
||||
<td><a href="?commit_id={{ commit_id }}&base={{ basedir|urlencode }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&dir_path={{dir_path|urlencode}}">{{ dirent.obj_name }}</a></td>
|
||||
<td></td>
|
||||
@ -23,7 +23,7 @@
|
||||
<td><a class="normal" href="{% url 'view_trash_file' repo.id %}?obj_id={{ dirent.obj_id }}&commit_id={{ dirent.commit_id }}&base={{ dirent.basedir|urlencode }}&p=/{{ dirent.obj_name|urlencode }}" target="_blank">{{ dirent.obj_name }}</a></td>
|
||||
<td>{{ dirent.delete_time|translate_seahub_time }}</td>
|
||||
<td>{{ dirent.file_size|filesizeformat }}</td>
|
||||
<td><a class="op restore-file hide" href="#" data-url="{% url 'repo_revert_file' repo.id %}?commit={{ dirent.commit_id }}&p={{ dirent.basedir|urlencode }}{{dirent.obj_name|urlencode}}">{% trans "Restore" %}</a></td>
|
||||
<td><a class="op restore-file hide" href="#" data-commit_id="{{dirent.commit_id}}" data-path="{{dirent.basedir}}{{dirent.obj_name}}" data-url="{% url 'api2-file-revert' repo.id %}">{% trans "Restore" %}</a></td>
|
||||
{% else %}
|
||||
<td><a class="normal" href="{% url 'view_trash_file' repo.id %}?obj_id={{ dirent.obj_id }}&commit_id={{ commit_id }}&base={{ basedir|urlencode }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}" target="_blank">{{ dirent.obj_name }}</a></td>
|
||||
<td></td>
|
||||
@ -33,4 +33,3 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
var share_list = [], contacts = [];
|
||||
/*
|
||||
var share_list = [];
|
||||
$(function () {
|
||||
$.ajax({
|
||||
url:'{% url 'get_contacts' %}',
|
||||
@ -12,69 +13,21 @@ $(function () {
|
||||
for (var i = 0, len = contact_list.length; i < len; i++) {
|
||||
contact_email = contact_list[i].email;
|
||||
share_list.push({value: contact_email, label: contact_email});
|
||||
contacts.push({value:contact_email, avatar:contact_list[i].avatar});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
function showSharePopup(op, name, aj_data, type, cur_path) {
|
||||
var path;
|
||||
if (op.attr('id') == 'share-cur-dir') {
|
||||
path = cur_path.substr(0, cur_path.length - 1); // rm the last '/' as seafile_api treats '/xx' and '/xx/' as different
|
||||
} else {
|
||||
path = cur_path + name;
|
||||
}
|
||||
var path = cur_path + name;
|
||||
|
||||
var form = $('#file-share');
|
||||
|
||||
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual and is_repo_owner %}
|
||||
var grp_options_ct = $('#share-grp-options');
|
||||
if (!$.trim(grp_options_ct.html())) {
|
||||
var grp_options = '<ul class="option-list">';
|
||||
{% for group in request.user.joined_groups %}
|
||||
grp_options += '<li> <label class="checkbox-label"> <span class="checkbox"><input type="checkbox" name="grp" value="{{ group.group_name }}" class="checkbox-orig"/></span> {{group.avatar|safe}} <span class="checkbox-option">' + "{{ group.group_name }}" + '</span> </label> </li>';
|
||||
{% endfor %}
|
||||
grp_options += '</ul>';
|
||||
grp_options_ct.html(grp_options);
|
||||
}
|
||||
var contact_options_ct = $('#share-contact-options');
|
||||
if (!$.trim(contact_options_ct.html())) {
|
||||
var contact_options = '<ul class="option-list">';
|
||||
for (var i = 0, len = contacts.length; i < len; i++) {
|
||||
contact_email = contacts[i].value;
|
||||
contact_options += ' <li> <label class="checkbox-label"> <span class="checkbox"><input type="checkbox" name="contact" value="' + contact_email + '" class="checkbox-orig" /></span>' + contacts[i].avatar + ' <span class="checkbox-option">' + contact_email + '</span> </label> </li>';
|
||||
}
|
||||
contact_options += '</ul>';
|
||||
contact_options_ct.html(contact_options);
|
||||
}
|
||||
|
||||
$('.checkbox-orig', $('#share-grp-options, #share-contact-options')).click(function() {
|
||||
$(this).parent().toggleClass('checkbox-checked');
|
||||
});
|
||||
$(".checkbox-label", $('#share-grp-options, #share-contact-options')).hover(
|
||||
function() {
|
||||
$(this).addClass('hl');
|
||||
},
|
||||
function() {
|
||||
$(this).removeClass('hl');
|
||||
}
|
||||
);
|
||||
{% endif %}
|
||||
|
||||
form.modal({
|
||||
appendTo: "#main",
|
||||
focus: false
|
||||
});
|
||||
form.modal({appendTo: "#main",'focus':false, containerCss:{"padding":0}});
|
||||
|
||||
var hd = $('#file-share .hd');
|
||||
hd.html(hd.html().replace('%(name)s', '<span class="op-target">' + HTMLescape(trimFilename(name, 30)) + '</span>'));
|
||||
|
||||
if (type == 'd') {
|
||||
$('#private-share-tab, #private-share').remove();
|
||||
} else {
|
||||
$('#syncable-share-tab, #syncable-share, #upload-link-share-tab, #upload-link-share').remove();
|
||||
}
|
||||
$("#file-share-tabs").tabs();
|
||||
|
||||
// share link
|
||||
@ -95,61 +48,6 @@ function showSharePopup(op, name, aj_data, type, cur_path) {
|
||||
$('input[name="file_shared_name"]').val(name);
|
||||
$('input[name="file_shared_type"]').val(type);
|
||||
|
||||
{% if user_perm == 'rw' %}
|
||||
// share upload link
|
||||
$('#upload-link-share-tab .a').click(function() {
|
||||
if (op.attr('data-upload-link')) {
|
||||
$('#gen-upload-link-btn, #upload-link-options').addClass('hide');
|
||||
$('#share-upload-link-body').removeClass('hide');
|
||||
var link = op.attr('data-upload-link');
|
||||
$('#shared-upload-link-text, #upload-link-send-form input[name="shared_upload_link"]').val(link);
|
||||
$('#main').append('<p id="linkwidth" class="hide">' + link + '</p>');
|
||||
$('#shared-upload-link-text').css({'width':$('#linkwidth').width() + 25});
|
||||
$('#linkwidth').remove();
|
||||
} else {
|
||||
$('#gen-upload-link-btn, #upload-link-options').removeClass('hide');
|
||||
$('#share-upload-link-body').addClass('hide');
|
||||
}
|
||||
$('#gen-upload-link-btn').data('aj_data', aj_data).data('obj', op);
|
||||
$('#rm-shared-upload-link').data('obj', op);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
// 'private share' for file
|
||||
$('#private-share-tab a').click(function() {
|
||||
var form = $('#private-share-form');
|
||||
$("input[name=s_type]", form).val(type);
|
||||
$("input[name=path]", form).val(path);
|
||||
|
||||
var opts = '', email, avatar;
|
||||
for (var i = 0, len = contacts.length; i < len; i++) {
|
||||
email = contacts[i].value;
|
||||
opts += '<option value="' + email + '" data-index="' + i + '">' + email + '</option>';
|
||||
}
|
||||
var format = function(item) {
|
||||
return contacts[$(item.element).data('index')].avatar + '<span class="vam">' + item.text + '</span>';
|
||||
}
|
||||
$('[name="emails"]', form).html(opts).select2({
|
||||
placeholder: "{% trans "Select contacts" %}",
|
||||
formatResult: format,
|
||||
formatSelection: format,
|
||||
escapeMarkup: function(m) { return m; }
|
||||
});
|
||||
});
|
||||
|
||||
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual and is_repo_owner %}
|
||||
// syncable share for dir
|
||||
$('#syncable-share-tab a').click(function() {
|
||||
var form = $("#repo-share-form");
|
||||
form.removeClass('hide').css({'width':'auto', 'padding-top':0});
|
||||
$("h3", form).remove();
|
||||
$('.checkbox-label', form).css({'margin-right':'3px'}); // make it not show on top of the scrollbar when hover
|
||||
form.data('dir-path', path);
|
||||
$("#repo-share-tabs").tabs();
|
||||
$('#repo-share-tabs .ui-tabs-nav').css({'padding-left': '1.4em'});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$('#simplemodal-container').css({'height':'auto', 'width':'auto'});
|
||||
}
|
||||
|
||||
@ -161,7 +59,7 @@ $('#send-link').click(function() {
|
||||
var text = $('#download-extra-msg-text');
|
||||
text.css({'width': $('#link-share').width() - parseInt(text.css('padding-left')) - parseInt(text.css('padding-right')) - parseInt(text.css('border-left-width')) - parseInt(text.css('border-right-width'))});
|
||||
$('#link-send-form').removeClass('hide');
|
||||
addAutocomplete('#link-send-input', '#link-send-form', share_list);
|
||||
//addAutocomplete('#link-send-input', '#link-send-form', share_list);
|
||||
});
|
||||
|
||||
$("#link-send-form .cancel").click(function() {
|
||||
@ -241,8 +139,8 @@ $('#gen-link-btn').click(function() {
|
||||
obj = gen_link_btn.data('obj'),
|
||||
form = $('#link-options'),
|
||||
form_id = form.attr('id'),
|
||||
use_passwd = $('#link-passwd-switch').attr('checked'),
|
||||
set_expiration = $('#link-expire-switch').attr('checked'),
|
||||
use_passwd = $('#link-passwd-switch').prop('checked'),
|
||||
set_expiration = $('#link-expire-switch').prop('checked'),
|
||||
passwd, passwd_again, expire_days, post_data;
|
||||
|
||||
if (use_passwd) {
|
||||
@ -332,193 +230,12 @@ $('#rm-shared-link').click(function() {
|
||||
});
|
||||
});
|
||||
|
||||
{% if user_perm == 'rw' %}
|
||||
$('#shared-upload-link-text').click(function() {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('#send-upload-link').click(function() {
|
||||
$(this).addClass('hide');
|
||||
$('#rm-shared-upload-link').addClass('hide');
|
||||
var input = $('#upload-link-send-input');
|
||||
input.css({'width': $('#upload-link-share').width() - parseInt(input.css('padding-left')) - parseInt(input.css('padding-right')) - parseInt(input.css('border-left-width')) - parseInt(input.css('border-right-width'))});
|
||||
var text = $('#upload-extra-msg-text');
|
||||
text.css({'width': $('#upload-link-share').width() - parseInt(text.css('padding-left')) - parseInt(text.css('padding-right')) - parseInt(text.css('border-left-width')) - parseInt(text.css('border-right-width'))});
|
||||
$('#upload-link-send-form').removeClass('hide');
|
||||
addAutocomplete('#upload-link-send-input', '#upload-link-send-form', share_list);
|
||||
});
|
||||
|
||||
$("#upload-link-send-form .cancel").click(function() {
|
||||
$('#upload-link-send-form, #send-upload-link, #rm-shared-upload-link').toggleClass('hide');
|
||||
});
|
||||
|
||||
$("#upload-link-send-form").submit(function(event) {
|
||||
var form = $(this),
|
||||
shared_upload_link = form.children('input[name="shared_upload_link"]').val(),
|
||||
email = $.trim(form.children('input[name="email"]').val()),
|
||||
submit_btn = form.children('input[type="submit"]'),
|
||||
extra_msg = form.children('textarea[name="extra_msg"]').val();
|
||||
|
||||
if (!email) {
|
||||
apply_form_error('upload-link-send-form', "{% trans "Please input at least an email." %}");
|
||||
return false;
|
||||
}
|
||||
|
||||
disable(submit_btn);
|
||||
$('#upload-link-send-form .error').addClass('hide');
|
||||
$('#upload-sending').removeClass('hide');
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "{% url 'send_shared_upload_link' %}",
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: {
|
||||
shared_upload_link: shared_upload_link,
|
||||
email: email,
|
||||
extra_msg: extra_msg
|
||||
},
|
||||
success: function(data) {
|
||||
$.modal.close();
|
||||
var msg = "{% trans "Successfully sent to {placeholder}" %}"
|
||||
.replace('{placeholder}', data['send_success'].join(', '));
|
||||
feedback(msg, "success");
|
||||
if (data['send_failed'].length > 0) {
|
||||
msg += '<br />' + "{% trans "Failed to send to {placeholder}" %}"
|
||||
.replace('{placeholder}', data['send_failed'].join(', '));
|
||||
feedback(msg, 'info');
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
$('#upload-sending').addClass('hide');
|
||||
enable(submit_btn);
|
||||
var err_str = '';
|
||||
if (xhr.responseText) {
|
||||
var err = jQuery.parseJSON(xhr.responseText);
|
||||
if (err.error) {
|
||||
err_str = err.error;
|
||||
} else {
|
||||
for (var i in err) {
|
||||
err_str += err[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err_str = "{% trans "Failed. Please check the network." %}";
|
||||
}
|
||||
apply_form_error('upload-link-send-form', err_str);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gen-upload-link-btn').click(function() {
|
||||
var gen_upload_link_btn = $(this),
|
||||
obj = gen_upload_link_btn.data('obj'),
|
||||
form = $('#upload-link-options'),
|
||||
form_id = form.attr('id'),
|
||||
passwd_switch = $('#upload-link-passwd-switch'),
|
||||
use_passwd = passwd_switch.attr('checked'),
|
||||
passwd, passwd_again, post_data;
|
||||
|
||||
if (use_passwd) {
|
||||
passwd = $('input[name="passwd"]', form).val();
|
||||
passwd_again = $('input[name="passwd_again"]', form).val();
|
||||
|
||||
if (!$.trim(passwd)) {
|
||||
apply_form_error(form_id, "{% trans "Please enter password" %}");
|
||||
return false;
|
||||
}
|
||||
if ($.trim(passwd).length < {{share_link_password_min_length}}) {
|
||||
apply_form_error(form_id, "{% trans "Password is too short" %}");
|
||||
return false;
|
||||
}
|
||||
if (!$.trim(passwd_again)) {
|
||||
apply_form_error(form_id, "{% trans "Please enter the password again" %}");
|
||||
return false;
|
||||
}
|
||||
if ($.trim(passwd) != $.trim(passwd_again)) {
|
||||
apply_form_error(form_id, "{% trans "Passwords don't match" %}");
|
||||
return false;
|
||||
}
|
||||
post_data = {'use_passwd': 1, 'passwd': passwd};
|
||||
} else {
|
||||
post_data = {'use_passwd': 0};
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '{% url 'ajax_get_upload_link' %}',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: $.extend(post_data, {
|
||||
'repo_id': $(this).data('aj_data').repo_id,
|
||||
'p': $(this).data('aj_data').p
|
||||
}),
|
||||
success: function(data) {
|
||||
var upload_link = data['upload_link'];
|
||||
gen_upload_link_btn.addClass('hide');
|
||||
|
||||
$('#upload-link-options, #upload-link-options .error').addClass('hide');
|
||||
$('#upload-link-passwd').hide();
|
||||
$('#upload-link-passwd-switch').attr('checked', false).parent().removeClass('checkbox-checked');
|
||||
$('[type="password"]', form).val('').attr('disabled', false).removeClass('input-disabled');
|
||||
|
||||
$('#shared-upload-link-text, #upload-link-send-form input[name="shared_upload_link"]').val(upload_link);
|
||||
$('#main').append('<p id="linkwidth" class="hide">' + upload_link + '</p>');
|
||||
$('#shared-upload-link-text').css({'width':$('#linkwidth').width() + 25});
|
||||
$('#linkwidth').remove();
|
||||
$('#share-upload-link-body').removeClass('hide');
|
||||
obj.attr({'data-upload-link': upload_link, 'data-upload-token': data['token']});
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
location.reload(true);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#rm-shared-upload-link').click(function() {
|
||||
var obj = $(this).data('obj'),
|
||||
token = obj.attr('data-upload-token');
|
||||
|
||||
$.ajax({
|
||||
url: '{% url 'ajax_remove_shared_upload_link' %}',
|
||||
type: 'POST',
|
||||
data: {'t': token},
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
success: function(data) {
|
||||
$('#share-upload-link-body').addClass('hide');
|
||||
$('#upload-link-options, #gen-upload-link-btn').removeClass('hide');
|
||||
obj.attr({'data-upload-link': '', 'data-upload-token':''});
|
||||
},
|
||||
error:ajaxErrorHandler
|
||||
});
|
||||
});
|
||||
|
||||
$('#upload-link-passwd-switch').click(function () {
|
||||
var form = $('#upload-link-options'),
|
||||
pwd_input = $('input[type="password"]', form);
|
||||
var link_passwd = $('#upload-link-passwd');
|
||||
|
||||
if ($(this).attr('checked')) {
|
||||
pwd_input.attr('disabled', false).removeClass('input-disabled');
|
||||
link_passwd.slideDown(100);
|
||||
} else {
|
||||
link_passwd.slideUp(100);
|
||||
pwd_input.attr('disabled', true).addClass('input-disabled');
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$('#link-passwd-switch').click(function () {
|
||||
var form = $('#link-options'),
|
||||
pwd_input = $('input[type="password"]', form);
|
||||
var link_passwd = $('#link-passwd');
|
||||
|
||||
if ($(this).attr('checked')) {
|
||||
if ($(this).prop('checked')) {
|
||||
pwd_input.attr('disabled', false).removeClass('input-disabled');
|
||||
link_passwd.slideDown(100);
|
||||
} else {
|
||||
@ -532,7 +249,7 @@ $('#link-expire-switch').click(function () {
|
||||
days_input = $('input[name="expire-days"]', form);
|
||||
var link_expire = $('#link-expire');
|
||||
|
||||
if ($(this).attr('checked')) {
|
||||
if ($(this).prop('checked')) {
|
||||
link_expire.slideDown(100);
|
||||
days_input.attr('disabled', false).removeClass('input-disabled');
|
||||
} else {
|
||||
|
@ -49,51 +49,43 @@
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
var user_list = [], user_email;
|
||||
{% for user in not_admin_users %}
|
||||
user_email = '{{ user.email }}';
|
||||
user_list.push({value:user_email, label:user_email});
|
||||
{% endfor %}
|
||||
$('#add-admin-btn').click(function() {
|
||||
var form = $("#add-admin-form");
|
||||
form.modal({appendTo: "#main", focus:false});
|
||||
$('#simplemodal-container').css({'height':'auto', 'padding':0});
|
||||
$('#add-admin-tabs').tabs();
|
||||
addAutocomplete('#added-member-name', '#enter', user_list);
|
||||
|
||||
});
|
||||
$('#add-admin-form').submit(function() {
|
||||
var form = $(this),
|
||||
cur_tab_id = $('.ui-tabs-selected a', form).attr('href'),
|
||||
post_data = '',
|
||||
input = $('[name="user_email"]', form);
|
||||
post_data = input.val();
|
||||
var $form = $(this),
|
||||
emails = $.trim($('[name="user_email"]', $form).val());
|
||||
|
||||
if (!post_data) {
|
||||
apply_form_error(form.attr('id'), '{% trans "Please enter emails, or select some." %}');
|
||||
if (!emails) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var submit_btn = $('[type="submit"]', form);
|
||||
disable(submit_btn);
|
||||
var $submitBtn = $('[type="submit"]', $form);
|
||||
disable($submitBtn);
|
||||
$.ajax({
|
||||
url: '{% url 'batch_user_make_admin' %}',
|
||||
url: '{% url 'batch_user_make_admin' %}',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: {
|
||||
'set_admin_emails': post_data
|
||||
'set_admin_emails': emails
|
||||
},
|
||||
success: function(data) {
|
||||
location.reload('true');
|
||||
success: function() {
|
||||
location.reload(true);
|
||||
},
|
||||
error: function(data, textStatus, jqXHR) {
|
||||
var errors = $.parseJSON(data.responseText);
|
||||
$.each(errors, function(index, value) {
|
||||
apply_form_error(form.attr('id'), value);
|
||||
});
|
||||
enable(submit_btn);
|
||||
error: function(xhr) {
|
||||
var error_msg;
|
||||
if (xhr.responseText) {
|
||||
error_msg = $.parseJSON(xhr.responseText).error;
|
||||
} else {
|
||||
error_msg = "{% trans "Failed. Please check the network." %}";
|
||||
}
|
||||
$('.error', $form).html(error_msg).removeClass('hide');
|
||||
enable($submitBtn);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
from .settings import *
|
||||
|
||||
# no cache for testing
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
}
|
||||
}
|
||||
# CACHES = {
|
||||
# 'default': {
|
||||
# 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
# }
|
||||
# }
|
||||
|
||||
# enlarge api throttle
|
||||
REST_FRAMEWORK = {
|
||||
@ -15,3 +15,7 @@ REST_FRAMEWORK = {
|
||||
'user': '300/minute',
|
||||
},
|
||||
}
|
||||
|
||||
# Use static file storage instead of cached, since the cached need to run collect
|
||||
# command first.
|
||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
|
||||
|
@ -1,6 +1,7 @@
|
||||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.http import SimpleCookie
|
||||
@ -161,3 +162,8 @@ class BaseTestCase(TestCase, Fixtures):
|
||||
if session_cookie:
|
||||
session.delete(session_key=session_cookie.value)
|
||||
self.client.cookies = SimpleCookie()
|
||||
|
||||
def clear_cache(self):
|
||||
# clear cache between every test case to avoid config option cache
|
||||
# issue which cause test failed
|
||||
cache.clear()
|
||||
|
63
tests/api/test_dir_revert.py
Normal file
63
tests/api/test_dir_revert.py
Normal file
@ -0,0 +1,63 @@
|
||||
import os
|
||||
import json
|
||||
from seaserv import seafile_api
|
||||
from django.core.urlresolvers import reverse
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
class DirRevertTest(BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.repo_id = self.repo.id
|
||||
self.folder_path = self.folder
|
||||
self.parent_dir = os.path.dirname(self.folder_path)
|
||||
self.folder_name = os.path.basename(self.folder_path)
|
||||
self.username = self.user.username
|
||||
|
||||
self.url = reverse('api2-dir-revert', args=[self.repo_id])
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_repo()
|
||||
|
||||
def delete_dir(self):
|
||||
seafile_api.del_file(self.repo_id, self.parent_dir,
|
||||
self.folder_name, self.username)
|
||||
|
||||
def get_trash_dir_commit_id(self):
|
||||
deleted_file = seafile_api.get_deleted(self.repo_id, 0, '/', None)
|
||||
|
||||
return deleted_file[0].commit_id
|
||||
|
||||
def get_lib_folder_name(self):
|
||||
|
||||
url = reverse('list_lib_dir', args=[self.repo_id])
|
||||
resp = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
json_resp = json.loads(resp.content)
|
||||
|
||||
if len(json_resp['dirent_list']) == 0:
|
||||
return None
|
||||
|
||||
return json_resp['dirent_list'][0]['obj_name']
|
||||
|
||||
def test_can_revert_dir(self):
|
||||
self.login_as(self.user)
|
||||
|
||||
# check file exist when init
|
||||
assert self.get_lib_folder_name() == self.folder_name
|
||||
|
||||
# delete
|
||||
self.delete_dir()
|
||||
|
||||
# check file not exist after delete
|
||||
assert self.get_lib_folder_name() == None
|
||||
|
||||
# get commit_id of deleted file
|
||||
commit_id = self.get_trash_dir_commit_id()
|
||||
|
||||
resp = self.client.put(self.url,
|
||||
"p=%s&commit_id=%s" % (self.folder_path, commit_id),
|
||||
'application/x-www-form-urlencoded',
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check file has been reverted
|
||||
assert self.get_lib_folder_name() == self.folder_name
|
67
tests/api/test_file_revert.py
Normal file
67
tests/api/test_file_revert.py
Normal file
@ -0,0 +1,67 @@
|
||||
import os
|
||||
import json
|
||||
from seaserv import seafile_api
|
||||
from django.core.urlresolvers import reverse
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
class FileRevertTest(BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.repo_id = self.repo.id
|
||||
self.file_path = self.file
|
||||
self.parent_dir = os.path.dirname(self.file_path)
|
||||
self.file_name = os.path.basename(self.file_path)
|
||||
self.username = self.user.username
|
||||
|
||||
self.url = reverse('api2-file-revert', args=[self.repo_id])
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_repo()
|
||||
|
||||
def delete_file(self):
|
||||
seafile_api.del_file(self.repo_id, self.parent_dir,
|
||||
self.file_name, self.username)
|
||||
|
||||
def get_trash_file_commit_id(self):
|
||||
deleted_file = seafile_api.get_deleted(self.repo_id, 0, '/', None)
|
||||
|
||||
return deleted_file[0].commit_id
|
||||
|
||||
def get_lib_file_name(self):
|
||||
|
||||
url = reverse('list_lib_dir', args=[self.repo_id])
|
||||
resp = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
json_resp = json.loads(resp.content)
|
||||
|
||||
if len(json_resp['dirent_list']) == 0:
|
||||
return None
|
||||
|
||||
return json_resp['dirent_list'][0]['obj_name']
|
||||
|
||||
def test_can_revert_file(self):
|
||||
self.login_as(self.user)
|
||||
|
||||
# check file exist when init
|
||||
assert self.get_lib_file_name() == self.file_name
|
||||
|
||||
# delete
|
||||
self.delete_file()
|
||||
|
||||
# check file not exist after delete
|
||||
assert self.get_lib_file_name() == None
|
||||
|
||||
# get commit_id of deleted file
|
||||
commit_id = self.get_trash_file_commit_id()
|
||||
|
||||
resp = self.client.put(self.url,
|
||||
"p=%s&commit_id=%s" % (self.file_path, commit_id),
|
||||
'application/x-www-form-urlencoded',
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check file has been reverted
|
||||
assert self.get_lib_file_name() == self.file_name
|
||||
|
||||
def test_can_revert_unicode_filename(self):
|
||||
# todo
|
||||
pass
|
@ -18,6 +18,8 @@ class RepoPublicTest(BaseTestCase):
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_repo(self.repo_id)
|
||||
# clear cache between every test case to avoid config option cache issue
|
||||
self.clear_cache()
|
||||
|
||||
def test_admin_can_set_pub_repo(self):
|
||||
self.login_as(self.admin)
|
||||
|
@ -16,6 +16,7 @@ class SharedRepoTest(BaseTestCase):
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_repo(self.repo_id)
|
||||
self.clear_cache()
|
||||
|
||||
def test_admin_can_share_repo_to_public(self):
|
||||
self.login_as(self.admin)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from seahub.test_utils import BaseTestCase
|
||||
from seahub.options.models import (UserOptions, KEY_USER_GUIDE,
|
||||
VAL_USER_GUIDE_ON, VAL_USER_GUIDE_OFF)
|
||||
VAL_USER_GUIDE_ON, VAL_USER_GUIDE_OFF,
|
||||
KEY_DEFAULT_REPO)
|
||||
|
||||
class UserOptionsManagerTest(BaseTestCase):
|
||||
def test_is_user_guide_enabled(self):
|
||||
@ -25,3 +26,28 @@ class UserOptionsManagerTest(BaseTestCase):
|
||||
assert UserOptions.objects.is_user_guide_enabled(self.user.email) is True
|
||||
assert len(UserOptions.objects.filter(email=self.user.email,
|
||||
option_key=KEY_USER_GUIDE)) == 1
|
||||
|
||||
def test_get_default_repo(self):
|
||||
assert len(UserOptions.objects.filter(email=self.user.email, option_key=KEY_DEFAULT_REPO)) == 0
|
||||
|
||||
UserOptions.objects.create(email=self.user.email,
|
||||
option_key=KEY_DEFAULT_REPO,
|
||||
option_val=self.repo.id)
|
||||
|
||||
assert len(UserOptions.objects.filter(email=self.user.email, option_key=KEY_DEFAULT_REPO)) == 1
|
||||
assert UserOptions.objects.get_default_repo(self.user.email) is not None
|
||||
|
||||
def test_get_default_repo_with_multiple_records(self):
|
||||
assert len(UserOptions.objects.filter(email=self.user.email, option_key=KEY_DEFAULT_REPO)) == 0
|
||||
|
||||
UserOptions.objects.create(email=self.user.email,
|
||||
option_key=KEY_DEFAULT_REPO,
|
||||
option_val=self.repo.id)
|
||||
|
||||
UserOptions.objects.create(email=self.user.email,
|
||||
option_key=KEY_DEFAULT_REPO,
|
||||
option_val=self.repo.id)
|
||||
|
||||
assert len(UserOptions.objects.filter(email=self.user.email, option_key=KEY_DEFAULT_REPO)) == 2
|
||||
assert UserOptions.objects.get_default_repo(self.user.email) is not None
|
||||
assert len(UserOptions.objects.filter(email=self.user.email, option_key=KEY_DEFAULT_REPO)) == 1
|
||||
|
35
tests/seahub/profile/models/test_update_nickname_cache.py
Normal file
35
tests/seahub/profile/models/test_update_nickname_cache.py
Normal file
@ -0,0 +1,35 @@
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
from seahub.profile.models import Profile
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
from tests.common.utils import randstring
|
||||
|
||||
|
||||
class UpdateNicknameCacheTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
self.tmp_user = self.create_user('user_%s@test.com' % randstring(4),
|
||||
is_staff=False)
|
||||
assert len(Profile.objects.all()) == 0
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_user(self.tmp_user.username)
|
||||
|
||||
def test_update_when_call_object_method(self):
|
||||
username = self.tmp_user.username
|
||||
assert email2nickname(username) == username.split('@')[0]
|
||||
|
||||
Profile.objects.add_or_update(username, 'nickname')
|
||||
assert email2nickname(username) == 'nickname'
|
||||
|
||||
def test_updated_when_call_save(self):
|
||||
username = self.tmp_user.username
|
||||
assert email2nickname(username) == username.split('@')[0]
|
||||
|
||||
p = Profile.objects.get_profile_by_user(username)
|
||||
if p is None:
|
||||
p = Profile(user=username)
|
||||
|
||||
p.nickname = 'nickname'
|
||||
p.save()
|
||||
|
||||
assert email2nickname(username) == 'nickname'
|
33
tests/seahub/profile/views/test_edit_profile.py
Normal file
33
tests/seahub/profile/views/test_edit_profile.py
Normal file
@ -0,0 +1,33 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
from seahub.profile.models import Profile
|
||||
from seahub.test_utils import BaseTestCase
|
||||
from tests.common.utils import randstring
|
||||
|
||||
class EditProfileTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
self.tmp_user = self.create_user('user_%s@test.com' % randstring(4),
|
||||
is_staff=False)
|
||||
assert len(Profile.objects.all()) == 0
|
||||
|
||||
self.url = reverse('edit_profile')
|
||||
self.login_as(self.tmp_user)
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_user(self.tmp_user.username)
|
||||
|
||||
def test_can_render_edit_page(self):
|
||||
resp = self.client.get(self.url)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertTemplateUsed(resp, 'profile/set_profile.html')
|
||||
|
||||
def test_can_edit(self):
|
||||
assert email2nickname(self.tmp_user.username) == self.tmp_user.username.split('@')[0]
|
||||
|
||||
resp = self.client.post(self.url, {
|
||||
'nickname': 'new nickname'
|
||||
})
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertRegexpMatches(resp['Location'], r'http://testserver/profile/')
|
||||
assert email2nickname(self.tmp_user.username) == 'new nickname'
|
@ -27,6 +27,8 @@ class LibrariesTest(BaseTestCase):
|
||||
assert UserOptions.objects.is_user_guide_enabled(username) is False
|
||||
|
||||
def test_pub_repo_creation_config(self):
|
||||
self.clear_cache()
|
||||
|
||||
# user
|
||||
self.login_as(self.user)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user