When the user deletes a node, mm_emfield_media_mover() gets called with $op='delete', which then calls mm_emfield_node_file_delete(), which then calls node_save().
function mm_emfield_media_mover($op, $action = null, $configuration = null, &$file = array(), $job = null, $nid = null) {
if (module_exists('emfield')) {
switch ($op) {
...
case 'delete':
if ($action == MM_EMFIELD_STORE || $action == MM_EMFIELD_COMPLETE) {
mm_emfield_node_file_delete($configuration, $file);
}
break;
}
}
As a result, node_save() gets called while node_delete() is in the midst of executing. This causes a bunch of problems when a node is deleted, including:
- The emfield rows in content_field_myemfield, corresponding to the deleted node's harvested files, are not deleted. (The columns are instead set to NULL, because node_save() causes hook_nodeapi to get called with $op='update'.) See http://drupal.org/node/1497418
- Similarly, the rows in the content_field_myfilefield database table, corresponding to the deleted node's harvested files, are not deleted.
- Emfield outputs error message "unserialize() expects parameter 1 to be string". See http://drupal.org/node/1308856.
- Multiple calls to hook_nodeapi with $op='update', when user deletes node.
I modified media_mover_api.module so that mm_emfield_node_file_delete() is no longer called when a node is deleted. As a result, node_save() is no longer called in the midst of executing node_delete(), and all of the problems listed above went away.
The basic idea was to add an $op to hook_media_mover(), to distinguish between "user has deleted a node" ($op='node_delete') and "user has deleted a file" ($op='delete'). mm_emfield's implementation of hook_media_mover is then able to distinguish these two cases, and call mm_emfield_node_file_delete() only when the user has deleted a file, but not when the user has deleted a node. In this way, node_save() is no longer called in the midst of executing node_delete().
Because this approach distinguishes 'node_delete' from 'delete', it may be necessary to modify the implementation of hook_media_mover() in some other modules. For example, I found it was necessary to add a case statement for $op='node_delete' to mm_s3's implementation of hook_media_mover(), so that mm_s3_delete_file() would get called both when the user deleted a node and when the user deleted a file.
Here are the detailed code changes:
function media_mover_api_node_files_delete()
In the call to media_mover_api_file_delete_call(), I pass $op='node_delete' as a second parameter, to indicate that the user had deleted a node.
function media_mover_api_node_files_delete($node) {
...
if ($configuration->settings->mma_node_item_delete) {
foreach ($mm_files as $mm_file) {
// media_mover_api_file_delete_call($mm_file); // original code
media_mover_api_file_delete_call($mm_file, 'node_delete'); // <--- HERE. added second parameter
}
...
}
function media_mover_api_file_delete_call()
The function accepts a second parameter, which by default is 'delete', and passes this parameter to _media_mover_api_module_invoke().
function media_mover_api_file_delete_call($file, $op='delete') {
foreach (media_mover_api_verbs() as $verb) {
// _media_mover_api_module_invoke($configuration->{$verb}->module, 'media_mover', 'delete', $configuration->{$verb}->action, $configuration->{$verb}->configuration, $file, $configuration); // original code
_media_mover_api_module_invoke($configuration->{$verb}->module, 'media_mover', $op, $configuration->{$verb}->action, $configuration->{$verb}->configuration, $file, $configuration); // <--- HERE. replaced 'delete' with $op
}
With the changes above, mm_emfield_media_mover() no longer calls mm_emfield_node_file_delete() when a node has been deleted.
function mm_s3_media_mover()
I modified mm_s3's implementation of hook_media_mover to support these changes.
function mm_s3_media_mover($op, $action_id = null, $configuration = array(), &$file = array()) {
switch ($op) {
case 'node_delete': // <--- HERE. Added this line, so mm_s3_delete_file() gets called when node is deleted
case 'delete':
mm_s3_delete_file($file, $configuration);
break;
}
}
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | media_mover_api.bug20.version2.diff | 800 bytes | kobnim |
| #2 | mm_emfield.bug20.version2.diff | 968 bytes | kobnim |
| mm_s3.bug20.diff | 47 bytes | kobnim | |
| media_mover_api.bug20.diff | 674 bytes | kobnim |
Comments
Comment #0.0
kobnim commentedwrong link. fixed.
Comment #2
kobnim commentedOn second thought, here is an alternate approach for fixing the problem, that does not require any changes to the hook_media_mover() API.
function mm_emfield_node_file_delete()
Add an optional parameter, $options, that tells this function whether the user has deleted the entire node, or just the individual file. If the user has deleted the entire node, then the function does not call node_save().
.
How $options gets passed to mm_emfield_node_file_delete():
(a) media_mover_api_node_files_delete() passes $options to media_mover_api_file_delete_call():
(b) media_mover_api_node_files_delete() then passes $options to _media_mover_api_module_invoke(), which in turn passes $options to mm_emfield_media_mover():
(c) mm_emfield_media_mover() then passes passes $options to mm_emfield_node_file_delete():
Comment #3
kobnim commentedUPDATE / FYI:
I noticed a related problem that was not fixed by this patch.
If the user deletes a file from a filefield, and if
mm_emfield_node_file_delete()gets called to clear the url from the corresponding emfield, thenhook_nodeapigets called twice with $op='update'.The reason
hook_nodeapigets called twice with $op='update' is thatmm_emfield_node_file_delete()callsnode_save().I don't see any easy way of fixing this problem. My only thought is to rearchitect things so that
mm_emfield_node_file_delete()no longer needs to callnode_save(), by callingmm_emfield_node_file_delete()fromhook_nodeapi. But maybe it's not worth the trouble ...Comment #3.0
kobnim commentedcorrection