diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..04e5764
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+package.json
+package-lock.json
+assets/js/validate.min.js
diff --git a/README.md b/README.md
index 5a92214..aca0867 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,39 @@
# ghost-contact-form
Contact Forms in Ghost — Without External Services
+
+## Prerequisites
+- A supported version of Node.js
+- npm to manage packages
+
+## Quickstart Install
+
+```
+$ git clone https://github.com/styxlab/ghost-contact-form.git
+$ cd ghost-contact-form
+$ sh install.sh
+```
+
+## Configure
+
+Adapt the `.env` file to your needs. The following variables must be defined.
+
+```
+SMTP_HOST = mail.server.com
+SMTP_USER = user@server.com
+SMTP_PASS = strong password
+ALLOW_ORIGIN = https://your-blog.com
+EMAIL_FROM = noreply@your-blog.com
+EMAIL_TO = your@email.com
+```
+
+## Usage
+
+```
+$ node ghost-contact-svc.js
+```
+
+## Test Locally
+
+```
+$ http://lcoalhost:7000/v1/demo
+```
diff --git a/assets/css/demo.css b/assets/css/demo.css
new file mode 100644
index 0000000..a5d9905
--- /dev/null
+++ b/assets/css/demo.css
@@ -0,0 +1,53 @@
+body {
+ margin: 0px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
+ color: #333;
+ background-color: #fff;
+}
+h1 {
+ margin: 10px 0;
+ font-family: inherit;
+ font-weight: bold;
+ line-height: 20px;
+ color: inherit;
+ text-rendering: optimizelegibility;
+}
+h1 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999;
+}
+h1 {
+ line-height: 40px;
+}
+h1 {
+ font-size: 38.5px;
+}
+h1 small {
+ font-size: 24.5px;
+}
+body {
+ margin-top: 90px;
+}
+.header {
+ position: fixed;
+ top: 0;
+ left: 50%;
+ margin-left: -480px;
+ background-color: #fff;
+ border-bottom: 1px solid #ddd;
+ padding-top: 10px;
+ z-index: 10;
+}
+.footer {
+ color: #ddd;
+ font-size: 12px;
+ text-align: center;
+ margin-top: 20px;
+}
+.footer a {
+ color: #ccc;
+ text-decoration: underline;
+}
\ No newline at end of file
diff --git a/assets/css/form.css b/assets/css/form.css
new file mode 100644
index 0000000..3d3b30c
--- /dev/null
+++ b/assets/css/form.css
@@ -0,0 +1,56 @@
+form,input,select,textarea,button {
+ font-family: avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif;
+ display: block;
+ margin: 0 0 2rem 0;
+ padding: 1rem 0;
+ width: 100% !important;
+ align-items: flex-start;
+ box-sizing: border-box;
+}
+input,select,textarea {
+ color: darkslategray;
+ background-color: white;
+ background-clip: padding-box;
+ line-height: 1.5;
+ border-radius: .5rem;
+ padding: 1rem 4rem;
+ border: 1px solid #ced4da;
+ transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
+}
+select {
+ appearance: none;
+ -moz-appearance: none;
+ -webkit-appearance: none;
+}
+select:required:invalid {
+ color: silver;
+}
+input:focus, select:focus, textarea:focus {
+ border-color: royalblue;
+ box-shadow: none;
+ -webkit-box-shadow: none;
+}
+button {
+ margin-bottom: 0;
+ color: white;
+ background-color: royalblue;
+ border-color: royalblue;
+ user-select: none;
+ line-height: 1.5;
+ border-radius: .5rem;
+ border: 1px solid transparent;
+ transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
+}
+button:hover {
+ filter: brightness(80%);
+}
+input:placeholder {
+ color: silver;
+}
+option[value=""][disabled] {
+ display: none;
+ color: silver;
+}
+option {
+ color: black;
+}
\ No newline at end of file
diff --git a/assets/js/formProcessor.js b/assets/js/formProcessor.js
new file mode 100644
index 0000000..a368a00
--- /dev/null
+++ b/assets/js/formProcessor.js
@@ -0,0 +1,89 @@
+var formProcessor = (function(){
+
+ "use strict";
+
+ var constraints = {
+ name: {
+ presence: true,
+ length: {
+ minimum: 2,
+ maximum: 30,
+ message: "must be longer."
+ }
+ },
+ email: {
+ presence: true,
+ email: true,
+ },
+ subject: {
+ presence: true,
+ length: {
+ minimum: 1,
+ message: "must be selected."
+ }
+ },
+ message: {
+ presence: true,
+ length: {
+ minimum: 10,
+ maxumum: 4000,
+ message: "must be longer."
+ }
+ },
+ robot: {
+ presence: {
+ allowEmpty: true
+ },
+ length: {
+ is: 0,
+ message: "must be filled out."
+ }
+ }
+ };
+
+ function formAlert(text) {
+ document.getElementById("responsemsg").innerHTML = "
" + text + "
"; + }; + function responseAlert(text) { + document.getElementById("serverresponse").innerHTML = "" + text + "
"; + }; + + function sendData(data, url) { + formAlert("One second..."); + var postURL = (url || "https://api.your-blog.com/v1/contact"); + var http = new XMLHttpRequest(); + http.open("POST", postURL, true); + http.setRequestHeader("Content-Type", "application/json"); + data.source_url = window.location.href; + http.send(JSON.stringify(data)); + http.onload = function() { + formAlert("Thank you, your message has been sent!"); + if(url != undefined){ + responseAlert("Only the demo should display the server response: " + http.responseText); + } + document.getElementById("contact-form").reset(); + } + }; + + return ({ + process: function(url) { + var attributes = { + name: document.forms["contact-form"]["name"].value, + email: document.forms["contact-form"]["email"].value, + subject: document.forms["contact-form"]["subject"].value, + message: document.forms["contact-form"]["message"].value, + robot: document.forms["contact-form"]["_norobots"].value + }; + validate.async(attributes, constraints) + .then(function(success) { + //console.log("Success", success); + sendData(success, url); + }) + .catch(function(error) { + //console.log("ValidationError", error); + formAlert(Object.values(error)[0][0]); + }) + } + }); + +}()); diff --git a/demo.html b/demo.html new file mode 100644 index 0000000..1f52b98 --- /dev/null +++ b/demo.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + +Hello,
+
You got a new contact request.
+${data.message}
+ ` + email.html = sanitize(output, { + allowedTags: sanitize.defaults.allowedTags.concat([ 'img' ]) + }); + transporter.sendMail(email, function(error, info){ + if(error) return res.json({"sendEmail": "failed"}); + res.json({"sendEmail": "ok"}); + }); +}; \ No newline at end of file diff --git a/ghost-contact.service b/ghost-contact.service new file mode 100644 index 0000000..90518be --- /dev/null +++ b/ghost-contact.service @@ -0,0 +1,14 @@ +[Unit] +Description=ghost-contact-svc microservice +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=joe +ExecStart=/usr/bin/env node /home/joe/ghost-contact-form/ghost-contact-svc.js + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8e17a3c --- /dev/null +++ b/install.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +sudo npm -g install depcheck +npm -y init +deps=$(depcheck |cut -d: -f1 |tr -d '* ' | tail -n +2) + +for package in ${deps[@]}; do + echo 'Installing package: ' ${package} + npm install ${package} --save +done + +npm install validate.js --save +cp node_modules/validate.js/validate.min.js assets/js \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..0281b59 --- /dev/null +++ b/test.sh @@ -0,0 +1,8 @@ +#!/bin/bash + + +contact="https://api.your-server.com/v1/contact" + +curl -v -X POST ${contact} -H "Content-Type: application/json" \ + -d '{"email": "test@example.com", "name": "John Izzo", "subject": "feedback", "message": "Production Light!"}' + \ No newline at end of file