From 57c78bc603da7f9d4d329128c644d94550f628ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 16 Jul 2015 10:44:55 +0200 Subject: [PATCH 01/24] feat(invite): began work on invite users dialog, #2353 --- .../features/admin/partials/edit_user.html | 22 +++--- .../features/dashboard/unsavedChangesSrv.js | 15 +--- public/app/features/org/orgUsersCtrl.js | 9 +++ .../app/features/org/partials/orgUsers.html | 79 ++++++++----------- .../features/profile/partials/profile.html | 4 +- public/app/partials/confirm_modal.html | 2 +- public/app/partials/invite_users.html | 55 +++++++++++++ public/app/services/alertSrv.js | 2 +- public/app/services/utilSrv.js | 1 + public/css/less/bootswatch.dark.less | 2 - public/css/less/bootswatch.light.less | 13 --- public/css/less/gfbox.less | 8 +- public/css/less/grafana.less | 24 ++++-- public/css/less/variables.dark.less | 6 +- public/css/less/variables.light.less | 19 +++-- public/vendor/bootstrap/less/type.less | 6 +- 16 files changed, 152 insertions(+), 115 deletions(-) create mode 100644 public/app/partials/invite_users.html diff --git a/public/app/features/admin/partials/edit_user.html b/public/app/features/admin/partials/edit_user.html index a1a4cb989cd..674f1cf4b0d 100644 --- a/public/app/features/admin/partials/edit_user.html +++ b/public/app/features/admin/partials/edit_user.html @@ -9,7 +9,7 @@

- User details + Edit User

@@ -17,7 +17,7 @@
@@ -72,7 +72,7 @@ Confirm Password
  • - +
  • @@ -80,7 +80,7 @@
    - +
    diff --git a/public/css/less/grafana.less b/public/css/less/grafana.less index 2ec921cb267..163cb7d792e 100644 --- a/public/css/less/grafana.less +++ b/public/css/less/grafana.less @@ -1,3 +1,4 @@ +@import "type.less"; @import "login.less"; @import "submenu.less"; @import "graph.less"; @@ -277,11 +278,6 @@ } } -.long-tag { - width: 90%; - margin: 0 auto; -} - .confirm-modal { max-width: 500px; @@ -371,7 +367,7 @@ } .body-copy-emphasis { - color: @headingsColor; + color: @headingsColor; } .signup-page-container { diff --git a/public/css/less/type.less b/public/css/less/type.less new file mode 100644 index 00000000000..1d833792484 --- /dev/null +++ b/public/css/less/type.less @@ -0,0 +1,247 @@ +// +// Typography +// -------------------------------------------------- + + +// Body text +// ------------------------- + +p { + margin: 0 0 @baseLineHeight / 2; +} +.lead { + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + font-weight: 200; + line-height: @baseLineHeight * 1.5; +} + + +// Emphasis & misc +// ------------------------- + +// Ex: 14px base font * 85% = about 12px +small { font-size: 85%; } + +strong { font-weight: 500; } +em { font-style: italic; color: @headingsColor; } +cite { font-style: normal; } + +// Utility classes +.muted { color: @grayLight; } +a.muted:hover, +a.muted:focus { color: darken(@grayLight, 10%); } + +.text-warning { color: @warningText; } +a.text-warning:hover, +a.text-warning:focus { color: darken(@warningText, 10%); } + +.text-error { color: @errorText; } +a.text-error:hover, +a.text-error:focus { color: darken(@errorText, 10%); } + +.text-info { color: @infoText; } +a.text-info:hover, +a.text-info:focus { color: darken(@infoText, 10%); } + +.text-success { color: @successText; } +a.text-success:hover, +a.text-success:focus { color: darken(@successText, 10%); } + +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6 { + margin: (@baseLineHeight / 2) 0; + font-family: @headingsFontFamily; + font-weight: @headingsFontWeight; + line-height: @baseLineHeight; + color: @headingsColor; + text-rendering: optimizelegibility; // Fix the character spacing for headings + small { + font-weight: normal; + line-height: 1; + color: @grayLight; + } +} + +h1, +h2, +h3 { line-height: @baseLineHeight * 2; } + +h1 { font-size: @baseFontSize * 2.00; } // ~38px +h2 { font-size: @baseFontSize * 1.75; } // ~32px +h3 { font-size: @baseFontSize * 1.50; } // ~24px +h4 { font-size: @baseFontSize * 1.25; } // ~18px +h5 { font-size: @baseFontSize; } +h6 { font-size: @baseFontSize * 0.85; } // ~12px + +h1 small { font-size: @baseFontSize * 1.75; } // ~24px +h2 small { font-size: @baseFontSize * 1.25; } // ~18px +h3 small { font-size: @baseFontSize; } +h4 small { font-size: @baseFontSize; } + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: (@baseLineHeight / 2) - 1; + margin: @baseLineHeight 0 (@baseLineHeight * 1.5); + border-bottom: 1px solid @grayLighter; +} + + + +// Lists +// -------------------------------------------------- + +// Unordered and Ordered lists +ul, ol { + padding: 0; + margin: 0 0 @baseLineHeight / 2 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: @baseLineHeight; +} + +// Remove default list styles +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +// Single-line list items +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; + > li { + display: inline-block; + .ie7-inline-block(); + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-bottom: @baseLineHeight; +} +dt, +dd { + line-height: @baseLineHeight; +} +dt { + font-weight: bold; +} +dd { + margin-left: @baseLineHeight / 2; +} +// Horizontal layout (like forms) +.dl-horizontal { + .clearfix(); // Ensure dl clears floats if empty dd elements present + dt { + float: left; + width: @horizontalComponentOffset - 20; + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @horizontalComponentOffset; + } +} + +// MISC +// ---- + +// Horizontal rules +hr { + margin: @baseLineHeight 0; + border: 0; + border-top: 1px solid @hrBorder; + border-bottom: 1px solid @white; +} + +// Abbreviations and acronyms +abbr[title], +// Added data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @grayLight; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +// Blockquotes +blockquote { + padding: 0 0 0 15px; + margin: 0 0 @baseLineHeight; + border-left: 5px solid @grayLighter; + p { + margin-bottom: 0; + font-size: @baseFontSize * 1.25; + font-weight: 300; + line-height: 1.25; + } + small { + display: block; + line-height: @baseLineHeight; + color: @grayLight; + &:before { + content: '\2014 \00A0'; + } + } + + // Float right with text-align: right + &.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @grayLighter; + border-left: 0; + p, + small { + text-align: right; + } + small { + &:before { + content: ''; + } + &:after { + content: '\00A0 \2014'; + } + } + } +} + +// Quotes +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +// Addresses +address { + display: block; + margin-bottom: @baseLineHeight; + font-style: normal; + line-height: @baseLineHeight; +} diff --git a/public/vendor/bootstrap/less/bootstrap.less b/public/vendor/bootstrap/less/bootstrap.less index a22c4b24756..758dbde9848 100644 --- a/public/vendor/bootstrap/less/bootstrap.less +++ b/public/vendor/bootstrap/less/bootstrap.less @@ -22,7 +22,6 @@ // Base CSS @import "type.less"; -@import "code.less"; @import "forms.less"; @import "tables.less"; diff --git a/public/vendor/bootstrap/less/type.less b/public/vendor/bootstrap/less/type.less index 109b09de44d..8b137891791 100644 --- a/public/vendor/bootstrap/less/type.less +++ b/public/vendor/bootstrap/less/type.less @@ -1,247 +1 @@ -// -// Typography -// -------------------------------------------------- - -// Body text -// ------------------------- - -p { - margin: 0 0 @baseLineHeight / 2; -} -.lead { - margin-bottom: @baseLineHeight; - font-size: @baseFontSize * 1.5; - font-weight: 200; - line-height: @baseLineHeight * 1.5; -} - - -// Emphasis & misc -// ------------------------- - -// Ex: 14px base font * 85% = about 12px -small { font-size: 85%; } - -strong { font-weight: bold; } -em { font-style: italic; } -cite { font-style: normal; } - -// Utility classes -.muted { color: @grayLight; } -a.muted:hover, -a.muted:focus { color: darken(@grayLight, 10%); } - -.text-warning { color: @warningText; } -a.text-warning:hover, -a.text-warning:focus { color: darken(@warningText, 10%); } - -.text-error { color: @errorText; } -a.text-error:hover, -a.text-error:focus { color: darken(@errorText, 10%); } - -.text-info { color: @infoText; } -a.text-info:hover, -a.text-info:focus { color: darken(@infoText, 10%); } - -.text-success { color: @successText; } -a.text-success:hover, -a.text-success:focus { color: darken(@successText, 10%); } - -.text-left { text-align: left; } -.text-right { text-align: right; } -.text-center { text-align: center; } - - -// Headings -// ------------------------- - -h1, h2, h3, h4, h5, h6 { - margin: (@baseLineHeight / 2) 0; - font-family: @headingsFontFamily; - font-weight: @headingsFontWeight; - line-height: @baseLineHeight; - color: @headingsColor; - text-rendering: optimizelegibility; // Fix the character spacing for headings - small { - font-weight: normal; - line-height: 1; - color: @grayLight; - } -} - -h1, -h2, -h3 { line-height: @baseLineHeight * 2; } - -h1 { font-size: @baseFontSize * 2.00; } // ~38px -h2 { font-size: @baseFontSize * 1.75; } // ~32px -h3 { font-size: @baseFontSize * 1.50; } // ~24px -h4 { font-size: @baseFontSize * 1.25; } // ~18px -h5 { font-size: @baseFontSize; } -h6 { font-size: @baseFontSize * 0.85; } // ~12px - -h1 small { font-size: @baseFontSize * 1.75; } // ~24px -h2 small { font-size: @baseFontSize * 1.25; } // ~18px -h3 small { font-size: @baseFontSize; } -h4 small { font-size: @baseFontSize; } - - -// Page header -// ------------------------- - -.page-header { - padding-bottom: (@baseLineHeight / 2) - 1; - margin: @baseLineHeight 0 (@baseLineHeight * 1.5); - border-bottom: 1px solid @grayLighter; -} - - - -// Lists -// -------------------------------------------------- - -// Unordered and Ordered lists -ul, ol { - padding: 0; - margin: 0 0 @baseLineHeight / 2 25px; -} -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} -li { - line-height: @baseLineHeight; -} - -// Remove default list styles -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} - -// Single-line list items -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; - > li { - display: inline-block; - .ie7-inline-block(); - padding-left: 5px; - padding-right: 5px; - } -} - -// Description Lists -dl { - margin-bottom: @baseLineHeight; -} -dt, -dd { - line-height: @baseLineHeight; -} -dt { - font-weight: bold; -} -dd { - margin-left: @baseLineHeight / 2; -} -// Horizontal layout (like forms) -.dl-horizontal { - .clearfix(); // Ensure dl clears floats if empty dd elements present - dt { - float: left; - width: @horizontalComponentOffset - 20; - clear: left; - text-align: right; - .text-overflow(); - } - dd { - margin-left: @horizontalComponentOffset; - } -} - -// MISC -// ---- - -// Horizontal rules -hr { - margin: @baseLineHeight 0; - border: 0; - border-top: 1px solid @hrBorder; - border-bottom: 1px solid @white; -} - -// Abbreviations and acronyms -abbr[title], -// Added data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted @grayLight; -} -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} - -// Blockquotes -blockquote { - padding: 0 0 0 15px; - margin: 0 0 @baseLineHeight; - border-left: 5px solid @grayLighter; - p { - margin-bottom: 0; - font-size: @baseFontSize * 1.25; - font-weight: 300; - line-height: 1.25; - } - small { - display: block; - line-height: @baseLineHeight; - color: @grayLight; - &:before { - content: '\2014 \00A0'; - } - } - - // Float right with text-align: right - &.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid @grayLighter; - border-left: 0; - p, - small { - text-align: right; - } - small { - &:before { - content: ''; - } - &:after { - content: '\00A0 \2014'; - } - } - } -} - -// Quotes -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} - -// Addresses -address { - display: block; - margin-bottom: @baseLineHeight; - font-style: normal; - line-height: @baseLineHeight; -} From e53c1e39d309974d3a2f20dccf1f737dbd57452b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Aug 2015 14:03:08 +0200 Subject: [PATCH 19/24] feat(invite): can now add org user with sername again, #2353 --- pkg/api/dtos/invite.go | 8 ++++---- pkg/api/org_invite.go | 20 +++++++++----------- public/app/features/org/partials/invite.html | 7 +++---- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/pkg/api/dtos/invite.go b/pkg/api/dtos/invite.go index 495acce8da9..3f002a8b157 100644 --- a/pkg/api/dtos/invite.go +++ b/pkg/api/dtos/invite.go @@ -3,10 +3,10 @@ package dtos import m "github.com/grafana/grafana/pkg/models" type AddInviteForm struct { - Email string `json:"email" binding:"Required"` - Name string `json:"name"` - Role m.RoleType `json:"role" binding:"Required"` - SkipEmails bool `json:"skipEmails"` + LoginOrEmail string `json:"loginOrEmail" binding:"Required"` + Name string `json:"name"` + Role m.RoleType `json:"role" binding:"Required"` + SkipEmails bool `json:"skipEmails"` } type InviteInfo struct { diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go index f4c64e8db7d..41215259769 100644 --- a/pkg/api/org_invite.go +++ b/pkg/api/org_invite.go @@ -31,12 +31,9 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response if !inviteDto.Role.IsValid() { return ApiError(400, "Invalid role specified", nil) } - if !util.IsEmail(inviteDto.Email) { - return ApiError(400, "Invalid email specified", nil) - } // first try get existing user - userQuery := m.GetUserByLoginQuery{LoginOrEmail: inviteDto.Email} + userQuery := m.GetUserByLoginQuery{LoginOrEmail: inviteDto.LoginOrEmail} if err := bus.Dispatch(&userQuery); err != nil { if err != m.ErrUserNotFound { return ApiError(500, "Failed to query db for existing user check", err) @@ -46,17 +43,17 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: userQuery.Result.Id, Role: inviteDto.Role} if err := bus.Dispatch(&createOrgUserCmd); err != nil { if err == m.ErrOrgUserAlreadyAdded { - return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.Email), err) + return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err) } return ApiError(500, "Error while trying to create org user", err) } else { - return ApiSuccess("Existing Grafana user added to org " + c.OrgName) + return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", userQuery.Result.NameOrFallback(), c.OrgName)) } } cmd := m.CreateTempUserCommand{} cmd.OrgId = c.OrgId - cmd.Email = inviteDto.Email + cmd.Email = inviteDto.LoginOrEmail cmd.Name = inviteDto.Name cmd.Status = m.TmpUserInvitePending cmd.InvitedByUserId = c.UserId @@ -69,9 +66,9 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response } // send invite email - if !inviteDto.SkipEmails { + if !inviteDto.SkipEmails && util.IsEmail(inviteDto.LoginOrEmail) { emailCmd := m.SendEmailCommand{ - To: []string{inviteDto.Email}, + To: []string{inviteDto.LoginOrEmail}, Template: "new_user_invite.html", Data: map[string]interface{}{ "NameOrEmail": util.StringsFallback2(cmd.Name, cmd.Email), @@ -85,10 +82,11 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response if err := bus.Dispatch(&emailCmd); err != nil { return ApiError(500, "Failed to send email invite", err) } - return ApiSuccess(fmt.Sprintf("Sent invite to %s", inviteDto.Email)) + + return ApiSuccess(fmt.Sprintf("Sent invite to %s", inviteDto.LoginOrEmail)) } - return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.Email)) + return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail)) } func RevokeInvite(c *middleware.Context) Response { diff --git a/public/app/features/org/partials/invite.html b/public/app/features/org/partials/invite.html index 46f8be1c715..808f722c081 100644 --- a/public/app/features/org/partials/invite.html +++ b/public/app/features/org/partials/invite.html @@ -5,11 +5,11 @@

    - Invite New Users + Invite Users

    @@ -25,8 +25,7 @@ Email or Username
  • - +
  • Name From dfd1bff3893172453b5a575a82e4c82797540213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Aug 2015 17:50:02 +0200 Subject: [PATCH 20/24] feat(invite): began work on email template build system, and css inlining --- .editorconfig | 12 + emails/README.md | 79 +++ emails/assets/css/ink.css | 688 ++++++++++++++++++++ emails/assets/css/style.css | 1 + emails/dist/email1.html | 193 ++++++ emails/dist/email2.html | 209 +++++++ emails/grunt/aliases.yaml | 8 + emails/grunt/assemble.js | 16 + emails/grunt/clean.js | 5 + emails/grunt/premailer.js | 14 + emails/grunt/processhtml.js | 10 + emails/grunt/replace.js | 13 + emails/grunt/uncss.js | 9 + emails/grunt/watch.js | 14 + emails/gruntfile.js | 6 + emails/new_user_invite.html | 867 ++++++++++++++++++++++++++ emails/package.json | 24 + emails/reset_password.html | 34 + emails/templates/email1.html | 177 ++++++ emails/templates/email2.html | 193 ++++++ emails/templates/layouts/default.html | 16 + emails/welcome_on_signup.html | 30 + 22 files changed, 2618 insertions(+) create mode 100644 .editorconfig create mode 100644 emails/README.md create mode 100644 emails/assets/css/ink.css create mode 100644 emails/assets/css/style.css create mode 100644 emails/dist/email1.html create mode 100644 emails/dist/email2.html create mode 100644 emails/grunt/aliases.yaml create mode 100644 emails/grunt/assemble.js create mode 100644 emails/grunt/clean.js create mode 100644 emails/grunt/premailer.js create mode 100644 emails/grunt/processhtml.js create mode 100644 emails/grunt/replace.js create mode 100644 emails/grunt/uncss.js create mode 100644 emails/grunt/watch.js create mode 100644 emails/gruntfile.js create mode 100644 emails/new_user_invite.html create mode 100644 emails/package.json create mode 100644 emails/reset_password.html create mode 100644 emails/templates/email1.html create mode 100644 emails/templates/email2.html create mode 100644 emails/templates/layouts/default.html create mode 100644 emails/welcome_on_signup.html diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..5760be58369 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/emails/README.md b/emails/README.md new file mode 100644 index 00000000000..e00d048bc2a --- /dev/null +++ b/emails/README.md @@ -0,0 +1,79 @@ +# generator-ink-me [![Build Status](https://secure.travis-ci.org/dnnsldr/generator-ink-me.png?branch=master)](https://travis-ci.org/dnnsldr/generator-ink-me) + +A [Yeoman](http://yeoman.io) generator + +This is a Yeoman generator for building emails based on [Zurb Ink](http://zurb.com/ink/) templates. This also uses Grunt to add livereload, remove unused CSS and then to inline all CSS for the final build. + +Email templates include: +* Basic +* Hero +* Sidebar +* Sidebar Hero +* Order Confirmation +* Shipping Confirmation +* Shopify Order Confirmation +* Shopify Shipping Confirmation + +## +## Getting Started +## + +* Install: `npm install -g generator-ink-me` +* Run `yo ink-me` in the folder you wish to have your files (ex: Sites/my-email-project-folder-name) + + +## +## The Generator will ask you for the following information: +## +* Choose your template +* Give your project a name -- This will be the github/bitbucket repository name +* What domain will images be hosted on +* What is the folder path on the server of the hosting provider you want to use +* What is the `public` folder path for the images on the hosting server +* What is your FTP username +* What is your FTP password +* Do you want to Litmus test +* Litmus username **(this is conditional based on if you want litmus testing) +* Litmus password **(conditional) +* Litmus API name (in your litmus Settings -> Profile -> Subdomain for API) **(conditional) +* Choose your litmus testing clients **(conditional) + +## +## Using LiveReload +## + +To get started you can run `grunt` from the terminal. + +The default "grunt" will start the `watch` process for all css files and the index.html file. LiveReload is now enabled. +Grab the [Chrome extension](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei) for LiveReload to help with ports. + +## +## When Your Ready to Finalize the Files for Production +## Final Build +## + +Once you are ready to package up your final build, grunt will get rid of the unused css from Ink and inline all the css, ftp your images to your image hosting provider, and run a Litmus test if you chose Litmus - + +* run the command `grunt inkify` from the terminal + +* This will create a copy of your index file with a reference to a newly created css file. This new css file gets rid of all of the unused css from Ink and makes a copy of the new css for reference in the copied html. + +* The grunt task of 'premailer' will take the newly reference html and css and move all css inline. + +* Images will be optimized + +* Images will be FTP'd to your hosting provider. + +* Image relative URL's will be replaced with absolute URL's from Image Hosting Domain set during the generator. + +* A test will be sent to Litmus if you choose to with the clients you define. + +* Thats it. There will be a new folder called 'dist' that will have your `email-inline.html` file that is ready for use. + +## Tips + +* To get out of the 'Watch' when running the terminal, on MacOS click `control` and `c`. This will get your terminal back to the prompt to run new tasks. + +* Make all CSS changes in the `style.css`. The `ink.css` is from the bower componenet and is pulling the lastest ink css. These files will be merged togehter and will get rid of any unused when running `grunt inkify`. + +## \ No newline at end of file diff --git a/emails/assets/css/ink.css b/emails/assets/css/ink.css new file mode 100644 index 00000000000..f7d40238555 --- /dev/null +++ b/emails/assets/css/ink.css @@ -0,0 +1,688 @@ +/********************************************** +* Ink v1.0.5 - Copyright 2013 ZURB Inc * +**********************************************/ + +/* Client-specific Styles & Reset */ + +#outlook a { + padding:0; +} + +body{ + width:100% !important; + min-width: 100%; + -webkit-text-size-adjust:100%; + -ms-text-size-adjust:100%; + margin:0; + padding:0; +} + +.ExternalClass { + width:100%; +} + +.ExternalClass, +.ExternalClass p, +.ExternalClass span, +.ExternalClass font, +.ExternalClass td, +.ExternalClass div { + line-height: 100%; +} + +#backgroundTable { + margin:0; + padding:0; + width:100% !important; + line-height: 100% !important; +} + +img { + outline:none; + text-decoration:none; + -ms-interpolation-mode: bicubic; + width: auto; + max-width: 100%; + float: left; + clear: both; + display: block; +} + +center { + width: 100%; + min-width: 580px; +} + +a img { + border: none; +} + +p { + margin: 0 0 0 10px; +} + +table { + border-spacing: 0; + border-collapse: collapse; +} + +td { + word-break: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; + border-collapse: collapse !important; +} + +table, tr, td { + padding: 0; + vertical-align: top; + text-align: left; +} + +hr { + color: #d9d9d9; + background-color: #d9d9d9; + height: 1px; + border: none; +} + +/* Responsive Grid */ + +table.body { + height: 100%; + width: 100%; +} + +table.container { + width: 580px; + margin: 0 auto; + text-align: inherit; +} + +table.row { + padding: 0px; + width: 100%; + position: relative; +} + +table.container table.row { + display: block; +} + +td.wrapper { + padding: 10px 20px 0px 0px; + position: relative; +} + +table.columns, +table.column { + margin: 0 auto; +} + +table.columns td, +table.column td { + padding: 0px 0px 10px; +} + +table.columns td.sub-columns, +table.column td.sub-columns, +table.columns td.sub-column, +table.column td.sub-column { + padding-right: 10px; +} + +td.sub-column, td.sub-columns { + min-width: 0px; +} + +table.row td.last, +table.container td.last { + padding-right: 0px; +} + +table.one { width: 30px; } +table.two { width: 80px; } +table.three { width: 130px; } +table.four { width: 180px; } +table.five { width: 230px; } +table.six { width: 280px; } +table.seven { width: 330px; } +table.eight { width: 380px; } +table.nine { width: 430px; } +table.ten { width: 480px; } +table.eleven { width: 530px; } +table.twelve { width: 580px; } + +table.one center { min-width: 30px; } +table.two center { min-width: 80px; } +table.three center { min-width: 130px; } +table.four center { min-width: 180px; } +table.five center { min-width: 230px; } +table.six center { min-width: 280px; } +table.seven center { min-width: 330px; } +table.eight center { min-width: 380px; } +table.nine center { min-width: 430px; } +table.ten center { min-width: 480px; } +table.eleven center { min-width: 530px; } +table.twelve center { min-width: 580px; } + +table.one .panel center { min-width: 10px; } +table.two .panel center { min-width: 60px; } +table.three .panel center { min-width: 110px; } +table.four .panel center { min-width: 160px; } +table.five .panel center { min-width: 210px; } +table.six .panel center { min-width: 260px; } +table.seven .panel center { min-width: 310px; } +table.eight .panel center { min-width: 360px; } +table.nine .panel center { min-width: 410px; } +table.ten .panel center { min-width: 460px; } +table.eleven .panel center { min-width: 510px; } +table.twelve .panel center { min-width: 560px; } + +.body .columns td.one, +.body .column td.one { width: 8.333333%; } +.body .columns td.two, +.body .column td.two { width: 16.666666%; } +.body .columns td.three, +.body .column td.three { width: 25%; } +.body .columns td.four, +.body .column td.four { width: 33.333333%; } +.body .columns td.five, +.body .column td.five { width: 41.666666%; } +.body .columns td.six, +.body .column td.six { width: 50%; } +.body .columns td.seven, +.body .column td.seven { width: 58.333333%; } +.body .columns td.eight, +.body .column td.eight { width: 66.666666%; } +.body .columns td.nine, +.body .column td.nine { width: 75%; } +.body .columns td.ten, +.body .column td.ten { width: 83.333333%; } +.body .columns td.eleven, +.body .column td.eleven { width: 91.666666%; } +.body .columns td.twelve, +.body .column td.twelve { width: 100%; } + +td.offset-by-one { padding-left: 50px; } +td.offset-by-two { padding-left: 100px; } +td.offset-by-three { padding-left: 150px; } +td.offset-by-four { padding-left: 200px; } +td.offset-by-five { padding-left: 250px; } +td.offset-by-six { padding-left: 300px; } +td.offset-by-seven { padding-left: 350px; } +td.offset-by-eight { padding-left: 400px; } +td.offset-by-nine { padding-left: 450px; } +td.offset-by-ten { padding-left: 500px; } +td.offset-by-eleven { padding-left: 550px; } + +td.expander { + visibility: hidden; + width: 0px; + padding: 0 !important; +} + +table.columns .text-pad, +table.column .text-pad { + padding-left: 10px; + padding-right: 10px; +} + +table.columns .left-text-pad, +table.columns .text-pad-left, +table.column .left-text-pad, +table.column .text-pad-left { + padding-left: 10px; +} + +table.columns .right-text-pad, +table.columns .text-pad-right, +table.column .right-text-pad, +table.column .text-pad-right { + padding-right: 10px; +} + +/* Block Grid */ + +.block-grid { + width: 100%; + max-width: 580px; +} + +.block-grid td { + display: inline-block; + padding:10px; +} + +.two-up td { + width:270px; +} + +.three-up td { + width:173px; +} + +.four-up td { + width:125px; +} + +.five-up td { + width:96px; +} + +.six-up td { + width:76px; +} + +.seven-up td { + width:62px; +} + +.eight-up td { + width:52px; +} + +/* Alignment & Visibility Classes */ + +table.center, td.center { + text-align: center; +} + +h1.center, +h2.center, +h3.center, +h4.center, +h5.center, +h6.center { + text-align: center; +} + +span.center { + display: block; + width: 100%; + text-align: center; +} + +img.center { + margin: 0 auto; + float: none; +} + +.show-for-small, +.hide-for-desktop { + display: none; +} + +/* Typography */ + +body, table.body, h1, h2, h3, h4, h5, h6, p, td { + color: #222222; + font-family: "Helvetica", "Arial", sans-serif; + font-weight: normal; + padding:0; + margin: 0; + text-align: left; + line-height: 1.3; +} + +h1, h2, h3, h4, h5, h6 { + word-break: normal; +} + +h1 {font-size: 40px;} +h2 {font-size: 36px;} +h3 {font-size: 32px;} +h4 {font-size: 28px;} +h5 {font-size: 24px;} +h6 {font-size: 20px;} +body, table.body, p, td {font-size: 14px;line-height:19px;} + +p.lead, p.lede, p.leed { + font-size: 18px; + line-height:21px; +} + +p { + margin-bottom: 10px; +} + +small { + font-size: 10px; +} + +a { + color: #2ba6cb; + text-decoration: none; +} + +a:hover { + color: #2795b6 !important; +} + +a:active { + color: #2795b6 !important; +} + +a:visited { + color: #2ba6cb !important; +} + +h1 a, +h2 a, +h3 a, +h4 a, +h5 a, +h6 a { + color: #2ba6cb; +} + +h1 a:active, +h2 a:active, +h3 a:active, +h4 a:active, +h5 a:active, +h6 a:active { + color: #2ba6cb !important; +} + +h1 a:visited, +h2 a:visited, +h3 a:visited, +h4 a:visited, +h5 a:visited, +h6 a:visited { + color: #2ba6cb !important; +} + +/* Panels */ + +.panel { + background: #f2f2f2; + border: 1px solid #d9d9d9; + padding: 10px !important; +} + +.sub-grid table { + width: 100%; +} + +.sub-grid td.sub-columns { + padding-bottom: 0; +} + +/* Buttons */ + +table.button, +table.tiny-button, +table.small-button, +table.medium-button, +table.large-button { + width: 100%; + overflow: hidden; +} + +table.button td, +table.tiny-button td, +table.small-button td, +table.medium-button td, +table.large-button td { + display: block; + width: auto !important; + text-align: center; + background: #2ba6cb; + border: 1px solid #2284a1; + color: #ffffff; + padding: 8px 0; +} + +table.tiny-button td { + padding: 5px 0 4px; +} + +table.small-button td { + padding: 8px 0 7px; +} + +table.medium-button td { + padding: 12px 0 10px; +} + +table.large-button td { + padding: 21px 0 18px; +} + +table.button td a, +table.tiny-button td a, +table.small-button td a, +table.medium-button td a, +table.large-button td a { + font-weight: bold; + text-decoration: none; + font-family: Helvetica, Arial, sans-serif; + color: #ffffff; + font-size: 16px; +} + +table.tiny-button td a { + font-size: 12px; + font-weight: normal; +} + +table.small-button td a { + font-size: 16px; +} + +table.medium-button td a { + font-size: 20px; +} + +table.large-button td a { + font-size: 24px; +} + +table.button:hover td, +table.button:visited td, +table.button:active td { + background: #2795b6 !important; +} + +table.button:hover td a, +table.button:visited td a, +table.button:active td a { + color: #fff !important; +} + +table.button:hover td, +table.tiny-button:hover td, +table.small-button:hover td, +table.medium-button:hover td, +table.large-button:hover td { + background: #2795b6 !important; +} + +table.button:hover td a, +table.button:active td a, +table.button td a:visited, +table.tiny-button:hover td a, +table.tiny-button:active td a, +table.tiny-button td a:visited, +table.small-button:hover td a, +table.small-button:active td a, +table.small-button td a:visited, +table.medium-button:hover td a, +table.medium-button:active td a, +table.medium-button td a:visited, +table.large-button:hover td a, +table.large-button:active td a, +table.large-button td a:visited { + color: #ffffff !important; +} + +table.secondary td { + background: #e9e9e9; + border-color: #d0d0d0; + color: #555; +} + +table.secondary td a { + color: #555; +} + +table.secondary:hover td { + background: #d0d0d0 !important; + color: #555; +} + +table.secondary:hover td a, +table.secondary td a:visited, +table.secondary:active td a { + color: #555 !important; +} + +table.success td { + background: #5da423; + border-color: #457a1a; +} + +table.success:hover td { + background: #457a1a !important; +} + +table.alert td { + background: #c60f13; + border-color: #970b0e; +} + +table.alert:hover td { + background: #970b0e !important; +} + +table.radius td { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +table.round td { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +/* Outlook First */ + +body.outlook p { + display: inline !important; +} + +/* Media Queries */ + +@media only screen and (max-width: 600px) { + + table[class="body"] img { + width: auto !important; + height: auto !important; + } + + table[class="body"] center { + min-width: 0 !important; + } + + table[class="body"] .container { + width: 95% !important; + } + + table[class="body"] .row { + width: 100% !important; + display: block !important; + } + + table[class="body"] .wrapper { + display: block !important; + padding-right: 0 !important; + } + + table[class="body"] .columns, + table[class="body"] .column { + table-layout: fixed !important; + float: none !important; + width: 100% !important; + padding-right: 0px !important; + padding-left: 0px !important; + display: block !important; + } + + table[class="body"] .wrapper.first .columns, + table[class="body"] .wrapper.first .column { + display: table !important; + } + + table[class="body"] table.columns td, + table[class="body"] table.column td { + width: 100% !important; + } + + table[class="body"] .columns td.one, + table[class="body"] .column td.one { width: 8.333333% !important; } + table[class="body"] .columns td.two, + table[class="body"] .column td.two { width: 16.666666% !important; } + table[class="body"] .columns td.three, + table[class="body"] .column td.three { width: 25% !important; } + table[class="body"] .columns td.four, + table[class="body"] .column td.four { width: 33.333333% !important; } + table[class="body"] .columns td.five, + table[class="body"] .column td.five { width: 41.666666% !important; } + table[class="body"] .columns td.six, + table[class="body"] .column td.six { width: 50% !important; } + table[class="body"] .columns td.seven, + table[class="body"] .column td.seven { width: 58.333333% !important; } + table[class="body"] .columns td.eight, + table[class="body"] .column td.eight { width: 66.666666% !important; } + table[class="body"] .columns td.nine, + table[class="body"] .column td.nine { width: 75% !important; } + table[class="body"] .columns td.ten, + table[class="body"] .column td.ten { width: 83.333333% !important; } + table[class="body"] .columns td.eleven, + table[class="body"] .column td.eleven { width: 91.666666% !important; } + table[class="body"] .columns td.twelve, + table[class="body"] .column td.twelve { width: 100% !important; } + + table[class="body"] td.offset-by-one, + table[class="body"] td.offset-by-two, + table[class="body"] td.offset-by-three, + table[class="body"] td.offset-by-four, + table[class="body"] td.offset-by-five, + table[class="body"] td.offset-by-six, + table[class="body"] td.offset-by-seven, + table[class="body"] td.offset-by-eight, + table[class="body"] td.offset-by-nine, + table[class="body"] td.offset-by-ten, + table[class="body"] td.offset-by-eleven { + padding-left: 0 !important; + } + + table[class="body"] table.columns td.expander { + width: 1px !important; + } + + table[class="body"] .right-text-pad, + table[class="body"] .text-pad-right { + padding-left: 10px !important; + } + + table[class="body"] .left-text-pad, + table[class="body"] .text-pad-left { + padding-right: 10px !important; + } + + table[class="body"] .hide-for-small, + table[class="body"] .show-for-desktop { + display: none !important; + } + + table[class="body"] .show-for-small, + table[class="body"] .hide-for-desktop { + display: inherit !important; + } +} diff --git a/emails/assets/css/style.css b/emails/assets/css/style.css new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/emails/assets/css/style.css @@ -0,0 +1 @@ + diff --git a/emails/dist/email1.html b/emails/dist/email1.html new file mode 100644 index 00000000000..4b77baa1c41 --- /dev/null +++ b/emails/dist/email1.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + +
    + {{.Test}} +
    + + + + + +
    +
    + + + + + +
    + + + + + + + +
    + Add %%replyemailaddress%% to your address book. + + View Web Version +
    + +
    + +
    +
    + + + + + +
    + + + + + +
    + + + + + + +
    +

    Hi, John Doe

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    +
    + +
    + + + + + +
    + + + + + + +
    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + diff --git a/emails/dist/email2.html b/emails/dist/email2.html new file mode 100644 index 00000000000..b3ca3b18f2f --- /dev/null +++ b/emails/dist/email2.html @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + + +
    + Add %%replyemailaddress%% to your address book. + + View Web Version +
    + +
    + +
    +
    + + + + + +
    + + + + + +
    + + + + + + +
    +

    Hi, John Doe

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    +
    + +
    + + + + + +
    + + + + + + +
    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + + + + diff --git a/emails/grunt/aliases.yaml b/emails/grunt/aliases.yaml new file mode 100644 index 00000000000..9afb9004c34 --- /dev/null +++ b/emails/grunt/aliases.yaml @@ -0,0 +1,8 @@ +default: + - 'watch' + +inkify: + - 'clean' + - 'uncss' + - 'processhtml' + - 'premailer' diff --git a/emails/grunt/assemble.js b/emails/grunt/assemble.js new file mode 100644 index 00000000000..a4a6390c4c2 --- /dev/null +++ b/emails/grunt/assemble.js @@ -0,0 +1,16 @@ +module.exports = function() { + 'use strict'; + return { + options: { + layout: 'templates/layouts/default.html', + partials: ['templates/partials/*.hbs'], + helpers: ['templates/helpers/**/*.js'], + data: [], + flatten: true + }, + pages: { + src: ['templates/*.html'], + dest: 'dist/' + } + }; +}; diff --git a/emails/grunt/clean.js b/emails/grunt/clean.js new file mode 100644 index 00000000000..60b8ff0a917 --- /dev/null +++ b/emails/grunt/clean.js @@ -0,0 +1,5 @@ +module.exports = function(config) { + return { + dist: ['dist'], + }; +}; diff --git a/emails/grunt/premailer.js b/emails/grunt/premailer.js new file mode 100644 index 00000000000..37b444b048e --- /dev/null +++ b/emails/grunt/premailer.js @@ -0,0 +1,14 @@ +module.exports = { + main: { + options: { + verbose: true, + removeComments: true + }, + files: [{ + expand: true, // Enable dynamic expansion. + cwd: 'dist', // Src matches are relative to this path. + src: ['*.html'], // Actual pattern(s) to match. + dest: 'dist/', // Destination path prefix. + }], + } +}; diff --git a/emails/grunt/processhtml.js b/emails/grunt/processhtml.js new file mode 100644 index 00000000000..9bca41183c5 --- /dev/null +++ b/emails/grunt/processhtml.js @@ -0,0 +1,10 @@ +module.exports = { + dist: { + files: [{ + expand: true, // Enable dynamic expansion. + cwd: 'templates', // Src matches are relative to this path. + src: ['*.html'], // Actual pattern(s) to match. + dest: 'dist/', // Destination path prefix. + }], + } +}; diff --git a/emails/grunt/replace.js b/emails/grunt/replace.js new file mode 100644 index 00000000000..7038802d34e --- /dev/null +++ b/emails/grunt/replace.js @@ -0,0 +1,13 @@ +module.exports = { + dist: { + overwrite: true, + src: ['dist/*.html'], + replacements: [{ + from: '[[', + to: '{{' + }, { + from: ']]', + to: '}}' + }] + } +}; diff --git a/emails/grunt/uncss.js b/emails/grunt/uncss.js new file mode 100644 index 00000000000..7bc08d008e7 --- /dev/null +++ b/emails/grunt/uncss.js @@ -0,0 +1,9 @@ +module.exports = { + dist: { + src: ['templates/*.html'], + dest: 'dist/css/tidy.css', + options: { + report: 'min' // optional: include to report savings + } + } +}; diff --git a/emails/grunt/watch.js b/emails/grunt/watch.js new file mode 100644 index 00000000000..3613b40bc15 --- /dev/null +++ b/emails/grunt/watch.js @@ -0,0 +1,14 @@ +module.exports = { + src: { + files: [ + //what are the files that we want to watch + 'assets/css/*.css', + '*.html' + ], + options: { + nospawn: true, + livereload: true, + } + } + +}; diff --git a/emails/gruntfile.js b/emails/gruntfile.js new file mode 100644 index 00000000000..b6d54cf651b --- /dev/null +++ b/emails/gruntfile.js @@ -0,0 +1,6 @@ +module.exports = function(grunt) { + + // load grunt config + require('load-grunt-config')(grunt); + +}; diff --git a/emails/new_user_invite.html b/emails/new_user_invite.html new file mode 100644 index 00000000000..68a4a99b31a --- /dev/null +++ b/emails/new_user_invite.html @@ -0,0 +1,867 @@ +{{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} + + + + + + + + + + + + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + + + + +
    + + + + + +
    + + + + + + +
    +

    You're invited to sign up to Grafana and join {{.OrgName}}

    +
    + +
    + + + + + +
    + + + + + +
    + + + + +
    + Complete Sign Up +
    +
    + +
    + + +
    + +
    +
    + + diff --git a/emails/package.json b/emails/package.json new file mode 100644 index 00000000000..96779fb5609 --- /dev/null +++ b/emails/package.json @@ -0,0 +1,24 @@ +{ + "name": "Grafana-Email-Campaign", + "version": "1.0.0", + "description": "Grafana Email templates based on Zurb Ink", + "repository": "dnnsldr/", + "author": { + "name": "dnnsldr", + "email": "delder@riester.com", + "url": "https://github.com/dnnsldr" + }, + "devDependencies": { + "grunt": "^0.4.5", + "grunt-premailer": "^0.2.10", + "grunt-processhtml": "^0.3.3", + "grunt-uncss": "^0.3.7", + "load-grunt-config": "^0.14.0", + "grunt-contrib-watch": "^0.6.1", + "grunt-text-replace": "^0.3.12" + }, + "dependencies": { + "grunt-assemble": "^0.4.0", + "grunt-contrib-clean": "^0.6.0" + } +} diff --git a/emails/reset_password.html b/emails/reset_password.html new file mode 100644 index 00000000000..970d5da6ca0 --- /dev/null +++ b/emails/reset_password.html @@ -0,0 +1,34 @@ +{{Subject .Subject "Reset your Grafana password - {{.Name}}"}} + + + + + +{{.Name}}, please reset your password + + +
    +
    +
    +
    +
    +
    + Hi {{.Name}}, +
    +
    +

    Please click the following link to reset your password within {{.EmailCodeValidHours}} hours.

    +

    + {{.AppUrl}}user/password/reset?code={{.Code}} +

    +

    Not working? Try copying and pasting it to your browser.

    +
    +
    +
    +
    +
    + © 2014 Grafana +
    +
    +
    + + diff --git a/emails/templates/email1.html b/emails/templates/email1.html new file mode 100644 index 00000000000..124be9a35be --- /dev/null +++ b/emails/templates/email1.html @@ -0,0 +1,177 @@ + + + [[.Test]] +
    + + + + + +
    +
    + + + + + +
    + + + + + + + +
    + Add %%replyemailaddress%% to your address book. + + View Web Version +
    + +
    + +
    +
    + + + + + +
    + + + + + +
    + + + + + + +
    +

    Hi, John Doe

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    +
    + +
    + + + + + +
    + + + + + + +
    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + diff --git a/emails/templates/email2.html b/emails/templates/email2.html new file mode 100644 index 00000000000..fc18b0e67c6 --- /dev/null +++ b/emails/templates/email2.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + + +
    + Add %%replyemailaddress%% to your address book. + + View Web Version +
    + +
    + +
    +
    + + + + + +
    + + + + + +
    + + + + + + +
    +

    Hi, John Doe

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    +
    + +
    + + + + + +
    + + + + + + +
    +

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + diff --git a/emails/templates/layouts/default.html b/emails/templates/layouts/default.html new file mode 100644 index 00000000000..a5e8bf94f35 --- /dev/null +++ b/emails/templates/layouts/default.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + {{> body }} +
    + + diff --git a/emails/welcome_on_signup.html b/emails/welcome_on_signup.html new file mode 100644 index 00000000000..5018930da57 --- /dev/null +++ b/emails/welcome_on_signup.html @@ -0,0 +1,30 @@ +{{Subject .Subject "Welcome to Grafana"}} + + + + + +{{.Name}} Welcome to Grafana + + +
    +
    +
    +
    +
    +
    + Hi {{.Name}}, +
    +
    + +
    +
    +
    +
    +
    + © 2014 Grafana +
    +
    +
    + + From 4439545428a4afb96a7902b334eba57b68678580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Aug 2015 21:24:47 +0200 Subject: [PATCH 21/24] feat(invite): lots of work completing the new email template system and css inlineing, converted new_user_invite.html to new system, #2353 --- .gitignore | 1 + emails/README.md | 81 +-- emails/assets/css/ink.css | 150 ++-- emails/assets/css/style.css | 66 ++ emails/dist/email1.html | 193 ----- emails/dist/email2.html | 209 ------ emails/grunt/aliases.yaml | 6 +- emails/grunt/premailer.js | 2 +- emails/grunt/processhtml.js | 2 +- emails/grunt/uncss.js | 2 +- emails/grunt/watch.js | 4 +- emails/new_user_invite.html | 867 ---------------------- emails/templates/email1.html | 177 ----- emails/templates/email2.html | 193 ----- emails/templates/layouts/default.html | 68 +- emails/templates/new_user_invite.html | 42 ++ public/app/partials/signup_invited.html | 2 +- public/emails/README.md | 2 + public/emails/new_user_invite.html | 908 ++++-------------------- 19 files changed, 391 insertions(+), 2584 deletions(-) delete mode 100644 emails/dist/email1.html delete mode 100644 emails/dist/email2.html delete mode 100644 emails/new_user_invite.html delete mode 100644 emails/templates/email1.html delete mode 100644 emails/templates/email2.html create mode 100644 emails/templates/new_user_invite.html create mode 100644 public/emails/README.md diff --git a/.gitignore b/.gitignore index 99ed4c4adfd..882f4c09aeb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ coverage/ .aws-config.json awsconfig /dist +/emails/dist /tmp docs/AWS_S3_BUCKET diff --git a/emails/README.md b/emails/README.md index e00d048bc2a..61ff6af6ad2 100644 --- a/emails/README.md +++ b/emails/README.md @@ -1,79 +1,8 @@ -# generator-ink-me [![Build Status](https://secure.travis-ci.org/dnnsldr/generator-ink-me.png?branch=master)](https://travis-ci.org/dnnsldr/generator-ink-me) -A [Yeoman](http://yeoman.io) generator +- npm install +- grunt (default task will build new inlines email templates) +- grunt watch (will build on source html or css change) -This is a Yeoman generator for building emails based on [Zurb Ink](http://zurb.com/ink/) templates. This also uses Grunt to add livereload, remove unused CSS and then to inline all CSS for the final build. +assembled email templates will be in dist/ and final +inlined templates will be in ../public/emails/ -Email templates include: -* Basic -* Hero -* Sidebar -* Sidebar Hero -* Order Confirmation -* Shipping Confirmation -* Shopify Order Confirmation -* Shopify Shipping Confirmation - -## -## Getting Started -## - -* Install: `npm install -g generator-ink-me` -* Run `yo ink-me` in the folder you wish to have your files (ex: Sites/my-email-project-folder-name) - - -## -## The Generator will ask you for the following information: -## -* Choose your template -* Give your project a name -- This will be the github/bitbucket repository name -* What domain will images be hosted on -* What is the folder path on the server of the hosting provider you want to use -* What is the `public` folder path for the images on the hosting server -* What is your FTP username -* What is your FTP password -* Do you want to Litmus test -* Litmus username **(this is conditional based on if you want litmus testing) -* Litmus password **(conditional) -* Litmus API name (in your litmus Settings -> Profile -> Subdomain for API) **(conditional) -* Choose your litmus testing clients **(conditional) - -## -## Using LiveReload -## - -To get started you can run `grunt` from the terminal. - -The default "grunt" will start the `watch` process for all css files and the index.html file. LiveReload is now enabled. -Grab the [Chrome extension](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei) for LiveReload to help with ports. - -## -## When Your Ready to Finalize the Files for Production -## Final Build -## - -Once you are ready to package up your final build, grunt will get rid of the unused css from Ink and inline all the css, ftp your images to your image hosting provider, and run a Litmus test if you chose Litmus - - -* run the command `grunt inkify` from the terminal - -* This will create a copy of your index file with a reference to a newly created css file. This new css file gets rid of all of the unused css from Ink and makes a copy of the new css for reference in the copied html. - -* The grunt task of 'premailer' will take the newly reference html and css and move all css inline. - -* Images will be optimized - -* Images will be FTP'd to your hosting provider. - -* Image relative URL's will be replaced with absolute URL's from Image Hosting Domain set during the generator. - -* A test will be sent to Litmus if you choose to with the clients you define. - -* Thats it. There will be a new folder called 'dist' that will have your `email-inline.html` file that is ready for use. - -## Tips - -* To get out of the 'Watch' when running the terminal, on MacOS click `control` and `c`. This will get your terminal back to the prompt to run new tasks. - -* Make all CSS changes in the `style.css`. The `ink.css` is from the bower componenet and is pulling the lastest ink css. These files will be merged togehter and will get rid of any unused when running `grunt inkify`. - -## \ No newline at end of file diff --git a/emails/assets/css/ink.css b/emails/assets/css/ink.css index f7d40238555..e65aed19194 100644 --- a/emails/assets/css/ink.css +++ b/emails/assets/css/ink.css @@ -4,47 +4,47 @@ /* Client-specific Styles & Reset */ -#outlook a { - padding:0; -} - -body{ - width:100% !important; - min-width: 100%; - -webkit-text-size-adjust:100%; - -ms-text-size-adjust:100%; - margin:0; +#outlook a { padding:0; } -.ExternalClass { - width:100%; -} - -.ExternalClass, -.ExternalClass p, -.ExternalClass span, -.ExternalClass font, -.ExternalClass td, -.ExternalClass div { - line-height: 100%; -} - -#backgroundTable { - margin:0; - padding:0; - width:100% !important; - line-height: 100% !important; +body{ + width:100% !important; + min-width: 100%; + -webkit-text-size-adjust:100%; + -ms-text-size-adjust:100%; + margin:0; + padding:0; } -img { - outline:none; - text-decoration:none; +.ExternalClass { + width:100%; +} + +.ExternalClass, +.ExternalClass p, +.ExternalClass span, +.ExternalClass font, +.ExternalClass td, +.ExternalClass div { + line-height: 100%; +} + +#backgroundTable { + margin:0; + padding:0; + width:100% !important; + line-height: 100% !important; +} + +img { + outline:none; + text-decoration:none; -ms-interpolation-mode: bicubic; width: auto; - max-width: 100%; - float: left; - clear: both; + max-width: 100%; + float: left; + clear: both; display: block; } @@ -53,7 +53,7 @@ center { min-width: 580px; } -a img { +a img { border: none; } @@ -66,12 +66,12 @@ table { border-collapse: collapse; } -td { +td { word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; - border-collapse: collapse !important; + border-collapse: collapse !important; } table, tr, td { @@ -81,9 +81,9 @@ table, tr, td { } hr { - color: #d9d9d9; - background-color: #d9d9d9; - height: 1px; + color: #d9d9d9; + background-color: #d9d9d9; + height: 1px; border: none; } @@ -100,8 +100,8 @@ table.container { text-align: inherit; } -table.row { - padding: 0px; +table.row { + padding: 0px; width: 100%; position: relative; } @@ -122,7 +122,7 @@ table.column { table.columns td, table.column td { - padding: 0px 0px 10px; + padding: 0px 0px 10px; } table.columns td.sub-columns, @@ -316,13 +316,13 @@ img.center { /* Typography */ -body, table.body, h1, h2, h3, h4, h5, h6, p, td { +body, table.body, h1, h2, h3, h4, h5, h6, p, td { color: #222222; - font-family: "Helvetica", "Arial", sans-serif; - font-weight: normal; - padding:0; + font-family: "Helvetica", "Arial", sans-serif; + font-weight: normal; + padding:0; margin: 0; - text-align: left; + text-align: left; line-height: 1.3; } @@ -343,7 +343,7 @@ p.lead, p.lede, p.leed { line-height:21px; } -p { +p { margin-bottom: 10px; } @@ -352,48 +352,48 @@ small { } a { - color: #2ba6cb; + color: #2ba6cb; text-decoration: none; } -a:hover { +a:hover { color: #2795b6 !important; } -a:active { +a:active { color: #2795b6 !important; } -a:visited { +a:visited { color: #2ba6cb !important; } -h1 a, -h2 a, -h3 a, -h4 a, -h5 a, +h1 a, +h2 a, +h3 a, +h4 a, +h5 a, h6 a { color: #2ba6cb; } -h1 a:active, -h2 a:active, -h3 a:active, -h4 a:active, -h5 a:active, -h6 a:active { - color: #2ba6cb !important; -} +h1 a:active, +h2 a:active, +h3 a:active, +h4 a:active, +h5 a:active, +h6 a:active { + color: #2ba6cb !important; +} -h1 a:visited, -h2 a:visited, -h3 a:visited, -h4 a:visited, -h5 a:visited, -h6 a:visited { - color: #2ba6cb !important; -} +h1 a:visited, +h2 a:visited, +h3 a:visited, +h4 a:visited, +h5 a:visited, +h6 a:visited { + color: #2ba6cb !important; +} /* Panels */ @@ -516,7 +516,7 @@ table.medium-button td a:visited, table.large-button:hover td a, table.large-button:active td a, table.large-button td a:visited { - color: #ffffff !important; + color: #ffffff !important; } table.secondary td { diff --git a/emails/assets/css/style.css b/emails/assets/css/style.css index 8b137891791..47a5213e33e 100644 --- a/emails/assets/css/style.css +++ b/emails/assets/css/style.css @@ -1 +1,67 @@ +body, table.body, h1, h2, h3, h4, h5, h6, p, td { + font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: none; +} + +table.facebook td { + background: #3b5998; + border-color: #2d4473; +} + +table.facebook:hover td { + background: #2d4473 !important; +} + +table.twitter td { + background: #00acee; + border-color: #0087bb; +} + +table.twitter:hover td { + background: #0087bb !important; +} + +table.google-plus td { + background-color: #DB4A39; + border-color: #CC0000; +} + +table.google-plus:hover td { + background: #CC0000 !important; +} + +.template-label { + color: #ffffff; + font-weight: bold; + font-size: 11px; +} + +.callout .wrapper { + padding-bottom: 20px; +} + +.callout .panel { + background: #ECF8FF; + border-color: #b9e5ff; +} + +.header { + background: #333; +} + +.footer { + margin-top: 20px; +} + +@media only screen and (max-width: 600px) { + table[class="body"] .right-text-pad { + padding-left: 10px !important; + } + + table[class="body"] .left-text-pad { + padding-right: 10px !important; + } +} + diff --git a/emails/dist/email1.html b/emails/dist/email1.html deleted file mode 100644 index 4b77baa1c41..00000000000 --- a/emails/dist/email1.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - - - -
    - {{.Test}} -
    - - - - - -
    -
    - - - - - -
    - - - - - - - -
    - Add %%replyemailaddress%% to your address book. - - View Web Version -
    - -
    - -
    -
    - - - - - -
    - - - - - -
    - - - - - - -
    -

    Hi, John Doe

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    -
    - -
    - - - - - -
    - - - - - - -
    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    -
    - -
    - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - diff --git a/emails/dist/email2.html b/emails/dist/email2.html deleted file mode 100644 index b3ca3b18f2f..00000000000 --- a/emails/dist/email2.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    -
    - - - - - -
    -
    - - - - - -
    - - - - - - - -
    - Add %%replyemailaddress%% to your address book. - - View Web Version -
    - -
    - -
    -
    - - - - - -
    - - - - - -
    - - - - - - -
    -

    Hi, John Doe

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    -
    - -
    - - - - - -
    - - - - - - -
    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    -
    - -
    - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - - - - - - diff --git a/emails/grunt/aliases.yaml b/emails/grunt/aliases.yaml index 9afb9004c34..6a2e47777cd 100644 --- a/emails/grunt/aliases.yaml +++ b/emails/grunt/aliases.yaml @@ -1,8 +1,8 @@ -default: - - 'watch' -inkify: +default: - 'clean' + - 'assemble' + - 'replace' - 'uncss' - 'processhtml' - 'premailer' diff --git a/emails/grunt/premailer.js b/emails/grunt/premailer.js index 37b444b048e..c50ca1af015 100644 --- a/emails/grunt/premailer.js +++ b/emails/grunt/premailer.js @@ -8,7 +8,7 @@ module.exports = { expand: true, // Enable dynamic expansion. cwd: 'dist', // Src matches are relative to this path. src: ['*.html'], // Actual pattern(s) to match. - dest: 'dist/', // Destination path prefix. + dest: '../public/emails/', // Destination path prefix. }], } }; diff --git a/emails/grunt/processhtml.js b/emails/grunt/processhtml.js index 9bca41183c5..efe34626a49 100644 --- a/emails/grunt/processhtml.js +++ b/emails/grunt/processhtml.js @@ -2,7 +2,7 @@ module.exports = { dist: { files: [{ expand: true, // Enable dynamic expansion. - cwd: 'templates', // Src matches are relative to this path. + cwd: 'dist', // Src matches are relative to this path. src: ['*.html'], // Actual pattern(s) to match. dest: 'dist/', // Destination path prefix. }], diff --git a/emails/grunt/uncss.js b/emails/grunt/uncss.js index 7bc08d008e7..114af6942c5 100644 --- a/emails/grunt/uncss.js +++ b/emails/grunt/uncss.js @@ -1,6 +1,6 @@ module.exports = { dist: { - src: ['templates/*.html'], + src: ['dist/*.html'], dest: 'dist/css/tidy.css', options: { report: 'min' // optional: include to report savings diff --git a/emails/grunt/watch.js b/emails/grunt/watch.js index 3613b40bc15..284851c500c 100644 --- a/emails/grunt/watch.js +++ b/emails/grunt/watch.js @@ -3,8 +3,10 @@ module.exports = { files: [ //what are the files that we want to watch 'assets/css/*.css', - '*.html' + 'templates/**/*.html', + 'grunt/*.js', ], + tasks: ['default'], options: { nospawn: true, livereload: true, diff --git a/emails/new_user_invite.html b/emails/new_user_invite.html deleted file mode 100644 index 68a4a99b31a..00000000000 --- a/emails/new_user_invite.html +++ /dev/null @@ -1,867 +0,0 @@ -{{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} - - - - - - - - - - - - - - -
    -
    - - - - - -
    -
    - - - - - -
    - - - - - - -
    - -
    - -
    - -
    -
    - - - - - -
    - - - - - -
    - - - - - - -
    -

    You're invited to sign up to Grafana and join {{.OrgName}}

    -
    - -
    - - - - - -
    - - - - - -
    - - - - -
    - Complete Sign Up -
    -
    - -
    - - -
    - -
    -
    - - diff --git a/emails/templates/email1.html b/emails/templates/email1.html deleted file mode 100644 index 124be9a35be..00000000000 --- a/emails/templates/email1.html +++ /dev/null @@ -1,177 +0,0 @@ - - - [[.Test]] -
    - - - - - -
    -
    - - - - - -
    - - - - - - - -
    - Add %%replyemailaddress%% to your address book. - - View Web Version -
    - -
    - -
    -
    - - - - - -
    - - - - - -
    - - - - - - -
    -

    Hi, John Doe

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    -
    - -
    - - - - - -
    - - - - - - -
    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    -
    - -
    - - - - - - - - - - - - - - - - - - -
    - -
    - - diff --git a/emails/templates/email2.html b/emails/templates/email2.html deleted file mode 100644 index fc18b0e67c6..00000000000 --- a/emails/templates/email2.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - - -
    -
    - - - - - -
    -
    - - - - - -
    - - - - - - - -
    - Add %%replyemailaddress%% to your address book. - - View Web Version -
    - -
    - -
    -
    - - - - - -
    - - - - - -
    - - - - - - -
    -

    Hi, John Doe

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae.

    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. consequat vel lacus. Sed iaculis pulvinar ligula, ornare fringilla ante viverra et. In hac habitasse platea dictumst. Donec vel orci mi, eu congue justo. Integer eget odio est, eget malesuada lorem. Aenean sed tellus dui, vitae viverra risus. Nullam massa sapien, pulvinar eleifend fringilla id, convallis eget nisi. Mauris a sagittis dui. Pellentesque non lacinia mi. Fusce sit amet libero sit amet erat venenatis sollicitudin vitae vel eros. Cras nunc sapien, interdum sit amet porttitor ut, congue quis urna.

    -
    - -
    - - - - - -
    - - - - - - -
    -

    Phasellus dictum sapien a neque luctus cursus. Pellentesque sem dolor, fringilla et pharetra vitae. Click it! »

    -
    - -
    - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - - diff --git a/emails/templates/layouts/default.html b/emails/templates/layouts/default.html index a5e8bf94f35..c5dc579fa04 100644 --- a/emails/templates/layouts/default.html +++ b/emails/templates/layouts/default.html @@ -10,7 +10,73 @@ - {{> body }} + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + + + + + +
    + + + {{> body }} + + + + + + + + + +
    +
    +
    diff --git a/emails/templates/new_user_invite.html b/emails/templates/new_user_invite.html new file mode 100644 index 00000000000..1071802f3cd --- /dev/null +++ b/emails/templates/new_user_invite.html @@ -0,0 +1,42 @@ +[[Subject .Subject "[[.InvitedBy]] has invited you to join Grafana"]] + + + + + +
    + + + + + + +
    +

    You're invited to sign up to Grafana and join [[.OrgName]]

    +
    + +
    + + + + + +
    + + + + + +
    + + + + +
    + Complete Sign Up +
    +
    + +
    + + diff --git a/public/app/partials/signup_invited.html b/public/app/partials/signup_invited.html index 329d1fee1bf..8fe4569c3a3 100644 --- a/public/app/partials/signup_invited.html +++ b/public/app/partials/signup_invited.html @@ -15,7 +15,7 @@ diff --git a/public/emails/README.md b/public/emails/README.md new file mode 100644 index 00000000000..18ae81728e3 --- /dev/null +++ b/public/emails/README.md @@ -0,0 +1,2 @@ + +html files in this folder are generated from templates and build system in repo_root/emails diff --git a/public/emails/new_user_invite.html b/public/emails/new_user_invite.html index 68a4a99b31a..ee9774d8c1b 100644 --- a/public/emails/new_user_invite.html +++ b/public/emails/new_user_invite.html @@ -1,804 +1,115 @@ -{{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} - - + - - + + +
    +
    - - - - - - -
    -
    - - - -
    -
    - - - -
    - - - -
    - + + + - +
    +
    @@ -811,57 +122,84 @@ body.outlook p {
    - - -
    + + + +
    - - - - -
    - - - - - -
    -

    You're invited to sign up to Grafana and join {{.OrgName}}

    -
    + {{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} -
    + + +
    - - - -
    - - - + +
    - - - - -
    - Complete Sign Up -
    + + + + + +
    +

    You're invited to sign up to Grafana and join {{.OrgName}}

    +
    + +
    + + + + + +
    + + + + + +
    + + + + +
    + Complete Sign Up +
    +
    + +
    + + + + + + + + - +
    +
    From b0b96aa41044e90f430647658442beec718d62f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 11 Aug 2015 10:01:30 +0200 Subject: [PATCH 22/24] Moved reset password and welcome on sign up email to new email build template system --- emails/reset_password.html | 34 ---- emails/templates/reset_password.html | 42 +++++ emails/templates/welcome_on_signup.html | 37 ++++ emails/welcome_on_signup.html | 30 ---- public/emails/reset_password.html | 229 +++++++++++++++++++++--- public/emails/welcome_on_signup.html | 220 ++++++++++++++++++++--- 6 files changed, 474 insertions(+), 118 deletions(-) delete mode 100644 emails/reset_password.html create mode 100644 emails/templates/reset_password.html create mode 100644 emails/templates/welcome_on_signup.html delete mode 100644 emails/welcome_on_signup.html diff --git a/emails/reset_password.html b/emails/reset_password.html deleted file mode 100644 index 970d5da6ca0..00000000000 --- a/emails/reset_password.html +++ /dev/null @@ -1,34 +0,0 @@ -{{Subject .Subject "Reset your Grafana password - {{.Name}}"}} - - - - - -{{.Name}}, please reset your password - - -
    -
    -
    -
    -
    -
    - Hi {{.Name}}, -
    -
    -

    Please click the following link to reset your password within {{.EmailCodeValidHours}} hours.

    -

    - {{.AppUrl}}user/password/reset?code={{.Code}} -

    -

    Not working? Try copying and pasting it to your browser.

    -
    -
    -
    -
    -
    - © 2014 Grafana -
    -
    -
    - - diff --git a/emails/templates/reset_password.html b/emails/templates/reset_password.html new file mode 100644 index 00000000000..f3aca5da95e --- /dev/null +++ b/emails/templates/reset_password.html @@ -0,0 +1,42 @@ +[[Subject .Subject "Reset your Grafana password - [[.Name]]"]] + + + + + +
    + + + + + + +
    +

    Hi [[.Name]]

    +
    + +
    + + + + + +
    + + + + + +
    +

    + Please click the following link to reset your password within [[.EmailCodeValidHours]] hours. +

    +

    + [[.AppUrl]]user/password/reset?code=[[.Code]] +

    +

    Not working? Try copying and pasting it to your browser.

    +
    + +
    + + diff --git a/emails/templates/welcome_on_signup.html b/emails/templates/welcome_on_signup.html new file mode 100644 index 00000000000..b93c56b77b1 --- /dev/null +++ b/emails/templates/welcome_on_signup.html @@ -0,0 +1,37 @@ +[[Subject .Subject "Welcome to Grafana"]] + + + + + +
    + + + + + + +
    +

    Hi [[.Name]]

    +
    + +
    + + + + + +
    + + + + + +
    +

    + If you are new to Grafana please read the Getting Started guide. +

    +
    +
    + + diff --git a/emails/welcome_on_signup.html b/emails/welcome_on_signup.html deleted file mode 100644 index 5018930da57..00000000000 --- a/emails/welcome_on_signup.html +++ /dev/null @@ -1,30 +0,0 @@ -{{Subject .Subject "Welcome to Grafana"}} - - - - - -{{.Name}} Welcome to Grafana - - -
    -
    -
    -
    -
    -
    - Hi {{.Name}}, -
    -
    - -
    -
    -
    -
    -
    - © 2014 Grafana -
    -
    -
    - - diff --git a/public/emails/reset_password.html b/public/emails/reset_password.html index 970d5da6ca0..ef1c98d9107 100644 --- a/public/emails/reset_password.html +++ b/public/emails/reset_password.html @@ -1,34 +1,205 @@ -{{Subject .Subject "Reset your Grafana password - {{.Name}}"}} - - - + + - -{{.Name}}, please reset your password + + + - -
    -
    -
    -
    -
    -
    - Hi {{.Name}}, -
    -
    -

    Please click the following link to reset your password within {{.EmailCodeValidHours}} hours.

    -

    - {{.AppUrl}}user/password/reset?code={{.Code}} + + + + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + + + + + +
    + + + {{Subject .Subject "Reset your Grafana password - {{.Name}}"}} + + + + + +
    + + + + + + +
    +

    Hi {{.Name}}

    +
    + +
    + + + + + +
    + + + + + +
    +

    + Please click the following link to reset your password within {{.EmailCodeValidHours}} hours.

    -

    Not working? Try copying and pasting it to your browser.

    - - - - -
    - © 2014 Grafana -
    - - +

    + {{.AppUrl}}user/password/reset?code={{.Code}} +

    +

    Not working? Try copying and pasting it to your browser.

    +
    + +
    + + + + + + + + + + + + +
    +
    +
    diff --git a/public/emails/welcome_on_signup.html b/public/emails/welcome_on_signup.html index 5018930da57..b311bfca11c 100644 --- a/public/emails/welcome_on_signup.html +++ b/public/emails/welcome_on_signup.html @@ -1,30 +1,200 @@ -{{Subject .Subject "Welcome to Grafana"}} - - - + + - -{{.Name}} Welcome to Grafana + + + - -

    -
    -
    -
    -
    -
    - Hi {{.Name}}, -
    -
    + + + + + + +
    +
    - - - - -
    - © 2014 Grafana -
    - - + + + + +
    +
    + + + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + + + + + +
    + + + {{Subject .Subject "Welcome to Grafana"}} + + + + + +
    + + + + + + +
    +

    Hi {{.Name}}

    +
    + +
    + + + + + +
    + + + + + +
    +

    + If you are new to Grafana please read the Getting Started guide. +

    +
    +
    + + + + + + + + + + + + +
    +
    +
    From 74932e6311cf70486276ce3943badf280ae7ffdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 11 Aug 2015 10:35:10 +0200 Subject: [PATCH 23/24] feat(invite): added specific email for invites to existing grafana users --- emails/templates/invited_to_org.html | 38 +++++ emails/templates/layouts/default.html | 3 - emails/templates/new_user_invite.html | 16 ++- pkg/api/org_invite.go | 51 +++++-- public/emails/invited_to_org.html | 198 ++++++++++++++++++++++++++ public/emails/new_user_invite.html | 19 ++- public/emails/reset_password.html | 3 - public/emails/welcome_on_signup.html | 3 - 8 files changed, 290 insertions(+), 41 deletions(-) create mode 100644 emails/templates/invited_to_org.html create mode 100644 public/emails/invited_to_org.html diff --git a/emails/templates/invited_to_org.html b/emails/templates/invited_to_org.html new file mode 100644 index 00000000000..5aac6e28de3 --- /dev/null +++ b/emails/templates/invited_to_org.html @@ -0,0 +1,38 @@ + + +[[Subject .Subject "[[.InvitedBy]] has added you to the Grafana organization [[.OrgName]]"]] + + + + + +
    + + + + + + +
    +

    You have been added to the Grafana organization [[.OrgName]]

    +
    + +
    + + + + + +
    + + + + + +
    +

    You can switch organization in the left side menu, in the dropdown below your username.

    +
    + +
    + + diff --git a/emails/templates/layouts/default.html b/emails/templates/layouts/default.html index c5dc579fa04..0fd5b35ea48 100644 --- a/emails/templates/layouts/default.html +++ b/emails/templates/layouts/default.html @@ -44,10 +44,7 @@ diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go index 41215259769..d69eaabe584 100644 --- a/pkg/api/org_invite.go +++ b/pkg/api/org_invite.go @@ -39,16 +39,7 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response return ApiError(500, "Failed to query db for existing user check", err) } } else { - // user exists, add org role - createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: userQuery.Result.Id, Role: inviteDto.Role} - if err := bus.Dispatch(&createOrgUserCmd); err != nil { - if err == m.ErrOrgUserAlreadyAdded { - return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err) - } - return ApiError(500, "Error while trying to create org user", err) - } else { - return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", userQuery.Result.NameOrFallback(), c.OrgName)) - } + return inviteExistingUserToOrg(c, userQuery.Result, &inviteDto) } cmd := m.CreateTempUserCommand{} @@ -71,11 +62,11 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response To: []string{inviteDto.LoginOrEmail}, Template: "new_user_invite.html", Data: map[string]interface{}{ - "NameOrEmail": util.StringsFallback2(cmd.Name, cmd.Email), - "OrgName": c.OrgName, - "Email": c.Email, - "LinkUrl": setting.ToAbsUrl("invite/" + cmd.Code), - "InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login), + "Name": util.StringsFallback2(cmd.Name, cmd.Email), + "OrgName": c.OrgName, + "Email": c.Email, + "LinkUrl": setting.ToAbsUrl("invite/" + cmd.Code), + "InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login), }, } @@ -89,6 +80,36 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail)) } +func inviteExistingUserToOrg(c *middleware.Context, user *m.User, inviteDto *dtos.AddInviteForm) Response { + // user exists, add org role + createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: user.Id, Role: inviteDto.Role} + if err := bus.Dispatch(&createOrgUserCmd); err != nil { + if err == m.ErrOrgUserAlreadyAdded { + return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err) + } + return ApiError(500, "Error while trying to create org user", err) + } else { + + if !inviteDto.SkipEmails && util.IsEmail(user.Email) { + emailCmd := m.SendEmailCommand{ + To: []string{user.Email}, + Template: "invited_to_org.html", + Data: map[string]interface{}{ + "Name": user.NameOrFallback(), + "OrgName": c.OrgName, + "InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login), + }, + } + + if err := bus.Dispatch(&emailCmd); err != nil { + return ApiError(500, "Failed to send email invited_to_org", err) + } + } + + return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName)) + } +} + func RevokeInvite(c *middleware.Context) Response { cmd := m.UpdateTempUserStatusCommand{ Code: c.Params(":code"), diff --git a/public/emails/invited_to_org.html b/public/emails/invited_to_org.html new file mode 100644 index 00000000000..fb61438b441 --- /dev/null +++ b/public/emails/invited_to_org.html @@ -0,0 +1,198 @@ + + + + + + + + +
    - - {{> body }} - diff --git a/emails/templates/new_user_invite.html b/emails/templates/new_user_invite.html index 1071802f3cd..1368ba5a457 100644 --- a/emails/templates/new_user_invite.html +++ b/emails/templates/new_user_invite.html @@ -1,3 +1,5 @@ + + [[Subject .Subject "[[.InvitedBy]] has invited you to join Grafana"]] @@ -7,7 +9,7 @@
    @@ -24,12 +26,12 @@ + + +
    -

    You're invited to sign up to Grafana and join [[.OrgName]]

    +

    You're invited to sign up to Grafana and join organization [[.OrgName]]

    - - - -
    - Complete Sign Up -
    +
    + Complete Sign Up +
    + + + + +
    +
    + + + + + +
    +
    + + + + + +
    + + + + + + +
    + +
    + +
    + +
    +
    + + + + + + +
    + + +{{Subject .Subject "{{.InvitedBy}} has added you to the Grafana organization {{.OrgName}}"}} + + + + + +
    + + + + + + +
    +

    You have been added to the Grafana organization {{.OrgName}}

    +
    + +
    + + + + + +
    + + + + + +
    +

    You can switch organization in the left side menu, in the dropdown below your username.

    +
    + +
    + + + + + + + + + + + +
    +
    +
    + + diff --git a/public/emails/new_user_invite.html b/public/emails/new_user_invite.html index ee9774d8c1b..aeadbb98d7b 100644 --- a/public/emails/new_user_invite.html +++ b/public/emails/new_user_invite.html @@ -125,9 +125,9 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans- @@ -170,7 +170,6 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans- -
    + - - {{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} +{{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}} @@ -136,7 +136,7 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-
    @@ -153,12 +153,12 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans- + + +
    -

    You're invited to sign up to Grafana and join {{.OrgName}}

    +

    You're invited to sign up to Grafana and join organization {{.OrgName}}

    - - - -
    - Complete Sign Up -
    +
    + Complete Sign Up +
    diff --git a/public/emails/reset_password.html b/public/emails/reset_password.html index ef1c98d9107..55bc0f562a5 100644 --- a/public/emails/reset_password.html +++ b/public/emails/reset_password.html @@ -125,8 +125,6 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-
    - - {{Subject .Subject "Reset your Grafana password - {{.Name}}"}} @@ -170,7 +168,6 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans- -
    diff --git a/public/emails/welcome_on_signup.html b/public/emails/welcome_on_signup.html index b311bfca11c..ea38a8c6671 100644 --- a/public/emails/welcome_on_signup.html +++ b/public/emails/welcome_on_signup.html @@ -125,8 +125,6 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-
    - - {{Subject .Subject "Welcome to Grafana"}} @@ -165,7 +163,6 @@ font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans- -
    From 1ea0b5371a337dcb0de90bbeb5b3a64f15580105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 11 Aug 2015 10:45:03 +0200 Subject: [PATCH 24/24] feat(invite): new user invites are now also added to correct org after sign up is completed, #2353 --- pkg/api/org_invite.go | 11 +++++++++++ pkg/models/temp_user.go | 3 ++- pkg/services/sqlstore/temp_user.go | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go index d69eaabe584..ecad941ede3 100644 --- a/pkg/api/org_invite.go +++ b/pkg/api/org_invite.go @@ -178,6 +178,17 @@ func CompleteInvite(c *middleware.Context, completeInvite dtos.CompleteInviteFor Login: user.Login, }) + // add to org + addOrgUserCmd := m.AddOrgUserCommand{OrgId: invite.OrgId, UserId: user.Id, Role: invite.Role} + if err := bus.Dispatch(&addOrgUserCmd); err != nil { + return ApiError(500, "Error while trying to create org user", err) + } + + // set org to active + if err := bus.Dispatch(&m.SetUsingOrgCommand{OrgId: invite.OrgId, UserId: user.Id}); err != nil { + return ApiError(500, "Failed to set org as active", err) + } + // update temp user status updateTmpUserCmd := m.UpdateTempUserStatusCommand{Code: invite.Code, Status: m.TmpUserCompleted} if err := bus.Dispatch(&updateTmpUserCmd); err != nil { diff --git a/pkg/models/temp_user.go b/pkg/models/temp_user.go index 9aab24f48fc..6aeb67cda9b 100644 --- a/pkg/models/temp_user.go +++ b/pkg/models/temp_user.go @@ -75,9 +75,10 @@ type GetTempUserByCodeQuery struct { type TempUserDTO struct { Id int64 `json:"id"` + OrgId int64 `json:"orgId"` Name string `json:"name"` Email string `json:"email"` - Role string `json:"role"` + Role RoleType `json:"role"` InvitedByLogin string `json:"invitedByLogin"` InvitedByEmail string `json:"invitedByEmail"` InvitedByName string `json:"invitedByName"` diff --git a/pkg/services/sqlstore/temp_user.go b/pkg/services/sqlstore/temp_user.go index df1bdd96127..0cac59b21f6 100644 --- a/pkg/services/sqlstore/temp_user.go +++ b/pkg/services/sqlstore/temp_user.go @@ -52,6 +52,7 @@ func CreateTempUser(cmd *m.CreateTempUserCommand) error { func GetTempUsersForOrg(query *m.GetTempUsersForOrgQuery) error { var rawSql = `SELECT tu.id as id, + tu.org_id as org_id, tu.email as email, tu.name as name, tu.role as role, @@ -76,6 +77,7 @@ func GetTempUsersForOrg(query *m.GetTempUsersForOrgQuery) error { func GetTempUserByCode(query *m.GetTempUserByCodeQuery) error { var rawSql = `SELECT tu.id as id, + tu.org_id as org_id, tu.email as email, tu.name as name, tu.role as role,