Parent Directory
|
Revision Log
|
Revision Graph
feature request #489930 by jbemmel, fixed by jbemmel. Hooked nodeapi to apply links to ingredient nodes in existing recipes if the nodes are created later.
| 1 | <?php |
| 2 | // $Id: recipe.module,v 1.84 2009/06/26 12:18:11 marble Exp $ |
| 3 | |
| 4 | /** |
| 5 | * @file |
| 6 | * recipe.module - share recipes |
| 7 | * for drupal 5.x |
| 8 | * |
| 9 | * Updated to drupal 6.x by shadow |
| 10 | * |
| 11 | * @todo |
| 12 | */ |
| 13 | |
| 14 | /** |
| 15 | * Implementation of hook_perm(). |
| 16 | */ |
| 17 | function recipe_perm() { |
| 18 | return array(t('create recipes'), t('edit own recipes')); |
| 19 | } |
| 20 | |
| 21 | /** |
| 22 | * Implementation of hook_load(). |
| 23 | */ |
| 24 | function recipe_load($node) { |
| 25 | $recipe = db_fetch_object(db_query('SELECT * FROM {recipe} WHERE nid = %d', $node->nid)); |
| 26 | $recipe->ingredients = recipe_load_ingredients($node); |
| 27 | return $recipe; |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * Implementation of hook_link(). |
| 32 | */ |
| 33 | function recipe_link($type, $node = NULL, $teaser = FALSE) { |
| 34 | $links = array(); |
| 35 | |
| 36 | if ($type == 'node' && $node->type == 'recipe') { |
| 37 | if (!$teaser) { |
| 38 | if (variable_get('recipe_export_recipeml_enable', 1) == 1) { |
| 39 | $links['recipe_recipeml'] = array( |
| 40 | 'title' => t('Export to RecipeML'), |
| 41 | 'href' => "recipe/export/recipeml/$node->nid", |
| 42 | 'attributes' => array('title' => t('Export this recipe to RecipeML.')), |
| 43 | ); |
| 44 | } |
| 45 | if (variable_get('recipe_export_html_enable', 1) == 1) { |
| 46 | $links['recipe_html'] = array( |
| 47 | 'title' => t('Printer-friendly version'), |
| 48 | 'href' => "recipe/export/html/$node->nid", |
| 49 | 'attributes' => array('title' => t('Show a printer-friendly version of this recipe.')), |
| 50 | ); |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | return $links; |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Implementation of hook_node_info(). This function replaces hook_node_name() |
| 60 | * and hook_node_types() from 4.6. |
| 61 | */ |
| 62 | function recipe_node_info() { |
| 63 | return array( |
| 64 | 'recipe' => array( |
| 65 | 'name' => t('Recipe'), |
| 66 | 'module' => 'recipe', |
| 67 | 'description' => t("Share your favorite recipes with your fellow cooks."), |
| 68 | ) |
| 69 | ); |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Implementation of hook_help(). |
| 74 | */ |
| 75 | function recipe_help($path, $arg) { |
| 76 | switch ($path) { |
| 77 | case 'node/add/recipe': |
| 78 | return variable_get("recipe_help", ""); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Implementation of hook_insert(). |
| 84 | * |
| 85 | * As a new node is being inserted into the database, we need to do our own |
| 86 | * database inserts. |
| 87 | */ |
| 88 | function recipe_insert($node) { |
| 89 | 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); |
| 90 | recipe_save_ingredients($node); |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * Implementation of hook_update(). |
| 95 | * |
| 96 | * As an existing node is being updated in the database, we need to do our own |
| 97 | * database updates. |
| 98 | */ |
| 99 | function recipe_update($node) { |
| 100 | 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); |
| 101 | recipe_save_ingredients($node); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Implementation of hook_delete(). |
| 106 | * |
| 107 | * When a node is deleted, we need to clean up related tables. |
| 108 | */ |
| 109 | function recipe_delete($node) { |
| 110 | db_query("DELETE FROM {recipe} WHERE nid = %d", $node->nid); |
| 111 | db_query("DELETE FROM {recipe_node_ingredient} WHERE nid = %d", $node->nid); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Implementation of hook_form(). |
| 116 | */ |
| 117 | function recipe_form(&$node,$form_state) { |
| 118 | // drupal 4.7 requires the title field to be defined by the custom node's module |
| 119 | $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), |
| 120 | '#size' => 60, '#maxlength' => 128, '#required' => TRUE, |
| 121 | '#default_value' => $node->title); |
| 122 | |
| 123 | // Now we define the form elements specific to our node type. |
| 124 | $form['body'] = array( |
| 125 | '#type' => 'textarea', |
| 126 | '#title' => t('Description'), |
| 127 | '#default_value' => $node->body, |
| 128 | '#cols' => 60, |
| 129 | '#rows' => 2, |
| 130 | '#description' => t('A short description or "teaser" for the recipe.'), |
| 131 | '#required' => TRUE, |
| 132 | ); |
| 133 | $form['yield'] = array( |
| 134 | '#type' => 'textfield', |
| 135 | '#title' => t('Yield'), |
| 136 | '#default_value' => $node->yield, |
| 137 | '#size' => 10, |
| 138 | '#maxlength' => 10, |
| 139 | '#description' => t('The number of servings the recipe will make.'), |
| 140 | '#attributes' => NULL, |
| 141 | '#required' => TRUE, |
| 142 | ); |
| 143 | $form['preptime'] = array( |
| 144 | '#type' => 'select', |
| 145 | '#title' => t("Preparation time"), |
| 146 | '#default_value' => $node->preptime, |
| 147 | '#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')), |
| 148 | '#description' => t("How long does this recipe take to prepare (i.e. elapsed time)"), |
| 149 | ); |
| 150 | $form["source"] = array( |
| 151 | '#type' => 'textfield', |
| 152 | '#title' => t("Source"), |
| 153 | '#default_value' => $node->source, |
| 154 | '#size' => 60, |
| 155 | '#maxlength' => 127, |
| 156 | '#description' => t("Optional. Does anyone else deserve credit for this recipe?"), |
| 157 | ); |
| 158 | |
| 159 | // Table of existing ingredients |
| 160 | $form['ingredients']['#tree'] = TRUE; |
| 161 | $system = variable_get('recipe_ingredient_system', 'complex'); |
| 162 | if ($system == 'complex') { |
| 163 | $form['ingredients']['headings'] = array( |
| 164 | '#value' => '<div><table ><thead><tr><th>'. t('Quantity') .'</th><th>'. |
| 165 | t('Units') .'</th><th>'. t('Ingredient Name') . |
| 166 | '</th></tr></thead>'."\n".'<tbody>', |
| 167 | ); |
| 168 | } |
| 169 | else { |
| 170 | $form['ingredients']['headings'] = array( |
| 171 | '#value' => '<div><table ><thead><tr><th>'. t('Ingredients') .'</th></tr></thead>'."\n".'<tbody>', |
| 172 | ); |
| 173 | } |
| 174 | $rows = array(); |
| 175 | $callback = 'recipe/ingredient/autocomplete'; |
| 176 | $num_ingredients = 0; |
| 177 | if ($node->ingredients) { |
| 178 | foreach ($node->ingredients as $id => $ingredient) { |
| 179 | $num_ingredients = $id+1; |
| 180 | if ($id == 0) { |
| 181 | $j = '0'; |
| 182 | } else { |
| 183 | $j = $id; |
| 184 | } |
| 185 | |
| 186 | // For preview, node->ingredients is an array, for actual display, it's an object |
| 187 | $name = ''; |
| 188 | $unit_id = ''; |
| 189 | $abbreviation = ''; |
| 190 | $quantity = 0; |
| 191 | if (is_array($ingredient)) { |
| 192 | $name = $ingredient['name']; |
| 193 | $unit_id = $ingredient['unit_id']; |
| 194 | $abbreviation = $ingredient['abbreviation']; |
| 195 | $quantity = $ingredient['quantity']; |
| 196 | } else { |
| 197 | $name = $ingredient->name; |
| 198 | $unit_id = $ingredient->unit_id; |
| 199 | $abbreviation = $ingredient->abbreviation; |
| 200 | $quantity = $ingredient->quantity; |
| 201 | } |
| 202 | |
| 203 | if ($name && isset($quantity)) { |
| 204 | // When can the following statement be true? |
| 205 | if (!$ingredient) { |
| 206 | drupal_set_message(t('Recipe Module: An error has occured. Please report this error to the system administrator.'), 'error'); |
| 207 | if (is_array($ingredient)) { |
| 208 | $ingredient['quantity'] = ''; |
| 209 | $ingredient['unit_id'] = 21; |
| 210 | $ingredient['name'] = ''; |
| 211 | } else { |
| 212 | $ingredient->quantity = ''; |
| 213 | $ingredient->unit_id = 21; |
| 214 | $ingredient->name = ''; |
| 215 | } |
| 216 | } |
| 217 | if ($system == 'complex') { |
| 218 | $form['ingredients'][$j]['open_tags'] = array( |
| 219 | '#value' => '<tr><th>', |
| 220 | ); |
| 221 | $form['ingredients'][$j]['quantity'] = array( |
| 222 | '#type' => 'textfield', |
| 223 | '#title' => '', |
| 224 | '#default_value' => preg_replace('/\⁄/', '/', recipe_ingredient_quantity_from_decimal($quantity)), |
| 225 | '#size' => 8, |
| 226 | '#maxlength' => 8, |
| 227 | ); |
| 228 | $form['ingredients'][$j]['mid1_tags'] = array( |
| 229 | '#value' => '</th><th>', |
| 230 | ); |
| 231 | $form['ingredients'][$j]['unit_id'] = array( |
| 232 | '#type' => 'select', |
| 233 | '#title' => '', |
| 234 | '#default_value' => $unit_id, |
| 235 | '#options' => recipe_unit_options(), |
| 236 | ); |
| 237 | $form['ingredients'][$j]['mid2_tags'] = array( |
| 238 | '#value' => '</th><th>', |
| 239 | ); |
| 240 | $form['ingredients'][$j]['name'] = array( |
| 241 | '#type' => 'textfield', |
| 242 | '#title' => '', |
| 243 | '#default_value' => $name, |
| 244 | '#size' => 64, |
| 245 | '#maxlength' => 128, |
| 246 | '#autocomplete_path' => $callback, |
| 247 | ); |
| 248 | $form['ingredients'][$j]['close_tags'] = array( |
| 249 | '#value' => '</th></tr>', |
| 250 | ); |
| 251 | } |
| 252 | else { |
| 253 | if ($name) { |
| 254 | if ($quantity == 0) { |
| 255 | $quantity = ''; |
| 256 | } |
| 257 | else { |
| 258 | $quantity .= ' '; |
| 259 | } |
| 260 | if ($abbreviation != '') { |
| 261 | $abbreviation .= ' '; |
| 262 | } |
| 263 | $name = $quantity . $abbreviation . $name; |
| 264 | } |
| 265 | $form['ingredients'][$j]['open_tags'] = array( |
| 266 | '#value' => '<tr><th>', |
| 267 | '#tree' => TRUE, |
| 268 | ); |
| 269 | $form['ingredients'][$j]['name'] = array( |
| 270 | '#type' => 'textfield', |
| 271 | '#title' => '', |
| 272 | '#default_value' => $name, |
| 273 | '#size' => 64, |
| 274 | '#maxlength' => 128, |
| 275 | '#autocomplete_path' => $callback, |
| 276 | ); |
| 277 | $form['ingredients'][$j]['close_tags'] = array( |
| 278 | '#value' => '</th></tr>', |
| 279 | ); |
| 280 | } // else |
| 281 | } // if ($ingredient->name && isset($ingredient->quantity)) |
| 282 | } // foreach ($node->ingredients as $id => $ingredient) |
| 283 | } // if ($node->ingredients) |
| 284 | // Add ten more spots for ingredients than are already used |
| 285 | for ($i = $num_ingredients; $i < $num_ingredients+10; $i++) { |
| 286 | if ($i == 0) { |
| 287 | $j = '0'; |
| 288 | } |
| 289 | else { |
| 290 | $j = $i; |
| 291 | } |
| 292 | if ($system == 'complex') { |
| 293 | $form['ingredients'][$j]['open_tags'] = array( |
| 294 | '#value' => '<tr><th>', |
| 295 | ); |
| 296 | $form['ingredients'][$j]['quantity'] = array( |
| 297 | '#type' => 'textfield', |
| 298 | '#title' => '', |
| 299 | '#size' => 8, |
| 300 | '#maxlength' => 8, |
| 301 | ); |
| 302 | $form['ingredients'][$j]['mid1_tags'] = array( |
| 303 | '#value' => '</th><th>', |
| 304 | ); |
| 305 | $form['ingredients'][$j]['unit_id'] = array( |
| 306 | '#type' => 'select', |
| 307 | '#title' => '', |
| 308 | '#options' => recipe_unit_options(), |
| 309 | '#default_value' => 2, |
| 310 | ); |
| 311 | $form['ingredients'][$j]['mid2_tags'] = array( |
| 312 | '#value' => '</th><th>', |
| 313 | ); |
| 314 | $form['ingredients'][$j]['name'] = array( |
| 315 | '#type' => 'textfield', |
| 316 | '#title' => '', |
| 317 | '#size' => 64, |
| 318 | '#maxlength' => 128, |
| 319 | '#autocomplete_path' => $callback, |
| 320 | ); |
| 321 | $form['ingredients'][$j]['close_tags'] = array( |
| 322 | '#value' => '</th></tr>', |
| 323 | ); |
| 324 | } |
| 325 | else { |
| 326 | $form['ingredients'][$j]['open_tags'] = array( |
| 327 | '#value' => '<tr><th>', |
| 328 | ); |
| 329 | $form['ingredients'][$j]['name'] = array( |
| 330 | '#type' => 'textfield', |
| 331 | '#title' => '', |
| 332 | '#size' => 64, |
| 333 | '#maxlength' => 128, |
| 334 | '#autocomplete_path' => $callback, |
| 335 | ); |
| 336 | $form['ingredients'][$j]['close_tags'] = array( |
| 337 | '#value' => '</th></tr>', |
| 338 | ); |
| 339 | } |
| 340 | } |
| 341 | $form['ingredients']['end'] = array( |
| 342 | '#value' => '</tbody>'."\n".'</table></div>'."\n", |
| 343 | ); |
| 344 | |
| 345 | $form['instructions'] = array( |
| 346 | '#type' => 'textarea', |
| 347 | '#title' => t('Instructions'), |
| 348 | '#default_value' => $node->instructions, |
| 349 | '#cols' => 60, |
| 350 | '#rows' => 10, |
| 351 | '#description' => t('Step by step instructions on how to prepare and cook the recipe.'), |
| 352 | ); |
| 353 | $form['notes'] = array( |
| 354 | '#type' => 'textarea', |
| 355 | '#title' => t("Additional Notes"), |
| 356 | '#default_value' => $node->notes, |
| 357 | '#cols' => 60, |
| 358 | '#rows' => 5, |
| 359 | '#description' => t("Optional. Describe a great dining experience relating to this recipe, or note which wine or other dishes complement this recipe"), |
| 360 | ); |
| 361 | $form['filter'] = filter_form($node->format); |
| 362 | |
| 363 | return $form; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Settings form for menu callback |
| 368 | */ |
| 369 | function recipe_admin_settings() { |
| 370 | $form['recipe_index_depth'] = array( |
| 371 | '#type' => 'select', |
| 372 | '#title' => t('Index Depth'), |
| 373 | '#default_value' => variable_get('recipe_index_depth', 0), |
| 374 | '#options' => array(0 => t('All Terms'), 1 => "1", 2 => "2", 3 => "3", 4 => "4", 5 => "5", 6 => "6"), |
| 375 | '#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."), |
| 376 | ); |
| 377 | $form['recipe_recent_box_enable'] = array( |
| 378 | '#type' => 'radios', |
| 379 | '#title' => t('Recent Recipes Box'), |
| 380 | '#default_value' => variable_get('recipe_recent_box_enable', 1), |
| 381 | '#options' => array(t('Disabled'), t('Enabled')), |
| 382 | '#description' => t('Enables or Disables the recent recipes box on the recipes index page.'), |
| 383 | '#required' => false, |
| 384 | ); |
| 385 | $form['recipe_recent_box_title'] = array( |
| 386 | '#type' => 'textfield', |
| 387 | '#title' => t('Box Title'), |
| 388 | '#default_value' => variable_get('recipe_recent_box_title', t('Latest Recipes')), |
| 389 | '#size' => 35, |
| 390 | '#maxlength' => 255, |
| 391 | '#description' => t('Title of the Recent Recipes Box on the Recipes index page.'), |
| 392 | ); |
| 393 | $form['recipe_recent_display'] = array( |
| 394 | '#type' => 'select', |
| 395 | '#title' => t('Recipes to Display'), |
| 396 | '#default_value' => variable_get('recipe_recent_display', '5'), |
| 397 | '#options' => array(5 => "5", 10 => "10", 15 => "15"), |
| 398 | '#description' => t("Sets the number of recent recipes that will be displayed in the Recent Recipes box. (0 = not displayed)."), |
| 399 | ); |
| 400 | $form['recipe_help'] = array( |
| 401 | '#type' => 'textarea', |
| 402 | '#title' => t('Explanation or submission guidelines'), |
| 403 | '#default_value' => variable_get('recipe_help', ''), |
| 404 | '#cols' => 55, |
| 405 | '#rows' => 4, |
| 406 | '#description' => t('This text will be displayed at the top of the recipe submission form. Useful for helping or instructing your users.'), |
| 407 | ); |
| 408 | $options = array('simple' => t('Simple'), 'complex' => t('Complex')); |
| 409 | $form['recipe_ingredient_system'] = array( |
| 410 | '#type' => 'radios', |
| 411 | '#title' => t('Ingredient entering system'), |
| 412 | '#default_value' => variable_get('recipe_ingredient_system', 'complex'), |
| 413 | '#options' => $options, |
| 414 | '#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'), |
| 415 | ); |
| 416 | $form['recipe_fraction_display'] = array( |
| 417 | '#type' => 'textfield', |
| 418 | '#title' => t('Fractions Display String'), |
| 419 | '#default_value' => variable_get('recipe_fraction_display', t('{%d }%d⁄%d')), |
| 420 | '#size' => 35, |
| 421 | '#maxlength' => 255, |
| 422 | '#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>"'), |
| 423 | ); |
| 424 | $form['recipe_export_html_enable'] = array( |
| 425 | '#type' => 'radios', |
| 426 | '#title' => t('Export HTML'), |
| 427 | '#default_value' => variable_get('recipe_export_html_enable', 1), |
| 428 | '#options' => array(t('Disabled'), t('Enabled')), |
| 429 | '#description' => t('Enables or Disables the Export as HTML link.'), |
| 430 | '#required' => false, |
| 431 | ); |
| 432 | $form['recipe_export_recipeml_enable'] = array( |
| 433 | '#type' => 'radios', |
| 434 | '#title' => t('Export RecipeML'), |
| 435 | '#default_value' => variable_get('recipe_export_recipeml_enable', 1), |
| 436 | '#options' => array(t('Disabled'), t('Enabled')), |
| 437 | '#description' => t('Enables or Disables the Export as RecipeML link.'), |
| 438 | '#required' => false, |
| 439 | ); |
| 440 | return system_settings_form($form); |
| 441 | } |
| 442 | |
| 443 | /** |
| 444 | * Implementation of hook_menu(). |
| 445 | * |
| 446 | * Note: when editing this function you must visit 'admin/menu' to reset the cache |
| 447 | */ |
| 448 | function recipe_menu() { |
| 449 | $items = array(); |
| 450 | $items['recipe'] = array( |
| 451 | 'title' => t('Recipes'), |
| 452 | 'page callback' => 'recipe_page', |
| 453 | 'access arguments' => array('access content'), |
| 454 | 'type' => MENU_SUGGESTED_ITEM |
| 455 | ); |
| 456 | $items['recipe/ingredient/autocomplete'] = array( |
| 457 | 'title' => t('Ingredient autocomplete'), |
| 458 | 'page callback' => 'recipe_autocomplete_page', |
| 459 | 'type' => MENU_CALLBACK, |
| 460 | 'access arguments' => array('access content') |
| 461 | ); |
| 462 | $items['recipe/export'] = array( |
| 463 | 'page callback' => 'recipe_export', |
| 464 | 'type' => MENU_CALLBACK, |
| 465 | 'access arguments' => array('access content') |
| 466 | ); |
| 467 | $items['admin/settings/recipe'] = array( |
| 468 | 'title' => t('Recipe module'), |
| 469 | 'description' => t('Settings that control how the recipe module functions.'), |
| 470 | 'page callback' => 'drupal_get_form', |
| 471 | 'page arguments' => array('recipe_admin_settings'), |
| 472 | 'access arguments' => array('administer site configuration'), |
| 473 | 'type' => MENU_NORMAL_ITEM, |
| 474 | ); |
| 475 | return $items; |
| 476 | } |
| 477 | |
| 478 | /** |
| 479 | * Implementation of hook_access(). |
| 480 | */ |
| 481 | function recipe_access($op, $node, $account) { |
| 482 | global $user; |
| 483 | |
| 484 | if ($op == 'create') { |
| 485 | // Only users with permission to do so may create this node type. |
| 486 | return user_access('create recipes'); |
| 487 | } |
| 488 | |
| 489 | // Users who create a node may edit or delete it later, assuming they have the |
| 490 | // necessary permissions. |
| 491 | if ($op == 'update' || $op == 'delete') { |
| 492 | if (user_access('edit own recipes') && ($user->uid == $node->uid)) { |
| 493 | return TRUE; |
| 494 | } |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | /** |
| 499 | * Implementation of hook_block(). |
| 500 | */ |
| 501 | function recipe_block($op = 'list', $delta = 0, $edit = array()) { |
| 502 | // The $op parameter determines what piece of information is being requested. |
| 503 | switch ($op) { |
| 504 | case 'list': |
| 505 | // If $op is "list", we just need to return a list of block descriptions. |
| 506 | // This is used to provide a list of possible blocks to the administrator, |
| 507 | // end users will not see these descriptions. |
| 508 | $blocks[0]['info'] = t('Newest recipes'); |
| 509 | return $blocks; |
| 510 | case 'view': |
| 511 | // If $op is "view", then we need to generate the block for display |
| 512 | // purposes. The $delta parameter tells us which block is being requested. |
| 513 | switch ($delta) { |
| 514 | case 0: |
| 515 | // The subject is displayed at the top of the block. Note that it |
| 516 | // should be passed through t() for translation. |
| 517 | $block['subject'] = t('Newest Recipes'); |
| 518 | // The content of the block is typically generated by calling a custom |
| 519 | // function. |
| 520 | $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); |
| 521 | $block["content"] = node_title_list($result); |
| 522 | break; |
| 523 | } |
| 524 | return $block; |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | /** |
| 529 | * Implementation of hook_view(). |
| 530 | */ |
| 531 | function recipe_view(&$node, $teaser = FALSE, $page = FALSE) { |
| 532 | if ($page) { |
| 533 | drupal_set_breadcrumb(array(l(t('Home'), ''), l(t('Recipes'), 'recipe'))); |
| 534 | drupal_add_css(drupal_get_path('module', 'recipe') .'/recipe.css'); |
| 535 | } |
| 536 | $node = recipe_node_prepare($node, $teaser); |
| 537 | |
| 538 | $node->content['body'] = array( |
| 539 | '#value' => $teaser ? $node->teaser : theme('recipe_node', $node, $page), |
| 540 | '#weight' => 1, |
| 541 | ); |
| 542 | |
| 543 | return $node; |
| 544 | } |
| 545 | |
| 546 | /** |
| 547 | * Returns a cached array of recipe unit types |
| 548 | */ |
| 549 | function recipe_unit_options() { |
| 550 | static $options; |
| 551 | static $unit_rs; |
| 552 | if ( !isset( $unit_rs ) ) { |
| 553 | $unit_rs = db_query('SELECT id,type,name,abbreviation FROM {recipe_unit} ORDER BY type ASC, metric'); |
| 554 | $options = array(); |
| 555 | while ($r = db_fetch_object($unit_rs)) { |
| 556 | if (isset($r->type)) { |
| 557 | if (!isset($options[$r->type])) { |
| 558 | $options[$r->type] = array(); |
| 559 | } |
| 560 | $options[$r->type][$r->id] = $r->name .' ('. $r->abbreviation .')'; |
| 561 | } |
| 562 | else { |
| 563 | $options[$r->id] = $r->name .' ('. $r->abbreviation .')'; |
| 564 | } |
| 565 | } |
| 566 | } |
| 567 | return $options; |
| 568 | } |
| 569 | |
| 570 | /** |
| 571 | * Converts a recipe ingredient name to and ID |
| 572 | */ |
| 573 | function recipe_ingredient_id_from_name($name) { |
| 574 | static $cache; |
| 575 | |
| 576 | if (!$cache[$name]) { |
| 577 | $ingredient_id = db_result(db_query("SELECT id FROM {recipe_ingredient} WHERE LOWER(name)='%s'", trim(strtolower($name)))); |
| 578 | |
| 579 | if (!$ingredient_id) { |
| 580 | global $active_db; |
| 581 | $node_link = db_result(db_query("SELECT nid FROM {node} n WHERE title = '%s'", $name)); |
| 582 | |
| 583 | db_query("INSERT INTO {recipe_ingredient} (name, link) VALUES ('%s', '%s')", $name, $node_link); |
| 584 | $ingredient_id = db_result(db_query("SELECT id FROM {recipe_ingredient} WHERE LOWER(name)='%s'", trim(strtolower($name)))); |
| 585 | } |
| 586 | $cache[$name] = $ingredient_id; |
| 587 | } |
| 588 | |
| 589 | return $cache[$name]; |
| 590 | } |
| 591 | |
| 592 | /** |
| 593 | * Converts an ingredient's quantity from decimal to fraction |
| 594 | */ |
| 595 | function recipe_ingredient_quantity_from_decimal($ingredient_quantity) { |
| 596 | if (strpos($ingredient_quantity, '.') && variable_get('recipe_fraction_display', t('{%d} %d⁄%d'))) { |
| 597 | $decimal = $ingredient_quantity; |
| 598 | |
| 599 | if ($decimal == 0) { |
| 600 | $whole = 0; |
| 601 | $numerator = 0; |
| 602 | $denominator = 1; |
| 603 | $top_heavy = 0; |
| 604 | } |
| 605 | else { |
| 606 | $sign = 1; |
| 607 | if ($decimal < 0) |
| 608 | $sign = -1; |
| 609 | } |
| 610 | |
| 611 | if (floor(abs($decimal)) == 0) { |
| 612 | $whole = 0; |
| 613 | $conversion = abs($decimal); |
| 614 | } |
| 615 | else { |
| 616 | $whole = floor(abs($decimal)); |
| 617 | $conversion = abs($decimal); |
| 618 | } |
| 619 | |
| 620 | $power = 1; |
| 621 | $flag = 0; |
| 622 | while ($flag == 0) { |
| 623 | $argument = $conversion * $power; |
| 624 | if ($argument == floor($argument)) { |
| 625 | $flag = 1; |
| 626 | } |
| 627 | else { |
| 628 | $power = $power * 10; |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | // workaround for thirds, sixths, ninths, twelfths |
| 633 | $overrides = array( |
| 634 | '3333' => array(1, 3), '6666' => array(2, 3), '9999' => array(3, 3), // thirds |
| 635 | '1666' => array(1, 6), '8333' => array(5, 6), // sixths |
| 636 | '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 |
| 637 | '0833' => array(1, 12), '4166' => array(5, 12), '5833' => array(7, 12), '9166' => array(11, 12), // twelfths |
| 638 | ); |
| 639 | $conversionstr = substr((string) ($conversion - floor($conversion)), 2, 4); |
| 640 | if (array_key_exists($conversionstr, $overrides)) { |
| 641 | if ($overrides[$conversionstr][0] == $overrides[$conversionstr][1]) { |
| 642 | return ($whole + 1) * $sign; |
| 643 | } |
| 644 | $denominator = $overrides[$conversionstr][1]; |
| 645 | $numerator = (floor($conversion) * $denominator) + $overrides[$conversionstr][0]; |
| 646 | } |
| 647 | else { |
| 648 | $numerator = $conversion * $power; |
| 649 | $denominator = $power; |
| 650 | } |
| 651 | |
| 652 | $hcf = recipe_euclid($numerator, $denominator); |
| 653 | |
| 654 | $numerator = $numerator/$hcf; |
| 655 | $denominator = $denominator/$hcf; |
| 656 | $whole = $sign * $whole; |
| 657 | $top_heavy = $sign * $numerator; |
| 658 | |
| 659 | $numerator = abs($top_heavy) - (abs($whole) * $denominator); |
| 660 | |
| 661 | if (($whole == 0) && ($sign == -1)) { |
| 662 | $numerator = $numerator * $sign; |
| 663 | } |
| 664 | |
| 665 | $ingredient_quantity = sprintf(variable_get('recipe_fraction_display', t('{%d} %d⁄%d')), $whole, $numerator, $denominator); |
| 666 | |
| 667 | if ( ($whole == 0) && (strpos($ingredient_quantity, '{') >= 0) ) { |
| 668 | /* remove anything in curly braces */ |
| 669 | $ingredient_quantity = preg_replace('/{.*}/', '', $ingredient_quantity); |
| 670 | } |
| 671 | else { |
| 672 | /* remove just the curly braces, but keep everything between them */ |
| 673 | $ingredient_quantity = preg_replace('/{|}/', '', $ingredient_quantity); |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | return filter_xss_admin($ingredient_quantity); |
| 678 | } |
| 679 | |
| 680 | /** |
| 681 | * Converts an ingredient's quantity from fractions to decimal |
| 682 | */ |
| 683 | function recipe_ingredient_quantity_from_fraction($ingredient_quantity) { |
| 684 | if ($pos_slash = strpos($ingredient_quantity, '/')) { |
| 685 | $pos_space = strpos($ingredient_quantity, ' '); |
| 686 | // can't trust $pos_space to be a zero value if there is no space |
| 687 | // so set it explicitly |
| 688 | if ($pos_space === false) |
| 689 | $pos_space = 0; |
| 690 | |
| 691 | $whole = substr($ingredient_quantity, 0, $pos_space); |
| 692 | $numerator = substr($ingredient_quantity, $pos_space, $pos_slash); |
| 693 | $denominator = substr($ingredient_quantity, $pos_slash+1); |
| 694 | $ingredient_quantity = $whole+($numerator/$denominator); |
| 695 | } |
| 696 | |
| 697 | return $ingredient_quantity; |
| 698 | } |
| 699 | |
| 700 | /** |
| 701 | * Saves the changed ingredients of a recipe node to the database |
| 702 | * (by comparing the old and new ingredients first) |
| 703 | */ |
| 704 | function recipe_save_ingredients($node) { |
| 705 | if (!$node->ingredients) { |
| 706 | $node->ingredients = array(); |
| 707 | } |
| 708 | $changes = recipe_ingredients_diff($node->ingredients, recipe_load_ingredients($node)); |
| 709 | |
| 710 | if (count($changes->remove) > 0) { |
| 711 | $ids = implode(',', $changes->remove); |
| 712 | db_query("DELETE FROM {recipe_node_ingredient} WHERE id IN (%s)", $ids); |
| 713 | } |
| 714 | |
| 715 | foreach ($changes->add as $ingredient) { |
| 716 | $ingredient->id = recipe_ingredient_id_from_name($ingredient->name); |
| 717 | $ingredient->quantity = recipe_ingredient_quantity_from_fraction($ingredient->quantity); |
| 718 | 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); |
| 719 | } |
| 720 | |
| 721 | foreach ($changes->update as $ingredient) { |
| 722 | $ingredient->id = recipe_ingredient_id_from_name($ingredient->name); |
| 723 | $ingredient->quantity = recipe_ingredient_quantity_from_fraction($ingredient->quantity); |
| 724 | 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); |
| 725 | } |
| 726 | } |
| 727 | |
| 728 | /** |
| 729 | * Compares two arrays of ingredients and returns the differences |
| 730 | */ |
| 731 | function recipe_ingredients_diff($a1, $a2) { |
| 732 | $return->add = array(); |
| 733 | $return->remove = array(); |
| 734 | $return->update = array(); |
| 735 | |
| 736 | foreach ($a1 as $pl) { |
| 737 | $pl = (object)$pl; |
| 738 | $pl->name = trim($pl->name); |
| 739 | if ($pl->name) { |
| 740 | if (!_in_array($pl, $return->add)) { |
| 741 | // Duplicate entries for the same ingredient are ignored. |
| 742 | if (!_in_array($pl, $a2)) |
| 743 | $return->add[] = $pl; |
| 744 | else |
| 745 | if (!_in_array($pl, $return->update)) |
| 746 | $return->update[] = $pl; |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | foreach ($a2 as $k => $pl) { |
| 751 | if (!_in_array($pl, $a1)) { |
| 752 | $return->remove[] = $pl->id; |
| 753 | } |
| 754 | } |
| 755 | return $return; |
| 756 | } |
| 757 | |
| 758 | /** |
| 759 | * Custom in_array() function because PHP 4 in_aray() doesnt seem to |
| 760 | * handle the first arguement being an object |
| 761 | */ |
| 762 | function _in_array($a, $b) { |
| 763 | $a->name = trim(strtolower($a->name)); |
| 764 | foreach ($b as $row) { |
| 765 | $compareto=""; |
| 766 | if (is_array($row)) { |
| 767 | $compareto = trim(strtolower($row["name"])); |
| 768 | } |
| 769 | else { |
| 770 | $compareto = trim(strtolower($row->name)); |
| 771 | } |
| 772 | if ($a->name === $compareto) |
| 773 | return true; |
| 774 | } |
| 775 | return false; |
| 776 | } |
| 777 | |
| 778 | /** |
| 779 | * Loads the ingredients for a recipe |
| 780 | */ |
| 781 | function recipe_load_ingredients($node) { |
| 782 | $rs = db_query(' |
| 783 | SELECT |
| 784 | ri.id, |
| 785 | i.name, |
| 786 | i.link, |
| 787 | ri.quantity, |
| 788 | ri.unit_id, |
| 789 | u.abbreviation, |
| 790 | ri.ingredient_id |
| 791 | FROM |
| 792 | {recipe_node_ingredient} ri, |
| 793 | {recipe_ingredient} i, |
| 794 | {recipe_unit} u |
| 795 | WHERE |
| 796 | ri.ingredient_id = i.id |
| 797 | AND ri.unit_id = u.id |
| 798 | AND ri.nid=%d |
| 799 | ORDER BY |
| 800 | ri.id', $node->nid); |
| 801 | $ingredients = array(); |
| 802 | while ($ingredient = db_fetch_object($rs)) { |
| 803 | $ingredients[] = $ingredient; |
| 804 | } |
| 805 | return $ingredients; |
| 806 | } |
| 807 | |
| 808 | /** |
| 809 | * Converts a recipe unit ID to it's abbreviation |
| 810 | */ |
| 811 | function recipe_unit_abbreviation($unit_id) { |
| 812 | static $abbreviations; |
| 813 | |
| 814 | if (!$abbreviations) { |
| 815 | $rs = db_query('SELECT id,abbreviation FROM {recipe_unit}'); |
| 816 | while ($unit = db_fetch_object($rs)) { |
| 817 | $abbreviations[$unit->id] = $unit->abbreviation; |
| 818 | } |
| 819 | } |
| 820 | |
| 821 | return $abbreviations[$unit_id]; |
| 822 | } |
| 823 | |
| 824 | /** |
| 825 | * Converts a recipe unit ID to it's name */ |
| 826 | function recipe_unit_name($unit_id) { |
| 827 | static $unit_names; |
| 828 | |
| 829 | if (!$unit_names) { |
| 830 | $rs = db_query('SELECT id,name FROM {recipe_unit}'); |
| 831 | while ($unit = db_fetch_object($rs)) { |
| 832 | $unit_names[$unit->id] = $unit->name; |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | return $unit_names[$unit_id]; |
| 837 | } |
| 838 | |
| 839 | /** |
| 840 | * Menu callback; Generates various representation of a recipe page with |
| 841 | * all descendants and prints the requested representation to output. |
| 842 | * |
| 843 | * The function delegates the generation of output to helper functions. |
| 844 | * The function name is derived by prepending 'recipe_export_' to the |
| 845 | * given output type. So, e.g., a type of 'html' results in a call to |
| 846 | * the function recipe_export_html(). |
| 847 | * |
| 848 | * @param type |
| 849 | * - a string encoding the type of output requested. |
| 850 | * The following types are currently supported in recipe module |
| 851 | * html: HTML (printer friendly output) |
| 852 | * recipeml: XML (RecipeML formatted output) |
| 853 | * Other types can be supported with contributed modules. |
| 854 | * @param nid |
| 855 | * - an integer representing the node id (nid) of the node to export |
| 856 | * |
| 857 | */ |
| 858 | function recipe_export($type = 'html', $nid = 0) { |
| 859 | $type = drupal_strtolower($type); |
| 860 | $export_function = 'recipe_export_'. $type; |
| 861 | |
| 862 | if (function_exists($export_function)) { |
| 863 | echo call_user_func($export_function, $nid); |
| 864 | } |
| 865 | else { |
| 866 | drupal_set_message(t('Unknown export format.')); |
| 867 | drupal_not_found(); |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | /** |
| 872 | * This function is called by recipe_export() to generate HTML for export. |
| 873 | * |
| 874 | * @param nid |
| 875 | * - an integer representing the node id (nid) of the node to export |
| 876 | * @return |
| 877 | * - string containing HTML representing the recipe |
| 878 | */ |
| 879 | function recipe_export_html($nid) { |
| 880 | if ($nid == 0) { |
| 881 | drupal_goto('recipe'); |
| 882 | } |
| 883 | $node = node_load(array('nid' => $nid, 'type' => 'recipe')); |
| 884 | |
| 885 | $node = recipe_node_prepare($node, FALSE); |
| 886 | $output = theme('recipe_node', $node, FALSE); |
| 887 | |
| 888 | $html = theme('recipe_export_html', check_plain($node->title), $output); |
| 889 | return $html; |
| 890 | } |
| 891 | |
| 892 | /** |
| 893 | * This function is called by recipe_export() to generate RecipeML for export. |
| 894 | * |
| 895 | * @param nid |
| 896 | * - an integer representing the node id (nid) of the node to export |
| 897 | * @return |
| 898 | * - string containing the recipe in RecipeML |
| 899 | */ |
| 900 | function recipe_export_recipeml($nid) { |
| 901 | if ($nid == 0) { |
| 902 | drupal_goto('recipe'); |
| 903 | } |
| 904 | |
| 905 | $node = node_load(array('nid' => $nid, 'type' => 'recipe')); |
| 906 | |
| 907 | drupal_set_header('Content-type: text/xml'); |
| 908 | |
| 909 | $output = '<?xml version="1.0" encoding="UTF-8"?>'."\n". |
| 910 | '<!DOCTYPE recipeml PUBLIC "-//FormatData//DTD RecipeML 0.5//EN" "http://www.formatdata.com/recipeml/recipeml.dtd">'."\n". |
| 911 | '<recipeml version="0.5">'."\n". |
| 912 | ' <recipe>'."\n". |
| 913 | ' <head>'."\n". |
| 914 | ' <title>'. $node->title .'</title>'."\n". |
| 915 | ' </head>'."\n". |
| 916 | ' <yield><qty>'. $node->yield .'</qty></yield>'."\n". |
| 917 | ' <ingredients>'; |
| 918 | |
| 919 | foreach ($node->ingredients as $ingredient) { |
| 920 | $output .= "\n".'<ing><amt><qty>'. $ingredient->quantity .'</qty><unit>'. $ingredient->abbreviation .'</unit></amt><item>'. $ingredient->name .'</item></ing>'; |
| 921 | } |
| 922 | |
| 923 | $output .= "\n". |
| 924 | ' </ingredients>'."\n". |
| 925 | ' <directions>'. $node->instructions .'</directions>'."\n". |
| 926 | ' </recipe>'."\n". |
| 927 | '</recipeml>'; |
| 928 | |
| 929 | return $output; |
| 930 | } |
| 931 | |
| 932 | /** |
| 933 | * Callback function for ingredient autocomplete |
| 934 | */ |
| 935 | function recipe_autocomplete_page($string = "", $limit = 10) { |
| 936 | $matches = array(); |
| 937 | $rs = db_query("SELECT name FROM {recipe_ingredient} WHERE LOWER(name) LIKE '%s%%' ORDER BY name LIMIT %d", strtolower($string), $limit); |
| 938 | while ($r = db_fetch_object($rs)) { |
| 939 | $matches[$r->name] = check_plain($r->name); |
| 940 | } |
| 941 | print drupal_to_js($matches); |
| 942 | exit(); |
| 943 | } |
| 944 | |
| 945 | /** |
| 946 | * Implementation of hook_validate(). |
| 947 | * |
| 948 | * Errors should be signaled with form_set_error(). |
| 949 | */ |
| 950 | function recipe_validate(&$node) { |
| 951 | if (!$node->ingredients) return; |
| 952 | $ingredients = array(); |
| 953 | foreach ($node->ingredients as $key => $ingredient) { |
| 954 | $ingredient = (object)$ingredient; |
| 955 | if (!isset($ingredient->quantity)) { |
| 956 | $ingredient = recipe_parse_ingredient_string($ingredient->name); |
| 957 | } |
| 958 | if ($ingredient->name && _in_array($ingredient, $ingredients)) { |
| 959 | form_set_error("recipe", t('Duplicate ingredients are not allowed.')); |
| 960 | } |
| 961 | else { |
| 962 | $ingredients[] = $ingredient; |
| 963 | } |
| 964 | $node->ingredients[$key] = $ingredient; |
| 965 | } |
| 966 | } |
| 967 | |
| 968 | /** |
| 969 | * Converts an ingredients name string to an ingredient object |
| 970 | */ |
| 971 | function recipe_parse_ingredient_string($ingredient_string) { |
| 972 | if (preg_match('#([0-9.]+(?:\s?\d*/\d*)?\s?)?(?:([a-zA-Z.]*)\s)?(.*)#', trim($ingredient_string), $matches)) { |
| 973 | $ingredient->name = $matches[3]; |
| 974 | $ingredient->quantity = trim($matches[1]); |
| 975 | if ($ingredient->quantity == 0) { |
| 976 | $ingredient->quantity = 0; |
| 977 | } |
| 978 | $t_unit = $matches[2]; |
| 979 | $unit = recipe_unit_from_name($t_unit); |
| 980 | |
| 981 | if ($unit) { |
| 982 | $ingredient->unit_id = $unit->id; |
| 983 | $ingredient->abbreviation = $unit->abbreviation; |
| 984 | } |
| 985 | else { |
| 986 | $ingredient->unit_id = 29; |
| 987 | $ingredient->abbreviation = ''; |
| 988 | $ingredient->name = $t_unit .' '. $ingredient->name; |
| 989 | } |
| 990 | |
| 991 | $ingredient->name = trim($ingredient->name); |
| 992 | |
| 993 | return $ingredient; |
| 994 | } |
| 995 | else { |
| 996 | return false; |
| 997 | } |
| 998 | } |
| 999 | |
| 1000 | /** |
| 1001 | * Returns information about a unit based on a unit abbreviation or name |
| 1002 | */ |
| 1003 | function recipe_unit_from_name($name) { |
| 1004 | if (strlen($name) > 1) |
| 1005 | $string = strtolower($name); |
| 1006 | else |
| 1007 | $string = $name; |
| 1008 | $ending = substr($string, -1, 1); |
| 1009 | if ($ending == 's' && $string != 'ds' || $ending == '.') { |
| 1010 | $string = substr($string, 0, strlen($string) -1); |
| 1011 | } |
| 1012 | $ending = substr($string, -1, 1); |
| 1013 | if ($ending == 's' && $string != 'ds'|| $ending == '.') { |
| 1014 | $string = substr($string, 0, strlen($string) -1); |
| 1015 | } |
| 1016 | |
| 1017 | static $units_array; |
| 1018 | |
| 1019 | if (!$units_array) { |
| 1020 | $rs = db_query('SELECT id,name,abbreviation FROM {recipe_unit}'); |
| 1021 | while ($unit = db_fetch_object($rs)) { |
| 1022 | $units_array[strtolower($unit->name)] = $unit; |
| 1023 | $units_array[$unit->abbreviation] = $unit; |
| 1024 | } |
| 1025 | } |
| 1026 | |
| 1027 | return $units_array[$string]; |
| 1028 | } |
| 1029 | |
| 1030 | /** |
| 1031 | * Menu Callback - created output for the main recipe page. |
| 1032 | * |
| 1033 | * @return $body |
| 1034 | */ |
| 1035 | function recipe_page() { |
| 1036 | $body = ""; |
| 1037 | |
| 1038 | if (arg(1) == "feed") { |
| 1039 | module_invoke('node', 'feed', module_invoke('taxonomy', 'select_nodes', recipe_get_recipe_terms(), 'or', 0, FALSE)); |
| 1040 | } |
| 1041 | else { |
| 1042 | if (arg(1) != NULL) { |
| 1043 | $breadcrumb = drupal_get_breadcrumb(); |
| 1044 | $term = recipe_build_breadcrumbs($breadcrumb); |
| 1045 | drupal_set_breadcrumb($breadcrumb); |
| 1046 | |
| 1047 | if ($term != NULL) { |
| 1048 | $content = recipe_index($term->tid); |
| 1049 | if ($content != '') { |
| 1050 | $body = theme('box', $term->name .'- '. t('Sub Categories'), $content); |
| 1051 | } |
| 1052 | |
| 1053 | $terms = array_merge(array($term->tid), array_map('_recipe_get_tid_from_term', module_invoke('taxonomy', 'get_children', $term->tid))); |
| 1054 | $body .= module_invoke('taxonomy', 'render_nodes', module_invoke('taxonomy', 'select_nodes', $terms)); |
| 1055 | } |
| 1056 | } |
| 1057 | else { |
| 1058 | $body = ''; |
| 1059 | |
| 1060 | if (variable_get('recipe_recent_box_enable', 1)) { |
| 1061 | $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"), t('Syndicate')))); |
| 1062 | } |
| 1063 | $content = recipe_index(); |
| 1064 | if ($content != '') { |
| 1065 | $body .= theme('box', t('Recipe Categories'), $content); |
| 1066 | } |
| 1067 | } |
| 1068 | } |
| 1069 | return $body; |
| 1070 | } |
| 1071 | |
| 1072 | /** |
| 1073 | * Builds a breadcrumb list. |
| 1074 | * |
| 1075 | * @param breadcrumb a reference to the breadcrumb array. New items will be appending to this array. |
| 1076 | * |
| 1077 | * @return returns a term object if the last item in the url is a term, otherwise returns NULL. |
| 1078 | */ |
| 1079 | function recipe_build_breadcrumbs(&$breadcrumb) { |
| 1080 | if (arg(1) != NULL) { |
| 1081 | $i = 1; |
| 1082 | $url = 'recipe'; |
| 1083 | $breadcrumb[] = l(ucwords(t('Recipes')), $url); |
| 1084 | while (arg($i) != NULL) { |
| 1085 | $last_term = urldecode(arg($i)); |
| 1086 | $url = $url .'/'. urlencode($last_term); |
| 1087 | $breadcrumb[] = l(ucwords($last_term), $url); |
| 1088 | $i++; |
| 1089 | } |
| 1090 | |
| 1091 | $term = current(module_invoke('taxonomy', 'get_term_by_name', $last_term)); |
| 1092 | return $term; |
| 1093 | } |
| 1094 | return NULL; |
| 1095 | } |
| 1096 | |
| 1097 | /** |
| 1098 | * Recursively traverses the term tree to construct the index. |
| 1099 | * |
| 1100 | * @return string the output for this tree. |
| 1101 | */ |
| 1102 | function recipe_build_index(&$tree, $parent_url) { |
| 1103 | $output = ''; |
| 1104 | |
| 1105 | if ($tree == array()) { |
| 1106 | return ''; |
| 1107 | } |
| 1108 | |
| 1109 | do { |
| 1110 | $cur = |