I've create an installable script which creates forms on the fly for 2 to 3 party verification. Since these forms are filled out asynchronously, and often in batches, it is important to determine which form called the function.
First the response form is created, then a trigger is installed.
CreateForm function:
function createForm(title){
var form = FormApp.create(title)
//set parameters for new form
form.setCollectEmail(true);
form.setShowLinkToRespondAgain(false);
...
return form;
};
InstallTrigger function:
function InstallTrigger(form, funct) {
var trigger= ScriptApp.newTrigger(funct)
.forForm(form)
.onFormSubmit()
.create()
return trigger.getUniqueId();
};
During the initial form submit, a function called createMessage() creates an email based on responses and runs the two scripts above. The email contains a link to the newly created form to await approval. To that point, everything works fine.
The problem comes when the newly installed trigger fires. I haven't found a way to set the trigger source as a variable. I'm sure that it has to do with an event, but the documentation on events doesn't go too far into forms from what I can see. I read that it can be done (Understanding Events). I just don't quite understand where (e) is defined on an installable script. Would it be in the install function or some other place?
Thanks again Serge inas for the walkthrough.
(e) is defined when the function defined. In my example above, the InstallTrigger() function adds a trigger to the form passed to it. The script that executes on submission is also passed to it. So, the installation of the trigger comes from a pre-installed onSubmit() trigger. In the submission function, it calls
var NewForm = FormApp.createForm();
InstallTrigger(NewForm, triggerFunction);
The function "triggerFunction" should look something like this:
function triggerFunction (e) {
Logger.log(JSON.stringify(e))
GmailApp.sendEmail("yourEmailString","Debugging",Logger.getLog() //sends an email to you with the logfile.
var form = FormApp.openByUrl(e.response.getEditResponseUrl());
...
}
The e.source mentioned in the aforementioned article says that it works for installable triggers, but when enumerated, only the e.resource and e.authMode were available. Fortunately, the e.response contains the method editUrl that can be used with the method .openByUrl() and return the original form.
Related
I am trying to reset a form so that it appears to Drupal 8 that it hasn't been submitted. So far I have been unable to do this, as I cannot find any available methods (setSubmitted() hardcodes it to TRUE without a FALSE option). The reason is this isn't a full submit, but a submit of one field after which I would like the user to be redirected to another page that has another form, and I would like this secondary form to use the value obtained in the first step.
In the submit handler for the first part I use this to redirect:
$form_state->setRedirect('my.route', [], []);
And this works, but when the form reaches the second form (it seems) that the second form thinks it is a submission. As a result any submit buttons I add to the second form seem to make it auto-submit, and this breaks my user journey.
In the submit for the first part I have tried:
$form_state->setRebuild(TRUE);
$form_state = new FormState();
unset($form_state);
Tried the above in various configurations to no avail. They all prevent/ignore the setRedirect call that I make afterwards. The reason I want/need to do it this way is I want to preserve the POST method used.
Do you want to obtain something similar to what core search module does? It has simple SearchBlockForm that sends data to more complex SearchPageForm.
SearchBlockForm uses GET method (though you may use POST):
$form['#method'] = 'get';
and has no id and token fields:
function search_form_search_block_form_alter(&$form, FormStateInterface $form_state) {
$form['form_build_id']['#access'] = FALSE;
$form['form_token']['#access'] = FALSE;
$form['form_id']['#access'] = FALSE;
}
BTW, the last change allows you to avoid running submit callbacks.
Hope this helps.
I am using a script in a Google Spreadsheet to, upon form submission, copy an existing spreadsheet and google form to create a new spreadsheet and new form and then connect the new form to the new spreadsheet so the new spreadsheet is receiving the responses from the new form.
The script in the copied spreadsheet is copied to the new spreadsheet, but the installed triggers don't exist. Is there a way to create those triggers from the original spreadsheet's script (the spreadsheet that received the form submission that created the new SS and form) or do I need to rely upon non-installed triggers in the new spreadsheet to create the installed trigger?
Triggers run scripts which require authorization by the user. Because your script is bound to a spreadsheet, it will need to be authorized on each copy.
I have a similar system (copies of a master sheet + code) and we addressed this by adding a custom menu to run a script when someone makes a copy. I added a custom menu and a setup script which would authorize the trigger.
function onOpen(e) {
var ui = SpreadsheetApp.getUi().createMenu("PGP Setup").addItem("Run", "setup").addToUi();
}
function setup() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('makeDocs')
.timeBased()
.everyHours(1)
.create();
}
It's easy on the user who created the sheet and has been reliable for us so far.
I was able to solve my issue with the code below. The function 'newSSTrigger' is in the original script (not a copied one) and is called after the new SS and Form are created and sync'd - this is where the the variable idOfNewSS comes from. The trigger will not create a script in the new objects or even be seen in the new object's scripts as an installed trigger. To find the trigger go to Edit>All Your Triggers from any script. Triggers not greyed out are attached to the document in some way.
This seems to have two benefits:
1) I never have to deal with any permissions - the triggers work without me touching the new docs at all.
2) If I update the 'myFunction' (below) it changes how the existing triggers work because the trigger retrieves its instructions from the original script - in its current state. This means I can update all existing triggers created by this script just by editing this function.
function newSSTrigger(idOfNewSS) {
var newSS = SpreadsheetApp.openById(idOfNewSS);
ScriptApp.newTrigger("myFunction")
.forSpreadsheet(newSS)
.onFormSubmit()
.create();
}
function myFunction() {
do stuff...
}
A different option that would work for people who are not just copying a sheet programmatically (or who are anticipating users making copies that the developer doesn't have permission to edit) is to take care of it for the user inside the bound script. You could use code similar to this:
function onOpen(){
var triggers = ScriptApp.getProjectTriggers();
if(triggers.length == 0){
ScriptApp.newTrigger('yourFunction')
.timeBased()
.everyHours(1)
.create();
}
This says that if there are no triggers, add a time-based trigger. You can see this documentation for variations.
I want to create an "extension" for a Jacada Interaction (to extend functionality), in my case to parse and assign the numerical part of serialNumber (a letter, followed by digits) to a numeric global ("system") variable, say serialNumeric. What I am lacking is the structure and syntax to make this work, including the way to reference interaction variables from within the extension.
Here is my failed attempt, with lines commented out to make it innocuous after failing; I think I removed "return page;" after crashing, whereupon it still crashed:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
// Place your extension code here
//$('[data-refname="snum"]').val('serialNumber');
// snum = Number(substring(serialNumber,1))
});
});
Here is an example of one that works:
/**
* Description: Add swiping gestures to navigate the next/previous pages
*/
initExtensions("swipe", function(app) {
// Swipe gestures (mobile only)
app.registerExtension('pageRenderer', function(ctx, page) {
page.swipe(function(evt) {
(evt.swipestart.coords[0] - evt.swipestop.coords[0] > 0)
? app.nextButton.trigger('click')
: app.backButton.trigger('click')
});
return page;
});
});
After reading the comment below, I tried the following, unsuccessfully (the modified question variable is not written back to that variable). It rendered poorly in the comment section, so I am putting it here:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
var sernum = new String($('[data-refname="enter"] input'));
var snumeric = new String(sernum.substr(1));
$('[data-refname="enter"] input').val(snumeric);
});
});
I would like to understand when this code will run: it seems logical that it would run when the variable is assigned. Thanks for any insight ~
In your case, you extend loaded event. You don't have to return the page from the extension like in your working example below.
The page argument contains the DOM of the page you have just loaded, the ctx argument contains the data of the page in JSON form. You can inspect the content of both arguments in the browser's inspection tools. I like Chrome. Press F12 on Windows or Shift+Ctrl+I on Mac.
The selector $('[data-refname="snum"] input') will get you the input field from the question with the name snum that you defined in the designer. You can then place the value in the input field with the value from the serialNumber variable.
$('[data-refname="snum"] input').val(serialNumber);
You can also read values in the same way.
You can't (at this point) access interaction variables in the extension, unless you place theses variables inside question fields.
Here is a simple example how to put your own value programmatically into a input field and cause it to read it into the model, so upon next it will be sent to the server. You are welcome to try more sophisticated selectors to accommodate for your own form.
initExtensions("sample", function(app){
app.registerExtension("loaded", function(ctx, page) {
// simple selector
var i = $('input');
// set new value
i.val('some new value');
// cause trigger so we can read into our model
i.trigger('change');
});
});
I am using jQuery 1.9.1.
Suppose i have a button with id="clickMe"
My jQuery code is:
$('#clickMe').click(function(event)
{
eventHandler1();//do something
eventHandler2();//use output from eventHandler1() and do something
}
Now, i want "eventHandler2" to be executed at last so that i could use the output of "eventHandler1". Is there any way to do this manually and not just the way i have put the handlers inside the click event?
One more thing, "eventHandler1()" and "eventHandler2()" are present in different .js files and thus the requirement.
jQuery.when() provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events.
For example, when the Deferreds are jQuery.ajax() requests, the arguments will be the jqXHR objects for the requests, in the order they were given in the argument list.
$.when(eventHandler1).then(eventHandler2).done(function(){
alert('done.');
});
So can even use GLOBAL variable to store eventHandler1 output and access that inside eventHandler2
Example
var someVar;
function eventHandler1()
{
// process
someVar = some value from process
return someVar;
}
function eventHandler2()
{
alert(someVar);
}
Response to OP comment
as you have asked about execute handler in queue you can use Jai answer.
you can use .when .then and .done as below.
$.when(eventHandler1).then(eventHandler2).done(function(){
//process code
});
I need to know how to customize my contact and register forms. How to add new fileds ( and ) and make the information from these fields required or not required.
I need to know which files I must edit for these forms...
I use prestashop 1.4.7.0
This is really two separate questions as there are major differences in how you would handle each case.
Answer 1
For the registration form you can write a module which contains two hook handler functions. These will be:
public function hookCreateAccountForm() {}
public function hookCreateAccount($params) {}
The first function allows you to add additional fields to the registration form (by default these are inserted at the end of the form authentication.tpl, although you could move them all as a single group elsewhere). It should simply return the additional form html you require.
The second function provides you with two parameters to handle the account creation process. This is executed after the standard fields have been validated and the new customer has been created. Unfortunately you cannot do validation on your additional fields using this (you would need to either use javascript or override AuthController to perform your own authentication in the preProcess() member function). In one of my own custom modules for a site I have the following, for example:
public function hookCreateAccount($params)
{
$id_lang = (int)Configuration::get('PS_LANG_DEFAULT');
$customer = $params['newCustomer'];
$address = new Address(Address::getFirstCustomerAddressId((int)$customer->id));
$membership_number = $params['_POST']['membership_number'];
....
....
}
$params['newCustomer'] is a standard Prestashop element in the array and contains the newly created customer object. Your fields will be in the $params['_POST'] array - in my case it was an input field called membership_number.
Answer 2
For the contact form it's a whole lot more complicated I'm afraid. The simplest method for the html is to just hard-code your additional fields in the template file contact-form.tpl.
To actually process the form you will need to create an override for the controller by ceating a file called ContactController.php in /<web-root>/<your-optional-ps-folder>/override/controller containing something like:
<?php
class ContactController extends ContactControllerCore {
function preProcess()
{
if (Tools::isSubmit('submitMessage'))
{
// The form has been submitted so your field validation code goes in here.
// Get the entered values for your fields using Tools::getValue('<field-name>')
// Flag errors by adding a message to $this->errors e.g.
$this->errors[] = Tools::displayError('I haven't even bothered to check!');
}
parent::preProcess();
if (Tools::isSubmit('submitMessage') && is_empty($this->errors))
{
// Success so now perform any addition required actions
// Note that the only indication of success is that $this->errors is empty
}
}
}
Another method would be to just copy the entire preProcess function from controllers\ContactController and just hack away at it until it does what you want....