diff --git a/frontend_tests/node_tests/templates.js b/frontend_tests/node_tests/templates.js
index c36a56882c..139da37b2c 100644
--- a/frontend_tests/node_tests/templates.js
+++ b/frontend_tests/node_tests/templates.js
@@ -1410,6 +1410,24 @@ run_test('typing_notifications', () => {
});
+run_test('upload_space_stats', () => {
+ var args = {
+ show_upgrade_message: true,
+ percent_used: 50,
+ upload_quota: "1 GB",
+ };
+ var html = render('upload-space-stats', args);
+ assert.equal($(html).text().trim(), "translated: Organization using 50% of 1 GB.\n translated: Upgrade for more space.");
+
+ args = {
+ show_upgrade_message: false,
+ percent_used: 10,
+ upload_quota: "5 GB",
+ };
+ html = render('upload-space-stats', args);
+ assert.equal($(html).text().trim(), "translated: Organization using 10% of 5 GB.");
+});
+
run_test('user_group_info_popover', () => {
var html = render('user_group_info_popover');
diff --git a/static/js/attachments_ui.js b/static/js/attachments_ui.js
index 2a66b7cb77..bdc60ec16f 100644
--- a/static/js/attachments_ui.js
+++ b/static/js/attachments_ui.js
@@ -3,6 +3,7 @@ var attachments_ui = (function () {
var exports = {};
var attachments;
+var upload_space_used;
exports.bytes_to_size = function (bytes, kb_with_1024_bytes) {
if (kb_with_1024_bytes === undefined) {
@@ -21,6 +22,26 @@ exports.bytes_to_size = function (bytes, kb_with_1024_bytes) {
return size + ' ' + sizes[i];
};
+exports.percentage_used_space = function (uploads_size) {
+ if (page_params.realm_upload_quota === null) {
+ return null;
+ }
+ return (100 * uploads_size / page_params.realm_upload_quota).toFixed(1);
+};
+
+function set_upload_space_stats() {
+ if (page_params.realm_upload_quota === null) {
+ return;
+ }
+ var args = {
+ show_upgrade_message: page_params.realm_plan_type === 2,
+ percent_used: exports.percentage_used_space(upload_space_used),
+ upload_quota: attachments_ui.bytes_to_size(page_params.realm_upload_quota, true),
+ };
+ var rendered_upload_stats_html = templates.render("upload-space-stats", args);
+ $("#attachment-stats-holder").html(rendered_upload_stats_html);
+}
+
function delete_attachments(attachment) {
var status = $('#delete-upload-status');
channel.del({
@@ -36,6 +57,8 @@ function delete_attachments(attachment) {
}
function render_attachments_ui() {
+ set_upload_space_stats();
+
var uploaded_files_table = $("#uploaded_files_table").expectOne();
var $search_input = $("#upload_file_search");
@@ -99,7 +122,7 @@ exports.update_attachments = function (event) {
format_attachment_data([event.attachment]);
attachments.push(event.attachment);
}
-
+ upload_space_used = event.upload_space_used;
// TODO: This is inefficient and we should be able to do some sort
// of incremental list_render update instead.
render_attachments_ui();
@@ -125,6 +148,7 @@ exports.set_up_attachments = function () {
loading.destroy_indicator($('#attachments_loading_indicator'));
format_attachment_data(data.attachments);
attachments = data.attachments;
+ upload_space_used = data.upload_space_used;
render_attachments_ui();
},
error: function (xhr) {
diff --git a/static/styles/settings.scss b/static/styles/settings.scss
index 00d6351aee..fe0cac646a 100644
--- a/static/styles/settings.scss
+++ b/static/styles/settings.scss
@@ -1911,3 +1911,9 @@ thead .actions {
#get_api_key_button {
display: block;
}
+
+#attachment-stats-holder {
+ position: relative;
+ margin-top: 13px;
+ display: inline-block;
+}
diff --git a/static/templates/settings/attachments-settings.handlebars b/static/templates/settings/attachments-settings.handlebars
index bb24bbd82d..878c99899d 100644
--- a/static/templates/settings/attachments-settings.handlebars
+++ b/static/templates/settings/attachments-settings.handlebars
@@ -1,4 +1,5 @@
+
diff --git a/static/templates/settings/upload-space-stats.handlebars b/static/templates/settings/upload-space-stats.handlebars
new file mode 100644
index 0000000000..351b74afe3
--- /dev/null
+++ b/static/templates/settings/upload-space-stats.handlebars
@@ -0,0 +1,4 @@
+
+ {{#tr this}}Organization using __percent_used__% of __upload_quota__.{{/tr}}
+ {{#if show_upgrade_message}}{{#tr this}}Upgrade for more space.{{/tr}}{{/if}}
+
diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py
index 6bb346729b..108c305f90 100644
--- a/zerver/lib/actions.py
+++ b/zerver/lib/actions.py
@@ -5221,6 +5221,7 @@ def notify_attachment_update(user_profile: UserProfile, op: str,
'type': 'attachment',
'op': op,
'attachment': attachment_dict,
+ "upload_space_used": user_profile.realm.currently_used_upload_space_bytes(),
}
send_event(user_profile.realm, event, [user_profile.id])
diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py
index 3f848f6b67..c8a7d0135c 100644
--- a/zerver/tests/test_events.py
+++ b/zerver/tests/test_events.py
@@ -2525,6 +2525,7 @@ class EventsRegisterTest(ZulipTestCase):
('name', check_float),
]))),
])),
+ ('upload_space_used', equals(6)),
])
self.login(self.example_email("hamlet"))
@@ -2567,6 +2568,7 @@ class EventsRegisterTest(ZulipTestCase):
('name', check_float),
]))),
])),
+ ('upload_space_used', equals(6)),
])
self.subscribe(self.example_user("hamlet"), "Denmark")
@@ -2584,6 +2586,7 @@ class EventsRegisterTest(ZulipTestCase):
('attachment', check_dict_only([
('id', check_int),
])),
+ ('upload_space_used', equals(0)),
])
events = self.do_test(
diff --git a/zerver/tests/test_upload.py b/zerver/tests/test_upload.py
index 9bea68526f..1208f018b2 100644
--- a/zerver/tests/test_upload.py
+++ b/zerver/tests/test_upload.py
@@ -1762,15 +1762,15 @@ class UploadSpaceTests(UploadSerializeMixin, ZulipTestCase):
data = b'zulip!'
upload_message_file(u'dummy.txt', len(data), u'text/plain', data, self.user_profile)
- self.assertEqual(None, cache_get(get_realm_used_upload_space_cache_key(self.realm)))
- self.assertEqual(len(data), self.realm.currently_used_upload_space_bytes())
+ # notify_attachment_update function calls currently_used_upload_space_bytes which
+ # updates the cache.
self.assertEqual(len(data), cache_get(get_realm_used_upload_space_cache_key(self.realm))[0])
+ self.assertEqual(len(data), self.realm.currently_used_upload_space_bytes())
data2 = b'more-data!'
upload_message_file(u'dummy2.txt', len(data2), u'text/plain', data2, self.user_profile)
- self.assertEqual(None, cache_get(get_realm_used_upload_space_cache_key(self.realm)))
- self.assertEqual(len(data) + len(data2), self.realm.currently_used_upload_space_bytes())
self.assertEqual(len(data) + len(data2), cache_get(get_realm_used_upload_space_cache_key(self.realm))[0])
+ self.assertEqual(len(data) + len(data2), self.realm.currently_used_upload_space_bytes())
attachment = Attachment.objects.get(file_name="dummy.txt")
attachment.file_name = "dummy1.txt"
@@ -1781,17 +1781,6 @@ class UploadSpaceTests(UploadSerializeMixin, ZulipTestCase):
attachment.delete()
self.assertEqual(None, cache_get(get_realm_used_upload_space_cache_key(self.realm)))
self.assertEqual(len(data2), self.realm.currently_used_upload_space_bytes())
- self.assertEqual(len(data2), cache_get(get_realm_used_upload_space_cache_key(self.realm))[0])
-
- def test_upload_space_used_api(self) -> None:
- self.login(self.user_profile.email)
- response = self.client_get("/json/realm/upload_space_used")
- self.assert_in_success_response(["0"], response)
-
- data = b'zulip!'
- upload_message_file(u'dummy.txt', len(data), u'text/plain', data, self.user_profile)
- response = self.client_get("/json/realm/upload_space_used")
- self.assert_in_success_response([str(len(data))], response)
class ExifRotateTests(TestCase):
def test_image_do_not_rotate(self) -> None:
diff --git a/zerver/views/attachments.py b/zerver/views/attachments.py
index fc6fcdebb5..250c004b87 100644
--- a/zerver/views/attachments.py
+++ b/zerver/views/attachments.py
@@ -8,8 +8,10 @@ from zerver.lib.attachments import user_attachments, remove_attachment, \
def list_by_user(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
- return json_success({"attachments": user_attachments(user_profile)})
-
+ return json_success({
+ "attachments": user_attachments(user_profile),
+ "upload_space_used": user_profile.realm.currently_used_upload_space_bytes(),
+ })
def remove(request: HttpRequest, user_profile: UserProfile, attachment_id: str) -> HttpResponse:
attachment = access_attachment_by_id(user_profile, int(attachment_id),
@@ -17,6 +19,3 @@ def remove(request: HttpRequest, user_profile: UserProfile, attachment_id: str)
remove_attachment(user_profile, attachment)
notify_attachment_update(user_profile, "remove", {"id": int(attachment_id)})
return json_success()
-
-def upload_space_used(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
- return json_success({"upload_space_used": user_profile.realm.currently_used_upload_space_bytes()})
diff --git a/zproject/urls.py b/zproject/urls.py
index c367c27280..377a698485 100644
--- a/zproject/urls.py
+++ b/zproject/urls.py
@@ -127,9 +127,6 @@ v1_api_and_json_patterns = [
url(r'^realm/presence$', rest_dispatch,
{'GET': 'zerver.views.presence.get_statuses_for_realm'}),
- url(r'^realm/upload_space_used$', rest_dispatch,
- {'GET': 'zerver.views.attachments.upload_space_used'}),
-
# users -> zerver.views.users
#
# Since some of these endpoints do something different if used on