func getBlogContext(reqId uint, embed *service.EmbedNode, s *service.Session, settings *settings, renderer *mtemplate.Renderer) ( map[string][]byte, *service.CacheMods, error) { req, err := s.Monsti().GetRequest(reqId) if err != nil { return nil, nil, fmt.Errorf("Could not get request: %v", err) } query := req.Query blogPath := req.NodePath if embed != nil { embedUrl, err := url.Parse(embed.URI) if err != nil { return nil, nil, fmt.Errorf("Could not parse embed URI") } query = embedUrl.Query() blogPath = embedUrl.Path } limit := -1 if limitParam, err := strconv.Atoi(query.Get("limit")); err == nil { limit = limitParam if limit < 1 { limit = 1 } } context := mtemplate.Context{} context["Embedded"] = embed context["Posts"], err = getBlogPosts(req, blogPath, s, limit) if err != nil { return nil, nil, fmt.Errorf("Could not retrieve blog posts: %v", err) } rendered, err := renderer.Render("core/blogpost-list", context, req.Session.Locale, settings.Monsti.GetSiteTemplatesPath(req.Site)) if err != nil { return nil, nil, fmt.Errorf("Could not render template: %v", err) } mods := &service.CacheMods{ Deps: []service.CacheDep{{Node: req.NodePath, Descend: 1}}, } return map[string][]byte{"BlogPosts": rendered}, mods, nil }
func getBlogPosts(req *service.Request, blogPath string, s *service.Session, limit int) ([]*service.Node, error) { var posts []*service.Node years, err := s.Monsti().GetChildren(req.Site, blogPath) if err != nil { return nil, fmt.Errorf("Could not fetch year children: %v", err) } for _, year := range years { months, err := s.Monsti().GetChildren(req.Site, year.Path) if err != nil { return nil, fmt.Errorf("Could not fetch month children: %v", err) } for _, month := range months { monthPosts, err := s.Monsti().GetChildren(req.Site, month.Path) if err != nil { return nil, fmt.Errorf("Could not fetch month children: %v", err) } posts = append(posts, monthPosts...) } } order := func(left, right *service.Node) bool { return left.PublishTime.Before(right.PublishTime) } sort.Sort(sort.Reverse(&nodes.Sorter{posts, order})) return posts, nil }
func initBlog(settings *settings, session *service.Session, sessions *service.SessionPool, logger *log.Logger, renderer *mtemplate.Renderer) error { G := func(in string) string { return in } nodeType := service.NodeType{ Id: "core.Blog", AddableTo: []string{"."}, Name: i18n.GenLanguageMap(G("Blog"), availableLocales), Fields: []*service.FieldConfig{ {Id: "core.Title"}, }, } if err := session.Monsti().RegisterNodeType(&nodeType); err != nil { return fmt.Errorf("Could not register blog node type: %v", err) } nodeType = service.NodeType{ Id: "core.BlogPost", AddableTo: []string{"core.Blog"}, Name: i18n.GenLanguageMap(G("Blog Post"), availableLocales), Fields: []*service.FieldConfig{ {Id: "core.Title"}, {Id: "core.Body"}, }, Hide: true, PathPrefix: "$year/$month", } if err := session.Monsti().RegisterNodeType(&nodeType); err != nil { return fmt.Errorf("Could not register blog post node type: %v", err) } // Add a signal handler handler := service.NewNodeContextHandler(sessions, func(req uint, session *service.Session, nodeType string, embedNode *service.EmbedNode) ( map[string][]byte, *service.CacheMods, error) { switch nodeType { case "core.Blog": ctx, mods, err := getBlogContext(req, embedNode, session, settings, renderer) if err != nil { return nil, nil, fmt.Errorf("Could not get blog context: %v", err) } return ctx, mods, nil default: return nil, nil, nil } }) if err := session.Monsti().AddSignalHandler(handler); err != nil { logger.Fatalf("Could not add signal handler: %v", err) } return nil }
func initNodeTypes(settings *settings, session *service.Session, logger *log.Logger) error { G := func(in string) string { return in } pathType := service.NodeType{ Id: "core.Path", Hide: true, Name: util.GenLanguageMap(G("Path"), availableLocales), } if err := session.Monsti().RegisterNodeType(&pathType); err != nil { return fmt.Errorf("Could not register path node type: %v", err) } documentType := service.NodeType{ Id: "core.Document", AddableTo: []string{"."}, Name: util.GenLanguageMap(G("Document"), availableLocales), Fields: []*service.NodeField{ { Id: "core.Title", Required: true, Name: util.GenLanguageMap(G("Title"), availableLocales), Type: "Text", }, { Id: "core.Body", Required: true, Name: util.GenLanguageMap(G("Body"), availableLocales), Type: "HTMLArea", }, }, } if err := session.Monsti().RegisterNodeType(&documentType); err != nil { return fmt.Errorf("Could not register document node type: %v", err) } fileType := service.NodeType{ Id: "core.File", AddableTo: []string{"."}, Name: util.GenLanguageMap(G("File"), availableLocales), Fields: []*service.NodeField{ {Id: "core.Title"}, {Id: "core.Body"}, { Id: "core.File", Required: true, Name: util.GenLanguageMap(G("File"), availableLocales), Type: "File", }, }, } if err := session.Monsti().RegisterNodeType(&fileType); err != nil { return fmt.Errorf("Could not register file node type: %v", err) } imageType := service.NodeType{ Id: "core.Image", AddableTo: []string{"."}, Name: util.GenLanguageMap(G("Image"), availableLocales), Fields: []*service.NodeField{ {Id: "core.Title"}, {Id: "core.File"}, }, } if err := session.Monsti().RegisterNodeType(&imageType); err != nil { return fmt.Errorf("Could not register image node type: %v", err) } contactFormType := service.NodeType{ Id: "core.ContactForm", AddableTo: []string{"."}, Name: util.GenLanguageMap(G("Contact form"), availableLocales), Fields: []*service.NodeField{ {Id: "core.Title"}, {Id: "core.Body"}, }, } if err := session.Monsti().RegisterNodeType(&contactFormType); err != nil { return fmt.Errorf("Could not register contactform node type: %v", err) } return nil }
// renderInMaster renders the content in the master template. func renderInMaster(r template.Renderer, content []byte, env masterTmplEnv, settings *settings, site string, siteSettings *service.Settings, userLocale string, s *service.Session) ([]byte, *service.CacheMods) { mods := &service.CacheMods{Deps: []service.CacheDep{{Node: "/", Descend: -1}}} if env.Flags&EDIT_VIEW != 0 { ret, err := r.Render("admin/master", template.Context{ "Site": site, "SiteSettings": siteSettings, "Page": template.Context{ "Title": env.Title, "Node": env.Node, "EditView": true, "SlimView": env.Flags&SLIM_VIEW != 0, "Content": htmlT.HTML(content), }, "Session": env.Session}, userLocale, settings.Monsti.GetSiteTemplatesPath(site)) if err != nil { panic("Can't render admin template: " + err.Error()) } return ret, nil } firstDir := splitFirstDir(env.Node.Path) getNodeFn := func(path string) (*service.Node, error) { node, err := s.Monsti().GetNode(site, path) return node, err } getChildrenFn := func(path string) ([]*service.Node, error) { return s.Monsti().GetChildren(site, path) } priNavDepth := 1 if nav, ok := siteSettings.Fields["core.Navigations"].(*service.MapField). Fields["core.Main"]; ok { priNavDepth = nav.(*service.CombinedField). Fields["depth"].Value().(int) } prinav, err := getNav("/", path.Join("/", firstDir), env.Session.User == nil, getNodeFn, getChildrenFn, priNavDepth) if err != nil { panic(fmt.Sprint("Could not get primary navigation: ", err)) } prinav.MakeAbsolute("/") var secnav navigation = nil if env.Node.Path != "/" { secnav, err = getNav(env.Node.Path, env.Node.Path, env.Session.User == nil, getNodeFn, getChildrenFn, 1) if err != nil { panic(fmt.Sprint("Could not get secondary navigation: ", err)) } secnav.MakeAbsolute(env.Node.Path) } blocks := make(map[string][]renderedBlock) // EXPERIMENTAL Render blocks if _, ok := siteSettings.Fields["core.RegionBlocks"].(*service.MapField). Fields["core.PrimaryNavigation"].(*service.ListField); ok { renderedNav, err := r.Render("blocks/core/Navigation", template.Context{ "Id": "core.PrimaryNavigation", "Links": prinav, }, userLocale, settings.Monsti.GetSiteTemplatesPath(site)) if err != nil { panic(fmt.Sprintf("Could not render navigation: %v", err)) } blocks["core.PrimaryNavigation"] = append(blocks["core.PrimaryNavigation"], renderedBlock{ Rendered: htmlT.HTML(renderedNav), }) } title := getNodeTitle(env.Node) ret, err := r.Render("master", template.Context{ "Site": site, "SiteSettings": siteSettings, "Page": template.Context{ "Node": env.Node, "PrimaryNav": prinav, // TODO DEPRECATED "SecondaryNav": secnav, // TODO DEPRECATED "EditView": env.Flags&EDIT_VIEW != 0, "Title": title, "Content": htmlT.HTML(content), "ShowSecondaryNav": len(secnav) > 0, // TODO DEPRECATED "Blocks": blocks, }, "Session": env.Session}, userLocale, settings.Monsti.GetSiteTemplatesPath(site)) if err != nil { panic(fmt.Sprintf( "Can't render master template for site %v: %v", site, err)) } return ret, mods }
// renderInMaster renders the content in the master template. func renderInMaster(r template.Renderer, content []byte, env masterTmplEnv, settings *settings, site util.SiteSettings, locale string, s *service.Session) string { if env.Flags&EDIT_VIEW != 0 { ret, err := r.Render("admin/master", template.Context{ "Site": site, "Page": template.Context{ "Title": env.Title, "Node": env.Node, "EditView": env.Flags&EDIT_VIEW != 0, "Content": htmlT.HTML(content), }, "Session": env.Session}, locale, settings.Monsti.GetSiteTemplatesPath(site.Name)) if err != nil { panic("Can't render: " + err.Error()) } return ret } firstDir := splitFirstDir(env.Node.Path) getNodeFn := func(path string) (*service.Node, error) { node, err := s.Monsti().GetNode(site.Name, path) return node, err } getChildrenFn := func(path string) ([]*service.Node, error) { return s.Monsti().GetChildren(site.Name, path) } prinav, err := getNav("/", path.Join("/", firstDir), env.Session.User == nil, getNodeFn, getChildrenFn) if err != nil { panic(fmt.Sprint("Could not get primary navigation: ", err)) } prinav.MakeAbsolute("/") var secnav navigation = nil if env.Node.Path != "/" { secnav, err = getNav(env.Node.Path, env.Node.Path, env.Session.User == nil, getNodeFn, getChildrenFn) if err != nil { panic(fmt.Sprint("Could not get secondary navigation: ", err)) } secnav.MakeAbsolute(env.Node.Path) } title := getNodeTitle(env.Node) ret, err := r.Render("master", template.Context{ "Site": site, "Page": template.Context{ "Node": env.Node, "PrimaryNav": prinav, "SecondaryNav": secnav, "EditView": env.Flags&EDIT_VIEW != 0, "Title": title, "Content": htmlT.HTML(content), "ShowSecondaryNav": len(secnav) > 0}, "Session": env.Session}, locale, settings.Monsti.GetSiteTemplatesPath(site.Name)) if err != nil { panic("Can't render: " + err.Error()) } return ret }
func renderContactForm(req *service.Request, session *service.Session) ( *service.RenderNodeRet, error) { m := session.Monsti() siteSettings, err := m.LoadSiteSettings(req.Site) if err != nil { return nil, fmt.Errorf("Could not get site settings: %v", err) } G, _, _, _ := gettext.DefaultLocales.Use("", siteSettings.Fields["core.Locale"].Value().(string)) node, err := m.GetNode(req.Site, req.NodePath) if err != nil { return nil, fmt.Errorf("Could not get contact form node: %v", err) } data := make(service.NestedMap) var dataFields []dataField form := htmlwidgets.NewForm(data) formFields := node.Fields["core.ContactFormFields"].(*service.ListField) for i, field := range formFields.Fields { combinedField := field.(*service.CombinedField) name := combinedField.Fields["Name"].Value().(string) required := combinedField.Fields["Required"].Value().(bool) fieldId := fmt.Sprintf("field_%d", i) dataFields = append(dataFields, dataField{fieldId, name}) data[fieldId] = "" innerFieldType := combinedField.Fields["Field"].(*service.DynamicTypeField).DynamicType var widget htmlwidgets.Widget switch innerFieldType { case "text": textWidget := &htmlwidgets.TextWidget{ValidationError: G("Required.")} if required { textWidget.MinLength = 1 } widget = textWidget case "textarea": areaWidget := &htmlwidgets.TextAreaWidget{ValidationError: G("Required.")} if required { areaWidget.MinLength = 1 } widget = areaWidget default: panic(fmt.Sprintf("Unknow inner field type <%v>", innerFieldType)) } form.AddWidget(widget, fieldId, name, "") } if len(formFields.Fields) == 0 { // Add default fields for backward compatibility data["Name"] = "" data["Email"] = "" data["Subject"] = "" data["Message"] = "" dataFields = []dataField{ {"Name", G("Name")}, {"Email", G("Email")}, {"Subject", G("Subject")}, {"Message", G("Message")}, } form.AddWidget(&htmlwidgets.TextWidget{MinLength: 1, ValidationError: G("Required.")}, "Name", G("Name"), "") form.AddWidget(&htmlwidgets.TextWidget{MinLength: 1, ValidationError: G("Required.")}, "Email", G("Email"), "") form.AddWidget(&htmlwidgets.TextWidget{MinLength: 1, ValidationError: G("Required.")}, "Subject", G("Subject"), "") form.AddWidget(&htmlwidgets.TextAreaWidget{MinLength: 1, ValidationError: G("Required.")}, "Message", G("Message"), "") } context := make(map[string]interface{}) switch req.Method { case "GET": if _, submitted := req.Form["submitted"]; submitted { context["Submitted"] = 1 } case "POST": if form.Fill(req.PostForm) { mail := gomail.NewMessage() mail.SetAddressHeader("From", siteSettings.StringValue("core.EmailAddress"), siteSettings.StringValue("core.EmailName")) mail.SetAddressHeader("To", siteSettings.StringValue("core.OwnerEmail"), siteSettings.StringValue("core.OwnerName")) // mail.SetAddressHeader("Reply-To", data.Email, data.Name) mail.SetHeader("Subject", "Contact form submission") var fieldValues string for _, v := range dataFields { fieldValues += fmt.Sprintf("%v: %v\n", v.Name, data[v.Id]) } body := fmt.Sprintf("%v\n\n%v", fmt.Sprintf(G("Received from contact form at %v"), siteSettings.StringValue("core.Title")), fieldValues) mail.SetBody("text/plain", body) mailer := gomail.NewCustomMailer("", nil, gomail.SetSendMail( m.SendMailFunc())) err := mailer.Send(mail) if err != nil { return nil, fmt.Errorf("Could not send mail: %v", err) } return &service.RenderNodeRet{ Redirect: &service.Redirect{ path.Dir(node.Path) + "/?submitted", http.StatusSeeOther}}, nil } default: return nil, fmt.Errorf("Request method not supported: %v", req.Method) } context["Form"] = form.RenderData() return &service.RenderNodeRet{Context: context}, err }
func initNodeTypes(settings *settings, session *service.Session, logger *log.Logger) error { G := func(in string) string { return in } pathType := service.NodeType{ Id: "core.Path", Hide: true, Name: i18n.GenLanguageMap(G("Path"), availableLocales), } if err := session.Monsti().RegisterNodeType(&pathType); err != nil { return fmt.Errorf("Could not register path node type: %v", err) } documentType := service.NodeType{ Id: "core.Document", AddableTo: []string{"."}, Name: i18n.GenLanguageMap(G("Document"), availableLocales), Fields: []*service.FieldConfig{ { Id: "core.Title", Required: true, Name: i18n.GenLanguageMap(G("Title"), availableLocales), Type: new(service.TextFieldType), }, { Id: "core.Description", Name: i18n.GenLanguageMap(G("Description"), availableLocales), Type: new(service.TextFieldType), }, { Id: "core.Thumbnail", Name: i18n.GenLanguageMap(G("Thumbnail"), availableLocales), Type: new(service.RefFieldType), }, { Id: "core.Body", Required: true, Name: i18n.GenLanguageMap(G("Body"), availableLocales), Type: new(service.HTMLFieldType), }, }, } if err := session.Monsti().RegisterNodeType(&documentType); err != nil { return fmt.Errorf("Could not register document node type: %v", err) } fileType := service.NodeType{ Id: "core.File", AddableTo: []string{"."}, Name: i18n.GenLanguageMap(G("File"), availableLocales), Fields: []*service.FieldConfig{ {Id: "core.Title"}, { Id: "core.File", Required: true, Name: i18n.GenLanguageMap(G("File"), availableLocales), Type: new(service.FileFieldType), }, }, } if err := session.Monsti().RegisterNodeType(&fileType); err != nil { return fmt.Errorf("Could not register file node type: %v", err) } imageType := service.NodeType{ Id: "core.Image", Hide: true, AddableTo: []string{"."}, Name: i18n.GenLanguageMap(G("Image"), availableLocales), Fields: []*service.FieldConfig{ {Id: "core.Title"}, {Id: "core.File"}, }, } if err := session.Monsti().RegisterNodeType(&imageType); err != nil { return fmt.Errorf("Could not register image node type: %v", err) } return nil }