Example #1
0
// HandleShow displays a single story
func HandleShow(context router.Context) error {

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.InternalError(err)
	}

	// Redirect requests to the canonical url
	if context.Path() != story.URLShow() {
		return router.Redirect(context, story.URLShow())
	}

	// Find the comments for this story
	// Fetch the comments
	q := comments.Where("story_id=?", story.Id).Order(comments.RankOrder)
	rootComments, err := comments.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("story", story)
	view.AddKey("meta_title", story.Name)
	view.AddKey("meta_desc", story.Summary)
	view.AddKey("meta_keywords", story.Name)
	view.AddKey("comments", rootComments)

	return view.Render()
}
Example #2
0
// HandleDownvote handles POST to /stories/123/downvote
func HandleDownvote(context router.Context) error {

	// Prevent CSRF
	err := authorise.AuthenticityToken(context)
	if err != nil {
		return router.NotAuthorizedError(err, "Vote Failed", "CSRF failure")
	}

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.NotFoundError(err)
	}
	user := authorise.CurrentUser(context)
	ip := getUserIP(context)

	if !user.Admin() {
		// Check we have no votes already from this user, if we do fail
		if storyHasUserVote(story, user) {
			return router.NotAuthorizedError(err, "Vote Failed", "Sorry you are not allowed to vote twice, nice try!")
		}
	}

	// Authorise upvote on story for this user - our rules are:
	if !user.CanDownvote() {
		return router.NotAuthorizedError(err, "Vote Failed", "Sorry, you can't downvote yet")
	}

	err = authorise.Resource(context, story)
	if err != nil {
		return router.NotAuthorizedError(err, "Vote Failed", "Sorry you are not allowed to vote")
	}

	err = adjustUserPoints(user, -1)
	if err != nil {
		return err
	}

	// Adjust points on story and add to the vote table
	err = addStoryVote(story, user, ip, -1)
	if err != nil {
		return err
	}

	return updateStoriesRank()
}
Example #3
0
// HandleFlag handles POST to /stories/123/flag
func HandleFlag(context router.Context) error {

	// Protect against CSRF
	err := authorise.AuthenticityToken(context)
	if err != nil {
		return router.NotAuthorizedError(err, "Flag Failed", "CSRF failure")
	}

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.NotFoundError(err)
	}
	user := authorise.CurrentUser(context)
	ip := getUserIP(context)

	// Check we have no votes already from this user, if we do fail
	if storyHasUserFlag(story, user) {
		return router.NotAuthorizedError(err, "Flag Failed", "Sorry you are not allowed to flag twice, nice try!")
	}

	// Authorise upvote on story for this user
	if !user.CanFlag() {
		return router.NotAuthorizedError(err, "Flag Failed", "Sorry, you can't flag yet")
	}

	err = authorise.Resource(context, story)
	if err != nil {
		return router.NotAuthorizedError(err, "Flag Failed", "Sorry you are not allowed to flag")
	}

	err = adjustUserPoints(user, -1)
	if err != nil {
		return err
	}

	err = addStoryVote(story, user, ip, -5)
	if err != nil {
		return err
	}
	return updateStoriesRank()
}
Example #4
0
// HandleUpdateShow renders the form to update a story
func HandleUpdateShow(context router.Context) error {

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.NotFoundError(err)
	}

	// Authorise update story
	err = authorise.Resource(context, story)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("story", story)

	return view.Render()
}
Example #5
0
// HandleDestroy handles a DESTROY request for stories
func HandleDestroy(context router.Context) error {

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.NotFoundError(err)
	}

	// Authorise destroy story
	err = authorise.Resource(context, story)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Destroy the story
	story.Destroy()

	// Redirect to stories root
	return router.Redirect(context, story.URLIndex())
}
Example #6
0
// HandleUpdate handles the POST of the form to update a story
func HandleUpdate(context router.Context) error {

	// Find the story
	story, err := stories.Find(context.ParamInt("id"))
	if err != nil {
		return router.NotFoundError(err)
	}

	// Authorise update story
	err = authorise.ResourceAndAuthenticity(context, story)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Update the story from params
	params, err := context.Params()
	if err != nil {
		return router.InternalError(err)
	}

	// Clean params according to role
	accepted := stories.AllowedParams()
	if authorise.CurrentUser(context).Admin() {
		accepted = stories.AllowedParamsAdmin()
	}
	cleanedParams := params.Clean(accepted)

	err = story.Update(cleanedParams)
	if err != nil {
		return err // Create returns a router.Error
	}

	err = updateStoriesRank()
	if err != nil {
		return router.InternalError(err)
	}

	// Redirect to story
	return router.Redirect(context, story.URLShow())
}
Example #7
0
// HandleCreate handles the POST of the create form for comments
func HandleCreate(context router.Context) error {

	// Authorise csrf token
	err := authorise.AuthenticityToken(context)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Check permissions - if not logged in and above 0 points, redirect
	if !authorise.CurrentUser(context).CanComment() {
		return router.NotAuthorizedError(nil, "Sorry", "You need to be registered and have more than 0 points to comment.")
	}

	// Setup context
	params, err := context.Params()
	if err != nil {
		return router.InternalError(err)
	}

	// Find parent story - this must exist
	story, err := stories.Find(params.GetInt("story_id"))
	if err != nil {
		return router.NotFoundError(err)
	}

	params.SetInt("story_id", story.Id)
	params.Set("story_name", story.Name)

	// Set a few params
	user := authorise.CurrentUser(context)
	params.SetInt("user_id", user.Id)
	params.Set("user_name", user.Name)
	params.SetInt("points", 1)

	// Find the parent and set dotted id
	// these are of the form xx.xx. with a trailing dot
	// this saves us from saving twice on create
	parentID := context.ParamInt("parent_id")
	if parentID > 0 {
		parent, err := comments.Find(parentID)
		if err != nil {
			return router.NotFoundError(err)
		}
		context.Logf("PARENT:%d - %s", parent.Id, parent.DottedIds)
		params.Set("dotted_ids", fmt.Sprintf(parent.DottedIds+"."))
	}

	// Clean params allowing all through (since we have manually reset them above)
	accepted := comments.AllowedParamsAdmin()
	cleanedParams := params.Clean(accepted)
	id, err := comments.Create(cleanedParams)
	if err != nil {
		return router.InternalError(err)
	}

	// Log creation
	context.Logf("#info Created comment id,%d", id)

	// Update the story comment count
	storyParams := map[string]string{"comment_count": fmt.Sprintf("%d", story.CommentCount+1)}
	err = story.Update(storyParams)
	if err != nil {
		return router.InternalError(err, "Error", "Could not update story.")
	}

	// Redirect to the new comment
	m, err := comments.Find(id)
	if err != nil {
		return router.InternalError(err)
	}

	// Re-rank comments on this story
	err = updateCommentsRank(m.StoryId)
	if err != nil {
		return err
	}

	return router.Redirect(context, m.URLStory())
}
Example #8
0
// HandleCreate handles the POST of the create form for stories
func HandleCreate(context router.Context) error {

	// Check csrf token
	err := authorise.AuthenticityToken(context)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Check permissions - if not logged in and above 1 points, redirect to error
	if !authorise.CurrentUser(context).CanSubmit() {
		return router.NotAuthorizedError(nil, "Sorry", "You need to be registered and have more than 1 points to submit stories.")
	}

	// Get params
	params, err := context.Params()
	if err != nil {
		return router.InternalError(err)
	}

	// Get user details
	user := authorise.CurrentUser(context)
	ip := getUserIP(context)

	// Process urls
	url := params.Get("url")

	// Strip trailing slashes on url before comparisons
	if strings.HasSuffix(url, "/") {
		url = strings.Trim(url, "/")
	}

	// Strip ?utm_source etc - remove all after ?utm_source
	if strings.Contains(url, "?utm_") {
		url = strings.Split(url, "?utm_")[0]
	}

	// Strip url fragments (For example trailing # on medium urls)
	if strings.Contains(url, "#") {
		url = strings.Split(url, "#")[0]
	}

	// Rewrite mobile youtube links
	if strings.HasPrefix(url, "https://m.youtube.com") {
		url = strings.Replace(url, "https://m.youtube.com", "https://www.youtube.com", 1)
	}

	params.Set("url", url)

	// Check that no story with this url already exists
	q := stories.Where("url=?", url)
	duplicates, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	if len(duplicates) > 0 {
		story := duplicates[0]

		// Check we have no votes already from this user, if we do fail
		if storyHasUserVote(story, user) {
			return router.NotAuthorizedError(err, "Vote Failed", "Sorry you are not allowed to vote twice, nice try!")

		}

		// Add a point to dupe and return
		addStoryVote(story, user, ip, 1)
		return router.Redirect(context, story.URLShow())
	}
	// Clean params according to role
	accepted := stories.AllowedParams()
	if authorise.CurrentUser(context).Admin() {
		accepted = stories.AllowedParamsAdmin()
	}
	cleanedParams := params.Clean(accepted)

	// Set a few params
	cleanedParams["points"] = "1"
	cleanedParams["user_id"] = fmt.Sprintf("%d", user.Id)
	cleanedParams["user_name"] = user.Name

	id, err := stories.Create(cleanedParams)
	if err != nil {
		return err // Create returns a router.Error
	}

	// Log creation
	context.Logf("#info Created story id,%d", id)

	// Redirect to the new story
	story, err := stories.Find(id)
	if err != nil {
		return router.InternalError(err)
	}

	// We need to add a vote to the story here too by adding a join to the new id
	err = recordStoryVote(story, user, ip, +1)
	if err != nil {
		return err
	}

	// Re-rank stories
	err = updateStoriesRank()
	if err != nil {
		return err
	}

	return router.Redirect(context, story.URLIndex())
}