func renderContactForm(c *reqContext, context template.Context, formValues url.Values, h *nodeHandler) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.SiteSettings.Fields["core.Locale"].Value().(string)) m := c.Serv.Monsti() data := contactFormData{} form := htmlwidgets.NewForm(&data) 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"), "") switch c.Req.Method { case "GET": if _, submitted := formValues["submitted"]; submitted { context["Submitted"] = 1 } case "POST": if form.Fill(formValues) { mail := gomail.NewMessage() mail.SetAddressHeader("From", c.SiteSettings.StringValue("core.EmailAddress"), c.SiteSettings.StringValue("core.EmailName")) mail.SetAddressHeader("To", c.SiteSettings.StringValue("core.OwnerEmail"), c.SiteSettings.StringValue("core.OwnerName")) mail.SetAddressHeader("Reply-To", data.Email, data.Name) mail.SetHeader("Subject", data.Subject) body := fmt.Sprintf("%v\n%v\n\n%v", fmt.Sprintf(G("Received from contact form at %v"), c.SiteSettings.StringValue("core.Title")), fmt.Sprintf(G("Name: %v | Email: %v"), data.Name, data.Email), data.Message) mail.SetBody("text/plain", body) mailer := gomail.NewCustomMailer("", nil, gomail.SetSendMail( m.SendMailFunc())) err := mailer.Send(mail) if err != nil { return fmt.Errorf("Could not send mail: %v", err) } http.Redirect(c.Res, c.Req, path.Dir(c.Node.Path)+"/?submitted", http.StatusSeeOther) return nil } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } context["Form"] = form.RenderData() return nil }
// Remove handles remove requests. func (h *nodeHandler) Remove(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) data := removeFormData{} form := htmlwidgets.NewForm(&data) form.AddWidget(new(htmlwidgets.HiddenWidget), "Confirm", G("Confirm"), "") switch c.Req.Method { case "GET": data.Confirm = "ok" case "POST": if form.Fill(c.Req.Form) && data.Confirm == "ok" { if err := c.Serv.Monsti().RemoveNode(c.Site, c.Node.Path); err != nil { return fmt.Errorf("Could not remove node: %v", err) } http.Redirect(c.Res, c.Req, path.Dir(c.Node.Path), http.StatusSeeOther) return nil } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } body, err := h.Renderer.Render("actions/removeform", mtemplate.Context{ "Form": form.RenderData(), "Node": c.Node}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { panic("Can't render node remove formular: " + err.Error()) } env := masterTmplEnv{Node: c.Node, Session: c.UserSession, Flags: EDIT_VIEW, Title: G("Remove node")} rendered, _ := renderInMaster(h.Renderer, []byte(body), env, h.Settings, c.Site, c.SiteSettings, c.UserSession.Locale, c.Serv) c.Res.Write(rendered) return nil }
// Add handles add requests. func (h *nodeHandler) Add(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) data := addFormData{New: "1"} nodeTypeOptions := []htmlwidgets.SelectOption{} nodeTypes, err := c.Serv.Monsti().GetAddableNodeTypes(c.Site.Name, c.Node.Type.Id) if err != nil { return fmt.Errorf("Could not get addable node types: %v", err) } for _, id := range nodeTypes { nodeType, err := c.Serv.Monsti().GetNodeType(id) if err != nil { return fmt.Errorf("Could not get node type: %v", err) } nodeTypeOptions = append(nodeTypeOptions, htmlwidgets.SelectOption{nodeType.Id, nodeType.GetLocalName(c.UserSession.Locale), false}) } form := htmlwidgets.NewForm(&data) form.AddWidget(&htmlwidgets.SelectWidget{Options: nodeTypeOptions}, "NodeType", G("Content type"), "") form.AddWidget(new(htmlwidgets.HiddenWidget), "New", "", "") form.Action = path.Join(c.Node.Path, "@@edit") body, err := h.Renderer.Render("actions/addform", mtemplate.Context{ "Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site.Name)) if err != nil { return fmt.Errorf("Can't render node add formular: %v", err) } env := masterTmplEnv{Node: c.Node, Session: c.UserSession, Flags: EDIT_VIEW, Title: G("Add content")} fmt.Fprint(c.Res, renderInMaster(h.Renderer, []byte(body), env, h.Settings, *c.Site, c.UserSession.Locale, c.Serv)) return nil }
// Login handles login requests. func (h *nodeHandler) Login(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) data := loginFormData{} form := htmlwidgets.NewForm(&data) form.AddWidget(new(htmlwidgets.TextWidget), "Login", G("User name"), "") form.AddWidget(new(htmlwidgets.PasswordWidget), "Password", G("Password"), "") switch c.Req.Method { case "GET": case "POST": if form.Fill(c.Req.Form) { user, err := getUser(data.Login, h.Settings.Monsti.GetSiteDataPath(c.Site)) if err != nil { return fmt.Errorf("Could not get user: %v", err) } if user != nil && passwordEqual(user.Password, data.Password) { c.Session.Values["login"] = user.Login c.Session.Save(c.Req, c.Res) http.Redirect(c.Res, c.Req, c.Node.Path+"/", http.StatusSeeOther) return nil } form.AddError("", G("Wrong login or password.")) } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } data.Password = "" body, err := h.Renderer.Render("actions/loginform", template.Context{ "Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { return fmt.Errorf("Can't render login form: %v", err) } env := masterTmplEnv{Node: c.Node, Session: c.UserSession, Title: G("Login"), Description: G("Login with your site account."), Flags: EDIT_VIEW} rendered, _ := renderInMaster(h.Renderer, []byte(body), env, h.Settings, c.Site, c.SiteSettings, c.UserSession.Locale, c.Serv) c.Res.Write(rendered) return nil }
func renderContactForm(c *reqContext, context template.Context, formValues url.Values, h *nodeHandler) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.Site.Locale) data := contactFormData{} form := htmlwidgets.NewForm(&data) 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"), "") switch c.Req.Method { case "GET": if _, submitted := formValues["submitted"]; submitted { context["Submitted"] = 1 } case "POST": if form.Fill(formValues) { mail := mimemail.Mail{ From: mimemail.Address{data.Name, data.Email}, Subject: data.Subject, Body: []byte(data.Message)} site := h.Settings.Monsti.Sites[c.Site.Name] owner := mimemail.Address{site.Owner.Name, site.Owner.Email} mail.To = []mimemail.Address{owner} err := c.Serv.Monsti().SendMail(&mail) if err != nil { return fmt.Errorf("Could not send mail: %v", err) } http.Redirect(c.Res, c.Req, path.Dir(c.Node.Path)+"/?submitted", http.StatusSeeOther) return nil } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } context["Form"] = form.RenderData() return nil }
// ChangePassword allows to change the user's password. func (h *nodeHandler) ChangePassword(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) authenticated := c.UserSession.User != nil data := changePasswordFormData{} form := htmlwidgets.NewForm(&data) if authenticated { form.AddWidget(&htmlwidgets.PasswordWidget{}, "OldPassword", G("Old Password"), "") } form.AddWidget(&htmlwidgets.PasswordWidget{ VerifyLabel: G("Please repeat the password."), VerifyError: G("Passwords do not match."), }, "Password", G("New Password"), "") var token string tokenInvalid := false c.Req.ParseForm() var user *service.User if !authenticated { if tokens, ok := c.Req.Form["token"]; ok { token = tokens[0] } if len(token) == 0 { http.Redirect(c.Res, c.Req, "@@login", http.StatusSeeOther) return nil } getUserFn := func(login string) (*service.User, error) { return getUser(login, h.Settings.Monsti.GetSiteDataPath(c.Site.Name)) } var err error user, err = verifyRequestPasswordToken( c.Site.Name, getUserFn, c.Site.PasswordTokenKey, token) if err != nil { return fmt.Errorf("Could not verify request password token: %v", err) } if user == nil { tokenInvalid = true } } else { user = c.UserSession.User } changed := false switch c.Req.Method { case "GET": if _, ok := c.Req.Form["changed"]; ok { changed = true } case "POST": if authenticated || !tokenInvalid { if form.Fill(c.Req.Form) { changePassword := true if authenticated { changePassword = passwordEqual(user.Password, data.OldPassword) if !changePassword { form.AddError("Password", G("Wrong password.")) } } if changePassword { hashed, err := bcrypt.GenerateFromPassword([]byte(data.Password), 0) if err != nil { return fmt.Errorf("Could not hash user password: %v", err) } user.PasswordChanged = time.Now().UTC() user.Password = string(hashed) err = writeUser(user, h.Settings.Monsti.GetSiteDataPath(c.Site.Name)) if err != nil { return fmt.Errorf("Could not change user password: %v", err) } http.Redirect(c.Res, c.Req, "@@change-password?changed", http.StatusSeeOther) return nil } } } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } body, err := h.Renderer.Render("actions/change_password", template.Context{ "TokenInvalid": tokenInvalid, "Changed": changed, "Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site.Name)) if err != nil { return fmt.Errorf("Can't render ChangePassword form: %v", err) } env := masterTmplEnv{ Node: c.Node, Session: c.UserSession, Title: G("Change password"), Flags: EDIT_VIEW} fmt.Fprint(c.Res, renderInMaster(h.Renderer, []byte(body), env, h.Settings, *c.Site, c.UserSession.Locale, c.Serv)) return nil }
// RequestPasswordToken sends the user a token to be able to change // the login password. func (h *nodeHandler) RequestPasswordToken(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) data := requestPasswordTokenFormData{} form := htmlwidgets.NewForm(&data) form.AddWidget(new(htmlwidgets.TextWidget), "User", G("Login"), "") sent := false c.Req.ParseForm() switch c.Req.Method { case "GET": if _, ok := c.Req.Form["sent"]; ok { sent = true } case "POST": if form.Fill(c.Req.Form) { user, err := getUser(data.User, h.Settings.Monsti.GetSiteDataPath(c.Site.Name)) if err != nil { return fmt.Errorf("Could not get user: %v", err) } if user != nil { site := h.Settings.Monsti.Sites[c.Site.Name] link := getRequestPasswordToken(c.Site.Name, data.User, site.PasswordTokenKey) mail := mimemail.Mail{ From: mimemail.Address{site.EmailName, site.EmailAddress}, Subject: G("Password request"), Body: []byte(fmt.Sprintf(`Hello, someone, possibly you, requested a new password for your account %v at "%v". To change your password, visit the following link within 24 hours. If you did not request a new password, you may ignore this email. %v This is an automatically generated email. Please don't reply to it. `, data.User, site.Title, site.BaseURL+"/@@change-password?token="+link))} mail.To = []mimemail.Address{mimemail.Address{user.Login, user.Email}} err := c.Serv.Monsti().SendMail(&mail) if err != nil { return fmt.Errorf("Could not send mail: %v", err) } http.Redirect(c.Res, c.Req, "@@request-password-token?sent", http.StatusSeeOther) return nil } else { form.AddError("User", G("Unkown user.")) } } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } body, err := h.Renderer.Render("actions/request_password_token_form", template.Context{ "Sent": sent, "Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site.Name)) if err != nil { return fmt.Errorf("Can't render login form: %v", err) } env := masterTmplEnv{ Node: c.Node, Session: c.UserSession, Title: G("Request new password"), Flags: EDIT_VIEW} fmt.Fprint(c.Res, renderInMaster(h.Renderer, []byte(body), env, h.Settings, *c.Site, c.UserSession.Locale, c.Serv)) return nil }
// RequestPasswordToken sends the user a token to be able to change // the login password. func (h *nodeHandler) RequestPasswordToken(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) data := requestPasswordTokenFormData{} form := htmlwidgets.NewForm(&data) form.AddWidget(new(htmlwidgets.TextWidget), "User", G("Login"), "") sent := false switch c.Req.Method { case "GET": if _, ok := c.Req.Form["sent"]; ok { sent = true } case "POST": if form.Fill(c.Req.Form) { user, err := getUser(data.User, h.Settings.Monsti.GetSiteDataPath(c.Site)) if err != nil { return fmt.Errorf("Could not get user: %v", err) } if user != nil { link := getRequestPasswordToken(c.Site, data.User, c.SiteSettings.StringValue("core.PasswordTokenKey")) // Send email to user mail := gomail.NewMessage() mail.SetAddressHeader("From", c.SiteSettings.StringValue("core.EmailAddress"), c.SiteSettings.StringValue("core.EmailName")) mail.SetAddressHeader("To", user.Email, user.Login) mail.SetHeader("Subject", G("Password request")) body, err := h.Renderer.Render("mails/change_password", template.Context{ "SiteSettings": c.SiteSettings, "Account": data.User, "ChangeLink": c.SiteSettings.StringValue("core.BaseURL") + "/@@change-password?token=" + link, }, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { return fmt.Errorf("Can't render password change mail: %v", err) } mail.SetBody("text/plain", string(body)) mailer := gomail.NewCustomMailer("", nil, gomail.SetSendMail( c.Serv.Monsti().SendMailFunc())) err = mailer.Send(mail) if err != nil { return fmt.Errorf("Could not send mail: %v", err) } http.Redirect(c.Res, c.Req, "@@request-password-token?sent", http.StatusSeeOther) return nil } else { form.AddError("User", G("Unkown user.")) } } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } body, err := h.Renderer.Render("actions/request_password_token_form", template.Context{ "Sent": sent, "Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { return fmt.Errorf("Can't render login form: %v", err) } env := masterTmplEnv{ Node: c.Node, Session: c.UserSession, Title: G("Request new password"), Flags: EDIT_VIEW} rendered, _ := renderInMaster(h.Renderer, []byte(body), env, h.Settings, c.Site, c.SiteSettings, c.UserSession.Locale, c.Serv) c.Res.Write(rendered) return nil }
func (h *nodeHandler) SettingsAction(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) m := c.Serv.Monsti() settings, err := m.LoadSiteSettings(c.Site) if err != nil { return fmt.Errorf("Could not load site settings: %v", err) } formData := settingsFormData{} formData.Fields = make(service.NestedMap) form := htmlwidgets.NewForm(&formData) for _, field := range settings.FieldConfigs { if field.Hidden { continue } formData.Fields.Set(field.Id, settings.Fields[field.Id].FormData()) widget := settings.Fields[field.Id].FormWidget( c.UserSession.Locale, field) form.AddWidget(widget, "Fields."+field.Id, field.Name.Get(c.UserSession.Locale), "") } switch c.Req.Method { case "GET": case "POST": if form.Fill(c.Req.Form) { for _, field := range settings.FieldConfigs { if !field.Hidden { settings.Fields[field.Id].FromFormData(formData.Fields.Get(field.Id)) } } if err := m.WriteSiteSettings(c.Site, settings); err != nil { return fmt.Errorf("Could not update settings: %v", err) } /* err = m.MarkDep( c.Site.Name, service.CacheDep{Settings: path.Clean(settings.Path)}) if err != nil { return fmt.Errorf("Could not mark settings: %v", err) } */ http.Redirect(c.Res, c.Req, path.Join(c.Node.Path, "@@settings?saved=1"), http.StatusSeeOther) return nil } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } rendered, err := h.Renderer.Render("actions/settings", mtemplate.Context{ "Form": form.RenderData(), "Saved": c.Req.FormValue("saved"), }, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { return fmt.Errorf("Could not render settings template: %v", err) } content, _ := renderInMaster(h.Renderer, []byte(rendered), masterTmplEnv{Node: c.Node, Session: c.UserSession, Title: G("Settings"), Flags: EDIT_VIEW}, h.Settings, c.Site, c.SiteSettings, c.UserSession.Locale, c.Serv) c.Res.Write(content) return nil }
// EditNode handles node edits. func (h *nodeHandler) Edit(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) if err := c.Req.ParseMultipartForm(1024 * 1024); err != nil { if err != http.ErrNotMultipart { return fmt.Errorf("Could not parse form: %v", err) } } nodeType := c.Node.Type newNode := c.Req.Form.Get("NodeType") != "" if newNode { var err error nodeType, err = c.Serv.Monsti().GetNodeType(c.Req.FormValue("NodeType")) if err != nil { return fmt.Errorf("Could not get node type to add %q: %v", c.Req.FormValue("new"), err) } // TODO Check if node type may be added to this node } env := masterTmplEnv{Node: c.Node, Session: c.UserSession} if c.Action == service.EditAction { if newNode { env.Title = G("Add a new node") } else { env.Title = G("Edit node") } env.Flags = EDIT_VIEW } formData := editFormData{} formData.Fields = make(service.NestedMap) if newNode { formData.NodeType = nodeType.Id formData.Node.Type = nodeType err := formData.Node.InitFields(c.Serv.Monsti(), c.Site) if err != nil { return fmt.Errorf("Could not init node fields: %v", err) } formData.Node.PublishTime = time.Now().UTC() formData.Node.Public = true } else { formData.Node = *c.Node } form := htmlwidgets.NewForm(&formData) form.AddWidget(new(htmlwidgets.HiddenWidget), "NodeType", "", "") if !nodeType.Hide { form.AddWidget(new(htmlwidgets.BoolWidget), "Node.Hide", G("Hide"), G("Don't show node in navigation.")) } form.AddWidget(new(htmlwidgets.BoolWidget), "Node.Public", G("Public"), G("Is the node accessible by every visitor?")) location, err := time.LoadLocation(c.SiteSettings.StringValue("core.Timezone")) if err != nil { location = time.UTC } form.AddWidget(&htmlwidgets.TimeWidget{ Location: location}, "Node.PublishTime", G("Publish time"), G("The node won't be accessible to the public until it is published.")) if newNode || c.Node.Name() != "" { form.AddWidget(&htmlwidgets.TextWidget{ Regexp: `^[-\w.]+$`, ValidationError: G("Please enter a name consisting only of the characters A-Z, a-z, 0-9, '.', and '-'")}, "Name", G("Name"), G("The name as it should appear in the URL.")) } if !newNode { formData.Name = c.Node.Name() } fileFields := make([]string, 0) for _, field := range nodeType.Fields { if field.Hidden { continue } formData.Fields.Set(field.Id, formData.Node.Fields[field.Id].FormData()) widget := formData.Node.Fields[field.Id].FormWidget( c.UserSession.Locale, field) form.AddWidget(widget, "Fields."+field.Id, field.Name.Get(c.UserSession.Locale), "") if _, ok := field.Type.(*service.FileFieldType); ok { fileFields = append(fileFields, field.Id) } } switch c.Req.Method { case "GET": case "POST": if form.Fill(c.Req.Form) { node := formData.Node node.Type = nodeType pathPrefix := node.GetPathPrefix() oldPath := c.Node.Path parentPath := c.Node.GetParentPath() if newNode { parentPath = c.Node.Path } node.Path = path.Join(parentPath, pathPrefix, formData.Name) renamed := !newNode && c.Node.Name() != "" && oldPath != node.Path writeNode := true if newNode || renamed { existing, err := c.Serv.Monsti().GetNode(c.Site, node.Path) if err != nil { return fmt.Errorf("Could not fetch possibly existing node: %v", err) } if existing != nil { form.AddError("Name", G("A node with this name does already exist")) writeNode = false } if err = node.InitFields(c.Serv.Monsti(), c.Site); err != nil { return fmt.Errorf("Could not init node fields: %v", err) } } // Check file format for image nodes. if nodeType.Id == "core.Image" { file, _, err := c.Req.FormFile("Fields.core.File") if err == nil { content, err := ioutil.ReadAll(file) if err != nil { return fmt.Errorf("Could not read multipart file: %v", err) } if _, _, err := image.Decode(bytes.NewBuffer(content)); err != nil { form.AddError("Fields.core.File", G("Unsupported image format. Try GIF, JPEG, or PNG.")) writeNode = false } } } if writeNode { if renamed { err := c.Serv.Monsti().RenameNode(c.Site, c.Node.Path, node.Path) if err != nil { return fmt.Errorf("Could not move node: %v", err) } } for _, field := range nodeType.Fields { if !field.Hidden { node.Fields[field.Id].FromFormData(formData.Fields.Get(field.Id)) } } err := c.Serv.Monsti().WriteNode(c.Site, node.Path, &node) if err != nil { return fmt.Errorf("Could not update node: %v", err) } // Save any attached files if len(fileFields) > 0 && c.Req.MultipartForm != nil { for _, name := range fileFields { file, _, err := c.Req.FormFile("Fields." + name) if err == nil { content, err := ioutil.ReadAll(file) if err != nil { return fmt.Errorf("Could not read multipart file: %v", err) } if err = c.Serv.Monsti().WriteNodeData(c.Site, node.Path, "__file_"+name, content); err != nil { return fmt.Errorf("Could not save file: %v", err) } } } } http.Redirect(c.Res, c.Req, node.Path+"/", http.StatusSeeOther) err = c.Serv.Monsti().MarkDep( c.Site, service.CacheDep{Node: path.Clean(node.Path)}) if err != nil { return fmt.Errorf("Could not mark node: %v", err) } return nil } } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } rendered, err := h.Renderer.Render("edit", mtemplate.Context{"Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site)) if err != nil { return fmt.Errorf("Could not render template: %v", err) } content, _ := renderInMaster(h.Renderer, []byte(rendered), env, h.Settings, c.Site, c.SiteSettings, c.UserSession.Locale, c.Serv) c.Res.Write(content) return nil }
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 }
// EditNode handles node edits. func (h *nodeHandler) Edit(c *reqContext) error { G, _, _, _ := gettext.DefaultLocales.Use("", c.UserSession.Locale) h.Log.Printf("(%v) %v %v", c.Site.Name, c.Req.Method, c.Req.URL.Path) if err := c.Req.ParseMultipartForm(1024 * 1024); err != nil { if err != http.ErrNotMultipart { return fmt.Errorf("Could not parse form: %v", err) } } nodeType := c.Node.Type newNode := len(c.Req.FormValue("NodeType")) > 0 if newNode { var err error nodeType, err = c.Serv.Monsti().GetNodeType(c.Req.FormValue("NodeType")) if err != nil { return fmt.Errorf("Could not get node type to add %q: %v", c.Req.FormValue("new"), err) } // TODO Check if node type may be added to this node } env := masterTmplEnv{Node: c.Node, Session: c.UserSession} if c.Action == service.EditAction { if newNode { env.Title = fmt.Sprintf(G("Add %v to \"%s\""), nodeType.GetLocalName(c.UserSession.Locale), c.Node.Path) } else { env.Title = fmt.Sprintf(G("Edit \"%s\""), c.Node.Path) } env.Flags = EDIT_VIEW } formData := editFormData{} formData.Fields = make(util.NestedMap) if newNode { formData.NodeType = nodeType.Id formData.Node.Type = nodeType err := formData.Node.InitFields(c.Serv.Monsti(), c.Site.Name) if err != nil { return fmt.Errorf("Could not init node fields: %v", err) } formData.Node.PublishTime = time.Now().UTC() formData.Node.Public = true } else { formData.Node = *c.Node } form := htmlwidgets.NewForm(&formData) form.AddWidget(new(htmlwidgets.HiddenWidget), "NodeType", "", "") if !nodeType.Hide { form.AddWidget(new(htmlwidgets.BoolWidget), "Node.Hide", G("Hide"), G("Don't show node in navigation.")) } form.AddWidget(new(htmlwidgets.IntegerWidget), "Node.Order", G("Order"), G("Order in navigation or listings (lower numbered entries appear first).")) form.AddWidget(new(htmlwidgets.BoolWidget), "Node.Public", G("Public"), G("Is the node accessible by every visitor?")) var timezone string err := c.Serv.Monsti().GetSiteConfig(c.Site.Name, "core.timezone", &timezone) if err != nil { return fmt.Errorf("Could not get timezone: %v", err) } location, err := time.LoadLocation(timezone) if err != nil { location = time.UTC } form.AddWidget(&htmlwidgets.TimeWidget{ Location: location}, "Node.PublishTime", G("Publish time"), G("The node won't be accessible to the public until it is published.")) if newNode || c.Node.Name() != "" { form.AddWidget(&htmlwidgets.TextWidget{ Regexp: `^[-\w]+$`, ValidationError: G("Please enter a name consisting only of the characters A-Z, a-z, 0-9 and '-'")}, "Name", G("Name"), G("The name as it should appear in the URL.")) } if !newNode { formData.Name = c.Node.Name() } fileFields := make([]string, 0) nodeFields := nodeType.Fields if !newNode { nodeFields = append(nodeFields, c.Node.LocalFields...) } for _, field := range nodeFields { formData.Node.GetField(field.Id).ToFormField(form, formData.Fields, field, c.UserSession.Locale) if field.Type == "File" { fileFields = append(fileFields, field.Id) } } switch c.Req.Method { case "GET": case "POST": if len(c.Req.FormValue("New")) == 0 && form.Fill(c.Req.Form) { node := formData.Node node.Type = nodeType pathPrefix := node.GetPathPrefix() oldPath := c.Node.Path parentPath := c.Node.GetParentPath() if newNode { parentPath = c.Node.Path } node.Path = path.Join(parentPath, pathPrefix, formData.Name) renamed := !newNode && c.Node.Name() != "" && oldPath != node.Path writeNode := true if newNode || renamed { existing, err := c.Serv.Monsti().GetNode(c.Site.Name, node.Path) if err != nil { return fmt.Errorf("Could not fetch possibly existing node: %v", err) } if existing != nil { form.AddError("Name", G("A node with this name does already exist")) writeNode = false } if err = node.InitFields(c.Serv.Monsti(), c.Site.Name); err != nil { return fmt.Errorf("Could not init node fields: %v", err) } } if writeNode { if renamed { err := c.Serv.Monsti().RenameNode(c.Site.Name, c.Node.Path, node.Path) if err != nil { return fmt.Errorf("Could not move node: ", err) } } for _, field := range nodeFields { node.GetField(field.Id).FromFormField(formData.Fields, field) } err := c.Serv.Monsti().WriteNode(c.Site.Name, node.Path, &node) if err != nil { return fmt.Errorf("Could not update node: ", err) } if len(fileFields) > 0 && c.Req.MultipartForm != nil { for _, name := range fileFields { file, _, err := c.Req.FormFile("Fields." + name) if err == nil { content, err := ioutil.ReadAll(file) if err != nil { return fmt.Errorf("Could not read multipart file: %v", err) } if err = c.Serv.Monsti().WriteNodeData(c.Site.Name, node.Path, "__file_"+name, content); err != nil { return fmt.Errorf("Could not save file: %v", err) } } } } http.Redirect(c.Res, c.Req, node.Path+"/", http.StatusSeeOther) return nil } } default: return fmt.Errorf("Request method not supported: %v", c.Req.Method) } rendered, err := h.Renderer.Render("edit", mtemplate.Context{"Form": form.RenderData()}, c.UserSession.Locale, h.Settings.Monsti.GetSiteTemplatesPath(c.Site.Name)) if err != nil { return fmt.Errorf("Could not render template: %v", err) } content := []byte(renderInMaster(h.Renderer, []byte(rendered), env, h.Settings, *c.Site, c.UserSession.Locale, c.Serv)) err = c.Session.Save(c.Req, c.Res) if err != nil { return fmt.Errorf("Could not save user session: %v", err) } c.Res.Write(content) return nil }