mirror of https://github.com/gophish/gophish
Starting to integrate landing page functionality (still not working).
Also did some minor cleanup.pull/24/head
parent
d567153d2a
commit
c318424ac0
|
@ -256,6 +256,79 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API_Pages handles requests for the /api/pages/ endpoint
|
||||||
|
func API_Pages(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch {
|
||||||
|
case r.Method == "GET":
|
||||||
|
ps, err := models.GetPages(ctx.Get(r, "user_id").(int64))
|
||||||
|
if checkError(err, w, "Pages not found", http.StatusNotFound) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, ps, http.StatusOK)
|
||||||
|
//POST: Create a new page and return it as JSON
|
||||||
|
case r.Method == "POST":
|
||||||
|
p := models.Page{}
|
||||||
|
// Put the request into a page
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&p)
|
||||||
|
if checkError(err, w, "Invalid Request", http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = models.GetPageByName(p.Name, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err != gorm.RecordNotFound {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.ModifiedDate = time.Now()
|
||||||
|
p.UserId = ctx.Get(r, "user_id").(int64)
|
||||||
|
err = models.PostPage(&p)
|
||||||
|
if checkError(err, w, "Error inserting page", http.StatusInternalServerError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, p, http.StatusCreated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case r.Method == "GET":
|
||||||
|
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) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK)
|
||||||
|
case r.Method == "PUT":
|
||||||
|
p = models.Page{}
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&p)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
if p.Id != id {
|
||||||
|
http.Error(w, "Error: /:id and template_id mismatch", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Validate()
|
||||||
|
/* if checkError(err, w, http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}*/
|
||||||
|
p.ModifiedDate = time.Now()
|
||||||
|
p.UserId = ctx.Get(r, "user_id").(int64)
|
||||||
|
err = models.PutPage(&p)
|
||||||
|
if checkError(err, w, "Error updating group", http.StatusInternalServerError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, p, http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func API_Import_Group(w http.ResponseWriter, r *http.Request) {
|
func API_Import_Group(w http.ResponseWriter, r *http.Request) {
|
||||||
Logger.Println("Parsing CSV....")
|
Logger.Println("Parsing CSV....")
|
||||||
ts, err := util.ParseCSV(r)
|
ts, err := util.ParseCSV(r)
|
||||||
|
|
|
@ -39,6 +39,8 @@ func CreateAdminRouter() http.Handler {
|
||||||
api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey))
|
api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey))
|
api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))
|
api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))
|
||||||
|
api.HandleFunc("/pages/", Use(API_Pages, mid.RequireAPIKey))
|
||||||
|
api.HandleFunc("/pages/{id:[0-9]+}", Use(API_Pages_Id, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/import/group", API_Import_Group)
|
api.HandleFunc("/import/group", API_Import_Group)
|
||||||
|
|
||||||
// Setup static file serving
|
// Setup static file serving
|
||||||
|
|
|
@ -8,13 +8,18 @@ import (
|
||||||
"github.com/coopernurse/gorp"
|
"github.com/coopernurse/gorp"
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/jordan-wright/gophish/config"
|
"github.com/jordan-wright/gophish/config"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Conn is the connection to the SQLite database
|
||||||
var Conn *gorp.DbMap
|
var Conn *gorp.DbMap
|
||||||
var db gorm.DB
|
var db gorm.DB
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// ErrUsernameTaken is thrown when a user attempts to register a username that is taken.
|
||||||
var ErrUsernameTaken = errors.New("username already taken")
|
var ErrUsernameTaken = errors.New("username already taken")
|
||||||
|
|
||||||
|
// Logger is a global logger used to show informational, warning, and error messages
|
||||||
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
|
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,6 +40,7 @@ type Flash struct {
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response contains the attributes found in an API response
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
|
@ -62,16 +68,17 @@ func Setup() error {
|
||||||
db.CreateTable(GroupTarget{})
|
db.CreateTable(GroupTarget{})
|
||||||
db.CreateTable(Template{})
|
db.CreateTable(Template{})
|
||||||
db.CreateTable(Attachment{})
|
db.CreateTable(Attachment{})
|
||||||
|
db.CreateTable(Page{})
|
||||||
db.CreateTable(SMTP{})
|
db.CreateTable(SMTP{})
|
||||||
db.CreateTable(Event{})
|
db.CreateTable(Event{})
|
||||||
db.CreateTable(Campaign{})
|
db.CreateTable(Campaign{})
|
||||||
//Create the default user
|
//Create the default user
|
||||||
init_user := User{
|
initUser := User{
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
Hash: "$2a$10$IYkPp0.QsM81lYYPrQx6W.U6oQGw7wMpozrKhKAHUBVL4mkm/EvAS", //gophish
|
Hash: "$2a$10$IYkPp0.QsM81lYYPrQx6W.U6oQGw7wMpozrKhKAHUBVL4mkm/EvAS", //gophish
|
||||||
ApiKey: "12345678901234567890123456789012",
|
ApiKey: "12345678901234567890123456789012",
|
||||||
}
|
}
|
||||||
err = db.Save(&init_user).Error
|
err = db.Save(&initUser).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Page contains the fields used for a Page model
|
||||||
|
type Page struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId int64 `json:"-"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
HTML string `json:"html"`
|
||||||
|
ModifiedDate time.Time `json:"modified_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrPageNameNotSpecified is thrown if the name of the landing page is blank.
|
||||||
|
var ErrPageNameNotSpecified = errors.New("Template Name not specified")
|
||||||
|
|
||||||
|
// Validate ensures that a page contains the appropriate details
|
||||||
|
func (p *Page) Validate() error {
|
||||||
|
if p.Name == "" {
|
||||||
|
return ErrPageNameNotSpecified
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPages returns the pages owned by the given user.
|
||||||
|
func GetPages(uid int64) ([]Page, error) {
|
||||||
|
ps := []Page{}
|
||||||
|
err := db.Where("user_id=?", uid).Find(&ps).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
return ps, err
|
||||||
|
}
|
||||||
|
return ps, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPage returns the page, if it exists, specified by the given id and user_id.
|
||||||
|
func GetPage(id int64, uid int64) (Page, error) {
|
||||||
|
p := Page{}
|
||||||
|
err := db.Where("user_id=? and id=?", uid, id).Find(&p).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPageByName returns the page, if it exists, specified by the given name and user_id.
|
||||||
|
func GetPageByName(n string, uid int64) (Page, error) {
|
||||||
|
p := Page{}
|
||||||
|
err := db.Where("user_id=? and name=?", uid, n).Find(&p).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostPage creates a new page in the database.
|
||||||
|
func PostPage(p *Page) error {
|
||||||
|
// Insert into the DB
|
||||||
|
err := db.Save(p).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutPage edits an existing Page in the database.
|
||||||
|
// Per the PUT Method RFC, it presumes all data for a page is provided.
|
||||||
|
func PutPage(p *Page) error {
|
||||||
|
err := db.Debug().Where("id=?", p.Id).Save(p).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePage deletes an existing page in the database.
|
||||||
|
// An error is returned if a page with the given user id and page id is not found.
|
||||||
|
func DeletePage(id int64, uid int64) error {
|
||||||
|
err = db.Where("user_id=?", uid).Delete(Page{Id: id}).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -29,6 +29,11 @@ app.config(function($routeProvider) {
|
||||||
controller: 'TemplateCtrl'
|
controller: 'TemplateCtrl'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.when('/landing_pages', {
|
||||||
|
templateUrl: 'js/app/partials/landing_pages.html',
|
||||||
|
controller: 'LandingPageCtrl'
|
||||||
|
})
|
||||||
|
|
||||||
.when('/settings', {
|
.when('/settings', {
|
||||||
templateUrl: 'js/app/partials/settings.html',
|
templateUrl: 'js/app/partials/settings.html',
|
||||||
controller: 'SettingsCtrl'
|
controller: 'SettingsCtrl'
|
||||||
|
|
|
@ -684,6 +684,109 @@ var TemplateModalCtrl = function($scope, $upload, $modalInstance) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.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.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
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var LandingPageModalCtrl = function($scope, $modalInstance) {
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
$scope.ok = function(page) {
|
||||||
|
$modalInstance.dismiss('')
|
||||||
|
$scope.savePage(page)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
app.controller('SettingsCtrl', function($scope, $http, $window) {
|
app.controller('SettingsCtrl', function($scope, $http, $window) {
|
||||||
$scope.flashes = [];
|
$scope.flashes = [];
|
||||||
$scope.user = user;
|
$scope.user = user;
|
||||||
|
|
|
@ -26,4 +26,14 @@ app.factory('TemplateService', function($resource) {
|
||||||
method: 'PUT'
|
method: 'PUT'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.factory('LandingPageService', function($resource) {
|
||||||
|
return $resource('/api/pages/:id?api_key=' + user.api_key, {
|
||||||
|
id : "@id"
|
||||||
|
}, {
|
||||||
|
update: {
|
||||||
|
method: 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -25,7 +27,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||||
<i class="fa fa-cogs fa-lg"></i>
|
<i class="fa fa-cogs fa-lg"></i>
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-md-2 sidebar">
|
||||||
|
<ul class="nav nav-sidebar">
|
||||||
|
<li><a href="#">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/campaigns">Campaigns</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/users">Users & Groups</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li class="active"><a href="#/landing_pages">Landing Pages</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/settings">Settings</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/api/">API Documentation</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="LandingPageCtrl">
|
||||||
|
<h1 class="page-header">
|
||||||
|
Landing Pages
|
||||||
|
</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div ng-repeat="flash in flashes" style="text-align:center" class="alert alert-{{flash.type}}">
|
||||||
|
<i class="fa {{flash.icon}}"></i> {{flash.message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="editPage('new')" data-toggle="modal" data-target="#newLandingPageModal"><i class="fa fa-plus"></i> New Page</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<table ng-table="mainTableParams" class="table table-hover table-striped table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="page in $data" class="editable-row">
|
||||||
|
<td data-title="'Name'" sortable="'name'" class="col-sm-1">{{page.name}}</td>
|
||||||
|
<td data-title="'Modified Date'" class="col-sm-1">{{page.modified_date | date:'medium'}}</td>
|
||||||
|
<div class="btn-group" style="float: right;">
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle edit-button" data-toggle="dropdown">
|
||||||
|
<span class="caret" style="border-top-color:#FFFFFF"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" style="left:auto; right:0;" role="menu">
|
||||||
|
<li><a ng-click="editPage(page)">Edit</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li><a ng-click="deletePage(page)">Delete</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!-- New Template Modal -->
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" ng-click="cancel()">×</button>
|
||||||
|
<h4 class="modal-title" ng-hide="newPage" id="pageModalLabel">Edit Page: {{page.name}}</h4>
|
||||||
|
<h4 class="modal-title" ng-show="newPage" id="pageModalLabel">New Page</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<label class="control-label" for="name">Name:</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" ng-model="page.name" placeholder="Page name" id="name" autofocus/>
|
||||||
|
</div>
|
||||||
|
<!-- Nav tabs -->
|
||||||
|
<fieldset disabled>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-danger btn-disabled"><i class="fa fa-download"></i> Clone Site (Coming Soon!)</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<tabset>
|
||||||
|
<tab heading="HTML">
|
||||||
|
<textarea rows="10" class="form-control" ng-model="page.html" placeholder="HTML"></textarea>
|
||||||
|
</tab>
|
||||||
|
<tab heading="Preview">
|
||||||
|
<div ng-model="page.html" contenteditable></div>
|
||||||
|
</tab>
|
||||||
|
</tabset>
|
||||||
|
<br />
|
||||||
|
<fieldset disabled>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-danger btn-disabled"><i class="fa fa-external-link-square"></i> Preview in New Window (Coming Soon!)</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="ok(template)" data-dismiss="modal">Save Page</button>
|
||||||
|
</div>
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active"><a href="#/settings">Settings</a>
|
<li class="active"><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="active"><a href="#/templates">Templates</a>
|
<li class="active"><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -20,7 +22,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="TemplateCtrl">
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" ng-controller="TemplateCtrl">
|
||||||
<h1 class="page-header">
|
<h1 class="page-header">
|
||||||
Templates
|
Email Templates
|
||||||
</h1>
|
</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button>
|
<button type="button" class="btn btn-primary" ng-click="editTemplate('new')"><i class="fa fa-plus"></i> New Template</button>
|
||||||
|
@ -50,4 +52,4 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="active"><a href="#/users">Users & Groups</a>
|
<li class="active"><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -55,7 +55,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/users">Users & Groups</a>
|
<li><a href="#/users">Users & Groups</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -91,7 +93,7 @@
|
||||||
<!-- Placed at the end of the document so the pages load faster -->
|
<!-- Placed at the end of the document so the pages load faster -->
|
||||||
<script src="/js/jquery.js"></script>
|
<script src="/js/jquery.js"></script>
|
||||||
<script src="/js/bootstrap.min.js"></script>
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
<script src="/js/angular-file-upload-shim.min.js"></script>
|
<script src="/js/angular-file-upload-shim.min.js"></script>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.js"></script>
|
||||||
<script src="/js/angular-file-upload.min.js"></script>
|
<script src="/js/angular-file-upload.min.js"></script>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#/templates">Templates</a>
|
<li><a href="#/templates">Templates</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="#/landing_pages">Landing Pages</a>
|
||||||
|
</li>
|
||||||
<li><a href="#/settings">Settings</a>
|
<li><a href="#/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
|
|
|
@ -60,7 +60,6 @@ func processCampaign(c *models.Campaign) {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
e.HTML = html_buff.Bytes()
|
e.HTML = html_buff.Bytes()
|
||||||
//buff.Reset()
|
|
||||||
tmpl, err = template.New("text_template").Parse(c.Template.Text)
|
tmpl, err = template.New("text_template").Parse(c.Template.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
|
@ -70,7 +69,6 @@ func processCampaign(c *models.Campaign) {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
e.Text = text_buff.Bytes()
|
e.Text = text_buff.Bytes()
|
||||||
//buff.Reset()
|
|
||||||
Logger.Println("Creating email using template")
|
Logger.Println("Creating email using template")
|
||||||
e.To = []string{t.Email}
|
e.To = []string{t.Email}
|
||||||
err = e.Send(c.SMTP.Host, auth)
|
err = e.Send(c.SMTP.Host, auth)
|
||||||
|
|
Loading…
Reference in New Issue