recipe.module in Recipe 5
Same filename and directory in other branches
recipe.module - share recipes for drupal 5.x
@todo
File
recipe.moduleView source
<?php
/**
* @file
* recipe.module - share recipes
* for drupal 5.x
*
* @todo
*/
/**
* Implementation of hook_perm().
*/
function recipe_perm() {
return array(
t('create recipes'),
t('edit own recipes'),
);
}
/**
* Implementation of hook_load().
*/
function recipe_load($node) {
$recipe = db_fetch_object(db_query('SELECT * FROM {recipe} WHERE nid = %d', $node->nid));
$recipe->ingredients = recipe_load_ingredients($node);
return $recipe;
}
/**
* Implementation of hook_link().
*/
function recipe_link($type, $node = NULL, $teaser = FALSE) {
$links = array();
if ($type == 'node' && $node->type == 'recipe') {
if (!$teaser) {
if (variable_get('recipe_export_recipeml_enable', 1) == 1) {
$links['recipe_recipeml'] = array(
'title' => t('Export to RecipeML'),
'href' => "recipe/export/recipeml/{$node->nid}",
'attributes' => array(
'title' => t('Export this recipe to RecipeML.'),
),
);
}
if (variable_get('recipe_export_html_enable', 1) == 1) {
$links['recipe_html'] = array(
'title' => t('Printer-friendly version'),
'href' => "recipe/export/html/{$node->nid}",
'attributes' => array(
'title' => t('Show a printer-friendly version of this recipe.'),
),
);
}
}
}
return $links;
}
/**
* Implementation of hook_node_info(). This function replaces hook_node_name()
* and hook_node_types() from 4.6.
*/
function recipe_node_info() {
return array(
'recipe' => array(
'name' => t('Recipe'),
'module' => 'recipe',
'description' => t("Share your favorite recipes with your fellow cooks."),
),
);
}
/**
* Implementation of hook_help().
*/
function recipe_help($section) {
switch ($section) {
case 'node/add/recipe':
return variable_get("recipe_help", "");
}
}
/**
* Implementation of hook_insert().
*
* As a new node is being inserted into the database, we need to do our own
* database inserts.
*/
function recipe_insert($node) {
db_query("INSERT INTO {recipe} (nid, source, yield, notes, instructions, preptime) VALUES (%d, '%s', '%s', '%s', '%s', '%d')", $node->nid, $node->source, $node->yield, $node->notes, $node->instructions, $node->preptime);
recipe_save_ingredients($node);
}
/**
* Implementation of hook_update().
*
* As an existing node is being updated in the database, we need to do our own
* database updates.
*/
function recipe_update($node) {
db_query("UPDATE {recipe} SET source = '%s', yield = '%s', notes = '%s', instructions = '%s', preptime = '%d' WHERE nid = %d", $node->source, $node->yield, $node->notes, $node->instructions, $node->preptime, $node->nid);
recipe_save_ingredients($node);
}
/**
* Implementation of hook_delete().
*
* When a node is deleted, we need to clean up related tables.
*/
function recipe_delete($node) {
db_query("DELETE FROM {recipe} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {recipe_node_ingredient} WHERE nid = %d", $node->nid);
}
/**
* Implementation of hook_form().
*/
function recipe_form(&$node) {
// drupal 4.7 requires the title field to be defined by the custom node's module
$form['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
'#default_value' => $node->title,
);
// Now we define the form elements specific to our node type.
$form['body'] = array(
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => $node->body,
'#cols' => 60,
'#rows' => 2,
'#description' => t('A short description or "teaser" for the recipe.'),
'#required' => TRUE,
);
$form['yield'] = array(
'#type' => 'textfield',
'#title' => t('Yield'),
'#default_value' => $node->yield,
'#size' => 10,
'#maxlength' => 10,
'#description' => t('The number of servings the recipe will make.'),
'#attributes' => NULL,
'#required' => TRUE,
);
$form['preptime'] = array(
'#type' => 'select',
'#title' => t("Preparation time"),
'#default_value' => $node->preptime,
'#options' => array(
5 => t('5 minutes'),
10 => t('10 minutes'),
15 => t('15 minutes'),
20 => t('20 minutes'),
30 => t('30 minutes'),
45 => t('45 minutes'),
60 => t('1 hour'),
90 => t('1 1/2 hours'),
120 => t('2 hours'),
150 => t('2 1/2 hours'),
180 => t('3 hours'),
210 => t('3 1/2 hours'),
240 => t('4 hours'),
300 => t('5 hours'),
360 => t('6 hours'),
),
'#description' => t("How long does this recipe take to prepare (i.e. elapsed time)"),
);
$form["source"] = array(
'#type' => 'textfield',
'#title' => t("Source"),
'#default_value' => $node->source,
'#size' => 60,
'#maxlength' => 127,
'#description' => t("Optional. Does anyone else deserve credit for this recipe?"),
);
// Table of existing ingredients
$form['ingredients']['#tree'] = TRUE;
$system = variable_get('recipe_ingredient_system', 'complex');
if ($system == 'complex') {
$form['ingredients']['headings'] = array(
'#value' => '<div><table ><thead><tr><th>' . t('Quantity') . '</th><th>' . t('Units') . '</th><th>' . t('Ingredient Name') . '</th></tr></thead>' . "\n" . '<tbody>',
);
}
else {
$form['ingredients']['headings'] = array(
'#value' => '<div><table ><thead><tr><th>' . t('Ingredients') . '</th></tr></thead>' . "\n" . '<tbody>',
);
}
$rows = array();
$callback = 'recipe/ingredient/autocomplete';
$num_ingredients = 0;
if ($node->ingredients) {
foreach ($node->ingredients as $id => $ingredient) {
$num_ingredients = $id + 1;
if ($id == 0) {
$j = '0';
}
else {
$j = $id;
}
if ($ingredient->name && isset($ingredient->quantity)) {
// When can the following statement be true?
if (!$ingredient) {
drupal_set_message(t('Recipe Module: An error has occured. Please report this error to the system administrator.'), 'error');
$ingredient->quantity = '';
$ingredient->unit_id = 21;
$ingredient->name = '';
}
if ($system == 'complex') {
$form['ingredients'][$j]['open_tags'] = array(
'#value' => '<tr><th>',
);
$form['ingredients'][$j]['quantity'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => preg_replace('/\\⁄/', '/', recipe_ingredient_quantity_from_decimal($ingredient->quantity)),
'#size' => 8,
'#maxlength' => 8,
);
$form['ingredients'][$j]['mid1_tags'] = array(
'#value' => '</th><th>',
);
$form['ingredients'][$j]['unit_id'] = array(
'#type' => 'select',
'#title' => '',
'#default_value' => $ingredient->unit_id,
'#options' => recipe_unit_options(),
);
$form['ingredients'][$j]['mid2_tags'] = array(
'#value' => '</th><th>',
);
$form['ingredients'][$j]['name'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $ingredient->name,
'#size' => 64,
'#maxlength' => 128,
'#autocomplete_path' => $callback,
);
$form['ingredients'][$j]['close_tags'] = array(
'#value' => '</th></tr>',
);
}
else {
if ($ingredient->name) {
if ($ingredient->quantity == 0) {
$ingredient->quantity = '';
}
else {
$ingredient->quantity .= ' ';
}
if ($ingredient->abbreviation != '') {
$ingredient->abbreviation .= ' ';
}
$ingredient->name = $ingredient->quantity . $ingredient->abbreviation . $ingredient->name;
}
$form['ingredients'][$j]['open_tags'] = array(
'#value' => '<tr><th>',
'#tree' => TRUE,
);
$form['ingredients'][$j]['name'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $ingredient->name,
'#size' => 64,
'#maxlength' => 128,
'#autocomplete_path' => $callback,
);
$form['ingredients'][$j]['close_tags'] = array(
'#value' => '</th></tr>',
);
}
// else
}
// if ($ingredient->name && isset($ingredient->quantity))
}
// foreach ($node->ingredients as $id => $ingredient)
}
// if ($node->ingredients)
// Add ten more spots for ingredients than are already used
for ($i = $num_ingredients; $i < $num_ingredients + 10; $i++) {
if ($i == 0) {
$j = '0';
}
else {
$j = $i;
}
if ($system == 'complex') {
$form['ingredients'][$j]['open_tags'] = array(
'#value' => '<tr><th>',
);
$form['ingredients'][$j]['quantity'] = array(
'#type' => 'textfield',
'#title' => '',
'#size' => 8,
'#maxlength' => 8,
);
$form['ingredients'][$j]['mid1_tags'] = array(
'#value' => '</th><th>',
);
$form['ingredients'][$j]['unit_id'] = array(
'#type' => 'select',
'#title' => '',
'#options' => recipe_unit_options(),
'#default_value' => 2,
);
$form['ingredients'][$j]['mid2_tags'] = array(
'#value' => '</th><th>',
);
$form['ingredients'][$j]['name'] = array(
'#type' => 'textfield',
'#title' => '',
'#size' => 64,
'#maxlength' => 128,
'#autocomplete_path' => $callback,
);
$form['ingredients'][$j]['close_tags'] = array(
'#value' => '</th></tr>',
);
}
else {
$form['ingredients'][$j]['open_tags'] = array(
'#value' => '<tr><th>',
);
$form['ingredients'][$j]['name'] = array(
'#type' => 'textfield',
'#title' => '',
'#size' => 64,
'#maxlength' => 128,
'#autocomplete_path' => $callback,
);
$form['ingredients'][$j]['close_tags'] = array(
'#value' => '</th></tr>',
);
}
}
$form['ingredients']['end'] = array(
'#value' => '</tbody>' . "\n" . '</table></div>' . "\n",
);
$form['instructions'] = array(
'#type' => 'textarea',
'#title' => t('Instructions'),
'#default_value' => $node->instructions,
'#cols' => 60,
'#rows' => 10,
'#description' => t('Step by step instructions on how to prepare and cook the recipe.'),
);
$form['notes'] = array(
'#type' => 'textarea',
'#title' => t("Additional Notes"),
'#default_value' => $node->notes,
'#cols' => 60,
'#rows' => 5,
'#description' => t("Optional. Describe a great dining experience relating to this recipe, or note which wine or other dishes complement this recipe"),
);
$form['filter'] = filter_form($node->format);
return $form;
}
/**
* Settings form for menu callback
*/
function recipe_admin_settings() {
$form['recipe_index_depth'] = array(
'#type' => 'select',
'#title' => t('Index Depth'),
'#default_value' => variable_get('recipe_index_depth', 0),
'#options' => array(
0 => t('All Terms'),
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
),
'#description' => t("Defines how many levels of terms should be displayed on any given recipe index page. For example, if you select 1 then only one level of the Recipe index tree will be displayed at a time."),
);
$form['recipe_recent_box_enable'] = array(
'#type' => 'radios',
'#title' => t('Recent Recipes Box'),
'#default_value' => variable_get('recipe_recent_box_enable', 1),
'#options' => array(
t('Disabled'),
t('Enabled'),
),
'#description' => t('Enables or Disables the recent recipes box on the recipes index page.'),
'#required' => false,
);
$form['recipe_recent_box_title'] = array(
'#type' => 'textfield',
'#title' => t('Box Title'),
'#default_value' => variable_get('recipe_recent_box_title', t('Latest Recipes')),
'#size' => 35,
'#maxlength' => 255,
'#description' => t('Title of the Recent Recipes Box on the Recipes index page.'),
);
$form['recipe_recent_display'] = array(
'#type' => 'select',
'#title' => t('Recipes to Display'),
'#default_value' => variable_get('recipe_recent_display', '5'),
'#options' => array(
5 => "5",
10 => "10",
15 => "15",
),
'#description' => t("Sets the number of recent recipes that will be displayed in the Recent Recipes box. (0 = not displayed)."),
);
$form['recipe_help'] = array(
'#type' => 'textarea',
'#title' => t('Explanation or submission guidelines'),
'#default_value' => variable_get('recipe_help', ''),
'#cols' => 55,
'#rows' => 4,
'#description' => t('This text will be displayed at the top of the recipe submission form. Useful for helping or instructing your users.'),
);
$options = array(
'simple' => t('Simple'),
'complex' => t('Complex'),
);
$form['recipe_ingredient_system'] = array(
'#type' => 'radios',
'#title' => t('Ingredient entering system'),
'#default_value' => variable_get('recipe_ingredient_system', 'complex'),
'#options' => $options,
'#description' => t('The simple ingredient system allows all ingredients to be entered on one line. The complex system forces the user to seperate the quanity and units from the ingredient'),
);
$form['recipe_fraction_display'] = array(
'#type' => 'textfield',
'#title' => t('Fractions Display String'),
'#default_value' => variable_get('recipe_fraction_display', t('{%d }%d⁄%d')),
'#size' => 35,
'#maxlength' => 255,
'#description' => t('How fractions should be displayed. Leave blank to display as decimals. Each incidence of %d will be replaced by the whole number, the numerator, and the denominator in that order. Anything between curly braces will not be displayed when the whole number is equal to 0. Recommended settings are "{%d }%d&frasl;%d" or "{%d }<sup>%d</sup>/<sub>%d</sub>"'),
);
$form['recipe_export_html_enable'] = array(
'#type' => 'radios',
'#title' => t('Export HTML'),
'#default_value' => variable_get('recipe_export_html_enable', 1),
'#options' => array(
t('Disabled'),
t('Enabled'),
),
'#description' => t('Enables or Disables the Export as HTML link.'),
'#required' => false,
);
$form['recipe_export_recipeml_enable'] = array(
'#type' => 'radios',
'#title' => t('Export RecipeML'),
'#default_value' => variable_get('recipe_export_recipeml_enable', 1),
'#options' => array(
t('Disabled'),
t('Enabled'),
),
'#description' => t('Enables or Disables the Export as RecipeML link.'),
'#required' => false,
);
return system_settings_form($form);
}
/**
* Implementation of hook_menu().
*
* Note: when editing this function you must visit 'admin/menu' to reset the cache
*/
function recipe_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'node/add/recipe',
'title' => t('Recipe'),
'access' => user_access('create recipes'),
);
$items[] = array(
'path' => 'recipe',
'title' => t('Recipes'),
'callback' => 'recipe_page',
'access' => user_access('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
$items[] = array(
'path' => 'recipe/ingredient/autocomplete',
'title' => t('Ingredient autocomplete'),
'callback' => 'recipe_autocomplete_page',
'type' => MENU_CALLBACK,
'access' => user_access('access content'),
);
$items[] = array(
'path' => 'recipe/export',
'callback' => 'recipe_export',
'type' => MENU_CALLBACK,
'access' => user_access('access content'),
);
$items[] = array(
'path' => 'recipe/importolddb',
'title' => t('import old recipes'),
'callback' => 'recipe_import_from_46',
'type' => MENU_SUGGESTED_ITEM,
'access' => user_access('create recipes'),
);
$items[] = array(
'path' => 'admin/settings/recipe',
'title' => t('Recipe module'),
'description' => t('Settings that control how the recipe module functions.'),
'callback' => 'drupal_get_form',
'callback arguments' => 'recipe_admin_settings',
'access' => user_access('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
);
}
return $items;
}
/**
* Implementation of hook_access().
*/
function recipe_access($op, $node) {
global $user;
if ($op == 'create') {
// Only users with permission to do so may create this node type.
return user_access('create recipes');
}
// Users who create a node may edit or delete it later, assuming they have the
// necessary permissions.
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own recipes') && $user->uid == $node->uid) {
return TRUE;
}
}
}
/**
* Implementation of hook_block().
*/
function recipe_block($op = 'list', $delta = 0, $edit = array()) {
// The $op parameter determines what piece of information is being requested.
switch ($op) {
case 'list':
// If $op is "list", we just need to return a list of block descriptions.
// This is used to provide a list of possible blocks to the administrator,
// end users will not see these descriptions.
$blocks[0]['info'] = t('Newest recipes');
return $blocks;
case 'view':
// If $op is "view", then we need to generate the block for display
// purposes. The $delta parameter tells us which block is being requested.
switch ($delta) {
case 0:
// The subject is displayed at the top of the block. Note that it
// should be passed through t() for translation.
$block['subject'] = t('Newest Recipes');
// The content of the block is typically generated by calling a custom
// function.
$result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title, n.uid, u.name FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid WHERE n.type='recipe' AND n.status =1 ORDER BY n.created DESC"), 0, 5);
$block["content"] = node_title_list($result);
break;
}
return $block;
}
}
/**
* Implementation of hook_view().
*/
function recipe_view(&$node, $teaser = FALSE, $page = FALSE) {
if ($page) {
drupal_set_breadcrumb(array(
l(t('Home'), ''),
l(t('Recipes'), 'recipe'),
));
drupal_add_css(drupal_get_path('module', 'recipe') . '/recipe.css');
}
$node = recipe_node_prepare($node, $teaser);
$node->content['body'] = array(
'#value' => $teaser ? $node->teaser : theme('node_recipe', $node, $page),
'#weight' => 1,
);
return $node;
}
/**
* Returns a cached array of recipe unit types
*/
function recipe_unit_options() {
static $options;
static $unit_rs;
if (!isset($unit_rs)) {
$unit_rs = db_query('SELECT id,type,name,abbreviation FROM {recipe_unit} ORDER BY type ASC, metric');
$options = array();
while ($r = db_fetch_object($unit_rs)) {
if (isset($r->type)) {
if (!isset($options[$r->type])) {
$options[$r->type] = array();
}
$options[$r->type][$r->id] = $r->name . ' (' . $r->abbreviation . ')';
}
else {
$options[$r->id] = $r->name . ' (' . $r->abbreviation . ')';
}
}
}
return $options;
}
/**
* Converts a recipe ingredient name to and ID
*/
function recipe_ingredient_id_from_name($name) {
static $cache;
if (!$cache[$name]) {
$ingredient_id = db_result(db_query("SELECT id FROM {recipe_ingredient} WHERE LOWER(name)='%s'", trim(strtolower($name))));
if (!$ingredient_id) {
global $active_db;
$node_link = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'", $name));
db_query("INSERT INTO {recipe_ingredient} (name, link) VALUES ('%s', '%s')", $name, $node_link);
$ingredient_id = db_result(db_query("SELECT id FROM {recipe_ingredient} WHERE LOWER(name)='%s'", trim(strtolower($name))));
}
$cache[$name] = $ingredient_id;
}
return $cache[$name];
}
/**
* Converts an ingredient's quantity from decimal to fraction
*/
function recipe_ingredient_quantity_from_decimal($ingredient_quantity) {
if (strpos($ingredient_quantity, '.') && variable_get('recipe_fraction_display', t('{%d} %d⁄%d'))) {
$decimal = $ingredient_quantity;
if ($decimal == 0) {
$whole = 0;
$numerator = 0;
$denominator = 1;
$top_heavy = 0;
}
else {
$sign = 1;
if ($decimal < 0) {
$sign = -1;
}
}
if (floor(abs($decimal)) == 0) {
$whole = 0;
$conversion = abs($decimal);
}
else {
$whole = floor(abs($decimal));
$conversion = abs($decimal);
}
$power = 1;
$flag = 0;
while ($flag == 0) {
$argument = $conversion * $power;
if ($argument == floor($argument)) {
$flag = 1;
}
else {
$power = $power * 10;
}
}
// workaround for thirds, sixths, ninths, twelfths
$overrides = array(
'3333' => array(
1,
3,
),
'6666' => array(
2,
3,
),
'9999' => array(
3,
3,
),
// thirds
'1666' => array(
1,
6,
),
'8333' => array(
5,
6,
),
// sixths
'1111' => array(
1,
9,
),
'2222' => array(
2,
9,
),
'4444' => array(
4,
9,
),
'5555' => array(
5,
9,
),
'7777' => array(
7,
9,
),
'8888' => array(
8,
9,
),
// ninths
'0833' => array(
1,
12,
),
'4166' => array(
5,
12,
),
'5833' => array(
7,
12,
),
'9166' => array(
11,
12,
),
);
$conversionstr = substr((string) ($conversion - floor($conversion)), 2, 4);
if (array_key_exists($conversionstr, $overrides)) {
if ($overrides[$conversionstr][0] == $overrides[$conversionstr][1]) {
return ($whole + 1) * $sign;
}
$denominator = $overrides[$conversionstr][1];
$numerator = floor($conversion) * $denominator + $overrides[$conversionstr][0];
}
else {
$numerator = $conversion * $power;
$denominator = $power;
}
$hcf = recipe_euclid($numerator, $denominator);
$numerator = $numerator / $hcf;
$denominator = $denominator / $hcf;
$whole = $sign * $whole;
$top_heavy = $sign * $numerator;
$numerator = abs($top_heavy) - abs($whole) * $denominator;
if ($whole == 0 && $sign == -1) {
$numerator = $numerator * $sign;
}
$ingredient_quantity = sprintf(variable_get('recipe_fraction_display', t('{%d} %d⁄%d')), $whole, $numerator, $denominator);
if ($whole == 0 && strpos($ingredient_quantity, '{') >= 0) {
/* remove anything in curly braces */
$ingredient_quantity = preg_replace('/{.*}/', '', $ingredient_quantity);
}
else {
/* remove just the curly braces, but keep everything between them */
$ingredient_quantity = preg_replace('/{|}/', '', $ingredient_quantity);
}
}
return filter_xss_admin($ingredient_quantity);
}
/**
* Converts an ingredient's quantity from fractions to decimal
*/
function recipe_ingredient_quantity_from_fraction($ingredient_quantity) {
if ($pos_slash = strpos($ingredient_quantity, '/')) {
$pos_space = strpos($ingredient_quantity, ' ');
// can't trust $pos_space to be a zero value if there is no space
// so set it explicitly
if ($pos_space === false) {
$pos_space = 0;
}
$whole = substr($ingredient_quantity, 0, $pos_space);
$numerator = substr($ingredient_quantity, $pos_space, $pos_slash);
$denominator = substr($ingredient_quantity, $pos_slash + 1);
$ingredient_quantity = $whole + $numerator / $denominator;
}
return $ingredient_quantity;
}
/**
* Saves the changed ingredients of a recipe node to the database
* (by comparing the old and new ingredients first)
*/
function recipe_save_ingredients($node) {
if (!$node->ingredients) {
$node->ingredients = array();
}
$changes = recipe_ingredients_diff($node->ingredients, recipe_load_ingredients($node));
if (count($changes->remove) > 0) {
$ids = implode(',', $changes->remove);
db_query("DELETE FROM {recipe_node_ingredient} WHERE id IN (%s)", $ids);
}
foreach ($changes->add as $ingredient) {
$ingredient->id = recipe_ingredient_id_from_name($ingredient->name);
$ingredient->quantity = recipe_ingredient_quantity_from_fraction($ingredient->quantity);
db_query("INSERT INTO {recipe_node_ingredient} (nid,ingredient_id,quantity,unit_id) VALUES (%d,%d,%f,%d)", $node->nid, $ingredient->id, $ingredient->quantity, $ingredient->unit_id);
}
foreach ($changes->update as $ingredient) {
$ingredient->id = recipe_ingredient_id_from_name($ingredient->name);
$ingredient->quantity = recipe_ingredient_quantity_from_fraction($ingredient->quantity);
db_query("UPDATE {recipe_node_ingredient} SET quantity='%f', unit_id='%d' WHERE nid='%d' AND ingredient_id='%d'", $ingredient->quantity, $ingredient->unit_id, $node->nid, $ingredient->id);
}
}
/**
* Compares two arrays of ingredients and returns the differences
*/
function recipe_ingredients_diff($a1, $a2) {
$return->add = array();
$return->remove = array();
$return->update = array();
foreach ($a1 as $pl) {
$pl = (object) $pl;
$pl->name = trim($pl->name);
if ($pl->name) {
if (!_in_array($pl, $return->add)) {
// Duplicate entries for the same ingredient are ignored.
if (!_in_array($pl, $a2)) {
$return->add[] = $pl;
}
else {
if (!_in_array($pl, $return->update)) {
$return->update[] = $pl;
}
}
}
}
}
foreach ($a2 as $k => $pl) {
if (!_in_array($pl, $a1)) {
$return->remove[] = $pl->id;
}
}
return $return;
}
/**
* Custom in_array() function because PHP 4 in_aray() doesnt seem to
* handle the first arguement being an object
*/
function _in_array($a, $b) {
$a->name = trim(strtolower($a->name));
foreach ($b as $row) {
$compareto = "";
if (is_array($row)) {
$compareto = trim(strtolower($row["name"]));
}
else {
$compareto = trim(strtolower($row->name));
}
if ($a->name === $compareto) {
return true;
}
}
return false;
}
/**
* Loads the ingredients for a recipe
*/
function recipe_load_ingredients($node) {
$rs = db_query('
SELECT
ri.id,
i.name,
i.link,
ri.quantity,
ri.unit_id,
u.abbreviation,
ri.ingredient_id
FROM
{recipe_node_ingredient} ri,
{recipe_ingredient} i,
{recipe_unit} u
WHERE
ri.ingredient_id = i.id
AND ri.unit_id = u.id
AND ri.nid=%d
ORDER BY
ri.id', $node->nid);
$ingredients = array();
while ($ingredient = db_fetch_object($rs)) {
$ingredients[] = $ingredient;
}
return $ingredients;
}
/**
* Converts a recipe unit ID to it's abbreviation
*/
function recipe_unit_abbreviation($unit_id) {
static $abbreviations;
if (!$abbreviations) {
$rs = db_query('SELECT id,abbreviation FROM {recipe_unit}');
while ($unit = db_fetch_object($rs)) {
$abbreviations[$unit->id] = $unit->abbreviation;
}
}
return $abbreviations[$unit_id];
}
/**
* Converts a recipe unit ID to it's name */
function recipe_unit_name($unit_id) {
static $unit_names;
if (!$unit_names) {
$rs = db_query('SELECT id,name FROM {recipe_unit}');
while ($unit = db_fetch_object($rs)) {
$unit_names[$unit->id] = $unit->name;
}
}
return $unit_names[$unit_id];
}
/**
* Menu callback; Generates various representation of a recipe page with
* all descendants and prints the requested representation to output.
*
* The function delegates the generation of output to helper functions.
* The function name is derived by prepending 'recipe_export_' to the
* given output type. So, e.g., a type of 'html' results in a call to
* the function recipe_export_html().
*
* @param type
* - a string encoding the type of output requested.
* The following types are currently supported in recipe module
* html: HTML (printer friendly output)
* recipeml: XML (RecipeML formatted output)
* Other types can be supported with contributed modules.
* @param nid
* - an integer representing the node id (nid) of the node to export
*
*/
function recipe_export($type = 'html', $nid = 0) {
$type = drupal_strtolower($type);
$export_function = 'recipe_export_' . $type;
if (function_exists($export_function)) {
echo call_user_func($export_function, $nid);
}
else {
drupal_set_message(t('Unknown export format.'));
drupal_not_found();
}
}
/**
* This function is called by recipe_export() to generate HTML for export.
*
* @param nid
* - an integer representing the node id (nid) of the node to export
* @return
* - string containing HTML representing the recipe
*/
function recipe_export_html($nid) {
if ($nid == 0) {
drupal_goto('recipe');
}
$node = node_load(array(
'nid' => $nid,
'type' => 'recipe',
));
$node = recipe_node_prepare($node, FALSE);
$output = theme('node_recipe', $node, FALSE);
$html = theme('recipe_export_html', check_plain($node->title), $output);
return $html;
}
/**
* This function is called by recipe_export() to generate RecipeML for export.
*
* @param nid
* - an integer representing the node id (nid) of the node to export
* @return
* - string containing the recipe in RecipeML
*/
function recipe_export_recipeml($nid) {
if ($nid == 0) {
drupal_goto('recipe');
}
$node = node_load(array(
'nid' => $nid,
'type' => 'recipe',
));
drupal_set_header('Content-type: text/xml');
$output = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<!DOCTYPE recipeml PUBLIC "-//FormatData//DTD RecipeML 0.5//EN" "http://www.formatdata.com/recipeml/recipeml.dtd">' . "\n" . '<recipeml version="0.5">' . "\n" . ' <recipe>' . "\n" . ' <head>' . "\n" . ' <title>' . $node->title . '</title>' . "\n" . ' </head>' . "\n" . ' <yield><qty>' . $node->yield . '</qty></yield>' . "\n" . ' <ingredients>';
foreach ($node->ingredients as $ingredient) {
$output .= "\n" . '<ing><amt><qty>' . $ingredient->quantity . '</qty><unit>' . $ingredient->abbreviation . '</unit></amt><item>' . $ingredient->name . '</item></ing>';
}
$output .= "\n" . ' </ingredients>' . "\n" . ' <directions>' . $node->instructions . '</directions>' . "\n" . ' </recipe>' . "\n" . '</recipeml>';
return $output;
}
/**
* Callback function for ingredient autocomplete
*/
function recipe_autocomplete_page($string = "", $limit = 10) {
$matches = array();
$rs = db_query("SELECT name FROM {recipe_ingredient} WHERE LOWER(name) LIKE '%s%%' ORDER BY name LIMIT %d", strtolower($string), $limit);
while ($r = db_fetch_object($rs)) {
$matches[$r->name] = check_plain($r->name);
}
print drupal_to_js($matches);
exit;
}
/**
* Implementation of hook_validate().
*
* Errors should be signaled with form_set_error().
*/
function recipe_validate(&$node) {
if (!$node->ingredients) {
return;
}
$ingredients = array();
foreach ($node->ingredients as $key => $ingredient) {
$ingredient = (object) $ingredient;
if (!isset($ingredient->quantity)) {
$ingredient = recipe_parse_ingredient_string($ingredient->name);
}
if ($ingredient->name && _in_array($ingredient, $ingredients)) {
form_set_error("recipe", t('Duplicate ingredients are not allowed.'));
}
else {
$ingredients[] = $ingredient;
}
$node->ingredients[$key] = $ingredient;
}
}
/**
* Converts an ingredients name string to an ingredient object
*/
function recipe_parse_ingredient_string($ingredient_string) {
if (preg_match('#([0-9.]+(?:\\s?\\d*/\\d*)?\\s?)?(?:([a-zA-Z.]*)\\s)?(.*)#', trim($ingredient_string), $matches)) {
$ingredient->name = $matches[3];
$ingredient->quantity = trim($matches[1]);
if ($ingredient->quantity == 0) {
$ingredient->quantity = 0;
}
$t_unit = $matches[2];
$unit = recipe_unit_from_name($t_unit);
if ($unit) {
$ingredient->unit_id = $unit->id;
$ingredient->abbreviation = $unit->abbreviation;
}
else {
$ingredient->unit_id = 29;
$ingredient->abbreviation = '';
$ingredient->name = $t_unit . ' ' . $ingredient->name;
}
$ingredient->name = trim($ingredient->name);
return $ingredient;
}
else {
return false;
}
}
/**
* Returns information about a unit based on a unit abbreviation or name
*/
function recipe_unit_from_name($name) {
if (strlen($name) > 1) {
$string = strtolower($name);
}
else {
$string = $name;
}
$ending = substr($string, -1, 1);
if ($ending == 's' && $string != 'ds' || $ending == '.') {
$string = substr($string, 0, strlen($string) - 1);
}
$ending = substr($string, -1, 1);
if ($ending == 's' && $string != 'ds' || $ending == '.') {
$string = substr($string, 0, strlen($string) - 1);
}
static $units_array;
if (!$units_array) {
$rs = db_query('SELECT id,name,abbreviation FROM {recipe_unit}');
while ($unit = db_fetch_object($rs)) {
$units_array[strtolower($unit->name)] = $unit;
$units_array[$unit->abbreviation] = $unit;
}
}
return $units_array[$string];
}
/**
Menu Callback - created output for the main recipe page.
@return $body
**/
function recipe_page() {
$body = "";
if (arg(1) == "feed") {
module_invoke('node', 'feed', module_invoke('taxonomy', 'select_nodes', recipe_get_recipe_terms(), 'or', 0, FALSE));
}
else {
if (arg(1) != NULL) {
$breadcrumb = drupal_get_breadcrumb();
$term = recipe_build_breadcrumbs($breadcrumb);
drupal_set_breadcrumb($breadcrumb);
if ($term != NULL) {
$content = recipe_index($term->tid);
if ($content != '') {
$body = theme('box', $term->name . '- ' . t('Sub Categories'), $content);
}
$terms = array_merge(array(
$term->tid,
), array_map('_recipe_get_tid_from_term', module_invoke('taxonomy', 'get_children', $term->tid)));
$body .= module_invoke('taxonomy', 'render_nodes', module_invoke('taxonomy', 'select_nodes', $terms));
}
}
else {
$body = '';
if (variable_get('recipe_recent_box_enable', 1)) {
$body = theme('box', variable_get('recipe_recent_box_title', t('Latest Recipes')), module_invoke('node', 'title_list', recipe_get_latest(variable_get('recipe_recent_display', '5')), '') . theme('recipe_more_info', theme('feed_icon', url("recipe/feed"))));
}
$content = recipe_index();
if ($content != '') {
$body .= theme('box', t('Recipe Categories'), $content);
}
}
}
return $body;
}
/**
Builds a breadcrumb list.
@param breadcrumb a reference to the breadcrumb array. New items will be appending to this array.
@return returns a term object if the last item in the url is a term, otherwise returns NULL.
**/
function recipe_build_breadcrumbs(&$breadcrumb) {
if (arg(1) != NULL) {
$i = 1;
$url = 'recipe';
$breadcrumb[] = l(ucwords(t('Recipes')), $url);
while (arg($i) != NULL) {
$last_term = urldecode(arg($i));
$url = $url . '/' . urlencode($last_term);
$breadcrumb[] = l(ucwords($last_term), $url);
$i++;
}
$term = current(module_invoke('taxonomy', 'get_term_by_name', $last_term));
return $term;
}
return NULL;
}
/**
Recursively traverses the term tree to construct the index.
@return string the output for this tree.
**/
function recipe_build_index(&$tree, $parent_url) {
$output = '';
if ($tree == array()) {
return '';
}
do {
$cur = current($tree);
$nex = next($tree);
if ($nex === false) {
$next_depth = -1;
}
else {
$next_depth = $nex->depth;
}
$cur->link = $parent_url . '/' . urlencode(strtolower(trim($cur->name)));
$cur->children = '';
if ($next_depth > $cur->depth) {
$cur->children = recipe_build_index($tree, $cur->link);
// sync $next_depth, because 'next item' may be shoved forward
// Thanks for the patch Roderik.
$nex = current($tree);
if ($nex === false) {
$next_depth = -1;
}
else {
$next_depth = $nex->depth;
}
}
$cur->count = module_invoke('taxonomy', 'term_count_nodes', $cur->tid);
$output .= theme('recipe_index_item', $cur);
} while ($cur->depth == $next_depth);
return theme('recipe_list', $output);
}
/**
Constructs a url from the current url arguments list.
@return a string containing a formated URL.
**/
function recipe_get_current_url() {
$arg_index = 1;
$url = arg(0);
while ($argument = arg($arg_index)) {
$url .= '/' . urlencode($argument);
$arg_index++;
}
return $url;
}
/**
Get the latest recipes
@return a database query result.
**/
function recipe_get_latest($count = 0) {
$tids = recipe_get_recipe_terms();
return recipe_select_nodes($tids, 'or', 0, FALSE, $count);
}
/**
Get all the terms associated with Recipes.
@return an array of unique term ids.
**/
function recipe_get_recipe_terms() {
$vocabs = recipe_get_vocabularies();
$tids = array();
foreach ($vocabs as $vocab) {
$tids = array_merge($tids, recipe_tax_get_terms($vocab->vid));
}
return array_unique($tids);
}
/**
Gets all the vocabularies that are associated with the recipe module.
@return array the vocabularies.
**/
function recipe_get_vocabularies() {
$vocabularies = module_invoke('taxonomy', 'get_vocabularies', 'recipe');
return $vocabularies;
}
/**
Constructs the recipe index page, using theme functions.
@return a string containing the output ready for display.
**/
function recipe_index($tid = 0) {
$output = "";
$vocabularies = recipe_get_vocabularies();
foreach ($vocabularies as $vocab) {
$max_depth = variable_get('recipe_index_depth', 0);
$vocab_tree = module_invoke('taxonomy', 'get_tree', $vocab->vid, $tid, -1, $max_depth <= 0 ? NULL : $max_depth);
$content = '';
while (current($vocab_tree) != NULL) {
$content .= recipe_build_index($vocab_tree, recipe_get_current_url());
}
if ($content != '') {
$output .= theme('recipe_index', $vocab->name, $content);
}
}
return $output;
}
/**
* Custom version of node_prepare().
*
*/
function recipe_node_prepare($node, $teaser = FALSE) {
$node->readmore = TRUE;
if ($teaser == FALSE) {
$node->body = check_markup($node->body, $node->format, FALSE);
$node->instructions = check_markup($node->instructions, $node->format, FALSE);
if ($node->notes) {
$node->notes = check_markup($node->notes, $node->format, FALSE);
}
if ($node->source) {
$node->source = check_markup($node->source, $node->format, FALSE);
}
if ($node->ingredients) {
$tmp = $node->ingredients;
$node->ingredients = array();
foreach ($tmp as $ingredient) {
// For preview, node->ingredients is an array, for actual display, it's an object
if (is_array($ingredient)) {
if (isset($ingredient["name"])) {
$ingredient["name"] = check_plain($ingredient["name"]);
}
}
else {
if (is_object($ingredient)) {
if (isset($ingredient->name)) {
$ingredient->name = check_plain($ingredient->name);
}
}
}
$node->ingredients[] = $ingredient;
}
}
}
else {
$node->teaser = check_markup($node->body, $node->format, FALSE);
}
return $node;
}
/**
* Finds all nodes that match selected taxonomy conditions.
* This is just a copy of taxonomy_select_nodes() but
* includes node title field in the selection.
* Is this useful or try to find a taxonomy function
* to achieve this?
*
* @param $tids
* An array of term IDs to match.
* @param $operator
* How to interpret multiple IDs in the array. Can be "or" or "and".
* @param $depth
* How many levels deep to traverse the taxonomy tree. Can be a nonnegative
* integer or "all".
* @param $pager
* Whether the nodes are to be used with a pager (the case on most Drupal
* pages) or not (in an XML feed, for example).
* @return
* A resource identifier pointing to the query results.
*/
function recipe_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $count = 0) {
if (count($tids) > 0) {
// For each term ID, generate an array of descendant term IDs to the right depth.
$descendant_tids = array();
if ($depth === 'all') {
$depth = NULL;
}
foreach ($tids as $index => $tid) {
$term = module_invoke('taxonomy', 'get_term', $tid);
$tree = module_invoke('taxonomy', 'get_tree', $term->vid, $tid, -1, $depth);
$descendant_tids[] = array_merge(array(
$tid,
), array_map('_recipe_get_tid_from_term', $tree));
}
if ($operator == 'or') {
$str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids));
if (module_exists('category')) {
$sql = 'SELECT DISTINCT n.nid, n.title, n.sticky, n.created FROM {node} n INNER JOIN {category_node} cn ON n.nid = cn.nid WHERE cn.cid IN (' . $str_tids . ') AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC';
$sql_count = 'SELECT COUNT(n.nid) FROM {node} n INNER JOIN {category_node} cn ON n.nid = cn.nid WHERE cn.tid IN (' . $str_tids . ') AND n.status = 1';
}
else {
$sql = 'SELECT DISTINCT n.nid, n.title, n.sticky, n.created FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN (' . $str_tids . ') AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC';
$sql_count = 'SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN (' . $str_tids . ') AND n.status = 1';
}
}
else {
$joins = '';
$wheres = '';
if (module_exists('category')) {
foreach ($descendant_tids as $index => $tids) {
$joins .= ' INNER JOIN {category_node} cn' . $index . ' ON n.nid = cn' . $index . '.nid';
$wheres .= ' AND cn' . $index . '.cid IN (' . implode(',', $tids) . ')';
}
}
else {
foreach ($descendant_tids as $index => $tids) {
$joins .= ' INNER JOIN {term_node} tn' . $index . ' ON n.nid = tn' . $index . '.nid';
$wheres .= ' AND tn' . $index . '.tid IN (' . implode(',', $tids) . ')';
}
}
$sql = 'n.nid, n.title, n.sticky, n.created FROM {node} n ' . $joins . ' WHERE n.status = 1 AND ' . $wheres . ' ORDER BY n.sticky DESC, n.created DESC';
$sql_count = 'SELECT COUNT(n.nid) FROM {node} n ' . $joins . ' WHERE n.status = 1 AND ' . $wheres;
}
}
else {
// no taxonomy used
$sql = "SELECT nid, title, sticky, created FROM {node} WHERE status = 1 AND type = 'recipe' ORDER BY sticky DESC, created DESC";
$sql_count = "SELECT COUNT(nid) FROM {node} WHERE status = 1 AND type = 'recipe'";
}
if ($pager && $count > 0) {
$result = pager_query(db_rewrite_sql($sql), variable_get('default_nodes_main', 10), 0, db_rewrite_sql($sql_count));
}
else {
$count = $count > 0 ? $count : 15;
$result = db_query_range(db_rewrite_sql($sql), 0, $count);
}
return $result;
}
/**
Get all the terms in a given vocabulary.
@return an array of unique term ids.
**/
function recipe_tax_get_terms($vid) {
$result = db_query("SELECT tid FROM {term_data} WHERE vid = %d", $vid);
$tids = array();
while ($term = db_fetch_array($result)) {
$tids[] = $term['tid'];
}
return array_unique($tids);
}
/**
Helper function for array map purposes.
@param term the term object from which the tid will be extracted.
@return the tid member of $term.
**/
function _recipe_get_tid_from_term($term) {
return $term->tid;
}
/**
* Theme functions group
*/
/**
* A custom theme function.
*
* By using this function to format our node-specific information, themes
* can override this presentation if they wish. We also wrap the default
* presentation in a CSS class that is prefixed by the module name. This
* way, style sheets can modify the output without requiring theme code.
*/
function theme_node_recipe($node, $yield_form = TRUE) {
// Get custom yield or default to a factor of 1
if ($yield_form && intval($node->yield) == $node->yield) {
if ($_POST['op'] == t('Change')) {
$yield = $_POST['custom_yield'];
}
else {
if ($_POST['op'] == t('Halve')) {
$yield = $_POST['custom_yield'] / 2;
}
else {
if ($_POST['op'] == t('Double')) {
$yield = $_POST['custom_yield'] * 2;
}
}
}
if ($yield && $yield != $node->yield && $node->yield != 0) {
$factor = $yield / $node->yield;
$node->yield = $yield;
}
else {
$factor = 1;
}
$_POST = array();
$yield = drupal_get_form('recipe_custom_yield_form', $node);
}
else {
$yield = $node->yield;
$factor = 1;
}
// Construct the $ingredients[] array
if ($node->ingredients) {
foreach ($node->ingredients as $ingredient) {
if (isset($ingredient->quantity) && $ingredient->name) {
if (!$ingredient->abbreviation) {
$ingredient->abbreviation = recipe_unit_abbreviation($ingredient->unit_id);
}
if ($ingredient->quantity > 0) {
$ingredient->quantity *= $factor;
}
else {
$ingredient->quantity = '';
}
if (variable_get('recipe_fraction_display', t('{%d} %d⁄%d'))) {
$ingredient->quantity = recipe_ingredient_quantity_from_decimal($ingredient->quantity);
}
if (!empty($ingredient->link)) {
$ingredient->name = l($ingredient->name, 'node/' . $ingredient->link);
}
$ingredients[] = $ingredient->quantity . ' <acronym title="' . recipe_unit_name($ingredient->unit_id) . '">' . $ingredient->abbreviation . '</acronym> ' . $ingredient->name;
}
}
}
// Construct the summary
$summary = '<table>';
$summary .= '<tr><th>' . t('Yield') . '</th><td>' . $yield . '</td></tr>';
if ($node->source) {
$summary .= '<tr><th>' . t('Source') . '</th><td>' . $node->source . '</td></tr>';
}
if ($node->preptime) {
if ($node->preptime < 60) {
$preptime = format_plural($node->preptime, '1 minute', '@count minutes');
}
elseif ($node->preptime % 60 == 0) {
$preptime = format_plural($node->preptime / 60, '1 hour', '@count hours');
}
else {
$preptime = t('!time hours', array(
'!time' => recipe_ingredient_quantity_from_decimal($node->preptime / 60),
));
}
$summary .= '<tr><th>' . t('Prep Time') . '</th><td>' . $preptime . '</td></tr>';
}
$vocabs = taxonomy_get_vocabularies('recipe');
if (count($vocabs) > 0) {
foreach ($vocabs as $vocab) {
$terms = taxonomy_node_get_terms_by_vocabulary($node->nid, $vocab->vid);
if (count($terms) > 0) {
$summary .= '<tr><th>' . $vocab->name . '</th><td>';
foreach ($terms as $term) {
$summary .= l($term->name, 'taxonomy/term/' . $term->tid) . ' ';
}
$summary .= '</td></tr>';
}
}
}
$summary .= '</table>';
// Create the output
$output = '';
$output .= '<div class="recipe-summary">' . theme('box', t('Summary'), $summary) . '</div>';
$output .= '<div class="recipe-description">' . theme('box', t('Description'), $node->body) . '</div>';
$output .= '<div class="recipe-ingredients">' . theme('box', t('Ingredients'), theme('item_list', $ingredients)) . '</div>';
$output .= '<div class="recipe-instructions">' . theme('box', t('Instructions'), $node->instructions) . '</div>';
if ($node->notes !== '') {
$output .= '<div class="recipe-notes">' . theme('box', t('Notes'), $node->notes) . '</div>';
}
return $output;
}
function recipe_custom_yield_form($node) {
$form['custom_yield_container'] = array(
'#type' => 'fieldset',
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['custom_yield_container']['custom_yield'] = array(
'#type' => 'textfield',
'#default_value' => $node->yield,
'#size' => 2,
'#maxlength' => 4,
);
$form['custom_yield_container']['submit'] = array(
'#type' => 'submit',
'#value' => t('Change'),
);
$form['custom_yield_container']['halve'] = array(
'#type' => 'submit',
'#value' => t('Halve'),
);
$form['custom_yield_container']['double'] = array(
'#type' => 'submit',
'#value' => t('Double'),
);
return $form;
}
/**
Controls the output of the rendered index list.
@return string the output for the index list.
**/
function theme_recipe_index(&$name, &$index_list) {
if ($index_list != "") {
return "<div class=\"item-list\">\n{$index_list}\n</div>\n";
}
return "";
}
/**
Displays a single index item.
@return string the output for this item.
**/
function theme_recipe_index_item(&$term) {
$description = $term->description != '' ? "<p class=\"recipe-desc\">" . $term->description . "</p>" : '';
if ($term->count > 0) {
return "<li ><div class=\"recipe-title\">" . l($term->name . " ({$term->count})", $term->link) . "</div>" . $description . $term->children . "</li>";
}
else {
return "<li><div class=\"recipe-title\">" . $term->name . " ({$term->count})</div>" . $description . $term->children . "</li>";
}
}
/**
Displays a single one level list. Called for each group of items at the same depth.
@return string the output for this list.
**/
function theme_recipe_list(&$output) {
if ($output != '') {
return "<ul>" . $output . "</ul>\n";
}
return '';
}
/**
Displays more information content, suck as "more" links, and
feed images.
@return formatted string containint the output.
**/
function theme_recipe_more_info($content) {
return "<div class=\"more-link\">" . $content . "</div>";
}
/**
* How the recipe's HTML export should be themed
*
* @ingroup themeable
*/
function theme_recipe_export_html($title, $content) {
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>" . $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";
$html .= "</head>\n<body>\n<h1 class=title>" . $title . "</h1>\n" . $content . "\n</body>\n</html>\n";
return $html;
}
/**
*
*
*/
function recipe_euclid($number_one, $number_two) {
if ($number_one == 0 or $number_two == 0) {
$hcf = 1;
return $hcf;
}
else {
if ($number_one < $number_two) {
$buffer = $number_one;
$number_one = $number_two;
$number_two = $buffer;
}
$dividend = $number_one;
$divisor = $number_two;
$remainder = $dividend;
while ($remainder > 0) {
if (floor($dividend / $divisor) == $dividend / $divisor) {
$quotient = $dividend / $divisor;
$remainder = 0;
}
else {
$quotient = floor($dividend / $divisor);
$remainder = $dividend - $quotient * $divisor;
}
$hcf = $divisor;
$dividend = $divisor;
$divisor = $remainder;
}
}
return $hcf;
}
function recipe_import_from_46() {
$rcount = 0;
$icount = 0;
$out = "<p>";
$old = db_query("SELECT * FROM {recipe_old}");
while ($o = db_fetch_object($old)) {
db_query("INSERT INTO {recipe} (nid, source, yield, preptime, notes, instructions) VALUES('%d', '%s', '%d', '%d', '%s', '%s')", $o->nid, $o->source, intval($o->yield), $o->preptime, $o->notes, $o->instructions);
$out .= t('Imported recipe @recipeid', array(
'@recipeid' => $o->nid,
)) . '<br />';
$rcount++;
}
$out .= "</p>";
$out .= "<p><pre>";
$ings = db_query("SELECT * FROM {recipe_ingredients}");
while ($i = db_fetch_object($ings)) {
$unitid = 29;
// "unknown"
$quantity = recipe_ingredient_quantity_from_fraction($i->ingredient);
// best guess
$out .= "\n\n" . t('Parsing @ingredient', array(
'@ingredient' => $i->ingredient,
)) . "\n";
$nmatches = preg_match("/([^a-zA-Z]*)([^ ]*) (.*)/", $i->ingredient, $matches);
//print_r($matches);
if ($nmatches > 0) {
$quantity = recipe_ingredient_quantity_from_fraction($matches[1]);
$unit = $matches[2];
$name = $matches[3];
$out .= t('looking up unit for "@unit"', array(
'@unit' => strtolower($unit),
)) . "\n";
$unit_rs = db_query("SELECT id FROM {recipe_unit} WHERE '%s' LIKE CONCAT(LOWER(name),'%%') OR abbreviation='%s'", strtolower($unit), $unit);
// allow pints to match pint etc
if ($unito = db_fetch_object($unit_rs)) {
$out .= t('Got unit id @unitid', array(
'@unitid' => $unito->id,
)) . "\n";
$unitid = $unito->id;
}
else {
$name = $unit . " " . $name;
}
}
$iid = recipe_ingredient_id_from_name($name);
$out .= t('Got id for "@name": @iid', array(
'@name' => $name,
'@iid' => $iid,
));
db_query("INSERT INTO {recipe_node_ingredient} (nid, unit_id, quantity, ingredient_id) VALUES('%d', '%d', '%f', '%d')", $i->nid, $unitid, $quantity, $iid);
$icount++;
}
$out .= "</pre></p>";
$out .= '<p>' . t('Imported @rcount recipes and @icount ingredients.', array(
'@rcount' => $rcount,
'@icount' => $icount,
)) . '</p>';
return $out;
}
Functions
Name | Description |
---|---|
recipe_access | Implementation of hook_access(). |
recipe_admin_settings | Settings form for menu callback |
recipe_autocomplete_page | Callback function for ingredient autocomplete |
recipe_block | Implementation of hook_block(). |
recipe_build_breadcrumbs | Builds a breadcrumb list. |
recipe_build_index | Recursively traverses the term tree to construct the index. |
recipe_custom_yield_form | |
recipe_delete | Implementation of hook_delete(). |
recipe_euclid | |
recipe_export | Menu callback; Generates various representation of a recipe page with all descendants and prints the requested representation to output. |
recipe_export_html | This function is called by recipe_export() to generate HTML for export. |
recipe_export_recipeml | This function is called by recipe_export() to generate RecipeML for export. |
recipe_form | Implementation of hook_form(). |
recipe_get_current_url | Constructs a url from the current url arguments list. |
recipe_get_latest | Get the latest recipes |
recipe_get_recipe_terms | Get all the terms associated with Recipes. |
recipe_get_vocabularies | Gets all the vocabularies that are associated with the recipe module. |
recipe_help | Implementation of hook_help(). |
recipe_import_from_46 | |
recipe_index | Constructs the recipe index page, using theme functions. |
recipe_ingredients_diff | Compares two arrays of ingredients and returns the differences |
recipe_ingredient_id_from_name | Converts a recipe ingredient name to and ID |
recipe_ingredient_quantity_from_decimal | Converts an ingredient's quantity from decimal to fraction |
recipe_ingredient_quantity_from_fraction | Converts an ingredient's quantity from fractions to decimal |
recipe_insert | Implementation of hook_insert(). |
recipe_link | Implementation of hook_link(). |
recipe_load | Implementation of hook_load(). |
recipe_load_ingredients | Loads the ingredients for a recipe |
recipe_menu | Implementation of hook_menu(). |
recipe_node_info | Implementation of hook_node_info(). This function replaces hook_node_name() and hook_node_types() from 4.6. |
recipe_node_prepare | Custom version of node_prepare(). |
recipe_page | Menu Callback - created output for the main recipe page. |
recipe_parse_ingredient_string | Converts an ingredients name string to an ingredient object |
recipe_perm | Implementation of hook_perm(). |
recipe_save_ingredients | Saves the changed ingredients of a recipe node to the database (by comparing the old and new ingredients first) |
recipe_select_nodes | Finds all nodes that match selected taxonomy conditions. This is just a copy of taxonomy_select_nodes() but includes node title field in the selection. Is this useful or try to find a taxonomy function to achieve this? |
recipe_tax_get_terms | Get all the terms in a given vocabulary. |
recipe_unit_abbreviation | Converts a recipe unit ID to it's abbreviation |
recipe_unit_from_name | Returns information about a unit based on a unit abbreviation or name |
recipe_unit_name | Converts a recipe unit ID to it's name |
recipe_unit_options | Returns a cached array of recipe unit types |
recipe_update | Implementation of hook_update(). |
recipe_validate | Implementation of hook_validate(). |
recipe_view | Implementation of hook_view(). |
theme_node_recipe | A custom theme function. |
theme_recipe_export_html | How the recipe's HTML export should be themed |
theme_recipe_index | Controls the output of the rendered index list. |
theme_recipe_index_item | Displays a single index item. |
theme_recipe_list | Displays a single one level list. Called for each group of items at the same depth. |
theme_recipe_more_info | Displays more information content, suck as "more" links, and feed images. |
_in_array | Custom in_array() function because PHP 4 in_aray() doesnt seem to handle the first arguement being an object |
_recipe_get_tid_from_term | Helper function for array map purposes. |