The inner circle
This commit is contained in:
parent
17d9760b61
commit
9788ea6a33
|
@ -90,6 +90,7 @@ var (
|
||||||
// Default forum categories for forum landing page.
|
// Default forum categories for forum landing page.
|
||||||
ForumCategories = []string{
|
ForumCategories = []string{
|
||||||
"Rules and Announcements",
|
"Rules and Announcements",
|
||||||
|
"The Inner Circle",
|
||||||
"Nudists",
|
"Nudists",
|
||||||
"Exhibitionists",
|
"Exhibitionists",
|
||||||
"Photo Boards",
|
"Photo Boards",
|
||||||
|
|
137
pkg/controller/account/inner_circle.go
Normal file
137
pkg/controller/account/inner_circle.go
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -77,7 +77,8 @@ func Search() http.HandlerFunc {
|
||||||
Gender: gender,
|
Gender: gender,
|
||||||
Orientation: orientation,
|
Orientation: orientation,
|
||||||
MaritalStatus: maritalStatus,
|
MaritalStatus: maritalStatus,
|
||||||
Certified: isCertified == "true",
|
Certified: isCertified != "false",
|
||||||
|
InnerCircle: isCertified == "circle",
|
||||||
AgeMin: ageMin,
|
AgeMin: ageMin,
|
||||||
AgeMax: ageMax,
|
AgeMax: ageMax,
|
||||||
}, pager)
|
}, pager)
|
||||||
|
|
|
@ -63,6 +63,7 @@ func AddEdit() http.HandlerFunc {
|
||||||
isExplicit = r.PostFormValue("explicit") == "true"
|
isExplicit = r.PostFormValue("explicit") == "true"
|
||||||
isPrivileged = r.PostFormValue("privileged") == "true"
|
isPrivileged = r.PostFormValue("privileged") == "true"
|
||||||
isPermitPhotos = r.PostFormValue("permit_photos") == "true"
|
isPermitPhotos = r.PostFormValue("permit_photos") == "true"
|
||||||
|
isInnerCircle = r.PostFormValue("inner_circle") == "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sanity check admin-only settings.
|
// Sanity check admin-only settings.
|
||||||
|
@ -79,6 +80,7 @@ func AddEdit() http.HandlerFunc {
|
||||||
forum.Explicit = isExplicit
|
forum.Explicit = isExplicit
|
||||||
forum.Privileged = isPrivileged
|
forum.Privileged = isPrivileged
|
||||||
forum.PermitPhotos = isPermitPhotos
|
forum.PermitPhotos = isPermitPhotos
|
||||||
|
forum.InnerCircle = isInnerCircle
|
||||||
|
|
||||||
// Save it.
|
// Save it.
|
||||||
if err := forum.Save(); err == nil {
|
if err := forum.Save(); err == nil {
|
||||||
|
@ -111,6 +113,7 @@ func AddEdit() http.HandlerFunc {
|
||||||
Explicit: isExplicit,
|
Explicit: isExplicit,
|
||||||
Privileged: isPrivileged,
|
Privileged: isPrivileged,
|
||||||
PermitPhotos: isPermitPhotos,
|
PermitPhotos: isPermitPhotos,
|
||||||
|
InnerCircle: isInnerCircle,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.CreateForum(forum); err == nil {
|
if err := models.CreateForum(forum); err == nil {
|
||||||
|
|
|
@ -41,6 +41,12 @@ func Forum() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is it an inner circle forum?
|
||||||
|
if forum.InnerCircle && !currentUser.IsInnerCircle() {
|
||||||
|
templates.NotFoundPage(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get the pinned threads.
|
// Get the pinned threads.
|
||||||
pinned, err := models.PinnedThreads(forum)
|
pinned, err := models.PinnedThreads(forum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -54,6 +54,12 @@ func Thread() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is it an inner circle forum?
|
||||||
|
if forum.InnerCircle && !currentUser.IsInnerCircle() {
|
||||||
|
templates.NotFoundPage(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Ping the view count on this thread.
|
// Ping the view count on this thread.
|
||||||
if err := thread.View(currentUser.ID); err != nil {
|
if err := thread.View(currentUser.ID); err != nil {
|
||||||
log.Error("Couldn't ping view count on thread %d: %s", thread.ID, err)
|
log.Error("Couldn't ping view count on thread %d: %s", thread.ID, err)
|
||||||
|
|
|
@ -156,6 +156,9 @@ func notifyFriendsNewPhoto(photo *models.Photo, currentUser *models.User) {
|
||||||
// Private grantees
|
// Private grantees
|
||||||
friendIDs = models.PrivateGranteeUserIDs(currentUser.ID)
|
friendIDs = models.PrivateGranteeUserIDs(currentUser.ID)
|
||||||
log.Info("Notify %d private grantees about the new photo by %s", len(friendIDs), currentUser.Username)
|
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 {
|
} else {
|
||||||
// Get all our friend IDs. If this photo is Explicit, only select
|
// Get all our friend IDs. If this photo is Explicit, only select
|
||||||
// the friends who've opted-in for Explicit photo visibility.
|
// the friends who've opted-in for Explicit photo visibility.
|
||||||
|
|
|
@ -101,6 +101,11 @@ func UserPhotos() http.HandlerFunc {
|
||||||
visibility = append(visibility, models.PhotoFriends)
|
visibility = append(visibility, models.PhotoFriends)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inner circle photos.
|
||||||
|
if currentUser.IsInnerCircle() {
|
||||||
|
visibility = append(visibility, models.PhotoInnerCircle)
|
||||||
|
}
|
||||||
|
|
||||||
// Explicit photo filter?
|
// Explicit photo filter?
|
||||||
explicit := currentUser.Explicit
|
explicit := currentUser.Explicit
|
||||||
if isOwnPhotos {
|
if isOwnPhotos {
|
||||||
|
|
|
@ -20,6 +20,7 @@ type Forum struct {
|
||||||
Explicit bool `gorm:"index"`
|
Explicit bool `gorm:"index"`
|
||||||
Privileged bool
|
Privileged bool
|
||||||
PermitPhotos bool
|
PermitPhotos bool
|
||||||
|
InnerCircle bool
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,11 @@ func PaginateForums(user *User, categories []string, pager *Pagination) ([]*Foru
|
||||||
wheres = append(wheres, "explicit = false")
|
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?
|
// Filters?
|
||||||
if len(wheres) > 0 {
|
if len(wheres) > 0 {
|
||||||
query = query.Where(
|
query = query.Where(
|
||||||
|
@ -172,5 +178,14 @@ func CategorizeForums(fs []*Forum, categories []string) []*CategorizedForum {
|
||||||
result[idx].Forums = append(result[idx].Forums, forum)
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,30 @@ func FriendIDsAreExplicit(userId uint64) []uint64 {
|
||||||
return userIDs
|
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.
|
// CountFriendRequests gets a count of pending requests for the user.
|
||||||
func CountFriendRequests(userID uint64) (int64, error) {
|
func CountFriendRequests(userID uint64) (int64, error) {
|
||||||
var count int64
|
var count int64
|
||||||
|
|
|
@ -40,6 +40,7 @@ const (
|
||||||
NotificationCertApproved = "cert_approved"
|
NotificationCertApproved = "cert_approved"
|
||||||
NotificationPrivatePhoto = "private_photo"
|
NotificationPrivatePhoto = "private_photo"
|
||||||
NotificationNewPhoto = "new_photo"
|
NotificationNewPhoto = "new_photo"
|
||||||
|
NotificationInnerCircle = "inner_circle"
|
||||||
NotificationCustom = "custom" // custom message pushed
|
NotificationCustom = "custom" // custom message pushed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ type Photo struct {
|
||||||
type PhotoVisibility string
|
type PhotoVisibility string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PhotoPublic PhotoVisibility = "public" // on profile page and/or public gallery
|
PhotoPublic PhotoVisibility = "public" // on profile page and/or public gallery
|
||||||
PhotoFriends = "friends" // only friends can see it
|
PhotoFriends = "friends" // only friends can see it
|
||||||
PhotoPrivate = "private" // private
|
PhotoPrivate = "private" // private
|
||||||
|
PhotoInnerCircle = "circle" // inner circle
|
||||||
)
|
)
|
||||||
|
|
||||||
// PhotoVisibility preset settings.
|
// PhotoVisibility preset settings.
|
||||||
|
@ -42,6 +43,14 @@ var (
|
||||||
PhotoPrivate,
|
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.
|
// 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.
|
// Or: "Friends + Gallery" photos can appear to your friends in the Site Gallery.
|
||||||
PhotoVisibilityFriends = []string{
|
PhotoVisibilityFriends = []string{
|
||||||
|
@ -213,6 +222,12 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot
|
||||||
placeholders = []interface{}{}
|
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).
|
// Admins see everything on the site (only an admin user can get an admin view).
|
||||||
adminView = user.IsAdmin && adminView
|
adminView = user.IsAdmin && adminView
|
||||||
|
|
||||||
|
@ -229,7 +244,7 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot
|
||||||
)
|
)
|
||||||
placeholders = append(placeholders,
|
placeholders = append(placeholders,
|
||||||
friendIDs, PhotoVisibilityFriends,
|
friendIDs, PhotoVisibilityFriends,
|
||||||
privateUserIDs, PhotoVisibilityAll,
|
privateUserIDs, photosAll,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// You can see friends' Friend photos but only public for non-friends.
|
// 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,
|
placeholders = append(placeholders,
|
||||||
friendIDs, PhotoVisibilityFriends,
|
friendIDs, PhotoVisibilityFriends,
|
||||||
privateUserIDs, PhotoVisibilityAll,
|
privateUserIDs, photosAll,
|
||||||
friendIDs, PhotoPublic,
|
friendIDs, PhotoPublic,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,8 @@ type User struct {
|
||||||
Name *string
|
Name *string
|
||||||
Birthdate time.Time
|
Birthdate time.Time
|
||||||
Certified bool
|
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"`
|
CreatedAt time.Time `gorm:"index"`
|
||||||
UpdatedAt time.Time `gorm:"index"`
|
UpdatedAt time.Time `gorm:"index"`
|
||||||
LastLoginAt time.Time `gorm:"index"`
|
LastLoginAt time.Time `gorm:"index"`
|
||||||
|
@ -185,6 +186,7 @@ type UserSearch struct {
|
||||||
Orientation string
|
Orientation string
|
||||||
MaritalStatus string
|
MaritalStatus string
|
||||||
Certified bool
|
Certified bool
|
||||||
|
InnerCircle bool
|
||||||
AgeMin int
|
AgeMin int
|
||||||
AgeMax int
|
AgeMax int
|
||||||
}
|
}
|
||||||
|
@ -249,6 +251,11 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er
|
||||||
placeholders = append(placeholders, search.Certified, UserStatusActive)
|
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 {
|
if search.AgeMin > 0 {
|
||||||
date := time.Now().AddDate(-search.AgeMin, 0, 0)
|
date := time.Now().AddDate(-search.AgeMin, 0, 0)
|
||||||
wheres = append(wheres, "birthdate <= ?")
|
wheres = append(wheres, "birthdate <= ?")
|
||||||
|
|
67
pkg/models/user_inner_circle.go
Normal file
67
pkg/models/user_inner_circle.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -62,6 +62,8 @@ func New() http.Handler {
|
||||||
mux.Handle("/comments", middleware.LoginRequired(comment.PostComment()))
|
mux.Handle("/comments", middleware.LoginRequired(comment.PostComment()))
|
||||||
mux.Handle("/comments/subscription", middleware.LoginRequired(comment.Subscription()))
|
mux.Handle("/comments/subscription", middleware.LoginRequired(comment.Subscription()))
|
||||||
mux.Handle("/admin/unimpersonate", middleware.LoginRequired(admin.Unimpersonate()))
|
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.
|
// Certification Required. Pages that only full (verified) members can access.
|
||||||
mux.Handle("/photo/gallery", middleware.CertRequired(photo.SiteGallery()))
|
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("/admin/user-action", middleware.AdminRequired(admin.UserActions()))
|
||||||
mux.Handle("/forum/admin", middleware.AdminRequired(forum.Manage()))
|
mux.Handle("/forum/admin", middleware.AdminRequired(forum.Manage()))
|
||||||
mux.Handle("/forum/admin/edit", middleware.AdminRequired(forum.AddEdit()))
|
mux.Handle("/forum/admin/edit", middleware.AdminRequired(forum.AddEdit()))
|
||||||
|
mux.Handle("/inner-circle/remove", middleware.AdminRequired(account.RemoveCircle()))
|
||||||
|
|
||||||
// JSON API endpoints.
|
// JSON API endpoints.
|
||||||
mux.HandleFunc("/v1/version", api.Version())
|
mux.HandleFunc("/v1/version", api.Version())
|
||||||
|
|
|
@ -39,10 +39,14 @@ func TemplateFuncs(r *http.Request) template.FuncMap {
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
"PrettyTitleShort": func() template.HTML {
|
"PrettyTitleShort": func() template.HTML {
|
||||||
return template.HTML(fmt.Sprintf(
|
return template.HTML(`<strong style="color: #0077FF">n</strong>` +
|
||||||
`<strong style="color: #0077FF">n</strong>` +
|
`<strong style="color: #FF77FF">s</strong>`,
|
||||||
`<strong style="color: #FF77FF">s</strong>`,
|
)
|
||||||
))
|
},
|
||||||
|
"PrettyCircle": func() template.HTML {
|
||||||
|
return template.HTML(
|
||||||
|
`<span style="color: #0077ff">I</span><span style="color: #1c77ff">n</span><span style="color: #3877ff">n</span><span style="color: #5477ff">e</span><span style="color: #7077ff">r</span><span style="color: #8c77ff"> </span><span style="color: #aa77ff">c</span><span style="color: #b877ff">i</span><span style="color: #c677ff">r</span><span style="color: #d477ff">c</span><span style="color: #e277ff">l</span><span style="color: #f077ff">e</span>`,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
"Pluralize": Pluralize[int],
|
"Pluralize": Pluralize[int],
|
||||||
"Pluralize64": Pluralize[int64],
|
"Pluralize64": Pluralize[int64],
|
||||||
|
|
|
@ -55,6 +55,10 @@ abbr {
|
||||||
background-image: linear-gradient(141deg, #b329b1 0, #9948c7 71%, #7156d2 100%);
|
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 */
|
/* Mobile: notification badge near the hamburger menu */
|
||||||
.nonshy-mobile-notification {
|
.nonshy-mobile-notification {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
BIN
web/static/img/circle-10.png
Normal file
BIN
web/static/img/circle-10.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 733 B |
BIN
web/static/img/circle-16.png
Normal file
BIN
web/static/img/circle-16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 859 B |
BIN
web/static/img/circle-24.png
Normal file
BIN
web/static/img/circle-24.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
web/static/img/circle-32.png
Normal file
BIN
web/static/img/circle-32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -326,6 +326,8 @@
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
{{if and $Body.Photo (eq $Body.Photo.Visibility "private")}}
|
{{if and $Body.Photo (eq $Body.Photo.Visibility "private")}}
|
||||||
<i class="fa fa-eye has-text-private"></i>
|
<i class="fa fa-eye has-text-private"></i>
|
||||||
|
{{else if and $Body.Photo (eq $Body.Photo.Visibility "circle")}}
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="fa fa-image has-text-link"></i>
|
<i class="fa fa-image has-text-link"></i>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -349,6 +351,15 @@
|
||||||
<span>
|
<span>
|
||||||
Your <strong>certification photo</strong> was rejected!
|
Your <strong>certification photo</strong> was rejected!
|
||||||
</span>
|
</span>
|
||||||
|
{{else if eq .Type "inner_circle"}}
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
<span>
|
||||||
|
You have been added to the {{PrettyCircle}} of nonshy.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="block content mt-2">
|
||||||
|
<a href="/inner-circle">Click to learn more</a> about the inner circle.
|
||||||
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{.AboutUser.Username}} {{.Type}} {{.TableName}} {{.TableID}}
|
{{.AboutUser.Username}} {{.Type}} {{.TableName}} {{.TableID}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
118
web/templates/account/inner_circle.html
Normal file
118
web/templates/account/inner_circle.html
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
{{define "title"}}The inner circle{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="block">
|
||||||
|
<section class="hero is-inner-circle is-bold">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
<img src="/static/img/circle-24.png" class="mr-1">
|
||||||
|
The inner circle
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block p-4">
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Congratulations! You have been added to the <strong>{{PrettyCircle}}</strong> because you
|
||||||
|
exemplify what it truly means to be a {{PrettyTitle}} nudist.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>What is the inner circle?</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The inner circle is for {{PrettyTitle}} members who <em>actually</em> share a lot of nude pictures,
|
||||||
|
<strong>with face</strong>, 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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>What can I do for being in the inner circle?</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
As a part of the inner circle, you have access to the following new features:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
When
|
||||||
|
<a href="/photo/upload"><strong><i class="fa fa-upload mr-1"></i> Uploading a photo</strong></a> you
|
||||||
|
have a new Visibility option for "<strong>{{PrettyCircle}}</strong> <img src="/static/img/circle-16.png">"
|
||||||
|
so that only members of the inner circle can see those pictures.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
On the
|
||||||
|
<a href="/photo/gallery"><strong><i class="fa fa-image mr-1"></i> Site Gallery</strong></a>
|
||||||
|
you can filter for <a href="/photo/gallery?visibility=circle">Inner Circle-only photos</a> shared
|
||||||
|
by other members of the circle.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
On the
|
||||||
|
<a href="/members"><strong><i class="fa fa-people-group mr-1"></i> Member Directory</strong></a>
|
||||||
|
you can see who else is <a href="/members?certified=circle">in the inner circle.</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
On the
|
||||||
|
<a href="/members"><strong><i class="fa fa-comments mr-1"></i> Forums</strong></a>
|
||||||
|
you can access exclusive inner circle-only boards.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
On your <a href="/u/{{.CurrentUser.Username}}">profile page</a> you get an "Inner circle" badge near your
|
||||||
|
Certified status. This badge is <strong>only</strong> visible to members of the inner circle.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
You may <strong>invite</strong> other members to join the inner circle.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>How do I invite others to join the inner circle?</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When you are viewing a <strong>member's photo gallery</strong> page, look for the prompt at the top of the
|
||||||
|
page.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If a member posts several nude pics <strong>including face</strong> 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 <em>actually</em> share nudes <em>with face</em>
|
||||||
|
to join the inner circle.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Please keep the existence of the inner circle a secret</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Still continue to share at least <em>some</em> nudes on "public"</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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 <em>some</em> photos on
|
||||||
|
"Public" as you were doing previously. This is for a couple of reasons:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Members who are <em>not</em> in the circle won't see your circle-only photos. If for example you
|
||||||
|
placed <em>all</em> of your nudes on circle-only you would appear to look the same as someone who
|
||||||
|
uploaded no nudes at all.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The "<a href="/faq#shy-faqs">Shy Account</a>" 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.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{end}}
|
104
web/templates/account/invite_circle.html
Normal file
104
web/templates/account/invite_circle.html
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
{{define "title"}}Invite to the inner circle{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="container">
|
||||||
|
<section class="hero is-inner-circle is-bold">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
<img src="/static/img/circle-24.png">
|
||||||
|
Invite to the inner circle
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="block p-4">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-half">
|
||||||
|
|
||||||
|
<div class="card" style="width: 100%; max-width: 640px">
|
||||||
|
<header class="card-header has-background-link">
|
||||||
|
<p class="card-header-title has-text-light">
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
Invite to the inner circle
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<div class="card-content">
|
||||||
|
|
||||||
|
<div class="media block">
|
||||||
|
<div class="media-left">
|
||||||
|
{{template "avatar-64x64" .User}}
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
|
<p class="title is-4">{{.User.NameOrUsername}}</p>
|
||||||
|
<p class="subtitle is-6">
|
||||||
|
<span class="icon"><i class="fa fa-user"></i></span>
|
||||||
|
<a href="/u/{{.User.Username}}" target="_blank">{{.User.Username}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="/inner-circle/invite" method="POST">
|
||||||
|
{{InputCSRF}}
|
||||||
|
<input type="hidden" name="to" value="{{.User.Username}}">
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Do you want to invite <strong>{{.User.Username}}</strong> to join the
|
||||||
|
{{PrettyCircle}}? Please review the following notes:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The inner circle is designed for {{PrettyTitle}} members who <em>actually</em> post
|
||||||
|
several nude photos <strong>with face</strong> 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 <strong>do not</strong>
|
||||||
|
invite them to join the inner circle.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
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.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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 <em>you</em> who invited them to join the circle.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field has-text-centered">
|
||||||
|
<button type="submit" name="intent" value="confirm" class="button is-success">
|
||||||
|
Add to the inner circle
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="intent" value="cancel" class="button is-warning ml-1">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
let $file = document.querySelector("#file"),
|
||||||
|
$fileName = document.querySelector("#fileName");
|
||||||
|
|
||||||
|
$file.addEventListener("change", function() {
|
||||||
|
let file = this.files[0];
|
||||||
|
$fileName.innerHTML = file.name;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
|
@ -104,6 +104,17 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if and .CurrentUser.IsInnerCircle .User.IsInnerCircle}}
|
||||||
|
<div class="pt-1">
|
||||||
|
<div class="icon-text has-text-danger">
|
||||||
|
<span class="icon">
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
|
</span>
|
||||||
|
<strong>{{PrettyCircle}}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .User.IsAdmin}}
|
{{if .User.IsAdmin}}
|
||||||
<div class="pt-1">
|
<div class="pt-1">
|
||||||
<div class="icon-text has-text-danger">
|
<div class="icon-text has-text-danger">
|
||||||
|
|
94
web/templates/account/remove_circle.html
Normal file
94
web/templates/account/remove_circle.html
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
{{define "title"}}Remove from the inner circle{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="container">
|
||||||
|
<section class="hero is-inner-circle is-bold">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
<img src="/static/img/circle-24.png">
|
||||||
|
Remove from the inner circle
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="block p-4">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-half">
|
||||||
|
|
||||||
|
<div class="card" style="width: 100%; max-width: 640px">
|
||||||
|
<header class="card-header has-background-warning">
|
||||||
|
<p class="card-header-title has-text-black">
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
Remove from the inner circle
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<div class="card-content">
|
||||||
|
|
||||||
|
<div class="media block">
|
||||||
|
<div class="media-left">
|
||||||
|
{{template "avatar-64x64" .User}}
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
|
<p class="title is-4">{{.User.NameOrUsername}}</p>
|
||||||
|
<p class="subtitle is-6">
|
||||||
|
<span class="icon"><i class="fa fa-user"></i></span>
|
||||||
|
<a href="/u/{{.User.Username}}" target="_blank">{{.User.Username}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="/inner-circle/remove" method="POST">
|
||||||
|
{{InputCSRF}}
|
||||||
|
<input type="hidden" name="to" value="{{.User.Username}}">
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Do you want to <strong class="has-text-danger">remove</strong> {{.User.Username}} from
|
||||||
|
the {{PrettyCircle}}? Doing so will:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Unset their inner circle flag, removing them from all inner circle features.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Set any photo they had for "inner circle only" to be "public" instead.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Clean up any notification they once received about being invited to the inner circle.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field has-text-centered">
|
||||||
|
<button type="submit" name="intent" value="confirm" class="button is-danger">
|
||||||
|
Remove from the inner circle
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="intent" value="cancel" class="button is-warning ml-1">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
let $file = document.querySelector("#file"),
|
||||||
|
$fileName = document.querySelector("#fileName");
|
||||||
|
|
||||||
|
$file.addEventListener("change", function() {
|
||||||
|
let file = this.files[0];
|
||||||
|
$fileName.innerHTML = file.name;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
|
@ -54,10 +54,13 @@
|
||||||
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Certified:</label>
|
<label class="label">Status:</label>
|
||||||
<div class="select is-fullwidth">
|
<div class="select is-fullwidth">
|
||||||
<select id="certified" name="certified">
|
<select id="certified" name="certified">
|
||||||
<option value="true">Only certified users</option>
|
<option value="true">Only certified users</option>
|
||||||
|
{{if .CurrentUser.IsInnerCircle}}
|
||||||
|
<option value="circle"{{if eq $Root.Certified "circle"}} selected{{end}}>Inner circle only</option>
|
||||||
|
{{end}}
|
||||||
<option value="false"{{if eq $Root.Certified "false"}} selected{{end}}>Show all users</option>
|
<option value="false"{{if eq $Root.Certified "false"}} selected{{end}}>Show all users</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -189,6 +192,9 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
{{.NameOrUsername}}
|
{{.NameOrUsername}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if .InnerCircle}}
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
|
{{end}}
|
||||||
</a>
|
</a>
|
||||||
{{if eq .Visibility "private"}}
|
{{if eq .Visibility "private"}}
|
||||||
<sup class="fa fa-mask is-size-7" title="Private Profile"></sup>
|
<sup class="fa fa-mask is-size-7" title="Private Profile"></sup>
|
||||||
|
|
|
@ -167,6 +167,12 @@
|
||||||
<span class="icon"><i class="fa fa-eye"></i></span>
|
<span class="icon"><i class="fa fa-eye"></i></span>
|
||||||
<span>Private Photos</span>
|
<span>Private Photos</span>
|
||||||
</a>
|
</a>
|
||||||
|
{{if .CurrentUser.IsInnerCircle}}
|
||||||
|
<a class="navbar-item" href="/inner-circle">
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
<span>Inner circle</span>
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
<a class="navbar-item" href="/settings">
|
<a class="navbar-item" href="/settings">
|
||||||
<span class="icon"><i class="fa fa-gear"></i></span>
|
<span class="icon"><i class="fa fa-gear"></i></span>
|
||||||
<span>Settings</span>
|
<span>Settings</span>
|
||||||
|
|
|
@ -127,6 +127,19 @@
|
||||||
Check this box if the forum allows photos to be uploaded (not implemented)
|
Check this box if the forum allows photos to be uploaded (not implemented)
|
||||||
</p>
|
</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .CurrentUser.IsAdmin}}
|
||||||
|
<label class="checkbox mt-3">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="inner_circle"
|
||||||
|
value="true"
|
||||||
|
{{if and .EditForum .EditForum.InnerCircle}}checked{{end}}>
|
||||||
|
Inner circle <img src="/static/img/circle-16.png" class="ml-1">
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
This forum is only available to inner circle members.
|
||||||
|
</p>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -76,6 +76,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{{if .InnerCircle}}
|
||||||
|
<div class="tag is-info is-light">
|
||||||
|
<span class="icon"><img src="/static/img/circle-10.png" width="9" height="9"></span>
|
||||||
|
InnerCircle
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .Explicit}}
|
{{if .Explicit}}
|
||||||
<div class="tag is-danger is-light">
|
<div class="tag is-danger is-light">
|
||||||
<span class="icon"><i class="fa fa-fire"></i></span>
|
<span class="icon"><i class="fa fa-fire"></i></span>
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
<div class="column is-3 pt-0 pb-1">
|
<div class="column is-3 pt-0 pb-1">
|
||||||
|
|
||||||
<h2 class="is-size-4">
|
<h2 class="is-size-4">
|
||||||
|
{{if .InnerCircle}}<img src="/static/img/circle-24.png" width="20" height="20" class="mr-1">{{end}}
|
||||||
<strong><a href="/f/{{.Fragment}}">{{.Title}}</a></strong>
|
<strong><a href="/f/{{.Fragment}}">{{.Title}}</a></strong>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,15 @@
|
||||||
Friends
|
Friends
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
{{else if eq .Visibility "circle"}}
|
||||||
|
<span class="tag is-info is-light">
|
||||||
|
<span class="icon">
|
||||||
|
<img src="/static/img/circle-10.png">
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{PrettyCircle}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="tag is-private is-light">
|
<span class="tag is-private is-light">
|
||||||
<span class="icon"><i class="fa fa-lock"></i></span>
|
<span class="icon"><i class="fa fa-lock"></i></span>
|
||||||
|
@ -251,6 +260,9 @@
|
||||||
<select id="visibility" name="visibility">
|
<select id="visibility" name="visibility">
|
||||||
<option value="">All photos</option>
|
<option value="">All photos</option>
|
||||||
<option value="public"{{if eq .FilterVisibility "public"}} selected{{end}}>Public only</option>
|
<option value="public"{{if eq .FilterVisibility "public"}} selected{{end}}>Public only</option>
|
||||||
|
{{if .CurrentUser.IsInnerCircle}}
|
||||||
|
<option value="circle"{{if eq .FilterVisibility "circle"}} selected{{end}}>Inner circle only</option>
|
||||||
|
{{end}}
|
||||||
<option value="friends"{{if eq .FilterVisibility "friends"}} selected{{end}}>Friends only</option>
|
<option value="friends"{{if eq .FilterVisibility "friends"}} selected{{end}}>Friends only</option>
|
||||||
<option value="private"{{if eq .FilterVisibility "private"}} selected{{end}}>Private only</option>
|
<option value="private"{{if eq .FilterVisibility "private"}} selected{{end}}>Private only</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -328,6 +340,25 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
<!-- Inner circle invitation -->
|
||||||
|
{{if and (.CurrentUser.IsInnerCircle) (not .User.InnerCircle) (ne .CurrentUser.Username .User.Username)}}
|
||||||
|
<div class="block mt-0">
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
Does <strong>{{.User.Username}}</strong> show a lot of nudity? Consider
|
||||||
|
<a href="/inner-circle/invite?to={{.User.Username}}">inviting them to join the {{PrettyCircle}}</a>.
|
||||||
|
</div>
|
||||||
|
{{else if (and .CurrentUser.IsInnerCircle .User.IsInnerCircle)}}
|
||||||
|
<div class="block mt-0">
|
||||||
|
<span class="icon"><img src="/static/img/circle-16.png"></span>
|
||||||
|
<strong>{{.User.Username}}</strong> is a part of the {{PrettyCircle}}.
|
||||||
|
{{if .CurrentUser.IsAdmin}}
|
||||||
|
<a href="/inner-circle/remove?to={{.User.Username}}" class="has-text-danger ml-2">
|
||||||
|
<i class="fa fa-gavel"></i> Remove from circle?
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{template "pager" .}}
|
{{template "pager" .}}
|
||||||
|
|
||||||
<!-- "Full" view style? (blog style) -->
|
<!-- "Full" view style? (blog style) -->
|
||||||
|
@ -356,6 +387,8 @@
|
||||||
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
||||||
{{else if eq .Visibility "private"}}
|
{{else if eq .Visibility "private"}}
|
||||||
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
||||||
|
{{else if eq .Visibility "circle"}}
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -463,6 +496,8 @@
|
||||||
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
||||||
{{else if eq .Visibility "private"}}
|
{{else if eq .Visibility "private"}}
|
||||||
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
||||||
|
{{else if eq .Visibility "circle"}}
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -215,7 +215,7 @@
|
||||||
value="public"
|
value="public"
|
||||||
{{if or (not .EditPhoto) (eq .EditPhoto.Visibility "public")}}checked{{end}}>
|
{{if or (not .EditPhoto) (eq .EditPhoto.Visibility "public")}}checked{{end}}>
|
||||||
<strong class="has-text-link ml-1">
|
<strong class="has-text-link ml-1">
|
||||||
<span>Public</span>
|
<span>Public <small>(members only)</small></span>
|
||||||
<span class="icon"><i class="fa fa-eye"></i></span>
|
<span class="icon"><i class="fa fa-eye"></i></span>
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
|
@ -225,6 +225,27 @@
|
||||||
Gallery if that option is enabled, below.
|
Gallery if that option is enabled, below.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{{if .CurrentUser.IsInnerCircle}}
|
||||||
|
<div>
|
||||||
|
<label class="radio">
|
||||||
|
<input type="radio"
|
||||||
|
name="visibility"
|
||||||
|
value="circle"
|
||||||
|
{{if eq .EditPhoto.Visibility "circle"}}checked{{end}}>
|
||||||
|
<strong class="has-text-link ml-1">
|
||||||
|
<span>{{PrettyCircle}}</span>
|
||||||
|
<span class="icon">
|
||||||
|
<img src="/static/img/circle-16.png">
|
||||||
|
</span>
|
||||||
|
</strong>
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
Only members of the <a href="/inner-circle">inner circle</a> 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).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div>
|
<div>
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio"
|
<input type="radio"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user