diff --git a/FeedsSelfNodeProcessor.inc b/FeedsSelfNodeProcessor.inc
index 8149712..ee31187 100755
--- a/FeedsSelfNodeProcessor.inc
+++ b/FeedsSelfNodeProcessor.inc
@@ -11,6 +11,39 @@
 class FeedsSelfNodeProcessor extends FeedsNodeProcessor {
 
   /**
+   * Override parent::entityLoad().
+   *
+   * @note Assumes updating existing or replacing.
+   * /
+  protected function entityLoad(FeedsSource $source, $nid) {
+    $node = node_load($nid, NULL, TRUE);
+    node_object_prepare($node);
+    // Workaround for issue #1247506. See #1245094 for backstory.
+    if (!empty($node->menu)) {
+      // If the node has a menu item(with a valid mlid) it must be flagged
+      // 'enabled'.
+      $node->menu['enabled'] = (int) (bool) $node->menu['mlid'];
+    }
+    // Populate properties that are set by node_object_prepare().
+    if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
+      $node->log = 'Updated by FeedsSelfNodeProcessor';
+    }
+    else {
+      $node->log = 'Replaced by FeedsSelfNodeProcessor';
+    }
+    return $node;
+  }
+  //*/
+
+  /**
+   * Override parent::entitySave().
+   * /
+  public function entitySave($entity) {
+    node_save($entity);
+  }
+  //*/
+
+  /**
    * Process the result of the parsing stage.
    *
    * @param FeedsSource $source
@@ -19,43 +52,104 @@ class FeedsSelfNodeProcessor extends FeedsNodeProcessor {
    *   The result of the parsing stage.
    */
   public function process(FeedsSource $source, FeedsParserResult $parser_result) {
+    $state = $source->state(FEEDS_PROCESS);
+
+    // We are updating self so we only have one item, no need for while loop.
     $item = $parser_result->shiftItem();
 
-    // Only proceed if item has actually changed.
-    $hash = $this->hash($item);
-    if ($hash == $this->getHash($source->feed_nid)) {
+    // We know node exists because it is the source of this import.
+    $entity_id = $source->feed_nid;
+    $skip_existing = $this->config['update_existing'] == FEEDS_SKIP_EXISTING;
+
+    // If we are not updating return.
+    if ($skip_existing) {
       return;
     }
-    $node = node_load($source->feed_nid, NULL, TRUE);
-    $node->log = 'Updated by FeedsSelfNodeProcessor';
-    $node->changed = REQUEST_TIME;
-    $node->format = $this->config['input_format'];
 
-    $this->newItemInfo($node, $source->feed_nid, $hash);
-    $entity->feeds_item->entity_id = $source->feed_nid;
+    $hash = $this->hash($item);
+    $changed = ($hash !== $this->getHash($entity_id));
+    $force_update = $this->config['skip_hash_check'];
+
+    // Do not proceed if the item has not changed, and we're not forcing
+    // the update.
+    if (!$changed && !$force_update) {
+      return;
+    }
 
     try {
-      $this->map($source, $parser_result, $node);
-      node_object_prepare($node);
+      // Load entity.
+      $entity = $this->entityLoad($source, $entity_id);
+      // The feeds_item table is always updated with the info for the most recently processed entity.
+      // The only carryover is the entity_id.
+      $this->newItemInfo($entity, $source->feed_nid, $hash);
+      $entity->feeds_item->entity_id = $entity_id;
+
+      // Set property and field values.
+      $this->map($source, $parser_result, $entity);
+      $this->entityValidate($entity);
 
-      if (!isset($node->feeds)) {
-        $node->feeds = array();
+      // Allow modules to alter the entity before saving.
+      module_invoke_all('feeds_presave', $source, $entity, $item);
+      if (module_exists('rules')) {
+        rules_invoke_event('feeds_import_'. $source->importer()->id, $entity);
       }
-      node_save($node);
+
+      // Enable modules to skip saving at all.
+      if (!empty($entity->feeds_item->skip)) {
+        return;
+      }
+
+      // This will throw an exception on failure.
+      $this->entitySaveAccess($entity);
+      $this->entitySave($entity);
+
+      // Track progress.
+      $state->updated++;
     }
+    // Something bad happened, log it.
     catch (Exception $e) {
+      $state->failed++;
       drupal_set_message($e->getMessage(), 'warning');
       $message = $e->getMessage();
       $message .= '<h3>Original item</h3>';
       $message .= '<pre>' . var_export($item, TRUE) . '</pre>';
       $message .= '<h3>Entity</h3>';
-      $message .= '<pre>' . var_export($node, TRUE) . '</pre>';
+      $message .= '<pre>' . var_export($entity, TRUE) . '</pre>';
       $source->log('import', $message, array(), WATCHDOG_ERROR);
-      return;
     }
-    $message = t('@type %title has been updated.', array('@type' => node_type_get_name($node), '%title' => $node->title));
-    drupal_set_message($message);
-    $source->log('import', $message, array(), WATCHDOG_INFO);
+
+    // Set messages
+    $info = $this->entityInfo();
+    $tokens = array(
+      '@entity' => strtolower($info['label']),
+    );
+    $messages = array();
+    if ($state->updated) {
+      $messages[] = array(
+       'message' => t(
+         'Updated @number @entity.',
+          array('@number' => $state->updated) + $tokens
+        ),
+      );
+    }
+    if ($state->failed) {
+      $messages[] = array(
+       'message' => t(
+          'Failed importing @number @entity.',
+          array('@number' => $state->failed) + $tokens
+        ),
+        'level' => WATCHDOG_ERROR,
+      );
+    }
+    if (empty($messages)) {
+      $messages[] = array(
+        'message' => t('There are no new @entities.', array('@entities' => strtolower($info['label plural']))),
+      );
+    }
+    foreach ($messages as $message) {
+      drupal_set_message($message['message']);
+      $source->log('import', $message['message'], array(), isset($message['level']) ? $message['level'] : WATCHDOG_INFO);
+    }
   }
 
   /**
@@ -73,10 +167,14 @@ class FeedsSelfNodeProcessor extends FeedsNodeProcessor {
    */
   public function configForm(&$form_state) {
     $form = parent::configForm($form_state);
-    $form['expire']['#type'] = 'hidden';
-    unset($form['update_existing']);
-    unset($form['content_type']);
-    $form['author']['#type'] = 'hidden';
+    // @note The point of this processor is to update existing nodes so we
+    //   don't need the option FEEDS_SKIP_EXISTING but we can let users choose
+    //   to update or replace. We are able to adjust how users use this
+    //   processor through the interface but what happens if they have defined
+    //   e.g. FEEDS_SKIP_EXISTING in code?
+    unset($form['update_existing']['#options'][FEEDS_SKIP_EXISTING]);
+    unset($form['content_type']); // We're not creating new content so no need
+                                  // to specify a content type.
     return $form;
   }
 
@@ -84,6 +182,7 @@ class FeedsSelfNodeProcessor extends FeedsNodeProcessor {
    * Return available mapping targets.
    */
   public function getMappingTargets() {
+    // Force content type to be self.
     $this->config['content_type'] = feeds_importer($this->id)->config['content_type'];
     if (empty($this->config['content_type'])) {
       drupal_set_message(t('This importer must be attached to a content type for Feeds Self Node Processor to work.'), 'warning');
