Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/translate-ui'
Browse files Browse the repository at this point in the history
  • Loading branch information
ewandennis committed Mar 16, 2016
2 parents d6f6bb5 + 480bf0f commit bc3c4a1
Show file tree
Hide file tree
Showing 13 changed files with 310 additions and 60 deletions.
3 changes: 2 additions & 1 deletion bower.json
Expand Up @@ -22,6 +22,7 @@
"bootstrap": "^3.3.6",
"angular-bootstrap": "^1.2.1",
"jquery": "^2.2.1",
"angular-sanitize": "~1.5.0"
"angular-sanitize": "^1.5.1",
"angular-ui-ace": "bower"
}
}
51 changes: 2 additions & 49 deletions index.js
@@ -1,55 +1,8 @@
'use strict';

var express = require('express')
, bodyParser = require('body-parser')
, app = express()
, srv = require('http').Server(app)
, translateRouter = require('./routes/translate')
, migrateRouter = require('./routes/migrate')
, sandboxDomainRouter = require('./routes/sandboxDomain');
var startServer = require('./server').startServer;

// ----------------------------------------------------------------------------

app.use(express.static(__dirname + '/static'));
app.use('/vendor', express.static(__dirname + '/vendor/client'));

// ----------------------------------------------------------------------------

app.use(bodyParser.json({
limit: '2mb'
}));

app.use(function(req, res, next) {
function conditionError(err) {
var keylist = ['name', 'message', 'description', 'code', 'row', 'col']
, ret = {};
Object.keys(err).forEach(function(k) {
if (keylist.indexOf(k) >= 0) {
ret[k] = err[k];
}
});
return ret;
}

function errorResponse(code, errlist) {
res.status(code).send({
errors: errlist.map(conditionError)
});
}

res.clientError = function(err) { errorResponse(400, [{message: err}]); };
res.serverError = function(err) { errorResponse(500, [err]); };
res.clientErrorList = function(errList) { errorResponse(400, errList); };
res.serverErrorList = function(errList) { errorResponse(500, errList); };

next();
});

app.use('/api/translate', translateRouter);
app.use('/api/migrate', migrateRouter);
app.use('/api/sandboxDomain', sandboxDomainRouter);

srv.listen(process.env.PORT || 3000, function() {
startServer(function(srv, app) {
console.log('Listening on ' + srv.address().port);
});

3 changes: 2 additions & 1 deletion lib/index.js
Expand Up @@ -61,7 +61,8 @@ function translateContent(content) {
}

function translateTemplate(mandrillTpl, options) {
var useSandboxDomain = options.useSandboxDomain || false
var opts = options || {}
, useSandboxDomain = opts.useSandboxDomain || false
, sandboxDomain = process.env.SPARKPOST_SANDBOX_DOMAIN || 'sparkpostbox.com'
, emailAddr = mandrillTpl.from_email
, html
Expand Down
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -19,12 +19,14 @@
"express": "^4.13.4",
"handlebars": "file:vendor/handlebars",
"mandrill-api": "^1.0.45",
"morgan": "^1.7.0",
"q": "^1.4.1",
"sparkpost": "^1.1.0"
},
"devDependencies": {
"chai": "^3.5.0",
"mocha": "^2.4.5"
"mocha": "^2.4.5",
"request": "^2.69.0"
},
"engines": {
"node": "~0.12",
Expand Down
9 changes: 5 additions & 4 deletions routes/translate.js
Expand Up @@ -24,15 +24,16 @@ router.post('/', function(req, res) {
code: req.body.mandrillTemplate
});

// Response
res.json({sparkPostTemplate: tpl.content.html});

} catch (err) {
if (!ctrl.errorResponse(err, res)) {
res.serverError('Unexpected error: ' + err);
err.message = 'Unexpected error: ' + err;
res.serverError(err);
throw err;
}
}

// Response
res.json({sparkPostTemplate: tpl.content.html});
});

module.exports = router;
63 changes: 63 additions & 0 deletions server.js
@@ -0,0 +1,63 @@
'use strict';

var express = require('express')
, bodyParser = require('body-parser')
, http = require('http')
, morgan = require('morgan')
, translateRouter = require('./routes/translate')
, migrateRouter = require('./routes/migrate')
, sandboxDomainRouter = require('./routes/sandboxDomain');

function startServer(next) {
// ----------------------------------------------------------------------------
var app = express()
, srv = http.Server(app);

app.use(express.static(__dirname + '/static'));
app.use('/vendor', express.static(__dirname + '/vendor/client'));

// ----------------------------------------------------------------------------

app.use(bodyParser.json({
limit: '2mb'
}));

app.use(morgan('combined'));

app.use(function(req, res, next) {
function conditionError(err) {
var keylist = ['name', 'message', 'description', 'code', 'row', 'col']
, ret = {};
Object.keys(err).forEach(function(k) {
if (keylist.indexOf(k) >= 0) {
ret[k] = err[k];
}
});
return ret;
}

function errorResponse(code, errlist) {
res.status(code).send({
errors: errlist.map(conditionError)
});
}

res.clientError = function(err) { errorResponse(400, [{message: err}]); };
res.serverError = function(err) { errorResponse(500, [err]); };
res.clientErrorList = function(errList) { errorResponse(400, errList); };
res.serverErrorList = function(errList) { errorResponse(500, errList); };

next();
});

app.use('/api/translate', translateRouter);
app.use('/api/migrate', migrateRouter);
app.use('/api/sandboxDomain', sandboxDomainRouter);

srv.listen(process.env.PORT || 3000, function() {
next(srv, app);
});
}

exports.startServer = startServer;

5 changes: 4 additions & 1 deletion static/index.html
Expand Up @@ -58,9 +58,12 @@ <h2 class="bad">Unsupported Features</h2>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="col-md-4 col-md-offset-2">
<a class="btn btn-success btn-lg" href="migrate.html" role="button">Migrate A Template &raquo;</a>
</div>
<div class="col-md-4">
<a class="btn btn-success btn-lg" href="translate.html" role="button">Translate Template Text &raquo;</a>
</div>
</div>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions static/main.css
Expand Up @@ -73,3 +73,7 @@ body.loading:after {
}
}

.ace_editor {
height: 30em;
}

5 changes: 2 additions & 3 deletions static/migrate.html
Expand Up @@ -23,7 +23,7 @@
<script src="vendor/jquery/dist/jquery.min.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="vendor/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="app.js"></script>
<script src="migrate.js"></script>
</head>

<body ng-controller="MigratorControl" ng-class="loading ? 'loading' : ''">
Expand All @@ -34,10 +34,9 @@
</nav>
<div class="jumbotron">
<div class="container">
<h1>Mandrill Template Migration</h1>
<p>From here you can migrate a template from Mandrill to SparkPost. You will need a few things to hand to get started:
<div class="row">
<div class="col-md-8 col-md-offset-2">
<p>From here you can migrate a template from Mandrill to SparkPost.<br/>You will need a few things to hand to get started:</p>
<dl class="dl-horizontal">
<dt>Mandrill API Key</dt>
<dd>Requires access to Mandrill's <code>templates/info</code>API call</dd>
Expand Down
File renamed without changes.
69 changes: 69 additions & 0 deletions static/translate.html
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en" ng-app="mdrlTranslationApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>SparkPost: Mandrill Template Translation</title>

<link href="vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

<link href="main.css" rel="stylesheet">

<link rel="icon" href="favicon-16.png" sizes="16x16" />
<link rel="icon" href="favicon-32.png" sizes="32x32" />
<link rel="icon" href="favicon-48.png" sizes="48x48" />
<link rel="shortcut icon" href="favicon.ico" />

<script src="vendor/ace-builds/src-min-noconflict/ace.js"></script>
<script src="vendor/angular/angular.js"></script>
<script src="vendor/angular-sanitize/angular-sanitize.min.js"></script>
<script src="vendor/angular-ui-ace/ui-ace.js"></script>
<script src="vendor/jquery/dist/jquery.min.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="vendor/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="translate.js"></script>
</head>

<body ng-controller="TranslatorControl">
<nav class="navbar navbar-fixed-top">
<div class="container">
<a class="navbar-brand" href="https://www.sparkpost.com"><img src="https://developers.sparkpost.com/images/logo-sparkpost-white.png" alt="SparkPost" width="150"></a>
</div>
</nav>
<div ng-show="loading" style="position: absolute; width: 100px; height: 100px; top: calc(50% - 70px); left: calc(50% - 50px);"> LOADING
</div>
<div class="jumbotron">
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<p>From here you can translate a template from Mandrill to SparkPost. To get started, paste your Mandrill template into the editor on the left below.</p>
<button ng-disabled="loading" ng-click="translate()" class="btn btn-success btn-lg">Translate!</button>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">
<div><pre ng-show="alert.pre" ng-bind="alert.msg"></pre></div>
<div ng-show="!alert.pre" ng-bind="alert.msg"></div>
</uib-alert>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Mandrill Template</h3>
<div id="mandrillEditor" ui-ace="{mode:'handlebars', onLoad: mdlEditorLoaded}"></div>
</div>
<div class="col-md-6">
<h3>Translated SparkPost Template</h3>
<div id="sparkPostEditor" ui-ace="{mode:'handlebars', onLoad: spEditorLoaded}" readonly></div>
</div>
</div>
</div>
</div>
</body>
</html>

105 changes: 105 additions & 0 deletions static/translate.js
@@ -0,0 +1,105 @@
'use strict';

var mdrlTranslationApp = angular.module('mdrlTranslationApp',
['translationControllers', 'ui.bootstrap', 'ui.ace', 'ngSanitize'])
, translationControllers = angular.module('translationControllers', []);

translationControllers.controller('TranslatorControl', ['$scope', '$http', '$log', '$sce',
function($scope, $http, $log, $sce) {
$scope.loading = false;
$scope.mdlEditor = null;
$scope.spEditor = null;

function configureEditor(editor) {
// Disable Ctrl-L binding - it clashes with a common browser keyboard shortcut
editor.commands.addCommand({
name: 'gotoline',
bindKey: {
win: 'Ctrl-L',
mac: 'Command-L'
},
exec: function() { return false; },
readOnly: true
});

editor.$blockScrolling = Infinity;
}

$scope.spEditorLoaded = function(editor) {
$scope.spEditor = editor;
configureEditor(editor);
};

$scope.mdlEditorLoaded = function(editor) {
$scope.mdlEditor = editor;
configureEditor(editor);
};

$scope.translate = function() {
if (!$scope.spEditor || !$scope.mdlEditor) {
console.log('Editors not initialised!');
return;
}
$scope.spEditor.setValue('');
$scope.loading = true;
$http({
method: 'POST',
url: '/api/translate',
data: {
mandrillTemplate: $scope.mdlEditor.getValue()
}
}).then(function(result) {
if (result.errors) {
console.log('Error: ' + JSON.stringify(result.errors, null, ' '));
} else {
clearAlerts();
showInfo('Translation succeeded!');
$scope.spEditor.setValue(result.data.sparkPostTemplate);
}
}).catch(function(err) {
if (err.data.errors) {
clearAlerts();
err.data.errors.forEach(function(error) {
showSyntaxError(error.message);
});
} else {
console.error(err);
showError('Internal error HTTP=' + err.statusText +
'. Check your console for detail and please ping us on <a href="http://slack.sparkpost.com/">Slack</a> or help.'
);
}
}).finally(function() {
$scope.loading = false;
});
};

$scope.alerts = [];
$scope.closeAlert = function(idx) {
$scope.alerts.splice(idx, 1);
};

function clearAlerts() {
$scope.alerts = [];
};

function showInfo(msg) {
$scope.alerts.unshift({type: 'success', msg: markupMsg(msg)});
}

function showWarning(msg) {
$scope.alerts.unshift({type: 'warning', msg: markupMsg(msg)});
}

function showError(msg) {
$scope.alerts.unshift({type: 'danger', msg: markupMsg(msg)});
}

function showSyntaxError(msg) {
$scope.alerts.unshift({type: 'danger', msg: msg, pre: true});
}

function markupMsg(msg) {
return $sce.trustAsHtml(msg.replace(/\n/g, '<br>'));
}
}]);

0 comments on commit bc3c4a1

Please sign in to comment.