Implement download feature for calendars and addressbooks

Fixes #70
This commit is contained in:
Lennart
2025-06-10 17:23:11 +02:00
parent 300a0024ee
commit 103ac0b1f9
16 changed files with 223 additions and 15 deletions

View File

@@ -29,6 +29,7 @@ uuid.workspace = true
url.workspace = true
tracing.workspace = true
rustical_oidc.workspace = true
axum-extra.workspace= true
axum-extra.workspace = true
headers.workspace = true
tower-sessions = "0.14"
tower-sessions.workspace = true
percent-encoding.workspace = true

View File

@@ -62,7 +62,8 @@ html {
background-color: var(--background-color);
}
button {
button,
.button {
border: none;
background: var(--primary-color);
padding: 8px 12px;
@@ -163,9 +164,9 @@ table {
"title comps color-chip"
"description . color-chip"
"subscription-url . color-chip"
"restore . color-chip"
"actions . color-chip"
". . color-chip";
grid-template-rows: 12px auto auto auto 12px;
grid-template-rows: 12px auto auto auto auto 12px;
grid-template-columns: min-content auto 80px;
color: inherit;
text-decoration: none;
@@ -184,6 +185,10 @@ table {
white-space: nowrap;
}
span {
margin: 8px initial;
}
.comps {
grid-area: comps;
@@ -212,8 +217,9 @@ table {
grid-area: color-chip;
}
.restore-form {
grid-area: restore;
.actions {
grid-area: actions;
width: fit-content;
}
&:hover {

View File

@@ -78,6 +78,11 @@
{% if let Some(subscription_url) = calendar.subscription_url %}
<span class="subscription-url">{{ subscription_url }}</span>
{% endif %}
<div class="actions">
<form action="/caldav/principal/{{ calendar.principal }}/calendar/{{ calendar.id }}" target="_blank" method="GET">
<button type="submit">Download</button>
</form>
</div>
<div class="color-chip"></div>
</a>
</li>
@@ -101,9 +106,11 @@
<span class="description">
{% if let Some(description) = calendar.description %}{{ description }}{% endif %}
</span>
<form action="/frontend/user/{{ calendar.principal }}/calendar/{{ calendar.id}}/restore" method="POST" class="restore-form">
<button type="submit">Restore</button>
</form>
<div class="actions">
<form action="/frontend/user/{{ calendar.principal }}/calendar/{{ calendar.id}}/restore" method="POST" class="restore-form">
<button type="submit">Restore</button>
</form>
</div>
<div class="color-chip"></div>
</a>
</li>
@@ -168,6 +175,11 @@
<span class="description">
{% if let Some(description) = addressbook.description %}{{ description }}{% endif %}
</span>
<div class="actions">
<form action="/carddav/principal/{{ addressbook.principal }}/{{ addressbook.id }}" target="_blank" method="GET">
<button type="submit">Download</button>
</form>
</div>
</a>
</li>
{% else %}
@@ -184,9 +196,11 @@
<span class="description">
{% if let Some(description) = addressbook.description %}{{ description }}{% endif %}
</span>
<form action="/frontend/user/{{ addressbook.principal }}/addressbook/{{ addressbook.id}}/restore" method="POST" class="restore-form">
<button type="submit">Restore</button>
</form>
<div class="actions">
<form action="/frontend/user/{{ addressbook.principal }}/addressbook/{{ addressbook.id}}/restore" method="POST" class="restore-form">
<button type="submit">Restore</button>
</form>
</div>
</a>
</li>
{% endfor %}

View File

@@ -10,6 +10,7 @@ use axum::{
use axum_extra::extract::Host;
use headers::{ContentType, HeaderMapExt};
use http::{HeaderValue, StatusCode, header};
use percent_encoding::{CONTROLS, utf8_percent_encode};
use rand::{Rng, distr::Alphanumeric};
use rustical_store::auth::{AuthenticationProvider, User};
use serde::Deserialize;
@@ -79,6 +80,7 @@ pub async fn route_post_app_token<AP: AuthenticationProvider>(
ContentType::from_str("application/x-apple-aspen-config; charset=utf-8").unwrap(),
);
let filename = format!("rustical-{}.mobileconfig", user_id);
let filename = utf8_percent_encode(&filename, CONTROLS);
hdrs.insert(
header::CONTENT_DISPOSITION,
HeaderValue::from_str(&format!(