1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8function wikiplugin_include_info() 9{ 10 return [ 11 'name' => tr('Include'), 12 'documentation' => 'PluginInclude', 13 'description' => tr('Include a portion of another wiki page'), 14 'prefs' => ['wikiplugin_include'], 15 'iconname' => 'copy', 16 'introduced' => 1, 17 'tags' => [ 'basic' ], 18 'format' => 'wiki', 19 'params' => [ 20 'page' => [ 21 'required' => true, 22 'name' => tr('Page'), 23 'description' => tr('Name of the source wiki page (which contains the included portion)'), 24 'since' => '1', 25 'filter' => 'pagename', 26 'default' => '', 27 'profile_reference' => 'wiki_page', 28 ], 29 'start' => [ 30 'required' => false, 31 'name' => tr('Start'), 32 'description' => tr('When only a portion of the page should be included, full text of the line after which 33 inclusion should start'), 34 'since' => '1', 35 'default' => '', 36 ], 37 'stop' => [ 38 'required' => false, 39 'name' => tr('Stop'), 40 'description' => tr('When only a portion of the page should be included, full text of the line before which 41 inclusion should end'), 42 'since' => '1', 43 'default' => '', 44 ], 45 'linkoriginal' => [ 46 'required' => false, 47 'name' => tr('Read more button'), 48 'description' => tr('Add a "Read more" link at the end of included content, linking to the original page. (shows "Read More" by default)'), 49 'since' => '18.0', 50 'default' => 'n', 51 'options' => [ 52 ['text' => '', 'value' => ''], 53 ['text' => tr('Yes'), 'value' => 'y'], 54 ['text' => tr('No'), 'value' => 'n'], 55 ], 56 ], 57 'linkoriginal_text' => [ 58 'required' => false, 59 'name' => tr('Read more button label'), 60 'description' => tr('Label of the button linking to the source page (if it is displayed)'), 61 'since' => '18.0', 62 'filter' => 'text', 63 'default' => '', 64 ], 65 'nopage_text' => [ 66 'required' => false, 67 'name' => tr('Page not found'), 68 'description' => tr('Text to show when the page selected to be included is not found anymore.'), 69 'since' => '6.0', 70 'filter' => 'text', 71 'default' => '', 72 ], 73 'pagedenied_text' => [ 74 'required' => false, 75 'name' => tr('Permission denied message'), 76 'description' => tr('Text to show when the page exists but the user has insufficient permissions to see it.'), 77 'since' => '6.0', 78 'filter' => 'text', 79 'default' => '', 80 ], 81 'pagenotapproved_text' => [ 82 'required' => false, 83 'name' => tr('No version approved error message'), 84 'description' => tr('Text to show when the page exists but no version is approved.'), 85 'since' => '18.0', 86 'filter' => 'text', 87 'default' => '', 88 ], 89 'page_edit_icon' => [ 90 'required' => false, 91 'name' => tr('Edit Icon'), 92 'description' => tr('Option to show the edit icon for the included page (shown by default). Depends on the "edit icons" settings.'), 93 'since' => '12.1', 94 'default' => 'y', 95 'filter' => 'alpha', 96 'options' => [ 97 ['text' => '', 'value' => ''], 98 ['text' => tr('Yes'), 'value' => 'y'], 99 ['text' => tr('No'), 'value' => 'n'], 100 ], 101 ], 102 'max_inclusions' => [ 103 'required' => false, 104 'name' => tr('Max inclusions'), 105 'description' => tr('Maximum amount of times the same page can be included. Defaults to 5'), 106 'since' => '18.2', 107 'filter' => 'text', 108 'default' => 5, 109 ], 110 'parse_included_page' => [ 111 'required' => false, 112 'name' => tr('Parse Included Page'), 113 'description' => tr('Parse the page to be included before adding it to the parent page. This will help if html pages are included in wiki pages or vice versa, but will cause issues with the wiki table of contents.'), 114 'since' => '18.2', 115 'default' => 'n', 116 'filter' => 'alpha', 117 'options' => [ 118 ['text' => '', 'value' => ''], 119 ['text' => tr('Yes'), 'value' => 'y'], 120 ['text' => tr('No'), 'value' => 'n'], 121 ], 122 ], 123 'max_chars_included' => [ 124 'required' => false, 125 'name' => tr('Max characters included'), 126 'description' => tr('Limit the length of the included text'), 127 'since' => '20.0', 128 'filter' => 'int', 129 'default' => '', 130 ], 131 ], 132 ]; 133} 134 135function wikiplugin_include($dataIn, $params) 136{ 137 global $killtoc, $prefs; 138 139 /** @var int[] $numberOfInclusions Associative array of the number of times each key (fragment) was included */ 140 static $numberOfInclusions; 141 142 static $data; 143 $tikilib = TikiLib::lib('tiki'); 144 145 $killtoc = true; 146 $params = array_merge([ 147 'nopage_text' => '', 148 'pagedenied_text' => '', 149 'page_edit_icon' => 'y', 150 'parse_included_page' => 'n', 151 'pagenotapproved_text' => tr('There are no approved versions of this page.'), 152 ], $params); 153 extract($params, EXTR_SKIP); 154 if (! isset($page)) { 155 return ("<b>missing page for plugin INCLUDE</b><br />"); 156 } 157 if (! isset($max_inclusions)) { 158 $max_inclusions = 5; 159 } 160 161 // This variable is for accessing included page name within plugins in that page 162 global $wikiplugin_included_page; 163 $wikiplugin_included_page = $page; 164 165 /** @var string $fragmentIdentifier Identifier of included fragment */ 166 $fragmentIdentifier = $page; 167 if (isset($start)) { 168 $fragmentIdentifier .= "/$start"; 169 } 170 if (isset($end)) { 171 $fragmentIdentifier .= "/$end"; 172 } 173 174 if (isset($numberOfInclusions[$fragmentIdentifier])) { 175 if ($numberOfInclusions[$fragmentIdentifier] >= $max_inclusions) { 176 trigger_error('Inclusion failed', E_USER_WARNING); 177 return ''; 178 } 179 $numberOfInclusions[$fragmentIdentifier]++; 180 } else { 181 $numberOfInclusions[$fragmentIdentifier] = 1; 182 // only evaluate permission the first time round 183 // evaluate if object or system permissions enables user to see the included page 184 if ($prefs['flaggedrev_approval'] != 'y') { 185 $data[$fragmentIdentifier] = $tikilib->get_page_info($page); 186 } else { 187 $flaggedrevisionlib = TikiLib::lib('flaggedrevision'); 188 if ($flaggedrevisionlib->page_requires_approval($page)) { 189 if ($version_info = $flaggedrevisionlib->get_version_with($page, 'moderation', 'OK')) { 190 $data[$fragmentIdentifier] = $version_info; 191 } else { 192 $numberOfInclusions[$fragmentIdentifier] = $max_inclusions; 193 return($pagenotapproved_text); 194 } 195 } else { 196 $data[$fragmentIdentifier] = $tikilib->get_page_info($page); 197 } 198 } 199 if (! $data[$fragmentIdentifier]) { 200 $text = $nopage_text; 201 } 202 $perms = $tikilib->get_perm_object($page, 'wiki page', $data[$fragmentIdentifier], false); 203 if ($perms['tiki_p_view'] != 'y') { 204 $numberOfInclusions[$fragmentIdentifier] = $max_inclusions; 205 $text = $pagedenied_text; 206 return($text); 207 } 208 } 209 210 if (! empty($params['linkoriginal_text'])) { 211 $linkoriginal_text = tr($params['linkoriginal_text']); 212 } else { 213 $linkoriginal_text = tr('Read more'); 214 } 215 216 if ($data[$fragmentIdentifier]) { 217 $text = $data[$fragmentIdentifier]['data']; 218 if (isset($start) || isset($stop)) { 219 $lines = explode("\n", $text); 220 if (isset($start) && isset($stop)) { 221 $state = 0; 222 foreach ($lines as $i => $line) { 223 if ($state == 0) { 224 // Searching for start marker, dropping lines until found 225 unset($lines[$i]); // Drop the line 226 if (0 == strcmp($start, trim($line))) { 227 $state = 1; // Start retaining lines and searching for stop marker 228 } 229 } else { 230 // Searching for stop marker, retaining lines until found 231 if (0 == strcmp($stop, trim($line))) { 232 unset($lines[$i]); // Stop marker, drop the line 233 $state = 0; // Go back to looking for start marker 234 } 235 } 236 } 237 } elseif (isset($start)) { 238 // Only start marker is set. Search for it, dropping all lines until 239 // it is found. 240 foreach ($lines as $i => $line) { 241 unset($lines[$i]); // Drop the line 242 if (0 == strcmp($start, trim($line))) { 243 break; 244 } 245 } 246 } else { 247 // Only stop marker is set. Search for it, dropping all lines after 248 // it is found. 249 $state = 1; 250 foreach ($lines as $i => $line) { 251 if ($state == 0) { 252 // Dropping lines 253 unset($lines[$i]); 254 } else { 255 // Searching for stop marker, retaining lines until found 256 if (0 == strcmp($stop, trim($line))) { 257 unset($lines[$i]); // Stop marker, drop the line 258 $state = 0; // Start dropping lines 259 } 260 } 261 } 262 } 263 $text = implode("\n", $lines); 264 } 265 } 266 267 if (isset($params['max_chars_included']) && mb_strlen($text) > $params['max_chars_included']) { 268 $text = substr($text, 0 , $params['max_chars_included']) . '…'; 269 } 270 271 $parserlib = TikiLib::lib('parser'); 272 273 if ($params['parse_included_page'] === 'y') { 274 275 $old_options = $parserlib->option; 276 $options = [ 277 'is_html' => $data[$fragmentIdentifier]['is_html'], 278 'suppress_icons' => true, 279 ]; 280 if (! empty($_REQUEST['page'])) { 281 $options['page'] = $_REQUEST['page']; 282 } 283 $parserlib->setOptions($options); 284 $fragment = new WikiParser_Parsable($text); 285 $text = $fragment->parse($options); 286 $parserlib->setOptions($old_options); 287 } else { 288 if (!empty($_REQUEST['page'])) { 289 $options['page'] = $_REQUEST['page']; 290 } 291 $parserlib->setOptions(); 292 $parserlib->parse_wiki_argvariable($text); 293 } 294 295 global $smarty; 296 // append a "See full page" link at end of text if only a portion of page is being included 297 if (($prefs['wiki_plugin_include_link_original'] == 'y' && (isset($start) || isset($stop))) || (isset($linkoriginal) && $linkoriginal == 'y')) { 298 $wikilib = TikiLib::lib('wiki'); 299 $text .= '<p><a href="' . $wikilib->sefurl($page) . '" class="btn btn-primary"'; 300 $text .= 'title="' . sprintf(tr('The text above comes from page "%s". Click to go to that page.'), htmlspecialchars($page)) . '">'; 301 $text .= smarty_function_icon(['name' => 'align-left'], $smarty->getEmptyInternalTemplate()) . ' '; 302 $text .= $linkoriginal_text; 303 $text .= '</a><p>'; 304 } 305 306 // append an edit button if page_edit_icon does not equal 'n' 307 if ($page_edit_icon != 'n') { 308 if (isset($perms) && $perms['tiki_p_edit'] === 'y' && strpos($_SERVER['PHP_SELF'], 'tiki-send_newsletters.php') === false) { 309 $smarty = TikiLib::lib('smarty'); 310 $smarty->loadPlugin('smarty_block_ajax_href'); 311 $smarty->loadPlugin('smarty_function_icon'); 312 $tip = tr('Include Plugin') . ' | ' . tr('Edit the included page:') . ' "' . $page . '"'; 313 $returnto = ! empty($GLOBALS['page']) ? $GLOBALS['page'] : $_SERVER['REQUEST_URI']; 314 if (empty($_REQUEST['display']) || $_REQUEST['display'] != 'pdf') { 315 $text .= '<a class="editplugin" ' . // ironically smarty_block_self_link doesn't work for this! ;) 316 smarty_block_ajax_href(['template' => 'tiki-editpage.tpl'], 'tiki-editpage.php?page=' . urlencode($page) . '&returnto=' . urlencode($returnto), $smarty, $tmp = false) . '>' . 317 smarty_function_icon(['name' => 'edit', 'iclass' => 'tips', 'ititle' => $tip], $smarty->getEmptyInternalTemplate()) . '</a>'; 318 } 319 } 320 } 321 if ($params['parse_included_page'] === 'y') { 322 return "~np~$text~/np~"; 323 } else { 324 return $text; 325 } 326} 327