From 63a421e6a28c01744db2969dde8453677f11e542 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Tue, 17 Oct 2017 23:17:04 +0200 Subject: [PATCH] Implemented groups --- main.go | 9 +- mixins/Sidebar.pixy | 3 + pages/group/edit.go | 32 +++++++ pages/group/group.go | 29 +++++++ pages/group/group.pixy | 15 ++++ pages/groups/groups.go | 25 ++++++ pages/groups/groups.pixy | 21 +++++ pages/newsoundtrack/newsoundtrack.go | 20 ----- pages/newsoundtrack/newsoundtrack.pixy | 25 ------ pages/soundtrack/edit.go | 101 +--------------------- pages/soundtracks/soundtracks.pixy | 2 +- scripts/Actions.ts | 3 +- scripts/Actions/{Delete.ts => Object.ts} | 10 +++ scripts/Actions/SoundTrack.ts | 9 -- utils/editform/editform.go | 102 +++++++++++++++++++++++ 15 files changed, 248 insertions(+), 158 deletions(-) create mode 100644 pages/group/edit.go create mode 100644 pages/group/group.go create mode 100644 pages/group/group.pixy create mode 100644 pages/groups/groups.go create mode 100644 pages/groups/groups.pixy delete mode 100644 pages/newsoundtrack/newsoundtrack.go delete mode 100644 pages/newsoundtrack/newsoundtrack.pixy rename scripts/Actions/{Delete.ts => Object.ts} (61%) delete mode 100644 scripts/Actions/SoundTrack.ts create mode 100644 utils/editform/editform.go diff --git a/main.go b/main.go index 44437560..ad60a955 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "github.com/animenotifier/notify.moe/pages/explore" "github.com/animenotifier/notify.moe/pages/forum" "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/inventory" "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/login" "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/notifications" "github.com/animenotifier/notify.moe/pages/paypal" @@ -99,10 +100,14 @@ func configure(app *aero.Application) *aero.Application { // Soundtracks app.Ajax("/soundtracks", soundtracks.Get) app.Ajax("/soundtracks/from/:index", soundtracks.From) - app.Ajax("/new/soundtrack", newsoundtrack.Get) app.Ajax("/soundtrack/:id", soundtrack.Get) 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 app.Ajax("/user", user.Get) app.Ajax("/user/:nick", profile.Get) diff --git a/mixins/Sidebar.pixy b/mixins/Sidebar.pixy index 0a53a96b..237f062b 100644 --- a/mixins/Sidebar.pixy +++ b/mixins/Sidebar.pixy @@ -21,6 +21,9 @@ component Sidebar(user *arn.User) //- SidebarButton("Search", "/search", "search") if user != nil + if user.Role == "admin" + SidebarButton("Groups", "/groups", "users") + SidebarButton("Shop", "/shop", "shopping-cart") SidebarButton("Statistics", "/statistics", "pie-chart") SidebarButton("Settings", "/settings", "cog") diff --git a/pages/group/edit.go b/pages/group/edit.go new file mode 100644 index 00000000..efaa21e0 --- /dev/null +++ b/pages/group/edit.go @@ -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)) +} diff --git a/pages/group/group.go b/pages/group/group.go new file mode 100644 index 00000000..5946a495 --- /dev/null +++ b/pages/group/group.go @@ -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)) +} diff --git a/pages/group/group.pixy b/pages/group/group.pixy new file mode 100644 index 00000000..d4c29012 --- /dev/null +++ b/pages/group/group.pixy @@ -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") \ No newline at end of file diff --git a/pages/groups/groups.go b/pages/groups/groups.go new file mode 100644 index 00000000..eb364d9d --- /dev/null +++ b/pages/groups/groups.go @@ -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)) +} diff --git a/pages/groups/groups.pixy b/pages/groups/groups.pixy new file mode 100644 index 00000000..6b6c8ad8 --- /dev/null +++ b/pages/groups/groups.pixy @@ -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 \ No newline at end of file diff --git a/pages/newsoundtrack/newsoundtrack.go b/pages/newsoundtrack/newsoundtrack.go deleted file mode 100644 index 5a3a7fb2..00000000 --- a/pages/newsoundtrack/newsoundtrack.go +++ /dev/null @@ -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)) -} diff --git a/pages/newsoundtrack/newsoundtrack.pixy b/pages/newsoundtrack/newsoundtrack.pixy deleted file mode 100644 index 80dbd8cd..00000000 --- a/pages/newsoundtrack/newsoundtrack.pixy +++ /dev/null @@ -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 \ No newline at end of file diff --git a/pages/soundtrack/edit.go b/pages/soundtrack/edit.go index 59c06a23..f1ced793 100644 --- a/pages/soundtrack/edit.go +++ b/pages/soundtrack/edit.go @@ -1,15 +1,11 @@ package soundtrack import ( - "bytes" - "fmt" "net/http" - "reflect" - "strconv" - "strings" "github.com/animenotifier/notify.moe/components" "github.com/animenotifier/notify.moe/utils" + "github.com/animenotifier/notify.moe/utils/editform" "github.com/aerogo/aero" "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 } - return ctx.HTML(components.SoundTrackTabs(track) + EditForm(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(`
`) - b.WriteString(`
`) - - b.WriteString(`

`) - b.WriteString(title) - b.WriteString(`

`) - - RenderObject(&b, obj, "") - - if user != nil && (user.Role == "editor" || user.Role == "admin") { - b.WriteString(`
`) - b.WriteString(``) - b.WriteString(`
`) - } - - b.WriteString("
") - b.WriteString("
") - - 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(`
`) - } - // else { - // b.WriteString(`
`) - // } - } - case "[]*arn.ExternalMedia": - for sliceIndex := 0; sliceIndex < fieldValue.Len(); sliceIndex++ { - b.WriteString(`
`) - b.WriteString(`
` + strconv.Itoa(sliceIndex+1) + ". " + field.Name + `
`) - - 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(`
`) - - b.WriteString(`
`) - } - - b.WriteString(`
`) - b.WriteString(``) - b.WriteString(`
`) - default: - panic("No edit form implementation for " + idPrefix + field.Name + " with type " + field.Type.String()) - } + return ctx.HTML(components.SoundTrackTabs(track) + editform.Render(track, "Edit soundtrack", user)) } diff --git a/pages/soundtracks/soundtracks.pixy b/pages/soundtracks/soundtracks.pixy index e05647d1..a219f5d7 100644 --- a/pages/soundtracks/soundtracks.pixy +++ b/pages/soundtracks/soundtracks.pixy @@ -4,7 +4,7 @@ component SoundTracks(tracks []*arn.SoundTrack, tracksPerPage int, user *arn.Use .music-buttons if user != nil 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") span Add soundtrack else diff --git a/scripts/Actions.ts b/scripts/Actions.ts index 71138565..b5e4969a 100644 --- a/scripts/Actions.ts +++ b/scripts/Actions.ts @@ -1,5 +1,4 @@ export * from "./Actions/AnimeList" -export * from "./Actions/Delete" export * from "./Actions/Diff" export * from "./Actions/FollowUser" export * from "./Actions/Forum" @@ -7,10 +6,10 @@ export * from "./Actions/InfiniteScroller" export * from "./Actions/Install" export * from "./Actions/Like" export * from "./Actions/Notifications" +export * from "./Actions/Object" export * from "./Actions/Publish" export * from "./Actions/Search" export * from "./Actions/Serialization" export * from "./Actions/Shop" export * from "./Actions/SideBar" -export * from "./Actions/SoundTrack" export * from "./Actions/StatusMessage" \ No newline at end of file diff --git a/scripts/Actions/Delete.ts b/scripts/Actions/Object.ts similarity index 61% rename from scripts/Actions/Delete.ts rename to scripts/Actions/Object.ts index 23559b91..7dac7d59 100644 --- a/scripts/Actions/Delete.ts +++ b/scripts/Actions/Object.ts @@ -1,5 +1,15 @@ 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 export function deleteObject(arn: AnimeNotifier, button: HTMLButtonElement) { let confirmType = button.dataset.confirmType diff --git a/scripts/Actions/SoundTrack.ts b/scripts/Actions/SoundTrack.ts deleted file mode 100644 index 1e435414..00000000 --- a/scripts/Actions/SoundTrack.ts +++ /dev/null @@ -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)) -} \ No newline at end of file diff --git a/utils/editform/editform.go b/utils/editform/editform.go new file mode 100644 index 00000000..c3a1c736 --- /dev/null +++ b/utils/editform/editform.go @@ -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(`
`) + b.WriteString(`
`) + + b.WriteString(`

`) + b.WriteString(title) + b.WriteString(`

`) + + RenderObject(&b, obj, "") + + if user != nil && (user.Role == "editor" || user.Role == "admin") { + b.WriteString(`
`) + b.WriteString(`
`) + b.WriteString(``) + b.WriteString(`
`) + } + + b.WriteString("
") + b.WriteString("
") + + 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(`
`) + b.WriteString(`
` + strconv.Itoa(sliceIndex+1) + ". " + field.Name + `
`) + + 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(`
`) + + b.WriteString(`
`) + } + + b.WriteString(`
`) + b.WriteString(``) + b.WriteString(`
`) + default: + panic("No edit form implementation for " + idPrefix + field.Name + " with type " + field.Type.String()) + } +}