PageRenderTime 66ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/SubPageFunctions/SubPageFunctions.php

https://github.com/tav/confluence
PHP | 505 lines | 381 code | 36 blank | 88 comment | 71 complexity | 54cf0391ad09387c0f1217324d60fa8c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0
  1. <?php
  2. ##########################################################################
  3. # SubPageFunctions.php Copyright (C) 2009 PM Gostelow
  4. #
  5. # This script is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This script is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ##########################################################################
  18. if ( !defined( 'MEDIAWIKI' ) ) {
  19. die( 'This file is a MediaWiki extension, it is not a valid entry point' );
  20. }
  21. // DO NOT EDIT CLASS!
  22. abstract
  23. class ExtAbstractCredits
  24. {
  25. const cdNAME = 'name'; // for package name
  26. const cdURL = 'url'; // for site link
  27. const cdAUTHOR = 'author'; // for author name/s
  28. const cdVERSION = 'version'; // for package release
  29. const cdNOTE = 'description'; // for short note
  30. const cdNOTEMSG = 'descriptionmsg'; // for long note
  31. const URLPREFIX = // for link name
  32. "http://www.mediawiki.org/wiki/Extension:";
  33. protected
  34. static $url = ''; // for package site name
  35. // always override, always call
  36. abstract static function register(); // for LocalSettings.php
  37. // seldom override, always call
  38. // updates and hooks credits into the extension
  39. static
  40. function register_credits( $hook, $pkgCredits, array $pages = array() )
  41. {
  42. global $wgExtensionCredits;
  43. if (empty(self::$url))
  44. self::$url = self::URLPREFIX . $pkgCredits[self::cdNAME];
  45. $pkgCredits[self::cdURL] = self::$url;
  46. foreach( $pages as $key => $value )
  47. $pkgCredits[$key] = $value;
  48. $wgExtensionCredits[$hook][] = $pkgCredits;
  49. }
  50. };
  51. // parser function specific class
  52. // DO NOT EDIT CLASS!
  53. abstract
  54. class pfAbstractFunctions extends ExtAbstractCredits
  55. {
  56. const SETUP = 'setup'; // function name
  57. const MAGIC = 'magic'; // function name
  58. const mgSUFFIX = "_magic"; // magic file suffix
  59. const mgSAMPLE = '_sample';
  60. const mgDEFAULT = 'default'; // default language code
  61. const mgFILE = "%s/%s%s.php"; // file name format
  62. const pkPREFIX = "\$wg"; // class var name prefix
  63. const mgHEADER = "<?php
  64. # automatically generated for %s on
  65. # %s.
  66. # Please add your own language and aliases.
  67. # New releases should NOT overwrite this file.
  68. #
  69. # To regenerate the default file, simply remove it.
  70. if ( !defined( 'MEDIAWIKI' ) ) {
  71. die( 'This file is a MediaWiki extension, it is not a valid entry point' );
  72. }
  73. ";
  74. static $hkCredit = 'parserhook'; // credit hook name
  75. static $hkLang = 'LanguageGetMagic';// magic hook name
  76. static $dir = ''; // our path name
  77. abstract
  78. static
  79. function setup();
  80. abstract
  81. static
  82. function magic( $word, $lang );
  83. // seldom override, never call
  84. // create magic file if it doesn't exist.
  85. // return file name, or false if it doesn't exist.
  86. static
  87. function make_magic( $pkgName, $funcs )
  88. {
  89. global $langCodeList;
  90. if ( empty( self::$dir ))
  91. self::$dir = dirname( __FILE__ );
  92. $magicfile = sprintf( self::mgFILE,
  93. self::$dir, $pkgName, self::mgSUFFIX );
  94. $samplefile = sprintf( self::mgFILE,
  95. self::$dir, $pkgName, self::mgSUFFIX . self::mgSAMPLE);
  96. if ( !file_exists( $magicfile ))
  97. {
  98. if ( file_exists( $samplefile ))
  99. require_once $samplefile;
  100. else
  101. $langCodeList = array( self::mgDEFAULT );
  102. $file = fopen( $magicfile, "w");
  103. fwrite( $file,
  104. sprintf( self::mgHEADER, $pkgName, date( DATE_RFC822 )));
  105. fwrite( $file, "$".__CLASS__.self::mgSUFFIX." = array(\n" );
  106. foreach( $langCodeList as $code )
  107. {
  108. fwrite( $file, " \"".$code."\" => array(\n");
  109. foreach( $funcs as $func )
  110. fwrite( $file, " \"".$func."\" => array(0,\"".$func."\"),\n");
  111. fwrite( $file, " ),\n");
  112. }
  113. fwrite( $file, ");\n?>\n");
  114. fclose( $file );
  115. if ( !file_exists( $magicfile ))
  116. $magicfile = false;
  117. }
  118. return $magicfile;
  119. }
  120. }
  121. // Everything is stored in this class
  122. class pfSubPages extends pfAbstractFunctions
  123. {
  124. // credit constants
  125. const AUTHOR = 'Peter Gostelow';
  126. const PACKAGE = 'SubPageFunctions';
  127. const VERSION = '0.0.3';
  128. const NOTE = 'Sub-page extended functions';
  129. // function constants
  130. const fnCALENDAR = "calendar";
  131. const fnPATHNAME = "pathname";
  132. const fnSUBPAGES = "subpages";
  133. const fnUSER = "user";
  134. // static function array
  135. protected
  136. static $funcs = array(
  137. self::fnCALENDAR,
  138. self::fnPATHNAME,
  139. self::fnSUBPAGES,
  140. self::fnUSER );
  141. // static credit array
  142. static $credits = array(
  143. self::cdNAME => self::PACKAGE,
  144. self::cdURL => '',
  145. self::cdAUTHOR => self::AUTHOR,
  146. self::cdNOTE => self::NOTE,
  147. self::cdVERSION => self::VERSION);
  148. // static magic array, see also *_magic.php file
  149. private
  150. static $magic = array(
  151. self::mgDEFAULT => array(
  152. self::fnCALENDAR => array(0, self::fnCALENDAR),
  153. self::fnPATHNAME => array(0, self::fnPATHNAME),
  154. self::fnSUBPAGES => array(0, self::fnSUBPAGES),
  155. self::fnUSER => array(0, self::fnUSER )),
  156. );
  157. // static methods
  158. // always call, seldom override
  159. // register setup and magic functions, and return magic file name.
  160. static
  161. function register()
  162. {
  163. global $wgHooks, $wgExtensionCredits, $wgExtensionFunctions;
  164. $magicfile = self::make_magic( self::PACKAGE, self::$funcs );
  165. if ( false != $magicfile )
  166. {
  167. self::register_credits( self::$hkCredit, self::$credits);
  168. $wgExtensionFunctions[] = __CLASS__.'::'.self::SETUP;
  169. $wgHooks[self::$hkLang][] = __CLASS__.'::'.self::MAGIC;
  170. }
  171. return $magicfile;
  172. }
  173. // seldom override, never call
  174. // add functions to parser hook
  175. static
  176. function setup()
  177. { // create a global instance variable
  178. $globalVar = self::pkPREFIX.self::PACKAGE;
  179. global $wgParser, $$globalVar;
  180. // create ourself globally
  181. $whoami = __CLASS__;
  182. $$globalVar = new $whoami;
  183. foreach( self::$funcs as $name )
  184. $wgParser->setFunctionHook( $name, array(
  185. &$$globalVar, $name ));
  186. }
  187. // never override, never call
  188. // return language specific function names
  189. static
  190. function magic( $magicWord, $langcode )
  191. {
  192. global $GLOBALS;
  193. $langVar = __CLASS__.self::mgSUFFIX;
  194. $lang = @$GLOBALS[$langVar][$langcode]; // try language var
  195. if ( !is_array( $lang ))
  196. $lang = @$GLOBALS[$langVar][self::mgDEFAULT]; // try file default
  197. if ( !is_array( $lang ))
  198. $lang = self::$magic[self::mgDEFAULT]; // try internal default
  199. if (is_array( $lang ))
  200. foreach ( self::$funcs as $key )
  201. $magicWord[$key]= $lang[$key];
  202. return true;
  203. }
  204. // figure out if $NS is name, number, or null
  205. private
  206. function ns_index( &$parser, &$NS )
  207. {
  208. if ( !is_numeric( $NS ))
  209. if ( !empty( $NS ))
  210. $ns_child = Namespace::getCanonicalIndex( strtolower( $NS ) );
  211. else $ns_child = $parser->getTitle()->getNamespace();
  212. else $ns_child = intval( $NS );
  213. return $ns_child;
  214. }
  215. // PARSER FUNCTIONS
  216. // Return a three column html table of subpage links rooted at the
  217. // parent's page. This allows navigating down path-names to provide
  218. // vertical tree browsing within a namespace, and horizontal jumping
  219. // between similarly named trees in other namespaces.
  220. // Note: This only lists the parent's immediate children.
  221. // FIXME: the column count should be a parametre, not hard-coded
  222. // syntax: {{#subpages: [NAMESPACE]}}}
  223. // where: NAMESPACE = name, index, or null
  224. function subpages( &$parser, $NS = NULL ) {
  225. global $wgCanonicalNamespaceNames; // for validating index
  226. $page_tab = '<span style="color:darkred;font-weight:800">
  227. [subpages] Warning: invalid namespace "'.$NS.'"</span>';
  228. // figure out if $NS is name, number, or null
  229. $ns_parent = $parser->getTitle()->getNamespace();
  230. if ( !is_numeric( $NS ))
  231. if ( !empty( $NS ))
  232. $ns_child = Namespace::getCanonicalIndex( strtolower( $NS ) );
  233. else $ns_child = $ns_parent;
  234. else $ns_child = intval( $NS );
  235. // validate ns and set caption
  236. if ( NS_MAIN != $ns_child ) // 0 is valid, but not in ns array
  237. // if ( 100 !== $ns_child )
  238. if ( is_null( $ns_child )
  239. || !array_key_exists( $ns_child, $wgCanonicalNamespaceNames ))
  240. return $page_tab; // invalid ns
  241. else $ns_caption = Namespace::getCanonicalName( $ns_child );
  242. // else $ns_caption = 'Faculty';
  243. else $ns_caption = "Page";
  244. $subpage = $ns_child == $ns_parent; // false == not a subpage
  245. // select parent's title + one path name
  246. $title_parent = str_replace( " ","_", $parser->getTitle()->getText() );
  247. $dbr = wfGetDB( DB_SLAVE );
  248. $res = $dbr->select(
  249. array( 'page' ),
  250. array( 'DISTINCT ON(page_title,page_namespace) page_title'/*, 'page_namespace'*/ ),
  251. array(
  252. 'page_title ~ \'^'.$title_parent.'/[^/]+$\'',
  253. 'page_namespace = \''.$ns_child.'\'',
  254. 'page_is_redirect = \'0\''),'',
  255. array( 'ORDER BY' => 'page_title ASC')/*,
  256. __METHOD__*/);
  257. $rows = $dbr->numRows( $res );
  258. // setup subpage table with one row and three columns
  259. if ( 0 < $rows ) {
  260. $limit = $rows / 3; // three columns per row
  261. $page_tab = '
  262. <table width="100%">
  263. <caption style="font-size:14pt">'
  264. .$ns_caption.' Sub-pages ('.$rows.')</caption>
  265. <tr>';
  266. // set link as either subpage, namespace, or main
  267. $link_prefix = '
  268. <li>[[';
  269. if ( !$subpage )
  270. if ( NS_MAIN != $ns_child )
  271. $link_prefix .= ':'.$ns_caption. ':' .$title_parent.'/';
  272. else $link_prefix .= $title_parent.'/';
  273. else $link_prefix .= '/';
  274. $link_suffix = ']]</li>';
  275. // names ordered by column and then row
  276. for( $col = 0; 3 > $col; ++$col ) {
  277. $page_tab .= '
  278. <td valign="top">';
  279. $ul = false;
  280. // NOTE: the list may have less than $limit objects
  281. // NOTE: $row is the list index, not the db row
  282. for( $row = 0; $limit > $row; ++$row ) {
  283. $x =& $dbr->fetchObject( $res );
  284. if ( $x ) {
  285. if ( !$ul ) {// open list
  286. $page_tab .= "<ul>";
  287. $ul = true; // remember to close list
  288. }
  289. $title_path = explode( '/', $x->page_title );
  290. $path_count = count( $title_path );
  291. // if the title is a pathed name, select the rightmost name
  292. if ( 0 < $path_count )
  293. $page_name = $title_path[ $path_count - 1 ];
  294. else
  295. $page_name = $x->page_title;
  296. // this is correct: the name must appear twice;
  297. // 1st for ending the link, 2nd for displayed link name
  298. if ( !$subpage )
  299. $page_name .= '|'.$page_name;
  300. $page_tab .= $link_prefix.$page_name.$link_suffix;
  301. } else
  302. {
  303. if ( $ul ) // open list, close it
  304. $page_tab .= "
  305. </ul>";
  306. $page_tab .= "</td>";
  307. break 2; // short list, simply continue
  308. }
  309. }
  310. if ( $ul ) // open list, close it
  311. $page_tab .= "
  312. </ul>";
  313. $page_tab .= "</td>";
  314. }
  315. $page_tab .= '
  316. </tr>
  317. </table>';
  318. } else $page_tab = ''; // no matches, ignore
  319. $dbr->freeResult( $res );
  320. return $page_tab;
  321. }
  322. // Return the $index path name in the title.
  323. function pathname( &$parser, $index, $count = 0 ) {
  324. $path_names = explode( "/", $parser->getTitle()->getText());
  325. if ( 0 == $count )
  326. $count = count( $path_names ) - $index;
  327. return implode("/", array_slice( $path_names, $index, $count ));
  328. }
  329. private
  330. function unqsort( array $value ) {
  331. $value = array_values( array_unique( $value ));
  332. sort( $value );
  333. return $value;
  334. }
  335. // Return a topic module/level table of existing topic pages
  336. // Depends on the path name of the current page.
  337. // FIXME: Add style params for table?
  338. function calendar( &$parser, $NS = NULL, $page_prefix = NULL )
  339. {
  340. global $wgCanonicalNamespaceNames; // for validating index
  341. $caption = 'Calendar';
  342. $page_tab = '<span style="color:darkred;font-weight:800">
  343. [calendar] Warning: invalid namespace "'.$NS.'"</span>';
  344. $ns_child = $this->ns_index( $parser, $NS );
  345. // validate ns and set caption
  346. if ( !is_null( $ns_child )
  347. && array_key_exists( $ns_child, $wgCanonicalNamespaceNames ))
  348. if ( NS_MAIN == $ns_child ) // 0 is valid, but not in ns array
  349. $ns_caption = "Page";
  350. else
  351. $ns_caption = Namespace::getCanonicalName( $ns_child );
  352. else return $page_tab;
  353. if ( !is_numeric( $page_prefix ))
  354. if ( is_null( $page_prefix ))
  355. $title_parent = $parser->getTitle()->getText();
  356. else
  357. {
  358. $title_parent = $page_prefix;
  359. $caption .= '['.$ns_caption.':'.$title_parent.']';
  360. }
  361. else return '<span style="color:darkred;font-weight:800">
  362. [calendar] Warning: invalid page name "'.$page_prefix.'"</span>';
  363. if ( !empty( $title_parent ))
  364. {
  365. $dbr = wfGetDB( DB_SLAVE );
  366. $row_res = $dbr->select(
  367. array( 'page' ),
  368. array( 'page_title', 'page_namespace', 'page_is_redirect' ),
  369. array(
  370. 'page_title ~ \''.$title_parent.'/[^/]+/[^/]+$\'',
  371. 'page_namespace = \''.$ns_child.'\''),
  372. array( 'ORDER BY' => 'page_title ASC'),
  373. __METHOD__);
  374. $row_count = $dbr->numRows( $row_res );
  375. } else return 'no parent page'; // no parent page
  376. if ( 0 == $row_count )
  377. // ..erm, free row_res first???
  378. return 'No modules found for '.$ns_caption.':'.$title_parent;
  379. // Create a keyed boolean table array, where the columns are
  380. // the schema and rows are the modules. A tab_array cell is true
  381. // if the schema is in the module's path.
  382. $col_headers = array(); // schema, e.g. 101, 102, ...
  383. $row_headers = array(); // modules, e.g. cooking, baking, ...
  384. for ( $row = 0; $row_count > $row; ++$row ) {
  385. $x = $dbr->fetchObject( $row_res );
  386. $path = array_slice( explode( "/", $x->page_title ), -2, 2 );
  387. $col_headers[] = $path[ 0 ];
  388. $row_headers[] = $path[ 1 ];
  389. $tab_array[ $path[ 0 ]][ $path[1] ] = $x->page_is_redirect;
  390. }
  391. $dbr->freeResult( $row_res );
  392. // clean up duplicates and rekey ...
  393. // echo $col_headers."= ".count($col_headers)."<br>";
  394. $col_headers = $this->unqsort( $col_headers );
  395. // must be 1st and out-of-order!
  396. array_unshift( $col_headers, 'Module/Level');
  397. $row_headers = $this->unqsort( $row_headers );
  398. // create table and column headers (as topic links)
  399. $page_tab = '
  400. <table width="100%" border="2">
  401. <caption style="font-size:14pt">'.$caption.'</caption>';
  402. $page_tab .= '<tr>';
  403. foreach( $col_headers as $key => $header )
  404. {
  405. if ( 0 == $key )
  406. $page_tab .= '<th>'.$header.'</th>';
  407. else
  408. // if ( empty( $levels ) || in_array( $header, $level_list ))
  409. $page_tab .= '<th>[['.$ns_caption.':'.$title_parent.'/'.$header.'|'.$header.']]</th>';
  410. }
  411. $page_tab .= '
  412. </tr>';
  413. // For every tab_array[schema][module] == true, create topic link
  414. // else create n/a data tag.
  415. for ( $row = 0; count( $row_headers) > $row; ++$row )
  416. {
  417. $row_key = $row_headers[ $row ];
  418. // if ( empty( $modules ) || in_array( $row_key, $module_list ))
  419. // {
  420. $page_tab .= '
  421. <tr>';
  422. for ( $col = 0; count( $col_headers) > $col; ++$col )
  423. {
  424. $col_key = $col_headers[ $col ];
  425. if ( 0 == $col ) // module name collumn
  426. {
  427. $page_tab .= '
  428. <td>'.$row_key.'</td>';
  429. }
  430. else
  431. if ( empty( $levels ) || in_array( $col_key, $level_list ))
  432. { // schema collumn
  433. // NOTE: tab_array is sparse, so ignore index warnings
  434. $value = @$tab_array[ $col_key ][ $row_key ];
  435. if ( !is_null( $value ))
  436. if ( 0 == intval( $value ) )
  437. $page_tab .= '
  438. <td style="text-align:center">[['.$ns_caption.':'
  439. .$title_parent.'/'.$col_key.'/'.$row_key.'|Available]]</td>';
  440. else
  441. $page_tab .= '
  442. <td style="text-align:center">[['.$ns_caption.':'
  443. .$title_parent.'/'.$col_key.'/'.$row_key.'|redirect]]</td>';
  444. else
  445. $page_tab .= '
  446. <td style="text-align:center">N/A</td>';
  447. }
  448. }
  449. $page_tab .= '
  450. </tr>';
  451. }//}
  452. $page_tab .= '
  453. <table>';
  454. return $page_tab;
  455. }
  456. public
  457. function user (&$parser)
  458. {
  459. global $wgUser;
  460. $parser->disableCache();
  461. if ($wgUser->isAnon()) {
  462. return 'anon';
  463. }
  464. return $wgUser->getName();
  465. }
  466. }
  467. // register the extension
  468. require_once( pfSubPages::register() );
  469. ?>