Commit 944a4aee authored by jvandervort's avatar jvandervort
Browse files

Resync with Drupal-6--1 branch.

parent 8dbd6b58
This diff is collapsed.
Installation
INSTALLATION
------------
1. Upload and install the module. Recipe includes optional
extra modules for Recipe Import and Export.
1. Copy this directory to the Drupal modules/ directory. Drupal will
automatically detect it. Then enable the module.
2. If you are upgrading from a previous version, run update.php.
2. Recommended, but not mandatory. Create a vocabulary (or several) for classifying recipes and associate recipe.module with that vocabulary
3. Adjust Permissions for user roles. NOTE: a 'site editor' role
is supported (see 'edit all recipes' permission).
3. Recommended, but not mandatory. Install the taxonomy_dhtml.module. You will get a nice taxonomy browser on your main recipe page. This
lets users browse all recipes for 'Fish' or 'Dessert' for example.
4. In Recipe Admin, enable/disable desired features.
5. Enable a Recipes menu item so users may find it...
Upgraders
---------
6. OPTIONAL: Create a taxonomy vocabulary and name it.
For example: 'Recipe Tags'.
If you are upgrading from a version with the Drupal 4.6 module database schema, eg. upgrading drupal from 4.6 to 4.7, or using khalid's 4.7 compatible recipe
module, then the upgrade procedure is as follows:
Under Content Types check 'Recipe', and under Settings check desired
options and Save.
1. Rename your recipe table to recipe_old
2. Uninstall the old recipe module, and issue this SQL statement:
DELETE FROM system WHERE filename='modules/recipe.module';
3. Install the 4.7 module
4. Under admin/menus make the 'import old db' menu option visible, and click
on it
5. The import to the new schema should now work, and you can remove the menu
option again.
Be sure to create at least one Term.
Please let marble (drupal@pebble.org.uk) know if you have any problems with
this procedure. I hope to make it easier in future releases. This procedure is
ONLY for the old database schema (with tables named recipe and
recipe_ingredients). If you have recipe, recipe_ingredient,
recipe_node_ingredient and recipe_unit, then any required updates should
happen automatically when you run drupal's update.php.
Note that this import is not perfect, and relies on parsing strings with no
fixed format. Even if it isn't able to parse out the quantity, unit and
ingredient name from your ingredients correctly, it should get something
which at least looks OK. It is strongly recommended that you try it out on a
backup copy of your database first.
In Menus, enable a link to Recipes so users may access the module.
Author
------
Moshe Weitzman <weitzman@tejasa.com>
7. OPTIONAL. Use CCK to extend the Recipe content type.. add images,
video, FiveStar ratings or voting, Flags, etc. Or just add
additional text fields for custom Recipe types...
Current maintainer
------------------
Ainsley Pereira <drupal@pebble.org.uk>
......@@ -4,61 +4,51 @@ CONTENTS OF THIS FILE
---------------------
* Introduction
* Installation
* Install
* TODO
* Database Information
INTRODUCTION
------------
Current Maintainers: brdwor, drawk, marble, and tzoscott
Current Maintainers: brdwor, drawk, marble, tzoscott and jvandervort.
Original Author: Moshe Weitzman <weitzman@tejasa.com>
Recipe is a module for sharing cooking recipes.
Recipe is a module for sharing cooking recipes.
INSTALLATION
------------
1. Upload and install the module.
2. Adjust Permissions for user roles. NOTE: a 'site editor' role is supported.
3. In Recipe Admin, enable/disable desired features.
4. Enable a Recipes menu item so users may find it...
5. OPTIONAL: Create a taxonomy vocabulary and name it.
For example: 'Recipe Tags'.
Under Content Types check 'Recipe', and under Settings check desired
options and Save.
Be sure to create at least one Term.
In Menus, enable a link to Recipes so users may access the module.
INSTALL
-------
See INSTALL file for important instructions.
TODO
-----
- Get ingredients into the searchable Index. Requires some SQL expertise.
NOTE: Some of these TODO items are old and predate the Drupal 6 architecture.
This means that some of these TODO items might be possible now, but have not
been tested or confirmed using other modules together with Recipe..
Some help testing/experimenting is appreciated.
- Make recipe_unit more translatable. This might mean moving units to an
included array file, or integration with one of the Drupal 'unit' modules.
- Get ingredients into the searchable Index. Requires some SQL expertise.
See recipe_update_index()
- emit recipeXML for syndicating recipes. Anyone know of a standard format?
- Let users maintain their own recipe collection just like a blog or
- Let users maintain their own recipe collection just like a blog or
personal image gallery
- Integrate with bookmarks.module so users may create a 'recipe box' listing
the favorite recipes
- Views2 support, including ingredients display.
- Views2 support - Views enabling, and custom handler for 'many' fields
(such as Ingredients).
- Investigate CCK Multigroup and Fields for D7.
DATABASE DESCRIPTION
--------------------
Data is saved in a quite normalized manner. Recipes are collections of
pointers to ingredients and to quantity terms. New terms can be added by
modifying the schema. New ingredients are added automatically whenever they
are used for the first time.
Data is saved in normal form. Recipes are collections of pointers to
ingredients and to quantity terms. New terms can be added by modifying the
schema. New ingredients are added automatically whenever they are used for
the first time.
Following is an ASCII art attempt to illustrate the DB relationships:
......
<?php
// $Id$
/**
* @file node-recipe.tpl.php
*/
?>
<div id="node-<?php print $node->nid; ?>" class="node<?php if ($sticky) { print ' sticky'; } ?><?php if (!$status) { print ' node-unpublished'; } ?> clear-block">
<?php print $picture ?>
<?php if (!$page): ?>
<h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>
<?php endif; ?>
<div class="meta">
<?php if ($submitted): ?>
<span class="submitted"><?php print $submitted ?></span>
<?php endif; ?>
<?php if ($terms): ?>
<div class="terms terms-inline"><?php print $terms ?></div>
<?php endif;?>
</div>
<div class="content">
<?php print $content ?>
</div>
<?php print $links; ?>
</div>
\ No newline at end of file
; $Id$
name = Recipe html
description = Enables importing and exporting of html format recipes.
dependencies[] = recipe
package = Recipe
core = 6.x
<?php
// $Id$
/**
* @file
* recipe_recipeML.module - Enables importing and exporting of recipeML format recipes.
*/
/**
* Implementation of hook_recipeio($type).
*/
function recipe_html_recipeio($type) {
$supported = array(
'export_single' => array(
'format_name' => t('HTML'),
'callback' => 'recipe_html_export_single',
'format_help' => t('Export to a printer friendly HTML format.'),
'access arguments' => 'access content', // everyone should be able to export HTML
)
);
if ( isset($supported[$type]) ) {
return array('reciphtml' => $supported[$type]);
}
else {
return FALSE;
}
}
/**
* Example implementation of hook_perm(). If you need special permissions for a format,
* use this and match the permission name to the access arguments above.
function recipe_html_perm() {
return array(t('export single'));
}
*/
function recipe_html_export_single($nid = NULL, $yield = NULL) {
if ( $nid === NULL ) {
drupal_set_message(t('Recipe not found.'));
drupal_not_found();
return;
}
$node = node_load(array('nid' => $nid, 'type' => 'recipe'));
// Set the custom yield so we can scale up/down the recipe quantities.
$node->custom_yield = $yield;
// Set yield_form_off to remove buttons.
$node->yield_form_off = 1;
// you should not be able to export unpublished recipes
if ( $node->status == 0 ) {
drupal_access_denied();
return;
}
// This calls other modules *_view hooks.
$node = node_build_content($node, FALSE, TRUE);
// Set the proper node part, then unset unused $node part so that a bad
// theme can not open a security hole.
$content = drupal_render($node->content);
if ($teaser) {
$node->teaser = $content;
unset($node->body);
}
else {
$node->body = $content;
unset($node->teaser);
}
// Allow modules to modify the fully-built node.
node_invoke_nodeapi($node, 'alter', $teaser, $page);
$html = theme('recipe_export_html_page', $node);
return $html;
}
/**
* Implementation of hook_theme().
*/
function recipe_html_theme() {
return array(
'recipe_export_html_page' => array(
'function' => 'theme_recipe_export_html_page',
'arguments' => array('node' => NULL),
),
'recipe_html_node' => array(
'arguments' => array('node' => NULL, 'teaser' => FALSE, 'page' => FALSE),
'template' => 'recipe_html_node',
),
);
}
/**
* How the recipe's HTML export should be themed.
*
* @ingroup themeable
*/
function theme_recipe_export_html_page($node = NULL) {
global $base_url;
$html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
$html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">';
$html .= "<head>\n<title>". check_plain($node->title) ."</title>\n";
$html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
$html .= '<base href="'. $base_url .'/" />'."\n";
$html .= "<style type=\"text/css\">\n@import url(". drupal_get_path('module', 'recipe') ."/recipe.css);\n</style>\n";
# 'title' is exception to "all recipe titles in recipe CSS namespace" rule
$html .= "</head>\n<body>\n". theme('recipe_html_node', $node, FALSE, TRUE) ."\n</body>\n</html>\n";
return $html;
}
function recipe_html_preprocess_recipe_html_node(&$variables) {
recipe_preprocess_node($variables);
}
\ No newline at end of file
<?php
// $Id$
/**
* @file node-recipe.tpl.php
*
* Theme implementation to display a recipe.
*/
?>
<div id="node-<?php print $node->nid; ?>" class="node clear-block">
<?php print $picture ?>
<h1><?php print $node->title ?></h1>
<?php if (isset($node->content['recipe_summary_box'])): ?>
<div class="recipe-summary">
<?php print $node->content['recipe_summary_box']['#value'] ?>
</div>
<?php endif;?>
<div class="recipe-description">
<?php print $node->content['recipe_description']['#value'] ?>
</div>
<div class="recipe-ingredients">
<?php print $node->content['recipe_ingredients']['#value'] ?>
</div>
<div class="recipe-instructions">
<?php print $node->content['recipe_instructions']['#value'] ?>
</div>
<?php if (isset($node->content['recipe_notes'])): ?>
<div class="recipe-notes">
<?php print $node->content['recipe_notes']['#value'] ?>
</div>
<?php endif;?>
<?php print $links; ?>
</div>
\ No newline at end of file
; $Id$
name = Recipe MasterCook4
description = Enables importing and exporting of the MasterCook4(.mxp) text format.
dependencies[] = recipe
package = Recipe
core = 6.x
<?php
// $Id$
/**
* @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 = "
* Exported from MasterCook *
<<title>>
Recipe By : <<recipeby>>
Serving Size : <<servingsize>> Preparation Time :<<preptime>>
Categories : <<categories>>
Amount Measure Ingredient -- Preparation Method
-------- ------------ --------------------------------
<<ingredients>>
<<instructions>>
- - - - - - - - - - - - - - - - - -
<<notes>>
";
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