Noah
93c13882aa
Finish implementing the basic forum features: * Pinned threads (admin or board owner only) * Edit Thread settings when you edit the top-most comment. * NoReply threads remove all the reply buttons. * Explicit forums and threads are filtered out unless opted-in (admins always see them). * Count the unique members who participated in each forum. * Get the most recently updated thread to show on forum list page. * Contact/Report page: handle receiving a comment ID to report on. Implement Likes & Notifications * Like buttons added to Photos and Profile Pages. Implemented via simple vanilla JS (likes.js) to make ajax requests to back-end to like/unlike. * Notifications: for your photo or profile being liked. If you unlike, the existing notifications about the like are revoked. * The notifications appear as an alert number in the nav bar and are read on the User Dashboard. Click to mark a notification as "read" or click the "mark all as read" button. Update DeleteUser to scrub likes, notifications, threads, and comments.
202 lines
4.9 KiB
Go
202 lines
4.9 KiB
Go
package deletion
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.kirsle.net/apps/gosocial/pkg/log"
|
|
"git.kirsle.net/apps/gosocial/pkg/models"
|
|
"git.kirsle.net/apps/gosocial/pkg/photo"
|
|
)
|
|
|
|
// DeleteUser wipes a user and all associated data from the database.
|
|
func DeleteUser(user *models.User) error {
|
|
log.Error("BEGIN DeleteUser(%d, %s)", user.ID, user.Username)
|
|
|
|
// Remove all linked tables and assets.
|
|
type remover struct {
|
|
Step string
|
|
Fn func(uint64) error
|
|
}
|
|
|
|
var todo = []remover{
|
|
{"Notifications", DeleteNotifications},
|
|
{"Likes", DeleteLikes},
|
|
{"Threads", DeleteForumThreads},
|
|
{"Comments", DeleteComments},
|
|
{"Photos", DeleteUserPhotos},
|
|
{"Certification Photo", DeleteCertification},
|
|
{"Messages", DeleteUserMessages},
|
|
{"Friends", DeleteFriends},
|
|
{"Profile Fields", DeleteProfile},
|
|
}
|
|
for _, item := range todo {
|
|
if err := item.Fn(user.ID); err != nil {
|
|
return fmt.Errorf("%s: %s", item.Step, err)
|
|
}
|
|
}
|
|
|
|
// Remove the user itself.
|
|
return user.Delete()
|
|
}
|
|
|
|
// DeleteUserPhotos scrubs data for deleting a user.
|
|
func DeleteUserPhotos(userID uint64) error {
|
|
log.Error("DeleteUser: BEGIN DeleteUserPhotos(%d)", userID)
|
|
|
|
// Deeply scrub all user photos.
|
|
pager := &models.Pagination{
|
|
Page: 1,
|
|
PerPage: 20,
|
|
Sort: "photos.id",
|
|
}
|
|
|
|
for {
|
|
photos, err := models.PaginateUserPhotos(
|
|
userID,
|
|
models.PhotoVisibilityAll,
|
|
true,
|
|
pager,
|
|
)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(photos) == 0 {
|
|
break
|
|
}
|
|
|
|
for _, item := range photos {
|
|
log.Warn("DeleteUserPhotos(%d): remove file %s", userID, item.Filename)
|
|
photo.Delete(item.Filename)
|
|
if item.CroppedFilename != "" {
|
|
log.Warn("DeleteUserPhotos(%d): remove file %s", userID, item.CroppedFilename)
|
|
photo.Delete(item.CroppedFilename)
|
|
}
|
|
if err := item.Delete(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Error("DeleteUser: END DeleteUserPhotos(%d)", userID)
|
|
return nil
|
|
}
|
|
|
|
// DeleteCertification scrubs data for deleting a user.
|
|
func DeleteCertification(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteCertification(%d)", userID)
|
|
if cert, err := models.GetCertificationPhoto(userID); err == nil {
|
|
if cert.Filename != "" {
|
|
log.Warn("DeleteCertification(%d): remove file %s", userID, cert.Filename)
|
|
photo.Delete(cert.Filename)
|
|
}
|
|
return cert.Delete()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteUserMessages scrubs data for deleting a user.
|
|
func DeleteUserMessages(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteUserMessages(%d)", userID)
|
|
result := models.DB.Where(
|
|
"source_user_id = ? OR target_user_id = ?",
|
|
userID, userID,
|
|
).Delete(&models.Message{})
|
|
return result.Error
|
|
}
|
|
|
|
// DeleteFriends scrubs data for deleting a user.
|
|
func DeleteFriends(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteUserFriends(%d)", userID)
|
|
result := models.DB.Where(
|
|
"source_user_id = ? OR target_user_id = ?",
|
|
userID, userID,
|
|
).Delete(&models.Friend{})
|
|
return result.Error
|
|
}
|
|
|
|
// DeleteNotifications scrubs all notifications about a user.
|
|
func DeleteNotifications(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteNotifications(%d)", userID)
|
|
result := models.DB.Where(
|
|
"user_id = ? OR about_user_id = ?",
|
|
userID, userID,
|
|
).Delete(&models.Notification{})
|
|
return result.Error
|
|
}
|
|
|
|
// DeleteLikes scrubs all Likes about a user.
|
|
func DeleteLikes(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteLikes(%d)", userID)
|
|
result := models.DB.Where(
|
|
"user_id = ? OR (table_name='users' AND table_id=?)",
|
|
userID, userID,
|
|
).Delete(&models.Like{})
|
|
return result.Error
|
|
}
|
|
|
|
// DeleteProfile scrubs data for deleting a user.
|
|
func DeleteProfile(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteProfile(%d)", userID)
|
|
result := models.DB.Where(
|
|
"user_id = ?",
|
|
userID,
|
|
).Delete(&models.ProfileField{})
|
|
return result.Error
|
|
}
|
|
|
|
// DeleteForumThreads scrubs all forum threads started by the user.
|
|
func DeleteForumThreads(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteForumThreads(%d)", userID)
|
|
|
|
var threadIDs = []uint64{}
|
|
result := models.DB.Table(
|
|
"threads",
|
|
).Joins(
|
|
"JOIN comments ON (threads.comment_id = comments.id)",
|
|
).Select(
|
|
"distinct(threads.id) as id",
|
|
).Where(
|
|
"comments.user_id = ?",
|
|
userID,
|
|
).Scan(&threadIDs)
|
|
|
|
if result.Error != nil {
|
|
return fmt.Errorf("Couldn't list thread IDs created by user: %s", result.Error)
|
|
}
|
|
|
|
log.Warn("thread IDs to wipe: %+v", threadIDs)
|
|
|
|
// Wipe all these threads and their comments.
|
|
if len(threadIDs) > 0 {
|
|
result = models.DB.Where(
|
|
"table_name = ? AND table_id IN ?",
|
|
"threads", threadIDs,
|
|
).Delete(&models.Comment{})
|
|
if result.Error != nil {
|
|
return fmt.Errorf("Couldn't wipe threads of comments: %s", result.Error)
|
|
}
|
|
|
|
// And finish the threads off too.
|
|
result = models.DB.Where(
|
|
"id IN ?",
|
|
threadIDs,
|
|
).Delete(&models.Thread{})
|
|
return result.Error
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteComments deletes all comments by the user.
|
|
func DeleteComments(userID uint64) error {
|
|
log.Error("DeleteUser: DeleteComments(%d)", userID)
|
|
|
|
result := models.DB.Where(
|
|
"user_id = ?",
|
|
userID,
|
|
).Delete(&models.Comment{})
|
|
return result.Error
|
|
}
|