// 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() }
// errHandler renders an error using error templates if available func errHandler(context router.Context, e error) { // Cast the error to a status error if it is one, if not wrap it in a Status 500 error err := router.ToStatusError(e) view := view.New(context) view.AddKey("title", err.Title) view.AddKey("message", err.Message) if !context.Production() { view.AddKey("status", err.Status) view.AddKey("file", err.FileLine()) view.AddKey("error", err.Err) } view.Template("app/views/error.html.got") // Log 404 as info only if err.Status == 404 { context.Logf("#info %s\n", err) } else { context.Logf("#error %s\n", err) } view.Render() }
// errHandler renders an error using error templates if available func errHandler(context router.Context, e error) { // Cast the error to a status error if it is one, if not wrap it in a Status 500 error err := router.ToStatusError(e) view := view.New(context) view.AddKey("title", err.Title) view.AddKey("message", err.Message) if !context.Production() { view.AddKey("status", err.Status) view.AddKey("file", err.FileLine()) view.AddKey("error", err.Err) } // Set the status correctly for errors context.Writer().WriteHeader(err.Status) // Use our error template view.Template("app/views/error.html.got") context.Logf("#error %s\n", err) view.Render() }
// HandleShowHome serves our home page func homeHandler(context router.Context) error { view := view.New(context) view.AddKey("title", "Fragmenta app") view.Template("app/views/home.html.got") return view.Render() }
// HandleHome displays a list of stories using gravity to order them // used for the home page for gravity rank see votes.go 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") // Fetch the stories results, err := stories.FindAll(q) if err != nil { return router.InternalError(err) } // Render the template view := view.New(context) view.AddKey("stories", results) view.AddKey("meta_title", "Golang News") view.AddKey("meta_desc", "News for Go Hackers, in the style of Hacker News. A curated selection of the latest links about the Go programming language.") view.AddKey("meta_keywords", "golang news, blog, links, go developers, go web apps, web applications, fragmenta") view.AddKey("authenticity_token", authorise.CreateAuthenticityToken(context)) view.Template("stories/views/index.html.got") return view.Render() }
// 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() }
// render the given page, with metadata set func render(context router.Context, page *pages.Page) error { // Render the template view := view.New(context) view.AddKey("page", page) view.AddKey("page", page) view.AddKey("meta_title", page.Name) view.AddKey("meta_desc", page.Summary) view.AddKey("meta_keywords", page.Keywords) view.Template("pages/views/show.html.got") return view.Render() }
func renderPage(context router.Context, page *pages.Page) error { view := view.New(context) // Setup context for template if page.Template != "" { view.Template(page.Template) } else { view.Template("pages/views/show.html.got") } view.AddKey("page", page) view.AddKey("meta_title", page.Name) view.AddKey("meta_desc", page.Summary) view.AddKey("meta_keywords", page.Keywords) // Serve template context.Logf("#info Rendering page for path %s", context.Path()) return view.Render() }
// 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() }
// 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() }
// 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() }
// 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() }
// HandleShowSetup shows the setup page at /fragmenta/setup func HandleShowSetup(context router.Context) error { // Setup context for template view := view.New(context) // If we have pages or users already, do not proceed if !missingUsersAndPages() { return router.NotAuthorizedError(nil) } view.Template("pages/views/setup.html.got") return view.Render() }
// 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() }
// Send sends mail func Send(recipients []string, subject string, template string, context map[string]interface{}) error { // At present we use sendgrid, we may later allow many mail services to be used // sg := sendgrid.NewSendGridClient(user, secret) sg := sendgrid.NewSendGridClientWithApiKey(secret) message := sendgrid.NewMail() message.SetFrom(from) message.AddTos(recipients) message.SetSubject(subject) // Hack for now, consider options TODO if context["reply_to"] != nil { replyTo := context["reply_to"].(string) message.SetReplyTo(replyTo) } // Load the template, and substitute using context // We should possibly set layout from caller too? view := view.New(&renderContext{}) view.Template(template) view.Context(context) // We have a panic: runtime error: invalid memory address or nil pointer dereference // because of reloading templates on the fly I think // github.com/fragmenta/view/parser/template.html.go:90 html, err := view.RenderString() if err != nil { return err } // For debug, print message //fmt.Printf("SENDING MAIL:\n%s", html) message.SetHTML(html) return sg.Send(message) }
// Send sends mail func Send(recipients []string, subject string, template string, context map[string]interface{}) error { // For now ensure that we don't send to more than 1 recipient while we debug emails if len(recipients) > 1 { return fmt.Errorf("mail.send: #error bad recipients for debug %v", recipients) } if recipients[0] != "*****@*****.**" { return fmt.Errorf("mail.send: #error bad recipients for debug %v", recipients) } // Send via sendgrid sg := sendgrid.NewSendGridClientWithApiKey(secret) message := sendgrid.NewMail() message.SetFrom(from) message.AddTos(recipients) message.SetSubject(subject) // Load the template, and substitute using context // We should possibly set layout from caller too? view := view.NewWithPath("", nil) view.Template(template) view.Context(context) html, err := view.RenderToString() if err != nil { return err } message.SetHTML(html) // For debug, print message fmt.Printf("#info sending MAIL to:%s", recipients) return sg.Send(message) }
// 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() }
// GET /users/password/sent func HandlePasswordResetSentShow(context router.Context) error { view := view.New(context) view.Template("users/views/password.html.got") return view.Render() }