/** * Get the post, extract the recipe and combine with the current style and output it * * @param integer $postID The post ID to print * @param integer $recipeIX The zero based index of the recipe in the post */ public function printRecipe($postID, $recipeIX) { /** @var $wpdb wpdb */ global $wpdb; $settings = EasyRecipePlusSettings::getInstance(); /** * Be paranoid and force the ID to an integer */ $postID = (int) $postID; $q = "SELECT * FROM {$wpdb->posts} WHERE ID = {$postID}"; $post = $wpdb->get_row($q); if (!$post) { return; } /** * Process the [br] shortcodes and remove the spurious <br>'s that wp_auto() inserts */ $content = str_replace("[br]", "<br>", $post->post_content); $content = preg_replace('%</div>\\s*</p></div>%im', '</div></div>', $content); $content = $this->plugin->possiblyConvert($postID, $post->post_type, $content); $postDOM = new EasyRecipePlusDocument($content); if (!$postDOM->isEasyRecipe) { return; } /** * If the post is formatted already then it came from the Object cache (?) * If that's the case we need to re-read the original */ if ($postDOM->isFormatted) { $post = $wpdb->get_row("SELECT * FROM " . $wpdb->prefix . "posts WHERE ID = {$postID}"); $content = str_replace("[br]", "<br>", $post->post_content); $content = preg_replace('%</div>\\s*</p></div>%im', '</div></div>', $content); $content = $this->plugin->possiblyConvert($postID, '', $content); $postDOM = new EasyRecipePlusDocument($content); if (!$postDOM->isEasyRecipe) { return; } } if (isset($_GET['style'])) { $styleName = $_GET['style']; } else { $styleName = $settings->printStyle; } // $printStyleData = call_user_func(array($this->stylesClass, 'getStyleData'), $styleName, $settings->get('customTemplates'), true); $printStyleData = EasyRecipePlusStyles::getStyleData($styleName, $settings->customTemplates, true); if (get_locale() != 'en_US') { EasyRecipePlusTemplate::setTranslate('easyrecipe'); } /** * Fix possibly broken times in older posts * Fix the Cholesterol oops in early versions */ if ($postDOM->recipeVersion < '3') { $postDOM->fixTimes("preptime"); $postDOM->fixTimes("cooktime"); $postDOM->fixTimes("duration"); $postDOM->setParentValueByClassName("cholestrol", $settings->lblCholesterol, "Cholestrol"); } $postDOM->setSettings($settings); $data = new stdClass(); $data->hasRating = false; $data->convertFractions = $settings->convertFractions; $settings->getLabels($data); $data->hasLinkback = $settings->allowLink; $data->title = $post->post_title; $data->blogname = get_option("blogname"); $data->recipeurl = get_permalink($post->ID); $data->customCSS = $this->plugin->getCSS('Print'); $data->extraPrintHeader = $settings->extraPrintHeader; $data->easyrecipeURL = EasyRecipePlus::$EasyRecipePlusUrl; $recipe = $postDOM->getRecipe($recipeIX); $photoURL = $postDOM->findPhotoURL($recipe); $data->hasPhoto = !empty($photoURL); $data->jqueryjs = self::JQUERYJS; $data->jqueryuijs = self::JQUERYUIJS; $data->jqueryuicss = self::JQUERYUICSS; if (current_user_can('edit_posts')) { $data->isAdmin = true; $data->formatDialog = $this->plugin->getFormatDialog($printStyleData, true); $cssLink = '<link href="' . EasyRecipePlus::$EasyRecipePlusUrl . '/css/%s?version=' . EasyRecipePlus::$pluginVersion . '" rel="stylesheet" type="text/css"/>'; $jsLink = '<script type="text/javascript" src="' . EasyRecipePlus::$EasyRecipePlusUrl . '/js/%s?version=' . EasyRecipePlus::$pluginVersion . '"></script>'; $data->formatCSS = sprintf($cssLink, 'easyrecipe-format-min.css'); $data->formatJS = sprintf($jsLink, 'easyrecipe-format-min.js'); } else { $data->formatDialog = ''; $data->printJS = '<script type="text/javascript" src="' . EasyRecipePlus::$EasyRecipePlusUrl . '/js/easyrecipe-print-min.js?version=' . EasyRecipePlus::$pluginVersion . '"></script>'; } $data->style = $styleName; if ($data->style[0] == '_') { $style = substr($data->style, 1); $data->css = "/easyrecipe-printstyle"; $templateFile = $settings->customTemplates . "/printstyles/{$style}/style.html"; } else { $data->css = EasyRecipePlus::$EasyRecipePlusUrl . "/printstyles/{$data->style}"; $templateFile = EasyRecipePlus::$EasyRecipePlusDir . "/printstyles/{$data->style}/style.html"; } $data->css .= "/style.css?version=" . EasyRecipePlus::$pluginVersion . ".{$printStyleData->version}"; $template = new EasyRecipePlusTemplate($templateFile); /** * Brain dead IE shows "friendly" error pages (i.e. it's non-compliant) so we need to force a 200 */ header("HTTP/1.1 200 OK"); /** * Set the character encoding explicitly */ $charset = get_bloginfo('charset'); header("Content-Type:text/html; charset={$charset}"); echo $postDOM->formatRecipe($recipe, $template, $data); flush(); exit; }
/** * Process any EasyRecipes in all the posts on the page * We need to do this here rather than in the_content hook because by then it's too late to queue up the scripts/styles we'll need * * @param $posts * * @return array */ function thePosts($posts) { /* @var $wp_rewrite WP_Rewrite */ global $wp_rewrite; /** @global $wpdb wpdb */ global $wpdb; /** * We don't want to process anything if it's a missing URL */ if (is_404()) { return $posts; } global $shortcode_tags; $guestpost = null; $newPosts = array(); /** * Process each post and replace placeholders with relevant data */ foreach ($posts as $post) { /** * Have we already processed this post? */ if (isset($this->easyrecipes[$post->ID])) { $post->post_content = $this->postContent[$post->ID]; $newPosts[] = $post; continue; } /** * We may have to change the rating method (e.g. for Ziplist recipes) so make a local copy */ $this->ratingMethod = $this->settings->ratings; /** * Do we want to take over processing of other recipe plugins posts? * If this is a post from another plugin and we are displaying it, make the rating method "Self Rated" unless ratings are disabled */ if (!is_admin()) { $this->converted = false; if ($this->settings->displayZiplist) { $post->post_content = preg_replace_callback('/\\[amd-(zlrecipe)-recipe:(\\d+)\\]/', array($this, 'doConvert'), $post->post_content); if ($this->converted && $this->settings->ratings != 'Disabled') { $this->ratingMethod = 'SelfRated'; } } if ($this->settings->displayRecipeCard) { $post->post_content = preg_replace_callback('/\\[(yumprint)-recipe id=[\'"]"(\\d+)[\'"]\\]/', array($this, 'doConvert'), $post->post_content); if ($this->converted && $this->settings->ratings != 'Disabled') { $this->ratingMethod = 'SelfRated'; } } if ($this->settings->displayGMC) { $post->post_content = preg_replace_callback('/\\[(gmc_recipe) (\\d+)\\]/', array($this, 'doConvert'), $post->post_content); if ($this->converted && $this->settings->ratings != 'Disabled') { $this->ratingMethod = 'SelfRated'; } } if ($this->settings->displayUltimateRecipe) { if ($post->post_type == 'recipe') { $post->post_content = "[ultimate-recipe id='{$post->ID}']"; $post->post_type = 'post'; } $post->post_content = preg_replace_callback('/\\[(ultimate-recipe) id=["\'](\\d+|random)["\']\\]/i', array($this, 'doConvert'), $post->post_content); if ($this->converted && $this->settings->ratings != 'Disabled') { $this->ratingMethod = 'SelfRated'; } } if ($this->settings->displayRecipage) { /** * Do a quick check before we go to the expense of instantiating a DOMDocument */ if (strpos($post->post_content, 'hrecipe f') !== false) { $document = new EasyRecipePlusDOMDocument($post->post_content); if ($document->isValid()) { /** @var DOMElement $hrecipe */ $hrecipe = $document->getElementByClassName('hrecipe'); if ($hrecipe) { $matches = array(); $matches[1] = 'recipage'; $matches[2] = $post->ID; $convertedRecipe = $this->doConvert($matches); /** @var DOMDocumentFragment $fragment */ $fragment = $document->createDocumentFragment(); $fragment->appendXML($convertedRecipe); $hrecipe->parentNode->replaceChild($fragment, $hrecipe); $content = $document->saveHTML(); $post->post_content = preg_replace('%^.*</head><body>(.*)</body></html>\\s*$%sm', '$1', $content); } } } } } /** * Handle the guest post shortcodes here because WP doesn't process them until much later * We may need to redirect so we need to do it before anything else has a chance to do output * We may also need to process a recipe */ if ($post->ID == $this->settings->gpDetailsPage || $post->ID == $this->settings->gpEntryPage || $post->ID == $this->settings->gpThanksPage) { if (empty($guestPost)) { $guestPost = new EasyRecipePlusGuestPost($this); } $gpResult = $guestPost->process($post); /** * If $guestPost->process() returns something, we processed the GP entry page * In this case, all we need to do is save it in $newPosts and continue * Otherwise, continue with processing since there may conceivably be an EasyRecipe on the post/page * * TODO - do we really need to do this? Won't it get picked up just below anyway? */ if ($gpResult) { $newPosts[] = $gpResult; continue; } } $postDOM = new EasyRecipePlusDocument($post->post_content); if (!$postDOM->isEasyRecipe) { if (strncasecmp($post->post_content, '[easyrecipe_page]', 17) === 0) { $this->easyrecipes[$post->ID] = true; $post->post_content = str_replace('[easyrecipe_page]', '', $post->post_content); } $newPosts[] = $post; continue; } $postDOM->setSettings($this->settings); /** * Mark this post as an easyrecipe so that the comment and rating processing know */ $this->easyrecipes[$post->ID] = true; /** * Make sure we haven't already formatted this post. This can happen in preview mode where WP replaces the post_content * of the parent with the autosave content which we've already processed. * If this is the case, save the formatted code and mark this post as having been processed * TODO - are there implications for the object cache for themes that re-read posts? */ if ($postDOM->isFormatted) { $this->postContent[$post->ID] = $post->post_content; $newPosts[] = $post; continue; } /** * Fix possibly broken times in older posts * Fix the Cholesterol typo oops in early versions */ if ($postDOM->recipeVersion < '3') { $postDOM->fixTimes("preptime"); $postDOM->fixTimes("cooktime"); $postDOM->fixTimes("duration"); $postDOM->setParentValueByClassName("cholestrol", $this->settings->lblCholesterol, "Cholestrol"); } $data = new stdClass(); /** * Get the ratings from the comment meta table if we use the EasyRecipe comment method * Other rating methods are handled in EasyRecipePlusDocument->applyStyle() * hasRatings is left unset for Self Rating */ if ($this->ratingMethod == 'EasyRecipe') { $q = "SELECT COUNT(*) AS count, SUM(meta_value) AS sum FROM {$wpdb->comments} JOIN {$wpdb->commentmeta} ON {$wpdb->commentmeta}.comment_id = {$wpdb->comments}.comment_ID "; $q .= "WHERE comment_approved = 1 AND meta_key = 'ERRating' AND comment_post_ID = {$post->ID} AND meta_value > 0"; $ratings = $wpdb->get_row($q); if ((int) $ratings->count > 0) { $data->ratingCount = $ratings->count; $data->ratingValue = number_format($ratings->sum / $ratings->count, 1); $data->ratingPC = $data->ratingValue * 100 / 5; $data->hasRating = true; } else { $data->hasRating = false; } } else { if ($this->ratingMethod == 'Disabled') { $data->hasRating = false; } } switch ($this->settings->saveButton) { case 'Ziplist': $data->saveButtonJS = self::ZIPLISTJS; $data->saveButton = sprintf(self::ZIPLISTBUTTON, $this->settings->ziplistPartnerKey, urlencode(get_permalink($post->ID)), $this->settings->lblSave); $data->hasSave = true; break; case 'BigOven': $data->saveButtonJS = ''; $data->saveButton = sprintf(self::BIGOVENBUTTON, self::$EasyRecipePlusUrl); $data->hasSave = true; break; } $this->settings->getLabels($data); $data->hasLinkback = $this->settings->allowLink; $data->displayPrint = $this->settings->displayPrint; $data->style = $this->styleName; $data->title = $post->post_title; $data->blogname = get_option("blogname"); // TODO - do all this stuff at initialise time? $data->siteURL = $this->homeURL; /** * If the site isn't using permalinks then just pass the print stuff as a qurerystring param */ if ($wp_rewrite->using_permalinks()) { $data->sitePrintURL = $data->siteURL; } else { $data->sitePrintURL = $data->siteURL . "?"; } $data->postID = $post->ID; $data->recipeurl = get_permalink($post->ID); $data->convertFractions = $this->settings->convertFractions; if ($this->styleName[0] == '_') { $styleName = substr($this->styleName, 1); $templateFile = $this->settings->customTemplates . "/styles/{$styleName}/style.html"; } else { $templateFile = self::$EasyRecipePlusDir . "/styles/{$this->styleName}/style.html"; } $template = new EasyRecipePlusTemplate($templateFile); $data->isLoggedIn = is_user_logged_in(); /** * Apply styles to the recipe data and return the content with recipes replace by a shortcode and also each recipe's HTML * Also keep a copy so we don't have to reformat in the case where the theme asks for the same post again * * This didn't work! Some themes don't call the_content() (esp for excerpts) so we can't rely on hooking into that to supply the formatted html * We need to do it right here - it seems that the_posts is the only reliable place to replace the base recipe HTML with the formatted recipe HTML */ /** * Replace the original content with the one that has the easyrecipe(s) nicely formatted and marked up * Also keep a copy so we don't have to reformat in the case where the theme asks for the same post again */ $this->postContent[$post->ID] = $post->post_content = $postDOM->applyStyle($template, $data); /** * If we haven't already done so, hook into the_content filter to stop wpauto() messing with recipe HTML */ if (empty($shortcode_tags['easyrecipe'])) { add_filter('the_content', array($this, 'theContent'), 0); add_shortcode('easyrecipe', array($this, 'replaceRecipeShortcode')); } /** * Some themes do a get_post() again instead of using the posts as modified by plugins * So make sure our modified post is in cache so the get_post() picks up the modified version not the original * Need to do both add and replace since add doesn't replace and replace doesn't add and we can't be sure if the cache key exists at this point */ wp_cache_add($post->ID, $post, 'posts'); wp_cache_replace($post->ID, $post, 'posts'); $newPosts[] = $post; } return $newPosts; }