diff --git a/feeds.module b/feeds.module index 48835d7..bfa0b78 100644 --- a/feeds.module +++ b/feeds.module @@ -572,6 +572,39 @@ function feeds_user_delete($account) { } /** + * Implements hook_entity_insert(). + */ +function feeds_entity_insert($entity, $type) { + if (!in_array ($type, array ('node', 'user', 'taxonomy_term'))) { + list($id) = entity_extract_ids($type, $entity); + feeds_item_info_insert($entity, $id); + } +} + +/** + * Implements hook_entity_update(). + */ +function feeds_entity_update($entity, $type) { + if (!in_array ($type, array ('node', 'user', 'taxonomy_term'))) { + list($id) = entity_extract_ids($type, $entity); + feeds_item_info_save($entity, $id); + } +} + +/** + * Implements hook_entity_delete(). + */ +function feeds_entity_delete($entity, $type) { + if (!in_array ($type, array ('node', 'user', 'taxonomy_term'))) { + list($id) = entity_extract_ids($type, $entity); + db_delete('feeds_item') + ->condition('entity_type', $type) + ->condition('entity_id', $id) + ->execute(); + } +} + +/** * Implements hook_form_alter(). */ function feeds_form_alter(&$form, $form_state, $form_id) { diff --git a/feeds.plugins.inc b/feeds.plugins.inc index 7d53023..2e9b2a7 100644 --- a/feeds.plugins.inc +++ b/feeds.plugins.inc @@ -130,6 +130,30 @@ function _feeds_feeds_plugins() { 'path' => $path, ), ); + if (module_exists('entity')) { + + foreach (entity_get_info() as $type => $entity_info) { + // @todo: test for saving and whatever else necessary? + if (entity_type_supports($type, 'create')) { + $info['FeedsEntityProcessor' . drupal_ucfirst($type)] = array( + 'name' => 'Entity processor ' . $entity_info['label'], + // @todo: Use plural label if there. + 'description' => 'Create and update ' . $entity_info['label'] . 's.', + 'help' => 'Create and update ' . $entity_info['label'] . 's from parsed content.', + 'plugin_key' => 'FeedsEntityProcessor', + 'handler' => array( + 'parent' => 'FeedsProcessor', + 'class' => 'FeedsEntityProcessor', + 'file' => 'FeedsEntityProcessor.inc', + 'path' => $path, + ), + // Add in the entity type used. + // @see FeedsEntityProcessor::entityType() + 'type' => $type, + ); + } + } + } $info['FeedsNodeProcessor'] = array( 'name' => 'Node processor', 'description' => 'Create and update nodes.', diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc index 1ad6826..7d1db61 100644 --- a/feeds_ui/feeds_ui.admin.inc +++ b/feeds_ui/feeds_ui.admin.inc @@ -416,6 +416,7 @@ function feeds_ui_plugin_form($form, &$form_state, $importer, $type) { $form = array(); $form['#importer'] = $importer; $form['#plugin_type'] = $type; + $config = $importer->getConfig(); foreach ($plugins as $key => $plugin) { $form['plugin_key'][$key] = array( @@ -424,7 +425,7 @@ function feeds_ui_plugin_form($form, &$form_state, $importer, $type) { '#title' => $plugin['name'], '#description' => isset($plugin['help']) ? $plugin['help'] : $plugin['description'], '#return_value' => $key, - '#default_value' => ($plugin['handler']['class'] == get_class($importer->$type)) ? $key : '', + '#default_value' => ($key == $config[$type]['plugin_key']) ? $key : '', ); } $form['submit'] = array( diff --git a/plugins/FeedsEntityProcessor.inc b/plugins/FeedsEntityProcessor.inc new file mode 100644 index 0000000..3f834e3 --- /dev/null +++ b/plugins/FeedsEntityProcessor.inc @@ -0,0 +1,163 @@ +id); + $plugin_key = $importer->config['processor']['plugin_key']; + $plugin_info = ctools_get_plugins('feeds', 'plugins', $plugin_key); + return $plugin_info['type']; + } + + /** + * Implements parent::entityInfo(). + */ + protected function entityInfo() { + $info = parent::entityInfo(); + $info += array('label plural' => $info['label']); + return $info; + } + + /** + * Creates a new entity in memory and returns it. + */ + protected function newEntity(FeedsSource $source) { + $entity = entity_property_values_create_entity($this->entityType(), $this->config['values'])->value(); + $entity->language = LANGUAGE_NONE; + return $entity; + } + + /** + * Loads an existing entity. + * + * If the update existing method is not FEEDS_UPDATE_EXISTING, only the entity + * table will be loaded, foregoing the entity_load API for better performance. + */ + protected function entityLoad(FeedsSource $source, $id) { + $result = entity_load(array($id)); + return reset($result); + } + + /** + * Save a entity. + */ + public function entitysave($entity) { + entity_save($this->entityType(), $entity); + } + + /** + * Delete a series of entities. + */ + protected function entityDeleteMultiple($ids) { + entity_delete_multiple($this->entityType(), $ids); + } + + /** + * Override parent::configDefaults(). + */ + public function configDefaults() { + return array( + 'values' => array(), + ) + parent::configDefaults(); + } + + /** + * Override parent::configForm(). + */ + public function configForm(&$form_state) { + $form = parent::configForm($form_state); + $form['values']['#tree'] = TRUE; + $form['input_format']['#description'] = t('Select the input format for the %entity to be created.', array('%entity' => $this->entityType())); + + $wrapper = entity_metadata_wrapper($this->entityType()); + foreach ($wrapper->getPropertyInfo() as $name => $property) { + if (!empty($property['required'])) { + $form['values'][$name] = array( + '#type' => 'textfield', + '#title' => $property['label'], + '#description' => isset($property['description']) ? $property['description'] : '', + '#default_value' => isset($this->config['values'][$name]) ? $this->config['values'][$name] : NULL, + '#required' => TRUE, + ); + if (!empty($property['options list'])) { + $form['values'][$name]['#type'] = 'select'; + $form['values'][$name]['#options'] = $wrapper->$name->optionsList(); + } + // @todo: Maybe implement per data-type forms like Rules does? + $form['values'][$name]['#description'] .= ' ' . t('Expected data type: %type.', array('%type' => $wrapper->$name->type())); + if ($wrapper->$name instanceof EntityDrupalWrapper) { + $info = $wrapper->$name->entityInfo(); + $id_info = $wrapper->$name->get($info['entity keys']['id'])->info(); + $form['values'][$name]['#description'] .= ' ' . t('Just enter the identifier of the entity, i.e. %id', array('%id' => $id_info['label'])); + } + } + } + return $form; + } + + public function configFormSubmit(&$values) { + parent::configFormSubmit($values); + } + + /** + * Override setTargetElement to operate on a target item that is a entity. + */ + public function setTargetElement(FeedsSource $source, $target_item, $target_element, $value) { + $wrapper = entity_metadata_wrapper($this->entityType(), $target_item); + switch ($target_element) { + case 'url': + case 'guid': + $target_item->feeds_item->$target_element = $value; + break; + default: + $wrapper->$target_element->set($value); + break; + } + } + + /** + * Return available mapping targets. + */ + public function getMappingTargets() { + // Get a wrapper with the right bundle info. + $entity_info = $this->entityInfo(); + $info = array('bundle' => NULL); + + if (isset($entity_info['entity keys']['bundle']) && isset($this->config['values'][$entity_info['entity keys']['bundle']])) { + $info['bundle'] = $this->config['values'][$entity_info['entity keys']['bundle']]; + } + else { + $info['bundle'] = $this->entityType(); + } + + $wrapper = entity_metadata_wrapper($this->entityType(), NULL, $info); + // @todo: maybe restrict to data types feeds can deal with. + foreach ($wrapper->getPropertyInfo() as $name => $property) { + if (!empty($property['setter callback'])) { + $targets[$name] = array( + 'name' => $property['label'], + 'description' => isset($property['description']) ? $property['description'] : NULL, + ); + } + } + $targets[$entity_info['entity keys']['id']]['optional_unique'] = TRUE; + + // Let other modules expose mapping targets. + self::loadMappers(); + $type = $this->entityType(); + drupal_alter('feeds_processor_targets', $targets, $type, $info['bundle']); + + return $targets; + } +}