Skip to content

Instantly share code, notes, and snippets.

@michaelhue
Created September 14, 2021 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michaelhue/88ea58a18977e8be7e67d13a3b29b179 to your computer and use it in GitHub Desktop.
Save michaelhue/88ea58a18977e8be7e67d13a3b29b179 to your computer and use it in GitHub Desktop.
Formie "New submission" extension
/**
* Implement our custom "New submission" button into the Formie submission index.
*/
(function () {
const newSubmissionActionUrl = "/admin/actions/site/formie-draft/new";
const newSubmissionLabel = Craft.t("formie", "New submission");
Craft.Formie.SubmissionIndex.implement({
forms: null,
$newEntryBtn: null,
init(...args) {
this.on("selectSource", this.updateButton.bind(this));
this.on("selectSite", this.updateButton.bind(this)); // not needed yet
this.base(...args);
},
/**
* Find form sources.
*/
afterInit() {
this.forms = Array.from(this.$visibleSources)
.filter((el) => el.dataset.handle)
.map((el) => ({
handle: el.dataset.handle,
label: el.innerText,
}));
this.base();
},
/**
* Update the "New submission" button.
*/
updateButton() {
if (!this.$source) {
return;
}
if (this.$newEntryBtn) {
this.$newEntryBtn.remove();
}
const handle = this.$source.data("handle");
const selected = this.forms.find((form) => form.handle === handle);
if (selected) {
this.$newEntryBtn = $("<a>")
.attr({
href: `${newSubmissionActionUrl}?handle=${selected.handle}`,
class: "btn submit add icon",
role: "button",
tabindex: 0,
})
.text(newSubmissionLabel);
this.addButton(this.$newEntryBtn);
}
},
});
})();
<?php
namespace site\web\assets\formie;
use craft\web\AssetBundle;
use verbb\formie\web\assets\cp\CpAsset;
class FormieAsset extends AssetBundle
{
public function init()
{
parent::init();
$this->sourcePath = __DIR__;
$this->depends = [CpAsset::class];
$this->js = ['extension.js'];
}
}
<?php
namespace site\controllers;
use Craft;
use craft\web\Controller;
use DateTime;
use DateTimeZone;
use Exception;
use verbb\formie\elements\Form;
use verbb\formie\elements\Submission;
use verbb\formie\Formie;
use yii\web\BadRequestHttpException;
class FormieDraftController extends Controller
{
protected $allowAnonymous = self::ALLOW_ANONYMOUS_NEVER;
/**
* Handle of the status to apply to new drafts.
*/
protected $draftStatus = "draft";
/**
* Creates a new submission draft.
*/
public function actionNew()
{
$this->requireCpRequest();
$this->requirePermission('formie-editSubmissions');
$request = Craft::$app->getRequest();
$handle = $request->getQueryParam("handle");
/** @var Form */
$form = Form::find()->handle($handle)->one();
if (!$form) {
throw new BadRequestHttpException("No form exists with the handle \"$handle\"");
}
$submission = new Submission();
$submission->setForm($form);
$submission->siteId = Craft::$app->getSites()->getCurrentSite()->id;
// Apply a special "draft" status to distinguish from front-end submissions.
$submission->setStatus(
Formie::$plugin->getStatuses()->getStatusByHandle($this->draftStatus)
?? $form->getDefaultStatus()
);
// Save IP and user if needed, same as original submit action.
if ($form->settings->collectIp) {
$submission->ipAddress = Craft::$app->getRequest()->userIP;
}
if ($form->settings->collectUser) {
if ($user = Craft::$app->getUser()->getIdentity()) {
$submission->setUser($user);
}
}
// Our title will be the submission timestamp, since no fields have been
// filled out yet. We use the `afterSaveSubmission` event to update the
// title of every submission anyway, so this is fine for now.
if (!$submission->title) {
$now = new DateTime('now', new DateTimeZone(Craft::$app->getTimeZone()));
$submission->title = $now->format(DateTime::W3C);
}
$success = Craft::$app->getElements()->saveElement($submission, false);
if (!$success) {
throw new Exception(Craft::t("formie", "Couldn’t save submission."));
}
// We skip triggering events and integrations at this point, since they
// are not needed for our use case.
// Redirect to our fresh submission.
return $this->redirect($submission->getCpEditUrl());
}
}
<?php
namespace site;
use Craft;
use craft\events\TemplateEvent;
use craft\web\View;
use site\web\assets\formie\FormieAsset;
use verbb\formie\elements\Submission;
use yii\base\Event;
class Module extends \yii\base\Module
{
/**
* Initializes the module.
*/
public function init(): void
{
// Set a @modules alias pointed to the modules/ directory
Craft::setAlias('@site', __DIR__);
// Set the controllerNamespace based on whether this is a console or web request
if (Craft::$app->getRequest()->getIsConsoleRequest()) {
$this->controllerNamespace = 'site\\console\\controllers';
} else {
$this->controllerNamespace = 'site\\controllers';
}
parent::init();
$this->registerFormieExtensions();
}
/**
* Registers event listeners for the Formie plugin.
*/
protected function registerFormieExtensions(): void
{
// Update the title according to format whenever a submission was saved.
// We could add some checks here to only apply to drafts, but I want to
// keep the submission title in sync with data always.
Event::on(Submission::class, Submission::EVENT_AFTER_SAVE, function (Event $event) {
/** @var Submission */
$submission = $event->sender;
if (!$event->isNew) {
$form = $submission->getForm();
$submission->updateTitle($form);
}
});
// Attach our asset bundle if the Formie's control panel bundle is loaded.
// The bundle will extend Formie's element index script in order to add
// the "New submission" button.
Event::on(View::class, View::EVENT_BEFORE_RENDER_TEMPLATE, function (TemplateEvent $event) {
$formieCpBundle = "verbb\\formie\\web\\assets\\cp\\CpAsset";
$loadedBundles = array_keys($event->sender->assetBundles ?? []);
if (!in_array($formieCpBundle, $loadedBundles)) {
return;
}
$event->sender->registerAssetBundle(FormieAsset::class);
$event->sender->registerTranslations('formie', [
'New submission',
]);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment