Update to Search API 1.18 causes HTML to be displayed as plaintext A security update to Search API (7.x-1.18) adds new sanitation as a security measure.  Great!  ..Except it now causes my custom field to be displayed as plaintext instead of HTML.

Here is the relevant code that was added to Search API:

/**
 * Sanitizes field values returned from the server.
 *
 * @param array $values
 *   The field values, as returned from the server. See
 *   SearchApiQueryInterface::execute() for documentation on the structure.
 *
 * @return array
 *   An associative array of field IDs mapped to their sanitized values (scalar
 *   or array-valued).
 */
function search_api_get_sanitized_field_values(array $values) {
  // Sanitize the field values returned from the server. Usually we use
  // check_plain(), but this can be overridden by setting the field value to
  // an array with "#value" and "#sanitize_callback" keys.
  foreach ($values as $field_id =&gt; $field_value) {
    if (is_array($field_value)
      &amp;&amp; isset($field_value['#sanitize_callback'])
      &amp;&amp; ($field_value['#sanitize_callback'] === FALSE || is_callable($field_value['#sanitize_callback']))
      &amp;&amp; array_key_exists('#value', $field_value)
    ) {
      $sanitize_callback = $field_value['#sanitize_callback'];
      $field_value = $field_value['#value'];
    }
    else {
      $sanitize_callback = 'check_plain';
    }
    if ($sanitize_callback !== FALSE) {
      $field_value = search_api_sanitize_field_value($field_value, $sanitize_callback);
    }
    $values[$field_id] = $field_value;
  }
  return $values;
}


Question

Because it says

  // Sanitize the field values returned from the server. Usually we use
  // check_plain(), but this can be overridden by setting the field value to
  // an array with "#value" and "#sanitize_callback" keys.


I know I need to set the field value to an array with #value (my custom HTML) and #sanitize_callback keys, but where do I set this, and what do I use for #sanitize_callback?

My Custom Code

I'm using a callback to generate my field with custom HTML:

&lt;?php

/**
 * @file
 * Contains SearchApiAlterAddViewedEntity.
 */

/**
 * Search API data alteration callback that adds an URL field for all items.
 */
class MySearchAlterAddUsercacheEn extends SearchApiAbstractAlterCallback {

  /**
   * Only support indexes containing entities.
   *
   * @see SearchApiAlterCallbackInterface::supportsIndex()
   */
  public function supportsIndex(SearchApiIndex $index) {
    return (bool) $index-&gt;getEntityType();
  }

  public function alterItems(array &amp;$items) {
    // Prevent session information from being saved while indexing.
    drupal_save_session(FALSE);

    // Force the current user to anonymous to prevent access bypass in search
    // indexes.
    $original_user = $GLOBALS['user'];
    $GLOBALS['user'] = drupal_anonymous_user();

    foreach ($items as &amp;$item) {
      // Since we can't really know what happens in entity_view() and render(),
      // we use try/catch. This will at least prevent some errors, even though
      // it's no protection against fatal errors and the like.
      try {
        $render = user_view($item, 'ensearch', 'en-US');
        $text = render($render);
        if (!$text) {
          $item-&gt;search_api_viewed_en = NULL;
          continue;
        }
        $item-&gt;search_api_viewed_en = $text;
      }
      catch (Exception $e) {
        $item-&gt;search_api_viewed_en = NULL;
      }
    }

    // Restore the user.
    $GLOBALS['user'] = $original_user;
    drupal_save_session(TRUE);
  }

  public function propertyInfo() {
    return array(
      'search_api_viewed_en' =&gt; array(
        'label' =&gt; t('Entity (en-US) HTML output'),
        'description' =&gt; t('The whole HTML content of the entity when viewed.'),
        'type' =&gt; 'text',
      ),
    );
  }

}
check_plain() encodes special characters that has special meaning in HTML (such as &lt; and &amp;) into plain text entities (i.e. &lt; and &amp; respectively) that will make these be rendered literally (not interpreted as HTML) when that string that is then displayed as part of a page with HTML-markup. 
filter_xss() filters an HTML string to prevent cross-site-scripting (XSS) vulnerabilities.

In other words, filter_xss() takes care of security, while not plain-texting HTML.

I propose you to patch the Search API function with 'filter_xss' as a callback instead of 'check_plain', create an issue on drupal.org and explan that case.