Creating new event in a module - drupal

I'm trying to execute some code right before content is deleted. The Rules module has events for
After updating existing content
Before saving content
After saving new content
After deleting content
However, none of these execute my code at the right time.
I discovered a simple module called Predelete, which provides hooks for executing code before deletion. This seemed like an excellent candidate to call the Rules event from.
So, I created a very simple module based on the "predelete_field" example module contained within Predelete. The folder is called "predelete_field", is in the "modules" folder, and contains the following files:
1: predelete_field.info
core = "7.x"
dependencies[] = "rules"
dependencies[] = "list"
dependencies[] = "predelete"
description = "Example for the predelete module with a content type and a node"
name = "Predelete Field"
package = Other
project = "predelete_field"
version = "7.x-1.0"
; Information added by drupal.org packaging script on 2011-07-11
version = "7.x-1.1"
core = "7.x"
project = "predelete"
datestamp = "1310360219"
2: predelete_field.module
<?php
/**
* Implements hook_predelete_node().
*/
function predelete_field_predelete_node($node) {
drupal_set_message( "PREDELETE HOOK CALLED", 'warning' );
rules_invoke_event('predelete_field', $node);
$deletable = TRUE;
$reason = t('Deletable by default.');
return array('result' => $deletable, 'reason' => $reason);
}
3: predelete_field.rules.inc
<?php
/**
* Implements hook_rules_event_info() on behalf of the predelete_field module.
*/
function rules_predelete_field_event_info() {
$items = array(
'predelete_field_predelete' => array(
'label' => t('Before deleting content'),
'group' => t('Node'),
),
);
return $items;
}
Unfortunately, this does not appear to work: The event does not show up in the event list, even after clearing cache and disabling and re-enabling the module. Likewise, the drupal_set_message function does not appear to fire.
Is anyone able to spot any errors I may have made, or provide a solution?

The Predelete module just seems to hook into the confirmation form for multiple node deletions, it won't ever be fired using Rules as Rules doesn't invoke the form but uses the node API instead.
If you look at the node_delete_multiple() function though you'll see that several hooks are called before any content is actually deleted from the database (namely hook_node_delete and hook_entity_delete). One of these is the hook you'll want to implement in your custom module, like so:
function mymodule_node_delete($node) {
// Perform some action based on values in $the node object.
// Nothing has been deleted from the database at this point.
}
You'll need to clear Drupal's caches again when you've added that hook but according to the documentation it should work.

Related

Drupal - Importing a taxonomy with migrate module from a table and creating/updating existing terms

I need to import a list of terms into my taxonomy from a source I loaded in the database.
The problem is I allready have this taxonomy on my site (loaded wihtout migrate) with terms that are used by reference in other content, so I got to keep existing term and update them or create the new ones.
To link my taxonomy source and the existing taxonomy I have an unique code for each term, so I added a code field to my vocabulary and filled it for each existing term.
I am currently able to create and update terms with my current Migration class, but if the name of my term on the site and the name of the term in my source is different, the import will create a new term instead of updating its name even if the code is the same.
Here my Migration Class :
class TotoMigration extends Migration {
private $list_term = array();
public function __construct($arguments) {
parent::__construct();
$this->softDependencies = array('TotoParent');
// get data from the custom table containing the new terms to create or update
$query = db_select('toto', 'f')
->fields('f', array(
'CODE', // code
'LIBLONG', // name
'PARENT', // parent
)
);
$this->source = new MigrateSourceSQL($query);
$this->destination = new MigrateDestinationTerm('toto_tax');
$this->map = new MigrateSQLMap($this->machineName,
array(
'CODE' => array('type' => 'varchar',
'length' => 5,
'not null' => TRUE,
'description' => 'Code',
)
),
MigrateDestinationTerm::getKeySchema()
);
$this->addFieldMapping('name', 'LIBLONG');
$this->addFieldMapping('field_code', 'CODE');
$this->addFieldMapping('parent', 'PARENT')
->arguments(array('source_type' => 'tid'))
->sourceMigration('TotoParent');
// create a list of existing toto terms with code => tid
$list_term = db_query("select fc.field_code_value, ttd.tid
from taxonomy_term_data ttd
left join taxonomy_term_hierarchy tth on tth.tid=ttd.tid
left join field_data_field_code fc on fc.entity_id = ttd.tid
where ttd.vid=10
and tth.parent!=0;")->fetchAllKeyed();
}
public function prepareRow($row) {
// Always include this fragment at the beginning of every prepareRow()
// implementation, so parent classes can ignore rows.
if (parent::prepareRow($row) === FALSE) {
return FALSE;
}
// if the destination is not mapped in migrate we tell him where to go
if (!isset($row->migrate_map_destid1) && isset($list_term[$row->CODE])) {
$row->migrate_map_destid1 = $list_term[$row->CODE];
}
}
}
I then load the import with drush (and --update option).
I must be missing something, if anyone got a clue it will be welcome.
After many tries, the problem reside in the fact the module Migrate does not support Creating content and Updating content in the same migration class (I even read it will sometime claim to update content and just do nothing).
So the solution is pretty simple, create 2 classes :
One for Creating content
One for Updating content
Your Creating class will be the same.
Your Updating class will need to have a systemeOfRecord set to DESTINATION :
$this->systemOfRecord = Migration::DESTINATION;
So it knows to only update and not recreate the content, it will keep current fields not mapped and update fields mapped that are not part of the MigrateSQLMap :
$this->map = new MigrateSQLMap($this->machineName,array(...));
The tricky part will be to find corresponding nid/tid of your content so you can map it to your imported data and then to separate data used to update or create content.

Search with custom condition

How can I add a condition to default search module?
I want to add a checkbox field called "Allow to search" to nodes, and unchecked items will not show in search results.
Extending Drupal 7 search seems to be my solution, but I can't make it work; hook_search_execute() is not executed.
Can you explain why this happens?
You need to first select your module on admin/config/search/settings, and possibly unselect the Node module in the "Active search modules." If your module is not selected there, your hook will not invoked.
As for the reason why one hook is invoked, and one is not invoke, the code executed by search_get_info() (the function called from search_menu() to build the search menu) first invokes every implementation of hook_search_info(), and then it checks for which modules the search integration has been enabled. Since your module doesn't have the search integration enabled, hook_search_execute() for your module will never be invoked.
if (!isset($search_hooks)) {
foreach (module_implements('search_info') as $module) {
$search_hooks[$module] = call_user_func($module . '_search_info');
// Use module name as the default value.
$search_hooks[$module] += array(
'title' => $module,
'path' => $module,
);
// Include the module name itself in the array.
$search_hooks[$module]['module'] = $module;
}
}
if ($all) {
return $search_hooks;
}
$active = variable_get('search_active_modules', array('node', 'user'));
return array_intersect_key($search_hooks, array_flip($active));

How to use Silverstripe 3 beta UploadField

I am trying to use a UploadField on frontend for user to upload their company logo.
There isn't much documentation on UploadField yet. And I have tried it but no luck so far.
Can anyone guide me on how to use it?
This is a little old, but if anyone else stumbles upon this like I did.
UploadField does work frontend. I haven't been able to save into a many_many relationship using the saveInto function. But the biggest thing I missed was the DataObject/Page needs to exist first, as in it needs to be saved before you can attach a related object like an image.
static $has_one = array(
"Photo" => "Image"
);
$fields = new FieldList(
new UploadField( 'Photo', 'Upload' )
);
function saveForm( $data, $form ) {
$object = new DataObject();
// for a new object write before saveinto
$object->write();
$form->saveInto($object);
$object->write();
Director::redirectBack();
}
using ss 3.0.1
Alternatively rather than using the saveinto function you can manually loop over the parameters and attach them on the object yourself for many_many images.
The upload field checks for permissions via the can*() methods in the object.
In order to allow front end editing - you may have to overload File::canEdit (or Image::canEdit) in your custom object to handle this.

Bypass Node Delete Confirm Form in Drupal

Looking for the best way to allow users to delete nodes on a site without the need to use a confirm form. I have tried using a form_alter to direct people to a custom submit function, without success.
Anyone ever tried this?
Assuming drupal 7, the node/%/delete menu entry is wired directly to the node_delete_confirm form. You can modify it with with a hook_menu_alter, and change the function from drupal_get_form to a page callback of your own design that will just delete the node.
Example:
In your module file you'd need:
function mymodule_menu_alter(&$items) {
$items['node/%node/delete']['page callback'] = 'my_node_delete_function';
$items['node/%node/delete']['page arguments'] = array(1);
$items['node/%node/delete']['module'] = 'mymodule';
$items['node/%node/delete']['file'] = 'mymodule.pages.inc';
}
And in your mymodule.pages.inc file you'd need:
function my_node_delete_function($node) {
// Taken from node modules node_delete_confirm submit handler
node_delete($node->nid);
watchdog('content', '#type: deleted %title.', array('#type' => $node->type, '%title' => $node->title));
drupal_set_message(t('#type %title has been deleted.', array('#type' => node_type_get_name($node), '%title' => $node->title)));
// Do a drupal goto here to preserver the 'destination' parameter
drupal_goto();
}

Need advice to get my logic correct in d7

Goal: Is to save information of a node which gets updated. We need to gather the node id of the node which is updated and also the user names of people who have bookmarked it.
Implementation:
I have managed to get the both this detail using flags and rules module. I made a custom module which implemented the hook to get this info.
I am getting stuck here:
Now I need to save the user name and the node id. I am still deciding if I want to use fields or the db layer.
One username can have multiple node id saved.
Now the problem is I don't know for sure how many nodes will be enough. It depends on the user. It can be 5 can be 500 or even 5000 node ids that might need to be saved for one user.
So how do I make provision for this ?
So I am stuck with the logic. How should I use the db layer or the fields in custom content type to save this ? and how should I do it ?
Please advice. I am using d7.
custom module code
/*
* Implementation of the hook_rules_action_info()
*
*/
function customvishal_rules_action_info()
{
$actions = array(
'customvishal_action_userdetail' => array(
'label' =>t('Custom function to send notifications'),
'group'=>t('Cusotm Code for sending notifications'),
'parameter'=> array(
'account'=> array(
'type'=>'user',
'label'=>t('Going to get user list'),
),
// for the node
'productdetail'=> array(
'type'=>'node',
'label'=>t('Passding the node data'),
),
)
),
);
return $actions;
}
/*
* The action function for the rules exampled hello world
*
*/
function customvishal_action_userdetail($account,$productdetail)
{
drupal_set_message(t('This user #username! has flagged it',
array('#username' => $account->mail)));
drupal_set_message(t('This node #nid has got updated',
array('#nid' => $productdetail->nid)));
// The above takes care of the node and the user information later I will put
// it in a db or something like that.
// end of the function customvishal_action_userdetail
}
It really seems like you should be using hook_node_update() and hook_node_insert() to for access to nodes that have just been added or updated.
If you wanted access to the node data just before it was saved, then hook_node_presave() would be the one to use.
I don't think you need presave though because you mentioned you needed the node ID, and presave does not have that for new nodes yet.
Here's a way to process new and updated nodes. The first 2 functions just hook into the right place and route the node to the 3rd function.
<?php
// hook into node inserts
function customvishal_node_insert($node) {
if ($node->type == 'mynodetype') {
customvishal_handle_data($node);
}
}
// hook into node updates
function customvishal_node_update($node) {
if ($node->type == 'mynodetype') {
customvishal_handle_data($node);
}
}
// custom handler for the nodes
function customvishal_handle_data($node) {
// load a user object of the node's author
$author = user_load($node->uid);
// now do what we need to do with $node and $user data
}
Remember you need to clear the Drupal cache for new hooks in your module to work in D7.

Resources