? 955236-3_error_handling.patch
? PATCHES.txt
Index: feeds.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.module,v
retrieving revision 1.74.2.11
diff -u -p -r1.74.2.11 feeds.module
--- feeds.module 27 Oct 2010 19:53:21 -0000 1.74.2.11
+++ feeds.module 29 Oct 2010 20:50:03 -0000
@@ -738,6 +738,39 @@ function feeds_log($importer_id, $feed_n
}
/**
+ * Gets the last caller from a backtrace.
+ *
+ * Modeled after _drupal_get_last_caller().
+ *
+ * @param $backtrace
+ * A standard PHP backtrace.
+ * @return
+ * An associative array with keys 'file', 'line' and 'function'.
+ */
+function feeds_get_last_caller($backtrace) {
+ // Shift off one to not report Feeds error handler methods.
+ array_shift($backtrace);
+
+ // The first trace is the call itself.
+ // It gives us the line and the file of the last call.
+ $call = $backtrace[0];
+
+ // The second call give us the function where the call originated.
+ if (isset($backtrace[1])) {
+ if (isset($backtrace[1]['class'])) {
+ $call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
+ }
+ else {
+ $call['function'] = $backtrace[1]['function'] . '()';
+ }
+ }
+ else {
+ $call['function'] = 'main()';
+ }
+ return $call;
+}
+
+/**
* Loads an item info object.
*
* Example usage:
Index: includes/FeedsSource.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/includes/FeedsSource.inc,v
retrieving revision 1.26.2.8
diff -u -p -r1.26.2.8 FeedsSource.inc
--- includes/FeedsSource.inc 26 Oct 2010 03:05:58 -0000 1.26.2.8
+++ includes/FeedsSource.inc 29 Oct 2010 20:50:03 -0000
@@ -328,6 +328,8 @@ class FeedsSource extends FeedsConfigura
*/
public function import() {
$this->acquireLock();
+ set_error_handler(array($this, 'importError'));
+
try {
// Fetch.
if (empty($this->fetcher_result) || FEEDS_BATCH_COMPLETE == $this->progressParsing()) {
@@ -354,6 +356,8 @@ class FeedsSource extends FeedsConfigura
unset($this->fetcher_result, $this->state);
}
$this->save();
+ restore_error_handler();
+
if (isset($e)) {
throw $e;
}
@@ -376,6 +380,8 @@ class FeedsSource extends FeedsConfigura
*/
public function clear() {
$this->acquireLock();
+ set_error_handler(array($this, 'clearError'));
+
try {
$this->importer->fetcher->clear($this);
$this->importer->parser->clear($this);
@@ -391,6 +397,8 @@ class FeedsSource extends FeedsConfigura
unset($this->state);
}
$this->save();
+ restore_error_handler();
+
if (isset($e)) {
throw $e;
}
@@ -398,6 +406,57 @@ class FeedsSource extends FeedsConfigura
}
/**
+ * Handles errors on import.
+ */
+ public function importError($error_level, $message, $filename, $line, $context) {
+ if ($error_level & error_reporting()) {
+ // If this is NOT a recoverable error, attempt to clean up.
+ // @todo: does this make sense or do we know that if an error was not
+ // recoverable we can't clean up?
+ // @todo: it may be safer to delete the state every time when loading it.
+ if ($this->handleError($error_level, $message, $filename, $line, $context)) {
+ unset($this->state);
+ $this->save();
+ }
+ _drupal_error_handler($error_level, $message, $filename, $line, $context);
+ }
+ }
+
+ /**
+ * Handles errors on clear.
+ */
+ public function clearError($error_level, $message, $filename, $line, $context) {
+ if ($error_level & error_reporting()) {
+ if ($this->handleError($error_level, $message, $filename, $line, $context)) {
+ unset($this->state);
+ $this->save();
+ }
+ _drupal_error_handler($error_level, $message, $filename, $line, $context);
+ }
+ }
+
+ /**
+ * Logs error and determines whether error is fatal
+ *
+ * @return
+ * TRUE if error is fatal, FALSE otherwise.
+ */
+ protected function handleError($error_level, $message, $filename, $line, $context) {
+ require_once DRUPAL_ROOT . '/includes/errors.inc';
+ $levels = drupal_error_levels();
+ list ($severity_message, $severity_level) = $levels[$error_level];
+ $caller = feeds_get_last_caller(debug_backtrace());
+ $variables = array(
+ '@message' => $message,
+ '%function' => $caller['function'],
+ '%file' => isset($caller['file']) ? $caller['file'] : '',
+ '%line' => isset($caller['line']) ? $caller['line'] : '',
+ );
+ $this->log('import', "@message
%function
%file
%line", $variables, $severity_level);
+ return $error_level & array(E_ERROR | E_COMPILE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
+ }
+
+ /**
* Report progress as float between 0 and 1. 1 = FEEDS_BATCH_COMPLETE.
*/
public function progressParsing() {
Index: mappers/link.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/mappers/link.inc,v
retrieving revision 1.5
diff -u -p -r1.5 link.inc
--- mappers/link.inc 17 Sep 2010 17:25:19 -0000 1.5
+++ mappers/link.inc 29 Oct 2010 20:50:03 -0000
@@ -1,87 +1,81 @@
$instance) {
- $fields = array();
- if (isset($info['fields']) && count($info['fields'])) {
- foreach ($info['fields'] as $field_name => $field) {
-
- if (in_array($field['type'], array('link'))) {
- $name = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name;
- $targets[$field_name .':url'] = array(
- 'name' => t('!field_name (URL)', array('!field_name' => $name)),
+ $info = field_info_field($name);
+ $allowed_types = array(
+ 'link_field',
+ );
+
+ if (in_array($info['type'], $allowed_types)) {
+ if (array_key_exists('url', $info['columns'])) {
+ $targets[$name . ':url'] = array(
+ 'name' => $instance['label'] . ' URL',
'callback' => 'link_feeds_set_target',
- 'description' => t('The URL for the CCK !name field of the node.', array('!name' => $name)),
- 'real_target' => $field_name,
+ 'description' => t('The @label field of the node.', array('@label' => $instance['label'])),
+ );
+ }
+ if (array_key_exists('title', $info['columns'])) {
+ $targets[$name . ':title'] = array(
+ 'name' => $instance['label'] . ' Title',
+ 'callback' => 'link_feeds_set_target',
+ 'description' => t('The @label field of the node.', array('@label' => $instance['label'])),
);
-
- //Provides a mapping target for the field title if used.
- if (in_array($field['title'], array('optional', 'required'))) {
- $targets[$field_name .':title'] = array(
- 'name' => $name .' (' . t('title').')',
- 'callback' => 'link_feeds_set_target',
- 'description' => t('The title for the CCK !name field of the node.', array('!name' => $name)),
- 'real_target' => $field_name,
- );
- }
}
}
}
}
/**
- * Callback for mapping to link field.
+ * Callback for mapping. Here is where the actual mapping happens.
*
- * @param $node
- * Reference to the node object we are working on.
- * @param $target
- * The selected link CCK field.
- * @param $value
- * The value to assign to the CCK field.
+ * When the callback is invoked, $target contains the name of the field the
+ * user has decided to map to and $value contains the value of the feed item
+ * element the user has picked as a source.
*/
-function link_feeds_set_target($node, $target, $value) {
- module_load_include('inc', 'link');
- if (!empty($value)) {
- static $defaults = array();
- list($field_name, $sub_field) = split(':', $target);
-
- if (!isset($defaults[$node->type][$field_name])) {
- $field = content_fields($field_name, $node->type);
- $defaults[$node->type][$field_name]['attributes'] = $field['attributes'];
- if (!in_array($field['title'], array('optional', 'required', 'none'))) {
- $defaults[$node->type][$field_name]['title'] = $field['title_value'];
- }
- }
- $field_data = isset($node->$field_name) ? $node->$field_name : array();
+function link_feeds_set_target($source, $entity, $target, $value) {
+ if (empty($value)) {
+ return;
+ }
- if (!is_array($value)) {
- $value = array($value);
- }
+ list($field_name, $sub_field) = preg_split("/:/", $target);
- $i = 0;
- foreach ($value as $v) {
- if ($v instanceof FeedsEnclosure) {
- $v = $v->getValue();
- }
- if (!isset($field_data[$i])) {
- $field_data[$i] = $defaults[$node->type][$field_name];
+ // Handle non-multiple value fields.
+ if (!is_array($value)) {
+ $value = array($value);
+ }
+
+ $info = field_info_field($target);
+
+ // Iterate over all values.
+ $i = 0;
+
+ foreach ($value as $v) {
+ if (!is_array($v) && !is_object($v)) {
+ if (strstr($target, 'url')) {
+ if(isset($entity->{$field_name}['und'][$i]['title'])) {
+ $field['und'][$i]['title'] = $entity->{$field_name}['und'][$i]['title'];
+ }
+ $field['und'][$i]['url'] = $v;
}
- if ($sub_field != 'url' || (($v = link_cleanup_url($v)) && valid_url($v, true))) {
- $field_data[$i][$sub_field] = $v;
+ elseif (strstr($target, 'title')) {
+ if(isset($entity->{$field_name}['und'][$i]['url'])) {
+ $field['und'][$i]['url'] = $entity->{$field_name}['und'][$i]['url'];
+ }
+ $field['und'][$i]['title'] = $v;
}
- $i++;
}
-
- $node->$field_name = $field_data;
+ if ($info['cardinality'] == 1) {
+ break;
+ }
+ $i++;
}
+
+ $entity->{$field_name} = $field;
}