Index: contrib/search_api_facets/search_api_facets.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.install,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 search_api_facets.install
--- contrib/search_api_facets/search_api_facets.install	28 Oct 2010 20:43:12 -0000	1.1.2.4
+++ contrib/search_api_facets/search_api_facets.install	30 Nov 2010 12:45:43 -0000
@@ -8,6 +8,12 @@
   $schema['search_api_facet'] = array(
     'description' => 'Stores all activated or configured facets of a site.',
     'fields' => array(
+      'id' => array(
+        'description' => 'The primary identifier for a facet.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
       'delta' => array(
         'description' => "The facet block's {block}.delta.",
         'type' => 'varchar',
@@ -51,7 +57,10 @@
       'field' => array('index_id', array('field', 50)),
       'enabled' => array('enabled'),
     ),
-    'primary key' => array('delta'),
+    'unique keys' => array(
+      'delta' => array('delta'),
+    ),
+    'primary key' => array('id'),
   );
 
   return $schema;
@@ -76,12 +85,9 @@
 }
 
 /**
- * Implements hook_update_N().
- *
  * Make {search_api_facet}.index_id the index' machine name.
  */
 function search_api_facets_update_7101() {
-  // Update of search_api_item:
   db_drop_index('search_api_facet', 'field');
   $spec = array(
     'description' => 'The {search_api_index}.machine_name this facet belongs to.',
@@ -102,3 +108,36 @@
       ->execute();
   }
 }
+
+/**
+ * Add a numeric id for facets, as required for all entities.
+ */
+function search_api_facets_update_7102() {
+  db_drop_primary_key('search_api_facet');
+  $spec = array(
+    'description' => 'The primary identifier for a facet.',
+    'type' => 'serial',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+  );
+  $keys_new = array(
+    'unique keys' => array(
+      'delta' => array('delta'),
+    ),
+    'primary key' => array('id'),
+  );
+  db_add_field('search_api_facet', 'id', $spec, $keys_new);
+  $deltas = db_select('search_api_facet', 'f')
+    ->fields('f', array('delta'))
+    ->execute()
+    ->fetchCol();
+  $i = 0;
+  foreach ($deltas as $delta) {
+    db_update('search_api_facet')
+      ->fields(array(
+        'id' => ++$i,
+      ))
+      ->condition('delta', $delta)
+      ->execute();
+  }
+}
Index: contrib/search_api_facets/search_api_facets.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.admin.inc,v
retrieving revision 1.1.2.8
diff -u -r1.1.2.8 search_api_facets.admin.inc
--- contrib/search_api_facets/search_api_facets.admin.inc	28 Oct 2010 20:43:12 -0000	1.1.2.8
+++ contrib/search_api_facets/search_api_facets.admin.inc	30 Nov 2010 12:45:43 -0000
@@ -17,7 +17,7 @@
     // We can't offer any facets, but maybe the user can delete old facet data.
     drupal_set_message(t("The server this index currently lies on doesn't support facets. " .
         'To use facets, you will have to move this index to a server supporting this feature.'), 'error');
-    if (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE index_id = :id', array(':id' => $index->machine_name))->fetchField()) {
+    if (search_api_facet_load(array('index_id' => $index->machine_name))) {
       $form['description'] = array(
         '#type' => 'item',
         '#title' => t('Delete facet settings'),
Index: contrib/search_api_facets/search_api_facets.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.info,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 search_api_facets.info
--- contrib/search_api_facets/search_api_facets.info	26 Sep 2010 18:00:36 -0000	1.1.2.4
+++ contrib/search_api_facets/search_api_facets.info	30 Nov 2010 12:45:43 -0000
@@ -3,9 +3,11 @@
 name = Search facets
 description = "Create facets for search queries executed via the Search API."
 dependencies[] = search_api
+dependencies[] = block
 core = 7.x
 package = Search
 
 files[] = search_api_facets.admin.inc
+files[] = search_api_facets.entity.inc
 files[] = search_api_facets.install
 files[] = search_api_facets.module
Index: contrib/search_api_facets/search_api_facets.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/contrib/search_api_facets/Attic/search_api_facets.module,v
retrieving revision 1.1.2.25
diff -u -r1.1.2.25 search_api_facets.module
--- contrib/search_api_facets/search_api_facets.module	7 Nov 2010 17:24:16 -0000	1.1.2.25
+++ contrib/search_api_facets/search_api_facets.module	30 Nov 2010 12:45:43 -0000
@@ -61,6 +61,28 @@
 }
 
 /**
+ * Implements hook_entity_info().
+ */
+function search_api_facets_entity_info() {
+  $info['search_api_facet'] = array(
+    'label' => t('Search facet'),
+    'controller class' => 'EntityAPIController',
+    'entity class' => 'SearchApiFacet',
+    'base table' => 'search_api_facet',
+    'uri callback' => 'search_api_facet_url',
+    'module' => 'search_api_facets',
+    'exportable' => TRUE,
+    'entity keys' => array(
+      'id' => 'id',
+      'name' => 'delta',
+      'label' => 'name',
+    ),
+  );
+
+  return $info;
+}
+
+/**
  * Implements hook_permission().
  */
 function search_api_facets_permission() {
@@ -410,19 +432,19 @@
 function search_api_facets_search_api_index_update(SearchApiIndex $index, $op = 'edit') {
   $id = $index->machine_name;
   if ($op == 'enable') {
-    if (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE index_id = :id', array(':id' => $id))->fetchField()) {
+    if (search_api_facet_load(array('index_id' => $id))) {
       drupal_set_message(t('There are saved facets for this index. ' .
           'Go to the <a href="!url">facet page</a> to enable them.',
           array('!url' => 'admin/config/search/search_api/index/' . $id . '/facets')));
     }
   }
   elseif ($op == 'disable') {
-    $count = db_update('search_api_facet')
-      ->fields(array('enabled' => 0))
-      ->condition('index_id', $id)
-      ->execute();
-    if ($count) {
-      drupal_set_message(t('The disabled index still had active facet blocks. They were deactivated.'), 'warning', FALSE);
+    foreach (search_api_facet_load(array('index_id' => $id, 'enabled' => TRUE)) as $facet) {
+      $facet->enabled = FALSE;
+      $facet->save();
+    }
+    if (isset($facet)) {
+      drupal_set_message(t('The disabled index still had active facet blocks. They were deactivated.'), 'status', FALSE);
     }
   }
   elseif ($op == 'fields') {
@@ -431,11 +453,11 @@
     foreach ($facets as $facet) {
       if (empty($fields[$facet->field]['indexed'])) {
         $facet->enabled = FALSE;
-        search_api_facet_save($facet);
+        $facet->save();
       }
       elseif (empty($facet->options['type']) || $facet->options['type'] != $fields[$facet->field]['type']) {
         _search_api_facets_refresh_type($facet, $facet->options);
-        search_api_facet_save($facet);
+        $facet->save();
       }
     }
   }
@@ -448,6 +470,9 @@
   db_delete('search_api_facet')
     ->condition('index_id', $index->machine_name)
     ->execute();
+  if (search_api_facet_load(array('index_id' => $index->machine_name))) {
+    drupal_set_message(t('A module defines default search facets for the deleted index. You should update the module code or disable it.'), 'warning');
+  }
 }
 
 /**
@@ -459,12 +484,22 @@
   foreach (search_api_facet_load(array('enabled' => 1)) as $facet) {
     if (empty($facet->options['type']) || $facet->options['type'] == 'options') {
       _search_api_facets_refresh_type($facet, $facet->options);
-      search_api_facet_save($facet);
+      $facet->save();
     }
   }
 }
 
 /**
+ * Entity uri callback.
+ */
+function search_api_facet_url(SearchApiFacet $facet) {
+  return array(
+    'path' => 'admin/structure/block/manage/search_api_facets/' . $facet->delta . '/configure',
+    'options' => array(),
+  );
+}
+
+/**
  * Load one or more facets. Either the block delta or an array of conditions
  * might be given.
  *
@@ -478,43 +513,36 @@
  *   If set to TRUE, the returned facets will be freshly loaded, instead of
  *   being loaded from the cache.
  *
- * @return stdClass
+ * @return SearchApiFacet
  *   Either the facet with the specified delta, or an array of facets matching
  *   the specified conditions, keyed by delta.
  */
 function search_api_facet_load($conditions = array(), $reset = FALSE) {
-  $cache = &drupal_static(__FUNCTION__, array());
-  $key = serialize($conditions);
-  if (!isset($cache[$key]) || $reset) {
-    if (is_scalar($conditions)) {
-      $delta = $conditions;
-      $f = db_query('SELECT * FROM {search_api_facet} WHERE delta = :delta', array(':delta' => $delta))->fetch();
-      if ($f) {
-        $f->options = $f->options ? unserialize($f->options) : array();
-      }
-      $cache[$key] = $f;
-    }
-    else {
-      $select = db_select('search_api_facet', 'f');
-      $select->fields('f');
-      foreach ($conditions as $field => $value) {
-        if ($field == 'b.status') {
-          global $theme;
-          $select->join('block', 'b', 'b.module = :module AND b.delta = f.delta AND b.theme = :theme AND b.status = :status',
-              array(':module' => 'search_api_facets', ':theme' => $theme, ':status' => $value));
-          continue;
-        }
-        $select->condition($field, $value);
-      }
-      $facets = $select->execute()->fetchAllAssoc('delta');
-      foreach ($facets as $facet) {
-        $facet->options = $facet->options ? unserialize($facet->options) : array();
-        $cache[serialize($facet->delta)] = $facet;
+  if (is_scalar($conditions)) {
+    $f = entity_load('search_api_facet', array($conditions), array(), $reset);
+    $f = $f ? reset($f) : FALSE;
+    return $f;
+  }
+  else {
+    $deltas = FALSE;
+    if (isset($conditions['b.status'])) {
+      $status = $conditions['b.status'];
+      unset($conditions['b.status']);
+      $cache = &drupal_static(__FUNCTION__, array());
+      if (!isset($cache[$status]) || $reset) {
+        global $theme;
+        $cache[$status] = db_select('block', 'b')
+        ->fields('b', array('delta'))
+        ->condition('module', 'search_api_facets')
+        ->condition('theme', $theme)
+        ->condition('status', $status)
+        ->execute()
+        ->fetchCol();
       }
-      $cache[$key] = $facets;
+      $deltas = $cache[$status];
     }
+    return entity_load('search_api_facet', $deltas, $conditions, $reset);
   }
-  return $cache[$key];
 }
 
 /**
@@ -523,47 +551,11 @@
  * or if $facet->is_new is set, the facet will be inserted. Otherwise, it will
  * be updated.
  *
- * @param stdClass $facet
+ * @param SearchApiFacet $facet
  *   The facet object to save.
  */
-function search_api_facet_save(stdClass $facet) {
-  // Always set default options.
-  $facet->options += array(
-    'limit'        => 10,
-    'min_count'    => 1,
-    'sort'         => 'count',
-    'missing'      => FALSE,
-    'default_true' => TRUE,
-    'ids_list'     => array(),
-    'type'         => '',
-  );
-  if (!empty($facet->delta) && empty($facet->is_new)) {
-    $enabled_changed = $facet->enabled != search_api_facet_load($facet->delta, TRUE)->enabled;
-    drupal_write_record('search_api_facet', $facet, 'delta');
-    if ($enabled_changed) {
-      block_flush_caches();
-      cache_clear_all('*', 'cache_block', TRUE);
-    }
-    return;
-  }
-  if (empty($facet->delta)) {
-    // Construct a delta using index id and field.
-    // We have to ensure that it's both unique and at most 32 characters long,
-    // leading to a bit of complexity.
-    $delta = substr($facet->index_id, 0, 20) . '_' . preg_replace('/\W+/', '_', $facet->field);
-    $base = $delta = substr($delta, 0, 32);
-    $i = 0;
-    while (db_query('SELECT COUNT(1) FROM {search_api_facet} WHERE delta = :delta', array(':delta' => $delta))->fetchField()) {
-      $suffix = '_' . ++$i;
-      $delta = substr($base, 0, 32 - strlen($suffix)) . $suffix;
-    }
-    $facet->delta = $delta;
-  }
-  drupal_write_record('search_api_facet', $facet);
-  if ($facet->enabled) {
-    block_flush_caches();
-    cache_clear_all('*', 'cache_block', TRUE);
-  }
+function search_api_facet_save(SearchApiFacet $facet) {
+  $facet->save();
 }
 
 /**
@@ -578,21 +570,23 @@
  */
 function search_api_facet_delete($conditions = array()) {
   if (is_scalar($conditions)) {
-    $delta = $conditions;
-    return db_delete('search_api_facet')
-      ->condition('delta', $delta)
-      ->execute() != 0;
-  }
-  $del = db_delete('search_api_facet');
-  foreach ($conditions as $field => $value) {
-    $del->condition($field, $value);
+    $facet = search_api_facet_load($conditions);
+    if (!$facet) {
+      return FALSE;
+    }
+    $facet->delete();
   }
-  if ($del->execute()) {
+  else {
+    foreach (search_api_facet_load($conditions) as $facet) {
+      $facet->delete(FALSE);
+    }
+    if (!isset($facet)) {
+      return FALSE;
+    }
     block_flush_caches();
     cache_clear_all('*', 'cache_block', TRUE);
-    return TRUE;
   }
-  return FALSE;
+  return TRUE;
 }
 
 /**
@@ -628,7 +622,7 @@
  * @param array $options
  *   An array for which 'type' and, if applicable, 'options' will be set.
  */
-function _search_api_facets_refresh_type(stdClass $facet, array &$options) {
+function _search_api_facets_refresh_type(SearchApiFacet $facet, array &$options) {
   $index = search_api_index_load($facet->index_id);
   $type = $index->options['fields'][$facet->field]['type'];
   $options['type'] = search_api_extract_inner_type($type);
Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/search_api/Attic/CHANGELOG.txt,v
retrieving revision 1.1.2.23
diff -u -r1.1.2.23 CHANGELOG.txt
--- CHANGELOG.txt	29 Nov 2010 22:17:24 -0000	1.1.2.23
+++ CHANGELOG.txt	30 Nov 2010 12:45:42 -0000
@@ -2,7 +2,8 @@
 
 Search API 1.0, dev (xx/xx/xxxx):
 ------------------------------------
-#984174: Bug in Index::prepareProcessors() when processors have not been set.
+- #963062: Make facets exportable.
+- #984174: Bug in Index::prepareProcessors() when processors have not been set.
 
 Search API 1.0, Beta 4 (11/29/2010):
 ------------------------------------
Index: contrib/search_api_facets/search_api_facets.entity.inc
===================================================================
RCS file: contrib/search_api_facets/search_api_facets.entity.inc
diff -N contrib/search_api_facets/search_api_facets.entity.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ contrib/search_api_facets/search_api_facets.entity.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,122 @@
+<?php
+// $Id$
+
+/**
+ * Class representing a search facet.
+ */
+class SearchApiFacet extends EntityExtendable {
+
+  /* Database values that will be set when object is loaded: */
+
+  /**
+   * @var integer
+   */
+  public $id = 0;
+
+  /**
+   * @var string
+   */
+  public $delta = '';
+
+  /**
+   * @var string
+   */
+  public $index_id = '';
+
+  /**
+   * @var string
+   */
+  public $field = '';
+
+  /**
+   * @var string
+   */
+  public $name = '';
+
+  /**
+   * @var integer
+   */
+  public $enabled = 1;
+
+  /**
+   * @var array
+   */
+  public $options = array();
+
+  /**
+   * Will be set by the Entity CRUD API for exporting.
+   */
+  public $module;
+  /**
+   * Will be set by the Entity CRUD API for exporting.
+   */
+  public $status;
+
+  /**
+   * Constructor as a helper to the parent constructor.
+   */
+  public function __construct(array $values = array()) {
+    parent::__construct($values, 'search_api_facet');
+  }
+
+  /**
+   * Saves this facet.
+   * If $this->delta is empty, it will be computed automatically. In this case,
+   * or if $this->is_new is set, the facet will be inserted. Otherwise, it will
+   * be updated.
+   */
+  public function save() {
+    // Always set default options.
+    $this->options += array(
+      'limit'        => 10,
+      'min_count'    => 1,
+      'sort'         => 'count',
+      'missing'      => FALSE,
+      'default_true' => TRUE,
+      'ids_list'     => array(),
+      'type'         => '',
+    );
+    if (!empty($this->id) && !empty($this->delta) && empty($this->is_new)) {
+      $enabled_changed = $this->enabled != search_api_facet_load($this->delta, TRUE)->enabled;
+      parent::save();
+      if ($enabled_changed) {
+        block_flush_caches();
+        cache_clear_all('*', 'cache_block', TRUE);
+      }
+      return;
+    }
+    if (empty($this->delta)) {
+      // Construct a delta using index id and field.
+      // We have to ensure that it's both unique and at most 32 characters long,
+      // leading to a bit of complexity.
+      $delta = substr($this->index_id, 0, 20) . '_' . preg_replace('/\W+/', '_', $this->field);
+      $base = $delta = substr($delta, 0, 32);
+      $i = 0;
+      while (search_api_facet_load($delta)) {
+        $suffix = '_' . ++$i;
+        $delta = substr($base, 0, 32 - strlen($suffix)) . $suffix;
+      }
+      $this->delta = $delta;
+    }
+    parent::save();
+    if ($this->enabled) {
+      block_flush_caches();
+      cache_clear_all('*', 'cache_block', TRUE);
+    }
+  }
+
+  /**
+   * Deletes this facet.
+   *
+   * @param $flush_block_caches
+   *   TRUE, if the block caches should be flushed after deleting the facet.
+   */
+  public function delete($flush_block_caches = TRUE) {
+    parent::delete();
+    if ($flush_block_caches) {
+      block_flush_caches();
+      cache_clear_all('*', 'cache_block', TRUE);
+    }
+  }
+
+}
