├── _config.yml ├── .gitignore ├── README.es.md ├── style.css ├── package.json ├── index.html ├── form-submission-handler.js ├── google-apps-script.js ├── test.html ├── LICENSE ├── README.ko.md ├── README.md └── README.pt.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 | # Older Translation 2 | 3 | **This translation is for an older version of the Tutorial:** 4 | 5 | The code snapshot is there but does not have the latest enhancements. If you would like to use it, be our guest. If you have time to update the translation, please do! We will gladly accept a [Pull Request](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/pulls) with an update. -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 2em; 3 | } 4 | aside { 5 | background: #1f8dd6; /* same color as selected state on site menu */ 6 | padding: 0.3em 1em; 7 | border-radius: 3px; 8 | color: #fff; 9 | margin-bottom: 2em; 10 | } 11 | textarea { 12 | width: 100%; 13 | } 14 | .content-head { 15 | font-weight: 400; 16 | text-transform: uppercase; 17 | letter-spacing: 0.1em; 18 | margin: 2em 0 1em; 19 | } 20 | .is-center { 21 | text-align: center; 22 | } 23 | .button-success { 24 | color: white; 25 | border-radius: 4px; 26 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); 27 | background: rgb(28, 184, 65); /* this is a green */ 28 | } 29 | .button-xlarge { 30 | font-size: 125%; 31 | } 32 | button { 33 | float: right; 34 | } 35 | #name, #email { 36 | width: 50%; 37 | } 38 | .honeypot-field { 39 | display: none; 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learn-to-send-email-via-google-script-html-no-server", 3 | "version": "2.1.0", 4 | "description": "Tutorial showing how to save data to a google doc spreadsheet from any html form (including sending emails!)", 5 | "main": "static-server.js", 6 | "scripts": { 7 | "start": "node_modules/.bin/live-server --port=8000" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server.git" 12 | }, 13 | "keywords": [ 14 | "tutorial", 15 | "google", 16 | "docs", 17 | "email", 18 | "form" 19 | ], 20 | "author": "nelsonic @dwyl", 21 | "license": "GPL-2.0", 22 | "bugs": { 23 | "url": "https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues" 24 | }, 25 | "homepage": "https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server#readme", 26 | "devDependencies": { 27 | "live-server": "^1.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Contact Form Example 8 | 9 | 10 | 11 |

Contact Us!

12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
28 | 29 | 30 |
31 |
32 | 33 | 34 |
35 | 36 |
37 | 38 | 40 |
41 | 42 |
43 | 44 | 46 |
47 | 48 |
49 | 50 | 51 |
52 | 53 |
54 | 55 | 56 |
57 | 58 | 60 |
61 | 62 | 63 | 67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /form-submission-handler.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // get all data in form and return object 3 | function getFormData(form) { 4 | var elements = form.elements; 5 | var honeypot; 6 | 7 | var fields = Object.keys(elements).filter(function(k) { 8 | if (elements[k].name === "honeypot") { 9 | honeypot = elements[k].value; 10 | return false; 11 | } 12 | return true; 13 | }).map(function(k) { 14 | if(elements[k].name !== undefined) { 15 | return elements[k].name; 16 | // special case for Edge's html collection 17 | }else if(elements[k].length > 0){ 18 | return elements[k].item(0).name; 19 | } 20 | }).filter(function(item, pos, self) { 21 | return self.indexOf(item) == pos && item; 22 | }); 23 | 24 | var formData = {}; 25 | fields.forEach(function(name){ 26 | var element = elements[name]; 27 | 28 | // singular form elements just have one value 29 | formData[name] = element.value; 30 | 31 | // when our element has multiple items, get their values 32 | if (element.length) { 33 | var data = []; 34 | for (var i = 0; i < element.length; i++) { 35 | var item = element.item(i); 36 | if (item.checked || item.selected) { 37 | data.push(item.value); 38 | } 39 | } 40 | formData[name] = data.join(', '); 41 | } 42 | }); 43 | 44 | // add form-specific values into the data 45 | formData.formDataNameOrder = JSON.stringify(fields); 46 | formData.formGoogleSheetName = form.dataset.sheet || "responses"; // default sheet name 47 | formData.formGoogleSendEmail 48 | = form.dataset.email || ""; // no email by default 49 | 50 | return {data: formData, honeypot: honeypot}; 51 | } 52 | 53 | function handleFormSubmit(event) { // handles form submit without any jquery 54 | event.preventDefault(); // we are submitting via xhr below 55 | var form = event.target; 56 | var formData = getFormData(form); 57 | var data = formData.data; 58 | 59 | // If a honeypot field is filled, assume it was done so by a spam bot. 60 | if (formData.honeypot) { 61 | return false; 62 | } 63 | 64 | disableAllButtons(form); 65 | var url = form.action; 66 | var xhr = new XMLHttpRequest(); 67 | xhr.open('POST', url); 68 | // xhr.withCredentials = true; 69 | xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 70 | xhr.onreadystatechange = function() { 71 | if (xhr.readyState === 4 && xhr.status === 200) { 72 | form.reset(); 73 | var formElements = form.querySelector(".form-elements") 74 | if (formElements) { 75 | formElements.style.display = "none"; // hide form 76 | } 77 | var thankYouMessage = form.querySelector(".thankyou_message"); 78 | if (thankYouMessage) { 79 | thankYouMessage.style.display = "block"; 80 | } 81 | } 82 | }; 83 | // url encode form data for sending as post data 84 | var encoded = Object.keys(data).map(function(k) { 85 | return encodeURIComponent(k) + "=" + encodeURIComponent(data[k]); 86 | }).join('&'); 87 | xhr.send(encoded); 88 | } 89 | 90 | function loaded() { 91 | // bind to the submit event of our form 92 | var forms = document.querySelectorAll("form.gform"); 93 | for (var i = 0; i < forms.length; i++) { 94 | forms[i].addEventListener("submit", handleFormSubmit, false); 95 | } 96 | }; 97 | document.addEventListener("DOMContentLoaded", loaded, false); 98 | 99 | function disableAllButtons(form) { 100 | var buttons = form.querySelectorAll("button"); 101 | for (var i = 0; i < buttons.length; i++) { 102 | buttons[i].disabled = true; 103 | } 104 | } 105 | })(); 106 | -------------------------------------------------------------------------------- /google-apps-script.js: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * This tutorial is based on the work of Martin Hawksey twitter.com/mhawksey * 3 | * But has been simplified and cleaned up to make it more beginner friendly * 4 | * All credit still goes to Martin and any issues/complaints/questions to me. * 5 | ******************************************************************************/ 6 | 7 | // if you want to store your email server-side (hidden), uncomment the next line 8 | // var TO_ADDRESS = "example@email.net"; 9 | 10 | // spit out all the keys/values from the form in HTML for email 11 | // uses an array of keys if provided or the object to determine field order 12 | function formatMailBody(obj, order) { 13 | var result = ""; 14 | if (!order) { 15 | order = Object.keys(obj); 16 | } 17 | 18 | // loop over all keys in the ordered form data 19 | for (var idx in order) { 20 | var key = order[idx]; 21 | result += "

" + key + "

" + sanitizeInput(obj[key]) + "
"; 22 | // for every key, concatenate an `

`/`
` pairing of the key name and its value, 23 | // and append it to the `result` string created at the start. 24 | } 25 | return result; // once the looping is done, `result` will be one long string to put in the email body 26 | } 27 | 28 | // sanitize content from the user - trust no one 29 | // ref: https://developers.google.com/apps-script/reference/html/html-output#appendUntrusted(String) 30 | function sanitizeInput(rawInput) { 31 | var placeholder = HtmlService.createHtmlOutput(" "); 32 | placeholder.appendUntrusted(rawInput); 33 | 34 | return placeholder.getContent(); 35 | } 36 | 37 | function doPost(e) { 38 | 39 | try { 40 | Logger.log(e); // the Google Script version of console.log see: Class Logger 41 | record_data(e); 42 | 43 | // shorter name for form data 44 | var mailData = e.parameters; 45 | 46 | // names and order of form elements (if set) 47 | var orderParameter = e.parameters.formDataNameOrder; 48 | var dataOrder; 49 | if (orderParameter) { 50 | dataOrder = JSON.parse(orderParameter); 51 | } 52 | 53 | // determine recepient of the email 54 | // if you have your email uncommented above, it uses that `TO_ADDRESS` 55 | // otherwise, it defaults to the email provided by the form's data attribute 56 | var sendEmailTo = (typeof TO_ADDRESS !== "undefined") ? TO_ADDRESS : mailData.formGoogleSendEmail; 57 | 58 | // send email if to address is set 59 | if (sendEmailTo) { 60 | MailApp.sendEmail({ 61 | to: String(sendEmailTo), 62 | subject: "Contact form submitted", 63 | // replyTo: String(mailData.email), // This is optional and reliant on your form actually collecting a field named `email` 64 | htmlBody: formatMailBody(mailData, dataOrder) 65 | }); 66 | } 67 | 68 | return ContentService // return json success results 69 | .createTextOutput( 70 | JSON.stringify({"result":"success", 71 | "data": JSON.stringify(e.parameters) })) 72 | .setMimeType(ContentService.MimeType.JSON); 73 | } catch(error) { // if error return this 74 | Logger.log(error); 75 | return ContentService 76 | .createTextOutput(JSON.stringify({"result":"error", "error": error})) 77 | .setMimeType(ContentService.MimeType.JSON); 78 | } 79 | } 80 | 81 | 82 | /** 83 | * record_data inserts the data received from the html form submission 84 | * e is the data received from the POST 85 | */ 86 | function record_data(e) { 87 | var lock = LockService.getDocumentLock(); 88 | lock.waitLock(30000); // hold off up to 30 sec to avoid concurrent writing 89 | 90 | try { 91 | Logger.log(JSON.stringify(e)); // log the POST data in case we need to debug it 92 | 93 | // select the 'responses' sheet by default 94 | var doc = SpreadsheetApp.getActiveSpreadsheet(); 95 | var sheetName = e.parameters.formGoogleSheetName || "responses"; 96 | var sheet = doc.getSheetByName(sheetName); 97 | 98 | var oldHeader = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; 99 | var newHeader = oldHeader.slice(); 100 | var fieldsFromForm = getDataColumns(e.parameters); 101 | var row = [new Date()]; // first element in the row should always be a timestamp 102 | 103 | // loop through the header columns 104 | for (var i = 1; i < oldHeader.length; i++) { // start at 1 to avoid Timestamp column 105 | var field = oldHeader[i]; 106 | var output = getFieldFromData(field, e.parameters); 107 | row.push(output); 108 | 109 | // mark as stored by removing from form fields 110 | var formIndex = fieldsFromForm.indexOf(field); 111 | if (formIndex > -1) { 112 | fieldsFromForm.splice(formIndex, 1); 113 | } 114 | } 115 | 116 | // set any new fields in our form 117 | for (var i = 0; i < fieldsFromForm.length; i++) { 118 | var field = fieldsFromForm[i]; 119 | var output = getFieldFromData(field, e.parameters); 120 | row.push(output); 121 | newHeader.push(field); 122 | } 123 | 124 | // more efficient to set values as [][] array than individually 125 | var nextRow = sheet.getLastRow() + 1; // get next row 126 | sheet.getRange(nextRow, 1, 1, row.length).setValues([row]); 127 | 128 | // update header row with any new data 129 | if (newHeader.length > oldHeader.length) { 130 | sheet.getRange(1, 1, 1, newHeader.length).setValues([newHeader]); 131 | } 132 | } 133 | catch(error) { 134 | Logger.log(error); 135 | } 136 | finally { 137 | lock.releaseLock(); 138 | return; 139 | } 140 | 141 | } 142 | 143 | function getDataColumns(data) { 144 | return Object.keys(data).filter(function(column) { 145 | return !(column === 'formDataNameOrder' || column === 'formGoogleSheetName' || column === 'formGoogleSendEmail' || column === 'honeypot'); 146 | }); 147 | } 148 | 149 | function getFieldFromData(field, data) { 150 | var values = data[field] || ''; 151 | var output = values.join ? values.join(', ') : values; 152 | return output; 153 | } 154 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | test: learn-to-send-email-via-google-script-html-no-server 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |

Test All Form and Input Elements

21 |

22 | Here is a form that is meant to include all form and input elements possible 23 | on a page. This way we can test out if the form functions as expected. We 24 | will populate with default values if possible or using Javascript to 25 | simplify testing. 26 |

27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 | 37 | 38 | 39 |
40 | 41 | 42 |
43 | 44 | 45 |
46 | 47 | 48 |
49 | 50 | 51 |
52 | 53 | 54 |
55 | 56 | 57 |
58 | 59 | 60 |
61 | 62 | 63 |
64 | 65 | Javascript 66 |
67 | 68 | 69 |
70 | OK, Now Really Choose: 71 | 72 | 73 | 74 | 75 | 76 |
77 | 78 | 79 |
80 | 81 | Javascript 82 |
83 | 84 | 85 |
86 | What are you Most Comfortable With? 87 | 88 | 89 | 90 | 91 | 92 |
93 | 94 | 95 |
96 | 97 | 104 |
105 | 106 | 107 |
108 | 109 | 116 |
117 | 118 | 119 |
120 | 121 | 122 | 123 | 129 |
130 | 131 | 132 |
133 | 134 | 135 |
136 | 137 | 138 |
139 | 140 | 141 |
142 | 143 | 144 |
145 | 146 | 147 |
148 | 149 | 150 |
151 | 152 | 153 |
154 | 155 | 156 |
157 | 158 | 159 |
160 | 161 | 162 |
163 | 164 | 165 |
166 | 167 | 168 |
169 | 170 | 171 |
172 | 173 | 174 |
175 | 176 | 177 |
178 | 179 | 180 |
181 | 182 | 183 |
184 | 185 | 186 |
187 | 188 | 189 |
190 | 191 | 192 | 193 |
194 | 195 | 199 | 200 |
201 | 202 | 203 |
204 | 205 |
207 | 208 | 209 |
210 |
211 | 212 | 213 |
214 | 215 |
216 | 217 | 219 |
220 | 221 |
222 | 223 | 225 |
226 | 227 |
228 | 229 | 230 |
231 | 232 | 234 |
235 | 236 | 237 | 241 | 242 |
243 | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.ko.md: -------------------------------------------------------------------------------- 1 | # Google Apps Mail을 사용해 **정적** HTML Form에서 메일을 보내세요! 2 | 3 | **Language : [English](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server#send-email-from-a-static-html-form-using-google-apps-mail) | `한국어` | [Español](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.es.md#older-translation) | [Português](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.pt.md)** 4 | 5 |
6 | 7 | 이것은 ***HTML Form*** 을 사용하여 "Contact Us" 메세지 보내기 기능을 구현할 때, 백엔드 서버없이 Google 스크립트를 활용하여 이메일을 보낼 수 있는 ***Step-by-Step(단계별)*** 예제입니다. PHP, Python, Ruby, Java, Node.js 등 필요하지 않습니다. 8 | 9 | 작동하는 예제 웹 페이지는 이곳에서 볼 수 있습니다. : https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 10 | 11 | ***참고:\*** EU's GDPR(유럽연합 일반 데이터 보호 규칙)에 따라, [사용자 개인 정보에 대한 권장 사항](https://cloud.google.com/privacy/gdpr)을 적극 권고합니다. 귀하는 사용자의 개인 데이터를 안전하게 보관하는 *책임* 을 받을 수 있으며, 귀하에게 연락할 수 있는 방법을 제공해야 합니다. 12 | 13 | **\*경고:\*** Google API에는 하루에 보낼 수 있는 이메일 수에 제한이 있습니다. 이는 Google 계정에 따라 다를 수 있습니다. [이곳에서](https://developers.google.com/apps-script/guides/services/quotas) 본인의 한도를 확인하세요. 데이터는 항상 스프레드시트에 먼저 추가되고 이메일을 보낼 수 있다면, 이메일로 전송되므로 3부까지 튜토리얼을 구현하는 것이 좋습니다. 14 | 15 | ## Why? 16 | 17 | 우리는 *서버가 없거나 서버를 원하지 않을때, "정적"* HTML 페이지에서 이메일 보내는 방법이 필요했습니다. 18 | 19 | ### *Key Advantages(주요 이점들)* 20 | 21 | - 배포/유지보수/지불 해야하는 "백엔드"가 필요 없습니다. 22 | 23 | - 모든 측면에서 ***사용자 맞춤 커스터마이징*** 이 가능합니다! 24 | 25 | - ***모든 곳에서 허용된 Google Mail*** 을 통해서 이메일을 전송합니다. ( *높은 전송 성공률* ) 26 | 27 | - 어떤 데이터든 쉽게 볼 수 있는 ***스프레드시트*** 에 **수집/저장** 합니다. 28 | 29 | ( *코딩을 전혀 모르는 사람과 공유해야하는 경우에도 완벽합니다.* ) 30 | 31 | ## What? 32 | 33 | 쉽지만 유지보수가 필요한 서버를 사용하여 이메일을 보내는 대신, Google을 사용하여 이메일을 보내고 Google 스프레드시트를 사용하여 데이터를 추적하세요! 34 | 35 | > 데이터를 보내는 위치에 상관하지 않고, 받은 편지함(messy ... yuck)에 제출된 데이터를 관리하려면 https://formspree.io/과 같은 무료 서비스를 사용하여 Form 제출을 처리할 수 있습니다. 36 | > 37 | > *또는*... 몇 분 동안 *투자하고* 데이터를 비공개로 관리할 수 있습니다. *당신이 선택하세요.* 38 | 39 | ## *How*? 40 | 41 | ### 1. 하단의 Sample Spreadsheet 복사하기 42 | 43 | > Sample: https://docs.google.com/spreadsheets/d/1Bn4m6iA_Xch1zzhNvo_6CoQWqOAgwwkOWJKC-phHx2Q/copy 44 | 45 | 당신의 Google 계정에 로그인하고 "**사본 만들기**"를 클릭합니다. 46 | 47 | [![1-make-copy](https://user-images.githubusercontent.com/1406149/29245471-feb7b034-7f97-11e7-9c0d-f06238e8362b.png)](https://user-images.githubusercontent.com/1406149/29245471-feb7b034-7f97-11e7-9c0d-f06238e8362b.png) 48 | 49 | 아래와 같이 나와야합니다. 50 | 51 | [![2-copy-of-sheet](https://cloud.githubusercontent.com/assets/194400/10559710/3aec92f0-74ef-11e5-9295-f1988a23257b.png)](https://cloud.githubusercontent.com/assets/194400/10559710/3aec92f0-74ef-11e5-9295-f1988a23257b.png) 52 | 53 | > 참고: 복사본 이름을 원하는대로 변경해도 결과에 아무런 영향을 미치지 않습니다. 54 | 55 | ### 2. 스크립트 편집기 열기 56 | 57 | "**도구**" > "**스크립트 편집기...**"를 눌러 편집기를 여세요. 58 | 59 | [![2 script-editor](https://cloud.githubusercontent.com/assets/194400/10559732/8db2b9f6-74ef-11e5-8bf2-de286f079929.png)](https://cloud.githubusercontent.com/assets/194400/10559732/8db2b9f6-74ef-11e5-8bf2-de286f079929.png) 60 | 61 | 여기에 필요한 스크립트의 스냅샷이 있습니다 : [google-script-just-email.js](https://raw.githubusercontent.com/dwyl/learn-to-send-email-via-google-script-html-no-server/1d1c6727f69dec64a6b7f6bd6ff0dd72d0374210/google-script-just-email.js) 62 | 63 | ### 3. 스크립트에서 `TO_ADDRESS` 를 설정하기 64 | 65 | **\*경고:\*** 만약 당신이 `TO_ADDRESS` 에 있는 주석을 제거하지 않고, `TO_ADDRESS`에 당신의 이메일을 직접 설정하면, 웹 스킬을 가진 사람이 당신의 Form과 데이터를 수정하여 임의의 이메일로 보낼 수 있습니다. 66 | 67 | 이 위험은 그다지 크지 않을 수 있습니다. 대신, 이 가능한 위험을 감수하면서도 HTML Form 내에서 이메일 변수를 `data-email` 속성으로 설정하는게 편하다면 이 변수를 주석으로 남겨둘 수 있습니다. 68 | 69 | 따라서 현재 속한 2-6단계를 거치지 않고도 HTML Form의 이메일을 보낼 위치를 쉽게 변경할 수 있습니다. 이 기능을 사용하려면 2부 10단계에서 **제공된** JS 파일을 사용해야 합니다. 70 | 71 | 이러한 잠재적 위험을 감수하지 않으려면 `TO_ADDRESS`의 주석을 제거하세요. 그리고 Form에서 Submit 할 때 데이터를 받고자하는 주소를 변수의 값으로 넣으세요. 72 | 73 | [![3-script-editor-showing-script](https://cloud.githubusercontent.com/assets/194400/10560379/9efa5b3a-7501-11e5-96ba-a9e3b2d77ee4.png)](https://cloud.githubusercontent.com/assets/194400/10560379/9efa5b3a-7501-11e5-96ba-a9e3b2d77ee4.png) 74 | 75 | ### 4. 스크립트를 *새로운 버전* 으로 저장하기 76 | 77 | *버전 관리* 를 클릭하세요. 78 | 79 | [![19 google-script-no-save-new-version](https://cloud.githubusercontent.com/assets/194400/10558249/527f3c98-74c1-11e5-8290-5af7fa7f5f75.png)](https://cloud.githubusercontent.com/assets/194400/10558249/527f3c98-74c1-11e5-8290-5af7fa7f5f75.png) 80 | 81 | 그리고 *새로운 버전* 을 만들어주세요. 82 | 83 | [![20 google-script-save-new-version](https://cloud.githubusercontent.com/assets/194400/10558250/53d21d5e-74c1-11e5-88c5-7bc2d8ce6228.png)](https://cloud.githubusercontent.com/assets/194400/10558250/53d21d5e-74c1-11e5-88c5-7bc2d8ce6228.png) 84 | 85 | ### 5. *업데이트 된* 스크립트를 웹앱으로 배포합니다. 86 | 87 | [![20 a-publish](https://cloud.githubusercontent.com/assets/194400/10558288/50980aa8-74c2-11e5-8576-72084a564779.png)](https://cloud.githubusercontent.com/assets/194400/10558288/50980aa8-74c2-11e5-8576-72084a564779.png) 88 | 89 | 배포 할 *최신 버전* 을 선택합니다. 90 | ⚠️ 참고: '웹에 액세스할 수 있는 사용자' 드롭다운에서 `Anyone, even anonymous` 옵션을 선택해야 합니다. 다른걸 선택하면 Form 응답이 스프레드시트에 추가되지 않습니다. ⚠️ 91 | 92 | [![21 deploy-new-version](https://cloud.githubusercontent.com/assets/194400/10558251/570a5428-74c1-11e5-8ced-5dd26d3de3c4.png)](https://cloud.githubusercontent.com/assets/194400/10558251/570a5428-74c1-11e5-8ced-5dd26d3de3c4.png) 93 | 94 | ### 6. 이메일을 보내기 위해 스크립트 인증하기 95 | 96 | [![5 auth-required](https://cloud.githubusercontent.com/assets/194400/10560412/89d3fb0c-7502-11e5-81ce-fb239bf545b2.png)](https://cloud.githubusercontent.com/assets/194400/10560412/89d3fb0c-7502-11e5-81ce-fb239bf545b2.png) 97 | 98 | [Google을 사용하여 스크립트 확인](https://developers.google.com/apps-script/guides/client-verification#requesting_verification)을 하지 않는 한, "Advanced(고급)"과 "Go to(이동) ... (unsafe)"을 클릭해서 이 앱 권한을 부여해야합니다. 99 | 100 | [![5-auth-failed-verification](https://user-images.githubusercontent.com/1406149/44312495-79583780-a3b6-11e8-9740-8c9b50f195d6.png)](https://user-images.githubusercontent.com/1406149/44312495-79583780-a3b6-11e8-9740-8c9b50f195d6.png) 101 | 102 | [![5-allow-sending-emails](https://cloud.githubusercontent.com/assets/194400/10560416/a86a26ae-7502-11e5-9add-d5081d409af4.png)](https://cloud.githubusercontent.com/assets/194400/10560416/a86a26ae-7502-11e5-9add-d5081d409af4.png) 103 | 104 | 웹앱 URL을 클립보드 혹은 메모장에 복사하시고 "확인" 버튼을 누르세요. 105 | 106 | [![22 1-deploy-as-web-app](https://cloud.githubusercontent.com/assets/194400/10558255/6eec31e2-74c1-11e5-9c07-cea6209526df.png)](https://cloud.githubusercontent.com/assets/194400/10558255/6eec31e2-74c1-11e5-9c07-cea6209526df.png) 107 | 108 | ### 7. *기본 HTML Form* 만들기 109 | 110 | 이 저장소의 `index.html` 템플릿을 사용하여, 기본 Form 형식을 가진 HTML 파일을 만들어서 저장하세요. 111 | 112 | ⚠️ 이 단계에서 이미 *자신의 Form* 을 사용하려고 하는 경우, 이 저장소에 있는 예제 대신 이 단계를 수행합니다. 113 | 114 | - 각각의 Form 태그의 `name` 속성은 Google 시트의 컬럼명과 같아야 합니다. 115 | - Form 태그의 `class`는 `gform`이 되어야 합니다. 즉, `
` 116 | - 나중에 이걸 변경하려면, `form-submission-handler.js`의 고유버전을 만들고 `class`을 고치면 됩니다. 117 | 118 | > Form 태그의 `action` 속성을 전 단계에서 복사해놓은 URL로 고쳐야 함을 잊지 마세요 : 119 | 120 | [![7-html-form](https://user-images.githubusercontent.com/1406149/44312329-9b9c8600-a3b3-11e8-9816-4bdbbc96dc62.png)](https://user-images.githubusercontent.com/1406149/44312329-9b9c8600-a3b3-11e8-9816-4bdbbc96dc62.png) 121 | 122 | ### 8. 브라우저에서 HTML Form (*페이지*) 열기 123 | 124 | HTML Form에 테스트 데이터를 채우세요 : 125 | 126 | [![html form](https://cloud.githubusercontent.com/assets/194400/10560494/674b64c4-7504-11e5-801a-b537d276f671.png)](https://cloud.githubusercontent.com/assets/194400/10560494/674b64c4-7504-11e5-801a-b537d276f671.png) 127 | 128 | Submit 하세요. 보내졌으면 아래와 같이 확인할 수 있습니다 : 129 | 130 | ![form sent](https://cloud.githubusercontent.com/assets/194400/10560501/8f605dd4-7504-11e5-8cd7-06d768beba4d.png) 131 | 132 | ### 9. 설정했던 이메일 계정의 받은 편지함 확인하기 133 | 134 | (*위의*) **3단계** 에서 설정했던 이메일 계정의 받은편지함을 열어보세요. 135 | 136 | [![email received](https://cloud.githubusercontent.com/assets/194400/10560512/f87f1652-7504-11e5-8b0f-c342c395a193.png)](https://cloud.githubusercontent.com/assets/194400/10560512/f87f1652-7504-11e5-8b0f-c342c395a193.png) 137 | 138 | > ***Done***. 이게 전부입니다. HTML Form 태그만을 이용해 이메일 보내기에 성공했습니다. 139 | 140 | # *Part Two - 보기 좋게 만들기* ... 141 | 142 | 우리 스타일에 [**순수 CSS**](https://purecss.io/start/)를 사용해 이 ***Super Lean*** 을 유지할 겁니다. ( *8단계의 "못생긴" HTML Form 태그를 고칠겁니다.* ). 그리고 당신의 페이지/사이트에 사용자를 유지하기 위해 Form 태그 양식을 `submit` 할 때 [**JQuery** "***AJAX***"](https://api.jquery.com/jquery.ajax/) 를 사용하겠습니다. ( *"구린" 응답페이지는 빼구요.* ) 143 | 144 | ### 10. **JavaScript** "***AJAX*** 을 이용한 Form Submit" 145 | 146 | 페이지가 `JSON` 응답/결과로 *변경되지 않도록* 하려면, ***AJAX***를 사용하여 Form을 submit 해야 합니다. 147 | 148 | 다음 [the following Javascript file](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/form-submission-handler.js)을 설치하고 당신의 `index.html`의 끝(`` 를 하기 전)에 설치한 javascript 파일을 포함하여 업데이트하세요. 149 | 150 | ``` 151 | 152 | ``` 153 | 154 | **경고:** 위의 3단계에서 `TO_ADDRESS` 변수를 설정하지 않은 경우, 메인 Form 요소에 `data-email="example@email.net"`를 포함시켜야 됩니다. 자세한 내용은 아래 양식을 확인하세요. 단, 설정하셨다면 이 속성은 필요하지 않습니다. 155 | 156 | 이제 Form이 submit 되면 "Thank You" *메시지* 가 띄워집니다: 157 | 158 | ### 11. Form을 Submit 한 후, 나오는 메시지 커스터마이징하기 159 | 160 | 10단계를 수행한 후, Submit 한 뒤 나오는 감사 메시지를 추가하여 커스터마이징 할 수 있습니다. 아래의 소스코드를 `and` 태그 안에 넣으세요. 161 | 162 | ```html 163 | 168 | ``` 169 | 170 | Form이 Submit 된 후에 아래와 같은 감사 메세지가 나타납니다. 171 | 172 | [![thankyou message](https://cloud.githubusercontent.com/assets/194400/10561147/4086a89a-7517-11e5-840d-7f490353e225.png)](https://cloud.githubusercontent.com/assets/194400/10561147/4086a89a-7517-11e5-840d-7f490353e225.png) 173 | 174 | `thankyou_message` div 태그를 수정하여 당신만의 감사 메세지를 작성하세요. 175 | 176 | ### 12. CSS를 사용해 Form을 *멋지게* 만들기 177 | 178 | *이번 예제* 에서는 ***Pure CSS*** : https://purecss.io/start/ 을 사용하고 있습니다. ***4.0KB로 축소 및 압축된 적은 용량*** 이고 현재 "문제"인 보기에 구린 것을 *해결하기* 때문입니다. 179 | 180 | [![PureCSS-Logo-Intro](https://camo.githubusercontent.com/27da1dfc8cc2f5541bb85d8be1dd88eb5c1142ff/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536353833382f37326436643532612d373564322d313165352d396239322d6361303262313132343932302e706e67)](https://camo.githubusercontent.com/27da1dfc8cc2f5541bb85d8be1dd88eb5c1142ff/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536353833382f37326436643532612d373564322d313165352d396239322d6361303262313132343932302e706e67) 181 | 182 | [![PureCSS-module-sizes](https://camo.githubusercontent.com/94cc657811248276133a9afb33b57e28da6e2f4a/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536353834342f38663438383561302d373564322d313165352d393439302d6533666334326333323631362e706e67)](https://camo.githubusercontent.com/94cc657811248276133a9afb33b57e28da6e2f4a/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536353834342f38663438383561302d373564322d313165352d393439302d6533666334326333323631362e706e67) 183 | 184 | 이 작업은 너무 *많은* 시간을 들이지 않고도 Form을 ***훨씬*** 멋지게 만들 수 있습니다: 185 | 186 | [![contact form with pure css](https://camo.githubusercontent.com/8dfef1bc299b5cf0f95fd13597558bc7434e8641/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536363339322f66333862633435342d373564642d313165352d383564642d3638313934393461393866322e706e67)](https://camo.githubusercontent.com/8dfef1bc299b5cf0f95fd13597558bc7434e8641/68747470733a2f2f6769746875622d636c6f75642e73332e616d617a6f6e6177732e636f6d2f6173736574732f3139343430302f31303536363339322f66333862633435342d373564642d313165352d383564642d3638313934393461393866322e706e67) 187 | 188 | ### 13. 이메일도 멋지게 만들기! 189 | 190 | 기본적으로 이메일의 본문에는 Form의 key-value 쌍이 포함되며, key는 `

`이고 value는 `
`입니다. 이것은 심플하지만, 데이터를 보기엔 어색합니다. 191 | 192 | 아마 대충 이런 형식의 메일을 받았을 겁니다: 193 | 194 | [![Nicely formatted email](https://cloud.githubusercontent.com/assets/5610747/22168070/335ad734-df62-11e6-9523-6e193e94151f.png)](https://cloud.githubusercontent.com/assets/5610747/22168070/335ad734-df62-11e6-9523-6e193e94151f.png) 195 | 196 | > 이 작업은 진행 중인 작업이며 이 작업으로 예상한 것보다 더 많은 이메일을 받을 수 있습니다. 이메일 내용이 Form으로 전송된 모든 데이터를 루핑하고 있기 때문에 로봇이나 악의적인 사용자가 요청한 것보다 더 많이 `POST` 한 경우, 받은 편지함에 요청 이상의 메일이 쌓일 가능성이 높습니다. 일단 조심해서 사용하세요. 개선점을 조사하고 있습니다. 197 | 198 | 스크립트 편집기를 통해 이를 수정할 수 있습니다. 이 부분입니다: 199 | 200 | ``` 201 | result += "

" + key + "

" + obj[key] + "
"; 202 | ``` 203 | 204 | 이게 우리에게 필요한 전부이며, 마크업을 당신에게 맞게 조정할 수 있습니다. 우리는 `태그를 이메일에 가장 적합한 크기로 선택했고, 대소문자(key들은 JavaScript 객체에서 모두 소문자입니다)와 약간의 기본 간격을 고정하기 위해 약간의 CSS를 추가했습니다. inline style은 일반적인 웹 페이지에서는 죄악이지만 우리가 이 예제에서 CSS를 수행하려면 유일한 방법입니다. 또한 우리는 value 부분에 단일 행이든, 여러 행이든 대응할 수 있도록
` 를 사용했습니다. (예를 들어 `

` 태그는 그걸 잘라내지 않습니다.) 205 | 206 | 추가로, 우리에겐 기본적으로 주석처리 되어 있는 `sendEmail()` 함수에 대한 `replyTo` 옵션이 있습니다. 207 | 208 | ``` 209 | MailApp.sendEmail({ 210 | to: TO_ADDRESS, 211 | subject: "Contact form submitted", 212 | // replyTo: String(mailData.email), // This is optional and reliant on your form actually collecting a field named `email` 213 | htmlBody: formatMailBody(mailData) 214 | }); 215 | ``` 216 | 217 | 이메일에 답장 필드를 추가하려는 경우, 주석을 제거하면 됩니다. 스크립트의 예제는 `reply-to` 를 Form에 Submit 된 메일로 설정합니다. 관심이 있다면 Google 문서에서 MailApp.sendEmail (예 : `cc` / `bcc` 등)에 대한 자세한 정보를 확인해주세요 : https://developers.google.com/apps-script/reference/mail/mail-app 218 | 219 | # *Part Three - Submit 된 데이터를 스프레드시트에 저장하기* 220 | 221 | Form 데이터를 받은 편지함으로 직접 보내는 것은 *좋은* 첫 번째 단계이지만 더 좋은 방법이 있습니다. 또한 위에서 설명한 것처럼 Google은 하루에 보낼 수 있는 전자 메일 수에 제한이 있으므로 데이터를 스프레드시트에 저장하는 것이 더 안전하고 데이터 손실 가능성이 낮습니다. 222 | 223 | ### 14. Google Apps 스크립트에 `record_data` 기능 추가하기 224 | 225 | [![record_data example](https://cloud.githubusercontent.com/assets/194400/10581613/8b4f9ad4-767b-11e5-90cc-962a9d6acc91.png)](https://cloud.githubusercontent.com/assets/194400/10581613/8b4f9ad4-767b-11e5-90cc-962a9d6acc91.png) 226 | 227 | 이렇게 하면 `POST`로 받은 데이터가 스프레드시트의 *행(row)* 으로 기록됩니다. 참고: 다음 파일을 이용해 전체 소스코드를 복붙할 수도 있습니다. [**google-apps-script.js**](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/133ceb9eb7b3d6ea3205a533ed99468d5feaf7e1/google-apps-script.js) 228 | 229 | ### 15. 새로운 버전 저장하고 재배포하기 230 | 231 | 새로운 버전을 저장하고, 스크립트를 ***재배포*** 하기 위해 위의 4, 5, 6단계를 다시 따라하세요. 232 | 233 | ### 16. Form 재검사하기 234 | 235 | [![submit the form](https://cloud.githubusercontent.com/assets/194400/10582654/cf3081e6-7680-11e5-9fd1-b989a8ba0b65.png)](https://cloud.githubusercontent.com/assets/194400/10582654/cf3081e6-7680-11e5-9fd1-b989a8ba0b65.png) 236 | 237 | ### 17. 데이터가 스프레드 시트에 삽입되었는지 확인하기 238 | 239 | [![17-confirm-data-inserted](https://cloud.githubusercontent.com/assets/194400/10582676/eb8af5d8-7680-11e5-92bb-30dd08d2d7b3.png)](https://cloud.githubusercontent.com/assets/194400/10582676/eb8af5d8-7680-11e5-92bb-30dd08d2d7b3.png) 240 | 241 | ### *라이브* 서버 (*on your `localhost`*) 242 | 243 | 외부 *\*.js* 파일들을 로드하고 있기 때문에 우리의 웹브라우저는 로컬 디렉토리에서 **index.html**을 열지마세요. *(파일로 실행시켜 단순 웹브라우저로 테스트하지 마세요.)* 244 | 245 | 터미널을 열고 다음 커맨드를 실행해 ***node 모듈**을 **설치**하고 **live 서버** 를 시작하세요* : 246 | 247 | ``` 248 | npm install live-server --save-dev && node_modules/.bin/live-server --port=8000 249 | ``` 250 | 251 | 설치하는데 1분 정도 걸리고, 완료되면 당신의 `live-server` 가 작동합니다. 252 | 253 | 포트 8080에서 node.js HTTP 서버가 시작되고 방금 만들었던 Form이 기본 브라우저에서 열립니다. **style.css**의 form style 혹은 **form-submission-handler.js**의 클라이언트 측 자바스크립트를 업데이트하려면, GitHub가 아닌 로컬로 해당 자원을 로드하도록 **index.html**을 편집하세요. 254 | 255 | > **참고**: 이건 초보자를 위한 Node.js의 *맛보기* 입니다. 이 Form을 "배포"하기 위해 node.js가 무조건 필요한 것은 아니며, HTML / CSS / JavaScript를 제공하는 ***어떤\* 웹 서버**에서도 실행할 수 있습니다. 이전에 Node.js를 사용한 적이 없다면 http://nodeguide.com/beginner.html을 참고하세요. 하지만 이 연습 (*서버가 없는 Form Submit*) 을 위해 node.js 또는 `live-server`는 필요하지 않습니다. 그러나 IDE에서 편집기에서 변경 사항을 적용 할 때 페이지를 자동으로 다시 로드하기 때문에 있으면 좋습니다. 256 | 257 | # *Want more*? 258 | 259 | 이 튜토리얼보다 더 많은 것을 알고 싶다면, [***알려주세요***!](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues) 260 | 261 | 여러분의 편의를 위해 GitHub Pages에서 데모페이지를 호스트해 두었습니다. 코드를 확인하고 작동 방식을 확인하세요 : https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 262 | 263 | ## 자신만의 필드 추가하기! 264 | 265 | [Henry Beary의 요청](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/9)에 따라 Form 핸들러를 *제네릭* 으로 만들어 원하는 필드를 추가할 수 있도록 했습니다. 266 | 267 | 또한 모든 종류의 Form input 요소를 사용하는 `test.html`도 만들었으므로 원하는대로 요소를 복사하고 붙혀넣으세요. 요소의 name과 id 속성들만 업데이트하면 됩니다. 이 테스트 양식의 작동 예제는 이곳에서 확인할 수 있습니다 : https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/test.html 268 | 269 | class가 gform 인 Form 태그 내에 필드를 포함시키고 Form 요소의 이름이 스프레드시트의 새로운 열 제목과 일치하는지 확인하세요! 즉 : 270 | 271 | ```html 272 |

273 | 274 | 275 |
276 | ``` 277 | 278 | 위 코드는 사용자가 선호하는 색을 받아볼 수 있게 해줍니다. 즉: 279 | 280 | [![new-field-contains-data](https://cloud.githubusercontent.com/assets/194400/11547132/9f162f6a-9949-11e5-89ac-aeb91e025844.png)](https://cloud.githubusercontent.com/assets/194400/11547132/9f162f6a-9949-11e5-89ac-aeb91e025844.png) 281 | 282 | 질문이 더 있으면 알려주세요! 283 | 284 | ## 파일 업로드하기 285 | 286 | [이 자료](https://www.labnol.org/internet/receive-files-in-google-drive/19697/)가 Google 스크립트에서 Google Drive로 파일 업로드를 시작하는 데 도움이 될 수 있습니다. 287 | 288 | ## 자주 묻는 질문들 (FAQ's) 289 | 290 | 1. *이 자습서를 사용할 때 도움을 받으려면 어떻게 하나요?* 291 | 292 | - 어떤 단계를 거쳤으며 어떤 단계가 효과적이지 않은지 상세하게 설명하는 [문제를 게시](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/new)해주세요. 응답 받을 가능성을 높이시려면, 겪으신 문제를 재현한 [예제](https://stackoverflow.com/help/mcve)를 제공해주는 것이 가장 이상적입니다 (예 : [this sample CodePen](https://codepen.io/mckennapsean/pen/gdYzNY)) 293 | 294 | 2. *예제 스프레드시트에 대한 수정권한을 얻을 수 있을까요?* 295 | 296 | - 아니요. 다른 사람이 복사 할 수 있도록 작업 예제를 보여주기 위해 사용되고 있으며 편집 가능한 버전은 실수로 또는 악의적으로 모든 사용자를 손상시킬 수 있습니다. 297 | 298 | 3. *제출(submit)을 클릭하면 웹 페이지 포워딩이 여러 텍스트로 전송되는 이유는 무엇입니까?* 299 | 300 | - AJAX를 통해 데이터를 제출하는 데 필요한 자바 스크립트를 제대로 로드하지 않았거나 브라우저가 AJAX를 지원하지 않을 수 있습니다. 2단계에서 오류를 발견 할 경우를 대비하여 콘솔 로그를 확인하세요. 301 | 302 | 4. *웹 페이지에서 form이 성공적으로 submit 되지 않는 이유가 뭘까요?* 303 | 304 | - Javascript 콘솔 로그를 확인해주세요. 제공된 Javascript를 읽는 동안 또는 Form을 Submit하는 동안 오류가 발생할 수 있습니다. Google 스크립트 파일 내에 `TO_ADDRESS` 변수를 설정하지 않은 경우 Form 태그의 class가 `gform` 이고, `data-email` 속성이 있어야 합니다. 또한 제공된 Javascript 코드에는 전송 확인을 위해 사용하는 이메일 Form 요소, 잘못된 이메일을 제출할 때 해당 요소에 대한 경고 메시지, Form이 성공적으로 Submit 된 후 표시되는 감사메세지 `
`도 표시되어야 합니다. 이러한 모든 HTML 요소가 사용자 형태인지 확인하시기 바랍니다. 복사하여 붙여넣을 수 있는 코드는 샘플 파일을 참조하세요. 이러한 모든 요소와 적절한 양식을 설정한 경우 제출을 누르면 Javascript 콘솔에 오류 메시지가 표시되지 않아야 합니다. 305 | 306 | 5. *웹 페이지에서 데이터가 제출되었다고 하는데 데이터가 저장되거나 전송되지 않는 이유는 무엇입니까?* 307 | 308 | - 스프레드 시트를 복사하고 Google Script를 게시 할 때 권한을 " "Anyone, even Anonymous" 으로 설정하셨나요? 이것은 인터넷에 있는 사용자 누구나 자신의 데이터를 보내기 위해 보내기를 누를 수 있기 때문에 필요합니다. 변경 사항을 적용할 때 적절한 버전의 스크립트를 배포하고 "버전 기록"을 사용했는지 확인하세요. 309 | 310 | 6. *파일은 어떻게 업로드 할 수 있나요?* 311 | 312 | - You can tweak the Google Script on the server to send emails to anyone and in whatever format you wish. This could be used to send a confirmation email to those contacting you, but we have not added this feature to this tutorial to avoid potential spamming. The sender of the email will always be the Google account you use to create the form/script, however. Further details on how to customize the email can be found in [the `MailApp` API](https://developers.google.com/apps-script/reference/mail/mail-app). You can instead use [the `GmailApp` API](https://developers.google.com/apps-script/reference/gmail/) which may be more flexible for certain use-cases. 313 | - 서버의 Google 스크립트를 수정하여 원하는 형식으로 누구에게나 이메일을 보낼 수 있습니다. 이 기능은 연락하는 사용자에게 확인 이메일을 보내는 데 사용할 수 있지만 잠재적인 스팸을 방지하기 위해 이 튜토리얼에 이 기능을 추가하지 않았습니다. 그러나 이메일의 발신자는 항상 form/script를 만드는 데 사용하는 Google 계정이 됩니다. 사용자를 지정하는 방법에 대한 자세한 내용은 [`MailApp` API](https://developers.google.com/apps-script/reference/mail/mail-app)에서 확인할 수 있습니다. 또는 [`GmailApp` API](https://developers.google.com/apps-script/reference/gmail/)를 대신 사용하면 특정 사용 사례에 보다 유연하게 사용할 수 있습니다. 314 | 315 | 7. *이거 안전한가요? 민감한 데이터에 사용할 수 있나요?* 316 | 317 | - 아니요. POST를 통해 전송되는 데이터는 더 안전하게 보호될 수 있지만, 제3자나 중개인이 정보를 쉽게 가로 챌 수 있으며 Google은 Google 스프레드시트의 데이터에 대한 완전한 액세스 권한을 갖습니다. 또한 이메일은 기본적으로 매우 안전한 통신 매체가 아닙니다. 보안이 필요한 경우 데이터를 저장하기 위한 안전한 플랫폼과 서버에 투자하기를 추천합니다. 318 | 319 | ## 참고문서 320 | 321 | - Google Apps Scripts Basics: https://developers.google.com/apps-script/articles 322 | 323 | - Logger (like console.log): https://developers.google.com/apps-script/reference/base/logger 324 | 325 | - Simple Mail Merge using Google Spreadsheets: https://developers.google.com/apps-script/articles/mail_merge 326 | 327 | - Original Tutorial: AJAX post to google spreadsheet: 328 | 329 | https://stackoverflow.com/questions/10000020/ajax-post-to-google-spreadsheet 330 | 331 | which points to: 332 | 333 | - https://mashe.hawksey.info/2011/10/google-spreadsheets-as-a-database-insert-with-apps-script-form-postget-submit-method/ 334 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Send Email from a *Static* HTML Form using Google Apps Mail! 2 | 3 | **Language : `English` | [한국어](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.ko.md#google-apps-mail을-사용해-정적-html-form에서-메일을-보내세요) | [Español](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.es.md#older-translation) | [Português](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.pt.md)** 4 | 5 |
6 | 7 | A ***Step-by-Step Example*** of using an **HTML Form** to send a "Contact Us" Message via Email without a Backend Server using a Google Script - No PHP, Python, Ruby, Java, Node.js etc. 8 | 9 | See a working example here: https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 10 | 11 | **_Note:_** With EU's GDPR, we strongly advise [researching recommendations on user privacy;](https://cloud.google.com/privacy/gdpr) you may be held _responsible_ for the safekeeping of users' personal data and should provide them a way to contact you. 12 | 13 | **_Warning:_** Google's API has limits on how many emails it can send in a day. 14 | This may vary on your Google account, see [the limits here](https://developers.google.com/apps-script/guides/services/quotas). 15 | We recommend implementing this tutorial through Part 3, since the data will 16 | always be added to the spreadsheet first, then emailed if possible. 17 | 18 | ## Why? 19 | 20 | We needed a way of sending an email from a "*static*" HTML page 21 | when you don't (*want* to) *have* a *server*. 22 | 23 | ### *Key Advantages* 24 | 25 | + No "*Backend*" to Deploy/Maintain/Pay for 26 | + ***Fully Customisable*** - every aspect is customisable! 27 | + Email *sent via* ***Google Mail*** which is ***Whitelisted Everywhere*** (*high deliverability success*) 28 | + **Collect/Store** any **form data** in a ***Spreadsheet*** for easy viewing 29 | (*perfect if you need to share it with non-technical people*) 30 | 31 | ## What? 32 | 33 | Instead of using a server to send your email, 34 | which is *easy* but requires *maintenance*, 35 | use Google to send mail on your behalf 36 | and use Google Spreadsheets to keep track of the data! 37 | 38 | > You *could* use a "*free*" service like https://formspree.io/ to process your form submissions 39 | if you don't care where you are sending your data and want to manage the data submitted 40 | in your email inbox (*messy ... yuck*!) 41 | *Or*... you can *invest* a few minutes and keep data private/manageable. 42 | *Take your pick*. 43 | 44 | ## *How*? 45 | 46 | ### 1. Make a Copy of the Sample Spreadsheet 47 | 48 | > Sample: https://docs.google.com/spreadsheets/d/1Bn4m6iA_Xch1zzhNvo_6CoQWqOAgwwkOWJKC-phHx2Q/copy 49 | 50 | Sign in to your Google account and click on "**Make a copy**..." 51 | 52 | ![1-make-copy](https://user-images.githubusercontent.com/1406149/29245471-feb7b034-7f97-11e7-9c0d-f06238e8362b.png) 53 | 54 | This should give you something like this: 55 | 56 | ![2-copy-of-sheet](https://cloud.githubusercontent.com/assets/194400/10559710/3aec92f0-74ef-11e5-9295-f1988a23257b.png) 57 | 58 | > Note: Feel free to change the name of the Copy to anything you want, 59 | it will not affect the outcome. 60 | 61 | ### 2. Open the Script Editor 62 | 63 | Open the **Apps Script editor** by clicking "**Extensions**" > "**Apps Script**" 64 | 65 | ![2 script-editor](https://user-images.githubusercontent.com/1406149/148702005-00f3b846-d6a7-4e3a-9d92-c255a7233b46.png) 66 | 67 | Here's a *snapshot* of the script you need (*at this point in the exercise*): [google-script-just-email.js](https://raw.githubusercontent.com/dwyl/learn-to-send-email-via-google-script-html-no-server/1d1c6727f69dec64a6b7f6bd6ff0dd72d0374210/google-script-just-email.js) 68 | 69 | ### 3. Set the `TO_ADDRESS` in the Script 70 | 71 | **_Warning:_** If you do not uncomment and set your email as the value of 72 | `TO_ADDRESS`, it is possible for someone who has web skills to alter your form 73 | and send emailed data to an arbitrary email address. 74 | 75 | This risk may not be very likely. Instead, if you wish, you can leave this 76 | variable commented out if you accept this possible risk but want the added 77 | convenience of setting this email variable inside your HTML form as a 78 | `data-email` attribute. This allows you to easily change where to send emails 79 | inside your HTML form without going back through steps 2-6. This functionality 80 | does **require** you to use the provided JS file in Part Two, Step 10. 81 | 82 | If you do not want to accept that potential risk, please uncomment the code for 83 | the variable `TO_ADDRESS`, and set this value equal to the email which should 84 | receive the form's data when submitted. 85 | 86 | ![3-script-editor-showing-script](https://cloud.githubusercontent.com/assets/194400/10560379/9efa5b3a-7501-11e5-96ba-a9e3b2d77ee4.png) 87 | 88 | ### 4. Save changes to your Script 89 | 90 | After making any code changes, you must first save them in the editor using the save icon. 91 | 92 | ![4-apps-script-save-code](https://user-images.githubusercontent.com/1406149/148702006-bd16b31b-fca1-494a-ba46-502d52545128.png) 93 | 94 | ### 5. Publish the *Updated* Script as a Web App 95 | 96 | ![5-1-publish-button](https://user-images.githubusercontent.com/1406149/148702007-8ec6047c-804b-492c-bc00-a9b59e515914.png) 97 | 98 | :warning: Note: You *must* select the `Anyone` option for the 'Who has access' dropdown or form responses will not go through! :warning: 99 | 100 | ![5-2-deploy-new-version](https://user-images.githubusercontent.com/1406149/148702008-3d5e1b29-2670-4205-95fa-bfcb3ae2522b.png) 101 | 102 | ### 6. Authorize the Script to Send Emails 103 | 104 | ![6-1-auth-required](https://user-images.githubusercontent.com/1406149/148702009-ae03cbfe-0e1d-4eed-af6d-e32befe55d17.png) 105 | 106 | Unless you [verify your script with Google](https://developers.google.com/apps-script/guides/client-verification#requesting_verification), you will need to click on "Advanced" and "Go to ... (unsafe)" to give this app permissions. 107 | 108 | ![6-2-auth-failed-verification](https://user-images.githubusercontent.com/1406149/44312495-79583780-a3b6-11e8-9740-8c9b50f195d6.png) 109 | 110 | ![6-3-allow-sending-emails](https://user-images.githubusercontent.com/1406149/148702010-e7b10721-2dd5-43b7-94f2-4e0e59397a57.png) 111 | 112 | Copy the web app URL to your clip board / note pad. 113 | Then Click "OK". 114 | 115 | ![6-4-deploy-as-web-app](https://user-images.githubusercontent.com/1406149/148702011-95587469-bec5-4d5a-9ddf-18ed0043bced.png) 116 | 117 | 118 | ### 7. Create your *basic* HTML Form 119 | 120 | Using the template in `index.html` in this repo, 121 | create your own html file with the basic form. (*save the file*) 122 | 123 | :warning: If you're already trying to use *your own form* by this step rather than the example one in this repo: 124 | + Each of your form elements must have a `name` attribute equal to that of your column name in the Google sheet 125 | + The form's `class` must be `gform`, i.e. `` 126 | + If you want to alter this later, you will need to create your 127 | own version of `form-submission-handler.js` and amend the expected `class` 128 | 129 | 130 | > Remember to change the Form `action` URL to the one you copied in 131 | the previous step: 132 | 133 | ![7-html-form](https://user-images.githubusercontent.com/1406149/44312329-9b9c8600-a3b3-11e8-9816-4bdbbc96dc62.png) 134 | 135 | 136 | ### 8. Open the HTML Form (*page*) in your Browser 137 | 138 | Fill in some sample data in the HTML Form: 139 | 140 | ![html form](https://cloud.githubusercontent.com/assets/194400/10560494/674b64c4-7504-11e5-801a-b537d276f671.png) 141 | 142 | Submit the form. You should see a confirmation that it was sent: 143 | ![form sent](https://cloud.githubusercontent.com/assets/194400/10560501/8f605dd4-7504-11e5-8cd7-06d768beba4d.png) 144 | 145 | ### 9. Check the email inbox for the address you set 146 | 147 | Open the inbox for the email address you set in **Step 3** (*above*) 148 | 149 | ![email received](https://cloud.githubusercontent.com/assets/194400/10560512/f87f1652-7504-11e5-8b0f-c342c395a193.png) 150 | 151 | 152 | > ***Done***. That's it. You just created an HTML form that sends email! 153 | 154 | # *Part Two - Make It Look Good* ... 155 | 156 | We are going to keep this ***Super Lean*** by using [**PURE CSS**](https://purecss.io/start/) 157 | for our Style (*fix the "ugly" HTML Form in step 8*). 158 | And `submit` the form using [**JQuery** "***AJAX***"](https://api.jquery.com/jquery.ajax/) to keep the person 159 | on your page/site (*avoid "ugly" response page*) 160 | 161 | ### 10. Submit the Form using **JavaScript** "***AJAX***" 162 | 163 | To *prevent* the page from changing to the `JSON` response/result 164 | we need to submit the form using ***AJAX***. 165 | 166 | Download [the following Javascript file](form-submission-handler.js) and update your `index.html` to point to it at the *end* of your file 167 | (*before the closing `` tag) 168 | 169 | ```html 170 | 171 | ``` 172 | 173 | **Warning:** If you did not set the `TO_ADDRESS` variable in Step 3, then 174 | you need to include a `data-email="example@email.net"` attribute inside the 175 | main form element. See the example form for more details. Otherwise, if you did 176 | set this variable, then you do not need this form attribute. 177 | 178 | This keeps the person on the same page. No refresh. Next step is making a thank you message appear. 179 | 180 | ### 11. Add a customised Thank You Message Shown when Form Submitted 181 | 182 | After following step 10, you can choose to add a thank you message after submitting. Add the following code between the `` and `` tags: 183 | 184 | ```html 185 | 190 | ``` 191 | 192 | This will now display a "Thank You" *message* when the form is submitted: 193 | 194 | ![thankyou message](https://cloud.githubusercontent.com/assets/194400/10561147/4086a89a-7517-11e5-840d-7f490353e225.png) 195 | 196 | Tailor your message by editing the `thankyou_message` div. 197 | 198 | ### 12. Use CSS to Make the Form *Look Good* 199 | 200 | For `this` *example* we are using ***Pure CSS***: https://purecss.io/start/ 201 | because its ***light weight*** (***4.0KB minified and gzipped***) 202 | and *solves* our current "*problem*": Making it Look Good. 203 | 204 | ![PureCSS-Logo-Intro](https://github-cloud.s3.amazonaws.com/assets/194400/10565838/72d6d52a-75d2-11e5-9b92-ca02b1124920.png) 205 | 206 | ![PureCSS-module-sizes](https://github-cloud.s3.amazonaws.com/assets/194400/10565844/8f4885a0-75d2-11e5-9490-e3fc42c32616.png) 207 | 208 | Without spending *too much* time on this, we can make the form *look* 209 | ***a lot*** nicer: 210 | 211 | ![contact form with pure css](https://github-cloud.s3.amazonaws.com/assets/194400/10566392/f38bc454-75dd-11e5-85dd-6819494a98f2.png) 212 | 213 | ### 13. Make the email look good too! 214 | 215 | By default, the sent email's body contains the key-value pairs from the form, with the key as an `

` and the value as a `
`. This is a fairly basic, and foolproof view for the data. 216 | 217 | You should get something that looks roughly like: 218 | ![Nicely formatted email](https://cloud.githubusercontent.com/assets/5610747/22168070/335ad734-df62-11e6-9523-6e193e94151f.png) 219 | 220 | > Bear in mind that this is a work in progress and does potentially open you up to getting more than you bargained for in the email. Because the email content is now looping over all the data sent in the form, if a robot or malicious user decides to `POST` more than you've asked for, you'll likely get it in your inbox. Use with caution for now. We're investigating improvements. 221 | 222 | You can modify this though, via the script editor. The line: 223 | 224 | ```javascript 225 | result += "

" + key + "

" + obj[key] + "
"; 226 | ``` 227 | 228 | has all you need. You can adjust the markup to suit you. We chose an `

` because it was the best size for the email, and added the small amount of CSS to it to fix the capitalisation (the keys are all lower case in the JS object) and a bit of default spacing. While inline styles like this are generally bad practice on normal web pages, for email HTML they're about the only reliable way to do CSS! 229 | We went with a `
` for the value part, because it could be anything - single-line, multiline (a `

` for example wouldn't cut it). 230 | 231 | While we're here, there's also a `replyTo` option for the `sendEmail()` method which is commented out by default: 232 | 233 | ```javascript 234 | MailApp.sendEmail({ 235 | to: TO_ADDRESS, 236 | subject: "Contact form submitted", 237 | // replyTo: String(mailData.email), // This is optional and reliant on your form actually collecting a field named `email` 238 | htmlBody: formatMailBody(mailData) 239 | }); 240 | ``` 241 | 242 | You can uncomment that if you want to add a reply-to field to your email. The example in the script will set the reply-to as the email submitted in the form. 243 | 244 | Google's documentation provides more information about MailApp.sendEmail (for example `cc`/`bcc` etc.) if you're interested: 245 | https://developers.google.com/apps-script/reference/mail/mail-app 246 | 247 | # *Part Three - Store Submitted Contact Form Data in a Spreadsheet* 248 | 249 | Sending the form data directly to your email inbox is a *good* 250 | first step, but we can do better. Also, as noted above, Google 251 | has limits on how many emails you can send in a day, so storing 252 | the data into a spreadsheet is safer and less prone to data loss. 253 | 254 | ### 14. Add the `record_data` Function to your Google Apps Script 255 | 256 | ![record_data example](https://cloud.githubusercontent.com/assets/194400/10581613/8b4f9ad4-767b-11e5-90cc-962a9d6acc91.png) 257 | 258 | This will record the data received from the `POST` as a *row* in the spreadsheet. 259 | See: [**google-apps-script.js**](google-apps-script.js) for the full code you can *copy-paste*. 260 | 261 | ### 15. Save a New Version and Re-Publish it 262 | 263 | Follow Steps 4, 5 & 6 to save a new version and ***re-publish*** the script. 264 | 265 | ### 16. Re-Test Submitting the Form 266 | 267 | ![submit the form](https://cloud.githubusercontent.com/assets/194400/10582654/cf3081e6-7680-11e5-9fd1-b989a8ba0b65.png) 268 | 269 | ### 17 Confirm the Data was Inserted into the Spreadsheet 270 | 271 | ![17-confirm-data-inserted](https://cloud.githubusercontent.com/assets/194400/10582676/eb8af5d8-7680-11e5-92bb-30dd08d2d7b3.png) 272 | 273 | 274 | ### _Live_ Server (_on your `localhost`_) 275 | 276 | Because we are loading external **.js** files, our web browser 277 | will not _allow_ us to simply open the **index.html** from a 278 | local directory for testing out the form. 279 | 280 | Open your terminal and run this command 281 | to _**install** the **node modules** and **start** the **live server**_: 282 | 283 | ```sh 284 | npm install live-server --save-dev && node_modules/.bin/live-server --port=8000 285 | ``` 286 | 287 | It will take a minute to install, 288 | but once that's done your `live-server` will start up. 289 | 290 | That starts a node.js HTTP server on port 8000 291 | and opens the form you just created in your default browser. 292 | If you wish to update the form styles in **style.css** or the 293 | client-side Javascript in **form-submission-handler.js**, 294 | please be sure to edit **index.html** to load those resources 295 | locally rather than via GitHub. 296 | 297 | > **Note**: This is a _light_ taste of Node.js for absolute beginners. 298 | You do **not** need node.js to "deploy" this form, 299 | you can run it on an **_any_ web server** that serves HTML/CSS/JavaScript. 300 | If you have never used Node.js before, see: http://nodeguide.com/beginner.html 301 | but for the purposes of this exercise (_submitting a form **without** a server_) 302 | you _don't **need**_ node.js or `live-server` 303 | it's just a _nice_ thing to have when you are creating 304 | your form because it automatically re-loads the page when you make changes in your text editor! 305 | 306 | 307 | # *Want more*? 308 | 309 | If you want us to take this tutorial further, [***please let us know***!](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues) 310 | 311 | For your convenience, we have hosted a working demo of the field on GitHub 312 | Pages, check it out to see the code and how it works: 313 | https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 314 | 315 | 316 | ## Add your own fields! 317 | 318 | In response to [Henry Beary's request](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/9) 319 | we made the form handler *generic* which means you can now add any fields you want to the form. 320 | 321 | We also created a form, `test.html`, which uses all kinds of form input elements 322 | so you can just copy and paste elements as desired into your own form. Just be 323 | sure to update their names and IDs. You can find a working example of this test 324 | form here: 325 | https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/test.html 326 | 327 | Remember to include the fields *inside* the form that has the class `gform` 328 | and ensure that the `name` of the form element matches the new column heading in your spreadsheet. 329 | e.g: 330 | ```HTML 331 |

332 | 333 | 334 |
335 | ``` 336 | This will allow you to capture the person's favourite color: 337 | e.g: 338 | ![new-field-contains-data](https://cloud.githubusercontent.com/assets/194400/11547132/9f162f6a-9949-11e5-89ac-aeb91e025844.png) 339 | 340 | Let us know if you have any questions! 341 | 342 | 343 | ## Uploading Files 344 | 345 | [This resource](https://www.labnol.org/internet/receive-files-in-google-drive/19697/) may help you get started on uploading files to Google Drive from the Google Script. 346 | 347 | 348 | ## Frequently Asked Questions (FAQ's) 349 | 350 | 1. _How can I get help using this tutorial?_ 351 | 352 | - Feel free to [post an issue](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/new) describing in detail which steps you have gone through and what isn't working. To get a helpful response, please provide a [working example](https://stackoverflow.com/help/mcve) that reproduces your issue. For example, see [this sample CodePen](https://codepen.io/mckennapsean/pen/gdYzNY). 353 | 354 | 2. _Can I get edit access to the example spreadsheet?_ 355 | 356 | - No. This is being used to show a working example for anyone to copy, and an editable version could be broken accidentally, or maliciously, by any user. 357 | 358 | 3. _Why is the webpage forwarding to a bunch of text when I hit submit?_ 359 | 360 | - You are not properly loading the required Javascript which submits the data via AJAX, or your browser does not support AJAX. Please see Part 2 and check your console logs in case you are finding errors. 361 | 362 | 4. _Why is the webpage not successfully submitting the form?_ 363 | 364 | - Check your Javascript console logs. There could be an error while reading in the Javascript we have provided. There could be errors while submitting the form. It is required that your form have a class of `gform`, and also a `data-email` attribute if you have not set the `TO_ADDRESS` variable inside the Google Script file. Furthermore, the provided Javascript code also expects to see an email form element which it uses to check, a warning message for that element when an improper email is submitted, and then a `thank-you` div as well, which is shown after a form is successfully submitted. Please ensure that all of these HTML elements are in your form. See the sample file for code you can copy and paste. When you have all of these elements and a proper form set up, you should not see any error messages in your Javascript console when you hit submit. 365 | 366 | 5. _The webpage is saying my data was submitted, but why isn't my data being saved or sent to me?_ 367 | 368 | - When you copied the spreadsheet and published the Google Script, did you set the permissions to "Anyone, even Anonymous"? This is required for the form to work, since anyone on the internet can hit send to give you their data. Be sure that you have deployed the proper version of the script and used "Manage versions..." when making changes. 369 | 370 | 6. _How do I change the emails this script sends?_ 371 | 372 | - You can tweak the Google Script on the server to send emails to anyone and in whatever format you wish. This could be used to send a confirmation email to those contacting you, but we have not added this feature to this tutorial to avoid potential spamming. The sender of the email will always be the Google account you use to create the form/script, however. Further details on how to customize the email can be found in [the `MailApp` API](https://developers.google.com/apps-script/reference/mail/mail-app). You can instead use [the `GmailApp` API](https://developers.google.com/apps-script/reference/gmail/) which may be more flexible for certain use-cases. 373 | 374 | 375 | 7. _Is this secure? Can I use it for sensitive data?_ 376 | 377 | - No. While data that is sent over POST may be more protected, the information could easily be intercepted by a third party or middleman, and Google has complete access to the data inside a Google Spreadsheet. Email is also not a very secure communication medium by default. We would recommend you invest in a secure platform and server for storing your data if this is a requirement. 378 | 379 | 8. _What if my data is sent or stored in the wrong order?_ 380 | 381 | - If your data is in the wrong order, it is recommended to verify that you are loading the clientside JS correctly. The most effective way to do this is to place a `debugger` call inside the `handleFormSubmit()` function, and, if it hits the debugger and opens the respective Dev Tools for the broswer/environment, then the clientside JS is being loaded correctly. If the debugger isn't hit, then the JS is **not** either not loaded or not targeting your form, defaulting the data to a plain object which will have its own alphabetic ordering instead. 382 | 383 | ## Background Reading 384 | 385 | + Google Apps Scripts Basics: https://developers.google.com/apps-script/articles 386 | + Logger (like console.log): 387 | https://developers.google.com/apps-script/reference/base/logger 388 | + Simple Mail Merge using Google Spreadsheets: 389 | https://developers.google.com/apps-script/articles/mail_merge 390 | + Original Tutorial: AJAX post to google spreadsheet: https://stackoverflow.com/questions/10000020/ajax-post-to-google-spreadsheet which points to: 391 | + https://mashe.hawksey.info/2011/10/google-spreadsheets-as-a-database-insert-with-apps-script-form-postget-submit-method/ 392 | -------------------------------------------------------------------------------- /README.pt.md: -------------------------------------------------------------------------------- 1 | # Envie e-mail a partir de um formulário HTML *estático* usando o Google Apps Mail! 2 | 3 | **Language : [English](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.md) | [한국어](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.ko.md#google-apps-mail을-사용해-정적-html-form에서-메일을-보내세요) | [Español](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/blob/master/README.es.md#older-translation) | `Português`** 4 | 5 |
6 | 7 | Um **exemplo passo a passo** do uso de um **formulário HTML** para enviar uma mensagem de "Fale conosco" por e-mail sem um servidor de back-end usando um Google Script - sem PHP, Python, Ruby, Java, Node.js etc. 8 | 9 | Veja um exemplo funcional aqui: https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 10 | 11 | **_Observação_**: com o RGPD da UE, recomendamos [pesquisar recomendações sobre privacidade do usuário;](https://cloud.google.com/privacy/gdpr) você pode ser responsabilizado pela proteção dos dados pessoais dos usuários e deve fornecer a eles uma maneira de entrar em contato com você. 12 | 13 | **_Aviso_**: a API do Google tem limites para o número de e-mails que podem ser enviados por dia. 14 | Isso pode variar na sua conta do Google, consulte [os limites aqui](https://developers.google.com/apps-script/guides/services/quotas). 15 | Recomendamos implementar este tutorial até a Parte 3, já que os dados sempre serão adicionados à planilha/folha de cálculo primeiro 16 | e enviados por e-mail em seguida, se possível. 17 | 18 | ## Por quê? 19 | 20 | Precisávamos de uma maneira de enviar um e-mail de uma página HTML "*estática*" quando não *temos* (ou não *queremos ter*) um *servidor*. 21 | 22 | ### *Principais vantagens* 23 | 24 | + Sem "*back-end*" para implantar/manter/pagar 25 | + ***Totalmente personalizável*** - cada aspecto é personalizável! 26 | + E-mail *enviado via* ***Google Mail*** que está na ***lista de permissões em todos os lugares*** (*alta taxa de entregabilidade bem-sucedida*) 27 | + **Colete/Armazene** quaisquer **dados do formulário** em uma ***planilha*** para facilitar a visualização 28 | (*perfeito se você precisar compartilhar com pessoas não técnicas*) 29 | 30 | ## O quê? 31 | 32 | Em vez de usar um servidor para enviar seus e-mails, 33 | o que é *fácil* mas precisa de *manutenção*, 34 | use o Google para enviar e-mails por você 35 | e use o Google Spreadsheets para manter o registro dos dados! 36 | 37 | > Você *pode* usar um serviço "*gratuito*" como https://formspree.io/ para processar os envios de seus formulários 38 | se não se importar para onde está enviando seus dados e quiser gerenciar os dados enviados 39 | na caixa de entrada do seu e-mail (*bagunçado ... eca*!) 40 | *Ou*... você pode *investir* alguns minutos e manter os dados privados/gerenciáveis. *A escolha é sua*. 41 | 42 | ## *Como*? 43 | 44 | ### 1. Faça uma cópia da planilha de modelo 45 | 46 | > Sample: https://docs.google.com/spreadsheets/d/1Bn4m6iA_Xch1zzhNvo_6CoQWqOAgwwkOWJKC-phHx2Q/copy 47 | 48 | Faça login na sua conta Google e clique em "**Fazer uma cópia**..." 49 | 50 | ![1-make-copy](https://user-images.githubusercontent.com/1406149/29245471-feb7b034-7f97-11e7-9c0d-f06238e8362b.png) 51 | 52 | Isso deverá exibir para você algo como isso: 53 | 54 | ![2-copy-of-sheet](https://cloud.githubusercontent.com/assets/194400/10559710/3aec92f0-74ef-11e5-9295-f1988a23257b.png) 55 | 56 | > Observação: fique à vontade para alterar o nome da Cópia para o que quiser, isso não vai afetar o resultado. 57 | 58 | ### 2. Abra o Editor de Script 59 | 60 | Abra o **Editor de script...** clicando em "**Ferramentas**" > "**Editor de script...**" 61 | 62 | ![2 script-editor](https://cloud.githubusercontent.com/assets/194400/10559732/8db2b9f6-74ef-11e5-8bf2-de286f079929.png) 63 | 64 | Aqui está um *snapshot* do script que você precisa (*até este ponto do exercício*): [google-script-just-email.js](https://raw.githubusercontent.com/dwyl/learn-to-send-email-via-google-script-html-no-server/1d1c6727f69dec64a6b7f6bd6ff0dd72d0374210/google-script-just-email.js) 65 | 66 | ### 3. Defina o valor de `TO_ADDRESS` no Script 67 | 68 | **_Aviso:_** Se você não descomentar e definir seu e-mail como o valor de `TO_ADDRESS`, é possível que alguém que tenha habilidades na web altere seu formulário e envie os dados do e-mail para um endereço de e-mail definido por ele. 69 | 70 | Este risco pode ser pouco provável. Em vez disso, se quiser, pode deixar essa variável comentada se aceita esse possível risco e quiser ter a conveniência de definir essa variável do endereço do e-mail dentro do seu formulário HTML com um atributo `data-email`. Isso permite que você altere facilmente para onde enviar e-mails dentro de seu formulário HTML sem precisar voltar pelos passos 2-6. Essa funcionalidade *exige* que você use o arquivo JS fornecido na Parte Dois, Passo 10. 71 | 72 | Se você não quer aceitar esse risco em potencial, descomente o código da variável `TO_ADDRESS` e defina este valor igual ao e-mail que deverá receber os dados do formulário ao ser enviado. 73 | 74 | ![3-script-editor-showing-script](https://cloud.githubusercontent.com/assets/194400/10560379/9efa5b3a-7501-11e5-96ba-a9e3b2d77ee4.png) 75 | 76 | ### 4. Salve uma *nova versão* do seu Script 77 | 78 | Não é imediatamente *óbvio* mas você deve *clicar* em "**Arquivo**" > "**Gerenciar versões...**" 79 | **_Nota de tradução:_** pode ser que a interface em `https://script.google.com/` não esteja traduzida, mesmo se seu idioma esteja definido como Português por padrão, o caminho para salvar a nova versão então seria (em inglês): "**File**" > "**Manage versions...**" 80 | 81 | ![19 google-script-no-save-new-version](https://cloud.githubusercontent.com/assets/194400/10558249/527f3c98-74c1-11e5-8290-5af7fa7f5f75.png) 82 | 83 | Em seguida, *crie* sua nova versão: 84 | 85 | ![20 google-script-save-new-version](https://cloud.githubusercontent.com/assets/194400/10558250/53d21d5e-74c1-11e5-88c5-7bc2d8ce6228.png) 86 | 87 | ### 5. Publique o script *atualizado* como um Web App 88 | 89 | ![20 a-publish](https://cloud.githubusercontent.com/assets/194400/10558288/50980aa8-74c2-11e5-8576-72084a564779.png) 90 | 91 | Selecione a versão *mais recente* do projeto para publicar. 92 | :warning: Aviso: Você *deve* selecionar a opção `Qualquer um, mesmo anônimo` (`Anyone, even anonymous`) como a opção em 'Quem tem acesso ao aplicativo' ('Who has access to the app') ou as respostas do formulário não serão adicionadas à planilha! :warning: 93 | 94 | ![21 deploy-new-version](https://cloud.githubusercontent.com/assets/194400/10558251/570a5428-74c1-11e5-8ced-5dd26d3de3c4.png) 95 | 96 | ### 6. Autorize o script a enviar e-mails 97 | 98 | ![5 auth-required](https://cloud.githubusercontent.com/assets/194400/10560412/89d3fb0c-7502-11e5-81ce-fb239bf545b2.png) 99 | 100 | A menos que você [verifique seu script com o Google](https://developers.google.com/apps-script/guides/client-verification#requesting_verification), você precisará clicar em "Avançado" e "Ir para ... (inseguro)" para conceder permissões a este aplicativo. 101 | 102 | ![5-auth-failed-verification](https://user-images.githubusercontent.com/1406149/44312495-79583780-a3b6-11e8-9740-8c9b50f195d6.png) 103 | 104 | ![5-allow-sending-emails](https://cloud.githubusercontent.com/assets/194400/10560416/a86a26ae-7502-11e5-9add-d5081d409af4.png) 105 | 106 | Copie o URL do web app para a área de transferência / bloco de notas. 107 | Em seguida, clique em "OK". 108 | 109 | ![22 1-deploy-as-web-app](https://cloud.githubusercontent.com/assets/194400/10558255/6eec31e2-74c1-11e5-9c07-cea6209526df.png) 110 | 111 | 112 | ### 7. Crie seu formulário HTML *básico* 113 | 114 | Usando o modelo `index.html` deste repositório, 115 | crie seu próprio arquivo HTML com o formulário básico. (*salve o arquivo*) 116 | 117 | :warning: Se você já está tentando usar *seu próprio formulário* durante este passo, em vez do exemplo neste repositório: 118 | + Cada um dos elementos do seu formulário deve ter o atributo `name` igual ao nome da coluna na planilha do Google 119 | + A propriedade `class` do formulário deve ser `gform`, i.e. `
` 120 | + Se quiser alterar isso mais tarde, você precisará criar sua própria versão do arquivo `form-submit-handler.js` e adicionar o valor esperado na `class` 121 | 122 | 123 | > Lembre-se de alterar a propriedade `action` do formulário com a URL você copiou no passo anterior: 124 | 125 | ![7-html-form](https://user-images.githubusercontent.com/1406149/44312329-9b9c8600-a3b3-11e8-9816-4bdbbc96dc62.png) 126 | 127 | 128 | ### 8. Abra o formulário HTML (*página*) no seu navegador 129 | 130 | Preencha o formulário HTML com alguns dados de teste: 131 | 132 | ![html form](https://cloud.githubusercontent.com/assets/194400/10560494/674b64c4-7504-11e5-801a-b537d276f671.png) 133 | 134 | Envie o formulário. Você deve ver uma confirmação de que foi enviado: 135 | ![form sent](https://cloud.githubusercontent.com/assets/194400/10560501/8f605dd4-7504-11e5-8cd7-06d768beba4d.png) 136 | 137 | ### 9. Verifique a caixa de entrada do e-mail que você configurou 138 | 139 | Abra a caixa de entrada do endereço de e-mail que você definiu no **Passo 3** (*acima*) 140 | 141 | ![email received](https://cloud.githubusercontent.com/assets/194400/10560512/f87f1652-7504-11e5-8b0f-c342c395a193.png) 142 | 143 | 144 | > ***Pronto***. É isso. Você acabou de criar um formulário HTML que envia e-mails! 145 | 146 | # *Parte Dois - Deixando mais bonito* ... 147 | 148 | Vamos manter isso ***Bem Enxuto*** usando o [**PURE CSS**](https://purecss.io/start/) para os nossos estilos (*arrumar o formulário HTML "feio" do passo 8*). 149 | E enviar (`submit`) o formulário usando [**JQuery** "***AJAX***"](https://api.jquery.com/jquery.ajax/) para manter o usuário 150 | na sua página/site (*evitar a página de resposta "feia"*) 151 | 152 | ### 10. Enviar o formulário usando **JavaScript** "***AJAX***" 153 | 154 | Para *evitar* que a página mude para a resposta/resultado `JSON`, 155 | precisamos enviar o formulário usando ***AJAX***. 156 | 157 | Baixe [este arquivo Javascript](form-submission-handler.js) e atualize seu `index.html` para incluir ele ao *final* do arquivo 158 | (*antes do fechamento da tag``) 159 | 160 | ```html 161 | 162 | ``` 163 | 164 | **Aviso**: Se você não definiu a variável `TO_ADDRESS` no Passo 3, então será necessário incluir um atributo `data-email="example@email.net"` dentro do elemento principal do formulário. Veja o formulário de exemplo para mais detalhes. Caso contrário, se você definiu essa variável, não precisa deste atributo no formulário. 165 | 166 | Isso mantém a pessoa na mesma página. Sem atualizar. O próximo passo é fazer uma mensagem de agradecimento aparecer. 167 | 168 | ### 11. Adicione uma mensagem de agradecimento personalizada exibida quando o formulário for enviado 169 | 170 | Depois de concluir o passo 10, você pode escolher adicionar uma mensagem de agradecimento após o envio. Adicione o seguinte código entre as tags `` e `
`: 171 | 172 | ```html 173 | 178 | ``` 179 | 180 | Isso agora exibirá uma *mensagem* de "Agradecimento" quando o formulário for enviado: 181 | 182 | ![thankyou message](https://cloud.githubusercontent.com/assets/194400/10561147/4086a89a-7517-11e5-840d-7f490353e225.png) 183 | 184 | Ajuste sua mensagem editando o conteúdo da div `thankyou_message`. 185 | 186 | ### 12. Usando CSS para fazer o formulário *ficar bonito* 187 | 188 | Para **este** *exemplo* estamos usando ***Pure CSS***: https://purecss.io/start/ 189 | porque ele é ***leve*** (***4,0 KB minificado e gzipado***) 190 | e *resolve* nosso "*problema*" atual: fazer ele ficar bonito. 191 | 192 | ![PureCSS-Logo-Intro](https://github-cloud.s3.amazonaws.com/assets/194400/10565838/72d6d52a-75d2-11e5-9b92-ca02b1124920.png) 193 | 194 | ![PureCSS-module-sizes](https://github-cloud.s3.amazonaws.com/assets/194400/10565844/8f4885a0-75d2-11e5-9490-e3fc42c32616.png) 195 | 196 | Sem gastar *tanto* tempo, podemos fazer o formulário *parecer* ***muito mais*** agradável: 197 | 198 | ![contact form with pure css](https://github-cloud.s3.amazonaws.com/assets/194400/10566392/f38bc454-75dd-11e5-85dd-6819494a98f2.png) 199 | 200 | ### 13. Faça o e-mail ficar bonito também! 201 | 202 | Por padrão, o corpo do e-mail enviado contém os pares de chave-valor do formulário, com a chave como um `

` e o valor como um `
`. Esta é uma visulização relativamente básica e infalível dos dados. 203 | 204 | Você deve obter algo parecido com: 205 | ![Nicely formatted email](https://cloud.githubusercontent.com/assets/5610747/22168070/335ad734-df62-11e6-9523-6e193e94151f.png) 206 | 207 | > Lembre-se de que este é um projeto em andamento e pode permitir que você receba mais do que esperava no e-mail. Como o conteúdo do e-mail agora está passando por todos os dados enviados no formulário, se um robô ou usuário mal-intencionado decidir fazer um `POST` com mais dados do que você pediu, você provavelmente os receberá na sua caixa de entrada. Use com cautela por enquanto. Estamos investigando melhorias. 208 | 209 | No entanto, você pode modificar isso, por meio do **Editor de script**. A linha: 210 | 211 | ```javascript 212 | result += "

" + key + "

" + obj[key] + "
"; 213 | ``` 214 | 215 | tem tudo que você precisa. Você pode ajustar a marcação para se adequar à sua necessidade. 216 | Escolhemos um `

` porque era o melhor tamanho para o e-mail e adicionamos uma pequena quantidade de CSS para corrigir a capitalização (as letras são todas minúsculas no objeto JS) e um pouco de espaçamento por padrão. 217 | Embora estilos inline como estes sejam geralmente má prática em páginas web normais, para um HTML no e-mail eles são a única maneira fiável de usar CSS! 218 | Optamos por um `
` para a parte do valor, porque ele poderia ser qualquer coisa - uma linha só, múltiplas linhas (um `

` por exemplo não seria suficiente). 219 | 220 | Aproveitando que já estamos aqui, também há uma opção `replyTo` para o método `sendEmail()` que está comentada por padrão: 221 | 222 | ```javascript 223 | MailApp.sendEmail({ 224 | to: TO_ADDRESS, 225 | subject: "Contact form submitted", 226 | // replyTo: String(mailData.email), // Isso é opcional e depende do seu formulário possuir um campo denominado `email` 227 | htmlBody: formatMailBody(mailData) 228 | }); 229 | ``` 230 | 231 | Você pode descomentar essa opção se quiser adicionar um campo de "Responder para..." no seu e-mail. O exemplo no script definirá o "Responder para..." (`reply-to`) como o e-mail enviado no formulário. 232 | 233 | A documentação do Google fornece mais informações sobre o método `MailApp.sendEmail` (por exemplo `cc`/`bcc` etc.) se estiver interessado: https://developers.google.com/apps-script/reference/mail/mail-app 234 | 235 | # *Parte Três - Armazenar os dados enviados pelo formulário de contato em uma planilha* 236 | 237 | Enviar os dados do formulário diretamente para a caixa de entrada 238 | do seu e-mail é um *ótimo* primeiro passo, mas podemos fazer melhor. 239 | Além disso, conforme já vimos acima, o Google tem limites de quantos 240 | e-mails você pode enviar por dia, portanto, armazenar os dados em uma 241 | planilha é mais seguro e menos sujeito a perda de informações. 242 | 243 | ### 14. Adicione a função `record_data` no seu Google Apps Script 244 | 245 | ![record_data example](https://cloud.githubusercontent.com/assets/194400/10581613/8b4f9ad4-767b-11e5-90cc-962a9d6acc91.png) 246 | 247 | Isso irá salvar os dados recebidos do `POST` como uma *linha* na planilha. 248 | Consulte: [**google-apps-script.js**](google-apps-script.js) para o código completo que você pode *copiar e colar*. 249 | 250 | ### 15. Salve uma nova versão e publique-a novamente 251 | 252 | Siga os passos 4, 5 e 6 para salvar uma nova versão e ***publicar novamente*** (***re-publish***) o script. 253 | 254 | ### 16. Teste novamente o envio do formulário 255 | 256 | ![submit the form](https://cloud.githubusercontent.com/assets/194400/10582654/cf3081e6-7680-11e5-9fd1-b989a8ba0b65.png) 257 | 258 | ### 17. Confirme se os dados foram inseridos na planilha 259 | 260 | ![17-confirm-data-inserted](https://cloud.githubusercontent.com/assets/194400/10582676/eb8af5d8-7680-11e5-92bb-30dd08d2d7b3.png) 261 | 262 | 263 | ### _Live_ Server (_no seu `localhost`_) 264 | 265 | Como estamos carregando arquivos **.js** externos, nosso navegador 266 | não nos _permite_ simplesmente abrir o **index.html** de um diretório 267 | local para testar o formulário. 268 | 269 | Abra seu terminal e execute o commando 270 | para _**instalar** o **node_modules** e **iniciar** o **live server**_: 271 | 272 | ```sh 273 | npm install live-server --save-dev && node_modules/.bin/live-server --port=8000 274 | ``` 275 | 276 | A instalação levará um minuto para concluir, 277 | mas assim que terminar, seu `live-server` será inicializado. 278 | 279 | Isso inicia um servidor HTTP Node.js na porta 8000 280 | e abre o formulário, que você acabou de criar, em seu navegador padrão. 281 | Se quiser atualizar os estilos de formulário em **style.css** 282 | ou o Javascript client-side em **form-submit-handler.js**, 283 | certifique-se de editar o **index.html** para carregar esses recursos 284 | localmente em vez de por meio do GitHub. 285 | 286 | > **Observação**: Esta é apenas uma _pequena_ amostra do Node.js para iniciantes. 287 | Você **não** precisa do Node.js para "publicar" este formulário, 288 | você consegue fazer ele funcionar em **_qualquer_ servidor web** que hospede HTML/CSS/JavaScript. 289 | Se você nunca usou Node.js antes, veja: http://nodeguide.com/beginner.html, 290 | mas para os fins deste exercício (_enviar um formulário **sem** um servidor_), 291 | você _não **precisa**_ do Node.js ou do `live-server` 292 | eles são apenas uma adição _agradável_ para quando se está criando 293 | seu formulário, pois eles automaticamente recarregam a página quando você 294 | faz alterações nos arquivos do projeto usando seu editor de texto! 295 | 296 | 297 | # *Quer mais*? 298 | 299 | Se quiser que continuemos com este tutorial, [***por favor nos avise***!](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues) 300 | 301 | Para sua conveniência, hospedamos uma demonstração do formulário no GitHub Pages, 302 | confira para ver o código e como funciona: 303 | https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/ 304 | 305 | 306 | ## Adicione seus próprios campos! 307 | 308 | Em resposta à [solicitação de Henry Beary](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/9), 309 | tornamos o tratamento do formulário *genérico*, o que significa que agora você pode adicionar quaisquer campos que desejar ao formulário. 310 | 311 | Também criamos um formulário, `test.html`, que usa todos os tipos de elementos de input para que você possa simplesmente copiar e colar os elementos como quiser em seu próprio formulário. Apenas certifique-se de atualizar seus `name`s e `id`s. Você pode encontrar um exemplo funcional deste formulário de teste aqui: 312 | https://dwyl.github.io/learn-to-send-email-via-google-script-html-no-server/test.html 313 | 314 | Lembre-se de incluir os campos *dentro* do formulário que possui a classe `gform` 315 | e certifique-se de que o `name` do elemento do formulário corresponda ao título da nova coluna em sua planilha. 316 | Por exemplo: 317 | ```HTML 318 |

319 | 320 | 321 |
322 | ``` 323 | Com isso você consegue salvar a cor favorita da pessoa. 324 | Por exemplo: 325 | ![new-field-contains-data](https://cloud.githubusercontent.com/assets/194400/11547132/9f162f6a-9949-11e5-89ac-aeb91e025844.png) 326 | 327 | Nos avisem se tiver quaisquer dúvidas! 328 | 329 | ## Enviando arquivos 330 | 331 | [Este material](https://www.labnol.org/internet/receive-files-in-google-drive/19697/) pode ajudá-lo a dar início no envio de arquivos para o Google Drive a partir do Google Script. 332 | 333 | 334 | ## Perguntas Frequentes (FAQ's) 335 | 336 | 1. _Como posso obter ajuda para usar este tutorial?_ 337 | 338 | - Sinta-se à vontade para [criar uma issue](https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server/issues/new) descrevendo detalhadamente quais passos você realizou e o que não está funcionando. Para obter uma resposta relevante, forneça um [exemplo funcional](https://stackoverflow.com/help/mcve) que reproduza seu problema. Por exemplo, veja [este CodePen de modelo](https://codepen.io/mckennapsean/pen/gdYzNY). 339 | 340 | 2. _Posso obter permissão de edição na planilha de modelo?_ 341 | 342 | - Não. Ela está sendo usada para demonstrar um exemplo funcional que qualquer pessoa pode copiar, e uma versão editável poderia quebrar acidentalmente, ou maliciosamente, por qualquer usuário. 343 | 344 | 3. _Por que a página está me encaminhando para um monte de texto quando clico em enviar?_ 345 | 346 | - Você não está carregando corretamente o JavaScript necessário para enviar os dados via AJAX ou seu navegador não oferece suporte a AJAX. Por favor, consulte a Parte 2 e verifique os logs do seu console caso esteja encontrando erros. 347 | 348 | 4. _Por que a página não está enviando o formulário com sucesso?_ 349 | 350 | - Verifique os logs do console JavaScript do seu navegador. Pode haver um erro ao ler o JavaScript que fornecemos. Pode haver erros ao enviar o formulário. É necessário que seu formulário tenha uma classe de `gform` e também um atributo `data-email` se você não definiu a variável `TO_ADDRESS` dentro do arquivo do Google Script. Além disso, o código JavaScript fornecido também espera encontrar um elemento de formulário de e-mail que usa para verificação, uma mensagem de aviso para esse elemento quando um e-mail impróprio é enviado e, em seguida, um div de `agradecimento` (`thank-you`) também, que é mostrado após ser enviado com sucesso. Certifique-se de que todos esses elementos HTML estejam em seu formulário. Consulte o arquivo de exemplo para obter códigos que você pode copiar e colar. Quando tiver todos esses elementos e um formulário configurado corretamente, não deverá ver nenhuma mensagem de erro no console JavaScript ao clicar em enviar. 351 | 352 | 5. _A página está dizendo que meus dados foram enviados, mas por que eles não estão sendo salvos ou enviados para mim?_ 353 | 354 | - Quando você copiou a planilha e publicou o Google Script, você definiu as permissões como "Qualquer pessoa, até mesmo anônima" ("Anyone, even Anonymous")? Isso é necessário para que o formulário funcione, já que qualquer pessoa na Internet pode clicar em enviar para fornecer seus dados. Certifique-se de ter publicado a versão adequada do script e usado "Gerenciar versões..." ao fazer alterações. 355 | 356 | 6. _Como altero os e-mails que este script envia?_ 357 | 358 | - Você pode ajustar o Google Script no servidor para enviar e-mails para qualquer pessoa e no formato que quiser. Isso pode ser usado para enviar um e-mail de confirmação para aqueles que entram em contato com você, mas não adicionamos esse recurso a este tutorial para evitar possível spam. Porém, o remetente do e-mail sempre será a conta do Google que você usar para criar o formulário/script. Mais detalhes sobre como personalizar o e-mail podem ser encontrados [na documentação da `MailApp` API](https://developers.google.com/apps-script/reference/mail/mail-app). Em vez disso, você pode usar [a `GmailApp` API](https://developers.google.com/apps-script/reference/gmail/), que pode ser mais flexível para certos casos de uso. 359 | 360 | 361 | 7. _Isso é seguro? Posso usá-lo para dados sensíveis?_ 362 | 363 | - Não. Embora os dados enviados por POST possam estar mais protegidos, as informações podem ser facilmente interceptadas por terceiros ou intermediários, e o Google tem acesso total aos dados em uma planilha do Google. E-mail também não é um meio de comunicação muito seguro por padrão. Recomendamos que você invista em uma plataforma e servidor seguros para armazenar seus dados, se isso for um requisito. 364 | 365 | 366 | ## Leituras complementares 367 | 368 | + Google Apps Scripts Basics: https://developers.google.com/apps-script/articles 369 | + Logger (como `console.log`): 370 | https://developers.google.com/apps-script/reference/base/logger 371 | + Mail Merge simples utilizando Google Spreadsheets: 372 | https://developers.google.com/apps-script/articles/mail_merge 373 | + Tutorial original: AJAX post to google spreadsheet: https://stackoverflow.com/questions/10000020/ajax-post-to-google-spreadsheet que direciona para: 374 | + https://mashe.hawksey.info/2011/10/google-spreadsheets-as-a-database-insert-with-apps-script-form-postget-submit-method/ 375 | --------------------------------------------------------------------------------