PageRenderTime 27ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/extensions/uniwiki/GenericEditPage/GenericEditPage.php

https://github.com/ChuguluGames/mediawiki-svn
PHP | 782 lines | 477 code | 134 blank | 171 comment | 110 complexity | 53d7496bcb641de7f7ac05a3e1a69d7b MD5 | raw file
  1. <?php
  2. /* vim: noet ts=4 sw=4
  3. * http://www.mediawiki.org/wiki/Extension:Uniwiki_Generic_Edit_Page
  4. * http://www.gnu.org/licenses/gpl-3.0.txt */
  5. if ( !defined( 'MEDIAWIKI' ) )
  6. die();
  7. $wgExtensionCredits['other'][] = array(
  8. 'path' => __FILE__,
  9. 'name' => 'GenericEditPage',
  10. 'author' => array( 'Merrick Schaefer', 'Mark Johnston', 'Evan Wheeler', 'Adam Mckaig (at UNICEF)' ),
  11. 'url' => 'http://www.mediawiki.org/wiki/Extension:Uniwiki_Generic_Edit_Page',
  12. 'descriptionmsg' => 'gep-desc',
  13. );
  14. $wgExtensionMessagesFiles['GenericEditPage'] = dirname( __FILE__ ) . '/GenericEditPage.i18n.php';
  15. /* ---- CONFIGURABLE OPTIONS ---- */
  16. $wgSectionBox = true;
  17. $wgCategoryBox = true;
  18. $wgAddSection = true;
  19. $wgAddCategory = true;
  20. $wgSuggestCategory = false;
  21. $wgSuggestCategoryRecipient = $wgEmergencyContact;
  22. $wgUseCategoryPage = false;
  23. $wgRequireCategory = false;
  24. $wgAlwaysShowIntroSection = false;
  25. $wgGenericEditPageWhiteList = array( NS_MAIN );
  26. $wgAllowSimilarTitles = true;
  27. /* not configurable. in fact,
  28. * it's a big ugly hack */
  29. $wgSwitchMode = false;
  30. /* ---- HOOKS ---- */
  31. $wgHooks['BeforePageDisplay'][] = "UW_GenericEditPage_addJS";
  32. $wgHooks['EditPage::showEditForm:fields'][] = 'UW_GenericEditPage_displayEditPage';
  33. $wgHooks['EditPage::attemptSave'][] = 'UW_GenericEditPage_combineBeforeSave';
  34. $wgHooks['EditPage::showEditForm:initial'][] = 'UW_GenericEditPage_combineBeforeSave';
  35. $wgAjaxExportList[] = "UW_GenericEditPage_emailSuggestion";
  36. function UW_GenericEditPage_addJS( $out ) {
  37. global $wgExtensionAssetsPath;
  38. $out->addScriptFile( "$wgExtensionAssetsPath/uniwiki/GenericEditPage/GenericEditPage.js" );
  39. $out->addExtensionStyle( "$wgExtensionAssetsPath/uniwiki/GenericEditPage/global.css" );
  40. return true;
  41. }
  42. function UW_GenericEditPage_emailSuggestion ( $category ) {
  43. global $wgSuggestCategoryRecipient, $wgEmergencyContact, $wgSitename, $wgUser;
  44. $from = new MailAddress ( $wgEmergencyContact );
  45. $to = new MailAddress ( $wgSuggestCategoryRecipient );
  46. $subj = wfMsg ( "gep-emailsubject", $wgSitename, $category );
  47. $body = wfMsg ( "gep-emailbody", $wgUser->getName(), $category, $wgSitename );
  48. // attempt to send the notification
  49. $result = UserMailer::send( $to, $from, $subj, $body );
  50. /* send a message back to the client, to let them
  51. * know if the suggestion was successfully sent (or not) */
  52. return WikiError::isError ( $result )
  53. ? wfMsg ( 'gep-emailfailure' )
  54. : wfMsg ( 'gep-emailsuccess', $category );
  55. }
  56. function UW_GenericEditPage_extractLayout ( &$text ) {
  57. /* match all layout tags (in case, some how, multiple
  58. * tags have ended up in the wiki markup). only the
  59. * first tag will be used, but all will be removed */
  60. $re = "/\n*<layout\s+name=\"(.+)\"\s+\/>/";
  61. preg_match_all ( $re, $text, $matches );
  62. $text = preg_replace ( $re, "", $text );
  63. /* if no layout tag was found, this
  64. * function does nothing useful */
  65. if ( !isset( $matches[1][0] ) ) {
  66. return array();
  67. }
  68. /* get the wiki markup (containing the
  69. * directives) from this page's layout */
  70. $layout = array();
  71. $layout_title = Title::newFromDBkey ( "Layout:" . $matches[1][0] );
  72. $layout_article = new Article ( $layout_title );
  73. $layout_text = $layout_article->getContent();
  74. // add the first element as page meta-data
  75. // and the second for the untitled first section
  76. $layout[] = array ( "name" => $matches[1][0] );
  77. $layout[] = array();
  78. // ignore (delete) the categories on the layout
  79. $layout_text = preg_replace ( "/\[\[category:(.+?)\]\]/i", "", $layout_text );
  80. // break it up into sections (same regex as in displayEditPage)
  81. $nodes = preg_split ( '/^(==?[^=].*)$/mi', $layout_text, -1, PREG_SPLIT_DELIM_CAPTURE );
  82. // build an array with the layout section attributes
  83. for ( $i = 0; $i < count ( $nodes ); $i++ ) {
  84. $value = trim ( $nodes[$i] );
  85. /* is this block of text a header?
  86. * update the 'current section' flag ($title), so
  87. * all following directives are dropped in to it */
  88. if ( preg_match ( '/^(==?)\s*(.+?)\s*\\1$/i', $value, $matches ) ) {
  89. $layout[] = array(
  90. "title" => htmlspecialchars( $matches[2] ),
  91. "level" => strlen( $matches[1] )
  92. );
  93. // not header -> plain text
  94. } else {
  95. /* find and iterate all directives in this section,
  96. * and add them to the last-seen title array */
  97. $re = "/^@(.+)$/m";
  98. $section_num = count ( $layout ) - 1;
  99. preg_match_all ( $re, $value, $matches );
  100. foreach ( $matches[1] as $attribute )
  101. $layout[$section_num][$attribute] = true;
  102. // add the remaining stuff as text
  103. $value = preg_replace ( $re, "", $value );
  104. $layout[$section_num]['text'] = trim( $value );
  105. }
  106. }
  107. return $layout;
  108. }
  109. function UW_GenericEditPage_extractCategoriesIntoBox( &$text ) {
  110. global $wgDBprefix, $wgAddCategory, $wgSuggestCategory, $wgRequest,
  111. $wgEmergencyContact, $wgUseCategoryPage;
  112. $out = "";
  113. /* build an array of the categories, either from a page
  114. * or from all available categories in the wiki */
  115. $categories = array();
  116. if ( $wgUseCategoryPage ) {
  117. // from the specified page
  118. $revision = Revision::newFromTitle ( Title::newFromDBKey ( wfMsgForContent( 'gep-categorypage' ) ) );
  119. $results = $revision ? split ( "\n", $revision->getText() ) : array();
  120. foreach ( $results as $result ) {
  121. if ( trim( $result ) != '' )
  122. $categories[] = Title::newFromText ( trim ( $result ) )->getDBkey();
  123. }
  124. } else {
  125. // all the categories
  126. $db = wfGetDB ( DB_MASTER );
  127. $results = $db->resultObject ( $db->query(
  128. "select distinct cl_to from {$wgDBprefix}categorylinks order by cl_to" ) );
  129. while ( $result = $results->next() )
  130. $categories[] = $result->cl_to;
  131. }
  132. // extract the categories on this page
  133. $regex = "/\[\[category:(.+?)(?:\|.*)?\]\]/i";
  134. preg_match_all ( $regex, strtolower( $text ), $matches );
  135. $text = preg_replace ( $regex, "", $text );
  136. // an array of the categories on the page (in db form)
  137. $on_page = array();
  138. foreach ( $matches[1] as $cat )
  139. $on_page[] = strtolower ( Title::newFromText ( $cat )->getDBkey() );
  140. /* add any categories that may have been passed with the
  141. * GET request as if they started out on the page */
  142. $data = $wgRequest->getValues();
  143. foreach ( $data as $key => $value ) {
  144. if ( $key == 'category' ) {
  145. $category = substr ( $value, 9 ); // value = category-categoryname
  146. $on_page[] = strtolower ( $category );
  147. if ( !in_array ( $category, $categories ) )
  148. $categories[] = $category;
  149. }
  150. }
  151. /* add checkboxes for the categories,
  152. * with ones from the page already checked */
  153. $out .= "<div id='category-box'><h3>" . wfMsg ( 'gep-categories' ) . "</h3>";
  154. foreach ( $categories as $category ) {
  155. $fm_id = "category-$category";
  156. $caption = Title::newFromDBkey ( $category )->getText();
  157. $checked = in_array ( strtolower ( $category ), $on_page )
  158. ? "checked='checked'" : '';
  159. $out .= "
  160. <div>
  161. <input type='checkbox' name='$fm_id' id='$fm_id' $checked/>
  162. <label for='$fm_id'>$caption</label>
  163. </div>
  164. ";
  165. }
  166. // add a text field to add new categories
  167. if ( $wgAddCategory ) {
  168. $out .= "
  169. <div class='add'>
  170. <label for='fm-add-cat'>" . wfMsg ( 'gep-addcategory' ) . "</label>
  171. <input type='text' id='fm-add-cat' autocomplete='off' />
  172. <input type='button' value='" . wfMsg ( 'gep-addcategorybutton' ) . "' id='fm-add-cat-button' />
  173. </div>
  174. <script type='text/javascript'>
  175. // hook up the 'add category' box events
  176. Uniwiki.GenericEditPage.Events.add_category();
  177. </script>
  178. ";
  179. }
  180. /* add a text field to suggest a category
  181. * as email to $wgEmergencyContact */
  182. if ( $wgSuggestCategory ) {
  183. $out .= "
  184. <div class='suggest'>
  185. <label for='fm-suggest-cat'>" . wfMsg ( 'gep-suggestcategory' ) . "</label>
  186. <input type='text' id='fm-suggest-cat' autocomplete='off' />
  187. <input type='button' value='" . wfMsg ( 'gep-suggestcategorybutton' ) . "' id='fm-suggest-cat-button' />
  188. </div>
  189. <script type='text/javascript'>
  190. $('fm-suggest-cat-button').addEvent ('click', function() {
  191. var field = this.getPrevious();
  192. var that = this;
  193. // only if a category name was entered...
  194. var cat_name = field.value.trim();
  195. if (cat_name != '') {
  196. sajax_do_call ('UW_GenericEditPage_emailSuggestion', [cat_name], function (msg) {
  197. /* got response from the server, append it after the
  198. * suggest form (so subsequent suggestions are injected
  199. * ABOVE existing suggetions before they're removed) */
  200. var n = new Element ('div')
  201. .injectAfter ('fm-suggest-cat-button', 'after')
  202. .appendText (msg.responseText)
  203. .highlight();
  204. /* fade out and destroy the notification within
  205. * a timely manner, to keep the DOM tidy */
  206. (function() { n.fade() }).delay(6000);
  207. (function() { n.destroy() }).delay(8000);
  208. });
  209. // clear the suggestion
  210. field.value = '';
  211. }
  212. });
  213. // catch the ENTER key in the suggestion box,
  214. // to prevent the entire edit form from submitting
  215. $('fm-suggest-cat').addEvent ('keypress', function (e) {
  216. if (e.key == 'enter') {
  217. this.getNext().fireEvent ('click');
  218. e.stop();
  219. }
  220. });
  221. </script>
  222. ";
  223. }
  224. // end of category box
  225. $out .= "</div>";
  226. return $out;
  227. }
  228. function UW_GenericEditPage_renderSectionBox ( $sections ) {
  229. global $wgAddSection;
  230. $out = "
  231. <div id='section-box'>
  232. <h3>" . wfMsg ( 'gep-sections' ) . "</h3>
  233. <div class='sortables'>
  234. ";
  235. for ( $i = 1; $i < count ( $sections ); $i++ ) {
  236. if ( !empty($sections[$i]['required']) ) {
  237. /* required sections are checked and disabled, but...
  238. * this doesn't pass a value back to the server! so
  239. * we must include a hidden field to fill in */
  240. $out .= "
  241. <div id='sect-$i' class='section-box disabled'>
  242. <input type='hidden' name='enable-$i' value='on' />
  243. <input type='checkbox' checked='checked' disabled='disabled' />
  244. <label title='" . wfMsg ( 'gep-sectionnotdisabled' ) . "'>{$sections[$i]['title']}</label>
  245. </div>
  246. ";
  247. } else {
  248. // sections that are currently in us are pre-checked
  249. $checked = $sections[$i]['in-use'] ? " checked='checked'" : "";
  250. $out .= "
  251. <div id='sect-$i' class='section-box'>
  252. <input type='checkbox' name='enable-$i' $checked />
  253. <label>{$sections[$i]['title']}</label>
  254. </div>
  255. ";
  256. }
  257. }
  258. $out .= "
  259. </div>
  260. ";
  261. if ( $wgAddSection ) {
  262. $out .= "
  263. <div class='add'>
  264. <label for='fm-add-sect'>" . wfMsg ( 'gep-addsection' ) . "</label>
  265. <input type='text' id='fm-add-sect' autocomplete='off' />
  266. <input type='button' value='" . wfMsg ( 'gep-addsectionbutton' ) . "' id='fm-add-sect-button' />
  267. </div>
  268. <script type='text/javascript'>
  269. // hook up the 'add section' box events
  270. Uniwiki.GenericEditPage.Events.add_section();
  271. </script>
  272. ";
  273. }
  274. $out .= "
  275. </div>
  276. <script type='text/javascript'>
  277. Uniwiki.GenericEditPage.Events.toggle_sections();
  278. Uniwiki.GenericEditPage.Events.reorder_sections();
  279. </script>
  280. ";
  281. return $out;
  282. }
  283. function UW_GenericEditPage_displayEditPage ( $editor, $out ) {
  284. global $wgHooks, $wgParser, $wgTitle, $wgRequest, $wgUser, $wgCategoryBox, $wgSectionBox, $wgRequireCategory;
  285. global $wgGenericEditPageClass, $wgSwitchMode, $wgGenericEditPageWhiteList, $wgAllowSimilarTitles;
  286. global $wgAlwaysShowIntroSection, $wgExtensionAssetsPath;
  287. // disable this whole thing on conflict and comment pages
  288. if ( $editor->section == "new" || $editor->isConflict )
  289. return true;
  290. // get the article text (as wiki markup)
  291. $text = trim ( $editor->safeUnicodeOutput ( $editor->textbox1 ) );
  292. // see if we have a link to a layout
  293. $layout = UW_GenericEditPage_extractLayout ( $text );
  294. /* remove the categories, to be added as
  295. * checkboxes later (after the edit form) */
  296. if ( $wgCategoryBox ) {
  297. $catbox = UW_GenericEditPage_extractCategoriesIntoBox ( $text );
  298. }
  299. /* break the page up into sections by splitting
  300. * at header level =one= or ==two== */
  301. $nodes = preg_split( '/^(==?[^=].*)$/mi', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
  302. // add css hooks only to the edit page
  303. $out->addExtensionStyle( "$wgExtensionAssetsPath/uniwiki/GenericEditPage/style.css" );
  304. $wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'UW_GenericEditPage_addCssHookGenEd';
  305. $wgHooks['OutputPageBodyAttributes'][] = 'UW_GenericEditPage_onOutputPageBodyAttributes';
  306. /* the current contents of the page we are
  307. * editing will be broken up into $page(and
  308. * combined with $layout, later on) */
  309. $page = array();
  310. $title = "";
  311. /* always create a space for the first un-named
  312. * section, even if it is not used */
  313. $page[] = array();
  314. for ( $i = 0; $i < count ( $nodes ); $i++ ) {
  315. $value = trim ( $nodes[$i] );
  316. $this_section = count ( $page ) - 1;
  317. // is this block of text a header?
  318. $node_is_title = preg_match ( '/^(==?)\s*(.+?)\s*\\1$/i', $value, $matches );
  319. /* for titles, create a new element in the
  320. * page array, to store the title and text */
  321. if ( $node_is_title ) {
  322. // extract header level and title
  323. $level = strlen ( $matches[1] );
  324. $title = htmlspecialchars ( $matches[2] );
  325. // add this section and mark it as in use
  326. $page[] = array(
  327. 'title' => $title,
  328. 'level' => $level,
  329. 'in-use' => true
  330. );
  331. /* not header -> plain text (store in
  332. / * the previous section, with title) */
  333. } else {
  334. $page[$this_section]['text'] = $value;
  335. }
  336. /* fetch the meta-data for new sections, or
  337. * the un-named first section. this is done
  338. * here (not when merging) because meta-data
  339. * can ONLY come from the layout (not the page) */
  340. if ( $node_is_title or $i == 0 ) {
  341. /* now check if any meta-data exists for this
  342. * section in the layout for this page
  343. * (ignore the first two sections)
  344. * j = 0: page meta-data
  345. * j = 1: special first section */
  346. for ( $j = 1; $j < count ( $layout ); $j++ ) {
  347. if ( $layout[$j]['title'] == $title ) {
  348. // we found a corresponding section in the layout
  349. // so we copy all the associated meta-data
  350. foreach ( $layout[$j] as $k => $v ) {
  351. // don't overwrite the page text!
  352. if ( $k != "text" ) $page[count ( $page ) - 1][$k] = $v;
  353. }
  354. }
  355. }
  356. }
  357. }
  358. /* the results of the
  359. * layout + page merge */
  360. $result = array();
  361. /* special case: if the first (un-named) section has text in the layout,
  362. * but not in the page, copy it. otherwise, use the page text (even if empty) */
  363. $result[] = ( !empty($layout[0]) && $layout[0]['text'] && !$page[0]['text'] ) ? $layout[0] : $page[0];
  364. /* decide whether or not to show the introduction section.
  365. * if configured to do so, it won't appear if there isn't any text in it already */
  366. $result[0]['in-use'] = $wgAlwaysShowIntroSection ? true : ( $result[0]['text'] != '' );
  367. // get sections that are in the layout
  368. for ( $i = 2; $i < count ( $layout ); $i++ ) {
  369. $found_at = null;
  370. for ( $j = 0; $j < count ( $page ); $j++ ) {
  371. if ( $layout[$i]['title'] == $page[$j]['title'] ) {
  372. $found_at = $j;
  373. break;
  374. }
  375. }
  376. if ( !$found_at ) { // take the section from the layout
  377. $result[] = $layout[$i];
  378. } else {
  379. $result[] = $page[$found_at];
  380. }
  381. }
  382. // now put in the stuff that is not in the layout, but IS in the page
  383. for ( $i = 1; $i < count ( $page ); $i++ ) {
  384. // if this section is already in the result,
  385. // then skip to the next page section
  386. if(!$wgAllowSimilarTitles){
  387. for ( $j = 0; $j < count ( $result ); $j++ ) {
  388. if (!empty($result[$j]['title']) && $page[$i]['title'] == $result[$j]['title'] )
  389. continue 2;
  390. }
  391. }
  392. /* this page section has not been added yet!
  393. * re-iterate the results, to find the index
  394. * of the section BEFORE this section, which
  395. * we will insert after */
  396. $insert_at = null;
  397. for ( $j = 0; $j < count ( $result ); $j++ ) {
  398. if (!empty($result[$j]['title']) && $result[$j]['title'] == $page[$i - 1]['title'] ) {
  399. $insert_at = $j + 1;
  400. break;
  401. }
  402. }
  403. if ( $insert_at === null )
  404. $result[] = $page[$i];
  405. else
  406. array_splice ( $result, $insert_at, 0, array( $page[$i] ) );
  407. }
  408. /* check to see if there were any sections in use
  409. * if there are not, we will display instructions */
  410. $any_in_use = false;
  411. for ( $i = 0; $i < count( $result ); $i++ ) {
  412. if ( $result[$i]['in-use'] ) {
  413. $any_in_use = true;
  414. break;
  415. }
  416. }
  417. // use the default (untitled) section if there is nothing else
  418. if ( !$any_in_use ) {
  419. $result[0]['in-use'] = true;
  420. $any_in_use = true;
  421. }
  422. // this line is sort of outdated... may want to remove?
  423. $gen_editor_class = $any_in_use ? "" : " show-instructions";
  424. /* add the buttons to switch between editing modes
  425. * (only one is visible at a time, via css/js) and
  426. * start the generic editor div */
  427. $out->addHTML( "
  428. <script type='text/javascript' language='javascript'>
  429. $(window).addEvent ('domready', function() {
  430. /* add CLASSIC button
  431. * (visibility via css) */
  432. new Element ('input', {
  433. type: 'button',
  434. 'class': 'switch sw-classic',
  435. events: { click: Uniwiki.GenericEditPage.toggle_mode },
  436. value: '" . wfMsg ( 'gep-classicmode' ) . "'
  437. }).inject ($('content'));
  438. /* add GENERIC button
  439. * (visibility via css) */
  440. new Element ('input', {
  441. type: 'button',
  442. 'class': 'switch sw-generic',
  443. events: { click: Uniwiki.GenericEditPage.toggle_mode },
  444. value: '" . wfMsg ( 'gep-genericmode' ) . "'
  445. }).inject($('content'));
  446. " );
  447. /* only enforce the categorization of pages in the
  448. * main namespace, using the generic edit page */
  449. if ( $wgRequireCategory && ( $wgTitle->getNamespace() == NS_MAIN ) ) {
  450. $out->addHTML( "
  451. /* when the form is submitted, check that one or
  452. * more categories were selected, or alert */
  453. $('editform').addEvent ('submit', function (e) {
  454. // only enforce when in generic mode
  455. if (!document.body.hasClass ('edit-generic'))
  456. return true;
  457. /* iterate the category checkboxes, and count
  458. * how many of them are 'ticked' */
  459. var checked = 0;
  460. $$('#category-box input[type=checkbox]').each (function (el) {
  461. if (el.checked) checked++;
  462. });
  463. if (checked==0) {
  464. alert('" . wfMsg ( 'gep-nocategories' ) . "');
  465. e.stop();
  466. }
  467. });
  468. " );
  469. }
  470. $out->addHTML( "
  471. /* if the sidebar is taller than the editor,
  472. * make the editor the same height */
  473. var sbs = $('sidebar').getSize();
  474. var eds = Uniwiki.GenericEditPage.editor().getSize();
  475. if (sbs.y > eds.y) Uniwiki.GenericEditPage.editor().setStyle
  476. ((Browser.Engine.trident ? 'height' : 'min-height'), sbs.y+'px');
  477. });
  478. </script>
  479. <!-- will be assigned the name of 'wpPreview' when the
  480. switch-mode button is pressed, because mediawiki
  481. only checks for its presence, not value (?!) -->
  482. <input type='hidden' id='wpFakePreview' name='' value='' />
  483. <input type='hidden' id='switch-mode' name='switch-mode' value='' />
  484. <div class='generic-editor $gen_editor_class'>
  485. <div class='instructions'>" . wfMsg ( 'gep-nosectioninstructions' ) . "</div>
  486. " );
  487. // now build the results into a real HTML page
  488. for ( $i = 0; $i < count ( $result ); $i++ ) {
  489. $in_use = $result[$i]['in-use'] ? "in-use" : "not-in-use";
  490. $out->addHTML( "<div id='section-$i' class='section sect-" . ( $i + 1 ) . " $in_use'>" );
  491. // if this section has a title, show it
  492. if ( !empty($result[$i]['title']) ) {
  493. $title = $result[$i]['title'];
  494. if (!empty($result[$i]['lock-header']) && $result[$i]['lock-header'] ) {
  495. $out->addHTML ( "<h2>$title</h2>" );
  496. $out->addHTML ( "<input type='hidden' name='title-$i' value='$title' />" );
  497. } else {
  498. // wrap the editable section header in a div, for css hackery
  499. $out->addHTML ( "<div class='sect-title'><input type='text' name='title-$i' class='section-title' value='$title' /></div>" );
  500. }
  501. // always include the level of titles
  502. $out->addHTML ( "<input type='hidden' name='level-$i' value='{$result[$i]['level']}' />" );
  503. }
  504. /* always add a textarea, whether or
  505. * not it is currently in use. titles
  506. * without text are kind of useless */
  507. if ( !empty($result[$i]['lock-text']) ) {
  508. /* render the wiki markup into HTML, the old-school
  509. * way which actually works, unlike recursiveTagParse() */
  510. $text = $wgParser->parse ( $result[$i]['text'], $wgTitle, new ParserOptions )->getText();
  511. $out->addHTML ( "<input type='hidden' name='section-$i' value='" . htmlspecialchars ( $result[$i]['text'], ENT_QUOTES ) . "' />" );
  512. $out->addHTML ( "<div class='locked-text' id='locked-text-$i'>" . $text . "</div>" );
  513. } else {
  514. // add the editable text for this section
  515. $text = (empty($result[$i]['text'])) ? "" : $result[$i]['text'];
  516. $text = htmlspecialchars ($text , ENT_QUOTES );
  517. $out->addHTML ( "<textarea name='section-$i' class='editor'>$text</textarea>" );
  518. }
  519. $out->addHTML( "</div>" );
  520. }
  521. /* end of .generic-editor
  522. * (and some javascript!) */
  523. $out->addHTML( "
  524. </div>
  525. <script type='text/javascript'>
  526. /* when any section title is changed (in the generic
  527. * editor), update the sections on the right, too */
  528. window.addEvent ('domready', function() {
  529. Uniwiki.GenericEditPage.Events.section_title_change()
  530. });
  531. </script>
  532. " );
  533. /* if the article we're editing is in the whitelist, then
  534. * default to the generic editor; otherwise, default to
  535. * classic mode (but allow switching) */
  536. $default = in_array ( $wgTitle->getNamespace(), $wgGenericEditPageWhiteList )
  537. ? "generic" : "classic";
  538. /* use the mode from the last request; default to generic (the first time)
  539. * if we are switching modes, then use the OPPOSITE mode */
  540. $klass = $wgRequest->getVal ( "edit-mode", $default );
  541. if ( $wgSwitchMode ) $klass = ( $klass == "classic" ) ? "generic" : "classic";
  542. $out->addHTML ( "<input type='hidden' name='edit-mode' id='edit-mode' value='$klass' />" );
  543. $wgGenericEditPageClass = $klass;
  544. // pass the layout name back, to be re-appended
  545. if(!empty($layout))
  546. $out->addHTML ( "<input type='hidden' name='layout-name' value='{$layout[0]['name']}' />" );
  547. // build the sidebar (cats, sections) in its entirety
  548. if ( $wgCategoryBox || $wgSectionBox ) {
  549. $out->addHTML ( "<div id='sidebar'>" );
  550. if ( $wgSectionBox ) $out->addHTML ( UW_GenericEditPage_renderSectionBox( $result ) );
  551. if ( $wgCategoryBox ) $out->addHTML ( $catbox );
  552. $out->addHTML ( "</div>" );
  553. }
  554. return true;
  555. }
  556. /* OH, HOW I WISH FOR CLOSURES IN PHP, SO
  557. * THESE DIDN'T HAVE TO BE GLOBAL FUNCTIONS */
  558. /* when this hook is called (only on the generic edit page), add a
  559. * special class to the <body> tag, to make targetting our css easier
  560. * (also add a hook if we just switched modes, to hide the preview) */
  561. function UW_GenericEditPage_addCssHookGenEd ( &$sktemplate, &$tpl ) {
  562. global $wgGenericEditPageClass, $wgSwitchMode, $wgCategoryBox;
  563. $tpl->data['pageclass'] .= " edit-$wgGenericEditPageClass";
  564. if ( $wgSwitchMode ) {
  565. $tpl->data['pageclass'] .= ' switching-mode';
  566. }
  567. if ( $wgCategoryBox ) {
  568. $tpl->data['pageclass'] .= ' with-sidebar';
  569. }
  570. return true;
  571. }
  572. function UW_GenericEditPage_onOutputPageBodyAttributes( $out, $sk, &$attr ) {
  573. global $wgGenericEditPageClass, $wgSwitchMode, $wgCategoryBox;
  574. $attr['class'] .= " edit-$wgGenericEditPageClass";
  575. if ( $wgSwitchMode ) {
  576. $attr['class'] .= ' switching-mode';
  577. }
  578. if ( $wgCategoryBox ) {
  579. $attr['class'] .= ' with-sidebar';
  580. }
  581. return true;
  582. }
  583. function UW_GenericEditPage_combineBeforeSave ( &$editpage_Obj ) {
  584. global $wgRequest, $wgSwitchMode;
  585. $data = $wgRequest->getValues();
  586. /* if this request was triggered by the user
  587. * pressing the "switch mode" button, then
  588. * set a global to do some jiggery-pokery
  589. * in the displayEditPage function, later */
  590. if ( isset( $data['switch-mode'] ) and strlen( $data['switch-mode']) > 0)
  591. $wgSwitchMode = true;
  592. /* if we are editing in classic mode,
  593. * then this function does nothing! */
  594. if ( ( isset( $data['edit-mode'] ) && $data['edit-mode'] != "generic" ) || !isset( $data['edit-mode'] ) )
  595. return true;
  596. /* otherwise, clear the textbox and rebuild it
  597. * from the generic input POST data */
  598. $editpage_Obj->textbox1 = '';
  599. $nodes = array();
  600. $categories = array();
  601. $directives = array();
  602. foreach ( $data as $key => $value ) {
  603. if ( trim ( $value ) != '' ) {
  604. if ( substr( $key, 0, 6 ) == 'title-' ) {
  605. $index = intval ( substr( $key, 6 ) );
  606. /* only add this section if it is enabled,
  607. * by checking the associated checkbox */
  608. if ( isset ( $data["enable-$index"] ) ) {
  609. /* got a title -> add it back as a header,
  610. * by fetching the level from field "level-N" */
  611. $level = isset ( $data["level-$index"] ) ? $data["level-$index"] : 2;
  612. $delim = str_repeat ( "=", $level );
  613. $nodes[$index]['title'] = "$delim " . trim ( $value ) . " $delim\n";
  614. }
  615. /* got a section -> check that associated checkbox is
  616. * ticked (or this is the first magic section, which
  617. * does not have a checkbox), and add it into the output */
  618. } else if ( substr ( $key, 0, 8 ) == 'section-' ) {
  619. $index = intval ( substr( $key, 8 ) );
  620. if ( isset ( $data["enable-$index"] ) || ( $index == 0 ) ) {
  621. $nodes[$index]['text'] = trim( $value ) . "\n\n";
  622. }
  623. /* got a category -> add it to the list of categories and put
  624. * it into the page as human-friendly text (i.e. not a DB key) */
  625. } else if ( substr ( $key, 0, 9 ) == 'category-' ) {
  626. $categories[] = Title::newFromDBkey ( substr ( $key, 9 ) )->getText();
  627. }
  628. }
  629. }
  630. /* put the section titles and text
  631. * back into the default textbox */
  632. foreach ( array_keys ( $nodes ) as $k ) {
  633. if ( !empty($nodes[$k]['title']) ) $editpage_Obj->textbox1 .= $nodes[$k]['title'];
  634. if ( !empty($nodes[$k]['text']) ) $editpage_Obj->textbox1 .= $nodes[$k]['text'];
  635. }
  636. // then add back the categories
  637. if ( count ( $categories ) != 0 ) {
  638. sort ( $categories );
  639. foreach ( $categories as $category ) {
  640. $editpage_Obj->textbox1 .= "[[Category:" . $category . "]]\n";
  641. }
  642. }
  643. // finally, re-add the layout name
  644. if ( isset( $data['layout-name'] ) ) {
  645. $editpage_Obj->textbox1 .= "<layout name=\"{$data['layout-name']}\" />";
  646. }
  647. return true;
  648. }