recipe_mastercook4.module in Recipe 6
recipe_mastercook4.module - Enables importing and exporting of MasterCook4 format recipes.
File
plugins/recipe_mastercook4.moduleView 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('⁄', '/', $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;
}