From 66dbe2e799af23874ca786b564f9decb0faa6c6f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 21 Feb 2015 00:11:22 -0600 Subject: [PATCH] Cleaned up error messages - *all* errors in JSON format Cleaned up flashes - fixes #13 Added specified errors - more to come soon Added Campaign validation Added Group validation Cleaned up the way angular errors are handled. Will double check, but for the most part fixes #11 Results are now shown on the webui with most recent shown first Added comments, additional cleanup, etc. --- controllers/api.go | 85 ++- controllers/route.go | 10 - models/campaign.go | 33 +- models/group.go | 30 +- models/template.go | 9 +- static/js/app/controllers.js | 543 ++++++++++-------- static/js/app/partials/campaigns.html | 4 +- static/js/app/partials/landing_pages.html | 2 +- .../js/app/partials/modals/campaignModal.html | 7 +- static/js/app/partials/modals/userModal.html | 5 + static/js/app/partials/templates.html | 2 +- static/js/app/partials/users.html | 4 +- worker/worker.go | 1 + 13 files changed, 411 insertions(+), 324 deletions(-) diff --git a/controllers/api.go b/controllers/api.go index 0e5c242c..a5f6cefb 100644 --- a/controllers/api.go +++ b/controllers/api.go @@ -69,15 +69,13 @@ func API_Campaigns(w http.ResponseWriter, r *http.Request) { c := models.Campaign{} // Put the request into a campaign err := json.NewDecoder(r.Body).Decode(&c) - if checkError(err, w, "Invalid Request", http.StatusBadRequest) { - return - } - if m, ok := c.Validate(); !ok { - http.Error(w, "Error: "+m, http.StatusBadRequest) + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } err = models.PostCampaign(&c, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Cannot insert campaign into database", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } Worker.Queue <- &c @@ -91,7 +89,8 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Campaign not found", http.StatusNotFound) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) return } switch { @@ -99,7 +98,8 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { JSONResponse(w, c, http.StatusOK) case r.Method == "DELETE": err = models.DeleteCampaign(id) - if checkError(err, w, "Error deleting campaign", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error deleting campaign"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK) @@ -112,7 +112,8 @@ func API_Groups(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": gs, err := models.GetGroups(ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Groups not found", http.StatusNotFound) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "No groups found"}, http.StatusNotFound) return } JSONResponse(w, gs, http.StatusOK) @@ -121,7 +122,8 @@ func API_Groups(w http.ResponseWriter, r *http.Request) { g := models.Group{} // Put the request into a group err := json.NewDecoder(r.Body).Decode(&g) - if checkError(err, w, "Invalid Request", http.StatusBadRequest) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } _, err = models.GetGroupByName(g.Name, ctx.Get(r, "user_id").(int64)) @@ -129,15 +131,11 @@ func API_Groups(w http.ResponseWriter, r *http.Request) { JSONResponse(w, models.Response{Success: false, Message: "Group name already in use"}, http.StatusConflict) return } - // Check to make sure targets were specified - if len(g.Targets) == 0 { - http.Error(w, "Error: No targets specified", http.StatusBadRequest) - return - } g.ModifiedDate = time.Now() g.UserId = ctx.Get(r, "user_id").(int64) err = models.PostGroup(&g) - if checkError(err, w, "Error inserting group", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } w.Header().Set("Location", "http://localhost:3333/api/groups/"+string(g.Id)) @@ -151,7 +149,8 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) g, err := models.GetGroup(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Group not found", http.StatusNotFound) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound) return } switch { @@ -159,7 +158,8 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) { JSONResponse(w, g, http.StatusOK) case r.Method == "DELETE": err = models.DeleteGroup(&g) - if checkError(err, w, "Error deleting group", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error deleting group"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK) @@ -168,18 +168,14 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) { g = models.Group{} err = json.NewDecoder(r.Body).Decode(&g) if g.Id != id { - http.Error(w, "Error: /:id and group_id mismatch", http.StatusBadRequest) - return - } - // Check to make sure targets were specified - if len(g.Targets) == 0 { - http.Error(w, "Error: No targets specified", http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and group_id mismatch"}, http.StatusInternalServerError) return } g.ModifiedDate = time.Now() g.UserId = ctx.Get(r, "user_id").(int64) err = models.PutGroup(&g) - if checkError(err, w, "Error updating group", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error updating group"}, http.StatusInternalServerError) return } JSONResponse(w, g, http.StatusOK) @@ -199,11 +195,11 @@ func API_Templates(w http.ResponseWriter, r *http.Request) { t := models.Template{} // Put the request into a template err := json.NewDecoder(r.Body).Decode(&t) - if checkError(err, w, "Invalid Request", http.StatusBadRequest) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } _, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64)) - fmt.Println(err) if err != gorm.RecordNotFound { JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict) return @@ -219,7 +215,9 @@ func API_Templates(w http.ResponseWriter, r *http.Request) { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } - if checkError(err, w, "Error inserting template", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error inserting template into database"}, http.StatusInternalServerError) + Logger.Println(err) return } JSONResponse(w, t, http.StatusCreated) @@ -230,8 +228,8 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) t, err := models.GetTemplate(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Template not found", http.StatusNotFound) { - Logger.Println(err) + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Template not found"}, http.StatusNotFound) return } switch { @@ -239,7 +237,8 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { JSONResponse(w, t, http.StatusOK) case r.Method == "DELETE": err = models.DeleteTemplate(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Error deleting template", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error deleting template"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK) @@ -250,17 +249,14 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { Logger.Println(err) } if t.Id != id { - http.Error(w, "Error: /:id and template_id mismatch", http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and template_id mismatch"}, http.StatusBadRequest) return } - err = t.Validate() - /* if checkError(err, w, http.StatusBadRequest) { - return - }*/ t.ModifiedDate = time.Now() t.UserId = ctx.Get(r, "user_id").(int64) err = models.PutTemplate(&t) - if checkError(err, w, "Error updating group", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } JSONResponse(w, t, http.StatusOK) @@ -306,8 +302,8 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) p, err := models.GetPage(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Page not found", http.StatusNotFound) { - Logger.Println(err) + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Page not found"}, http.StatusNotFound) return } switch { @@ -315,7 +311,8 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { JSONResponse(w, p, http.StatusOK) case r.Method == "DELETE": err = models.DeletePage(id, ctx.Get(r, "user_id").(int64)) - if checkError(err, w, "Error deleting page", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error deleting page"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK) @@ -348,7 +345,8 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { // API_Import_Group imports a CSV of group members func API_Import_Group(w http.ResponseWriter, r *http.Request) { ts, err := util.ParseCSV(r) - if checkError(err, w, "Error deleting template", http.StatusInternalServerError) { + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: "Error parsing CSV"}, http.StatusInternalServerError) return } JSONResponse(w, ts, http.StatusOK) @@ -371,8 +369,9 @@ func API_Import_Email(w http.ResponseWriter, r *http.Request) { // is written to the given ResponseWriter. func JSONResponse(w http.ResponseWriter, d interface{}, c int) { dj, err := json.MarshalIndent(d, "", " ") - if checkError(err, w, "Error creating JSON response", http.StatusInternalServerError) { - return + if err != nil { + http.Error(w, "Error creating JSON response", http.StatusInternalServerError) + Logger.Println(err) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(c) diff --git a/controllers/route.go b/controllers/route.go index 074989db..2c1cdf50 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -264,16 +264,6 @@ func getTemplate(w http.ResponseWriter, tmpl string) *template.Template { return template.Must(templates, err) } -func checkError(e error, w http.ResponseWriter, m string, c int) bool { - if e != nil { - Logger.Println(e) - w.WriteHeader(c) - JSONResponse(w, models.Response{Success: false, Message: m}, http.StatusBadRequest) - return true - } - return false -} - // Flash handles the rendering flash messages func Flash(w http.ResponseWriter, r *http.Request, t string, m string) { session := ctx.Get(r, "session").(*sessions.Session) diff --git a/models/campaign.go b/models/campaign.go index 56570b35..263f954c 100644 --- a/models/campaign.go +++ b/models/campaign.go @@ -1,6 +1,7 @@ package models import ( + "errors" "fmt" "time" @@ -26,17 +27,32 @@ type Campaign struct { SMTP SMTP `json:"smtp"` } +// ErrCampaignNameNotSpecified indicates there was no template given by the user +var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified") + +// ErrGroupNotSpecified indicates there was no template given by the user +var ErrGroupNotSpecified = errors.New("No groups specified") + +// ErrTemplateNotSpecified indicates there was no template given by the user +var ErrTemplateNotSpecified = errors.New("No email template specified") + +// ErrTemplateNotFound indicates the template specified does not exist in the database +var ErrTemplateNotFound = errors.New("Template not found") + +// ErrGroupnNotFound indicates a group specified by the user does not exist in the database +var ErrGroupNotFound = errors.New("Group not found") + // Validate checks to make sure there are no invalid fields in a submitted campaign -func (c *Campaign) Validate() (string, bool) { +func (c *Campaign) Validate() error { switch { case c.Name == "": - return "Must specify campaign name", false + return ErrCampaignNameNotSpecified case len(c.Groups) == 0: - return "No groups specified", false + return ErrGroupNotSpecified case c.Template.Name == "": - return "No template specified", false + return ErrTemplateNotSpecified } - return "", true + return nil } // UpdateStatus changes the campaign status appropriately @@ -105,6 +121,9 @@ func GetCampaign(id int64, uid int64) (Campaign, error) { // PostCampaign inserts a campaign and all associated records into the database. func PostCampaign(c *Campaign, uid int64) error { + if err := c.Validate(); err != nil { + return err + } // Fill in the details c.UserId = uid c.CreatedDate = time.Now() @@ -115,7 +134,7 @@ func PostCampaign(c *Campaign, uid int64) error { c.Groups[i], err = GetGroupByName(g.Name, uid) if err == gorm.RecordNotFound { Logger.Printf("Error - Group %s does not exist", g.Name) - return err + return ErrGroupNotFound } else if err != nil { Logger.Println(err) return err @@ -125,7 +144,7 @@ func PostCampaign(c *Campaign, uid int64) error { t, err := GetTemplateByName(c.Template.Name, uid) if err == gorm.RecordNotFound { Logger.Printf("Error - Template %s does not exist", t.Name) - return err + return ErrTemplateNotFound } else if err != nil { Logger.Println(err) return err diff --git a/models/group.go b/models/group.go index b7dd439c..4d2ecf61 100644 --- a/models/group.go +++ b/models/group.go @@ -1,12 +1,15 @@ package models import ( + "errors" "net/mail" "time" "github.com/jinzhu/gorm" ) +// Group contains the fields needed for a user -> group mapping +// Groups contain 1..* Targets type Group struct { Id int64 `json:"id"` UserId int64 `json:"-"` @@ -15,11 +18,14 @@ type Group struct { Targets []Target `json:"targets" sql:"-"` } +// GroupTarget is used for a many-to-many relationship between 1..* Groups and 1..* Targets type GroupTarget struct { GroupId int64 `json:"-"` TargetId int64 `json:"-"` } +// Target contains the fields needed for individual targets specified by the user +// Groups contain 1..* Targets, but 1 Target may belong to 1..* Groups type Target struct { Id int64 `json:"-"` FirstName string `json:"first_name"` @@ -27,6 +33,23 @@ type Target struct { Email string `json:"email"` } +// ErrGroupNameNotSpecified is thrown when a group name is not specified +var ErrGroupNameNotSpecified = errors.New("Group name not specified") + +// ErrNoTargetsSpecified is thrown when no targets are specified by the user +var ErrNoTargetsSpecified = errors.New("No targets specified") + +// Validate performs validation on a group given by the user +func (g *Group) Validate() error { + switch { + case g.Name == "": + return ErrGroupNameNotSpecified + case len(g.Targets) == 0: + return ErrNoTargetsSpecified + } + return nil +} + // GetGroups returns the groups owned by the given user. func GetGroups(uid int64) ([]Group, error) { gs := []Group{} @@ -76,7 +99,11 @@ func GetGroupByName(n string, uid int64) (Group, error) { // PostGroup creates a new group in the database. func PostGroup(g *Group) error { - // Insert into the DB + Logger.Printf("%v", g.Targets) + if err := g.Validate(); err != nil { + return err + } + // Insert the group into the DB err = db.Save(g).Error if err != nil { Logger.Println(err) @@ -189,6 +216,7 @@ func insertTargetIntoGroup(t Target, gid int64) error { return nil } +// GetTargets performs a many-to-many select to get all the Targets for a Group func GetTargets(gid int64) ([]Target, error) { ts := []Target{} err := db.Table("targets").Select("targets.id, targets.email, targets.first_name, targets.last_name").Joins("left join group_targets gt ON targets.id = gt.target_id").Where("gt.group_id=?", gid).Scan(&ts).Error diff --git a/models/template.go b/models/template.go index c153dff4..e66f74bb 100644 --- a/models/template.go +++ b/models/template.go @@ -20,7 +20,7 @@ type Template struct { } // ErrTemplateNameNotSpecified is thrown when a template name is not specified -var ErrTemplateNameNotSpecified = errors.New("Template Name not specified") +var ErrTemplateNameNotSpecified = errors.New("Template name not specified") // ErrTemplateMissingParameter is thrown when a needed parameter is not provided var ErrTemplateMissingParameter = errors.New("Need to specify at least plaintext or HTML content") @@ -112,8 +112,11 @@ func PostTemplate(t *Template) error { // PutTemplate edits an existing template in the database. // Per the PUT Method RFC, it presumes all data for a template is provided. func PutTemplate(t *Template) error { + if err := t.Validate(); err != nil { + return err + } // Delete all attachments, and replace with new ones - err := db.Debug().Where("template_id=?", t.Id).Delete(&Attachment{}).Error + err = db.Where("template_id=?", t.Id).Delete(&Attachment{}).Error if err != nil && err != gorm.RecordNotFound { Logger.Println(err) return err @@ -129,7 +132,7 @@ func PutTemplate(t *Template) error { return err } } - err = db.Debug().Where("id=?", t.Id).Save(t).Error + err = db.Where("id=?", t.Id).Save(t).Error if err != nil { Logger.Println(err) return err diff --git a/static/js/app/controllers.js b/static/js/app/controllers.js index 43decd6f..9db97850 100644 --- a/static/js/app/controllers.js +++ b/static/js/app/controllers.js @@ -132,7 +132,23 @@ app.controller('DashboardCtrl', function($scope, $filter, $location, CampaignSer }); }) app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupService, TemplateService, ngTableParams, $http) { - $scope.flashes = [] + $scope.errorFlash = function(message) { + $scope.flashes = {"main" : [], "modal" : []}; + $scope.flashes.main.push({ + "type": "danger", + "message": message, + "icon": "fa-exclamation-circle" + }) + } + + $scope.successFlash = function(message) { + $scope.flashes = {"main" : [], "modal" : []};; + $scope.flashes.main.push({ + "type": "success", + "message": message, + "icon": "fa-check-circle" + }) + } $scope.mainTableParams = new ngTableParams({ page: 1, // show first page count: 10, // count per page @@ -158,21 +174,6 @@ app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupSe $scope.templates = templates; }) - $scope.addGroup = function(group) { - if (group.name != "") { - $scope.campaign.groups.push({ - name: group.name - }); - group.name = "" - $scope.editGroupTableParams.reload() - } - }; - - $scope.removeGroup = function(group) { - $scope.campaign.groups.splice($scope.campaign.groups.indexOf(group), 1); - $scope.editGroupTableParams.reload() - }; - $scope.newCampaign = function() { $scope.campaign = { name: '', @@ -188,11 +189,19 @@ app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupSe scope: $scope }); - modalInstance.result.then(function(selectedItem) { - $scope.selected = selectedItem; + modalInstance.result.then(function(message) { + $scope.successFlash(message) + $scope.campaign = { + name: '', + groups: [], + }; }, function() { - console.log('closed') + $scope.campaign = { + name: '', + groups: [], + }; }); + $scope.mainTableParams.reload() }; $scope.editGroupTableParams = new ngTableParams({ @@ -209,35 +218,64 @@ app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupSe } }); - $scope.saveCampaign = function(campaign) { - $scope.flashes = [] - $scope.validated = true - var newCampaign = new CampaignService(campaign); - newCampaign.$save({}, function() { - $scope.successFlash("Campaign added successfully") - $scope.campaigns.push(newCampaign); - $scope.mainTableParams.reload() - }, function(response) { - $scope.errorFlash(response.data) - }); - $scope.campaign = { - groups: [], - }; - $scope.editGroupTableParams.reload() - } - $scope.deleteCampaign = function(campaign) { var deleteCampaign = new CampaignService(campaign); deleteCampaign.$delete({ id: deleteCampaign.id - }, function() { - $scope.successFlash("Campaign deleted successfully") + }, function(response) { + if (response.success) { + $scope.successFlash(response.message) + } else { + $scope.errorFlash(response.message) + } $scope.mainTableParams.reload(); }); } +}); +var CampaignModalCtrl = function($scope, CampaignService, $modalInstance) { $scope.errorFlash = function(message) { - $scope.flashes.push({ + $scope.flashes = {"main" : [], "modal" : []}; + $scope.flashes.modal.push({ + "type": "danger", + "message": message, + "icon": "fa-exclamation-circle" + }) + } + $scope.addGroup = function(group) { + if (group.name != "") { + $scope.campaign.groups.push({ + name: group.name + }); + group.name = "" + $scope.editGroupTableParams.reload() + } + }; + + $scope.removeGroup = function(group) { + $scope.campaign.groups.splice($scope.campaign.groups.indexOf(group), 1); + $scope.editGroupTableParams.reload() + }; + $scope.cancel = function() { + $modalInstance.dismiss('cancel'); + }; + $scope.ok = function(campaign) { + var newCampaign = new CampaignService(campaign); + newCampaign.$save({}, function() { + $modalInstance.close("Campaign added successfully") + $scope.campaigns.push(newCampaign); + $scope.mainTableParams.reload() + }, function(response) { + $scope.errorFlash(response.data.message) + }); + } +}; + +app.controller('CampaignResultsCtrl', function($scope, $filter, CampaignService, GroupService, ngTableParams, $http, $window) { + id = $window.location.hash.split('/')[2]; + $scope.errorFlash = function(message) { + $scope.flashes = {"main" : [], "modal" : []}; + $scope.flashes.main.push({ "type": "danger", "message": message, "icon": "fa-exclamation-circle" @@ -245,27 +283,13 @@ app.controller('CampaignCtrl', function($scope, $modal, CampaignService, GroupSe } $scope.successFlash = function(message) { - $scope.flashes.push({ + $scope.flashes = {"main" : [], "modal" : []};; + $scope.flashes.main.push({ "type": "success", "message": message, "icon": "fa-check-circle" }) } -}); - -var CampaignModalCtrl = function($scope, $modalInstance) { - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - $scope.ok = function(campaign) { - $modalInstance.dismiss("") - $scope.saveCampaign(campaign) - } -}; - -app.controller('CampaignResultsCtrl', function($scope, $filter, CampaignService, GroupService, ngTableParams, $http, $window) { - id = $window.location.hash.split('/')[2]; - $scope.flashes = [] $scope.mainTableParams = new ngTableParams({ page: 1, // show first page count: 10, // count per page @@ -378,51 +402,43 @@ app.controller('CampaignResultsCtrl', function($scope, $filter, CampaignService, xAxis: { type: 'datetime', dateTimeLabelFormats: { // don't display the dummy year - day: "%e of %b", - hour: "%l:%M", - second: '%l:%M:%S', - minute: '%l:%M' - }, - max: Date.now(), - title: { - text: 'Date' - } + day: "%e of %b", + hour: "%l:%M", + second: '%l:%M:%S', + minute: '%l:%M' }, + max: Date.now(), + title: { + text: 'Date' + } }, - series: [{ - name: "Events", - data: $scope.campaign.timeline - }], - title: { - text: 'Campaign Timeline' - }, - size: { - height: 300 - }, - credits: { - enabled: false - }, - loading: false, - } - params.total(campaign.results.length) - $defer.resolve(campaign.results.slice((params.page() - 1) * params.count(), params.page() * params.count())); - }) - } - }); - - $scope.errorFlash = function(message) { - $scope.flashes.push({ - "type": "danger", - "message": message, - "icon": "fa-exclamation-circle" + }, + series: [{ + name: "Events", + data: $scope.campaign.timeline + }], + title: { + text: 'Campaign Timeline' + }, + size: { + height: 300 + }, + credits: { + enabled: false + }, + loading: false, + } + params.total(campaign.results.length) + $defer.resolve(campaign.results.slice((params.page() - 1) * params.count(), params.page() * params.count())); }) } }); +}) app.controller('GroupCtrl', function($scope, $modal, GroupService, ngTableParams) { $scope.errorFlash = function(message) { - $scope.flashes = []; - $scope.flashes.push({ + $scope.flashes = {"main" : [], "modal" : []}; + $scope.flashes.main.push({ "type": "danger", "message": message, "icon": "fa-exclamation-circle" @@ -430,8 +446,8 @@ app.controller('GroupCtrl', function($scope, $modal, GroupService, ngTableParams } $scope.successFlash = function(message) { - $scope.flashes = []; - $scope.flashes.push({ + $scope.flashes = {"main" : [], "modal" : []};; + $scope.flashes.main.push({ "type": "success", "message": message, "icon": "fa-check-circle" @@ -487,8 +503,64 @@ app.controller('GroupCtrl', function($scope, $modal, GroupService, ngTableParams controller: GroupModalCtrl, scope: $scope }); + modalInstance.result.then(function(message) { + $scope.successFlash(message) + $scope.group = { + name: '', + targets: [], + }; + }, function() { + $scope.group = { + name: '', + targets: [], + }; + }); + $scope.mainTableParams.reload() + }; + $scope.deleteGroup = function(group) { + var deleteGroup = new GroupService(group); + deleteGroup.$delete({ + id: deleteGroup.id + }, function(response) { + if (response.success) { + $scope.successFlash(response.message) + } else { + $scope.errorFlash(response.message) + } + $scope.mainTableParams.reload(); + }); + } +}) + +var GroupModalCtrl = function($scope, GroupService, $modalInstance, $upload) { + $scope.errorFlash = function(message) { + $scope.flashes = {"main" : [], "modal" : []}; + $scope.flashes.modal.push({ + "type": "danger", + "message": message, + "icon": "fa-exclamation-circle" + }) + } + $scope.onFileSelect = function($file) { + $scope.upload = $upload.upload({ + url: '/api/import/group', + data: {}, + file: $file, + }).progress(function(evt) { + console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total)); + }).success(function(data, status, headers, config) { + angular.forEach(data, function(record, key) { + $scope.group.targets.push({ + first_name : record.first_name, + last_name : record.last_name, + email: record.email + }); + }); + $scope.editGroupTableParams.reload(); + }); + }; $scope.addTarget = function() { if ($scope.newTarget.email != "") { $scope.group.targets.push({ @@ -502,60 +574,27 @@ app.controller('GroupCtrl', function($scope, $modal, GroupService, ngTableParams $scope.group.targets.splice($scope.group.targets.indexOf(target), 1); $scope.editGroupTableParams.reload() }; - $scope.saveGroup = function(group) { + $scope.cancel = function() { + $modalInstance.dismiss(); + }; + $scope.ok = function(group) { var newGroup = new GroupService(group); if ($scope.newGroup) { newGroup.$save({}, function() { $scope.groups.push(newGroup); - $scope.mainTableParams.reload() + $modalInstance.close("Group created successfully!") + }, function(error){ + $scope.errorFlash(error.data.message) }); } else { newGroup.$update({ id: newGroup.id + },function(){ + $modalInstance.close("Group updated successfully!") + }, function(error){ + $scope.errorFlash(error.data.message) }) } - $scope.group = { - name: '', - targets: [], - }; - $scope.editGroupTableParams.reload() - } - $scope.deleteGroup = function(group) { - var deleteGroup = new GroupService(group); - deleteGroup.$delete({ - id: deleteGroup.id - }, function() { - $scope.mainTableParams.reload(); - }); - } -}) - -var GroupModalCtrl = function($scope, $modalInstance, $upload) { - $scope.onFileSelect = function($file) { - $scope.upload = $upload.upload({ - url: '/api/import/group', - data: {}, - file: $file, - }).progress(function(evt) { - console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total)); - }).success(function(data, status, headers, config) { - angular.forEach(data, function(record, key) { - $scope.group.targets.push({ - first_name : record.first_name, - last_name : record.last_name, - email: record.email - }); - }); - $scope.editGroupTableParams.reload(); - //.error(...) - }); - }; - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - $scope.ok = function(group) { - $modalInstance.dismiss('') - $scope.saveGroup(group) }; } @@ -623,11 +662,11 @@ app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTable text: '', }; }, function() { - $scope.template = { + $scope.template = { name: '', html: '', text: '', - }; + }; }); }; @@ -648,7 +687,7 @@ app.controller('TemplateCtrl', function($scope, $modal, TemplateService, ngTable var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstance, $modal) { $scope.editorOptions = { - fullPage: true, + fullPage: true, allowedContent: true, } $scope.errorFlash = function(message) { @@ -673,9 +712,9 @@ var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstanc var reader = new FileReader(); reader.onload = function(e) { $scope.template.attachments.push({ - name : file.name, - content : reader.result.split(",")[1], - type : file.type || "application/octet-stream" + name : file.name, + content : reader.result.split(",")[1], + type : file.type || "application/octet-stream" }) $scope.$apply(); } @@ -700,9 +739,8 @@ var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstanc // Close the dialog, returning the template $modalInstance.close("Template created successfully!") }, function(error){ - // Otherwise, leave the dialog open, showing the error - console.log(error.data) - $scope.errorFlash(error.data.message) + // Otherwise, leave the dialog open, showing the error + $scope.errorFlash(error.data.message) }); } else { newTemplate.$update({ @@ -710,7 +748,6 @@ var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstanc }, function(){ $modalInstance.close("Template updated successfully!") }, function(error){ - console.log(error.data) $scope.errorFlash(error.data.message) }) } @@ -733,109 +770,109 @@ var TemplateModalCtrl = function($scope, TemplateService, $upload, $modalInstanc }; var ImportEmailCtrl = function($scope, $http, $modalInstance) { - $scope.email = {} - $scope.ok = function() { - // Simple POST request example (passing data) : - $http.post('/api/import/email', $scope.email.raw, - { headers : {"Content-Type" : "text/plain"}} + $scope.email = {} + $scope.ok = function() { + // Simple POST request example (passing data) : + $http.post('/api/import/email', $scope.email.raw, + { headers : {"Content-Type" : "text/plain"}} ).success(function(data) {console.log("Success: " + data)}) .error(function(data) {console.log("Error: " + data)}); $modalInstance.close($scope.email.raw) - }; - $scope.cancel = function() {$modalInstance.dismiss()} +}; +$scope.cancel = function() {$modalInstance.dismiss()} }; app.controller('LandingPageCtrl', function($scope, $modal, LandingPageService, ngTableParams) { - $scope.errorFlash = function(message) { - $scope.flashes = []; - $scope.flashes.push({ - "type": "danger", - "message": message, - "icon": "fa-exclamation-circle" - }) - } + $scope.errorFlash = function(message) { + $scope.flashes = []; + $scope.flashes.push({ + "type": "danger", + "message": message, + "icon": "fa-exclamation-circle" + }) + } - $scope.successFlash = function(message) { - $scope.flashes = []; - $scope.flashes.push({ - "type": "success", - "message": message, - "icon": "fa-check-circle" - }) - } + $scope.successFlash = function(message) { + $scope.flashes = []; + $scope.flashes.push({ + "type": "success", + "message": message, + "icon": "fa-check-circle" + }) + } - $scope.mainTableParams = new ngTableParams({ - page: 1, // show first page - count: 10, // count per page - sorting: { - name: 'asc' // initial sorting - } - }, { - total: 0, // length of data - getData: function($defer, params) { - LandingPageService.query(function(pages) { - $scope.pages = pages - params.total(pages.length) - $defer.resolve(pages.slice((params.page() - 1) * params.count(), params.page() * params.count())); - }) - } - }); + $scope.mainTableParams = new ngTableParams({ + page: 1, // show first page + count: 10, // count per page + sorting: { + name: 'asc' // initial sorting + } + }, { + total: 0, // length of data + getData: function($defer, params) { + LandingPageService.query(function(pages) { + $scope.pages = pages + params.total(pages.length) + $defer.resolve(pages.slice((params.page() - 1) * params.count(), params.page() * params.count())); + }) + } + }); - $scope.editPage = function(page) { - if (page === 'new') { - $scope.newPage = true; - $scope.page = { - name: '', - html: '', - }; + $scope.editPage = function(page) { + if (page === 'new') { + $scope.newPage = true; + $scope.page = { + name: '', + html: '', + }; - } else { - $scope.newPage = false; - $scope.page = page; - } - var modalInstance = $modal.open({ - templateUrl: '/js/app/partials/modals/LandingPageModal.html', - controller: LandingPageModalCtrl, - scope: $scope - }); + } else { + $scope.newPage = false; + $scope.page = page; + } + var modalInstance = $modal.open({ + templateUrl: '/js/app/partials/modals/LandingPageModal.html', + controller: LandingPageModalCtrl, + scope: $scope + }); - modalInstance.result.then(function(selectedItem) { - $scope.selected = selectedItem; - }, function() { - console.log('closed') - }); - }; + modalInstance.result.then(function(selectedItem) { + $scope.selected = selectedItem; + }, function() { + console.log('closed') + }); + }; - $scope.savePage = function(page) { - var newPage = new LandingPageService(page); - if ($scope.newPage) { - newPage.$save({}, function() { - $scope.pages.push(newPage); - $scope.mainTableParams.reload() - }); - } else { - newPage.$update({ - id: newPage.id - }) - } - $scope.page = { - name: '', - html: '', - }; - } - $scope.deletePage = function(page) { - var deletePage = new LandingPageService(page); - deletePage.$delete({ - id: deletePage.id - }, function(response) { - if (response.success) { - $scope.successFlash(response.message) - } else { - $scope.errorFlash(response.message) - } - $scope.mainTableParams.reload(); - }); - } + $scope.savePage = function(page) { + var newPage = new LandingPageService(page); + if ($scope.newPage) { + newPage.$save({}, function() { + $scope.pages.push(newPage); + $scope.mainTableParams.reload() + }); + } else { + newPage.$update({ + id: newPage.id + }) + } + $scope.page = { + name: '', + html: '', + }; + } + $scope.deletePage = function(page) { + var deletePage = new LandingPageService(page); + deletePage.$delete({ + id: deletePage.id + }, function(response) { + if (response.success) { + $scope.successFlash(response.message) + } else { + $scope.errorFlash(response.message) + } + $scope.mainTableParams.reload(); + }); + } }); var LandingPageModalCtrl = function($scope, $modalInstance) { @@ -887,13 +924,13 @@ app.controller('SettingsCtrl', function($scope, $http, $window) { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload) }) - .success(function(response) { - if (response.success) { - $scope.user.api_key = response.data; - $window.user.api_key = response.data; - $scope.successFlash(response.message) - } - }) + .success(function(response) { + if (response.success) { + $scope.user.api_key = response.data; + $window.user.api_key = response.data; + $scope.successFlash(response.message) + } + }) } $scope.save_settings = function() { $http({ @@ -904,12 +941,12 @@ app.controller('SettingsCtrl', function($scope, $http, $window) { 'Content-Type': 'application/x-www-form-urlencoded' } }) - .success(function(data) { - if (data.success) { - $scope.successFlash(data.message) - } else { - $scope.errorFlash(data.message) - } - }) + .success(function(data) { + if (data.success) { + $scope.successFlash(data.message) + } else { + $scope.errorFlash(data.message) + } + }) } }) diff --git a/static/js/app/partials/campaigns.html b/static/js/app/partials/campaigns.html index 3701890c..e2f45a52 100644 --- a/static/js/app/partials/campaigns.html +++ b/static/js/app/partials/campaigns.html @@ -25,7 +25,7 @@ Campaigns
-
+
{{flash.message}}
@@ -43,7 +43,7 @@
- +
{{campaign.created_date | date:'medium'}} {{campaign.name}}
diff --git a/static/js/app/partials/landing_pages.html b/static/js/app/partials/landing_pages.html index f8a77e6d..cbc8b6e5 100644 --- a/static/js/app/partials/landing_pages.html +++ b/static/js/app/partials/landing_pages.html @@ -43,7 +43,7 @@
- +
{{page.name}}
diff --git a/static/js/app/partials/modals/userModal.html b/static/js/app/partials/modals/userModal.html index 6ebd01a0..f3ce3747 100644 --- a/static/js/app/partials/modals/userModal.html +++ b/static/js/app/partials/modals/userModal.html @@ -6,6 +6,11 @@