/** * If there's an EasyRecipe in the content, load the HTML and pre-process, else just return * * @param $content * @param bool $load */ public function __construct($content, $load = true) { /** * If there's no EasyRecipe, just return */ if (!@preg_match(self::regexEasyRecipe, $content)) { return; } /** * Load the html - make sure we could parse it */ parent::__construct($content, $load); if (!$this->isValid()) { return; } /** * Find the easyrecipe(s) */ $this->easyrecipes = $this->getElementsByClassName('easyrecipe'); /** * Sanity check - make sure we could actually find at least one */ if (count($this->easyrecipes) == 0) { // echo "<!-- ER COUNT = 0 -->\n"; return; } /** * This is a valid easyrecipe post * Find a version number - the version will be the same for every recipe in a multi recipe post so just get the first */ $this->isEasyRecipe = true; /* @var $node DOMElement */ $node = $this->getElementByClassName("endeasyrecipe", "div", $this->easyrecipes[0], false); $this->recipeVersion = $node->nodeValue; /* * See if this post has already been formatted. * Wordpress replaces the parent post_content with the autosave post content (as already formatted by us) on a preview. * so we need to know if this post has already been formatted. This is a pretty icky way of doing it since it relies * on the style template having a specific title attribute on the endeasyrecipe div - need to make this more robust */ $this->isFormatted = $node !== null && $node->hasAttribute('title'); }
/** * Process the template text * * @param null $data * @param int $options * @return mixed|string */ function replace($data = null, $options = 0) { /** * If we have replacements, pre-process the template and remove conditionally INCLUDEd stuff */ if ($data === null) { $data = new stdClass(); } $currentPosition = 0; $this->opText = ''; $inText = $this->inText; $firstType = null; /** * We return from within this loop when we have nothing left to process */ while (true) { /** * Look for stuff to replace and find the first of them */ $firstPosition = strlen($inText); $varPosition = strpos($inText, $this->delimiter, $currentPosition); if ($varPosition !== false) { $firstPosition = $varPosition; $firstType = self::VARIABLEREPLACE; } $repeatPosition = strpos($inText, '<!-- START REPEAT ', $currentPosition); if ($repeatPosition !== false && $repeatPosition < $firstPosition) { $firstPosition = $repeatPosition; $firstType = self::REPEATREPLACE; } $includeifPosition = strpos($inText, '<!-- START INCLUDEIF ', $currentPosition); if ($includeifPosition !== false && $includeifPosition < $firstPosition) { $firstPosition = $includeifPosition; $firstType = self::INCLUDEIF; } $startStripPosition = strpos($inText, '<!-- START STRIP WHITESPACE ', $currentPosition); if ($startStripPosition !== false && $startStripPosition < $firstPosition) { $firstPosition = $startStripPosition; $firstType = self::STARTSTRIP; } $endStripPosition = strpos($inText, '<!-- END STRIP WHITESPACE ', $currentPosition); if ($endStripPosition !== false && $endStripPosition < $firstPosition) { $firstPosition = $endStripPosition; $firstType = self::ENDSTRIP; } /** * If there's nothing to do, just return what we've got */ if ($firstPosition == strlen($inText)) { /** * Copy any remaining input over to the output */ $this->opText .= substr($inText, $currentPosition); /* * If there's any block to whitespace strip, do it * Tidy up the start/stop positions first to make it easy to process * This allows overlapping and unterminated stip blocks */ if (count($this->stripWhitespace) > 0) { $stripBlocks = array(); $startPosition = -1; foreach ($this->stripWhitespace as $position) { /** * End strip? */ if ($position < 0) { /** * If there's no previous unterminated START STRIP, ignore this END */ if ($startPosition == -1) { continue; } $stripBlocks[] = array($startPosition, abs($position)); $startPosition = -1; } else { /** * If there's a previous unterminated START STRIP, ignore this one */ if ($startPosition != -1) { continue; } $startPosition = $position; } } /** * Allow for a missing END STRIP */ if ($startPosition != -1) { $stripBlocks[] = array($startPosition, strlen($this->opText)); } /** * Actually strip out whitespace in the strip blocks */ $currentPosition = 0; $strippedText = ''; foreach ($stripBlocks as $stripBlock) { $strippedText .= substr($this->opText, $currentPosition, $stripBlock[0] - $currentPosition); $text = substr($this->opText, $stripBlock[0], $stripBlock[1] - $stripBlock[0]); $text = preg_replace('/>\\s+</', '><', $text); $strippedText .= trim($text); $currentPosition = $stripBlock[1]; } $strippedText .= substr($this->opText, $currentPosition, strlen($this->opText) - $currentPosition); $this->opText = $strippedText; } /** * If we aren't translating, then just (optionally) clean up whitespace and return */ if (!self::$translate || !class_exists('EasyRecipeDOMDocument')) { return $this->cleanWhitespace($this->opText, $options); } $doc = new EasyRecipeDOMDocument($this->opText, true); if (!$doc) { return $this->opText; } $xlates = $doc->getElementsByClassName('xlate'); if (count($xlates) == 0) { return $this->cleanWhitespace($this->opText, $options); } // FIXME - use gettext if no __ foreach ($xlates as $xlate) { $original = $doc->innerHTML($xlate); $translation = __($original, self::$textDomain); if ($translation != $original) { $xlate->nodeValue = $translation; } } $html = $doc->getHTML(true); return $this->cleanWhitespace($html, $options); } /** * Copy over everything up to the first thing we need to process */ $length = $firstPosition - $currentPosition; $this->opText .= substr($inText, $currentPosition, $length); $currentPosition = $firstPosition; /** * Get the thing to be replaced */ switch ($firstType) { /** * INCLUDEIF includes the code up to the matching END INCLUDEIF: * IF the condition variable exists and it's not false or null */ case self::INCLUDEIF: /** * Get the conditional. * Only check a smallish substring for efficiency * This limits include condition names to 20 characters */ $subString = substr($inText, $currentPosition, 60); if (preg_match('/<!-- START INCLUDEIF (!?)([_a-z][_0-9a-z]{0,31}) -->/i', $subString, $regs)) { $negate = $regs[1]; $trueFalse = $negate != '!'; $includeCondition = $regs[2]; } else { trigger_error("Malformed START INCLUDEIF at {$currentPosition} ({$subString})", E_USER_NOTICE); $this->opText .= "<"; $currentPosition++; continue; } $endInclude = "<!-- END INCLUDEIF {$negate}{$includeCondition} -->"; $endIncludeLength = strlen($endInclude); $endPosition = strpos($inText, $endInclude); if ($endPosition == false) { trigger_error("'{$endInclude}' not found", E_USER_NOTICE); $this->opText .= "<"; $currentPosition++; break; } /** * If the condition is met, just remove the INCLUDEIF comments * If the condition isn't met, remove everything up to the END INCLUDEIF * The condition must be present, and NOT false or NULL */ $condition = isset($data->{$includeCondition}) && $data->{$includeCondition} !== false && $data->{$includeCondition} !== null; if ($condition === $trueFalse) { $startInclude = "<!-- START INCLUDEIF {$negate}{$includeCondition} -->"; $startIncludeLength = strlen($startInclude); $inText = substr($inText, 0, $currentPosition) . substr($inText, $currentPosition + $startIncludeLength, $endPosition - $currentPosition - $startIncludeLength) . substr($inText, $endPosition + $endIncludeLength); } else { $inText = substr($inText, 0, $currentPosition) . substr($inText, $endPosition + $endIncludeLength); } break; /** * Remove whitespace between tags. * Useful to remove unwanted significant HTML whitespace that may have been introduced by auto formatting the source template */ /** * Remove whitespace between tags. * Useful to remove unwanted significant HTML whitespace that may have been introduced by auto formatting the source template */ case self::STARTSTRIP: $currentPosition += 31; $this->stripWhitespace[] = strlen($this->opText); break; /** * Save the output position at which to stop stripping. -ve indicates that it's an end position */ /** * Save the output position at which to stop stripping. -ve indicates that it's an end position */ case self::ENDSTRIP: $currentPosition += 29; $this->stripWhitespace[] = -strlen($this->opText); break; /** * A variable is a valid PHP variable name (limited to 20 chars) between delimiters * If we don't find a valid name, copy over the delimiter and continue * FIXME - fall back to caller's vars if it doesn't exist */ /** * A variable is a valid PHP variable name (limited to 20 chars) between delimiters * If we don't find a valid name, copy over the delimiter and continue * FIXME - fall back to caller's vars if it doesn't exist */ case self::VARIABLEREPLACE: $s = substr($inText, $currentPosition, 34); if (!preg_match("/^{$this->delimiter}([_a-z][_0-9a-z]{0,31}){$this->delimiter}/si", $s, $regs)) { $this->opText .= $this->delimiter; $currentPosition++; continue; } /** * If we don't have a match for the variable, just assume it's not what we wanted to do * so put the string we matched back into the output and continue from the trailing delimiter */ $varName = $regs[1]; if (!isset($data->{$varName})) { $this->opText .= $this->delimiter . $varName; $currentPosition += strlen($varName) + 1; continue; } /** * Got a match - replace the <delimiter>...<delimiter> with the vars stuff * We *could* pass this on for recursive processing, but it's not something we would normally want to do * Maybe have a special naming convention for vars we want to do recursively? */ $this->opText .= $data->{$varName}; $currentPosition += strlen($varName) + 2; break; /** * We've seen a start repeat. * Find the name of the repeat (limited to 20 chars) * If we can't find the name, assume it's not what we want and continue * * Look for a valid START REPEAT in the next 45 characters */ /** * We've seen a start repeat. * Find the name of the repeat (limited to 20 chars) * If we can't find the name, assume it's not what we want and continue * * Look for a valid START REPEAT in the next 45 characters */ case self::REPEATREPLACE: $s = substr($inText, $currentPosition, 45); if (!preg_match('/<!-- START REPEAT ([_a-zA-Z][_0-9a-zA-Z]{0,19}) -->/m', $s, $regs)) { $this->opText .= '<'; $currentPosition++; continue; } $rptName = $regs[1]; /** * Make sure we have a matching key and it's an array */ if (!isset($data->{$rptName}) || !is_array($data->{$rptName})) { $this->opText .= '<'; $currentPosition++; continue; } /** * Now try to find the end of this repeat */ $currentPosition += strlen($rptName) + 22; $rptEnd = strpos($inText, "<!-- END REPEAT {$rptName} -->", $currentPosition); if ($rptEnd === false) { $this->opText .= '<!-- START REPEAT $rptName -->'; trigger_error("END REPEAT not found for {$rptName}", E_USER_NOTICE); continue; } /** * Do the repeat processing. * For each item in the repeated array, process as a new template */ $rptLength = $rptEnd - $currentPosition; $rptString = substr($inText, $currentPosition, $rptLength); $rptVars = $data->{$rptName}; for ($i = 0; $i < count($rptVars); $i++) { $saveTranslate = self::$translate; self::$translate = false; $rpt = new EasyRecipeTemplate($rptString, self::TEXT, $this->delimiter); $this->opText .= $rpt->replace($rptVars[$i], $options); self::$translate = $saveTranslate; } /** * Step over the end repeat */ $currentPosition += strlen($rptName) + $rptLength + 20; break; } } return ''; }
/** * Remove non display stuff * * @param $content * * @return mixed */ function filterExcerpt($content) { $dom = new EasyRecipeDOMDocument($content); $dom->removeElementsByClassName('ERSSavePrint', 'div'); $dom->removeElementsByClassName('ERSRating', 'div'); $dom->removeElementsByClassName('ERSRatings', 'div'); $dom->removeElementsByClassName('ERSClear', 'div'); $dom->removeElementsByClassName('endeasyrecipe', 'div'); $dom->removeElementsByClassName('ERSLinkback', 'div'); $content = $dom->getHTML(true); /** * Remove empty lines left over from the deletions */ return preg_replace('/(\\r\\n|\\n)(?:\\r\\n|\\n)+/', '$1', $content); }