You are here

recipe_mastercook4.module in Recipe 6

recipe_mastercook4.module - Enables importing and exporting of MasterCook4 format recipes.

File

plugins/recipe_mastercook4.module
View source
<?php

/**
 * @file
 * recipe_mastercook4.module - Enables importing and exporting of MasterCook4 format recipes.
 */

/**
 * Implementation of hook_recipeio($type).
 */
function recipe_mastercook4_recipeio($type) {
  $supported = array(
    'import_single' => array(
      'format_name' => t('MasterCook4'),
      'callback' => 'recipe_mastercook4_import_single',
      'format_help' => '',
    ),
    'export_single' => array(
      'format_name' => t('MasterCook4'),
      'callback' => 'recipe_mastercook4_export_single',
      'format_help' => t('Export to a recipe to a MasterCook(1-4 .mxp) based text format.'),
    ),
    'export_multi' => array(
      'format_name' => t('MasterCook4'),
      'callback' => 'recipe_mastercook4_export_multi',
      'format_help' => t('Export all recipes to a MasterCook(1-4 .mxp) based text format.'),
    ),
    'import_multi' => array(
      'format_name' => t('MasterCook4'),
      'callback' => 'recipe_mastercook4_import_multi',
      'format_help' => t('Import recipes from a MasterCook(1-4 .mxp) based text file.'),
    ),
  );
  if (isset($supported[$type])) {
    return array(
      'mastercook4' => $supported[$type],
    );
  }
  else {
    return FALSE;
  }
}
function recipe_mastercook4_export_multi() {

  // you should not be able to export unpublished recipes
  $rs = db_query("SELECT n.nid from {node} n WHERE n.type='recipe' and n.status>0 ORDER BY n.title");
  $o = '';
  while ($row = db_fetch_object($rs)) {
    $o .= recipe_mastercook4_export_single($row->nid);
  }
  drupal_set_header('Content-type: text');
  return $o;
}
function recipe_mastercook4_export_single($nid = NULL) {
  if ($nid === NULL) {
    drupal_set_message(t('Recipe not found.'));
    drupal_not_found();
  }
  $node = node_load(array(
    'nid' => $nid,
    'type' => 'recipe',
  ));

  // you should not be able to export unpublished recipes
  if ($node->status == 0) {
    drupal_access_denied();
    return;
  }
  drupal_set_header('Content-type: text');
  return merge_template($node);
}
function merge_template($node = NULL) {

  //prepare prepare time
  $decimal_hours = $node->preptime / 60;
  $hours = floor($decimal_hours);
  $minutes = sprintf("%02d", floor(($decimal_hours - $hours) * 60));
  $preptime = "{$hours}:{$minutes}";

  //prepare categories
  $categories = '';
  $vocabs = taxonomy_get_vocabularies('recipe');
  foreach ($vocabs as $vocab) {
    $terms = taxonomy_node_get_terms_by_vocabulary($node, $vocab->vid);
    foreach ($terms as $term) {
      $term = array_shift($terms);
      $categories .= sprintf("%-33s", $term->name);
    }
  }
  $categories = wordwrap($categories, 66, "\n                ");

  //prepare ingredients
  $ingredients = '';
  foreach ($node->ingredients as $key => $i) {
    $ingredients .= format_mastercook4_ingredient($i);
  }

  // get the template string
  $template = get_template();

  // merge title
  $template = str_replace("<<title>>", $node->title, $template);

  // merge recipe by
  $template = str_replace("<<recipeby>>", $node->source, $template);

  // merge serving size
  $template = str_replace("<<servingsize>>", $node->yield, $template);

  // merge preptime
  $template = str_replace("<<preptime>>", $preptime, $template);

  // merge categories
  $template = str_replace("<<categories>>", $categories, $template);

  // merge ingredients
  $template = str_replace("<<ingredients>>", $ingredients, $template);

  // merge instructions
  $template = str_replace("<<instructions>>", strip_html_and_encode_entities($node->instructions), $template);

  // merge notes
  if ($node->notes != '') {
    $node->notes = "NOTES : " . strip_html_and_encode_entities($node->notes);
  }
  $template = str_replace("<<notes>>", $node->notes, $template);
  return $template;
}
function get_template() {
  $template = "\n                     *  Exported from  MasterCook  *\n\n<<title>>\n\nRecipe By     : <<recipeby>>\nServing Size  : <<servingsize>>   Preparation Time :<<preptime>>\nCategories    : <<categories>>\n\n  Amount  Measure       Ingredient -- Preparation Method\n--------  ------------  --------------------------------\n<<ingredients>>\n<<instructions>>\n\n                   - - - - - - - - - - - - - - - - - -\n\n<<notes>>\n";
  return $template;
}
function format_mastercook4_ingredient($ingredient = NULL) {
  $ingredient['quantity'] = recipe_ingredient_quantity_from_decimal($ingredient['quantity'], TRUE);

  // no html entities
  $ingredient['quantity'] = str_replace('&frasl;', '/', $ingredient['quantity']);
  $ingredient['unit_name'] = recipe_unit_name($ingredient['unit_id']);
  $fullingredient = strlen($ingredient['note']) > 0 ? $ingredient['name'] . ' -- ' . $ingredient['note'] : $ingredient['name'];
  $fullingredient = strip_html_and_encode_entities($fullingredient);
  $fullingredient = wordwrap($fullingredient, 66, "\n                        ");
  $o = sprintf("%8s  %-12s  %s\n", $ingredient['quantity'], $ingredient['unit_name'], $fullingredient);
  return $o;
}
function recipe_mastercook4_import_multi() {
  $o = drupal_get_form('recipe_mastercook4_import_form');
  return theme('page', $o);
}
function recipe_mastercook4_import_form($form_state) {
  $form = array();
  $form['#attributes'] = array(
    'enctype' => "multipart/form-data",
  );
  $form['recipe_import_file'] = array(
    '#type' => 'file',
    '#title' => t('MasterCook(1-4 .mxp) File'),
    '#default_value' => $object['foo'],
    '#size' => 64,
    '#description' => t("Note: This will add taxonomy terms to the lightest weight recipe taxonomy."),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  return $form;
}
function recipe_mastercook4_import_form_submit($form, &$form_state) {

  // save to a temp files
  if ($file = file_save_upload('recipe_import_file', array(), 'files', FILE_EXISTS_RENAME)) {
    drupal_set_message(t('The attached file was successfully uploaded'));
  }
  else {
    drupal_set_message(t('The attched file failed to upload.'), 'error');
    return;
  }
  if ($file) {

    // Load the file into a string.
    $fp = fopen($file->filepath, "r");
    if ($fp) {
      $recipe_txt = '';

      // Get the vocabs first.
      $vocabs = taxonomy_get_vocabularies('recipe');
      list($lightest_vid, $vocab) = each($vocabs);
      reset($vocabs);
      while (!feof($fp)) {
        $buf = fgets($fp);
        if (preg_match("/\\* +Exported +from/i", $buf)) {
          $recipe_txt = trim($recipe_txt);

          // Save recipe
          if (strlen($recipe_txt) > 0) {
            $parsed_recipe_object = recipe_mastercook4_import_single($recipe_txt);
            if (strlen($parsed_recipe_object['title']) > 0) {
              if (($node = recipe_import_get_node($parsed_recipe_object)) != FALSE) {

                // Save the taxonomy.
                foreach ($parsed_recipe_object['categories'] as $category) {

                  // Search the lightest weight recipe vocab for this term.
                  $term = recipe_get_term_by_name($category, $lightest_vid);

                  // You didn't find that term, so add it.
                  if ($term == FALSE && isset($lightest_vid)) {
                    $term = array(
                      'name' => $category,
                      'vid' => $lightest_vid,
                    );
                    drupal_set_message(t('Adding term %term_name', array(
                      '%term_name' => $category,
                    )));
                    taxonomy_save_term($term);

                    // Cast back to object so it's like the return value from recipe_get_term_by_name().
                    $term = (object) $term;
                  }

                  // You have the term now (existing or new), link it ink.
                  if (isset($term)) {
                    $node->taxonomy[] = $term->tid;
                  }
                }

                // Save the recipe.
                node_save($node);
              }
            }
          }

          // Clear recipe buffer.
          $recipe_txt = '';
        }
        $recipe_txt .= $buf;
      }
      fclose($fp);

      // Handle the last one needed.
      $parsed_recipe_object = recipe_mastercook4_import_single($recipe_txt);
      if (strlen($parsed_recipe_object['title']) > 0) {
        if (($node = recipe_import_get_node($parsed_recipe_object)) != FALSE) {
          node_save($node);
        }
      }
    }
  }
}
function recipe_mastercook4_import_single($recipe_txt = NULL) {

  // region constants
  define('HEAD', 0);
  define('TITLE', 1);
  define('META', 2);
  define('INGREDIENTS', 3);
  define('DIRECTIONS', 4);
  define('NOTES', 5);
  define('EOR', 6);
  $recipe = array(
    'title' => '',
    'categories' => array(),
    'ingredients' => array(),
    'instructions' => '',
    'notes' => '',
    'source' => '',
  );

  // A reference to the last ingredient added.
  $last_ingred_key = NULL;
  $region = HEAD;
  foreach (split("\n", $recipe_txt) as $line) {
    $trimmed_line = trim($line);

    // Head
    if ($region == HEAD) {

      // blank line in head section, move to next section.
      if ($trimmed_line == '') {
        $region++;
        continue;
      }
    }

    // Title
    if ($region == TITLE) {

      // Capture title.
      if ($trimmed_line != '') {
        $recipe['title'] = $trimmed_line;
      }
      else {

        // blank line in title section, move to next section.
        $region++;
        continue;
      }
    }
    if ($region == META) {

      // Get the source.
      if (preg_match('/Recipe +By *: *(.*)/i', $line, $matches)) {
        $recipe['source'] = $matches[1];
      }

      // Get the categories.
      if (preg_match('/Categories *: *(.*)/i', $line, $matches)) {
        $cat1 = trim(substr($matches[1], 0, 33));
        $cat2 = trim(substr($matches[1], 33, 33));
        if ($cat1 != '') {
          $recipe['categories'][] = $cat1;
        }
        if ($cat2 != '') {
          $recipe['categories'][] = $cat2;
        }
      }

      // Category continuation.
      if (count($recipe['categories']) > 0 && preg_match('/^ {16}(.*)/', $line, $matches)) {
        $cat1 = trim(substr($matches[1], 0, 33));
        $cat2 = trim(substr($matches[1], 33, 33));
        if ($cat1 != '') {
          $recipe['categories'][] = $cat1;
        }
        if ($cat2 != '') {
          $recipe['categories'][] = $cat2;
        }
      }

      // If you hit a blank line or the beginning ingredient header, move to next section.
      if ($trimmed_line == '' || preg_match('/Amount +Measure +Ingredient +-- +Preparation Method/i', $line)) {
        $region++;
        continue;
      }
    }
    if ($region == INGREDIENTS) {
      if (preg_match('/Amount +Measure +Ingredient +-- +Preparation Method/i', $line)) {

        // Do nothing.
      }
      elseif (preg_match('/-------- +------------ +--------------------------------/', $line)) {

        // Do nothing.
      }
      elseif ($trimmed_line != '') {
        $q = trim(substr($line, 0, 8));
        $u = trim(substr($line, 10, 12));
        $i = trim(substr($line, 24));

        // If you have an ingredient continuation, add note to previous ingredient.
        if ($q == '' && $u == '' && $last_ingred_key != NULL) {
          $recipe['ingredients'][$last_ingred_key]['ingredient_note'] .= ' ' . $i;

          // For non-ingredient continuation, add ingredient like normal.
        }
        else {
          $ing = array();
          $ing['quantity'] = $q;
          $ing['unit_name'] = $u;
          if (preg_match('/(.*?) ?-- ?(.*)/', $i, $matches)) {
            $ing['ingredient_name'] = $matches[1];
            $ing['ingredient_note'] = $matches[2];
          }
          else {
            $ing['ingredient_name'] = $i;
          }

          // FALSE if no-match
          $ing['unit_obj'] = recipe_unit_fuzzymatch($ing['unit_name']);

          // FALSE if no-match
          $ing['ingred_obj'] = recipe_ingredient_match($ing['ingredient_name']);
          $recipe['ingredients'][] = $ing;
          end($recipe['ingredients']);
          $last_ingred_key = key($recipe['ingredients']);
        }
      }
      else {

        // blank line in ingredient section, move to next section.
        $region++;
        continue;
      }
    }
    elseif ($region == DIRECTIONS) {
      if (preg_match('/- - - - - - - - - - - - - - - - - -/', $line)) {
        $region++;
        continue;
      }
      if (preg_match('/^Notes: +(.*)/i', $line, $matches)) {
        $recipe['notes'] .= $matches[1] . "\n";
        $region++;
        continue;
      }
      else {
        $recipe['instructions'] .= $line . "\n";
      }
    }
    elseif ($region == NOTES) {
      $recipe['notes'] .= $line . "\n";
    }
  }
  return $recipe;
}