You are here

recipe_plaintext.module in Recipe 7.2

File

modules/recipe_plaintext.module
View source
<?php

/**
 * @file
 * Support for the plaintext format input and output.
 */
$recipe_plaintext_example = '
Recipe Title
<--blank line-->
3 lb salt
4 T pepper
5-1/2 t oregano
<--blank line-->
Free-form instuctions.
Step 1.
Step 2.  Step3.
Whatever
';

/**
 * Implements hook_recipeio().
 */
function recipe_plaintext_recipeio($type) {
  global $recipe_plaintext_example;
  $supported = array(
    'import_single' => array(
      'format_name' => t('Plain Text'),
      'callback' => 'recipe_plaintext_import',
      'format_help' => '',
      'format_example' => "{$recipe_plaintext_example}",
    ),
    'export_single' => array(
      'format_name' => t('Plain Text'),
      'callback' => 'recipe_plaintext_export',
      'format_help' => t('Export to a plaintext format.'),
    ),
    'export_multi' => array(
      'format_name' => t('Plain Text'),
      'callback' => 'recipe_plaintext_export_multi',
      'format_help' => t('Export all recipes to a plaintext format.'),
    ),
  );
  if (isset($supported[$type])) {
    return array(
      'plaintext' => $supported[$type],
    );
  }
  else {
    return FALSE;
  }
}

/**
 * Parsing instance for plain text recipes
 */
function recipe_plaintext_import($recipe_txt = NULL) {

  // region constants
  define('TITLE', 0);
  define('INGREDIENTS', 1);
  define('DIRECTIONS', 2);
  $recipe = array(
    'title' => '',
    'yield' => '1',
    'yield_unit' => 'Servings',
    'preptime' => 0,
    'cooktime' => 0,
    'categories' => array(),
    'ingredients' => array(),
    'instructions' => '',
    'notes' => '',
    'source' => '',
  );
  $region = TITLE;
  $recipe_lines = explode("\n", $recipe_txt);
  foreach ($recipe_lines as $line) {
    $line = trim($line);
    if ($region < DIRECTIONS && $line == '') {
      $region++;
      continue;
    }
    if ($region == TITLE) {
      $recipe['title'] = $line;
    }
    elseif ($region == INGREDIENTS) {

      // Replace a decimal quantity with a fractional one.  Needs to be done before ' ' to '-' below.
      $line = preg_replace_callback('/^(\\d+\\.\\d+)/', 'decimal_to_fraction_callback', $line);

      // Replace a space separated fraction with a '-' to normalize the input string.
      $line = preg_replace('/^(\\d+)[ \\-](\\d+)[\\/](\\d+)/', '${1}-${2}/${3}', $line);

      // 1 cup flour
      if (preg_match('/^([0-9\\-\\.\\/]+) +([A-Za-z\\.]+) +(.*)/', $line, $matches)) {
        $i = array();
        $i['quantity'] = recipe_ingredient_quantity_from_fraction($matches[1]);
        $i['unit_name'] = $matches[2];
        $i['ingredient_name'] = $matches[3];
        $i['ingredient_note'] = '';
        $i['unit_key'] = recipe_unit_fuzzymatch($i['unit_name']);
        if ($i['unit_key'] == FALSE) {
          $i['ingredient_note'] = $i['unit_name'] . '! ' . $i['ingredient_note'];
          $i['unit_key'] = 'unit';
          drupal_set_message(t('Could not find the ingredient units in %recipe (%line)', array(
            '%recipe' => $recipe['title'],
            '%line' => $line,
          )), 'warning');
        }

        // FALSE if no-match
        $i['ingred_obj'] = recipe_ingredient_match($i['ingredient_name']);
        $recipe['ingredients'][] = $i;
      }
      elseif (preg_match('/^([0-9\\-\\.\\/]+) +(.*)/', $line, $matches)) {
        $i = array();
        $i['quantity'] = recipe_ingredient_quantity_from_fraction($matches[1]);
        $i['unit_name'] = 'Unit';
        $i['ingredient_name'] = $matches[2];
        $i['ingredient_note'] = '';
        $i['unit_key'] = recipe_unit_fuzzymatch($i['unit_name']);
        if ($i['unit_key'] == FALSE) {
          $i['ingredient_note'] = $i['unit_name'] . '! ' . $i['ingredient_note'];
          $i['unit_key'] = 'unit';
          drupal_set_message(t('Could not find the ingredient units in %recipe (%line)', array(
            '%recipe' => $recipe['title'],
            '%line' => $line,
          )), 'warning');
        }

        // FALSE if no-match
        $i['ingred_obj'] = recipe_ingredient_match($i['ingredient_name']);
        $recipe['ingredients'][] = $i;
      }
      else {
        $i = array();
        $i['quantity'] = 0;
        $i['unit_name'] = 'Unit';
        $i['ingredient_name'] = "failed: " . $line;
        $i['unit_key'] = FALSE;
        $i['ingred_obj'] = FALSE;
        $recipe['ingredients'][] = $i;
      }
    }
    elseif ($region == DIRECTIONS) {
      $recipe['instructions'] .= $line . "\n";
    }
  }
  return $recipe;
}
function recipe_plaintext_export_multi() {

  // you should not be able to export unpublished recipes
  $result = db_query("SELECT n.nid from {node} n WHERE n.type='recipe' and n.status>0 ORDER BY n.title");
  $o = '';
  foreach ($result as $record) {
    $o .= recipe_plaintext_export($record->nid);
    $o .= "\n====\n";
  }
  drupal_add_http_header('Content-type', 'text/plain; charset=utf-8');
  print $o;
}
function recipe_plaintext_export($nid = NULL, $yield = NULL) {
  if ($nid === NULL) {
    drupal_set_message(t('Recipe not found.'));
    drupal_not_found();
    return;
  }
  $node = node_load($nid);

  // you should not be able to export unpublished recipes
  if ($node->status == 0) {
    drupal_access_denied();
    return;
  }

  // Set the custom yield so we can scale up/down the recipe quantities.
  $node->recipe_custom_yield = $yield;
  $output = $node->title . "\n\n";
  $output .= t('Ingredients:') . "\n";
  $output .= recipe_plaintext_format_ingredients($node) . "\n";
  $output .= t('Instructions:') . "\n";
  $instructions_items = field_get_items('node', $node, 'recipe_instructions');
  $output .= wordwrap(strip_html_and_encode_entities($instructions_items[0]['value']), 75, "\n") . "\n\n";
  $output .= t('Description:') . "\n";
  $description_items = field_get_items('node', $node, 'recipe_description');
  $output .= strip_html_and_encode_entities($description_items[0]['value']) . "\n\n";
  $output .= t('Notes:') . "\n";
  $notes_items = field_get_items('node', $node, 'recipe_notes');
  $output .= strip_html_and_encode_entities($notes_items[0]['value']) . "\n";
  drupal_add_http_header('Content-type', 'text/plain; charset=utf-8');
  return $output;
}

/**
 * Formats an ingredient for display as a MasterCook4 recipe.
 *
 * @param stdClass $node
 *   The node being displayed.
 * @return string
 *   The formatted ingredients.
 */
function recipe_plaintext_format_ingredients($node = NULL) {
  $col_widths = array(
    'quantity' => 0,
    'unit' => 0,
  );

  //prepare ingredients factor
  $factor = 1;
  if (isset($node->recipe_custom_yield)) {
    $factor = $node->recipe_custom_yield / $node->recipe_yield;
    $node->recipe_yield = $node->recipe_custom_yield;
  }
  $unit_list = recipe_get_units();
  $ingredient_items = field_get_items('node', $node, 'recipe_ingredient');
  $ingredient_instance = field_read_instance('node', 'recipe_ingredient', 'recipe');
  $quantity_format = $ingredient_instance['display']['default']['settings']['fraction_format'];
  $abbreviation_setting = $ingredient_instance['display']['default']['settings']['unit_abbreviation'];
  $recipe_ingredients = array();
  foreach ($ingredient_items as $item) {

    // Load the ingredient so we can print its name.
    $ingredient = recipe_load_ingredient($item['iid']);
    $item['name'] = $ingredient->name;

    // Sanitize the name and note.
    $name = filter_xss($item['name'], array());
    $note = filter_xss($item['note'], array());

    // Format the ingredient quantity.
    if ($item['quantity'] > 0) {
      $quantity = recipe_ingredient_quantity_from_decimal($item['quantity'] * $factor, $quantity_format, TRUE);
      $quantity = str_replace('&frasl;', '/', $quantity);
    }
    else {
      $quantity = ' ';
    }

    // Collect column widths.
    if (strlen($quantity) > $col_widths['quantity']) {
      $col_widths['quantity'] = strlen($quantity);
    }

    // Concatenate the ingredient note to the name, if set.
    if (!empty($note)) {
      $name .= ' (' . $note . ')';
    }

    // Print the unit unless it has no abbreviation. Those units do not get
    // printed in any case.
    $unit_display = '';
    $item['unit'] = isset($unit_list[$item['unit_key']]) ? $unit_list[$item['unit_key']] : array();
    if (!empty($item['unit']['abbreviation'])) {

      // Print the abbreviation if recipe_unit_display == 0.
      if ($abbreviation_setting == 0) {
        $unit_display = $item['unit']['abbreviation'];
      }
      else {
        $unit_display = $item['quantity'] > 1 ? $item['unit']['plural'] : $item['unit']['name'];
      }
      if (strlen($unit_display) > $col_widths['unit']) {
        $col_widths['unit'] = strlen($unit_display);
      }
    }
    $recipe_ingredients[] = array(
      'quantity' => $quantity,
      'unit' => $unit_display,
      'name' => $name,
    );
  }

  // Render output with the correct column padding.
  $output = '';
  $wrap_pad = str_repeat(" ", $col_widths['unit'] + $col_widths['quantity'] + 2);
  foreach ($recipe_ingredients as $ingredient) {

    // Format the ingredient display.
    $ingredient_display = sprintf("%" . $col_widths['quantity'] . "s %-" . $col_widths['unit'] . "s %s\n", $ingredient['quantity'], $ingredient['unit'], $ingredient['name']);
    $output .= wordwrap($ingredient_display, 75, "\n{$wrap_pad}");
  }
  return $output;
}
function decimal_to_fraction_callback($matches) {
  $fraction_str = recipe_ingredient_quantity_from_decimal($matches[1], TRUE);

  // Don't want the frasl hassle.
  $fraction_str = preg_replace('/\\&frasl;/', '/', $fraction_str);
  return $fraction_str;
}

Functions

Namesort descending Description
decimal_to_fraction_callback
recipe_plaintext_export
recipe_plaintext_export_multi
recipe_plaintext_format_ingredients Formats an ingredient for display as a MasterCook4 recipe.
recipe_plaintext_import Parsing instance for plain text recipes
recipe_plaintext_recipeio Implements hook_recipeio().