Esempio n. 1
0
// POST /users/password/reset
func HandlePasswordResetSend(context router.Context) error {

	// Find the user by email (if not found let them know)
	// Find the user by hex token in the db
	email := context.Param("email")
	user, err := users.First(users.Where("email=?", email))
	if err != nil {
		return router.Redirect(context, "/users/password/reset?message=invalid_email")
	}

	// Generate a random token and url for the email
	token := auth.BytesToHex(auth.RandomToken())

	// Generate the url to use in our email
	base := fmt.Sprintf("%s://%s", context.Request().URL.Scheme, context.Request().URL.Host)
	url := fmt.Sprintf("%s/users/password?token=%s", base, token)

	context.Logf("#info sending reset email:%s url:%s", email, url)

	// Update the user record with with this token
	p := map[string]string{"reset_token": token}
	user.Update(p)

	// Send a password reset email out
	//mail.Send("mymail")

	// Tell the user what we have done
	return router.Redirect(context, "/users/password/sent")
}
Esempio n. 2
0
// HandleIndex serves a get request at /pages
func HandleIndex(context router.Context) error {

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

	// Fetch the pages
	q := pages.Query().Order("url asc")

	// Filter if necessary
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("(to_tsvector(name) || to_tsvector(summary) || to_tsvector(url) @@ to_tsquery(?) )", filter)
	}

	pageList, err := pages.FindAll(q)
	if err != nil {
		context.Logf("#error Error indexing pages %s", err)
		return router.InternalError(err)
	}

	// Serve template
	view := view.New(context)
	view.AddKey("filter", filter)
	view.AddKey("pages", pageList)
	return view.Render()

}
Esempio n. 3
0
// HandleHome displays a list of stories using gravity to order them
// used for the home page for gravity rank see votes.go
// responds to GET /
func HandleHome(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Select only above 0 points,  Order by rank, then points, then name
	q.Where("points > 0").Order("rank desc, points desc, id desc")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 4
0
// Serve a get request at /images
//
//
func HandleIndex(context router.Context) error {

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

	// Setup context for template
	view := view.New(context)

	// Build a query
	q := images.Query()

	// Show only published (defaults to showing all)
	// q.Apply(status.WherePublished)

	// Order by required order, or default to id asc
	switch context.Param("order") {

	case "1":
		q.Order("created_at desc")

	case "2":
		q.Order("updated_at desc")

	case "3":
		q.Order("name asc")

	default:
		q.Order("id desc")

	}

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("( to_tsvector(name) || to_tsvector(summary) @@ to_tsquery(?) )", filter)
	}

	// Fetch the images
	imagesList, err := images.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Serve template
	view.AddKey("filter", filter)
	view.AddKey("images", imagesList)
	// Can we add these programatically?
	view.AddKey("admin_links", helpers.Link("Create image", images.New().URLCreate()))

	return view.Render()

}
Esempio n. 5
0
// HandleIndex displays a list of stories at /stories
func HandleIndex(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Order by date by default
	q.Where("points > -6").Order("created_at desc")

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("q")
	if len(filter) > 0 {

		// Replace special characters with escaped sequence
		filter = strings.Replace(filter, "_", "\\_", -1)
		filter = strings.Replace(filter, "%", "\\%", -1)

		wildcard := "%" + filter + "%"

		// Perform a wildcard search for name or url
		q.Where("stories.name ILIKE ? OR stories.url ILIKE ?", wildcard, wildcard)

		// If filtering, order by rank, not by date
		q.Order("rank desc, points desc, id desc")
	}

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)

	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 6
0
// HandleLoginShow handles GET /users/login
func HandleLoginShow(context router.Context) error {

	// Check we have no current user
	u := authorise.CurrentUser(context)
	if !u.Anon() {
		return router.Redirect(context, fmt.Sprintf("/users/%d", u.Id))
	}

	// Render the template
	view := view.New(context)
	view.AddKey("error", context.Param("error"))
	return view.Render()
}
Esempio n. 7
0
// HandleShowKey displays a single user's key
func HandleShowKey(context router.Context) error {

	// Find the user
	user, err := users.FindName(context.Param("name"))
	if err != nil {
		return router.InternalError(err)
	}

	// Render the key directly to the httpwriter as text
	context.Writer().Header().Set("Content-Type", "text/plain; charset=utf-8")
	_, err = io.WriteString(context.Writer(), user.Key)
	return err
}
Esempio n. 8
0
// HandleIndex displays a list of posts
func HandleIndex(context router.Context) error {

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

	// Build a query
	q := posts.Query()

	// Order by required order, or default to id asc
	switch context.Param("order") {

	case "1":
		q.Order("created desc")

	case "2":
		q.Order("updated desc")

	case "3":
		q.Order("name asc")

	default:
		q.Order("id asc")

	}

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("( to_tsvector(name) || to_tsvector(summary) @@ to_tsquery(?) )", filter)
	}

	// Fetch the posts
	results, err := posts.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("filter", filter)
	view.AddKey("posts", results)
	return view.Render()

}
Esempio n. 9
0
// HandleShowName displays a single user by name
func HandleShowName(context router.Context) error {

	// Find the user
	user, err := users.FindName(context.Param("name"))
	if err != nil {
		return router.NotFoundError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("user", user)
	view.Template("users/views/show.html.got")
	return view.Render()
}
Esempio n. 10
0
// AuthenticityToken checks the token in the current request
func AuthenticityToken(context router.Context) error {
	token := context.Param(auth.SessionTokenKey)
	err := auth.CheckAuthenticityToken(token, context.Request())
	if err != nil {
		// If the check fails, log out the user and completely clear the session
		context.Logf("#warn invalid authenticity token at %v", context)
		session, err := auth.SessionGet(context.Request())
		if err != nil {
			return err
		}
		session.Clear(context.Writer())
	}

	return err
}
Esempio n. 11
0
// HandleUpvoted displays a list of stories the user has upvoted in the past
func HandleUpvoted(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Select only above 0 points,  Order by rank, then points, then name
	q.Where("points > 0").Order("rank desc, points desc, id desc")

	// Select only stories which the user has upvoted
	user := authorise.CurrentUser(context)
	if !user.Anon() {
		// Can we use a join instead?
		v := query.New("votes", "story_id").Select("select story_id as id from votes").Where("user_id=? AND story_id IS NOT NULL AND points > 0", user.Id)

		storyIDs := v.ResultIDs()
		if len(storyIDs) > 0 {
			q.WhereIn("id", storyIDs)
		}
	}

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 12
0
// POST /users/password?token=DEADFISH - handle password reset link
func HandlePasswordReset(context router.Context) error {

	token := context.Param("token")
	if len(token) == 0 {
		return router.InternalError(fmt.Errorf("Blank reset token"))
	}

	// Find the user by hex token in the db
	user, err := users.First(users.Where("token=?", token))
	if err != nil {
		return router.InternalError(err)
	}

	// If we found a user with this token, log the sender in as them
	// and remove the token from the user so that it can't be used twice
	// we should possibly add a time limit to tokens too?

	// Redirect to the user update page so that they can change their password
	return router.Redirect(context, fmt.Sprintf("/users/%d/update", user.Id))
}
Esempio n. 13
0
// HandleUpdateShow serves a get request at /images/1/update (show form to update)
func HandleUpdateShow(context router.Context) error {
	// Setup context for template
	view := view.New(context)

	image, err := images.Find(context.ParamInt("id"))
	if err != nil {
		context.Logf("#error Error finding image %s", err)
		return router.NotFoundError(err)
	}

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

	view.AddKey("redirect", context.Param("redirect"))
	view.AddKey("image", image)
	return view.Render()
}
Esempio n. 14
0
// HandleLoginShow shows the page at /users/login
func HandleLoginShow(context router.Context) error {
	// Setup context for template
	view := view.New(context)

	// Check we're not already logged in, if so redirect with a message
	// we could alternatively display an error here?
	if !authorise.CurrentUser(context).Anon() {
		return router.Redirect(context, "/?warn=already_logged_in")
	}

	switch context.Param("error") {
	case "failed_email":
		view.AddKey("warning", "Sorry, we couldn't find a user with that email.")
	case "failed_password":
		view.AddKey("warning", "Sorry, the password was incorrect, please try again.")
	}

	// Serve
	return view.Render()
}
Esempio n. 15
0
// HandleCode displays a list of stories linking to repos (github etc) using gravity to order them
// responds to GET /stories/code
func HandleCode(context router.Context) error {

	// Build a query
	q := stories.Query().Where("points > -6").Order("rank desc, points desc, id desc").Limit(listLimit)

	// Restrict to stories with have a url starting with github.com or bitbucket.org
	// other code repos can be added later
	q.Where("url ILIKE 'https://github.com%'").OrWhere("url ILIKE 'https://bitbucket.org'")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	//	view.AddKey("tags", []string{"web", "mobile", "data", "email", "crypto", "data", "graphics", "ui", "security"})

	// TODO: remove these calls and put in a filter
	// - given it is not too expensive, we could just generate tokens on every request

	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 16
0
// HandleIndex displays a list of stories at /stories
func HandleIndex(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Order by date by default
	q.Where("points > -6").Order("created_at desc")

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("filter")
	if len(filter) > 0 {
		context.Logf("FILTER %s", filter)

		// Replace special characters with escaped sequence
		filter = strings.Replace(filter, "_", "\\_", -1)
		filter = strings.Replace(filter, "%", "\\%", -1)

		// initially very simple, do ilike query for filter with wildcards
		q.Where("stories.name ILIKE ?", "%"+filter+"%")

		// If filtering, order by rank, not by date
		q.Order("rank desc, points desc, id desc")
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("filter", filter)
	view.AddKey("stories", results)
	view.AddKey("meta_title", "Go Hacker News Links")
	view.AddKey("authenticity_token", authorise.CreateAuthenticityToken(context))

	return view.Render()

}
Esempio n. 17
0
// HandleCode displays a list of stories linking to repos (github etc) using gravity to order them
// responds to GET /stories/code
func HandleCode(context router.Context) error {

	// Build a query
	q := stories.Query().Where("points > -6").Order("rank desc, points desc, id desc").Limit(listLimit)

	// Restrict to stories with have a url starting with github.com or bitbucket.org
	// other code repos can be added later
	q.Where("url ILIKE 'https://github.com%'").OrWhere("url ILIKE 'https://bitbucket.org'")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.AddKey("pubdate", storiesModTime(results))
	view.AddKey("meta_title", "Go Code")
	view.AddKey("meta_desc", context.Config("meta_desc"))
	view.AddKey("meta_keywords", context.Config("meta_keywords"))
	view.AddKey("meta_rss", storiesXMLPath(context))
	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 18
0
// HandleIndex displays a list of comments
func HandleIndex(context router.Context) error {

	// No auth this is public

	// Build a query to fetch latest 100 comments
	q := comments.Query().Limit(100).Order("created_at desc")

	// Filter on user id - we only show the actual user's comments
	// so not a nested view as in HN
	userID := context.ParamInt("u")
	if userID > 0 {
		q.Where("user_id=?", userID)
	}

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("(to_tsvector(text) @@ to_tsquery(?) )", filter)
	}

	// Fetch the comments
	results, err := comments.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("filter", filter)
	view.AddKey("comments", results)
	view.AddKey("meta_title", "Comments")
	view.AddKey("authenticity_token", authorise.CreateAuthenticityToken(context))

	return view.Render()

}
Esempio n. 19
0
// HandleHome displays a list of stories using gravity to order them
// used for the home page for gravity rank see votes.go
// responds to GET /
func HandleHome(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Select only above 0 points,  Order by rank, then points, then name
	q.Where("points > 0").Order("rank desc, points desc, id desc")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.Template("stories/views/index.html.got")
	view.AddKey("pubdate", storiesModTime(results))
	view.AddKey("meta_title", fmt.Sprintf("%s, %s", context.Config("meta_title"), context.Config("meta_desc")))
	view.AddKey("meta_desc", context.Config("meta_desc"))
	view.AddKey("meta_keywords", context.Config("meta_keywords"))
	view.AddKey("meta_rss", storiesXMLPath(context))

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Esempio n. 20
0
// HandleIndex serves a get request at /tags
func HandleIndex(context router.Context) error {

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

	//UpdateAllDottedIds()

	// Setup context for template
	view := view.New(context)

	// Fetch the tags
	q := tags.RootTags().Order("name asc")

	// Filter if necessary
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("(to_tsvector(name) || to_tsvector(summary) @@ to_tsquery(?))", filter)
	}

	tagList, err := tags.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Serve template
	view.AddKey("filter", filter)
	view.AddKey("tags", tagList)

	return view.Render()

}
Esempio n. 21
0
// HandleBlog displays a list of posts in reverse chronological order
func HandleBlog(context router.Context) error {

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

	// Build a query
	q := posts.Published().Order("created_at desc")

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("filter")
	if len(filter) > 0 {
		filter = strings.Replace(filter, "&", "", -1)
		filter = strings.Replace(filter, " ", "", -1)
		filter = strings.Replace(filter, " ", " & ", -1)
		q.Where("( to_tsvector(name) || to_tsvector(summary) @@ to_tsquery(?) )", filter)
	}

	// Fetch the posts
	results, err := posts.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	context.Logf("POSTS HERE :%v", results)

	// Render the template
	view := view.New(context)
	view.AddKey("filter", filter)
	view.AddKey("posts", results)
	view.Template("posts/views/blog.html.got")
	return view.Render()

}
Esempio n. 22
0
// HandleSetup responds to a POST at /fragmenta/setup
// by creating our first user and page
func HandleSetup(context router.Context) error {

	// If we have pages or users already, do not proceed
	if !missingUsersAndPages() {
		return router.NotAuthorizedError(nil)
	}

	// Take the details given and create the first user
	params := map[string]string{
		"email":    context.Param("email"),
		"password": context.Param("password"),
		"name":     nameFromEmail(context.Param("email")),
		"status":   "100",
		"role":     "100",
		"title":    "Administrator",
	}

	uid, err := users.Create(params)
	if err != nil {
		return router.InternalError(err)
	}
	context.Logf("#info Created user #%d", uid)
	user, err := users.Find(uid)
	if err != nil {
		return router.InternalError(err)
	}
	// Login this user automatically - save cookie
	session, err := auth.Session(context, context.Request())
	if err != nil {
		return router.InternalError(err)
	}
	context.Logf("#info Automatic login for first user: %d %s", user.Id, user.Email)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)

	// Load our welcomepage template html
	// and put it into the text field of a new page with id 1

	welcomeText, err := ioutil.ReadFile("src/pages/views/welcome.html.got")
	if err != nil {
		return router.InternalError(err)
	}

	params = map[string]string{
		"status": "100",
		"name":   "Fragmenta",
		"url":    "/",
		"text":   string(welcomeText),
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}

	// Create another couple of simple pages as examples (about and privacy)
	params = map[string]string{
		"status": "100",
		"name":   "About Us",
		"url":    "/about",
		"text":   "<section class=\"narrow\"><h1>About us</h1><p>About us</p></section>",
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}
	params = map[string]string{
		"status": "100",
		"name":   "Privacy Policy",
		"url":    "/privacy",
		"text":   "<section class=\"narrow\"><h1>Privacy Policy</h1><p>We respect your privacy.</p></section>",
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}

	// Redirect back to the newly populated home page
	return router.Redirect(context, "/")
}
Esempio n. 23
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 user details
	user := authorise.CurrentUser(context)
	ip := getUserIP(context)

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

	if len(duplicates) > 0 {
		dupe := duplicates[0]
		// Add a point to dupe
		addStoryVote(dupe, user, ip, 1)

		return router.Redirect(context, dupe.URLShow())
	}

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

	// Set a few params
	params.SetInt("points", 1)
	params.SetInt("user_id", user.Id)
	params.Set("user_name", user.Name)

	id, err := stories.Create(params.Map())
	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())
}
Esempio n. 24
0
// HandleCreate handles the POST of the create form for files
func HandleCreate(context router.Context) error {

	// Do not perform auth on posts - we could check a shared secret here or similar but that is not secure
	// better to require siging of posts by users with their own key to confirm identity if we wanted to check submissions.

	// Parse multipart first - must fix this to do it automatically
	fileParams, err := context.ParamFiles("file")
	if err != nil || len(fileParams) < 1 {
		return router.InternalError(err, "Invalid file", "Sorry, the file upload failed.")
	}
	fh := fileParams[0]

	context.Logf("#info FILE RECD:%v", fh.Filename)

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

	// First find the username, if we have no user, reject post
	//context.Logf("PARAMS:%v", params)
	// PARAMS:map[sender:[Kenny Grant] recipient:[testtest]]
	// Check for user with name recipient

	// Find the user
	user, err := users.FindName(context.Param("recipient"))
	if err != nil || user == nil {
		return router.NotFoundError(err, "User not found", "Sorry this user doesn't exist")
	}

	// link with the named recipient user
	params.SetInt("user_id", user.Id)

	context.Logf("PARAMS:%v", params)

	// Ideally perform some identity check on the sender here, and set sender id if we have a user?
	// Perhaps require sending pgp sig of data?

	// We only consider the first file
	id, err := files.Create(params.Map(), fh)
	if err != nil {
		return router.InternalError(err)
	}

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

	/*
		_, err := files.Find(id)
		if err != nil {
			return router.InternalError(err)
		}
	*/

	// Render a 200 response
	view := view.New(context)
	view.Layout("files/views/create.json.got")
	return view.Render()
	//	return router.Redirect(context, m.URLIndex())
}