PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/web/wiki.php

https://bitbucket.org/dhobsd/mtrack
PHP | 383 lines | 341 code | 33 blank | 9 comment | 38 complexity | e0805916fea760dc66b85ddadfe48f49 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. <?php # vim:ts=2:sw=2:et:
  2. /* For licensing and copyright terms, see the file named LICENSE */
  3. include '../inc/common.php';
  4. $pi = mtrack_get_pathinfo();
  5. if (empty($pi)) {
  6. header("Location: {$ABSWEB}wiki.php/WikiStart");
  7. exit;
  8. }
  9. $rev = isset($_GET['rev']) ? $_GET['rev'] : null;
  10. if (!$rev || !preg_match("/^[a-fA-F0-9]+$/", $rev)) {
  11. $rev = null;
  12. }
  13. $hide_outline = false;
  14. try {
  15. if ($rev) {
  16. $W = MTrackAPI::invoke('GET', '/wiki/page/' . $pi, null, array(
  17. 'rev' => $rev
  18. ))->result;
  19. } else {
  20. $W = MTrackAPI::invoke('GET', '/wiki/page/' . $pi)->result;
  21. }
  22. $ATTACH = json_encode(MTrackAPI::invoke(
  23. 'GET', "/wiki/attach/" . $pi)->result);
  24. $edit = ($rev == null) && MTrackACL::hasAnyRights("wiki:$pi", 'modify');
  25. } catch (MTrackAPI_Exception $e) {
  26. if ($e->getCode() != 404) {
  27. throw $e;
  28. }
  29. /* if they're looking at a directory, give them a list of candidate
  30. * pages to look at instead */
  31. $tree = MTrackWikiItem::get_wiki_tree();
  32. $path_bits = explode('/', $pi);
  33. $dir = $tree;
  34. foreach ($path_bits as $name) {
  35. if (!isset($tree->{$name})) {
  36. $tree = null;
  37. break;
  38. }
  39. $tree = $tree->{$name};
  40. }
  41. if ($tree) {
  42. $edit = false;
  43. /* fake an empty summary page listing out the elements on this one */
  44. $W = new stdclass;
  45. $W->id = $pi;
  46. $W->version = null;
  47. $W->content = "\n\n";
  48. foreach ($tree as $k => $name) {
  49. $W->content .= " * [wiki:$pi/$k $k]\n";
  50. }
  51. $W->content .= "\n\n[help: Complete List of Wiki Pages]\n";
  52. $W->content_html = MTrackWiki::format_to_html($W->content);
  53. $W->changelog_html = null;
  54. $W->changelog = null;
  55. $W->when = null;
  56. $W->who = null;
  57. $ATTACH = json_encode(array());
  58. $hide_outline = true;
  59. } else {
  60. /* fake an empty one so that we can give the user the option of creating
  61. * a new page */
  62. $W = new stdclass;
  63. $W->id = $pi;
  64. $W->version = null;
  65. $W->content = null;
  66. $W->content_html = '';
  67. $W->changelog_html = null;
  68. $W->changelog = null;
  69. $W->when = null;
  70. $W->who = null;
  71. $ATTACH = json_encode(array());
  72. $edit = MTrackACL::hasAnyRights("wiki:$pi", 'create');
  73. if (!$edit) {
  74. header("HTTP/1.0 404 Not Found");
  75. mtrack_head($pi);
  76. echo "<h1>Page Not Found</h1>";
  77. mtrack_foot();
  78. exit;
  79. }
  80. }
  81. }
  82. mtrack_head($pi);
  83. if ($hide_outline) {
  84. echo <<<HTML
  85. <script>
  86. $(document).ready(function() {
  87. $('#outlinetoggle').hide();
  88. $('#wikiinfo').hide();
  89. });
  90. </script>
  91. HTML;
  92. }
  93. $edit = json_encode($edit);
  94. $latest = json_encode($rev == null);
  95. $J = json_encode($W);
  96. $crumbs = mtrack_breadcrumb($pi, $ABSWEB . 'wiki.php');
  97. echo <<<HTML
  98. <div id='wikiview'>
  99. $crumbs
  100. <div id='wiki' class='wikipage'></div>
  101. <div id='attachments'>
  102. <h2>Attachments</h2>
  103. <div id="attachlist"></div>
  104. </div>
  105. </div>
  106. <button class='btn' id='outlinetoggle'>Toggle Outline</button>
  107. <div id='wikiinfo'>
  108. <div id='wikiinfotmpl'></div>
  109. <label>Page Outline</label>
  110. <ol id='outline'></ul>
  111. </div>
  112. <div id="attachment-form" class="popupForm" style="display:none">
  113. <form action="${ABSWEB}post-attachment.php" method="POST"
  114. id="upload-form" enctype="multipart/form-data" target="upload_target">
  115. <input type="hidden" name="object" value="wiki:$pi">
  116. <label for='attachments[]'>Select file(s) to be attached</label>
  117. <input name="attachments[]" class='btn multi' type="file">
  118. <iframe id="upload_target" name="upload_target" src="${ABSWEB}/mtrack.css">
  119. </iframe>
  120. <input type="submit" class="btn btn-primary"
  121. id="confirm-upload" value="Upload">
  122. <button class='btn' id="cancel-upload">Cancel</button>
  123. </form>
  124. </div>
  125. <script type="text/template" id='attach-template'>
  126. <a class='attachment' href='<%= ABSWEB %>attachment.php/<%- object %>/<%- cid %>/<%- filename %>'><%- filename %></a> (<%- size %>) added by <%- who %>
  127. <abbr class='timeinterval' title='<%- changedate %>'><%- changedate %></abbr>
  128. <button class='btn'>x</button>
  129. <% if (image && 0) { %>
  130. <br><a href='<%= ABSWEB %>attachment.php/<%- object %>/<%- cid %>/<%- filename %>'><img src='<%= ABSWEB %>attachment.php/<%- object %>/<%- cid %>/<%- filename %>' width='<%- width %>' height='<%- height %>' border='0'></a>
  131. <% } %>
  132. </script>
  133. <script type='text/template' id='wiki-info-template'>
  134. <label><%- id %></label><br>
  135. <% if (!latest) { %>
  136. <br>
  137. <div class='ui-state-highlight ui-corner-all'>
  138. <span class='ui-icon ui-icon-info'></span>
  139. This is not the most recent version of this page!<br>
  140. <a href="${ABSWEB}wiki.php/<%- id %>"><b>Show latest version</b></a>
  141. </div>
  142. <br>
  143. <% } %>
  144. <div id="wiki-buttons">
  145. <button id="save-wiki" class="btn btn-success hide-until-change"><%
  146. if (version) {
  147. %>Save<% } else { %>Create this page<% } %></button>
  148. <button class='btn btn-primary' id="edit-wiki"
  149. ><i class="icon-edit icon-white"></i> Edit this page</button>
  150. <button id="cancel-wiki"
  151. class="btn hide-until-change">Cancel</button>
  152. <button class="btn" id="attach-wiki"><i class="icon-upload"></i> Add Attachment</button>
  153. <button class='btn'
  154. onclick="document.location.href = '${ABSWEB}help.php'; return false;"
  155. ><i class='icon-book'></i> Help &amp; Page List</button>
  156. <div class='alert alert-danger' id='wiki-error' style="display:none">
  157. <a class='close' data-dismiss='alert'>&times;</a>
  158. <span id="wiki-error-text">Whoops</span>
  159. </div>
  160. <br>
  161. </div>
  162. <% if (who) { %>
  163. <div id="wiki-change-info">
  164. <img class='gravatar' src="${ABSWEB}avatar.php?u=<%- who %>&amp;s=24">
  165. <div class="wiki-change-desc">
  166. <%= changelog_html %>
  167. </div>
  168. <abbr class='timeinterval' title='<%- when %>'><%- when %></abbr>
  169. by <%- who %><br>
  170. <a href="${ABSWEB}log.php/default/wiki/<%- filename %>">Page History</a>
  171. </div>
  172. <% } %>
  173. </script>
  174. <script>
  175. $(document).ready(function() {
  176. var WIKIPAGE = $J;
  177. // Fake up a property so we can tell if we are looking at a historical
  178. // version of the wiki page
  179. WIKIPAGE.latest = $latest;
  180. var attachments = $ATTACH;
  181. WIKI = new MTrackWiki(WIKIPAGE);
  182. WIKI.getAttachments().reset(attachments);
  183. var editable = $edit;
  184. var editor = null;
  185. var attach_view = null;
  186. var wasNew = WIKI.isNew();
  187. $('#outlinetoggle').click(function () {
  188. $('#wikiinfo').toggle();
  189. });
  190. function update_attachments() {
  191. if (WIKI.getAttachments().length) {
  192. $('#attachments').show();
  193. } else {
  194. $('#attachments').hide();
  195. }
  196. }
  197. function save() {
  198. var overlay = $('<div class="overlay"><div class="progress progress-striped progress-success active"><div class="bar" style="width: 100%"></div></div></div>');
  199. overlay.appendTo('body').fadeIn('fast', function () {
  200. WIKI.save(WIKI.toJSON(), {
  201. success: function(model, response) {
  202. $('button.hide-until-change').fadeOut('fast');
  203. overlay.fadeOut('fast', function () {
  204. WIKIPAGE = WIKI.toJSON();
  205. WIKI.unset('comment', {silent: true});
  206. if (wasNew || !WIKI.get('content') ||
  207. WIKI.get('content').length == 0) {
  208. // created/deleted, easier just to reload
  209. window.onbeforeunload = null;
  210. window.location.href = window.location.href;
  211. return;
  212. }
  213. editor.render();
  214. overlay.remove();
  215. WIKI.changed = false;
  216. attach_view.editable = true;
  217. attach_view.render();
  218. mtrack_wiki_outline($('#wiki'), $('#outline'));
  219. });
  220. },
  221. error: function(model, response) {
  222. var err;
  223. var conflict = null;
  224. if (!_.isObject(response)) {
  225. err = response;
  226. } else {
  227. err = response.statusText;
  228. try {
  229. var r = JSON.parse(response.responseText);
  230. err = r.message;
  231. if (r.code == 409) {
  232. conflict = r.extra;
  233. }
  234. } catch (e) {
  235. err = response.statusText;
  236. }
  237. }
  238. overlay.fadeOut('fast', function () {
  239. overlay.remove();
  240. if (conflict) {
  241. WIKI.set(conflict);
  242. }
  243. $('#wiki-error-text').text(err);
  244. $('#wiki-error').fadeIn('fast')
  245. });
  246. }
  247. });
  248. });
  249. }
  250. var comment_explanation =
  251. "\\n\\n> Please enter a reason for this change. Your changes will not be applied until you click Save. (this line will be automatically removed)\\n";
  252. WIKI.bind('change:comment', function () {
  253. var c = WIKI.get('comment').replace(comment_explanation, "");
  254. WIKI.set({comment: c},{silent: true});
  255. save();
  256. });
  257. function update_info() {
  258. update_attachments();
  259. if (WIKI.hasChanged()) {
  260. WIKI.changed = true;
  261. }
  262. $('#wikiinfotmpl').html(_.template(
  263. $('#wiki-info-template').html(), WIKI.toJSON()
  264. ));
  265. if (!editable) {
  266. $('#wiki-buttons').hide();
  267. }
  268. if (WIKI.changed) {
  269. $('button.hide-until-change').fadeIn('fast');
  270. }
  271. $('#wiki-error').click(function () {
  272. $(this).fadeOut('fast');
  273. return false;
  274. });
  275. $('#wikiinfotmpl .timeinterval').timeago();
  276. $('#edit-wiki').click(function () {
  277. editor.edit();
  278. return false;
  279. });
  280. $('#cancel-wiki').click(function () {
  281. $('#wiki-error').fadeOut('fast');
  282. WIKI.set(WIKIPAGE);
  283. WIKI.changed = false;
  284. $('button.hide-until-change').fadeOut('fast');
  285. });
  286. $('#save-wiki').click(function () {
  287. $('#wiki-error').fadeOut('fast');
  288. if (!WIKI.get('comment')) {
  289. WIKI.set({comment: comment_explanation}, {silent:true});
  290. }
  291. var reason = new MTrackWikiTextAreaView({
  292. model: WIKI,
  293. srcattr: 'comment',
  294. use_overlay: true,
  295. Caption: "Comment on this wiki edit",
  296. OKLabel: "Save Wiki Page",
  297. CancelLabel: "Return to Wiki Editor"
  298. });
  299. reason.edit();
  300. });
  301. mtrack_wiki_outline($('#wiki'), $('#outline'));
  302. }
  303. WIKI.bind('change', update_info);
  304. WIKI.bind('error', function (model, err) {
  305. $('#wiki-error-text').text(err);
  306. $('#wiki-error').fadeIn('fast');
  307. });
  308. WIKI.getAttachments().bind('all', update_attachments);
  309. editor = new MTrackWikiTextAreaView({
  310. model: WIKI,
  311. wikiContext: "wiki:",
  312. use_overlay: true,
  313. srcattr: "content",
  314. readonly: !editable,
  315. renderedattr: "content_html",
  316. doubleclick: false,
  317. placeholder:
  318. WIKI.isNew() ?
  319. "This page doesn't exist; click the \"Edit this page\" button to start editing!"
  320. : "Saving this page now (with no content) will delete it",
  321. el: "#wiki"
  322. });
  323. /* hide the outline while editing, in case the user wants to search
  324. * for text in the page */
  325. editor.bind('editstart', function () {
  326. $('#wikiinfo').hide();
  327. });
  328. editor.bind('editend', function () {
  329. $('#wikiinfo').show();
  330. });
  331. update_info();
  332. attach_view = new MTrackTicketAttachmentsView({
  333. model: WIKI,
  334. collection: WIKI.getAttachments(),
  335. type: 'wiki',
  336. button: '#attach-wiki',
  337. editable: editable && !WIKI.isNew(),
  338. el: '#attachlist'
  339. });
  340. window.onbeforeunload = function() {
  341. if (WIKI.changed) {
  342. return "You haven't saved your changes!";
  343. }
  344. };
  345. });
  346. </script>
  347. HTML;
  348. mtrack_foot();