1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-08 10:22:46 +00:00

Merge branch 'setLibHistory'

Conflicts:
	seahub/urls.py
This commit is contained in:
zhengxie
2017-08-23 15:37:29 +08:00
12 changed files with 209 additions and 44 deletions

View File

@@ -0,0 +1,88 @@
import logging
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django.utils.translation import ugettext as _
from pysearpc import SearpcError
from seaserv import seafile_api
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from constance import config
logger = logging.getLogger(__name__)
class AdminLibraryHistoryLimit(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAdminUser, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# no settings for virtual repo
if repo.is_virtual:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
keep_days = seafile_api.get_repo_history_limit(repo_id)
return Response({'keep_days': keep_days})
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
def put(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# no settings for virtual repo
if repo.is_virtual or \
not config.ENABLE_REPO_HISTORY_SETTING:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# check arg validation
keep_days = request.data.get('keep_days', None)
if not keep_days:
error_msg = 'keep_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
keep_days = int(keep_days)
except ValueError:
error_msg = 'keep_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
# days <= -1, keep full history
# days = 0, not keep history
# days > 0, keep a period of days
res = seafile_api.set_repo_history_limit(repo_id, keep_days)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if res == 0:
new_limit = seafile_api.get_repo_history_limit(repo_id)
return Response({'keep_days': new_limit})
else:
error_msg = 'Failed to set library history limit.'
return api_error(status.HTTP_520_OPERATION_FAILED, error_msg)

View File

@@ -106,9 +106,10 @@
</div> </div>
</div> </div>
{% include 'js/templates.html' %}
</div><!-- wrapper --> </div><!-- wrapper -->
{% include 'js/common-templates.html' %}
{% include 'js/templates.html' %}
<script type="text/javascript"> <script type="text/javascript">
var app = { var app = {
config: { config: {

View File

@@ -0,0 +1,34 @@
{# templates in this file are used for both 'app' & 'sysadmin-app' #}
{% load i18n %}
<script type="text/template" id="history-settings-dialog-tmpl">
<h3 id="dialogTitle"><%= title %></h3>
<p class="history-settings-notice outstanding-tip hide">{% trans "Setting library history is disabled by Admin" %}</p>
<form action="" method="post">
<label class="radio-item">
<input type="radio" name="history" value="full_history" class="vam" />
<span class="vam">{% trans "Keep full history" %}</span>
</label>
<br />
<label class="radio-item">
<input type="radio" name="history" value="no_history" class="vam" />
<span class="vam">{% trans "Don't keep history" %}</span>
</label>
<br />
<label class="radio-item">
<input type="radio" name="history" value="partial_history" class="vam" />
<span calss="vam">{% trans "Only keep a period of history:" %}
<input type="text" name="days" size="4" disabled="disabled" class="input-disabled" value="<%= default_history_limit %>" /> {% trans "days" %}
</span>
</label>
<br />
<input type="submit" value="{% trans "Submit" %}" class="submit" />
</form>
<span class="loading-icon loading-tip"></span>
<p class="error hide"></p>
</script>

View File

@@ -410,9 +410,15 @@
<td> <td>
<a href="#" class="sf2-icon-delete sf2-x repo-delete-btn op-icon vh" title="{% trans "Delete" %}" aria-label="{% trans "Delete" %}"></a> <a href="#" class="sf2-icon-delete sf2-x repo-delete-btn op-icon vh" title="{% trans "Delete" %}" aria-label="{% trans "Delete" %}"></a>
<a href="#" class="sf2-icon-move sf2-x repo-transfer-btn op-icon vh" title="{% trans "Transfer" %}" aria-label="{% trans "Transfer" %}"></a> <a href="#" class="sf2-icon-move sf2-x repo-transfer-btn op-icon vh" title="{% trans "Transfer" %}" aria-label="{% trans "Transfer" %}"></a>
<div class="sf-dropdown sf-dropdown-inline">
<a href="#" class="sf2-icon-caret-down more-op-icon op-icon vh sf-dropdown-toggle" title="{% trans "More Operations" %}" aria-label="{% trans "More Operations" %}"></a>
<ul class="hidden-op repo-hidden-op hide sf-dropdown-menu">
<% if (!encrypted) { %> <% if (!encrypted) { %>
<a href="#" class="sf2-icon-share sf2-x repo-share-btn op-icon vh" title="{% trans "Share" %}" aria-label="{% trans "Share" %}"></a> <li><a class="op js-repo-share" href="#">{% trans "Share" %}</a></li>
<% } %> <% } %>
<li><a class="op js-popup-history-setting" href="#">{% trans "History Setting" %}</a></li>
</ul>
</div>
</td> </td>
</script> </script>

View File

@@ -1798,39 +1798,6 @@
<% } %> <% } %>
</script> </script>
<script type="text/template" id="history-settings-dialog-tmpl">
<h3 id="dialogTitle"><%= title %></h3>
<p class="history-settings-notice outstanding-tip hide">{% trans "Setting library history is disabled by Admin" %}</p>
<form action="" method="post">
<label class="radio-item">
<input type="radio" name="history" value="full_history" class="vam" />
<span class="vam">{% trans "Keep full history" %}</span>
</label>
<br />
<label class="radio-item">
<input type="radio" name="history" value="no_history" class="vam" />
<span class="vam">{% trans "Don't keep history" %}</span>
</label>
<br />
<label class="radio-item">
<input type="radio" name="history" value="partial_history" class="vam" />
<span calss="vam">{% trans "Only keep a period of history:" %}
<input type="text" name="days" size="4" disabled="disabled" class="input-disabled" value="<%= default_history_limit %>" /> {% trans "days" %}
</span>
</label>
<br />
<input type="submit" value="{% trans "Submit" %}" class="submit" />
</form>
<span class="loading-icon loading-tip"></span>
<p class="error hide"></p>
</script>
<script type="text/template" id="repo-shared-links-admin-dialog-tmpl"> <script type="text/template" id="repo-shared-links-admin-dialog-tmpl">
<h3 id="dialogTitle"><%= title %></h3> <h3 id="dialogTitle"><%= title %></h3>
<div id="repo-shared-links" class="nav-con-tabs js-tabs"> <div id="repo-shared-links" class="nav-con-tabs js-tabs">

View File

@@ -72,9 +72,10 @@
</div> </div>
</div> </div>
{% include 'js/sysadmin-templates.html' %}
</div><!-- wrapper --> </div><!-- wrapper -->
{% include 'js/common-templates.html' %}
{% include 'js/sysadmin-templates.html' %}
{% include "js/lib-op-popups.html" %} {% include "js/lib-op-popups.html" %}
<script type="text/javascript"> <script type="text/javascript">
var app = { var app = {
@@ -98,6 +99,7 @@ app["pageOptions"] = {
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 }},
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 %},
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 %},
folder_perm_enabled: {% if folder_perm_enabled %} true {% else %} false {% endif %}, folder_perm_enabled: {% if folder_perm_enabled %} true {% else %} false {% endif %},
is_pro: {% if is_pro %} true {% else %} false {% endif %}, is_pro: {% if is_pro %} true {% else %} false {% endif %},

View File

@@ -84,6 +84,7 @@ from seahub.api2.endpoints.admin.logo import AdminLogo
from seahub.api2.endpoints.admin.favicon import AdminFavicon from seahub.api2.endpoints.admin.favicon import AdminFavicon
from seahub.api2.endpoints.admin.license import AdminLicense from seahub.api2.endpoints.admin.license import AdminLicense
from seahub.api2.endpoints.admin.invitations import InvitationsView as AdminInvitationsView from seahub.api2.endpoints.admin.invitations import InvitationsView as AdminInvitationsView
from seahub.api2.endpoints.admin.library_history import AdminLibraryHistoryLimit
# Uncomment the next two lines to enable the admin: # Uncomment the next two lines to enable the admin:
#from django.contrib import admin #from django.contrib import admin
@@ -281,6 +282,7 @@ urlpatterns = patterns(
## admin::libraries ## admin::libraries
url(r'^api/v2.1/admin/libraries/$', AdminLibraries.as_view(), name='api-v2.1-admin-libraries'), url(r'^api/v2.1/admin/libraries/$', AdminLibraries.as_view(), name='api-v2.1-admin-libraries'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminLibrary.as_view(), name='api-v2.1-admin-library'), url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/$', AdminLibrary.as_view(), name='api-v2.1-admin-library'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/history-limit/$', AdminLibraryHistoryLimit.as_view(), name="api-v2.1-admin-library-history-limit"),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirents/$', AdminLibraryDirents.as_view(), name='api-v2.1-admin-library-dirents'), url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirents/$', AdminLibraryDirents.as_view(), name='api-v2.1-admin-library-dirents'),
url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirent/$', AdminLibraryDirent.as_view(), name='api-v2.1-admin-library-dirent'), url(r'^api/v2.1/admin/libraries/(?P<repo_id>[-0-9a-f]{36})/dirent/$', AdminLibraryDirent.as_view(), name='api-v2.1-admin-library-dirent'),

View File

@@ -13,6 +13,7 @@ define([
initialize: function(options) { initialize: function(options) {
this.repo_name = options.repo_name; this.repo_name = options.repo_name;
this.repo_id = options.repo_id; this.repo_id = options.repo_id;
this.url_name = options.url_name;
this.render(); this.render();
this.$('.op-target').css({'max-width':280}); // for long repo name this.$('.op-target').css({'max-width':280}); // for long repo name
@@ -48,7 +49,7 @@ define([
$.ajax({ $.ajax({
url: Common.getUrl({ url: Common.getUrl({
'name': 'repo_history_limit', 'name': this.url_name,
'repo_id': this.repo_id 'repo_id': this.repo_id
}), }),
type: 'get', type: 'get',
@@ -120,7 +121,7 @@ define([
$.ajax({ $.ajax({
url: Common.getUrl({ url: Common.getUrl({
'name': 'repo_history_limit', 'name': this.url_name,
'repo_id': this.repo_id 'repo_id': this.repo_id
}), }),
type: 'put', type: 'put',

View File

@@ -291,7 +291,8 @@ define([
popupHistorySetting: function() { popupHistorySetting: function() {
var options = { var options = {
'repo_name': this.model.get('name'), 'repo_name': this.model.get('name'),
'repo_id': this.model.get('id') 'repo_id': this.model.get('id'),
'url_name': 'repo_history_limit'
}; };
this.togglePopup(); // close the popup this.togglePopup(); // close the popup
new HistorySettingsDialog(options); new HistorySettingsDialog(options);

View File

@@ -180,6 +180,7 @@ define([
case 'admin-device-errors': return siteRoot + 'api/v2.1/admin/device-errors/'; case 'admin-device-errors': return siteRoot + 'api/v2.1/admin/device-errors/';
case 'admin-libraries': return siteRoot + 'api/v2.1/admin/libraries/'; case 'admin-libraries': return siteRoot + 'api/v2.1/admin/libraries/';
case 'admin-library': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/'; case 'admin-library': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/';
case 'admin-library-history-limit': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/history-limit/';
case 'admin-library-dirents': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/dirents/'; case 'admin-library-dirents': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/dirents/';
case 'admin-groups': return siteRoot + 'api/v2.1/admin/groups/'; case 'admin-groups': return siteRoot + 'api/v2.1/admin/groups/';
case 'admin-group': return siteRoot + 'api/v2.1/admin/groups/' + options.group_id + '/'; case 'admin-group': return siteRoot + 'api/v2.1/admin/groups/' + options.group_id + '/';

View File

@@ -8,9 +8,12 @@ define([
'select2', 'select2',
'jquery.ui.tabs', 'jquery.ui.tabs',
'sysadmin-app/views/share', 'sysadmin-app/views/share',
'app/views/widgets/hl-item-view' 'app/views/dialogs/repo-history-settings',
'app/views/widgets/hl-item-view',
'app/views/widgets/dropdown'
], function($, _, Backbone, Common, Moment, Simplemodal, ], function($, _, Backbone, Common, Moment, Simplemodal,
Select2, Tabs, ShareView, HLItemView) { Select2, Tabs, ShareView, HistorySettingsDialog,
HLItemView, DropdownView) {
'use strict'; 'use strict';
@@ -21,7 +24,8 @@ define([
transferTemplate: _.template($('#library-transfer-form-tmpl').html()), transferTemplate: _.template($('#library-transfer-form-tmpl').html()),
events: { events: {
'click .repo-share-btn': 'share', 'click .js-repo-share': 'share',
'click .js-popup-history-setting': 'popupHistorySetting',
'click .repo-delete-btn': 'deleteLibrary', 'click .repo-delete-btn': 'deleteLibrary',
'click .repo-transfer-btn': 'transferLibrary' 'click .repo-transfer-btn': 'transferLibrary'
}, },
@@ -32,9 +36,25 @@ define([
'repo_name': this.model.get('name') 'repo_name': this.model.get('name')
}; };
new ShareView(options); new ShareView(options);
this.togglePopup(); // close the popup
return false; return false;
}, },
popupHistorySetting: function() {
var options = {
'repo_name': this.model.get('name'),
'repo_id': this.model.get('id'),
'url_name': 'admin-library-history-limit'
};
this.togglePopup(); // close the popup
new HistorySettingsDialog(options);
return false;
},
togglePopup: function() {
this.dropdown.hide();
},
initialize: function() { initialize: function() {
HLItemView.prototype.initialize.call(this); HLItemView.prototype.initialize.call(this);
this.listenTo(this.model, "change", this.render); this.listenTo(this.model, "change", this.render);
@@ -146,6 +166,11 @@ define([
this.$el.html(this.template(data)); this.$el.html(this.template(data));
this.dropdown = new DropdownView({
el: this.$('.sf-dropdown'),
right: 0
});
return this; return this;
} }

View File

@@ -0,0 +1,37 @@
import json
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
class LibraryHistory(BaseTestCase):
def setUp(self):
self.login_as(self.admin)
self.repo_id = self.repo.id
self.url = reverse('api-v2.1-admin-library-history-limit', kwargs=dict(repo_id=self.repo_id))
def test_can_get(self):
resp = self.client.get(self.url)
self.assertEqual(200, resp.status_code)
def test_can_put(self):
data = "keep_days=-1"
resp = self.client.put(self.url, data, 'application/x-www-form-urlencoded')
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
self.assertEqual(-1, json_resp['keep_days'])
data = "keep_days=0"
resp = self.client.put(self.url, data, 'application/x-www-form-urlencoded')
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
self.assertEqual(0, json_resp['keep_days'])
data = "keep_days=8"
resp = self.client.put(self.url, data, 'application/x-www-form-urlencoded')
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
self.assertEqual(8, json_resp['keep_days'])
data = "keep_days=q"
resp = self.client.put(self.url, data, 'application/x-www-form-urlencoded')
self.assertEqual(400, resp.status_code)