recipe_mastercook4.module in Recipe 7.2
Enables importing and exporting of MasterCook4 format recipes.
File
modules/recipe_mastercook4.moduleView source
<?php
/**
* @file
* Enables importing and exporting of MasterCook4 format recipes.
*/
/**
* Implements hook_recipeio().
*/
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
$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_mastercook4_export_single($record->nid);
}
drupal_add_http_header('Content-type', 'text/plain; charset=utf-8');
print $o;
}
function recipe_mastercook4_export_single($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;
drupal_add_http_header('Content-type', 'text/plain; charset=utf-8');
return merge_template($node);
}
function merge_template($node) {
$categories = '';
//prepare ingredients
$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();
// get the template string
$template = get_template();
// merge title
$template = str_replace("<<title>>", filter_xss($node->title, array()), $template);
// merge recipe by
$source_items = field_get_items('node', $node, 'recipe_source');
$recipe_source = '';
if (!empty($source_items[0]['value'])) {
$recipe_source = strip_html_and_encode_entities($source_items[0]['value']);
}
$template = str_replace("<<recipeby>>", $recipe_source, $template);
// merge serving size
$template = str_replace("<<servingsize>>", $node->recipe_yield, $template);
// merge preptime
$prep_time_items = field_get_items('node', $node, 'recipe_prep_time');
$recipe_prep_time = '0:00';
if (!empty($prep_time_items[0]['value'])) {
$duration = $prep_time_items[0]['value'];
$hours = floor($duration / 60);
$minutes = sprintf("%02d", $duration % 60);
$recipe_prep_time = $hours . ':' . $minutes;
}
$template = str_replace("<<preptime>>", $recipe_prep_time, $template);
// merge categories
$template = str_replace("<<categories>>", $categories, $template);
// merge ingredients
$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 = '';
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('⁄', '/', $quantity);
}
else {
$quantity = ' ';
}
// 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'];
}
}
// Add the ingredient text to the output.
$recipe_ingredients .= recipe_mastercook4_format_ingredient($name, $quantity, $unit_display, $note);
}
$template = str_replace("<<ingredients>>", $recipe_ingredients, $template);
// merge instructions
$instructions_items = field_get_items('node', $node, 'recipe_instructions');
$recipe_instructions = '';
if (!empty($instructions_items[0]['value'])) {
$recipe_instructions = strip_html_and_encode_entities($instructions_items[0]['value']);
}
$template = str_replace("<<instructions>>", $recipe_instructions, $template);
// merge notes
$notes_items = field_get_items('node', $node, 'recipe_notes');
$recipe_notes = '';
if (!empty($notes_items[0]['value'])) {
$recipe_notes = 'NOTES : ' . strip_html_and_encode_entities($notes_items[0]['value']);
}
$template = str_replace("<<notes>>", $recipe_notes, $template);
$description_items = field_get_items('node', $node, 'recipe_description');
if (!empty($description_items[0]['value'])) {
$recipe_description = "DESCRIPTION : " . strip_html_and_encode_entities($description_items[0]['value']) . "\n\n";
$template = str_replace("<<description>>", $recipe_description, $template);
}
else {
$template = str_replace("<<description>>\n", "", $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<<description>>\n";
return $template;
}
/**
* Formats an ingredient for display as a MasterCook4 recipe.
*
* @param string $name
* The ingredient name.
* @param string $quantity
* The ingredient quantity.
* @param string $unit
* The units for the ingredient quantity.
* @param string $note
* The ingredient preparation note.
* @return string
* The formatted ingredient.
*/
function recipe_mastercook4_format_ingredient($name, $quantity = ' ', $unit = '', $note = '') {
// Concatenate the ingredient note to the name, if set.
if (!empty($note)) {
$name .= ' -- ' . $note;
}
$name = wordwrap($name, 66, "\n ");
$output = sprintf("%8s %-12s %s\n", $quantity, $unit, $name);
return $output;
}
function recipe_mastercook4_import_multi() {
$o = drupal_get_form('recipe_mastercook4_import_form');
return $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' => '',
'#size' => 64,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Import'),
);
return $form;
}
function recipe_mastercook4_import_form_submit($form, &$form_state) {
// save to a temp files
$data = array();
$validators = array(
'file_validate_extensions' => array(
'mxp xml',
),
);
if ($file = file_save_upload('recipe_import_file', $validators, FALSE, FILE_EXISTS_RENAME)) {
$data = file($file->uri);
drupal_set_message(t('The attached file was successfully uploaded'));
}
else {
drupal_set_message(t('The attched file failed to upload.'), 'error');
return;
}
$recipe_txt = '';
foreach ($data as $line) {
if (preg_match("/\\* +Exported +from/i", $line)) {
$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 recipe.
node_save($node);
}
}
}
// Clear recipe buffer.
$recipe_txt = '';
}
$recipe_txt .= $line;
}
// 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) {
// loose bad characters.
$recipe_txt = fixEncoding($recipe_txt);
// region constants
$reg = array(
'head' => 0,
'title' => 1,
'meta' => 2,
'ingredients' => 3,
'directions' => 4,
'notes' => 5,
'eor' => 6,
);
$recipe = array(
'title' => '',
'yield' => '1',
'yield_unit' => 'Servings',
'preptime' => 0,
'cooktime' => 0,
'categories' => array(),
'ingredients' => array(),
'instructions' => '',
'notes' => '',
'source' => '',
);
// A reference to the last ingredient added.
$last_ingred_key = NULL;
$region = $reg['head'];
$recipe_lines = explode("\n", $recipe_txt);
foreach ($recipe_lines as $line) {
$trimmed_line = trim($line);
// Head
if ($region == $reg['head']) {
// blank line in head section, move to next section.
if ($trimmed_line == '') {
$region++;
continue;
}
}
// Title
if ($region == $reg['title']) {
// Capture title.
if ($trimmed_line != '') {
$recipe['title'] = $trimmed_line;
}
else {
// blank line in title section, move to next section.
$region++;
continue;
}
}
if ($region == $reg['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;
}
}
// blank line in meta section, move to next section.
if ($trimmed_line == '' || preg_match('/Amount +Measure +Ingredient +-- +Preparation Method/i', $line)) {
$region++;
continue;
}
}
if ($region == $reg['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.
// Ingredient line continuation must start with a -- in the ingredient name position.
if ($q == '' && $u == '' && $last_ingred_key != NULL && preg_match('/^ *--/i', $i)) {
$recipe['ingredients'][$last_ingred_key]['ingredient_note'] .= ' ' . $i;
}
else {
$ing = array(
'ingredient_name' => '',
'ingredient_note' => '',
'quantity' => '',
'unit_key' => '',
);
$ing['quantity'] = recipe_ingredient_quantity_from_fraction($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;
}
$ing['unit_key'] = recipe_unit_fuzzymatch($ing['unit_name']);
if ($ing['unit_key'] == FALSE) {
$ing['ingredient_note'] = '!' . $ing['unit_name'] . ' ' . $ing['ingredient_note'];
$ing['unit_key'] = 'unit';
drupal_set_message(t('Could not find the ingredient units in %recipe (%line)', array(
'%recipe' => $recipe['title'],
'%line' => $line,
)), 'warning');
}
// Look up the ingredient, if it is not found it will be added later at node_save.
$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 == $reg['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 == $reg['notes']) {
$recipe['notes'] .= $line . "\n";
}
}
return $recipe;
}
Functions
Name | Description |
---|---|
get_template | |
merge_template | |
recipe_mastercook4_export_multi | |
recipe_mastercook4_export_single | |
recipe_mastercook4_format_ingredient | Formats an ingredient for display as a MasterCook4 recipe. |
recipe_mastercook4_import_form | |
recipe_mastercook4_import_form_submit | |
recipe_mastercook4_import_multi | |
recipe_mastercook4_import_single | |
recipe_mastercook4_recipeio | Implements hook_recipeio(). |