{"id":37013665,"url":"https://github.com/ohlincik/craft-perform","last_synced_at":"2026-01-14T01:21:32.110Z","repository":{"id":57037852,"uuid":"154236866","full_name":"ohlincik/craft-perform","owner":"ohlincik","description":"PerForm is a Craft CMS 3 plugin that facilitates form submission storage \u0026 email delivery, form testing, client-side validation, spam protection, and more.","archived":false,"fork":false,"pushed_at":"2020-10-27T19:49:45.000Z","size":1317,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-17T22:35:54.834Z","etag":null,"topics":["craft","craft-plugin","craft3","craftcms","craftcms-plugin","perfom","webform"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ohlincik.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}},"created_at":"2018-10-23T00:42:34.000Z","updated_at":"2022-01-03T19:18:22.000Z","dependencies_parsed_at":"2022-08-23T21:00:31.570Z","dependency_job_id":null,"html_url":"https://github.com/ohlincik/craft-perform","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/ohlincik/craft-perform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohlincik%2Fcraft-perform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohlincik%2Fcraft-perform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohlincik%2Fcraft-perform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohlincik%2Fcraft-perform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ohlincik","download_url":"https://codeload.github.com/ohlincik/craft-perform/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohlincik%2Fcraft-perform/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28407684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["craft","craft-plugin","craft3","craftcms","craftcms-plugin","perfom","webform"],"created_at":"2026-01-14T01:21:31.511Z","updated_at":"2026-01-14T01:21:32.096Z","avatar_url":"https://github.com/ohlincik.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PerForm plugin for Craft CMS 3.x\n\nPerForm facilitates form handling for Craft developers. Process, test, store, send email notifications, configure client side validation, include powerful spam protection, and more.\n\n![Screenshot](resources/img/plugin-logo.png)\n\n## Requirements\n\nThis plugin requires Craft CMS 3.0.18 or later.\n\n## Installation\n\nTo install the PerForm plugin, follow these instructions:\n\n1. Open your terminal and go to your Craft project:\n\n        cd /path/to/project\n\n2. Then tell Composer to load the plugin:\n\n        composer require perfectus/perform\n\n3. In the Control Panel, go to *Settings → Plugins* and click the “Install” button for PerForm.\n\nPerForm works on Craft 3.x.\n\n## PerForm Usage Notes\n\n### Issues\n\nPlease report any issues you find to the [PerForm Issues](https://github.com/ohlincik/craft-perform/issues) page.\n\n## PerForm Overview\n\nPerForm is built for **Craft CMS web developers** with ultimate flexibility in mind. The goal is to let you build the web form front-end anyway you'd like while following a handful of simple conventions. After you include the web form in your Twig template, the plugin will assist you with the following:\n\n* Client-side form validation using [Parsley](http://parsleyjs.org/)\n* Spam protection using the [Google Invisible ReCaptcha](https://developers.google.com/recaptcha/docs/invisible)\n* Saving the form submissions in the Control Panel for future reference\n* Delivering the form submission content via email (built-in testing using [Mailtrap](https://mailtrap.io/))\n\n![Screenshot](resources/screenshots/perform-control-panel-index-example.png)\n\n## Using PerForm\n\nUsing PerForm is quite simple. The following needs to be in place to get PerForm working properly.\n\n1. PerForm **formTag** — to make sure that the form will be submitted to and processed by PerForm\n2. Form built according to PerForm **form fields conventions** — to make sure that PerForm knows which fields need to be processed\n3. PerForm **form settings** — to let PerForm know how to process the form submission\n4. PerForm **plugin settings** — to configure additional options and connect PerForm with external services\n\nContinue reading below to learn more about each concept. We're going to put together a simple form that collects *First Name*, *Last Name* and *Email* to illustrate how to build, setup and configure a form that will be processed by PerForm.\n\n### PerForm `formTag`\n\nThe `formTag` variable generates the opening `\u003cform\u003e` tag. The form will not be processed without it.\n\n```twig\n{{ craft.perForm.formTag({\n  'entryId': entry.id\n}) }}\n```\n\nThe `formTag` has one required configuration option **entryId** that specifies which entry contains the *Form Settings* for the form that will be included below. This will generate html code that looks something like this.\n\n```html\n\u003cnoscript\u003e\n    \u003cstyle type=\"text/css\"\u003e\n        #perform-submission {\n            display: none;\n        }\n    \u003c/style\u003e\n    \u003cp class=\"perform--warning-panel\"\u003e\n        \u003cstrong\u003eYou don't have JavaScript enabled.\u003c/strong\u003e\u003cbr\u003e\n        To continue, please \u003ca href=\"http://enable-javascript.com/\" target=\"_blank\"\u003e\n        enable JavaScript in your browser\u003c/a\u003e.\n    \u003c/p\u003e\n\u003c/noscript\u003e\n\n\u003cform role=\"form\" id=\"perform-submission\" method=\"post\" action=\"\" accept-charset=\"UTF-8\"\u003e\n    \u003cinput type=\"hidden\" name=\"action\" value=\"perform/public/submit-form\"\u003e\n    \u003cinput type=\"hidden\" name=\"entry_id\" value=\"2\"\u003e\n    \u003cinput type=\"hidden\" name=\"CRAFT_CSRF_TOKEN\" value=\"yWz6...\"\u003e\n\n```\n\nNote, that if you provide an incorrect **entryId** or have not setup the *Form Settings* associated with the provided **entryId** properly, you will see a message on the screen that looks like this.\n\n![Screenshot](resources/screenshots/perform-invalid-formtag-message.png)\n\n### PerForm form fields conventions\n\nPerForm is not concerned with how you decide to build your form, that's totally up to you. While you're building your form, you need to keep one important convention in mind. You need to let PerForm know what are the field lables and values that whould be processed and stored.\n\n```html\n\u003clabel for=\"fields_firstName\"\u003eFirst Name\u003c/label\u003e\n\u003cinput type=\"hidden\" name=\"fields[firstName][label]\" value=\"First Name\"\u003e\n\u003cinput type=\"text\" id=\"fields_firstName\" name=\"fields[firstName][value]\"\u003e\n```\n\nIn the example above, PerForm will process and store the field for **First Name** textbox. You need to let PerForm know what is the field label and what is the field value:\n\n- *Label:* use a hidden field `\u003cinput type=\"hidden\" name=\"fields[firstName][label]\" value=\"First Name\"\u003e`\n- *Value:* configure the input **name** to correspond with the hidden field for label `name=\"fields[firstName][value]\"`\n\nNote that in the example above, you can replace `firstName` with anything you'd like. In fact, you will need to provide a unique 'handle' for each field you want PerForm to process. Of course, you don't need to build the fields by hand (and you shouldn't). There are many ways to go about this, for example, you can use *Twig Macros*, *Twig Includes* or a combination of both.\n\n### PerForm form settings\n\nPerForm comes with a *Form Settings* field type that allows you to add a field to an entry that will include your form. You will need to create a field and add it to the layout of your entry (actually, this can be any entry, not specifically the entry that will include the template to generate your form). You will specify which entry includes the form settings through the [formTag variable](#perform-formtag).\n\n![Screenshot](resources/screenshots/perform-create-form-settings-field.png)\n\nOnce you create the field and associate it with an entry layout, you can provide the appropriate settings for the particular form.\n\n![Screenshot](resources/screenshots/perform-form-settings.png)\n\n- **Form Title** — a user-friendly label to identify the form submissions that come from this form in the listing page in Control Panel.\n- **Form Handle** — this setting is useful if you decide to provide different templates for different form and if you want to setup your own *email content templates*.\n- **Form Subject** — what will be displayed on the form submissions listing in the Control Panel and used for the *Email Subject* of the email notification. The subject is processed by Twig so you can include the content of any field that appears in the form using the following format `{{ fieldHandle.value }}`.\n- **Notification Recipients** — one or more email addresses that should receive the form submission notification after the form is submitted and processed.\n- **Notification ReplyTo** — the form submission notification email is sent using the **System Email Address** configured in the *Control Panel → Settings → Email* so whenever the recipient clicks the 'Reply' button in their email client, the email reply would not go to whomever actually submitted the form. You can provide the email address of the actual person that submitted the form if you are collecting it through the form, in which case you just need to reference the value of the form field. For example, `{{ email.value }}` if the form input handle happens to be 'email'.\n- **Test Mode Enabled** — when this setting is ON, the form submissions will be automatically set to **Test** status and delivered according to the *Test Email Delivery Settings* instead of the email addresses specified in the 'Notification Recipients'.\n\n### PerForm plugin settings\n\n![Screenshot](resources/screenshots/perform-plugin-settings.png)\n\n- **Test Email Delivery Settings** — use the [Mailtrap](https://mailtrap.io) email testing service to test out the PerForm email notifications. Sign up for an account at Mailtrap and input the Inbox *Username* and *Password* in the provided fields.\n- **Client Side Validation** — PerForm currently does not offer *Server-side form validation* but you can easily validate your form fields **before** the form is submitted. When you check this setting, PerForm will automatically include 'parsley.js' in each form. You will need to [configure your form inputs](http://parsleyjs.org/doc/index.html#validators) to work with Parsley.\n- **Google Invisible reCAPTCHA Configuration** — when you turn on this setting, the Google Invisible reCAPTCHA will be automatically enabled for all PerForm driven forms within your site. You can [learn more](https://developers.google.com/recaptcha/docs/invisible) about how it works, but all you need to do is [create reCAPTCHA API keys](https://www.google.com/recaptcha/admin#list) for your site and PerForm will take care of everything else.\n- **Custom Email Templates** — provide your own templates to generate the *email notifications*\n\n## Example: Simple Contact Form\n\nThis example will introduce you to how the [various concepts](#using-perform) fit together and will ultimately allow you to build much larger and more complex forms.\n\nThe following template code:\n\n```twig\n{% extends \"_layouts/base\" %}\n{% block pageContent %}\n  \u003ch1\u003eContact Form\u003c/h1\u003e\n\n  {% if craft.app.request.getParam('success') == '✓' %}\n    \u003cp\u003eThank you for submitting your information\u003c/p\u003e\n  {% else %}\n    {# Generate the PerForm form tag #}\n    {{ craft.perForm.formTag({\n      'entryId': entry.id\n    }) }}\n\n      {# First Name field #}\n      \u003clabel for=\"fields_firstName\"\u003eFirst Name\u003c/label\u003e\u003cbr\u003e\n      \u003cinput type=\"hidden\" name=\"fields[firstName][label]\" value=\"First Name\"\u003e\n      \u003cinput type=\"text\" id=\"fields_firstName\" name=\"fields[firstName][value]\"\u003e\n\n      \u003cbr\u003e\u003cbr\u003e\n\n      {# Last Name field #}\n      \u003clabel for=\"fields_lastName\"\u003eLast Name\u003c/label\u003e\u003cbr\u003e\n      \u003cinput type=\"hidden\" name=\"fields[lastName][label]\" value=\"Last Name\"\u003e\n      \u003cinput type=\"text\" id=\"fields_lastName\" name=\"fields[lastName][value]\"\u003e\n\n      \u003cbr\u003e\u003cbr\u003e\n\n      {# Email field #}\n      \u003clabel for=\"fields_email\"\u003eEmail\u003c/label\u003e\u003cbr\u003e\n      \u003cinput type=\"hidden\" name=\"fields[email][label]\" value=\"Email\"\u003e\n      \u003cinput type=\"email\" id=\"fields_email\" name=\"fields[email][value]\"\u003e\n\n      \u003cbr\u003e\u003cbr\u003e\n\n      \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n    \u003c/form\u003e\n  {% endif %}\n{% endblock %}\n```\n\nWith the following Form Settings:\n\n![Screenshot](resources/screenshots/perform-simple-form-settings.png)\n\nAnd, without any styling, will generate a very simple form.\n\n![Screenshot](resources/screenshots/perform-simple-form.png)\n\nBy default, PerForm will redirect the browser to the **original url** and set a flash message with the **submissionId** key ([more details](#redirecting-after-submit) about redirecting and flash messages) which you can test for and display a \"Thank You\" message for the visitor. So, after filling out and submitting the form, the visitor will see this content in the browser:\n\n![Screenshot](resources/screenshots/perform-simple-form-success.png)\n\nThe form submission will be processed by PerForm, saved in the Control Panel:\n\n![Screenshot](resources/screenshots/perform-control-panel-index.png)\n\nWhere a user with the appropriate permissions can see the form submission detail:\n\n![Screenshot](resources/screenshots/perform-control-panel-detail.png)\n\nAnd, the **notification recipient** receives the following email notification:\n\n![Screenshot](resources/screenshots/perform-simple-form-email-notification.png)\n\n### Redirecting after submit\n\nBy default, PerForm will automatically reload the current page. \n\nTo override this default behavior, include `redirectInput` function after the PerForm `formTag` and specify your redirect destination.\n\n```twig\n{# Generate the PerForm form tag #}\n{{ craft.perForm.formTag({\n'entryId': entry.id\n}) }}\n\n{{ redirectInput('contact-form/thank-you') }}\n```\n\nSometimes it's convenient to retrieve and display the information included in the form submission on the 'thank you' page. You can achieve this by setting query string parameters (although, there is a [much better way](#using-flash-messages)). The following variables can be used within the URL/path you set:\n\n- `submissionId` — the ID of the Form Submission. You can use it to retrieve the entire submission content using the `getSubmissionById` plugin variable, however, for security reasons it is not advisable to expose the ID as a query string parameter. There is a [much better way](#using-flash-messages) to do this built into PerForm.\n- `dateCreated` — the date/time when submission was created\n- `dateUpdated` — the date/time when submission was last updated (same as `dateCreated` in this case)\n- `statusType` — new, read, test *(note that since the submission was just submitted, it will be set to either 'new' or 'test' status)*\n- `formHandle` — specified in the *Form Settings*\n- `formTitle` — specified in the *Form Settings*\n- `subject` — the subject generated using the **Form Subject** template specified in the *Form Settings*\n- `replyTo` — the email address extracted based on the **Notification ReplyTo** template specified in the *Form Settings*\n- `recipients` — one or more email addresses as specified in the **Notification Recipients** setting in the *Form Settings*. The values are returned as an *array*.\n- `fields` — the fields included in the form and submitted to PerForm. The values are returned as an *array* and you can use the field handles to retrieve the submitted values and labels.\n\nFor example, if your form looks like this:\n\n```twig\n{{ redirectInput('contact-form/thank-you?replyTo={replyTo}\u0026firstName={fields.firstName.value}') }}\n\n{# First Name field #}\n\u003cinput type=\"text\" id=\"fields_firstName\" name=\"fields[firstName][value]\"\u003e\n\n{# Last Name field #}\n\u003cinput type=\"text\" id=\"fields_lastName\" name=\"fields[lastName][value]\"\u003e\n\n{# Email field #}\n\u003cinput type=\"text\" id=\"fields_email\" name=\"fields[email][value]\"\u003e\n```\n\nOn your `contact-form/thank-you.html` template, you can access the parameters using the `craft.app.request.getQueryParam()`:\n\n```twig\n\u003cp\u003e\n\t{{ craft.app.request.getParam('firstName') }}, thank you for submitting your information.\n\t{% if craft.app.request.getParam('replyTo') is not empty %}\n\t\t\u003cbr\u003e\n\t\tWe will use the {{ craft.app.request.getParam('replyTo') }} email address to send you a reply.\n\t{% endif %}\n\u003c/p\u003e\n```\n\n### Using flash messages\n\nWhen a form submission is successfully processed, PerForm will set a flash message with the **submissionId** key and submission ID as value to indicate that the form submission was successful. In addition to checking whether the form was successfully processed, this also gives you the ability to retrieve the entire submission content using the  `getSubmissionById` plugin variable. The advantage of using this method as opposed to [sending the submissionId as a query parameter](#redirecting-after-submit) is two-fold. First, the actual submission ID cannot be easily extracted from the response, and second, it is no longer available when the page is refreshed (making this approach work very well when redirecting to the same template that includes the form itself).\n\nUsing the example from above:\n\n```twig\n{{ redirectInput('contact-form/thank-you') }}\n\n{# First Name field #}\n\u003cinput type=\"text\" id=\"fields_firstName\" name=\"fields[firstName][value]\"\u003e\n\n{# Last Name field #}\n\u003cinput type=\"text\" id=\"fields_lastName\" name=\"fields[lastName][value]\"\u003e\n\n{# Email field #}\n\u003cinput type=\"text\" id=\"fields_email\" name=\"fields[email][value]\"\u003e\n```\n\nOn your `contact-form/thank-you.html` template, you can retrieve the **submissionId** from the session flash and use it to fetch the entire submission content using the `getSubmissionById` plugin variable (here is the [list of all submission variables](#redirecting-after-submit)):\n\n```twig\n{% if craft.app.session.hasFlash('submissionId') %}\n\t{% set submissionId = craft.app.session.getFlash('submissionId') %}\n\t{% set submission = craft.perForm.getSubmissionById(submissionId) %}\n    \u003cp\u003e\n\t\t{{ submission.fields.firstName.value }}, thank you for submitting your information.\n        {% if submission.replyTo is not empty %}\n            \u003cbr\u003e\n            We will use the {{ submission.replyTo }} email address to send you a reply.\n        {% endif %}\n\t\u003c/p\u003e\n{% elseif craft.app.session.hasFlash('error') %}\n    \u003cp class=\"message error\"\u003e{{ craft.app.session.getFlash('error') }}\u003c/p\u003e\n{% endif %}\n```\n\nAs you can see in the example above, if a flash with the **submissionId** key has not been set, PerForm could not process the form submission and it is likely that an `error` flash message was set instead.\n\n## Example: Full-featured Complex Form\n\nContent is coming soon...\n\n## PerForm Widget\n\nYou can enable the PerForm **dashboard widget** to keep track of the various form submissions.\n\n![Screenshot](resources/screenshots/perform-dashboard-widget.png)\n\n\n\n## PerForm Utility\n\nThe handy PerForm **utility** allows you to easily delete all the *Test Submissions* from the Control Panel.\n\n![Screenshot](resources/screenshots/perform-utility.png)\n\n\n\n## Control Panel Permissions\n\nIf you are using Craft CMS \"Pro\" edition, PerForm provides permissions that can be used to grant access to users based on their User Group. These are set in *Settings → Users → User Groups*.\n\n#### Grant General Access to PerForm\n\n![Screenshot](resources/screenshots/perform-general-permissions.png)\n\nWith **General Access** to PerForm the users are able to:\n\n- List, view, delete and change the status of all form submissions\n- Add and utilize the _PerForm Dashboard Widget_\n\n#### Grant Utilities Access to PerForm\n\n![Screenshot](resources/screenshots/perform-utilities-permissions.png)\n\nWith **Utilities Access** to PerForm the users are able to:\n\n- Access the PerForm page under Craft CMS Utilities section and easily delete all test form submissions\n\n## PerForm Roadmap\n\nSome things to do, and ideas for potential features:\n\n* Process forms submitted through AJAX\n* Add preview of form submission fields in **Test Mode**\n* Add the option to send a *receipt* or *confirmation* after a form is submitted\n* [Submit your new feature request](https://github.com/ohlincik/craft-perform/issues) if you need PerForm to do something specific that would be useful for other Craft developers...\n\nBrought to you by [Perfectus Digital Solutions](https://perfectus.us)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fohlincik%2Fcraft-perform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fohlincik%2Fcraft-perform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fohlincik%2Fcraft-perform/lists"}