diff --git a/pkg/config/enum.go b/pkg/config/enum.go index dc497e2..7694d47 100644 --- a/pkg/config/enum.go +++ b/pkg/config/enum.go @@ -90,6 +90,7 @@ var ( // Default forum categories for forum landing page. ForumCategories = []string{ "Rules and Announcements", + "The Inner Circle", "Nudists", "Exhibitionists", "Photo Boards", diff --git a/pkg/controller/account/inner_circle.go b/pkg/controller/account/inner_circle.go new file mode 100644 index 0000000..94f5748 --- /dev/null +++ b/pkg/controller/account/inner_circle.go @@ -0,0 +1,137 @@ +package account + +import ( + "net/http" + + "code.nonshy.com/nonshy/website/pkg/models" + "code.nonshy.com/nonshy/website/pkg/session" + "code.nonshy.com/nonshy/website/pkg/templates" +) + +// InnerCircle is the landing page for inner circle members only. +func InnerCircle() http.HandlerFunc { + tmpl := templates.Must("account/inner_circle.html") + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + currentUser, err := session.CurrentUser(r) + if err != nil || !currentUser.IsInnerCircle() { + templates.NotFoundPage(w, r) + return + } + + if err := tmpl.Execute(w, r, nil); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }) +} + +// InviteCircle is the landing page to invite a user into the circle. +func InviteCircle() http.HandlerFunc { + tmpl := templates.Must("account/invite_circle.html") + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + currentUser, err := session.CurrentUser(r) + if err != nil || !currentUser.IsInnerCircle() { + templates.NotFoundPage(w, r) + return + } + + // Invite whom? + username := r.FormValue("to") + user, err := models.FindUser(username) + if err != nil { + templates.NotFoundPage(w, r) + return + } + + if currentUser.ID == user.ID && currentUser.InnerCircle { + session.FlashError(w, r, "You are already part of the inner circle.") + templates.Redirect(w, "/inner-circle") + return + } + + // Any blocking? + if models.IsBlocking(currentUser.ID, user.ID) && !currentUser.IsAdmin { + session.FlashError(w, r, "You are blocked from inviting this user to the circle.") + templates.Redirect(w, "/inner-circle") + return + } + + // POSTing? + if r.Method == http.MethodPost { + var ( + confirm = r.FormValue("intent") == "confirm" + ) + + if !confirm { + templates.Redirect(w, "/u/"+username) + return + } + + // Add them! + if err := models.AddToInnerCircle(user); err != nil { + session.FlashError(w, r, "Couldn't add to the inner circle: %s", err) + } + + session.Flash(w, r, "%s has been added to the inner circle!", user.Username) + templates.Redirect(w, "/photo/u/"+user.Username) + return + } + + var vars = map[string]interface{}{ + "User": user, + } + if err := tmpl.Execute(w, r, vars); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }) +} + +// RemoveCircle is the admin-only page to remove a member from the circle. +func RemoveCircle() http.HandlerFunc { + tmpl := templates.Must("account/remove_circle.html") + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + currentUser, err := session.CurrentUser(r) + if err != nil || !currentUser.IsInnerCircle() { + templates.NotFoundPage(w, r) + return + } + + // Remove whom? + username := r.FormValue("to") + user, err := models.FindUser(username) + if err != nil { + templates.NotFoundPage(w, r) + return + } + + // POSTing? + if r.Method == http.MethodPost { + var ( + confirm = r.FormValue("intent") == "confirm" + ) + + if !confirm { + templates.Redirect(w, "/u/"+username) + return + } + + // Add them! + if err := models.RemoveFromInnerCircle(user); err != nil { + session.FlashError(w, r, "Couldn't remove from the inner circle: %s", err) + } + + session.Flash(w, r, "%s has been removed from the inner circle!", user.Username) + templates.Redirect(w, "/u/"+user.Username) + return + } + + var vars = map[string]interface{}{ + "User": user, + } + if err := tmpl.Execute(w, r, vars); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }) +} diff --git a/pkg/controller/account/search.go b/pkg/controller/account/search.go index e743540..b0fcf85 100644 --- a/pkg/controller/account/search.go +++ b/pkg/controller/account/search.go @@ -77,7 +77,8 @@ func Search() http.HandlerFunc { Gender: gender, Orientation: orientation, MaritalStatus: maritalStatus, - Certified: isCertified == "true", + Certified: isCertified != "false", + InnerCircle: isCertified == "circle", AgeMin: ageMin, AgeMax: ageMax, }, pager) diff --git a/pkg/controller/forum/add_edit.go b/pkg/controller/forum/add_edit.go index 41e78f8..d0aa26a 100644 --- a/pkg/controller/forum/add_edit.go +++ b/pkg/controller/forum/add_edit.go @@ -63,6 +63,7 @@ func AddEdit() http.HandlerFunc { isExplicit = r.PostFormValue("explicit") == "true" isPrivileged = r.PostFormValue("privileged") == "true" isPermitPhotos = r.PostFormValue("permit_photos") == "true" + isInnerCircle = r.PostFormValue("inner_circle") == "true" ) // Sanity check admin-only settings. @@ -79,6 +80,7 @@ func AddEdit() http.HandlerFunc { forum.Explicit = isExplicit forum.Privileged = isPrivileged forum.PermitPhotos = isPermitPhotos + forum.InnerCircle = isInnerCircle // Save it. if err := forum.Save(); err == nil { @@ -111,6 +113,7 @@ func AddEdit() http.HandlerFunc { Explicit: isExplicit, Privileged: isPrivileged, PermitPhotos: isPermitPhotos, + InnerCircle: isInnerCircle, } if err := models.CreateForum(forum); err == nil { diff --git a/pkg/controller/forum/forum.go b/pkg/controller/forum/forum.go index e45bcd6..396aba1 100644 --- a/pkg/controller/forum/forum.go +++ b/pkg/controller/forum/forum.go @@ -41,6 +41,12 @@ func Forum() http.HandlerFunc { return } + // Is it an inner circle forum? + if forum.InnerCircle && !currentUser.IsInnerCircle() { + templates.NotFoundPage(w, r) + return + } + // Get the pinned threads. pinned, err := models.PinnedThreads(forum) if err != nil { diff --git a/pkg/controller/forum/thread.go b/pkg/controller/forum/thread.go index 1a1cb22..4ade684 100644 --- a/pkg/controller/forum/thread.go +++ b/pkg/controller/forum/thread.go @@ -54,6 +54,12 @@ func Thread() http.HandlerFunc { return } + // Is it an inner circle forum? + if forum.InnerCircle && !currentUser.IsInnerCircle() { + templates.NotFoundPage(w, r) + return + } + // Ping the view count on this thread. if err := thread.View(currentUser.ID); err != nil { log.Error("Couldn't ping view count on thread %d: %s", thread.ID, err) diff --git a/pkg/controller/photo/upload.go b/pkg/controller/photo/upload.go index 66922a7..d99683d 100644 --- a/pkg/controller/photo/upload.go +++ b/pkg/controller/photo/upload.go @@ -156,6 +156,9 @@ func notifyFriendsNewPhoto(photo *models.Photo, currentUser *models.User) { // Private grantees friendIDs = models.PrivateGranteeUserIDs(currentUser.ID) log.Info("Notify %d private grantees about the new photo by %s", len(friendIDs), currentUser.Username) + } else if photo.Visibility == models.PhotoInnerCircle { + friendIDs = models.FriendIDsInCircle(currentUser.ID) + log.Info("Notify %d circle friends about the new photo by %s", len(friendIDs), currentUser.Username) } else { // Get all our friend IDs. If this photo is Explicit, only select // the friends who've opted-in for Explicit photo visibility. diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index d4c1b8b..3f3325c 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -101,6 +101,11 @@ func UserPhotos() http.HandlerFunc { visibility = append(visibility, models.PhotoFriends) } + // Inner circle photos. + if currentUser.IsInnerCircle() { + visibility = append(visibility, models.PhotoInnerCircle) + } + // Explicit photo filter? explicit := currentUser.Explicit if isOwnPhotos { diff --git a/pkg/models/forum.go b/pkg/models/forum.go index 43aba2a..92944fe 100644 --- a/pkg/models/forum.go +++ b/pkg/models/forum.go @@ -20,6 +20,7 @@ type Forum struct { Explicit bool `gorm:"index"` Privileged bool PermitPhotos bool + InnerCircle bool CreatedAt time.Time UpdatedAt time.Time } @@ -95,6 +96,11 @@ func PaginateForums(user *User, categories []string, pager *Pagination) ([]*Foru wheres = append(wheres, "explicit = false") } + // Hide circle forums if the user isn't in the circle. + if !user.IsInnerCircle() { + wheres = append(wheres, "inner_circle is not true") + } + // Filters? if len(wheres) > 0 { query = query.Where( @@ -172,5 +178,14 @@ func CategorizeForums(fs []*Forum, categories []string) []*CategorizedForum { result[idx].Forums = append(result[idx].Forums, forum) } - return result + // Remove any blank categories with no boards. + var filtered = []*CategorizedForum{} + for _, forum := range result { + if len(forum.Forums) == 0 { + continue + } + filtered = append(filtered, forum) + } + + return filtered } diff --git a/pkg/models/friend.go b/pkg/models/friend.go index 541465d..67289dc 100644 --- a/pkg/models/friend.go +++ b/pkg/models/friend.go @@ -131,6 +131,30 @@ func FriendIDsAreExplicit(userId uint64) []uint64 { return userIDs } +// FriendIDsInCircle returns friend IDs who are part of the inner circle. +func FriendIDsInCircle(userId uint64) []uint64 { + var ( + userIDs = []uint64{} + ) + + err := DB.Table( + "friends", + ).Joins( + "JOIN users ON (users.id = friends.target_user_id)", + ).Select( + "friends.target_user_id AS friend_id", + ).Where( + "friends.source_user_id = ? AND friends.approved = ? AND (users.inner_circle = ? OR users.is_admin = ?)", + userId, true, true, true, + ).Scan(&userIDs) + + if err.Error != nil { + log.Error("SQL error collecting circle FriendIDs for %d: %s", userId, err) + } + + return userIDs +} + // CountFriendRequests gets a count of pending requests for the user. func CountFriendRequests(userID uint64) (int64, error) { var count int64 diff --git a/pkg/models/notification.go b/pkg/models/notification.go index 3a8e9da..02da47e 100644 --- a/pkg/models/notification.go +++ b/pkg/models/notification.go @@ -40,6 +40,7 @@ const ( NotificationCertApproved = "cert_approved" NotificationPrivatePhoto = "private_photo" NotificationNewPhoto = "new_photo" + NotificationInnerCircle = "inner_circle" NotificationCustom = "custom" // custom message pushed ) diff --git a/pkg/models/photo.go b/pkg/models/photo.go index b534924..31ea4e3 100644 --- a/pkg/models/photo.go +++ b/pkg/models/photo.go @@ -29,9 +29,10 @@ type Photo struct { type PhotoVisibility string const ( - PhotoPublic PhotoVisibility = "public" // on profile page and/or public gallery - PhotoFriends = "friends" // only friends can see it - PhotoPrivate = "private" // private + PhotoPublic PhotoVisibility = "public" // on profile page and/or public gallery + PhotoFriends = "friends" // only friends can see it + PhotoPrivate = "private" // private + PhotoInnerCircle = "circle" // inner circle ) // PhotoVisibility preset settings. @@ -42,6 +43,14 @@ var ( PhotoPrivate, } + // "All" but also for Inner Circle members. + PhotoVisibilityCircle = []PhotoVisibility{ + PhotoPublic, + PhotoFriends, + PhotoPrivate, + PhotoInnerCircle, + } + // Site Gallery visibility for when your friends show up in the gallery. // Or: "Friends + Gallery" photos can appear to your friends in the Site Gallery. PhotoVisibilityFriends = []string{ @@ -213,6 +222,12 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot placeholders = []interface{}{} ) + // Define "all photos visibilities" + var photosAll = PhotoVisibilityAll + if user.IsInnerCircle() { + photosAll = PhotoVisibilityCircle + } + // Admins see everything on the site (only an admin user can get an admin view). adminView = user.IsAdmin && adminView @@ -229,7 +244,7 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot ) placeholders = append(placeholders, friendIDs, PhotoVisibilityFriends, - privateUserIDs, PhotoVisibilityAll, + privateUserIDs, photosAll, ) } else { // You can see friends' Friend photos but only public for non-friends. @@ -240,7 +255,7 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot ) placeholders = append(placeholders, friendIDs, PhotoVisibilityFriends, - privateUserIDs, PhotoVisibilityAll, + privateUserIDs, photosAll, friendIDs, PhotoPublic, ) } diff --git a/pkg/models/user.go b/pkg/models/user.go index e8dabde..f078421 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -25,7 +25,8 @@ type User struct { Name *string Birthdate time.Time Certified bool - Explicit bool // user has opted-in to see explicit content + Explicit bool `gorm:"index"` // user has opted-in to see explicit content + InnerCircle bool `gorm:"index"` // user is in the inner circle CreatedAt time.Time `gorm:"index"` UpdatedAt time.Time `gorm:"index"` LastLoginAt time.Time `gorm:"index"` @@ -185,6 +186,7 @@ type UserSearch struct { Orientation string MaritalStatus string Certified bool + InnerCircle bool AgeMin int AgeMax int } @@ -249,6 +251,11 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er placeholders = append(placeholders, search.Certified, UserStatusActive) } + if search.InnerCircle { + wheres = append(wheres, "inner_circle = ? OR is_admin = ?") + placeholders = append(placeholders, true, true) + } + if search.AgeMin > 0 { date := time.Now().AddDate(-search.AgeMin, 0, 0) wheres = append(wheres, "birthdate <= ?") diff --git a/pkg/models/user_inner_circle.go b/pkg/models/user_inner_circle.go new file mode 100644 index 0000000..bac7403 --- /dev/null +++ b/pkg/models/user_inner_circle.go @@ -0,0 +1,67 @@ +package models + +import ( + "errors" + + "code.nonshy.com/nonshy/website/pkg/log" +) + +// Helper functions relating to the inner circle. + +// IsInnerCircle returns whether the user is in the inner circle (including if the user is an admin, who is always in the inner circle). +func (u *User) IsInnerCircle() bool { + return u.InnerCircle || u.IsAdmin +} + +// AddToInnerCircle adds a user to the circle, sending them a notification in the process. +func AddToInnerCircle(u *User) error { + if u.InnerCircle { + return errors.New("already a part of the inner circle") + } + + u.InnerCircle = true + if err := u.Save(); err != nil { + return err + } + + // Send them a notification. + notif := &Notification{ + UserID: u.ID, + AboutUserID: &u.ID, + Type: NotificationInnerCircle, + Link: "/inner-circle", + TableName: "__inner_circle", + TableID: u.ID, + } + if err := CreateNotification(notif); err != nil { + log.Error("AddToInnerCircle: couldn't create notification: %s", err) + } + + return nil +} + +// RemoveFromInnerCircle kicks a user from the inner circle. Any photo they +// had that was marked circle-only is updated to public. +func RemoveFromInnerCircle(u *User) error { + if !u.InnerCircle { + return errors.New("is not a part of the inner circle") + } + + u.InnerCircle = false + if err := u.Save(); err != nil { + return err + } + + // Update their circle-only photos to public. + if err := DB.Model(&Photo{}).Where( + "user_id = ? AND visibility = ?", + u.ID, PhotoInnerCircle, + ).Update("visibility", PhotoPublic); err != nil { + log.Error("RemoveFromInnerCircle: couldn't update photo visibility: %s", err) + } + + // Revoke any historic notification about the circle. + RemoveNotification("__inner_circle", u.ID) + + return nil +} diff --git a/pkg/router/router.go b/pkg/router/router.go index ec87181..5e418ed 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -62,6 +62,8 @@ func New() http.Handler { mux.Handle("/comments", middleware.LoginRequired(comment.PostComment())) mux.Handle("/comments/subscription", middleware.LoginRequired(comment.Subscription())) mux.Handle("/admin/unimpersonate", middleware.LoginRequired(admin.Unimpersonate())) + mux.Handle("/inner-circle", middleware.LoginRequired(account.InnerCircle())) + mux.Handle("/inner-circle/invite", middleware.LoginRequired(account.InviteCircle())) // Certification Required. Pages that only full (verified) members can access. mux.Handle("/photo/gallery", middleware.CertRequired(photo.SiteGallery())) @@ -81,6 +83,7 @@ func New() http.Handler { mux.Handle("/admin/user-action", middleware.AdminRequired(admin.UserActions())) mux.Handle("/forum/admin", middleware.AdminRequired(forum.Manage())) mux.Handle("/forum/admin/edit", middleware.AdminRequired(forum.AddEdit())) + mux.Handle("/inner-circle/remove", middleware.AdminRequired(account.RemoveCircle())) // JSON API endpoints. mux.HandleFunc("/v1/version", api.Version()) diff --git a/pkg/templates/template_funcs.go b/pkg/templates/template_funcs.go index 851c36f..d41a58e 100644 --- a/pkg/templates/template_funcs.go +++ b/pkg/templates/template_funcs.go @@ -39,10 +39,14 @@ func TemplateFuncs(r *http.Request) template.FuncMap { )) }, "PrettyTitleShort": func() template.HTML { - return template.HTML(fmt.Sprintf( - `n` + - `s`, - )) + return template.HTML(`n` + + `s`, + ) + }, + "PrettyCircle": func() template.HTML { + return template.HTML( + `Inner circle`, + ) }, "Pluralize": Pluralize[int], "Pluralize64": Pluralize[int64], diff --git a/web/static/css/theme.css b/web/static/css/theme.css index b13db08..953abe3 100644 --- a/web/static/css/theme.css +++ b/web/static/css/theme.css @@ -55,6 +55,10 @@ abbr { background-image: linear-gradient(141deg, #b329b1 0, #9948c7 71%, #7156d2 100%); } +.hero.is-inner-circle { + background-image: linear-gradient(141deg, #294eb3 0, #9948c7 71%, #d256d2 100%) +} + /* Mobile: notification badge near the hamburger menu */ .nonshy-mobile-notification { position: absolute; diff --git a/web/static/img/circle-10.png b/web/static/img/circle-10.png new file mode 100644 index 0000000..36dc3a1 Binary files /dev/null and b/web/static/img/circle-10.png differ diff --git a/web/static/img/circle-16.png b/web/static/img/circle-16.png new file mode 100644 index 0000000..77dc809 Binary files /dev/null and b/web/static/img/circle-16.png differ diff --git a/web/static/img/circle-24.png b/web/static/img/circle-24.png new file mode 100644 index 0000000..1bb0030 Binary files /dev/null and b/web/static/img/circle-24.png differ diff --git a/web/static/img/circle-32.png b/web/static/img/circle-32.png new file mode 100644 index 0000000..5e5c8ee Binary files /dev/null and b/web/static/img/circle-32.png differ diff --git a/web/templates/account/dashboard.html b/web/templates/account/dashboard.html index 29d5c71..c71ccb9 100644 --- a/web/templates/account/dashboard.html +++ b/web/templates/account/dashboard.html @@ -326,6 +326,8 @@ {{if and $Body.Photo (eq $Body.Photo.Visibility "private")}} + {{else if and $Body.Photo (eq $Body.Photo.Visibility "circle")}} + {{else}} {{end}} @@ -349,6 +351,15 @@ Your certification photo was rejected! + {{else if eq .Type "inner_circle"}} + + + You have been added to the {{PrettyCircle}} of nonshy. + + +
+ Click to learn more about the inner circle. +
{{else}} {{.AboutUser.Username}} {{.Type}} {{.TableName}} {{.TableID}} {{end}} diff --git a/web/templates/account/inner_circle.html b/web/templates/account/inner_circle.html new file mode 100644 index 0000000..e0548c8 --- /dev/null +++ b/web/templates/account/inner_circle.html @@ -0,0 +1,118 @@ +{{define "title"}}The inner circle{{end}} +{{define "content"}} +
+
+
+
+

+ + The inner circle +

+
+
+
+
+ +
+ +
+

+ Congratulations! You have been added to the {{PrettyCircle}} because you + exemplify what it truly means to be a {{PrettyTitle}} nudist. +

+ +

What is the inner circle?

+ +

+ The inner circle is for {{PrettyTitle}} members who actually share a lot of nude pictures, + with face, of themselves on their profile page. It is "the party inside the party" + designed only for members who truly embrace the spirit of the {{PrettyTitle}} website by boldly + sharing nude pics with face for other nonshy nudists to see. +

+ +

What can I do for being in the inner circle?

+ +

+ As a part of the inner circle, you have access to the following new features: +

+ +
    +
  • + When + Uploading a photo you + have a new Visibility option for "{{PrettyCircle}} " + so that only members of the inner circle can see those pictures. +
  • +
  • + On the + Site Gallery + you can filter for Inner Circle-only photos shared + by other members of the circle. +
  • +
  • + On the + Member Directory + you can see who else is in the inner circle. +
  • +
  • + On the + Forums + you can access exclusive inner circle-only boards. +
  • +
  • + On your profile page you get an "Inner circle" badge near your + Certified status. This badge is only visible to members of the inner circle. +
  • +
  • + You may invite other members to join the inner circle. +
  • +
+ +

How do I invite others to join the inner circle?

+ +

+ When you are viewing a member's photo gallery page, look for the prompt at the top of the + page. +

+ +

+ If a member posts several nude pics including face you should invite them to join + the inner circle. All members of the circle are allowed to invite new members to join. We trust your + judgment: please only invite like-minded nudists who actually share nudes with face + to join the inner circle. +

+ +

Please keep the existence of the inner circle a secret

+ +

+ The inner circle is not publicly advertised on the site. This is to help ensure that "bad actors" won't + try and game the system (e.g., by begging somebody to invite them into the circle, or uploading a bare + minimum of nude pics for somebody to invite them only for them to delete their nudes and try and stay + in the inner circle). Plus, it adds an air of exclusivity to keep the existence of the circle on the + down low. +

+ +

Still continue to share at least some nudes on "public"

+ +

+ With the new Photo visibility option for "inner circle only" you may tag your best nudes for only members + of the inner circle to see. However, you should still continue to share at least some photos on + "Public" as you were doing previously. This is for a couple of reasons: +

+ +
    +
  • + Members who are not in the circle won't see your circle-only photos. If for example you + placed all of your nudes on circle-only you would appear to look the same as someone who + uploaded no nudes at all. +
  • +
  • + The "Shy Account" system of the main website still applies: if you have + not one public photo on your page you may be marked as a Shy Account and be limited from some site + features such as the Chat Room. +
  • +
+
+ +
+{{end}} \ No newline at end of file diff --git a/web/templates/account/invite_circle.html b/web/templates/account/invite_circle.html new file mode 100644 index 0000000..dfc1c3b --- /dev/null +++ b/web/templates/account/invite_circle.html @@ -0,0 +1,104 @@ +{{define "title"}}Invite to the inner circle{{end}} +{{define "content"}} +
+
+
+
+

+ + Invite to the inner circle +

+
+
+
+ +
+
+
+ +
+ +
+ +
+
+ {{template "avatar-64x64" .User}} +
+
+

{{.User.NameOrUsername}}

+

+ + {{.User.Username}} +

+
+
+ +
+ {{InputCSRF}} + + +
+

+ Do you want to invite {{.User.Username}} to join the + {{PrettyCircle}}? Please review the following notes: +

+ +
    +
  • + The inner circle is designed for {{PrettyTitle}} members who actually post + several nude photos with face on their profile page. + If {{.User.Username}} only has clothed selfies on their page, or keeps + their face hidden or cropped out of their nudes, please do not + invite them to join the inner circle. +
  • +
  • + All members of the inner circle are allowed to invite others to join the + circle. We trust your judgment -- help ensure that the inner circle is only + made up of truly non-shy nudists who show their whole body on their profile + page. +
  • +
+ +

+ Note: while we use the word "invite" they will actually be added to the inner circle + immediately and receive a notification that they had been added. They won't know that + it was you who invited them to join the circle. +

+
+ +
+ + +
+
+ +
+
+ +
+
+
+ +
+ + +{{end}} \ No newline at end of file diff --git a/web/templates/account/profile.html b/web/templates/account/profile.html index e97ef25..112d69e 100644 --- a/web/templates/account/profile.html +++ b/web/templates/account/profile.html @@ -104,6 +104,17 @@ {{end}} + {{if and .CurrentUser.IsInnerCircle .User.IsInnerCircle}} +
+
+ + + + {{PrettyCircle}} +
+
+ {{end}} + {{if .User.IsAdmin}}
diff --git a/web/templates/account/remove_circle.html b/web/templates/account/remove_circle.html new file mode 100644 index 0000000..ac9b883 --- /dev/null +++ b/web/templates/account/remove_circle.html @@ -0,0 +1,94 @@ +{{define "title"}}Remove from the inner circle{{end}} +{{define "content"}} +
+
+
+
+

+ + Remove from the inner circle +

+
+
+
+ +
+
+
+ +
+
+

+ + Remove from the inner circle +

+
+
+ +
+
+ {{template "avatar-64x64" .User}} +
+
+

{{.User.NameOrUsername}}

+

+ + {{.User.Username}} +

+
+
+ +
+ {{InputCSRF}} + + +
+

+ Do you want to remove {{.User.Username}} from + the {{PrettyCircle}}? Doing so will: +

+ +
    +
  • + Unset their inner circle flag, removing them from all inner circle features. +
  • +
  • + Set any photo they had for "inner circle only" to be "public" instead. +
  • +
  • + Clean up any notification they once received about being invited to the inner circle. +
  • +
+
+ +
+ + +
+
+ +
+
+ +
+
+
+ +
+ + +{{end}} \ No newline at end of file diff --git a/web/templates/account/search.html b/web/templates/account/search.html index 962477a..bacf132 100644 --- a/web/templates/account/search.html +++ b/web/templates/account/search.html @@ -54,10 +54,13 @@
- +
@@ -189,6 +192,9 @@ {{else}} {{.NameOrUsername}} {{end}} + {{if .InnerCircle}} + + {{end}} {{if eq .Visibility "private"}} diff --git a/web/templates/base.html b/web/templates/base.html index d3747ec..029cfc2 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -167,6 +167,12 @@ Private Photos + {{if .CurrentUser.IsInnerCircle}} + + + Inner circle + + {{end}} Settings diff --git a/web/templates/forum/add_edit.html b/web/templates/forum/add_edit.html index dc9ac4c..a3bc0cc 100644 --- a/web/templates/forum/add_edit.html +++ b/web/templates/forum/add_edit.html @@ -127,6 +127,19 @@ Check this box if the forum allows photos to be uploaded (not implemented)

{{end}} + + {{if .CurrentUser.IsAdmin}} + +

+ This forum is only available to inner circle members. +

+ {{end}}
diff --git a/web/templates/forum/admin.html b/web/templates/forum/admin.html index 13b248a..e93b7c2 100644 --- a/web/templates/forum/admin.html +++ b/web/templates/forum/admin.html @@ -76,6 +76,13 @@
+ {{if .InnerCircle}} +
+ + InnerCircle +
+ {{end}} + {{if .Explicit}}
diff --git a/web/templates/forum/index.html b/web/templates/forum/index.html index f1c3b1b..86ae1a0 100644 --- a/web/templates/forum/index.html +++ b/web/templates/forum/index.html @@ -64,6 +64,7 @@

+ {{if .InnerCircle}}{{end}} {{.Title}}

diff --git a/web/templates/photo/gallery.html b/web/templates/photo/gallery.html index 106bd96..8c81fd1 100644 --- a/web/templates/photo/gallery.html +++ b/web/templates/photo/gallery.html @@ -40,6 +40,15 @@ Friends + {{else if eq .Visibility "circle"}} + + + + + + {{PrettyCircle}} + + {{else}} @@ -251,6 +260,9 @@ @@ -328,6 +340,25 @@
{{end}} + + {{if and (.CurrentUser.IsInnerCircle) (not .User.InnerCircle) (ne .CurrentUser.Username .User.Username)}} +
+ + Does {{.User.Username}} show a lot of nudity? Consider + inviting them to join the {{PrettyCircle}}. +
+ {{else if (and .CurrentUser.IsInnerCircle .User.IsInnerCircle)}} +
+ + {{.User.Username}} is a part of the {{PrettyCircle}}. + {{if .CurrentUser.IsAdmin}} + + Remove from circle? + + {{end}} +
+ {{end}} + {{template "pager" .}} @@ -356,6 +387,8 @@ {{else if eq .Visibility "private"}} + {{else if eq .Visibility "circle"}} + {{else}} {{end}} @@ -463,6 +496,8 @@ {{else if eq .Visibility "private"}} + {{else if eq .Visibility "circle"}} + {{else}} {{end}} diff --git a/web/templates/photo/upload.html b/web/templates/photo/upload.html index c340580..60e3e8f 100644 --- a/web/templates/photo/upload.html +++ b/web/templates/photo/upload.html @@ -215,7 +215,7 @@ value="public" {{if or (not .EditPhoto) (eq .EditPhoto.Visibility "public")}}checked{{end}}> - Public + Public (members only) @@ -225,6 +225,27 @@ Gallery if that option is enabled, below.

+ {{if .CurrentUser.IsInnerCircle}} +
+ +

+ Only members of the inner circle will see this photo. This is + like the "Public" visibility except only people in the inner circle will see it on your + profile page or on the Site Gallery (if that option is enabled, below). +

+
+ {{end}}