Experimental project

This is a sandbox project, which contains experimental code for developer use only.

The sandwich module provides an extendible way to inject arbitrary markup into the content of text fields. It can be used to inject banner ads into long texts or to place floating figures. Beside content injection the sandwich module is also capable removing or replacing parts of rendered markup.

In order to leverage the full potential of this module you need to be familiar with drupal theming. In its essence, sandwich implements hook_field_attach_view_alter, slices the content of text-fields according to configurable rules (e.g. on paragraph boundaries). Slicing methods are configurable separately for each field and view mode via the formatter settings.

The resulting array of strings is then rendered using a theming function. This opens up many possibilities for themes and third party modules to alter and augment the markup. The default implementation is shown below:

/**
 * Returns HTML for a sandwichable field.
 *
 * This is the default theme implementation to render sliced up field contents.
 * Theme developers who are comfortable with overriding theme functions may do
 * so in order to customize this markup. This function can be overridden with
 * varying levels of specificity. For example, for a field named 'body'
 * displayed on the 'article' content type, any of the following functions will
 * override this default implementation. The first of these functions that
 * exists is used:
 * - THEMENAME_sandwich__body__article()
 * - THEMENAME_sandwich__body()
 * - THEMENAME_sandwich()
 *
 * @param $variables
 *   An associative array containing:
 *   - slices: An array of strings derived from slicing up the markup of a
 *     field.
 *   - slices_field_name: A string containing the name of the field from which
 *     the sliced markup was derived from.
 *   - slices_field_delta: A number indicating the delta of the field value.
 *   - slice_prefix: An array of render arrays indexed by slice number of
 *     content which will be prepended to the specified slices. See detalied
 *     description below.
 *   - slice_suffix: An array of render arrays indexed by slice number of
 *     content which will be appended to the specified slices. See detailed
 *     description below.
 *   - slice_prefix_fields: An array of arrays containing field names indexed
 *     by slice number of fields which will be prepended to the specified
 *     slices.
 *   - slice_suffix_fields: An array of arrays containing field names indexed
 *     by slice number of fields which will be appended to the specified slice.
 *   - slicer: The function used to slice up the content as a string.
 *   - entity: The entity object this field belongs to.
 *   - entity_output: The structured content array tree for all of the entity's
 *     fields.
 *   - entity_type: The type of entity; for example, 'node' or 'user'.
 *   - bundle: The bundle of the entity; for example, 'article'. For entities
 *     without bundle this defaults to the entity type.
 *   - view_mode: View mode; for example, 'full' or 'teaser'.
 *   - display: An array of display settings, as found in the 'display' entry
 *     of instance definitions. See field_view_field for detailed information.
 *
 * In order to inject arbritrary markup into sliced field content, implement a
 * preprocess function following this pattern belowe. Note that if you implement
 * the function like this, the markup will be injected in every sliced field on
 * every entity type before the third slice. Note also that if there are less
 * than three slices, the markup will not be inserted.
 *
 *     function MYTHEME_preprocess_sandwich(&$variables) {
 *       // Insert banner tag before third slice.
 *       $variables['slice_prefix'][2]['mybanner'] = array(
 *         '#markup' => '<div>Put your banner tag here</div>',
 *       );
 *     }
 *
 * In the following example, the image field is always placed before the second
 * last slice:
 *
 *     function MYTHEME_preprocess_sandwich(&$variables) {
 *       // Insert field_image after the second last slice.
 *       $slice = max(0, count($variables['slices']) - 3);
 *       $variables['slice_suffix_fields'][$slice][] = 'field_image';
 *     }
 *
 * Tip: Examine slices_field_name, entity_type and bundle in order to restrict where
 * your custom code gets applied.
 *
 * @see template_preprocess_sandwich()
 * @see template_process_sandwich()
 *
 * @ingroup themeable
 */
function theme_sandwich($variables) {
  $result = '';

  // Iterate over slices
  foreach ($variables['slices'] as $i => $markup) {
    // Render injected fields and markup before the slice
    $result .= render(sandwich_build_injects($variables['slice_prefix'], $i));

    // Add the markup from the slide.
    $result .= $markup;

    // Render injected fields and markup after the slice
    $result .= render(sandwich_build_injects($variables['slice_suffix'], $i));
  }

  return $result;
}

In order to provide additional slicers, a module may define them by implementing hook_sandwich_slicer_info. The following excerpt shows the default implementation:

/**
 * Implementation of hook_sandwich_slicer_info().
 */
function hook_sandwich_slicer_info() {
  return array(
    'sandwich_slice_on_paragraphs' => array(
      'label' => t('Between paragraphs'),
      'description' => t('Slice up field content between all paragraphs'),
      'field_formatters' => array(
        'text_default' => array('#markup'),
        'text_trimmed' => array('#markup'),
        'text_summary_or_trimmed' => array('#markup'),
      ),
    ),
  );
}

/**
 * Callback for paragraph slicer
 */
function sandwich_slice_on_paragraphs($text) {
  return preg_split('/^(?=<p>)/m', $text, NULL, PREG_SPLIT_NO_EMPTY);
}

Project information