class NodeStorageController extends EntityDatabaseStorageController {
  
  public function delete($ids) {
    $entities = $ids ? $this->load($ids) : FALSE;
    if (!$entities) {
            return;
    }
    $transaction = db_transaction();
    try {
      $this->preDelete($entities);
      foreach ($entities as $id => $entity) {
        $this->invokeHook('predelete', $entity);
      }
      $ids = array_keys($entities);
      db_delete($this->entityInfo['base table'])
        ->condition($this->idKey, $ids, 'IN')
        ->execute();
      if ($this->revisionKey) {
        db_delete($this->revisionTable)
          ->condition($this->idKey, $ids, 'IN')
          ->execute();
      }
            $this->resetCache($ids);
      $this->postDelete($entities);
      foreach ($entities as $id => $entity) {
        $this->invokeHook('delete', $entity);
      }
            db_ignore_replica();
    }
    catch (Exception $e) {
      $transaction->rollback();
      watchdog_exception($this->entityType, $e);
      throw new EntityStorageException($e->getMessage(), (int) $e->getCode(), $e);
    }
  }
  
  public function save(EntityInterface $entity) {
    $transaction = db_transaction();
    try {
            if (!$entity->isNew() && !isset($entity->original)) {
        $entity->original = entity_load_unchanged($this->entityType, $entity->id());
      }
      $this->preSave($entity);
      $this->invokeHook('presave', $entity);
      if ($entity->isNew()) {
        $op = 'insert';
        $return = backdrop_write_record($this->entityInfo['base table'], $entity);
        unset($entity->is_new);
      }
      else {
        $op = 'update';
                        if ($entity->isActiveRevision()) {
          $return = backdrop_write_record($this->entityInfo['base table'], $entity, $this->idKey);
        }
        else {
          $return = SAVED_UPDATED;
        }
      }
      if ($this->revisionKey) {
        $this->saveRevision($entity);
      }
            if ($op == 'update') {
        $this->resetCache(array($entity->{$this->idKey}));
      }
      $this->postSave($entity, $op == 'update');
      $this->invokeHook($op, $entity);
            db_ignore_replica();
      unset($entity->original);
      return $return;
    }
    catch (Exception $e) {
      $transaction->rollback();
      watchdog_exception($this->entityType, $e);
      throw new EntityStorageException($e->getMessage(), (int) $e->getCode(), $e);
    }
  }
  
  protected function saveRevision(EntityInterface $entity) {
    $record = clone $entity;
    $record->uid = $entity->revision_uid;
    $record->timestamp = $entity->revision_timestamp;
    if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) {
      backdrop_write_record($this->revisionTable, $record);
                  if ($entity->isActiveRevision()) {
        db_update($this->entityInfo['base table'])
          ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
          ->condition($this->idKey, $entity->{$this->idKey})
          ->execute();
      }
    }
    else {
      backdrop_write_record($this->revisionTable, $record, $this->revisionKey);
    }
        $entity->{$this->revisionKey} = $record->{$this->revisionKey};
  }
  
  protected function attachLoad(&$nodes, $revision_id = FALSE) {
            $typed_nodes = array();
    foreach ($nodes as $id => $entity) {
      $typed_nodes[$entity->type][$id] = $entity;
    }
        foreach ($typed_nodes as $node_type => $nodes_of_type) {
      if (node_hook($node_type, 'load')) {
        $function = node_type_get_base($node_type) . '_load';
        $function($nodes_of_type);
      }
    }
            $argument = array_keys($typed_nodes);
    $this->hookLoadArguments = array($argument);
    parent::attachLoad($nodes, $revision_id);
  }
  
  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
            $query = parent::buildQuery($ids, $conditions, $revision_id);
    $fields = &$query->getFields();
    unset($fields['timestamp']);
    $query->addField('revision', 'timestamp', 'revision_timestamp');
    $fields['uid']['table'] = 'base';
    $query->addField('revision', 'uid', 'revision_uid');
    return $query;
  }
  
  protected function invokeHook($hook, EntityInterface $node) {
    if ($hook == 'insert' || $hook == 'update') {
      node_invoke($node, $hook);
    }
    elseif ($hook == 'predelete') {
                  node_invoke($node, 'delete');
    }
    parent::invokeHook($hook, $node);
    if ($hook == 'presave') {
      if ($node->isNew() || !empty($node->revision)) {
                                                                        if (!isset($node->log)) {
          $node->log = '';
        }
      }
      elseif (!isset($node->log) || $node->log === '') {
                                                        unset($node->log);
      }
                        if (!$node->isNew() && !empty($node->revision) && $node->vid) {
        $node->old_vid = $node->vid;
        $node->vid = NULL;
      }
    }
  }
  
  protected function preSave(EntityInterface $node) {
        $node->changed = REQUEST_TIME;
    if ($this->revisionKey && !empty($node->revision)) {
      $node->revision_timestamp = REQUEST_TIME;
      if (!isset($node->revision_uid)) {
        $node->revision_uid = $GLOBALS['user']->uid;
      }
    }
  }
  
  function postSave(EntityInterface $node, $update) {
                if ($node->isActiveRevision()) {
      node_access_acquire_grants($node, $update);
    }
  }
  
  function preDelete($nodes) {
    if (module_exists('search')) {
      foreach ($nodes as $nid => $node) {
        search_reindex($node->nid, 'node');
      }
    }
  }
  
  protected function postDelete($nodes) {
        $ids = array_keys($nodes);
    db_delete('history')
      ->condition('nid', $ids, 'IN')
      ->execute();
    db_delete('node_access')
      ->condition('nid', $ids, 'IN')
      ->execute();
  }
  
  public function buildContent(EntityInterface $node, $view_mode = 'full', $langcode = NULL) {
    global $language_content;
    $langcode = $langcode ? $langcode : $language_content->langcode;
        $node->content = array();
        $view_mode = key(entity_view_mode_prepare('node', array($node->nid => $node), $view_mode, $langcode));
            if (node_hook($node, 'view')) {
      $node = node_invoke($node, 'view', $view_mode, $langcode);
    }
                    field_attach_prepare_view('node', array($node->nid => $node), $view_mode, $langcode);
    entity_prepare_view('node', array($node->nid => $node));
    $node->content += field_attach_view('node', $node, $view_mode, $langcode);
            $links = array();
    $node->content['links'] = array(
      '#theme' => 'links__node',
      '#pre_render' => array('backdrop_pre_render_links'),
      '#attributes' => array('class' => array('links', 'inline')),
    );
    if ($view_mode == 'teaser') {
      $type = node_type_get_type($node);
            if (!($type->settings['hidden_path'] && !user_access('view hidden paths'))) {
        $node_title_stripped = strip_tags($node->title);
        $links['node-readmore'] = array(
          'title' => t('Read more<span class="element-invisible"> about @title</span>', array('@title' => $node_title_stripped)),
          'href' => 'node/' . $node->nid,
          'html' => TRUE,
          'attributes' => array('rel' => 'tag', 'title' => $node_title_stripped),
        );
      }
    }
    $node->content['links']['node'] = array(
      '#theme' => 'links__node__node',
      '#links' => $links,
      '#attributes' => array('class' => array('links', 'inline')),
    );
        module_invoke_all('node_view', $node, $view_mode, $langcode);
    module_invoke_all('entity_view', $node, 'node', $view_mode, $langcode);
            $node->content += array('#view_mode' => $view_mode);
  }
  
  public function view($nodes, $view_mode = 'full', $langcode = NULL, $page = NULL) {
    global $language_content;
    $langcode = $langcode ? $langcode : $language_content->langcode;
    $view = array();
    foreach ($nodes as $node) {
      
      if (isset($node->preview) && ($node->preview == 'Preview')) {
        backdrop_set_message(t('This is a preview. Links within the page are disabled.'), 'warning', FALSE);
        backdrop_set_message(t('<strong>Changes are stored temporarily</strong>. Click <em>Save</em> to make your changes permanent, or click <em>Back to content editing</em> to make additional changes.'), 'warning', FALSE);
      }
            $this->buildContent($node, $view_mode, $langcode);
      $build = $node->content;
            unset($node->content);
      $build += array(
        '#theme' => 'node',
        '#node' => $node,
        '#view_mode' => $view_mode,
        '#langcode' => $langcode,
        '#page' => $page,
      );
                              if (!empty($node->nid) && !($view_mode == 'full' && node_is_page($node))) {
        $build['#contextual_links']['node'] = array('node', array($node->nid));
      }
            $type = 'node';
      backdrop_alter(array('node_view', 'entity_view'), $build, $type);
      $view[$type][$node->id()] = $build;
    }
    return $view;
  }
}