Likes on Comments, and other minor improvements
* Add "Like" buttons to comments and forum posts. * Make "private" profiles more private (logged-in users see only their profile pic, display name, and can friend request or message, if they are not approved friends of the private user) * Add "logged-out view" visibility setting to profiles: to share a link to your page on other sites. Opt-in setting - default is login required to view your public profile page. * CSRF cookie fix. * Updated FAQ & Privacy pages.
This commit is contained in:
parent
d2700490cc
commit
8419958b25
|
@ -29,7 +29,7 @@ const (
|
||||||
const (
|
const (
|
||||||
BcryptCost = 14
|
BcryptCost = 14
|
||||||
SessionCookieName = "session_id"
|
SessionCookieName = "session_id"
|
||||||
CSRFCookieName = "csrf_token"
|
CSRFCookieName = "xsrf_token"
|
||||||
CSRFInputName = "_csrf" // html input name
|
CSRFInputName = "_csrf" // html input name
|
||||||
SessionCookieMaxAge = 60 * 60 * 24 * 30
|
SessionCookieMaxAge = 60 * 60 * 24 * 30
|
||||||
SessionRedisKeyFormat = "session/%s"
|
SessionRedisKeyFormat = "session/%s"
|
||||||
|
|
|
@ -30,12 +30,26 @@ func Profile() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current user (if logged in).
|
// Forcing an external view? (preview of logged-out profile view for visibility=external accounts)
|
||||||
|
if r.FormValue("view") == "external" {
|
||||||
|
vars := map[string]interface{}{
|
||||||
|
"User": user,
|
||||||
|
"IsPrivate": true,
|
||||||
|
"IsExternalView": true,
|
||||||
|
}
|
||||||
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current user (if logged in). If not, check for external view.
|
||||||
currentUser, err := session.CurrentUser(r)
|
currentUser, err := session.CurrentUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The viewer is not logged in, bail now with the basic profile page. If this
|
// The viewer is not logged in, bail now with the basic profile page. If this
|
||||||
// user is private, redirect to login.
|
// user doesn't allow external viewers, redirect to login page.
|
||||||
if user.Visibility == models.UserVisibilityPrivate {
|
if user.Visibility != models.UserVisibilityExternal {
|
||||||
session.FlashError(w, r, "You must be signed in to view this page.")
|
session.FlashError(w, r, "You must be signed in to view this page.")
|
||||||
templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String()))
|
templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String()))
|
||||||
return
|
return
|
||||||
|
|
|
@ -93,14 +93,16 @@ func Settings() http.HandlerFunc {
|
||||||
case "preferences":
|
case "preferences":
|
||||||
var (
|
var (
|
||||||
explicit = r.PostFormValue("explicit") == "true"
|
explicit = r.PostFormValue("explicit") == "true"
|
||||||
private = r.PostFormValue("private") == "true"
|
visibility = models.UserVisibility(r.PostFormValue("visibility"))
|
||||||
)
|
)
|
||||||
|
|
||||||
user.Explicit = explicit
|
user.Explicit = explicit
|
||||||
if private {
|
|
||||||
user.Visibility = models.UserVisibilityPrivate
|
|
||||||
} else {
|
|
||||||
user.Visibility = models.UserVisibilityPublic
|
user.Visibility = models.UserVisibilityPublic
|
||||||
|
|
||||||
|
for _, cmp := range models.UserVisibilityOptions {
|
||||||
|
if visibility == cmp {
|
||||||
|
user.Visibility = visibility
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user.Save(); err != nil {
|
if err := user.Save(); err != nil {
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Envelope is the standard JSON response envelope.
|
// Envelope is the standard JSON response envelope.
|
||||||
|
@ -27,8 +25,6 @@ func ParseJSON(r *http.Request, v interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Error("body: %+v", body)
|
|
||||||
|
|
||||||
// Parse params from JSON.
|
// Parse params from JSON.
|
||||||
if err := json.Unmarshal(body, v); err != nil {
|
if err := json.Unmarshal(body, v); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -16,6 +16,7 @@ func Likes() http.HandlerFunc {
|
||||||
TableName string `json:"name"`
|
TableName string `json:"name"`
|
||||||
TableID uint64 `json:"id"`
|
TableID uint64 `json:"id"`
|
||||||
Unlike bool `json:"unlike,omitempty"`
|
Unlike bool `json:"unlike,omitempty"`
|
||||||
|
Referrer string `json:"page"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response JSON schema.
|
// Response JSON schema.
|
||||||
|
@ -51,8 +52,18 @@ func Likes() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sanity check things. The page= param (Referrer) must be a relative URL, the path
|
||||||
|
// is useful for "liked your comment" notifications to supply the Link URL for the
|
||||||
|
// notification.
|
||||||
|
if len(req.Referrer) > 0 && req.Referrer[0] != '/' {
|
||||||
|
req.Referrer = ""
|
||||||
|
}
|
||||||
|
|
||||||
// Who do we notify about this like?
|
// Who do we notify about this like?
|
||||||
var targetUser *models.User
|
var (
|
||||||
|
targetUser *models.User
|
||||||
|
notificationMessage string
|
||||||
|
)
|
||||||
switch req.TableName {
|
switch req.TableName {
|
||||||
case "photos":
|
case "photos":
|
||||||
if photo, err := models.GetPhoto(req.TableID); err == nil {
|
if photo, err := models.GetPhoto(req.TableID); err == nil {
|
||||||
|
@ -70,6 +81,15 @@ func Likes() http.HandlerFunc {
|
||||||
} else {
|
} else {
|
||||||
log.Error("For like on users table: didn't find user %d: %s", req.TableID, err)
|
log.Error("For like on users table: didn't find user %d: %s", req.TableID, err)
|
||||||
}
|
}
|
||||||
|
case "comments":
|
||||||
|
log.Error("subject is users, find %d", req.TableID)
|
||||||
|
if comment, err := models.GetComment(req.TableID); err == nil {
|
||||||
|
targetUser = &comment.User
|
||||||
|
notificationMessage = comment.Message
|
||||||
|
log.Warn("found user %s", targetUser.Username)
|
||||||
|
} else {
|
||||||
|
log.Error("For like on users table: didn't find user %d: %s", req.TableID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the table likeable?
|
// Is the table likeable?
|
||||||
|
@ -108,6 +128,8 @@ func Likes() http.HandlerFunc {
|
||||||
Type: models.NotificationLike,
|
Type: models.NotificationLike,
|
||||||
TableName: req.TableName,
|
TableName: req.TableName,
|
||||||
TableID: req.TableID,
|
TableID: req.TableID,
|
||||||
|
Message: notificationMessage,
|
||||||
|
Link: req.Referrer,
|
||||||
}
|
}
|
||||||
if err := models.CreateNotification(notif); err != nil {
|
if err := models.CreateNotification(notif); err != nil {
|
||||||
log.Error("Couldn't create Likes notification: %s", err)
|
log.Error("Couldn't create Likes notification: %s", err)
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
var ThreadPathRegexp = regexp.MustCompile(`^/forum/thread/(\d+)$`)
|
var ThreadPathRegexp = regexp.MustCompile(`^/forum/thread/(\d+)$`)
|
||||||
|
|
||||||
// Thread view for a specific board index.
|
// Thread view for the comment thread body of a forum post.
|
||||||
func Thread() http.HandlerFunc {
|
func Thread() http.HandlerFunc {
|
||||||
tmpl := templates.Must("forum/thread.html")
|
tmpl := templates.Must("forum/thread.html")
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -74,6 +74,13 @@ func Thread() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the like map for these comments.
|
||||||
|
commentIDs := []uint64{}
|
||||||
|
for _, com := range comments {
|
||||||
|
commentIDs = append(commentIDs, com.ID)
|
||||||
|
}
|
||||||
|
commentLikeMap := models.MapLikes(currentUser, "comments", commentIDs)
|
||||||
|
|
||||||
// Is the current user subscribed to notifications on this thread?
|
// Is the current user subscribed to notifications on this thread?
|
||||||
_, isSubscribed := models.IsSubscribed(currentUser, "threads", thread.ID)
|
_, isSubscribed := models.IsSubscribed(currentUser, "threads", thread.ID)
|
||||||
|
|
||||||
|
@ -81,6 +88,7 @@ func Thread() http.HandlerFunc {
|
||||||
"Forum": forum,
|
"Forum": forum,
|
||||||
"Thread": thread,
|
"Thread": thread,
|
||||||
"Comments": comments,
|
"Comments": comments,
|
||||||
|
"LikeMap": commentLikeMap,
|
||||||
"Pager": pager,
|
"Pager": pager,
|
||||||
"IsSubscribed": isSubscribed,
|
"IsSubscribed": isSubscribed,
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,13 @@ func View() http.HandlerFunc {
|
||||||
log.Error("Couldn't list comments for photo %d: %s", photo.ID, err)
|
log.Error("Couldn't list comments for photo %d: %s", photo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the like map for these comments.
|
||||||
|
commentIDs := []uint64{}
|
||||||
|
for _, com := range comments {
|
||||||
|
commentIDs = append(commentIDs, com.ID)
|
||||||
|
}
|
||||||
|
commentLikeMap := models.MapLikes(currentUser, "comments", commentIDs)
|
||||||
|
|
||||||
// Is the current user subscribed to notifications on this thread?
|
// Is the current user subscribed to notifications on this thread?
|
||||||
_, isSubscribed := models.IsSubscribed(currentUser, "photos", photo.ID)
|
_, isSubscribed := models.IsSubscribed(currentUser, "photos", photo.ID)
|
||||||
|
|
||||||
|
@ -86,6 +93,7 @@ func View() http.HandlerFunc {
|
||||||
"LikeMap": likeMap,
|
"LikeMap": likeMap,
|
||||||
"CommentMap": commentMap,
|
"CommentMap": commentMap,
|
||||||
"Comments": comments,
|
"Comments": comments,
|
||||||
|
"CommentLikeMap": commentLikeMap,
|
||||||
"IsSubscribed": isSubscribed,
|
"IsSubscribed": isSubscribed,
|
||||||
}
|
}
|
||||||
if err := tmpl.Execute(w, r, vars); err != nil {
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
||||||
|
|
|
@ -58,6 +58,7 @@ func MakeCSRFCookie(r *http.Request, w http.ResponseWriter) string {
|
||||||
Name: config.CSRFCookieName,
|
Name: config.CSRFCookieName,
|
||||||
Value: token,
|
Value: token,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Path: "/",
|
||||||
}
|
}
|
||||||
// log.Debug("MakeCSRFCookie: giving cookie value %s to user", token)
|
// log.Debug("MakeCSRFCookie: giving cookie value %s to user", token)
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
|
|
|
@ -20,6 +20,7 @@ type Like struct {
|
||||||
var LikeableTables = map[string]interface{}{
|
var LikeableTables = map[string]interface{}{
|
||||||
"photos": nil,
|
"photos": nil,
|
||||||
"users": nil,
|
"users": nil,
|
||||||
|
"comments": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLike to something.
|
// AddLike to something.
|
||||||
|
|
|
@ -40,9 +40,17 @@ type UserVisibility string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UserVisibilityPublic UserVisibility = "public"
|
UserVisibilityPublic UserVisibility = "public"
|
||||||
|
UserVisibilityExternal = "external"
|
||||||
UserVisibilityPrivate = "private"
|
UserVisibilityPrivate = "private"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// All visibility options.
|
||||||
|
var UserVisibilityOptions = []UserVisibility{
|
||||||
|
UserVisibilityPublic,
|
||||||
|
UserVisibilityExternal,
|
||||||
|
UserVisibilityPrivate,
|
||||||
|
}
|
||||||
|
|
||||||
// Preload related tables for the user (classmethod).
|
// Preload related tables for the user (classmethod).
|
||||||
func (u *User) Preload() *gorm.DB {
|
func (u *User) Preload() *gorm.DB {
|
||||||
return DB.Preload("ProfileField").Preload("ProfilePhoto")
|
return DB.Preload("ProfileField").Preload("ProfilePhoto")
|
||||||
|
|
|
@ -51,6 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Touching the user drop-down button toggles it.
|
// Touching the user drop-down button toggles it.
|
||||||
|
if (userMenu !== null) {
|
||||||
userMenu.addEventListener("touchstart", (e) => {
|
userMenu.addEventListener("touchstart", (e) => {
|
||||||
// On mobile/tablet screens they had to hamburger menu their way here anyway, let it thru.
|
// On mobile/tablet screens they had to hamburger menu their way here anyway, let it thru.
|
||||||
if (screen.width < 1024) {
|
if (screen.width < 1024) {
|
||||||
|
@ -64,6 +65,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
userMenu.classList.add(activeClass);
|
userMenu.classList.add(activeClass);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Touching a link from the user menu lets it click thru.
|
// Touching a link from the user menu lets it click thru.
|
||||||
(document.querySelectorAll(".navbar-dropdown") || []).forEach(node => {
|
(document.querySelectorAll(".navbar-dropdown") || []).forEach(node => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Bind to the like buttons.
|
// Bind to the like buttons.
|
||||||
(document.querySelectorAll(".nonshy-like-button") || []).forEach(node => {
|
(document.querySelectorAll(".nonshy-like-button") || []).forEach(node => {
|
||||||
node.addEventListener("click", (e) => {
|
node.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
|
|
||||||
let $icon = node.querySelector(".icon"),
|
let $icon = node.querySelector(".icon"),
|
||||||
|
@ -36,6 +37,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
"name": tableName, // TODO
|
"name": tableName, // TODO
|
||||||
"id": parseInt(tableID),
|
"id": parseInt(tableID),
|
||||||
"unlike": !liking,
|
"unlike": !liking,
|
||||||
|
"page": window.location.pathname + window.location.search + window.location.hash,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
|
|
|
@ -201,6 +201,12 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if eq .TableName "users"}}
|
{{else if eq .TableName "users"}}
|
||||||
profile page.
|
profile page.
|
||||||
|
{{else if eq .TableName "comments"}}
|
||||||
|
{{if .Link}}
|
||||||
|
<a href="{{.Link}}">comment</a>:
|
||||||
|
{{else}}
|
||||||
|
comment.
|
||||||
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{.TableName}}.
|
{{.TableName}}.
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{define "title"}}{{.User.Username}}{{end}}
|
{{define "title"}}{{.User.Username}}{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<section class="hero {{if .LoggedIn}}is-info{{else}}is-light is-bold{{end}}">
|
<section class="hero {{if and .LoggedIn (not .IsPrivate)}}is-info{{else}}is-light is-bold{{end}}">
|
||||||
<div class="hero-body">
|
<div class="hero-body">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- CurrentUser can upload a new profile pic -->
|
<!-- CurrentUser can upload a new profile pic -->
|
||||||
{{if and .LoggedIn (eq .CurrentUser.ID .User.ID)}}
|
{{if and .LoggedIn (eq .CurrentUser.ID .User.ID) (not .IsPrivate)}}
|
||||||
<span class="corner">
|
<span class="corner">
|
||||||
<button class="button is-small p-1 is-success">
|
<button class="button is-small p-1 is-success">
|
||||||
<a href="/photo/upload?intent=profile_pic"
|
<a href="/photo/upload?intent=profile_pic"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
({{.User.Status}})
|
({{.User.Status}})
|
||||||
</h2>
|
</h2>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if not .LoggedIn}}
|
{{if or (not .LoggedIn) .IsPrivate}}
|
||||||
<h2 class="subtitle">is on {{PrettyTitle}}, a social network for nudists & exhibitionists.</h2>
|
<h2 class="subtitle">is on {{PrettyTitle}}, a social network for nudists & exhibitionists.</h2>
|
||||||
<p>
|
<p>
|
||||||
{{PrettyTitle}} is a new social network for <strong>real</strong> nudists and exhibionists.
|
{{PrettyTitle}} is a new social network for <strong>real</strong> nudists and exhibionists.
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .LoggedIn}}
|
{{if and .LoggedIn (not .IsPrivate)}}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div>
|
<div>
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
{{end}}<!-- if .LoggedIn -->
|
{{end}}<!-- if .LoggedIn -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .LoggedIn}}
|
{{if and .LoggedIn (not .IsExternalView)}}
|
||||||
<div class="columns is-centered is-gapless">
|
<div class="columns is-centered is-gapless">
|
||||||
<div class="column is-narrow has-text-centered">
|
<div class="column is-narrow has-text-centered">
|
||||||
<form action="/friends/add" method="POST">
|
<form action="/friends/add" method="POST">
|
||||||
|
@ -134,6 +134,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Like button -->
|
<!-- Like button -->
|
||||||
|
{{if not .IsPrivate}}
|
||||||
{{$Like := .LikeMap.Get .User.ID}}
|
{{$Like := .LikeMap.Get .User.ID}}
|
||||||
<div class="column is-narrow has-text-centered">
|
<div class="column is-narrow has-text-centered">
|
||||||
<button type="button" class="button is-fullwidth nonshy-like-button"
|
<button type="button" class="button is-fullwidth nonshy-like-button"
|
||||||
|
@ -148,6 +149,7 @@
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div class="column is-narrow has-text-centered">
|
<div class="column is-narrow has-text-centered">
|
||||||
<form action="/users/block" method="POST">
|
<form action="/users/block" method="POST">
|
||||||
|
@ -183,7 +185,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{if not .LoggedIn}}
|
{{if or (not .LoggedIn) .IsPrivate}}
|
||||||
<div class="py-6"></div>
|
<div class="py-6"></div>
|
||||||
{{else if .IsPrivate}}
|
{{else if .IsPrivate}}
|
||||||
<div class="block p-4">
|
<div class="block p-4">
|
||||||
|
|
|
@ -220,18 +220,46 @@
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Private Profile</label>
|
<label class="label">Profile Visibility</label>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox"
|
<input type="radio"
|
||||||
name="private"
|
name="visibility"
|
||||||
value="true"
|
value="public"
|
||||||
|
{{if eq .CurrentUser.Visibility "public"}}checked{{end}}>
|
||||||
|
Public + Login Required
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
The default is that users must be logged-in to even look at your profile
|
||||||
|
page. If your profile URL (/u/{{.CurrentUser.Username}}) is visited by a
|
||||||
|
logged-out browser, they are prompted to log in.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<label class="checkbox mt-2">
|
||||||
|
<input type="radio"
|
||||||
|
name="visibility"
|
||||||
|
value="external"
|
||||||
|
{{if eq .CurrentUser.Visibility "external"}}checked{{end}}>
|
||||||
|
Public + Limited Logged-out View
|
||||||
|
</label>
|
||||||
|
<p class="help">
|
||||||
|
Your profile is fully visible to logged-in users, but if a logged-out browser
|
||||||
|
visits your page they will see a very minimal view: only your profile picture
|
||||||
|
and display name are shown.
|
||||||
|
<a href="/u/{{.CurrentUser.Username}}?view=external" target="_blank">Preview <i class="fa fa-external-link"></i></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<label class="checkbox mt-2">
|
||||||
|
<input type="radio"
|
||||||
|
name="visibility"
|
||||||
|
value="private"
|
||||||
{{if eq .CurrentUser.Visibility "private"}}checked{{end}}>
|
{{if eq .CurrentUser.Visibility "private"}}checked{{end}}>
|
||||||
Mark my profile page as "private"
|
Mark my profile page as "private"
|
||||||
</label>
|
</label>
|
||||||
<p class="help">
|
<p class="help">
|
||||||
If you check this box then only friends who you have approved are able to
|
If you check this box then only friends who you have approved are able to
|
||||||
see your profile page and gallery. Your gallery photos also will NOT appear
|
see your profile page and gallery. Your gallery photos also will NOT appear
|
||||||
on the Site Gallery page.
|
on the Site Gallery page. If your profile page is visited by a logged-out
|
||||||
|
viewer, they are prompted to log in.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,37 @@
|
||||||
until your profile has been certified.
|
until your profile has been certified.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3>What are the visibility options for my profile page?</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
There are currently three different choices for your profile visibility on your
|
||||||
|
<a href="/settings">Settings</a> page:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The <strong>default</strong> visibility is <strong class="has-text-success-dark">"Public + Login Required."</strong> Users must be
|
||||||
|
logged-in to an account in order to see anything about your profile page - if an
|
||||||
|
external (logged out) browser visits your profile URL, they will be redirected to
|
||||||
|
log in to an account first.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
You may optionally go <em>more</em> public with a <strong class="has-text-warning-dark">"Limited Logged-out View."</strong>
|
||||||
|
This enables your profile URL (e.g.,
|
||||||
|
{{if .LoggedIn}}<a href="/u/{{.CurrentUser.Username}}?view=external">/u/{{.CurrentUser.Username}}</a>{{else}}/u/username{{end}})
|
||||||
|
to show a <em>basic</em> page (with your square profile picture and display name) to
|
||||||
|
logged-out browsers. This may be useful if you wish to link to your page from an external
|
||||||
|
site (e.g. your Twitter page) and present new users with a better experience than just
|
||||||
|
a redirect to login page.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
You may <strong class="has-text-private">"Mark my profile as 'private'"</strong> to
|
||||||
|
be private even from other logged-in members who are not on your Friends
|
||||||
|
list. Logged-in users will see only your square profile picture and display
|
||||||
|
name, and be able only to send you a friend request or a message.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h1>Photo FAQs</h1>
|
<h1>Photo FAQs</h1>
|
||||||
|
|
||||||
<h3>Do I have to post my nudes here?</h3>
|
<h3>Do I have to post my nudes here?</h3>
|
||||||
|
|
|
@ -157,6 +157,21 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-narrow">
|
||||||
|
{{$Like := $Root.LikeMap.Get .ID}}
|
||||||
|
<a href="#" class="has-text-dark nonshy-like-button"
|
||||||
|
data-table-name="comments" data-table-id="{{.ID}}"
|
||||||
|
title="Like">
|
||||||
|
<span class="icon{{if $Like.UserLikes}} has-text-danger{{end}}"><i class="fa fa-heart"></i></span>
|
||||||
|
<span class="nonshy-likes">
|
||||||
|
Like
|
||||||
|
{{if gt $Like.Count 0}}
|
||||||
|
({{$Like.Count}})
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<a href="/contact?intent=report&subject=report.comment&id={{.ID}}" class="has-text-dark">
|
<a href="/contact?intent=report&subject=report.comment&id={{.ID}}" class="has-text-dark">
|
||||||
<span class="icon"><i class="fa fa-flag"></i></span>
|
<span class="icon"><i class="fa fa-flag"></i></span>
|
||||||
|
|
|
@ -21,15 +21,9 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This website was designed by a life-long nudist, exhibitionist and software engineer to create
|
This website was designed by a life-long nudist, exhibitionist and software engineer to create
|
||||||
a safe space for like-minded individuals online, especially in the modern online political
|
a safe space for like-minded individuals online. This website is open to <em>all</em> nudists
|
||||||
climate and after Tumblr, Pornhub and other social networks had begun clamping down and kicking
|
and exhibitionists (18+), but I understand that not all nudists want to see any sexual content, so
|
||||||
off all the nudists from their platforms.
|
this site provides some controls to support both camps:
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This website is open to <em>all</em> nudists and exhibitionists, but I understand that not all
|
|
||||||
nudists want to see any sexual content, so this site provides some controls to support
|
|
||||||
both camps:
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -44,6 +38,11 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You can read more <a href="/about">about this website</a> and check out the <a href="/faq">FAQ</a>
|
||||||
|
page for more information.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h1>Site Rules</h1>
|
<h1>Site Rules</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -241,6 +241,21 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-narrow">
|
||||||
|
{{$Like := $Root.CommentLikeMap.Get .ID}}
|
||||||
|
<a href="#" class="has-text-dark nonshy-like-button"
|
||||||
|
data-table-name="comments" data-table-id="{{.ID}}"
|
||||||
|
title="Like">
|
||||||
|
<span class="icon{{if $Like.UserLikes}} has-text-danger{{end}}"><i class="fa fa-heart"></i></span>
|
||||||
|
<span class="nonshy-likes">
|
||||||
|
Like
|
||||||
|
{{if gt $Like.Count 0}}
|
||||||
|
({{$Like.Count}})
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<a href="/contact?intent=report&subject=report.comment&id={{.ID}}" class="has-text-dark">
|
<a href="/contact?intent=report&subject=report.comment&id={{.ID}}" class="has-text-dark">
|
||||||
<span class="icon"><i class="fa fa-flag"></i></span>
|
<span class="icon"><i class="fa fa-flag"></i></span>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This page was last updated on <strong>August 26, 2022.</strong>
|
This page was last updated on <strong>August 29, 2022.</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -43,6 +43,10 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
By default, your profile page on {{PrettyTitle}} may <strong>only</strong> be seen
|
||||||
|
by logged-in members of the website.
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
You may mark your entire profile as "Private" which limits some of the contact you
|
You may mark your entire profile as "Private" which limits some of the contact you
|
||||||
may receive:
|
may receive:
|
||||||
|
@ -58,6 +62,12 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
Optionally, you may mark your Public profile to allow a limited "logged out" view which
|
||||||
|
shows only your square profile picture and display name. This may be useful to link to
|
||||||
|
your profile from external sites (like Twitter) so the visitor isn't just redirected to a
|
||||||
|
"login required" page.
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Profile photos have visibility settings including Public, Friends-only or Private:
|
Profile photos have visibility settings including Public, Friends-only or Private:
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -77,6 +87,12 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Notice:</strong> the square default profile picture that appears on your page
|
||||||
|
will always be visible to all logged-in users. The full size version on your Gallery
|
||||||
|
page may be restricted to friends or private, but the square cropped version that appears
|
||||||
|
next to your username on many parts of the website is always seen by logged-in users.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Site-Wide Photo Gallery</h3>
|
<h3>Site-Wide Photo Gallery</h3>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user