Add ICS/webcal HTTP subscription calendars
Users can now subscribe to a remote ICS/webcal feed from the dashboard (name + color), the same way they set up a real calendar or the virtual Birthdays calendar. Subscriptions are read-only, per-user, and share the "/cal/home/<name>/" namespace with real calendars and "birthdays" (name collisions are rejected in both directions). - internal/db: new ics_subscriptions table + CRUD (internal/db/ics.go); CreateCalendarWithColor checks for a colliding subscription name. - internal/icssub: shared HTTP-fetch + TTL cache (15 min) for remote ICS calendars, with webcal:// -> https:// rewriting and stale-on-error fallback, used by both the web UI and the CalDAV backend. - internal/web: dashboard "Subscribe to an ICS/webcal calendar" form, color picker, delete button (internal/web/ics.go, templates/dashboard.templ); month view renders subscription events in their chosen color, read-only (internal/web/calendar.go). - internal/caldav: subscriptions are exposed as read-only calendars (internal/caldav/ics.go) - listed in PROPFIND, events served via GET/REPORT, PUT/DELETE on individual events rejected with 403, but DELETE on the calendar itself unsubscribes; calendar-color is injected the same way as for real calendars and Birthdays. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -11,9 +11,10 @@ type ShareRow struct {
|
||||
// ResourceCard describes one of the user's own calendars/address books
|
||||
// plus who it's currently shared with.
|
||||
type ResourceCard struct {
|
||||
Kind string // "calendar" or "addressbook"
|
||||
Kind string // "calendar", "addressbook", or "ics" (read-only ICS subscription)
|
||||
Name string
|
||||
Color string // hex color like "#3b82f6"; only used for calendars
|
||||
Color string // hex color like "#3b82f6"; only used for calendars/ics
|
||||
URL string // remote ICS/webcal URL; only used for kind "ics"
|
||||
Shares []ShareRow
|
||||
Virtual bool // true for computed calendars (e.g. birthdays): no delete/sharing, just a color picker
|
||||
}
|
||||
@@ -73,6 +74,32 @@ templ Dashboard(username string, resources []ResourceCard, sharedWithMe []Shared
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
<form
|
||||
class="flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4"
|
||||
hx-post="/web/resources/ics"
|
||||
hx-target="#resources"
|
||||
hx-swap="outerHTML"
|
||||
hx-on::after-request="if(event.detail.successful) this.reset()"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<label class="block text-xs text-gray-500 mb-1">Subscribe to an ICS/webcal calendar</label>
|
||||
<input name="name" type="text" required pattern="[a-zA-Z0-9_-]{1,64}"
|
||||
placeholder="e.g. holidays"
|
||||
class="w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm mb-2"/>
|
||||
<input name="url" type="text" required
|
||||
placeholder="https://example.com/calendar.ics or webcal://..."
|
||||
class="w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">Color</label>
|
||||
<input name="color" type="color" value="#10b981"
|
||||
class="w-12 h-9 rounded-md border-gray-300 border p-0.5"/>
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700">
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ResourceList(resources)
|
||||
@@ -114,7 +141,7 @@ templ ResourceCardView(r ResourceCard) {
|
||||
<div id={ "resource-" + r.Kind + "-" + r.Name } class="bg-white rounded-lg border border-gray-200 p-5">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h2 class="font-medium flex items-center gap-2">
|
||||
if r.Kind == "calendar" {
|
||||
if r.Kind == "calendar" || r.Kind == "ics" {
|
||||
<form
|
||||
hx-post={ colorEndpoint(r) }
|
||||
hx-target={ "#resource-" + r.Kind + "-" + r.Name }
|
||||
@@ -136,6 +163,9 @@ templ ResourceCardView(r ResourceCard) {
|
||||
if r.Virtual {
|
||||
<span class="text-xs text-gray-400">(computed from contacts)</span>
|
||||
}
|
||||
if r.Kind == "ics" {
|
||||
<span class="text-xs text-gray-400">(read-only subscription)</span>
|
||||
}
|
||||
</h2>
|
||||
if !r.Virtual {
|
||||
<button
|
||||
@@ -151,7 +181,11 @@ templ ResourceCardView(r ResourceCard) {
|
||||
}
|
||||
</div>
|
||||
|
||||
if !r.Virtual {
|
||||
if r.Kind == "ics" {
|
||||
<p class="text-xs text-gray-500 mb-4 break-all">{ r.URL }</p>
|
||||
}
|
||||
|
||||
if !r.Virtual && r.Kind != "ics" {
|
||||
<ul class="divide-y divide-gray-100 mb-4">
|
||||
for _, sh := range r.Shares {
|
||||
<li class="py-2 flex items-center justify-between text-sm">
|
||||
@@ -208,6 +242,9 @@ func colorEndpoint(r ResourceCard) string {
|
||||
if r.Virtual {
|
||||
return "/web/resources/birthdays/color"
|
||||
}
|
||||
if r.Kind == "ics" {
|
||||
return "/web/resources/ics/color"
|
||||
}
|
||||
return "/web/resources/calendar/color"
|
||||
}
|
||||
|
||||
@@ -223,10 +260,14 @@ func shareVals(resource, sharedWith string) string {
|
||||
}
|
||||
|
||||
func resourceEndpoint(kind string) string {
|
||||
if kind == "calendar" {
|
||||
switch kind {
|
||||
case "calendar":
|
||||
return "/web/resources/calendar"
|
||||
case "ics":
|
||||
return "/web/resources/ics"
|
||||
default:
|
||||
return "/web/resources/addressbook"
|
||||
}
|
||||
return "/web/resources/addressbook"
|
||||
}
|
||||
|
||||
func resourceVals(name string) string {
|
||||
|
||||
@@ -19,9 +19,10 @@ type ShareRow struct {
|
||||
// ResourceCard describes one of the user's own calendars/address books
|
||||
// plus who it's currently shared with.
|
||||
type ResourceCard struct {
|
||||
Kind string // "calendar" or "addressbook"
|
||||
Kind string // "calendar", "addressbook", or "ics" (read-only ICS subscription)
|
||||
Name string
|
||||
Color string // hex color like "#3b82f6"; only used for calendars
|
||||
Color string // hex color like "#3b82f6"; only used for calendars/ics
|
||||
URL string // remote ICS/webcal URL; only used for kind "ics"
|
||||
Shares []ShareRow
|
||||
Virtual bool // true for computed calendars (e.g. birthdays): no delete/sharing, just a color picker
|
||||
}
|
||||
@@ -68,7 +69,7 @@ func Dashboard(username string, resources []ResourceCard, sharedWithMe []SharedW
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1 class=\"text-2xl font-semibold mb-6\">Your calendars & address books</h1><div class=\"flex flex-col sm:flex-row gap-4 mb-6\"><form class=\"flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4\" hx-post=\"/web/resources/calendar\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-on::after-request=\"if(event.detail.successful) this.reset()\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">New calendar</label> <input name=\"name\" type=\"text\" required pattern=\"[a-zA-Z0-9_-]{1,64}\" placeholder=\"e.g. work\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><div><label class=\"block text-xs text-gray-500 mb-1\">Color</label> <input name=\"color\" type=\"color\" value=\"#3b82f6\" class=\"w-12 h-9 rounded-md border-gray-300 border p-0.5\"></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Add</button></form><form class=\"flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4\" hx-post=\"/web/resources/addressbook\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-on::after-request=\"if(event.detail.successful) this.reset()\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">New address book</label> <input name=\"name\" type=\"text\" required pattern=\"[a-zA-Z0-9_-]{1,64}\" placeholder=\"e.g. contacts\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Add</button></form></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1 class=\"text-2xl font-semibold mb-6\">Your calendars & address books</h1><div class=\"flex flex-col sm:flex-row gap-4 mb-6\"><form class=\"flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4\" hx-post=\"/web/resources/calendar\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-on::after-request=\"if(event.detail.successful) this.reset()\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">New calendar</label> <input name=\"name\" type=\"text\" required pattern=\"[a-zA-Z0-9_-]{1,64}\" placeholder=\"e.g. work\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><div><label class=\"block text-xs text-gray-500 mb-1\">Color</label> <input name=\"color\" type=\"color\" value=\"#3b82f6\" class=\"w-12 h-9 rounded-md border-gray-300 border p-0.5\"></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Add</button></form><form class=\"flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4\" hx-post=\"/web/resources/addressbook\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-on::after-request=\"if(event.detail.successful) this.reset()\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">New address book</label> <input name=\"name\" type=\"text\" required pattern=\"[a-zA-Z0-9_-]{1,64}\" placeholder=\"e.g. contacts\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Add</button></form><form class=\"flex flex-col sm:flex-row sm:items-end gap-2 bg-white rounded-lg border border-gray-200 p-4\" hx-post=\"/web/resources/ics\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-on::after-request=\"if(event.detail.successful) this.reset()\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">Subscribe to an ICS/webcal calendar</label> <input name=\"name\" type=\"text\" required pattern=\"[a-zA-Z0-9_-]{1,64}\" placeholder=\"e.g. holidays\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm mb-2\"> <input name=\"url\" type=\"text\" required placeholder=\"https://example.com/calendar.ics or webcal://...\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><div><label class=\"block text-xs text-gray-500 mb-1\">Color</label> <input name=\"color\" type=\"color\" value=\"#10b981\" class=\"w-12 h-9 rounded-md border-gray-300 border p-0.5\"></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Add</button></form></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func Dashboard(username string, resources []ResourceCard, sharedWithMe []SharedW
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.Owner)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 86, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 113, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -106,7 +107,7 @@ func Dashboard(username string, resources []ResourceCard, sharedWithMe []SharedW
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 86, Col: 68}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 113, Col: 68}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -119,7 +120,7 @@ func Dashboard(username string, resources []ResourceCard, sharedWithMe []SharedW
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.Kind)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 87, Col: 47}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 114, Col: 47}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -132,7 +133,7 @@ func Dashboard(username string, resources []ResourceCard, sharedWithMe []SharedW
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(item.Permission)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 89, Col: 83}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 116, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -234,7 +235,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue("resource-" + r.Kind + "-" + r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 114, Col: 46}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 141, Col: 46}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -244,7 +245,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if r.Kind == "calendar" {
|
||||
if r.Kind == "calendar" || r.Kind == "ics" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<form hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
@@ -252,7 +253,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(colorEndpoint(r))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 119, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 146, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -265,7 +266,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue("#resource-" + r.Kind + "-" + r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 120, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 147, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -278,7 +279,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 124, Col: 53}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 151, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -291,7 +292,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(colorOrDefault(r.Color))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 128, Col: 38}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 155, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -305,7 +306,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 134, Col: 12}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 161, Col: 12}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -318,7 +319,7 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(r.Kind)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 135, Col: 77}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 162, Col: 77}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -329,204 +330,229 @@ func ResourceCardView(r ResourceCard) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if r.Virtual {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<span class=\"text-xs text-gray-400\">(computed from contacts)</span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<span class=\"text-xs text-gray-400\">(computed from contacts)</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</h2>")
|
||||
if r.Kind == "ics" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<span class=\"text-xs text-gray-400\">(read-only subscription)</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !r.Virtual {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<button class=\"text-red-600 hover:underline text-xs\" hx-delete=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<button class=\"text-red-600 hover:underline text-xs\" hx-delete=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(resourceEndpoint(r.Kind))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 143, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 173, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" hx-vals=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" hx-vals=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(resourceVals(r.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 144, Col: 35}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 174, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-confirm=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" hx-target=\"#resources\" hx-swap=\"outerHTML\" hx-confirm=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue("Delete " + r.Kind + " " + r.Name + "? This removes all its data and cannot be undone.")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 147, Col: 105}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 177, Col: 105}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\">Delete</button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">Delete</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !r.Virtual {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<ul class=\"divide-y divide-gray-100 mb-4\">")
|
||||
if r.Kind == "ics" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p class=\"text-xs text-gray-500 mb-4 break-all\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(r.URL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 185, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if !r.Virtual && r.Kind != "ics" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<ul class=\"divide-y divide-gray-100 mb-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, sh := range r.Shares {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<li class=\"py-2 flex items-center justify-between text-sm\"><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(sh.SharedWith)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 158, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</span> <span class=\"flex items-center gap-3\"><span class=\"text-xs uppercase tracking-wide text-gray-500\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<li class=\"py-2 flex items-center justify-between text-sm\"><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(sh.Permission)
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(sh.SharedWith)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 160, Col: 82}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 192, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</span> <button class=\"text-red-600 hover:underline text-xs\" hx-delete=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</span> <span class=\"flex items-center gap-3\"><span class=\"text-xs uppercase tracking-wide text-gray-500\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareEndpoint(r.Kind))
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(sh.Permission)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 163, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 194, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" hx-vals=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</span> <button class=\"text-red-600 hover:underline text-xs\" hx-delete=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareVals(r.Name, sh.SharedWith))
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareEndpoint(r.Kind))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 164, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 197, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" hx-vals=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue("#resource-" + r.Kind + "-" + r.Name)
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareVals(r.Name, sh.SharedWith))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 165, Col: 56}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 198, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" hx-swap=\"outerHTML\" hx-confirm=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove access for " + sh.SharedWith + "?")
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue("#resource-" + r.Kind + "-" + r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 167, Col: 63}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 199, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\">Remove</button></span></li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\" hx-swap=\"outerHTML\" hx-confirm=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove access for " + sh.SharedWith + "?")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 201, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\">Remove</button></span></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if len(r.Shares) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<li class=\"py-2 text-sm text-gray-400\">Not shared with anyone yet.</li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li class=\"py-2 text-sm text-gray-400\">Not shared with anyone yet.</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</ul><form class=\"flex flex-col sm:flex-row sm:items-end gap-2\" hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareEndpoint(r.Kind))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 181, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" hx-target=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</ul><form class=\"flex flex-col sm:flex-row sm:items-end gap-2\" hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue("#resource-" + r.Kind + "-" + r.Name)
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(shareEndpoint(r.Kind))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 182, Col: 52}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 215, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" hx-swap=\"outerHTML\"><input type=\"hidden\" name=\"resource\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name)
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue("#resource-" + r.Kind + "-" + r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 185, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 216, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">Username</label> <input name=\"shared_with\" type=\"text\" required class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><div><label class=\"block text-xs text-gray-500 mb-1\">Permission</label> <select name=\"permission\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"><option value=\"read\">read</option> <option value=\"write\">write</option></select></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Share</button></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\" hx-swap=\"outerHTML\"><input type=\"hidden\" name=\"resource\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/dashboard.templ`, Line: 219, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\"><div class=\"flex-1\"><label class=\"block text-xs text-gray-500 mb-1\">Username</label> <input name=\"shared_with\" type=\"text\" required class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"></div><div><label class=\"block text-xs text-gray-500 mb-1\">Permission</label> <select name=\"permission\" class=\"w-full rounded-md border-gray-300 border px-2 py-1.5 text-sm\"><option value=\"read\">read</option> <option value=\"write\">write</option></select></div><button type=\"submit\" class=\"w-full sm:w-auto bg-indigo-600 text-white rounded-md px-3 py-1.5 text-sm font-medium hover:bg-indigo-700\">Share</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -538,6 +564,9 @@ func colorEndpoint(r ResourceCard) string {
|
||||
if r.Virtual {
|
||||
return "/web/resources/birthdays/color"
|
||||
}
|
||||
if r.Kind == "ics" {
|
||||
return "/web/resources/ics/color"
|
||||
}
|
||||
return "/web/resources/calendar/color"
|
||||
}
|
||||
|
||||
@@ -553,10 +582,14 @@ func shareVals(resource, sharedWith string) string {
|
||||
}
|
||||
|
||||
func resourceEndpoint(kind string) string {
|
||||
if kind == "calendar" {
|
||||
switch kind {
|
||||
case "calendar":
|
||||
return "/web/resources/calendar"
|
||||
case "ics":
|
||||
return "/web/resources/ics"
|
||||
default:
|
||||
return "/web/resources/addressbook"
|
||||
}
|
||||
return "/web/resources/addressbook"
|
||||
}
|
||||
|
||||
func resourceVals(name string) string {
|
||||
|
||||
Reference in New Issue
Block a user