Implemented groups
This commit is contained in:
parent
e9b6cb3ac8
commit
63a421e6a2
9
main.go
9
main.go
@ -25,6 +25,8 @@ import (
|
|||||||
"github.com/animenotifier/notify.moe/pages/explore"
|
"github.com/animenotifier/notify.moe/pages/explore"
|
||||||
"github.com/animenotifier/notify.moe/pages/forum"
|
"github.com/animenotifier/notify.moe/pages/forum"
|
||||||
"github.com/animenotifier/notify.moe/pages/forums"
|
"github.com/animenotifier/notify.moe/pages/forums"
|
||||||
|
"github.com/animenotifier/notify.moe/pages/group"
|
||||||
|
"github.com/animenotifier/notify.moe/pages/groups"
|
||||||
"github.com/animenotifier/notify.moe/pages/home"
|
"github.com/animenotifier/notify.moe/pages/home"
|
||||||
"github.com/animenotifier/notify.moe/pages/inventory"
|
"github.com/animenotifier/notify.moe/pages/inventory"
|
||||||
"github.com/animenotifier/notify.moe/pages/listimport"
|
"github.com/animenotifier/notify.moe/pages/listimport"
|
||||||
@ -33,7 +35,6 @@ import (
|
|||||||
"github.com/animenotifier/notify.moe/pages/listimport/listimportmyanimelist"
|
"github.com/animenotifier/notify.moe/pages/listimport/listimportmyanimelist"
|
||||||
"github.com/animenotifier/notify.moe/pages/login"
|
"github.com/animenotifier/notify.moe/pages/login"
|
||||||
"github.com/animenotifier/notify.moe/pages/me"
|
"github.com/animenotifier/notify.moe/pages/me"
|
||||||
"github.com/animenotifier/notify.moe/pages/newsoundtrack"
|
|
||||||
"github.com/animenotifier/notify.moe/pages/newthread"
|
"github.com/animenotifier/notify.moe/pages/newthread"
|
||||||
"github.com/animenotifier/notify.moe/pages/notifications"
|
"github.com/animenotifier/notify.moe/pages/notifications"
|
||||||
"github.com/animenotifier/notify.moe/pages/paypal"
|
"github.com/animenotifier/notify.moe/pages/paypal"
|
||||||
@ -99,10 +100,14 @@ func configure(app *aero.Application) *aero.Application {
|
|||||||
// Soundtracks
|
// Soundtracks
|
||||||
app.Ajax("/soundtracks", soundtracks.Get)
|
app.Ajax("/soundtracks", soundtracks.Get)
|
||||||
app.Ajax("/soundtracks/from/:index", soundtracks.From)
|
app.Ajax("/soundtracks/from/:index", soundtracks.From)
|
||||||
app.Ajax("/new/soundtrack", newsoundtrack.Get)
|
|
||||||
app.Ajax("/soundtrack/:id", soundtrack.Get)
|
app.Ajax("/soundtrack/:id", soundtrack.Get)
|
||||||
app.Ajax("/soundtrack/:id/edit", soundtrack.Edit)
|
app.Ajax("/soundtrack/:id/edit", soundtrack.Edit)
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
app.Ajax("/groups", groups.Get)
|
||||||
|
app.Ajax("/group/:id", group.Get)
|
||||||
|
app.Ajax("/group/:id/edit", group.Edit)
|
||||||
|
|
||||||
// User profiles
|
// User profiles
|
||||||
app.Ajax("/user", user.Get)
|
app.Ajax("/user", user.Get)
|
||||||
app.Ajax("/user/:nick", profile.Get)
|
app.Ajax("/user/:nick", profile.Get)
|
||||||
|
@ -21,6 +21,9 @@ component Sidebar(user *arn.User)
|
|||||||
//- SidebarButton("Search", "/search", "search")
|
//- SidebarButton("Search", "/search", "search")
|
||||||
|
|
||||||
if user != nil
|
if user != nil
|
||||||
|
if user.Role == "admin"
|
||||||
|
SidebarButton("Groups", "/groups", "users")
|
||||||
|
|
||||||
SidebarButton("Shop", "/shop", "shopping-cart")
|
SidebarButton("Shop", "/shop", "shopping-cart")
|
||||||
SidebarButton("Statistics", "/statistics", "pie-chart")
|
SidebarButton("Statistics", "/statistics", "pie-chart")
|
||||||
SidebarButton("Settings", "/settings", "cog")
|
SidebarButton("Settings", "/settings", "cog")
|
||||||
|
32
pages/group/edit.go
Normal file
32
pages/group/edit.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
|
"github.com/animenotifier/notify.moe/utils"
|
||||||
|
"github.com/animenotifier/notify.moe/utils/editform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Edit ...
|
||||||
|
func Edit(ctx *aero.Context) string {
|
||||||
|
id := ctx.Get("id")
|
||||||
|
group, err := arn.GetGroup(id)
|
||||||
|
user := utils.GetUser(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx.Error(http.StatusNotFound, "Track not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data = &arn.OpenGraph{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"og:title": group.Name,
|
||||||
|
"og:url": "https://" + ctx.App.Config.Domain + group.Link(),
|
||||||
|
"og:site_name": "notify.moe",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.HTML(components.GroupTabs(group) + editform.Render(group, "Edit group", user))
|
||||||
|
}
|
29
pages/group/group.go
Normal file
29
pages/group/group.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get ...
|
||||||
|
func Get(ctx *aero.Context) string {
|
||||||
|
id := ctx.Get("id")
|
||||||
|
group, err := arn.GetGroup(id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx.Error(http.StatusNotFound, "Group not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data = &arn.OpenGraph{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"og:title": group.Name,
|
||||||
|
"og:url": "https://" + ctx.App.Config.Domain + group.Link(),
|
||||||
|
"og:site_name": "notify.moe",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.HTML(components.Group(group))
|
||||||
|
}
|
15
pages/group/group.pixy
Normal file
15
pages/group/group.pixy
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
component Group(group *arn.Group)
|
||||||
|
GroupTabs(group)
|
||||||
|
|
||||||
|
if group.Name != ""
|
||||||
|
h1= group.Name
|
||||||
|
else
|
||||||
|
h1 untitled
|
||||||
|
|
||||||
|
p= len(group.Members)
|
||||||
|
p= group.CreatedBy
|
||||||
|
|
||||||
|
component GroupTabs(group *arn.Group)
|
||||||
|
.tabs
|
||||||
|
Tab("Group", "users", group.Link())
|
||||||
|
Tab("Edit", "pencil", group.Link() + "/edit")
|
25
pages/groups/groups.go
Normal file
25
pages/groups/groups.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package groups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/aerogo/aero"
|
||||||
|
"github.com/animenotifier/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
|
"github.com/animenotifier/notify.moe/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get ...
|
||||||
|
func Get(ctx *aero.Context) string {
|
||||||
|
user := utils.GetUser(ctx)
|
||||||
|
|
||||||
|
groups, err := arn.FilterGroups(func(group *arn.Group) bool {
|
||||||
|
return !group.IsDraft
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctx.Error(http.StatusInternalServerError, "Error fetching groups", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.HTML(components.Groups(groups, user))
|
||||||
|
}
|
21
pages/groups/groups.pixy
Normal file
21
pages/groups/groups.pixy
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
component Groups(groups []*arn.Group, user *arn.User)
|
||||||
|
.tabs
|
||||||
|
Tab("Groups", "users", "/groups")
|
||||||
|
|
||||||
|
h1.page-title Groups
|
||||||
|
|
||||||
|
.buttons
|
||||||
|
if user != nil
|
||||||
|
if user.DraftIndex().GroupID == ""
|
||||||
|
button.action(data-action="newObject", data-trigger="click", data-type="group")
|
||||||
|
Icon("plus")
|
||||||
|
span New group
|
||||||
|
else
|
||||||
|
a.button.ajax(href="/group/" + user.DraftIndex().GroupID + "/edit")
|
||||||
|
Icon("pencil")
|
||||||
|
span Edit draft
|
||||||
|
|
||||||
|
.groups
|
||||||
|
each group in groups
|
||||||
|
.group
|
||||||
|
h3= group.Name
|
@ -1,20 +0,0 @@
|
|||||||
package newsoundtrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
|
||||||
"github.com/animenotifier/notify.moe/components"
|
|
||||||
"github.com/animenotifier/notify.moe/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get forums page.
|
|
||||||
func Get(ctx *aero.Context) string {
|
|
||||||
user := utils.GetUser(ctx)
|
|
||||||
|
|
||||||
if user == nil {
|
|
||||||
return ctx.Error(http.StatusBadRequest, "Not logged in", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.HTML(components.NewSoundTrack(user))
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
component NewSoundTrack(user *arn.User)
|
|
||||||
.widget-form
|
|
||||||
.widget
|
|
||||||
h1 New soundtrack
|
|
||||||
|
|
||||||
.widget-section
|
|
||||||
label(for="soundcloud-link") Soundcloud link:
|
|
||||||
input#soundcloud-link.widget-ui-element(type="text", placeholder="https://soundcloud.com/abc/123")
|
|
||||||
|
|
||||||
.widget-section
|
|
||||||
label(for="youtube-link") Youtube link:
|
|
||||||
input#youtube-link.widget-ui-element(type="text", placeholder="https://www.youtube.com/watch?v=123")
|
|
||||||
|
|
||||||
.widget-section
|
|
||||||
label(for="anime-link") Anime link:
|
|
||||||
input#anime-link.widget-ui-element(type="text", placeholder="https://notify.moe/anime/123")
|
|
||||||
|
|
||||||
.widget-section
|
|
||||||
label(for="osu-link") Osu beatmap (optional):
|
|
||||||
input#osu-link.widget-ui-element(type="text", placeholder="https://osu.ppy.sh/s/123")
|
|
||||||
|
|
||||||
.buttons
|
|
||||||
button.action(data-action="createSoundTrack", data-trigger="click")
|
|
||||||
Icon("check")
|
|
||||||
span Add soundtrack
|
|
@ -1,15 +1,11 @@
|
|||||||
package soundtrack
|
package soundtrack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/animenotifier/notify.moe/components"
|
"github.com/animenotifier/notify.moe/components"
|
||||||
"github.com/animenotifier/notify.moe/utils"
|
"github.com/animenotifier/notify.moe/utils"
|
||||||
|
"github.com/animenotifier/notify.moe/utils/editform"
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
"github.com/aerogo/aero"
|
||||||
"github.com/animenotifier/arn"
|
"github.com/animenotifier/arn"
|
||||||
@ -38,98 +34,5 @@ func Edit(ctx *aero.Context) string {
|
|||||||
ctx.Data.(*arn.OpenGraph).Tags["og:image"] = track.MainAnime().Image.Large
|
ctx.Data.(*arn.OpenGraph).Tags["og:image"] = track.MainAnime().Image.Large
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.HTML(components.SoundTrackTabs(track) + EditForm(track, "Edit soundtrack", user))
|
return ctx.HTML(components.SoundTrackTabs(track) + editform.Render(track, "Edit soundtrack", user))
|
||||||
}
|
|
||||||
|
|
||||||
// EditForm ...
|
|
||||||
func EditForm(obj interface{}, title string, user *arn.User) string {
|
|
||||||
t := reflect.TypeOf(obj).Elem()
|
|
||||||
v := reflect.ValueOf(obj).Elem()
|
|
||||||
id := reflect.Indirect(v.FieldByName("ID"))
|
|
||||||
lowerCaseTypeName := strings.ToLower(t.Name())
|
|
||||||
endpoint := `/api/` + lowerCaseTypeName + `/` + id.String()
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
|
|
||||||
b.WriteString(`<div class="widget-form">`)
|
|
||||||
b.WriteString(`<div class="widget" data-api="` + endpoint + `">`)
|
|
||||||
|
|
||||||
b.WriteString(`<h1>`)
|
|
||||||
b.WriteString(title)
|
|
||||||
b.WriteString(`</h1>`)
|
|
||||||
|
|
||||||
RenderObject(&b, obj, "")
|
|
||||||
|
|
||||||
if user != nil && (user.Role == "editor" || user.Role == "admin") {
|
|
||||||
b.WriteString(`<div class="buttons">`)
|
|
||||||
b.WriteString(`<button class="action" data-action="deleteObject" data-trigger="click" data-return-path="/soundtracks" data-confirm-type="soundtrack">` + utils.Icon("trash") + `Delete ` + t.Name() + `</button>`)
|
|
||||||
b.WriteString(`</div>`)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString("</div>")
|
|
||||||
b.WriteString("</div>")
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderObject ...
|
|
||||||
func RenderObject(b *bytes.Buffer, obj interface{}, idPrefix string) {
|
|
||||||
t := reflect.TypeOf(obj).Elem()
|
|
||||||
v := reflect.ValueOf(obj).Elem()
|
|
||||||
|
|
||||||
// Fields
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
RenderField(b, &v, field, idPrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderField ...
|
|
||||||
func RenderField(b *bytes.Buffer, v *reflect.Value, field reflect.StructField, idPrefix string) {
|
|
||||||
if field.Anonymous || field.Tag.Get("editable") != "true" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValue := reflect.Indirect(v.FieldByName(field.Name))
|
|
||||||
|
|
||||||
switch field.Type.String() {
|
|
||||||
case "string":
|
|
||||||
b.WriteString(components.InputText(idPrefix+field.Name, fieldValue.String(), field.Name, ""))
|
|
||||||
case "[]string":
|
|
||||||
b.WriteString(components.InputTags(idPrefix+field.Name, fieldValue.Interface().([]string), field.Name, field.Tag.Get("tooltip")))
|
|
||||||
case "bool":
|
|
||||||
if field.Name == "IsDraft" {
|
|
||||||
if fieldValue.Bool() {
|
|
||||||
b.WriteString(`<div class="buttons"><button class="action" data-action="publish" data-trigger="click">` + utils.Icon("unlock-alt") + `Publish</button></div>`)
|
|
||||||
}
|
|
||||||
// else {
|
|
||||||
// b.WriteString(`<div class="buttons"><button class="action" data-action="unpublish" data-trigger="click">` + utils.Icon("lock") + `Unpublish</button></div>`)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
case "[]*arn.ExternalMedia":
|
|
||||||
for sliceIndex := 0; sliceIndex < fieldValue.Len(); sliceIndex++ {
|
|
||||||
b.WriteString(`<div class="widget-section">`)
|
|
||||||
b.WriteString(`<div class="widget-title">` + strconv.Itoa(sliceIndex+1) + ". " + field.Name + `</div>`)
|
|
||||||
|
|
||||||
arrayObj := fieldValue.Index(sliceIndex).Interface()
|
|
||||||
arrayIDPrefix := fmt.Sprintf("%s[%d].", field.Name, sliceIndex)
|
|
||||||
RenderObject(b, arrayObj, arrayIDPrefix)
|
|
||||||
|
|
||||||
// Preview
|
|
||||||
b.WriteString(components.ExternalMedia(fieldValue.Index(sliceIndex).Interface().(*arn.ExternalMedia)))
|
|
||||||
|
|
||||||
// Remove button
|
|
||||||
b.WriteString(`<div class="buttons"><button class="action" data-action="arrayRemove" data-trigger="click" data-field="` + field.Name + `" data-index="`)
|
|
||||||
b.WriteString(strconv.Itoa(sliceIndex))
|
|
||||||
b.WriteString(`">` + utils.RawIcon("trash") + `</button></div>`)
|
|
||||||
|
|
||||||
b.WriteString(`</div>`)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString(`<div class="buttons">`)
|
|
||||||
b.WriteString(`<button class="action" data-action="arrayAppend" data-trigger="click" data-field="` + field.Name + `">` + utils.Icon("plus") + `Add ` + field.Name + `</button>`)
|
|
||||||
b.WriteString(`</div>`)
|
|
||||||
default:
|
|
||||||
panic("No edit form implementation for " + idPrefix + field.Name + " with type " + field.Type.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ component SoundTracks(tracks []*arn.SoundTrack, tracksPerPage int, user *arn.Use
|
|||||||
.music-buttons
|
.music-buttons
|
||||||
if user != nil
|
if user != nil
|
||||||
if user.DraftIndex().SoundTrackID == ""
|
if user.DraftIndex().SoundTrackID == ""
|
||||||
button.action(data-action="newSoundTrack", data-trigger="click")
|
button.action(data-action="newObject", data-trigger="click", data-type="soundtrack")
|
||||||
Icon("plus")
|
Icon("plus")
|
||||||
span Add soundtrack
|
span Add soundtrack
|
||||||
else
|
else
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export * from "./Actions/AnimeList"
|
export * from "./Actions/AnimeList"
|
||||||
export * from "./Actions/Delete"
|
|
||||||
export * from "./Actions/Diff"
|
export * from "./Actions/Diff"
|
||||||
export * from "./Actions/FollowUser"
|
export * from "./Actions/FollowUser"
|
||||||
export * from "./Actions/Forum"
|
export * from "./Actions/Forum"
|
||||||
@ -7,10 +6,10 @@ export * from "./Actions/InfiniteScroller"
|
|||||||
export * from "./Actions/Install"
|
export * from "./Actions/Install"
|
||||||
export * from "./Actions/Like"
|
export * from "./Actions/Like"
|
||||||
export * from "./Actions/Notifications"
|
export * from "./Actions/Notifications"
|
||||||
|
export * from "./Actions/Object"
|
||||||
export * from "./Actions/Publish"
|
export * from "./Actions/Publish"
|
||||||
export * from "./Actions/Search"
|
export * from "./Actions/Search"
|
||||||
export * from "./Actions/Serialization"
|
export * from "./Actions/Serialization"
|
||||||
export * from "./Actions/Shop"
|
export * from "./Actions/Shop"
|
||||||
export * from "./Actions/SideBar"
|
export * from "./Actions/SideBar"
|
||||||
export * from "./Actions/SoundTrack"
|
|
||||||
export * from "./Actions/StatusMessage"
|
export * from "./Actions/StatusMessage"
|
@ -1,5 +1,15 @@
|
|||||||
import { AnimeNotifier } from "../AnimeNotifier"
|
import { AnimeNotifier } from "../AnimeNotifier"
|
||||||
|
|
||||||
|
// New
|
||||||
|
export function newObject(arn: AnimeNotifier, button: HTMLButtonElement) {
|
||||||
|
let dataType = button.dataset.type
|
||||||
|
|
||||||
|
arn.post(`/api/new/${dataType}`, "")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(obj => arn.app.load(`/${dataType}/${obj.id}/edit`))
|
||||||
|
.catch(err => arn.statusMessage.showError(err))
|
||||||
|
}
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
export function deleteObject(arn: AnimeNotifier, button: HTMLButtonElement) {
|
export function deleteObject(arn: AnimeNotifier, button: HTMLButtonElement) {
|
||||||
let confirmType = button.dataset.confirmType
|
let confirmType = button.dataset.confirmType
|
@ -1,9 +0,0 @@
|
|||||||
import { AnimeNotifier } from "../AnimeNotifier"
|
|
||||||
|
|
||||||
// New soundtrack
|
|
||||||
export function newSoundTrack(arn: AnimeNotifier, button: HTMLButtonElement) {
|
|
||||||
arn.post("/api/new/soundtrack", "")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(track => arn.app.load(`/soundtrack/${track.id}/edit`))
|
|
||||||
.catch(err => arn.statusMessage.showError(err))
|
|
||||||
}
|
|
102
utils/editform/editform.go
Normal file
102
utils/editform/editform.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package editform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/animenotifier/arn"
|
||||||
|
"github.com/animenotifier/notify.moe/components"
|
||||||
|
"github.com/animenotifier/notify.moe/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Render ...
|
||||||
|
func Render(obj interface{}, title string, user *arn.User) string {
|
||||||
|
t := reflect.TypeOf(obj).Elem()
|
||||||
|
v := reflect.ValueOf(obj).Elem()
|
||||||
|
id := reflect.Indirect(v.FieldByName("ID"))
|
||||||
|
lowerCaseTypeName := strings.ToLower(t.Name())
|
||||||
|
endpoint := `/api/` + lowerCaseTypeName + `/` + id.String()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString(`<div class="widget-form">`)
|
||||||
|
b.WriteString(`<div class="widget" data-api="` + endpoint + `">`)
|
||||||
|
|
||||||
|
b.WriteString(`<h1>`)
|
||||||
|
b.WriteString(title)
|
||||||
|
b.WriteString(`</h1>`)
|
||||||
|
|
||||||
|
RenderObject(&b, obj, "")
|
||||||
|
|
||||||
|
if user != nil && (user.Role == "editor" || user.Role == "admin") {
|
||||||
|
b.WriteString(`<div class="buttons">`)
|
||||||
|
b.WriteString(`<div class="buttons"><button class="action" data-action="publish" data-trigger="click">` + utils.Icon("share-alt") + `Publish</button></div>`)
|
||||||
|
b.WriteString(`<button class="action" data-action="deleteObject" data-trigger="click" data-return-path="/` + lowerCaseTypeName + "s" + `" data-confirm-type="` + lowerCaseTypeName + `">` + utils.Icon("trash") + `Delete ` + t.Name() + `</button>`)
|
||||||
|
b.WriteString(`</div>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString("</div>")
|
||||||
|
b.WriteString("</div>")
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderObject ...
|
||||||
|
func RenderObject(b *bytes.Buffer, obj interface{}, idPrefix string) {
|
||||||
|
t := reflect.TypeOf(obj).Elem()
|
||||||
|
v := reflect.ValueOf(obj).Elem()
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
RenderField(b, &v, field, idPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderField ...
|
||||||
|
func RenderField(b *bytes.Buffer, v *reflect.Value, field reflect.StructField, idPrefix string) {
|
||||||
|
if field.Anonymous || field.Tag.Get("editable") != "true" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValue := reflect.Indirect(v.FieldByName(field.Name))
|
||||||
|
|
||||||
|
switch field.Type.String() {
|
||||||
|
case "string":
|
||||||
|
b.WriteString(components.InputText(idPrefix+field.Name, fieldValue.String(), field.Name, ""))
|
||||||
|
case "[]string":
|
||||||
|
b.WriteString(components.InputTags(idPrefix+field.Name, fieldValue.Interface().([]string), field.Name, field.Tag.Get("tooltip")))
|
||||||
|
case "bool":
|
||||||
|
if field.Name == "IsDraft" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "[]*arn.ExternalMedia":
|
||||||
|
for sliceIndex := 0; sliceIndex < fieldValue.Len(); sliceIndex++ {
|
||||||
|
b.WriteString(`<div class="widget-section">`)
|
||||||
|
b.WriteString(`<div class="widget-title">` + strconv.Itoa(sliceIndex+1) + ". " + field.Name + `</div>`)
|
||||||
|
|
||||||
|
arrayObj := fieldValue.Index(sliceIndex).Interface()
|
||||||
|
arrayIDPrefix := fmt.Sprintf("%s[%d].", field.Name, sliceIndex)
|
||||||
|
RenderObject(b, arrayObj, arrayIDPrefix)
|
||||||
|
|
||||||
|
// Preview
|
||||||
|
b.WriteString(components.ExternalMedia(fieldValue.Index(sliceIndex).Interface().(*arn.ExternalMedia)))
|
||||||
|
|
||||||
|
// Remove button
|
||||||
|
b.WriteString(`<div class="buttons"><button class="action" data-action="arrayRemove" data-trigger="click" data-field="` + field.Name + `" data-index="`)
|
||||||
|
b.WriteString(strconv.Itoa(sliceIndex))
|
||||||
|
b.WriteString(`">` + utils.RawIcon("trash") + `</button></div>`)
|
||||||
|
|
||||||
|
b.WriteString(`</div>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(`<div class="buttons">`)
|
||||||
|
b.WriteString(`<button class="action" data-action="arrayAppend" data-trigger="click" data-field="` + field.Name + `">` + utils.Icon("plus") + `Add ` + field.Name + `</button>`)
|
||||||
|
b.WriteString(`</div>`)
|
||||||
|
default:
|
||||||
|
panic("No edit form implementation for " + idPrefix + field.Name + " with type " + field.Type.String())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user