diff --git a/_test/nested_include.test.php b/_test/nested_include.test.php index f577388..9e411b6 100644 --- a/_test/nested_include.test.php +++ b/_test/nested_include.test.php @@ -34,7 +34,23 @@ public function test_inner_to_outer() { $this->_validateContent($mainHTML, $secondHTML, $thirdHTML); } - private function _validateContent($mainHTML, $secondHTML, $thirdHTML) { + public function test_outer_to_inner_cut_off() { + $this->_createCutOffPages(); + $mainHTML = p_wiki_xhtml('test:plugin_include:nested:start'); + $secondHTML = p_wiki_xhtml('test:plugin_include:nested:second'); + $thirdHTML = p_wiki_xhtml('test:plugin_include:nested:third'); + $this->_validateContent($mainHTML, $secondHTML, $thirdHTML, true); + } + + public function test_inner_to_outer_cut_off() { + $this->_createCutOffPages(); + $thirdHTML = p_wiki_xhtml('test:plugin_include:nested:third'); + $secondHTML = p_wiki_xhtml('test:plugin_include:nested:second'); + $mainHTML = p_wiki_xhtml('test:plugin_include:nested:start'); + $this->_validateContent($mainHTML, $secondHTML, $thirdHTML, true); + } + + private function _validateContent($mainHTML, $secondHTML, $thirdHTML, $cutOff=false) { $this->assertTrue(strpos($mainHTML, 'Main Content') !== false, 'Main content contains "Main Content"'); $this->assertTrue($this->_matchHeader('1', 'Main Test Page', $mainHTML), 'Main page header is h1'); $this->assertTrue(strpos($mainHTML, 'Second Content') !== false, 'Main content contains "Second Content"'); @@ -47,6 +63,11 @@ private function _validateContent($mainHTML, $secondHTML, $thirdHTML) { $this->assertTrue($this->_matchHeader('2', 'Third Test Page', $secondHTML), 'Third page header on second page is h2'); $this->assertTrue(strpos($thirdHTML, 'Third Content') !== false, 'Third content contains "Third Content"'); $this->assertTrue($this->_matchHeader('1', 'Third Test Page', $thirdHTML), 'Third page header on third page is h1'); + if ($cutOff) { + $this->assertTrue(strpos($mainHTML, 'this-should-be-cut-off') === false, 'Main content contains "this-should-be-cut-off"'); + $this->assertTrue(strpos($mainHTML, 'this-should-be-included2') !== false, 'Main content does not contain "this-should-be-included2"'); + $this->assertTrue(strpos($mainHTML, 'this-should-be-included3') !== false, 'Main content does not contain "this-should-be-included3"'); + } } private function _matchHeader($level, $text, $html) { @@ -70,5 +91,23 @@ private function _createPages() { .'{{page>third}}'.DOKU_LF, 'setup for test'); } + + private function _createCutOffPages() { + saveWikiText('test:plugin_include:nested:start', + '====== Main Test Page ======'.DOKU_LF.DOKU_LF + .'Main Content'.rand().DOKU_LF.DOKU_LF + .'{{page>second}}'.DOKU_LF, + 'setup for test'); + saveWikiText('test:plugin_include:nested:second', + '====== Second Test Page ======'.DOKU_LF.DOKU_LF + .'Second Content'.rand().DOKU_LF.DOKU_LF + .'{{page>third}}this-should-be-included2{{includestop}} this-should-be-cut-off [[this-should-be-cut-off]]'.DOKU_LF, + 'setup for test'); + saveWikiText('test:plugin_include:nested:third', + '====== Third Test Page ======'.DOKU_LF.DOKU_LF + .'Third Content'.rand().DOKU_LF.DOKU_LF + .'{{page>third}}this-should-be-included3{{includestop}} this-should-be-cut-off [[this-should-be-cut-off]]'.DOKU_LF, + 'setup for test'); + } } diff --git a/helper.php b/helper.php index f693e9a..b7b6132 100644 --- a/helper.php +++ b/helper.php @@ -224,6 +224,9 @@ function get_flags($setflags) { case 'noreadmore': $flags['readmore'] = 0; break; + case 'length': + $flags['length'] = max(intval($value), 0); + break; } } // the include_content URL parameter overrides flags @@ -232,6 +235,134 @@ function get_flags($setflags) { return $flags; } + /** + * Shortens page instructions if it finds a 'includestop' + * or content is longer than $flags['length']. + */ + protected function _shorten_instructions(&$ins, $flags) { + // Cut off page content if required + $length = false; + if (!empty($flags['length'])) { + $length = $flags['length']; + } + $stop = false; + $tr_closed = false; + $t_closed = false; + $li_closed = false; + $l_closed = false; + $max_entries = count($ins); + $sum = 0; + + for ($index = 0 ; $index < $max_entries ; $index++) { + $entry = &$ins[$index]; + switch ($entry[0]) { + case 'plugin': + if ($entry[1][0] == 'include_stop') { + $stop = true; + unset($ins[$index]); + } + break; + case 'cdata': + $entryLength = strlen($entry[1][0]); + if (!$stop && $length !== false && $sum+$entryLength > $length) { + if ($sum < $length) { + $max = $length - $sum; + $entry[1][0] = substr ($entry[1][0], 0, $max).'...'; + $sum = $length; + $stop = true; + } else { + unset($ins[$index]); + } + } else { + if ($stop) { + unset($ins[$index]); + } + } + $sum += $entryLength; + break; + case 'tablerow_close': + if ($stop) { + if ($t_closed || $tr_closed) { + unset ($ins[$index]); + } else { + $tr_closed = true; + } + } + break; + case 'table_close': + if ($stop) { + if ($t_closed) { + unset ($ins[$index]); + } else { + $t_closed = true; + } + } + break; + case 'table_open': + case 'tablerow_open': + case 'tablethead_open': + case 'tableheader_open': + if ($stop) { + unset ($ins[$index]); + } + break; + case 'tablethead_close': + case 'tableheader_close': + case 'tablecell_open': + case 'tablecell_close': + if ($t_closed || $tr_closed) { + unset ($ins[$index]); + } + break; + case 'listitem_close': + if ($stop) { + if ($l_closed || $li_closed) { + unset ($ins[$index]); + } else { + $li_closed = true; + } + } + break; + case 'listu_close': + case 'listo_close': + if ($stop) { + if ($l_closed) { + unset ($ins[$index]); + } else { + $l_closed = true; + } + } + break; + case 'listu_open': + case 'listo_open': + case 'listitem_open': + if ($stop) { + unset ($ins[$index]); + } + break; + case 'listcontent_open': + case 'listcontent_close': + if ($l_closed || $li_closed) { + unset ($ins[$index]); + } + break; + case 'section_close': + if ($stop) { + // Delete everything behind this point + for ($del = $index++ ; $del < $max_entries ; $del++) { + unset($ins[$del]); + } + // Leave switch and for loop! + break 2; + } + break; + } + } + + // Return if content was cut of or not + return $stop; + } + /** * Returns the converted instructions of a give page/section * @@ -278,6 +409,10 @@ function _get_instructions($page, $sect, $mode, $lvl, $flags, $root_id = null, $ $this->_convert_instructions($ins, $lvl, $page, $sect, $flags, $root_id, $included_pages); } + + if ($this->_shorten_instructions($ins, $flags) && $flags['readmore']) { + $ins[] = array('plugin', array('include_readmore', array($page))); + } return $ins; } diff --git a/syntax/stop.php b/syntax/stop.php new file mode 100644 index 0000000..eb26103 --- /dev/null +++ b/syntax/stop.php @@ -0,0 +1,65 @@ +Lexer->addSpecialPattern("{{includestop}}", $mode, 'plugin_include_stop'); + } + + /** + * Handle syntax matches + * + * @param string $match The current match + * @param int $state The match state + * @param int $pos The position of the match + * @param Doku_Handler $handler The hanlder object + * @return array The instructions of the plugin + */ + function handle($match, $state, $pos, Doku_Handler $handler) { + return true; + } + + /** + * Renders the include stop - dummy. + * 'includestop' is handled in helper_plugin_include::_shorten_instructions() + */ + function render($format, Doku_Renderer $renderer, $data) { + return true; + } +} +// vim:ts=4:sw=4:et: