Skip to content
Closed
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
6 changes: 6 additions & 0 deletions examples/todo application (PostgreSQL)/csv_attachment.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
select
'csv_attachment' as component,
';' as separator,
'todo.csv' as filename;

select * from todos;
6 changes: 5 additions & 1 deletion examples/todo application (PostgreSQL)/index.sql
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ select
'todo_form.sql' as link,
'green' as color,
'Add new todo' as title,
'circle-plus' as icon;
'circle-plus' as icon;
select
'/csv_attachment.sql' as link,
'Download' as title,
'download' as icon;
6 changes: 6 additions & 0 deletions sqlpage/templates/csv_attachment.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{#each_row}}
{{#if (eq @row_index 0)}}
{{#each this}}{{@key}}{{#unless @last}}{{default ../../separator ","}}{{/unless}}{{/each}}{{../linebreak}}
{{/if}}
{{#each this}}{{this}}{{#unless @last}}{{default ../../separator ","}}{{/unless}}{{/each}}{{../linebreak}}
{{/each_row}}
20 changes: 20 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ impl<'a, W: std::io::Write> HeaderContext<W> {
Some("http_header") => self.add_http_header(&data).map(PageContext::Header),
Some("redirect") => self.redirect(&data).map(PageContext::Close),
Some("json") => self.json(&data).map(PageContext::Close),
Some("csv_attachment") => self.csv_attachment(data).await,
Some("cookie") => self.add_cookie(&data).map(PageContext::Header),
Some("authentication") => self.authentication(data).await,
_ => self.start_body(data).await,
Expand Down Expand Up @@ -201,6 +202,25 @@ impl<'a, W: std::io::Write> HeaderContext<W> {
Ok(self.response.body(json_response))
}

async fn csv_attachment(mut self, data: JsonValue) -> anyhow::Result<PageContext<W>> {
let filename = data
.get("filename")
.and_then(JsonValue::as_str)
.unwrap_or("output.csv");

self.response
.insert_header((header::CONTENT_TYPE, "text/csv"))
.insert_header((
header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{}\"", filename),
));

self.request_context.is_embedded = true;
let page_content = self.start_body(data).await?;

Ok(page_content)
}

async fn authentication(mut self, mut data: JsonValue) -> anyhow::Result<PageContext<W>> {
let password_hash = take_object_str(&mut data, "password_hash");
let password = take_object_str(&mut data, "password");
Expand Down
Loading