Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow saved dashboards to be exported by users with execute_sql permission #144

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{% if result.truncated %}
<p class="results-truncated">
Results were truncated
{% if user_can_export_data and not saved_dashboard %}
{% if user_can_export_data %}
<input
class="btn"
style="font-size: 0.6rem"
Expand Down Expand Up @@ -64,7 +64,7 @@
</table>
<details style="margin-top: 1em;"><summary style="font-size: 0.7em; margin-bottom: 0.5em; cursor: pointer;">Copy and export data</summary>
<textarea id="copyable-{{ result.index }}" style="height: 10em">{{ result|sql_dashboard_tsv }}</textarea>
{% if user_can_export_data and not saved_dashboard %}
{% if user_can_export_data %}
<div class="export-buttons">
<input
class="btn"
Expand All @@ -83,6 +83,44 @@
</details>
<p>Duration: {{ result.duration_ms|floatformat:2 }}ms</p>
<!-- templates considered: {{ result.templates|join:", " }} -->

{% if saved_dashboard and user_can_export_data %}
{% comment %}
This is a bit of a hack to allow users with execute_sql permission to export
saved dashboards. We're essentially simulating a user who typed a SQL
query into the dashboard index page and decided to export it.
{% endcomment %}
<template id="export-template-{{ result.index}}">
<form action="{% url 'django_sql_dashboard-index' %}" method="POST" hidden>
{% csrf_token %}
<input type="hidden" name="sql" value="{{ result.sql }}">
</form>
</template>
<script>
(function() {
var exportButtons = document.querySelectorAll("input[name^=export_]");
var template = document.querySelector("#export-template-{{ result.index }}");
var formTemplate = template.content.querySelector('form');
Array.from(exportButtons).forEach(button => {
var exportType = button.name.match(/^export_(.+)_/)[1];

button.addEventListener("click", e => {
e.preventDefault();
var form = formTemplate.cloneNode(true);
var exportEl = document.createElement('input');
exportEl.type = "hidden";
exportEl.name = 'export_' + exportType + '_0';
exportEl.value = button.value;
form.appendChild(exportEl);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
});
});
})();
</script>
{% endif %}

<script>
(function() {
var ta = document.querySelector("#copyable-{{ result.index }}");
Expand Down
4 changes: 2 additions & 2 deletions test_project/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ def test_export_requires_setting(admin_client, dashboard_db):
assert response.status_code == 403


def test_no_export_on_saved_dashboard(
def test_export_on_saved_dashboard(
admin_client, dashboard_db, settings, saved_dashboard
):
settings.DASHBOARD_ENABLE_FULL_EXPORT = True
response = admin_client.get("/dashboard/test/")
assert response.status_code == 200
assert b'<pre class="sql">select 22 + 55</pre>' in response.content
assert b"Export all as CSV" not in response.content
assert b"Export all as CSV" in response.content


def test_export_csv(admin_client, dashboard_db, settings):
Expand Down