// CreateComment creates a new comment on a post
func (handler CommentHandler) CreateComment(c *gin.Context) {
	// lookup post by uuid
	postUUID := c.Param("puuid")
	var post table.Post
	if err := handler.db.Where("uuid = ?", postUUID).First(&post).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Question does not exist"})
		return
	}

	// deserialize comment
	var comment table.Comment
	c.Bind(&comment)
	if !comment.IsValidForCreate() {
		c.JSON(http.StatusBadRequest, resp.APIResponse{IsError: true, Message: "Invalid data"})
		return
	}
	comment.PostID = post.ID
	comment.UserID = auth.GetUserIDFromCookie(c)

	// create new comment
	if err := handler.db.Create(&comment).Error; err != nil {
		c.JSON(http.StatusInternalServerError, resp.APIResponse{IsError: true, Message: "Unknown error"})
		return
	}

	// get comment that we just inserted with user info
	handler.db.Preload("User").Find(&comment)

	c.JSON(http.StatusCreated, resp.APIResponse{IsError: false, Value: comment})
}
// GetAllQuestionsCSV serves a csv file report of all the questions in a group
// Columns: Question, Score (upvotes - downvotes), Total votes (upvotes + downvotes), Upvotes, Downvotes, Created by
func (handler ExportHandler) GetAllQuestionsCSV(c *gin.Context) {
	gname := c.Param("gname")
	if !auth.HasAccessToGroup(auth.GetUserIDFromCookie(c), gname, handler.db) {
		c.JSON(http.StatusForbidden, resp.APIResponse{IsError: true, Message: "You don't have permission to access this group"})
		return
	}

	var group table.Group
	if err := handler.db.Where("name = ?", gname).First(&group).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Group does not exist"})
		return
	}

	// get all posts for a group with comments and users for those comments
	var posts []table.Post
	handler.db.First(&group, table.Group{Name: gname})
	handler.db.Model(&group).Order("id").Preload("User").Association("Posts").Find(&posts)
	output := "Question,Score (upvotes - downvotes),Total votes (upvotes + downvotes),Upvotes,Downvotes,Created by" + "\n"
	for _, post := range posts {
		data := []string{post.Name, fmt.Sprintf("%v", int(post.Upvotes)-int(post.Downvotes)), fmt.Sprintf("%v", post.Upvotes+post.Downvotes), fmt.Sprintf("%v", post.Upvotes), fmt.Sprintf("%v", post.Downvotes), fmt.Sprintf("%v", post.User.FullName())}
		for i := range data {
			data[i] = escapeForCSV(data[i])
		}
		output += strings.Join(data[:], ",") + "\n"
	}

	c.Writer.Header().Set("Content-Disposition", "attachment; filename=questions.csv")
	c.Writer.Header().Set("Content-Type", c.Request.Header.Get("Content-Type"))

	c.String(http.StatusOK, output)
}
Exemple #3
0
// CreatePost creates a new question
func (handler PostHandler) CreatePost(c *gin.Context) {
	// lookup group by name
	groupname := c.Param("gname")
	var group table.Group
	if err := handler.db.Where("name = ?", groupname).First(&group).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Group does not exist"})
		return
	}

	// deserialize post
	var post table.Post
	c.Bind(&post)
	if !post.IsValidForCreate() {
		c.JSON(http.StatusBadRequest, resp.APIResponse{IsError: true, Message: "Invalid data"})
		return
	}
	post.GroupID = group.ID
	post.UUID = uuid.NewV4().String() //TODO make sure this doesn't break everything
	post.CreatedBy = auth.GetUserIDFromCookie(c)

	// create new question
	if err := handler.db.Create(&post).Error; err != nil {
		c.JSON(http.StatusConflict, resp.APIResponse{IsError: true, Message: "Question has already been asked"})
		return
	}

	//TODO - this is not ideal...
	// go gives the timestamp more precision than pg stores, so we now get the obj from pg
	handler.db.Where("uuid = ?", post.UUID).First(&post)

	// make sure comments are empty slice and not nil
	post.Comments = make([]table.Comment, 0)

	c.JSON(http.StatusCreated, resp.APIResponse{IsError: false, Value: post})
}
// ShowAdminPage either shows the login page or the admin panel, depending
// on whether the user is logged in and has access to the group
func (gc AdminController) ShowAdminPage(c *gin.Context) {
	gname := c.Param("gname")
	if auth.IsLoggedIn(c) && auth.HasAccessToGroup(auth.GetUserIDFromCookie(c), gname, gc.db) {
		// user has admin access
		http.ServeFile(c.Writer, c.Request, "views/admin_panel.html")
	} else {
		// show login page
		http.ServeFile(c.Writer, c.Request, "views/admin_login.html")
	}
}
Exemple #5
0
// GetUserVotes gets the user's votes for a group
func (handler VoteHandler) GetUserVotes(c *gin.Context) {
	gname := c.Param("gname")
	var group table.Group
	if err := handler.db.Where("name = ?", gname).First(&group).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Group does not exist"})
		return
	}

	userID := auth.GetUserIDFromCookie(c)
	var votes []table.Vote
	if err := handler.db.Joins("left join posts on posts.id = votes.post_id").Where("posts.group_id = ? and votes.user_id = ?", group.ID, userID).Find(&votes).Error; err != nil {
		// return empty slice instead of nil for no data
		votes = make([]table.Vote, 0)
	}

	c.JSON(http.StatusOK, resp.APIResponse{IsError: false, Value: votes})
}
Exemple #6
0
// DeletePost soft deletes a question
func (handler PostHandler) DeletePost(c *gin.Context) {
	// lookup post by uuid
	postUUID := c.Param("puuid")
	var post table.Post
	if err := handler.db.Where("uuid = ?", postUUID).Preload("Group").First(&post).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Question does not exist"})
		return
	}

	// make sure user is an admin
	if !auth.HasAccessToGroup(auth.GetUserIDFromCookie(c), post.Group.Name, handler.db) {
		c.JSON(http.StatusForbidden, resp.APIResponse{IsError: true, Message: "You don't have permission to access this group"})
		return
	}

	handler.db.Delete(&post)
	c.String(http.StatusOK, "Successfully deleted")
}
// GetTopUsersCSV serves a csv file report of users with the most votes
// Columns: Name, Total votes
func (handler ExportHandler) GetTopUsersCSV(c *gin.Context) {
	gname := c.Param("gname")
	if !auth.HasAccessToGroup(auth.GetUserIDFromCookie(c), gname, handler.db) {
		c.JSON(http.StatusForbidden, resp.APIResponse{IsError: true, Message: "You don't have permission to access this group"})
		return
	}

	var group table.Group
	if err := handler.db.Where("name = ?", gname).First(&group).Error; err != nil {
		c.JSON(http.StatusNotFound, resp.APIResponse{IsError: true, Message: "Group does not exist"})
		return
	}

	handler.db.First(&group, table.Group{Name: gname})
	rows, err := handler.db.Table("users").Select("users.firstname, users.lastname, count(*) as total_votes").Joins("join votes on votes.user_id = users.id join posts on posts.id = votes.post_id join groups on groups.id = posts.group_id").Where("groups.id = ?", group.ID).Group("users.id, users.firstname, users.lastname").Rows()
	if err != nil {
		log.Printf("Unable to get top user rows: %s", err)
		c.JSON(http.StatusInternalServerError, resp.APIResponse{IsError: true, Message: "Unable to export top users"})
		return
	}
	output := "Name,Total votes" + "\n"
	for rows.Next() {
		var firstname string
		var lastname string
		var totalVotes int
		rows.Scan(&firstname, &lastname, &totalVotes)
		data := []string{firstname + " " + lastname, fmt.Sprintf("%v", totalVotes)}
		for i := range data {
			data[i] = escapeForCSV(data[i])
		}
		output += strings.Join(data[:], ",") + "\n"
	}

	c.Writer.Header().Set("Content-Disposition", "attachment; filename=top_users.csv")
	c.Writer.Header().Set("Content-Type", c.Request.Header.Get("Content-Type"))

	c.String(http.StatusOK, output)
}
Exemple #8
0
// CreateOrUpdateVote create a new vote or update the user's existing one
func (handler VoteHandler) CreateOrUpdateVote(c *gin.Context) {
	puuid := c.Param("puuid")
	var voteReq req.VoteCreateRequest
	if err := c.BindJSON(&voteReq); err != nil {
		log.Printf("Unable request: %s", err)
		c.JSON(http.StatusBadRequest, resp.APIResponse{IsError: true, Message: "Error parsing request"})
		return
	}
	if !voteReq.IsValid() {
		c.JSON(http.StatusBadRequest, resp.APIResponse{IsError: true, Message: "Invalid data"})
		return
	}

	// lookup post by uuid
	var post table.Post
	if err := handler.db.Where("uuid = ?", puuid).First(&post).Error; err != nil {
		c.JSON(http.StatusOK, resp.APIResponse{IsError: true, Message: "Question does not exist"})
		return
	}

	// get current user id
	userID := auth.GetUserIDFromCookie(c)

	// start transaction
	tx := handler.db.Begin()

	// attempt to get existing vote
	var vote table.Vote
	isChangingVote := false
	if err := tx.Where("user_id = ? AND post_id = ?", userID, post.ID).First(&vote).Error; err != nil {
		// vote does not exist
		vote.PostID = post.ID
		vote.PostUUID = post.UUID
		vote.UserID = userID
		vote.Value = voteReq.Value
		if err := tx.Create(&vote).Error; err != nil {
			log.Printf("Unable to create vote: %s", err)
			c.JSON(http.StatusInternalServerError, resp.APIResponse{IsError: true, Message: "Unable to create vote"})
			return
		}
	} else {
		// don't allow user to vote same way multiple times
		if vote.Value == voteReq.Value {
			c.JSON(http.StatusConflict, resp.APIResponse{IsError: true, Message: "Already voted that way"})
			return
		}

		// changing vote
		vote.Value = voteReq.Value
		isChangingVote = true
		tx.Save(&vote)
	}

	// update question's vote counts
	if vote.Value == 1 {
		post.Upvotes++
		if isChangingVote {
			post.Downvotes--
		}
	} else {
		post.Downvotes++
		if isChangingVote {
			post.Upvotes--
		}
	}

	tx.Save(&post)

	// commit transaction
	tx.Commit()

	c.JSON(http.StatusOK, resp.APIResponse{IsError: false, Value: post})
}