How and where to write Webform submit hook? - drupal-7

I am new to Drupal(7) and hence need some help for following situations.
I have created one Webform(I have other webform too) and now instead of inserting in default webform_submitted_data table, I want for this webfrom to insert into myTable. From what I found, I need to write a hook for this. Actually I am getting confused for way to write this hook. I have below questions.
Where to write this hook (in which file).
How to write this hook only for one webform.
Please help and let me know if you need any more information for this.

First, be very sure before you start twisting Drupal's arm into making things work differently then they are supposed to. Rerouting the data for a Webform could potentially provide hiccups in how Webform works and this could bite you later. It may still expect the data to be saved in its own bookkeeping tables, but fail to find it there later if you overwrite its behavior.
That being said, if you wish to alter the behavior of other modules, such as Webform, you will have to write your own, tiny module. Some of these hooks can also be influenced via the templating layer (using your templates template.php file), but this is the wrong place to alter this kind of behavior in my opinion.
A Drupal 7 module is basically comprised out of a minimal of two files, a *.info file and a *.module file. The former contains some meta data necessary for Drupal to categorize your module and calculate possible dependencies. The latter contains the actual PHP code.
These files have to be saved in a directory with (preferably) the same name as you named your info and module file. For Drupal to find your module, you can place it under sites/all/modules.
If, for example, you name your module changemyform, these are the minimal required files:
changemyform.info
changemyform.module
And both should reside in: sites/all/modules/changemyform.
I suggest you check Drupal's developer's manual for a more detailed explanation about writing modules, including licensing, unit testing, ... . But for this example, the mentioned two files will do.
In your info file you have to at least write the name for the module, a small description, for which core version it is applicable and which dependencies it has. Something like this would suffice for our example:
name = Change my form
description = Changes the submission behavior of my form.
core = 7.x
dependencies[] = webform
Next we should write the logic for the module file itself. The general hook for intercepting any form submission (including a Webform) is:
function mymodule_form_alter( &$form, &$form_state,$form_id ){
...
}
With this hook you can, as the name suggests, alter all the forms rendered with Drupal. Not only the submission handler, but add/remove fields, add markup, ... . Replace mymodule with the actual name of your module, in our example case changemyform. Next you need to scope it down to only effect your desired form:
function changemyform_form_alter( &$form, &$form_state,$form_id ){
if ($form_id == 'my_desired_webform_form_id') {
$form['#submit'][] = 'changemyform_submit_handler';
}
}
Notice that I now replace mymodule with changemyform. As you can also see I have added a custom handler to the form's submit property. You will have to write this handler as a function which then will contain all the logic you desire. Thus the total module file now becomes (minus the <?php ?> tags):
function changemyform_form_alter( &$form, &$form_state,$form_id ){
if ($form_id == 'my_desired_webform_form_id') {
$form['#submit'][] = 'changemyform_submit_handler';
}
}
function changemyform_submit_handler($form, &$form_state) {
... your submission logic ...
}
Now you are ready to write all the logic you need to capture the data on submit and do as you please.
Since this is a module, you should, of course, enable it in your administration modules overview screen for it to function.
Also (as a nitpick) when writing your own modules, do decorate each function with documentation headers that describe what each function does and what each parameter could hold. Even for tiny, trivial functions.

You can use hook_webform_submission_insert().
function MYMODULE_webform_submission_insert($node, $submission) {
// Insert a record into a 3rd-party module table when a submission is added.
db_insert('mymodule_table')
->fields(array(
'nid' => $node->nid,
'sid' => $submission->sid,
'foo' => 'foo_data',
))
->execute();
}

In Drupal 8, the best way to refer to entity_insert hook -
Write this hook in MODULE_NAME.module file of your module folder
function MODULE_NAME_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
if($entity->getEntityTypeId() == 'webform_submission') {
$result = \Drupal::database()->insert('TABLE_NAME')
->fields([
'nid' => $node->nid,
'sid' => $submission->sid,
'foo' => 'foo_data'
])
->execute();
}
}

Related

Writing custom php script in joomla

I am good in php and javascript. I need a guidance where to place my custom php script if i make a ajax call from joomla.
I need to fetch the file names from a specific folder. I could make an ajax that will communicate with my php code and in return, my php code will give me the files of the specific folder.
Where is that php script to be place and how it should be routed.
For example
$.ajax({
url:'....../fetchfiles.php',
success:function(data){
}
})
Hope i was clear.
If I understood correctly your question my answer would be that you can place your fetchfiles.php wherever you want providing that you put right path to the file in your $.ajax call.
Then in the fetches.php you should call Joomla framework before you start your code, something like this:
// no direct access
define( '_JEXEC', 1 );
// get joomla root
$jpath_root = $_POST['jpath_root'];
//load Joomla
define( 'JPATH_BASE', $jpath_root );
require_once ( JPATH_BASE .'/includes/defines.php' );
require_once ( JPATH_BASE .'/includes/framework.php' );
jimport( 'joomla.application.application' );
jimport( 'joomla.filter.filteroutput' );
$japp = JFactory::getApplication('site');
/* now you are good to go with your code */
Just a few words about
// get joomla root
$jpath_root = $_POST['jpath_root'];
part.
My experience is that it is the best if you pass JPATH_ROOT variable via $.ajax( one way would be to have hidden input with value set to 'JPATH_ROOT' in your joomla file from where you call ajax and then in your ajax.js just pick that value up and pass to the ajax.php or fetches.php in your case)
You can define JPATH_BASE another way too, eg
define( 'JPATH_BASE', realpath(dirname(__FILE__)));
or
define( 'JPATH_BASE', realpath(dirname(__FILE__).'/../../..' ));
depending on your actual files structure but be aware that this way you practically redefined original JPATH_BASE constant and that it may cause conflicts in case you call, later within your code, some third party component functions/classes which may depend on original Joomla defined constants ...
I've learned it hard way with ZOO Component and my ajax calls ... :)
Hope this makes some sense.
Based on where you want to show the results you can create a component or a module (you can also easily include the module in an article to print the result in component position).
In the case of module:
https://docs.joomla.org/J3.x:Creating_a_simple_module/Developing_a_Basic_Module
Obviously your ajax call will be in tmpl/default.php and your php script in the module folder, simply you have to include it in the file list in mod_helloworld.xml. Then zip your folder and install it using Module Manager.
In case of problem with your jquery code, you have to use the noConflict() mode.

Trigger Rule with Filefield 'when field has changed' in Drupal 7

I've got a set of different rules that check if various field types have been updated 'after updating existing content'. The problem is that each one works fine, except for the only filefield type which will not work. The condition used is a 'NOT Data comparison' on the field checking the 'node-unchanged' version against the new 'node' version. This works with every other type of field and actions appropriately (that I have used at least), just not the filefield type; the rule just fires regardless of changes or not on the filefield.
I also found this post about a very similar problem: https://www.drupal.org/node/1011014#comment-10040082
I think I have the makings of a workaround, but I just wanted to check this with some fellow developers first as my PHP isn't the best.
If I were to enable the PHP module then add a condition in rules that checks the 'source' attribute to see if a new file has been added... would this work? The code I have is:
if (isset($object->field_FILEFIELD_NAME[0]['source'])) { //Check for new files }
I believe that $object is the node passed on by rule function as argument.
Is this a good idea/best approach? Any ideas or workarounds would be great.
Yes, you can use PHP for filefield checks as it is more clear (but always less secure). Here are my suggestions:
Create a Rule component (of type "Condition set (AND)") instead of a rule to do this check.
Use a parameter with your rule component (of type Node) so you have the $node available.
Use php to do the checks. Get the file data from the filefield using $node.
Create a normal rule (eg with event Before Saving Content) where you will use this component as a condition among others.
As you said you need to check for differences not for empty values, right? So instead of using isset you need to see if there is a different fid (which normally changes when there is a different file). Other methods are available and if you want to see which data can change do a dpm() to the filefield using devel module.
In order to get the unchanged value of the filefield use the same component on you main Rule but with the $unchanged node as parameter.

Drupal: D7 rewriting values returned by views

I have a requirement to perform an indexed search across content which must include a couple of tags in the result. The tags must be a random selection. The platform is Drupal 7.12
I have created a view that manages the results of a SOLR search through the search_api. The view returns the required content and seems to work as intended. I have included a couple of Global: custom text fields as placeholders for the tag entries.
I am now looking for a solution to manage the requirement to randomise the tag values. The randomisation is not the issue, the issue is how to include the random values into the view result.
My current approach is to write a views_pre_render hook to intercept the placeholders which appear as fields ([nothing] and [nothing_1]). The test code looks like the following
function MODULE_views_pre_render( &$view )
{
$view_display = $view->display['default'];
$display_option = $view_display->display_options;
$fields = $display_option['fields'];
foreach( $view->result as $result )
{
$fields['nothing']['alter']['text'] = sprintf("test %d", rand(1,9));
}
}
I am currently not seeing any change in the placeholder when the view is rendered.
Any pointers to approach, alternate solutions etc would be gratefully received as this is consuming a lot of scarce time at the moment. Calling print_r( $view ) from within the hook dumps over 46M into a log file for a result set of 2 items.
There are two possible solutions for your task.
First approach is do everything on the template level. Define a template for the view field you want to randomize. In advanced settings of your display go to Theme: Information. Make sure that the proper theme is selected and find the template suggestions for your field. They are listed starting from most general to the most specific and you can choose whatever suits you better.
I guess the most specific template suggestion for your field would be something like this: views-view-field--[YOR VIEW NAME]--[YOUR DISPLAY NAME]--nothing.tpl.php. Create the file with that name in the theme templates directory and in this template you can render what ever you want.
By default this template has only one line:
print $output;
you can change this to:
print sprintf("test %d", rand(1,9));
or to anything else, whatsoever :)
Second approach is to go with Views PHP module. WIth this module you can add a custom PHP field in which you can do whatever you want. Even though the module hasn't been released it seems to work quite well for the most of the tasks and most certainly for such a simple task as randomizing numbers it will work out for sure.
I stumbled upon this while searching for another issue and thought I would contribute.
Instead of adding another module or modifying a template, just add a views "sort criteria" of "Global: Random".

ExtJS MVC, dynamic loading and i18n

I would like to translate my ExtJS application in different languages. My issue is that I'm using ExtJS MVC framework, and most of my JS files are downloaded dynamically by the framework itself.
The ideal solution (that I thought of) would be to have an extra option in the Ext.Loader (or in my Ext.app.Application) that would define the language to use, and depending on this to automatically download such file as "a.MyClass.fr.js" after loading my "a.MyClass.js" (which would contain an Ext.apply, overriding my string resources). That's probably not available in the ExtJS framework at the moment.
The alternative solution I can see, is to perform a trick on the server-side. First, a cookie would be created on the client, to set to the language. On the server-side, I could catch all the requests to JS files, then if a cookie is set (='fr' for example), I'd combine the requested JS file (MyClass.js) with its i18n's friend (MyClass.fr.js) dynamically on the server and return the result. That would work, but it's really tricky because it implies other things (caching...).
Maybe the best way is to implement the first behavior I described in the ExtJS framework myself...
What do you think? I'm looking for a really clean and neat way of doing it! Thanks :)
I recently struggled with the same problem.
Finding a clean way to do this was quite a challenge - most alternatives were either..
1) Duplicate your code base per locale (WTH)
2) Download localized files overriding each of your components (Maintenance hell? What about the poor translators?)
3) Use/generate a static file containing translations and refer to it (All languages are downloaded? Extra build step to generate it? How do you keep them in synch?)
I tried to get the best of all worlds and ended up with a utility class responsible for:
1) Loading the ExtJS translation files (which basically apply overrides to extjs base components)
2) Loading a locale specific property resourcebundle (specifying which locale to load) from the server.
3) Prototyping String with a translate() method which queries the loaded store (containing the message bundle from the server) and returns the translation based on the value of the string.
This is the gist of things:
Bundle & prototyping:
localeStore.load({
callback : function(records, operation, success) {
// Define translation function (NB! Must be defined before any components which want to use it.)
function translate() {
var record = localeStore.getById(this.valueOf()) ;
if(record === null) {
alert('Missing translation for: ' + this.valueOf()); // Key is not found in the corresponding messages_<locale>.properties file.
return this.valueOf(); // Return key name as placeholder
} else {
var value = record.get('value');
}
return value;
}
String.prototype.translate = translate;
callback.call(); // call back to caller(app.js / Ext.Application), loading rest of application
}
});
As an example from a view:
this.copyButton = Ext.create('Ext.button.Button', {
disabled: true,
text: 'DOCUMENT_LIBRARY_MENU_COPYTO_BUTTON'.translate(),
action: 'openCopyDialog'
});
Bundle on the server (mesages_en.properties):
DOCUMENT_LIBRARY_MENU_COPYTO_BUTTON=Copy file
etc..
Pros:
No-fuss code, 'Your_key'.translate() makes it easy to read and aware that this is a localized string
None/little maintenance overhead (Keeping an override file for each locale? Jesus..)
You only load the locale you need - not the whole shabang.
If you really want to, you could even have your own translation for the ExtJS locale files in the same bundle.
You could write unit tests to ensure that all bundles contain the same keys, thus avoiding orphaned translations later
Cons:
Synchronous - the store must be loaded before your main app starts. I solved this by adding a callback from the utility class which was called once all texts were loaded.
No real-time population of texts.. though I didn't want to make my users overload the server either :P
So far my approach has worked out pretty well for my requirements.
Site load isn't noticeably slower and the bundles (containing ~200 keys/values per bundle) measure out at ~10kb during load.
There is currently no solution so I decided to create my own hack/addon on the Ext.Loader. I uploaded the code on GitHub: https://github.com/TigrouMeow/extjs-locale-loader. It's exactly what I needed and I really hope it will help others as well!
You should first complete your development phase and build your project or use ext-all.js file to I18s translate your UI
see: http://docs.sencha.com/ext-js/4-0/#!/example/locale/multi-lang.html
The appropriate language modifier script (/ext/local/ext-lang-xxx.js) needs to be loaded after ext is loaded (including dynamically loaded classes). In the example above, I would have probably used Ext.Loader.loadScriptFile but they eval a downloaded one directly. The only other thing is that your classes need to be built in different languages or you just use variables and reference the lang-specific variable file.
you could also use a variable in the Loader paths:
var lang='fr';
Loader
{
paths:
{
'Ext': '.',
'My': './src/my_own_folder'+'/'+lang
}

CakePHP routing syntax

How do I do a simple route in CakePHP?
I need that each and every URL will be routed by swapping the action and the controller.
I just couldn't understand the placeholders syntax.
Example:
/files/read/3
to
/read/files/3
-- supplemental --
In my application I use aliases for the controllers.
and I want to route every url that have a certain keyword, as an action, to a certain controller.
I also want to provide the original controller name as a parameter.
Here is a 1:1 example:
There are to alises: fruits and streets.
The keyword that I want to catch in the action is find.
The new controller name is finder.
The following calls match my condition:
/fruits/find/apple/red and /streets/find/longer
The router should catch these urls and convert them, to:
/finder/fruits/apple/red(or supply the parameters in other way, I don't mind) and /finder/streets/longer
How should it be done?
Here is the line of code that you need to put in /app/config/routes.php:
Router::connect('/:action/:controller/*', array('controller' => ':controller', 'action' => ':action'));
Know more: As you can see from the CakePHP book, there are some 'reserved' patterns for routing configuration. An example would be what I used in the line above: :action and :controller. These patterns allow you to tweak routes extensively.
Beware: changing the order of controller and actions in urls might have unintended consequences in the functionality of other CakePHP features. I haven't tested thoroughly, but this is just a general warning.
Beware: Also, I noticed that you put in your example: /files/read/3. Maybe this was just some dummy example, but if you indeed plan to have an MVC named as 'file', be advised that it will conflict will CakePHP core classes (e.g. File model will conflict with File class).
Anyway, hope this answer helps you well. And I really like how the change of controller and action names make the url more readable. :D

Resources