Need advice to get my logic correct in d7 - drupal

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.

Related

Modifying a field collection programmatically missing hostEntity fields

I am trying to modify a field collection in a node that already exists so I can change an image on the first element in an array of 3. The problem is, the hostEntity info is not set when I do a entity_load or entity_load_single so when I do a:
$field_collection_item->save(true); // with or without the true
// OR
$fc_wrapper->save(true); // with or without the true
I get the following error:
Exception: Unable to save a field collection item without a valid reference to a host entity. in FieldCollectionItemEntity->save()
When i print_r the field collection entity the hostEntity:protected fields are indeed empty. My field collection is setup as follows:
field_home_experts
Expert Image <--- Want to change this data only and keep the rest below
field_expert_image
Image
Expert Name
field_expert_name
Text
Expert Title
field_expert_title
Text
Here is the code I am trying to use to modify the existing nodes field collection:
$node = getNode(1352); // Get the node I want to modify
// There can be up to 3 experts, and I want to modify the image of the first expert
$updateItem = $node->field_home_experts[LANGUAGE_NONE][0];
if ($updateItem) { // Updating
// Grab the field collection that currently exists in the 0 spot
$fc_item = reset(entity_load('field_collection_item', array($updateItem)));
// Wrap the field collection entity in the field API wrapper
$fc_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);
// Set the new image in place of the current
$fc_wrapper->field_expert_image->set((array)file_load(4316));
// Save the field collection
$fc_wrapper->save(true);
// Save the node with the new field collection (not sure this is needed)
node_save($node);
}
Any help would be greatly appreciated, I am still quite new to Drupal as a whole (end-user or developer)
Alright so I think I have figured this out, I wrote up a function that will set a field collection values:
// $node: (obj) node object returned from node_load()
// $collection: (string) can be found in drupal admin interface:
// structure > field collections > field name
// $fields: (array) see usage below
// $index: (int) the index to the element you wish to edit
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
if ($node && $collection && !empty($fields)) {
// Get the field collection ID
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
// Load the field collection with the ID from above
$entity = entity_load_single('field_collection_item', array($eid));
// Wrap the loaded field collection which makes setting/getting much easier
$node_wrapper = entity_metadata_wrapper('field_collection_item', $entity);
// Loop through our fields and set the values
foreach ($fields as $field => $data) {
$node_wrapper->{$field}->set($data);
}
// Once we have added all the values we wish to change then we need to
// save. This will modify the node and does not require node_save() so
// at this point be sure it is all correct as this will save directly
// to a published node
$node_wrapper->save(true);
}
}
USAGE:
// id of the node you wish to modify
$node = node_load(123);
// Call our function with the node to modify, the field collection machine name
// and an array setup as collection_field_name => value_you_want_to_set
// collection_field_name can be found in the admin interface:
// structure > field collections > manage fields
updateFieldCollection(
$node,
'field_home_experts',
array (
'field_expert_image' => (array)file_load(582), // Loads up an existing image
'field_expert_name' => 'Some Guy',
'field_expert_title' => 'Some Title',
)
);
Hope this helps someone else as I spent a whole day trying to get this to work (hopefully I won't be a noob forever in Drupal7). There may be an issue getting formatted text to set() properly but I am not sure what that is at this time, so just keep that in mind (if you have a field that has a format of filtered_html for example, not sure that will set correctly without doing something else).
Good luck!
Jake
I was still getting the error, mentioned in the question, after using the above function.
This is what worked for me:
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
$fc_item = entity_load('field_collection_item', array($eid));
foreach ($fields as $field => $data) {
$fc_item[$eid]->{$field}[LANGUAGE_NONE][0]['value'] = $data;
}
$fc_item[$eid]->save(TRUE);
}
I hope this helps someone as it took me quite some time to get this working.

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();
}

Creating new event in a module

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.

Adding avatar to $user in Drupal 7

I've created a simple module for displaying a flash game in a custom block by overwriting game_block_view() and game_block_info() in the sites/default/modules/game.module and it works ok.
I need however to pass user avatar and also gender and city (I've added the 2 mandatory fields to the registration form) through the FlashVars-parameter to the flash game in my block.
So I'm trying to overload the hook_user_load, because I suppose that this is the method where you add properties to the $user object after it has been initiated from the database (this probably happens when the user logins or alters his/her profile data?):
function game_user_load($users) {
global $user;
$uid = $user->uid;
$result = db_query('select filename from {file_managed} where uid=:uid', array(':uid' => array($uid)));
$avatar = $result->fetchField();
$users[$uid]->avatar = $avatar;
drupal_set_message("<pre>$uid: $avatar</pre>\n");
print_r($users);
}
Unfortunately I see no output produced by the last 2 lines above in the web page
What am I doing wrong?
Thank you!
Alex
The global user object does not go through hook_user_load(), see http://api.drupal.org/api/drupal/includes--session.inc/function/_drupal_session_read/7. Don't ask me why, that's just the way it is :)
When using user_load(), any added fields will automatically be loaded, you don't need custom code for that. You just need to know how to access them, which is a bit complicated.
Something like this should work:
global $user;
// $account is now a fully loaded user object.
$account = user_load($user->uid);
// Your field name is probably 'field_avatar'.
if ($avatar = field_get_items('user', $account, 'field_avatar')) {
dpm($avatar); // only works with devel.module, strongly suggested!
}

Change node author automatically

Anonymous user is able to post nodes. After posting node, user is redirected to registration. After registration, the previously submitted node should be linked with newly registered user.
I played with rules and entities but I was not able to get it work properly. Any ideas?
I would write a custom module (but that's me). The module needs to implement hook_node_insert and save the nid into SESSION. Then on hook_user_insert it can do the change. Untested code:
function foo_node_insert($node) {
$_SESSION['mynodes'][] = $node->nid;
}
function foo_user_insert($edit, $account) {
if (!empty($_SESSION['mynodes'])) {
foreach ($_SESSION['mynodes'] as $nid) {
$node = node_load($nid);
$node->uid = $account->uid;
// This saves the revision as the current user uid but that's just what we wanted.
node_save($node);
}
}
}
Edit: don't forget unset($_SESSION['mynodes']);
Save the node data until after registration and post it then.
There's the Anonymous Node Create module.
The module allows anonymous users to create nodes. But 'anonymous' is questionable in this module. This module alters the node form for anonymous users by adding two field groups at the end before the save button.
The first field group has fields that allow users to create a new account. This new account is then the author of the new node created.

Resources