mirror of https://github.com/zulip/zulip.git
data export: Add UI to trigger data export.
This commit serves as the frontend piece for the "public export" webapp feature. Fixes: #11930
This commit is contained in:
parent
8ac8247dbe
commit
f623540409
|
@ -148,6 +148,7 @@
|
|||
"settings_bots": false,
|
||||
"settings_display": false,
|
||||
"settings_emoji": false,
|
||||
"settings_exports": false,
|
||||
"settings_linkifiers": false,
|
||||
"settings_invites": false,
|
||||
"settings_muting": false,
|
||||
|
|
|
@ -56,6 +56,12 @@ set_global('settings_bots', {
|
|||
update_bot_permissions_ui: noop,
|
||||
});
|
||||
|
||||
set_global('settings_exports', {
|
||||
populate_exports_table: function (exports) {
|
||||
return exports;
|
||||
},
|
||||
});
|
||||
|
||||
// page_params is highly coupled to dispatching now
|
||||
set_global('page_params', {test_suite: false});
|
||||
var page_params = global.page_params;
|
||||
|
@ -712,6 +718,14 @@ var event_fixtures = {
|
|||
user_id: test_user.user_id,
|
||||
status_text: 'out to lunch',
|
||||
},
|
||||
realm_export: {
|
||||
type: 'realm_export',
|
||||
exports: {
|
||||
acting_user_id: 55,
|
||||
event_time: 'noon',
|
||||
path: 'some_path',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function assert_same(actual, expected) {
|
||||
|
@ -1512,3 +1526,18 @@ with_overrides(function (override) {
|
|||
assert.equal(status_text, 'out to lunch');
|
||||
});
|
||||
});
|
||||
|
||||
with_overrides(function (override) {
|
||||
var event = event_fixtures.realm_export;
|
||||
override('settings_exports.populate_exports_table', noop);
|
||||
dispatch(event);
|
||||
global.with_stub(function (stub) {
|
||||
override('settings_exports.populate_exports_table', stub.f);
|
||||
dispatch(event);
|
||||
|
||||
var args = stub.get_args('exports');
|
||||
assert.equal(args.exports.acting_user_id, 55);
|
||||
assert.equal(args.exports.event_time, 'noon');
|
||||
assert.equal(args.exports.path, 'some_path');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -838,6 +838,10 @@ run_test('set_up', () => {
|
|||
const allow_topic_edit_label_parent = $.create('allow-topic-edit-label-parent');
|
||||
$('#id_realm_allow_community_topic_editing_label').set_parent(allow_topic_edit_label_parent);
|
||||
|
||||
channel.get = function (opts) {
|
||||
assert.equal(opts.url, '/json/export/realm');
|
||||
};
|
||||
|
||||
// TEST set_up() here, but this mostly just allows us to
|
||||
// get access to the click handlers.
|
||||
settings_org.maybe_disable_widgets = noop;
|
||||
|
|
|
@ -174,6 +174,7 @@ import "../settings_bots.js";
|
|||
import "../settings_muting.js";
|
||||
import "../settings_sections.js";
|
||||
import "../settings_emoji.js";
|
||||
import "../settings_exports.js";
|
||||
import "../settings_org.js";
|
||||
import "../settings_users.js";
|
||||
import "../settings_streams.js";
|
||||
|
|
|
@ -111,6 +111,7 @@ declare var settings_account: any;
|
|||
declare var settings_bots: any;
|
||||
declare var settings_display: any;
|
||||
declare var settings_emoji: any;
|
||||
declare var settings_exports: any;
|
||||
declare var settings_invites: any;
|
||||
declare var settings: any;
|
||||
declare var settings_linkifiers: any;
|
||||
|
|
|
@ -510,6 +510,9 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
|
|||
activity.redraw_user(event.user_id);
|
||||
}
|
||||
break;
|
||||
case 'realm_export':
|
||||
settings_exports.populate_exports_table(event.exports);
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ var header_map = {
|
|||
"invites-list-admin": i18n.t("Invitations"),
|
||||
"user-groups-admin": i18n.t("User groups"),
|
||||
"profile-field-settings": i18n.t("Profile field settings"),
|
||||
"data-exports-admin": i18n.t("Data exports"),
|
||||
};
|
||||
|
||||
$("body").ready(function () {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
var render_admin_export_list = require('../templates/admin_export_list.hbs');
|
||||
|
||||
var settings_exports = (function () {
|
||||
|
||||
var exports = {};
|
||||
|
||||
var meta = {
|
||||
loaded: false,
|
||||
};
|
||||
|
||||
exports.reset = function () {
|
||||
meta.loaded = false;
|
||||
};
|
||||
|
||||
exports.populate_exports_table = function (exports) {
|
||||
if (!meta.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
var exports_table = $('#admin_exports_table').expectOne();
|
||||
exports_table.find('tr.export_row').remove();
|
||||
_.each(exports, function (data) {
|
||||
if (data.export_data.deleted_timestamp === undefined) {
|
||||
exports_table.append(render_admin_export_list({
|
||||
realm_export: {
|
||||
id: data.id,
|
||||
acting_user: people.my_full_name(data.acting_user_id),
|
||||
// Convert seconds -> milliseconds
|
||||
event_time: timerender.last_seen_status_from_date(
|
||||
new XDate(data.export_time * 1000)
|
||||
),
|
||||
path: data.export_data.export_path,
|
||||
},
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.set_up = function () {
|
||||
meta.loaded = true;
|
||||
|
||||
$("#export-data").on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var export_status = $('#export_status');
|
||||
|
||||
channel.post({
|
||||
url: '/json/export/realm',
|
||||
success: function () {
|
||||
ui_report.success(i18n.t("Export started. Check back in a few minutes."), export_status);
|
||||
},
|
||||
error: function (xhr) {
|
||||
ui_report.error(i18n.t("Export failed"), xhr, export_status);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Do an initial population of the table
|
||||
channel.get({
|
||||
url: '/json/export/realm',
|
||||
success: function (data) {
|
||||
exports.populate_exports_table(data.exports);
|
||||
},
|
||||
});
|
||||
|
||||
$('.admin_exports_table').on('click', '.delete', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var btn = $(this);
|
||||
|
||||
channel.del({
|
||||
url: '/json/export/realm/' + encodeURIComponent(btn.attr('data-export-id')),
|
||||
error: function (xhr) {
|
||||
ui_report.generic_row_button_error(xhr, btn);
|
||||
},
|
||||
// No success function, since UI updates are done via server_events
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return exports;
|
||||
}());
|
||||
if (typeof modules !== 'undefined') {
|
||||
module.exports = settings_exports;
|
||||
}
|
||||
window.settings_exports = settings_exports;
|
|
@ -46,6 +46,7 @@ exports.initialize = function () {
|
|||
load_func_dict.set('invites-list-admin', settings_invites.set_up);
|
||||
load_func_dict.set('user-groups-admin', settings_user_groups.set_up);
|
||||
load_func_dict.set('profile-field-settings', settings_profile_fields.set_up);
|
||||
load_func_dict.set('data-exports-admin', settings_exports.set_up);
|
||||
};
|
||||
|
||||
exports.load_settings_section = function (section) {
|
||||
|
@ -72,6 +73,7 @@ exports.load_settings_section = function (section) {
|
|||
exports.reset_sections = function () {
|
||||
is_loaded.clear();
|
||||
settings_emoji.reset();
|
||||
settings_exports.reset();
|
||||
settings_linkifiers.reset();
|
||||
settings_invites.reset();
|
||||
settings_org.reset();
|
||||
|
|
|
@ -723,13 +723,15 @@ input[type=checkbox].inline-block {
|
|||
|
||||
.add-new-emoji-box,
|
||||
.add-new-user-group-box,
|
||||
.add-new-alert-word-box {
|
||||
.add-new-alert-word-box,
|
||||
.add-new-export-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.add-new-emoji-box .new-emoji-form,
|
||||
.add-new-user-group-box .new-user-group-form,
|
||||
.add-new-alert-word-box .new-alert-word-form {
|
||||
.add-new-alert-word-box .new-alert-word-form,
|
||||
.add-new-export-box {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
|
@ -1949,3 +1951,7 @@ thead .actions {
|
|||
margin-left: 2px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.admin_exports_table {
|
||||
margin: 20px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{{#with realm_export}}
|
||||
<tr class="export_row" id="export_{{id}}">
|
||||
<td>
|
||||
<span class="acting_user">{{acting_user}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="export_time">{{event_time}}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{#if path}}
|
||||
<span class="export_url"><a href="{{path}}">{{t 'Download' }}</a></span>
|
||||
{{else}}
|
||||
<span class="export_url">{{t 'The export URL is not yet available... Check back soon.' }}</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
<button class="button rounded small delete btn-danger" data-export-id="{{id}}">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{/with}}
|
|
@ -28,3 +28,5 @@
|
|||
{{> user_groups_admin }}
|
||||
|
||||
{{> settings/profile_field_settings_admin }}
|
||||
|
||||
{{> settings/data_exports_admin }}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<div id="data-exports" class="settings-section" data-name="data-exports-admin">
|
||||
<p class="alert-word-settings-note">{{#tr this}}Depending on the size of your organization, the time to complete an export can range from several minutes to an hour.{{/tr}}</p>
|
||||
<h3>{{t "Data exports" }}
|
||||
<a href="/help/export-your-organization" target="_blank">
|
||||
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
{{#if is_admin}}
|
||||
<div class="alert" id="export_status" role="alert">
|
||||
<span class="export_status_text"></span>
|
||||
</div>
|
||||
<form class="form-horizontal">
|
||||
<div class="add-new-export-box grey-box">
|
||||
<div class="wrapper">
|
||||
<button type="submit" class="button rounded sea-green" id="export-data">
|
||||
{{t 'Start public export' }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="alert-word-settings-note">{{#tr this}}Zulip also supports exporting private streams and messages under some circumstances. <a href="/help/export-your-organization" target="_blank">Read more</a>{{/tr}}</p>
|
||||
</div>
|
||||
</form>
|
||||
{{/if}}
|
||||
<p class="alert-word-settings-note">{{#tr this}}Any member with administrative access can conduct an export. Please note that individual organizations are limited to five exports per week.{{/tr}}</p>
|
||||
<div class="admin-table-wrapper">
|
||||
<table class="table table-condensed table-striped wrapped-table admin_exports_table">
|
||||
<thead>
|
||||
<th>{{t "Requesting user" }}</th>
|
||||
<th>{{t "Time" }}</th>
|
||||
<th>{{t "File" }}</th>
|
||||
<th>{{t "Actions" }}</th>
|
||||
</thead>
|
||||
<tbody id="admin_exports_table" class="required-text" data-empty="{{t 'No exports.' }}"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
|
@ -145,6 +145,10 @@
|
|||
<i class="icon fa fa-user" aria-hidden="true"></i>
|
||||
<div class="text">{{ _('Invitations') }}</div>
|
||||
</li>
|
||||
<li tabindex="0" data-section="data-exports-admin">
|
||||
<i class="icon fa fa-database" aria-hidden="true"></i>
|
||||
<div class="text">{{ _('Data exports') }}</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if not is_admin %}
|
||||
<div class="collapse-settings-btn">
|
||||
|
|
Loading…
Reference in New Issue