forked from kennygrant/gohackernews
/
create.go
executable file
·184 lines (146 loc) · 4.77 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package storyactions
import (
"fmt"
"net/http"
"strings"
"github.com/fragmenta/auth/can"
"github.com/fragmenta/mux"
"github.com/fragmenta/server"
"github.com/fragmenta/server/log"
"github.com/fragmenta/view"
"github.com/kennygrant/gohackernews/src/lib/session"
"github.com/kennygrant/gohackernews/src/stories"
)
// HandleCreateShow serves the create form via GET for stories.
func HandleCreateShow(w http.ResponseWriter, r *http.Request) error {
story := stories.New()
// Authorise
currentUser := session.CurrentUser(w, r)
err := can.Create(story, currentUser)
if err != nil {
return server.NotAuthorizedError(err)
}
// Get the params
params, err := mux.Params(r)
if err != nil {
return server.InternalError(err)
}
// If the bookmarklet or user has set params, use them
story.URL = params.Get("u")
story.Name = params.Get("n")
story.Summary = params.Get("s")
// Render the template
view := view.NewRenderer(w, r)
view.AddKey("story", story)
view.AddKey("hideSubmit", true)
view.AddKey("currentUser", currentUser)
return view.Render()
}
// HandleCreate handles the POST of the create form for stories
func HandleCreate(w http.ResponseWriter, r *http.Request) error {
story := stories.New()
// Check the authenticity token
err := session.CheckAuthenticity(w, r)
if err != nil {
return err
}
// Get user details
currentUser := session.CurrentUser(w, r)
ip := getUserIP(r)
// Authorise
err = can.Create(story, currentUser)
if err != nil {
return server.NotAuthorizedError(err)
}
// Check permissions - if not logged in and above 1 points, redirect to error
if !currentUser.CanSubmit() {
return server.NotAuthorizedError(nil, "Sorry", "You need to be registered and have more than 1 points to submit stories.")
}
// Get the params
params, err := mux.Params(r)
if err != nil {
return server.InternalError(err)
}
url := params.Get("url")
name := params.Get("name")
// Disallow invalid urls, except empty, which is allowed
if url != "" && (len(url) < 5 || len(name) < 5 || !strings.HasPrefix(url, "http")) {
return server.NotAuthorizedError(nil, "Incomplete Name or URL", "The story submitted contained incomplete or short data.")
}
// Disallow short names
if len(name) < 3 {
return server.NotAuthorizedError(nil, "Name too short", "The name of your story is too short, the min length is 3 characters.")
}
if len(name) > 100 {
return server.NotAuthorizedError(nil, "Name too long", "The name of your story is too long, the maximum length is 100 characters.")
}
if len(url) > 666 {
return server.NotAuthorizedError(nil, "URL too long", "The URL of your story is too long, the maximum is 666.")
}
// 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)
// for now only strip on medium urls
if strings.Contains(url, "#") && strings.Contains(url, "medium.com") {
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", []string{url})
// Check that no story with this url already exists
q := stories.Where("url=?", url)
duplicates, err := stories.FindAll(q)
if err != nil {
return server.InternalError(err)
}
// If we have a duplicate story, with the same non-null url, upvote or reject
if len(duplicates) > 0 && url != "" {
story = duplicates[0]
// Add a point to dupe if not already voted
if !storyHasUserVote(story, currentUser) {
addStoryVote(story, currentUser, ip, 1)
}
// Redirect to the story
return server.Redirect(w, r, story.ShowURL())
}
// Clean params according to role
accepted := stories.AllowedParams()
if currentUser.Admin() {
accepted = stories.AllowedParamsAdmin()
}
storyParams := story.ValidateParams(params.Map(), accepted)
// Set a few params to known good values
storyParams["points"] = "1"
storyParams["user_id"] = fmt.Sprintf("%d", currentUser.ID)
storyParams["user_name"] = currentUser.Name
ID, err := story.Create(storyParams)
if err != nil {
return err // Create returns a router.Error
}
// Log creation
log.Info(log.V{"msg": "Created story", "story_id": ID})
// Redirect to the new story
story, err = stories.Find(ID)
if err != nil {
return server.InternalError(err)
}
// We need to add a vote to the story here too by adding a join to the new id
err = recordStoryVote(story, currentUser, ip, +1)
if err != nil {
return err
}
// Re-rank stories
err = updateStoriesRank()
if err != nil {
return err
}
return server.Redirect(w, r, story.IndexURL())
}