{"id":18561271,"url":"https://github.com/apostrophecms/form-submission-google","last_synced_at":"2025-10-04T17:10:17.770Z","repository":{"id":44880919,"uuid":"413967722","full_name":"apostrophecms/form-submission-google","owner":"apostrophecms","description":"Google spreadsheet submission for the ApostropheCMS form builder","archived":false,"fork":false,"pushed_at":"2025-04-14T07:33:38.000Z","size":42,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-22T14:15:36.824Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apostrophecms.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-10-05T20:21:58.000Z","updated_at":"2025-04-14T07:32:27.000Z","dependencies_parsed_at":"2025-04-10T17:54:14.671Z","dependency_job_id":"709eff2b-72c8-4a4d-b17a-310e4221bd58","html_url":"https://github.com/apostrophecms/form-submission-google","commit_stats":{"total_commits":9,"total_committers":4,"mean_commits":2.25,"dds":0.5555555555555556,"last_synced_commit":"cf113361eded93c26f4f76260e7e3084ee0a78af"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/apostrophecms/form-submission-google","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fform-submission-google","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fform-submission-google/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fform-submission-google/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fform-submission-google/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apostrophecms","download_url":"https://codeload.github.com/apostrophecms/form-submission-google/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fform-submission-google/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262598162,"owners_count":23334668,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T22:06:23.913Z","updated_at":"2025-10-04T17:10:12.667Z","avatar_url":"https://github.com/apostrophecms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/apostrophecms/apostrophe/main/logo.svg\" alt=\"ApostropheCMS logo\" width=\"80\" height=\"80\"\u003e\n\n  \u003ch1\u003eApostrophe Forms Google Sheets Submission\u003c/h1\u003e\n  \u003cp\u003e\n    \u003ca aria-label=\"Apostrophe logo\" href=\"https://docs.apostrophecms.org\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/MADE%20FOR%20ApostropheCMS-000000.svg?style=for-the-badge\u0026logo=Apostrophe\u0026labelColor=6516dd\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"Join the community on Discord\" href=\"http://chat.apostrophecms.org\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/discord/517772094482677790?color=5865f2\u0026label=Join%20the%20Discord\u0026logo=discord\u0026logoColor=fff\u0026labelColor=000\u0026style=for-the-badge\u0026logoWidth=20\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"License\" href=\"https://github.com/apostrophecms/blog/blob/main/LICENSE.md\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/static/v1?style=for-the-badge\u0026labelColor=000000\u0026label=License\u0026message=MIT\u0026color=3DA639\"\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\nThis module adds an additional form submission option to the Apostrophe Forms extension. It allows website managers to configure individual forms to submit to a specific Google Docs spreadsheet.\n\n## Installation\n\n```bash\nnpm install @apostrophecms/form-submission-google\n```\n\n## Usage\n\n### Initialization\n\nAdd this module along with the other Apostrophe Form modules in `app.js` to instantiate it.\n\n```javascript\nrequire('apostrophe')({\n  shortName: 'my-project',\n  modules: {\n    // The main form module\n    '@apostrophecms/form': {},\n    '@apostrophecms/form-submission-google': {},\n    // ... Other form widgets\n  }\n});\n```\n\n### Set up the Google Sheets API project\n\n1. **Create a project in [the Google Cloud Platform API console](https://console.developers.google.com/apis/dashboard).** Enable the Google Sheets API on the project in the API Library.\n2. **Create an [API service account](https://cloud.google.com/iam/docs/service-accounts)** in the Google API Console service.\n3. **Save the credentials JSON file** provided with the new service account. *You may not be able to download this again.*\n4. **Add the credentials file to the `modules/@apostrophecms/form` directory in your Apostrophe project as `credentials.json`.**\n  - Note: We do not recommend committing this file to version control if your code is public. You should add it to the `.gitignore` file (for Git) and put it directly on your production server. *Alternately* you can provide the credentials as JSON in an environment variable named `GOOGLE_APPLICATION_CREDENTIALS`.\n5. **Copy the service account email address.** You will need to add this as an \"Edit\"-level user on your Google spreadsheet as you would a human editor.\n6. **Plan for the service account credentials to expire in 10 years.** The service account credentials have a long life span, but it is not infinite.\n\n### Create your spreadsheet.\n\nThe sheet must exist, but does not necessarily need to be set up with column headings before use. This can be done later by CMS users as well. There is help text in the UI directing them to make note of the spreadsheet ID and sheet name.\n\nColumn headers in the Google spreadsheet must match the form field *names* (not the field labels), or else the module will add new columns to the spreadsheet.\n\n#### A warning about editing the spreadsheet\n\nPlease note that you must not add any empty, unlabeled columns to the spreadsheet once submissions begin. Due to the [rules of Google's spreadsheet API](https://developers.google.com/sheets/api/guides/values#appending_values) the gap will be considered as the start of a \"new table\" and newly appended rows will start at that column, which is probably not what you want. If this does happen, move the data over and add a header to the empty column.\n\n### Note on dates and times\n\n\"Date Submitted\" and \"Time Submitted\" columns are included in the Google spreadsheet automatically. These are always in [UTC (Coordinated Universal Time)](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). For best results, format the Google spreadsheet columns as plain text.\n\nYou can rename these by setting options on the `@apostrophecms/form` module. The column label should be set before the form is in use to keep all date/time data in the same column.\n\n```javascript\n// modules/@apostrophecms/form/index.js\nmodule.exports = {\n  options: {\n    dateColumnLabel: 'Submission date'\n    timeColumnLabel: 'Submission time'\n  }\n};\n```\n\n\n### Modifying the submission before it is sent to Google\n\nIf you wish to modify the submitted data just before it goes to Google, for instance to add a new property, you can add a handler to the `@apostrophecms/form:beforeGoogleSheetSubmit` event. This event handler may be asynchronous.\n\nThe example below demonstrates adding a \"Unique Key\" property to the data based on the date submitted, time submitted, and an email field in the submission:\n\n```javascript\n// modules/@apostrophecms/form/index.js\nmodule.exports = {\n  handlers (self) {\n    return {\n      beforeGoogleSheetSubmit: {\n        addUniqueKey (req, form, data) {\n          data['Unique Key'] = `${data['Date Submitted']}_${data['Time Submitted']}_${data.email}`;\n        }\n      }\n    };\n  }\n};\n```\n\nThe submitted spreadsheet rows will now include the additional column.\n\n### Issues with column formatting\n\nThis module sends data to Google Sheets \"as entered,\" i.e. as if the it were typed by the user in Google Sheets. In most cases this does good things: dates are detected as dates, times as times, numbers as numbers, etc.\n\nHowever in certain cases, the results may be surprising. For instance, a phone number with a leading `0` and no spaces or punctuation will lose its leading `0` because this is the standard behavior of Google Sheets when it believes it has detected a number. Google does not store the zero in this situation, it is truly gone.\n\nFortunately you can correct this by formatting the column correctly in Google Sheets. Open the sheet, select the column that will contain phone numbers, and select \"Format -\u003e Number -\u003e Plain text\". Leading zeroes will not be removed from future submissions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fform-submission-google","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapostrophecms%2Fform-submission-google","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fform-submission-google/lists"}