OSDN Git Service

BugTrack/2420 AutoTicketLink
authorumorigu <umorigu@gmail.com>
Wed, 1 Mar 2017 21:55:02 +0000 (06:55 +0900)
committerumorigu <umorigu@gmail.com>
Sat, 27 May 2017 20:43:20 +0000 (05:43 +0900)
lib/html.php
pukiwiki.ini.php
skin/main.js [new file with mode: 0644]
skin/pukiwiki.skin.php

index 33b43bd..7f085c8 100644 (file)
@@ -186,12 +186,47 @@ function catbody($title, $page, $body)
                }
        }
 
+       // Embed Scripting data
+       $html_scripting_data = get_html_scripting_data();
+
        // Compat: 'HTML convert time' without time about MenuBar and skin
        $taketime = elapsedtime();
 
        require(SKIN_FILE);
 }
 
+/**
+ * Get data used by JavaScript modules
+ */
+function get_html_scripting_data()
+{
+       global $ticket_link_sites;
+       if (!isset($ticket_link_sites) || !is_array($ticket_link_sites)) {
+               return '';
+       }
+       // Require: PHP 5.4+
+       if (!defined('JSON_UNESCAPED_UNICODE')) {
+               return '';
+       };
+       $text = '';
+       foreach ($ticket_link_sites as $s) {
+               if (!preg_match('/^([a-zA-Z0-9]+)([\.\-][a-zA-Z0-9]+)*$/', $s['key'])) {
+                       continue;
+               }
+               $site_info_json = htmlsc(json_encode($s, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
+               $text .= <<<EOS
+  <span class="pukiwiki-ticketlink-site" data-site="$site_info_json"></span>
+EOS;
+               $text .= "\n";
+       }
+       $data = <<<EOS
+<div class="pukiwiki-ticketlink-def" style="display:none;">
+$text
+</div>
+EOS;
+       return $data;
+}
+
 // Show 'edit' form
 function edit_form($page, $postdata, $digest = FALSE, $b_template = TRUE)
 {
index 4dda53d..8f7cefb 100644 (file)
@@ -288,6 +288,31 @@ $edit_auth_pages = array(
 $search_auth = 0;
 
 /////////////////////////////////////////////////
+// AutoTicketLink
+$ticket_link_sites = array(
+/*
+       array(
+               'key' => 'phpbug',
+               'type' => 'redmine', // type: redmine, jira or git
+               'title' => 'PHP :: Bug #$1',
+               'base_url' => 'https://bugs.php.net/bug.php?id=',
+       ),
+       array(
+               'key' => 'asfjira',
+               'type' => 'jira',
+               'title' => 'ASF JIRA [$1]',
+               'base_url' => 'https://issues.apache.org/jira/browse/',
+       ),
+       array(
+               'key' => 'pukiwiki-commit',
+               'type' => 'git',
+               'title' => 'PukiWiki revision $1',
+               'base_url' => 'https://ja.osdn.net/projects/pukiwiki/scm/git/pukiwiki/commits/',
+       ),
+*/
+);
+
+/////////////////////////////////////////////////
 // $whatsnew: Max number of RecentChanges
 $maxshow = 60;
 
diff --git a/skin/main.js b/skin/main.js
new file mode 100644 (file)
index 0000000..5b5597a
--- /dev/null
@@ -0,0 +1,147 @@
+// PukiWiki - Yet another WikiWikiWeb clone.
+// main.js
+// Copyright
+//   2017 PukiWiki Development Team
+// License: GPL v2 or (at your option) any later version
+//
+// PukiWiki JavaScript client script
+if (window.addEventListener) {
+  window.addEventListener('DOMContentLoaded', function() {
+    if (!Array.prototype.indexOf || !document.createDocumentFragment) {
+      return;
+    }
+    var headReText = '([\\s\\b]|^)';
+    var tailReText = '\\b';
+    var _siteList = getSiteListFromBody();
+    function ticketToLink(keyText) {
+      var siteList = getSiteList();
+      for (var i = 0; i < siteList.length; i++) {
+        var site = siteList[i];
+        var m = keyText.match(site.re);
+        if (m) {
+          var title = site.title;
+          var ticketKey = m[3]
+          if (title) {
+            title = title.replace(/\$1/g, ticketKey);
+          }
+          return {
+            url: site.base_url + m[3],
+            title: title
+          };
+        }
+      }
+      return null;
+    }
+    function regexEscape(key) {
+      return key.replace(/[\-\.]/g, function (m) {
+        return '\\' + m;
+      });
+    }
+    function setupSites(siteList) {
+      for (var i = 0, length = siteList.length; i < length; i++) {
+        var site = siteList[i];
+        var reText = '';
+        switch (site.type) {
+          case 'jira':
+            reText = '(' + regexEscape(site.key) + '):' + '([A-Z][A-Z0-9_]+-\\d+)';
+            break;
+          case 'redmine':
+            reText = '(' + regexEscape(site.key) + '):' + '(\\d+)';
+            break;
+          default:
+            continue;
+        }
+        site.reText = reText;
+        site.re = new RegExp(headReText + reText + tailReText);
+      }
+    }
+    function getSiteList() {
+      return _siteList;
+    }
+    function getSiteListFromBody() {
+      var list = [];
+      var defRoot = document.querySelector('.pukiwiki-ticketlink-def');
+      var siteNodes = defRoot.querySelectorAll('.pukiwiki-ticketlink-site');
+      Array.prototype.forEach.call(siteNodes, function (e) {
+        var siteInfoText = e.dataset && e.dataset.site;
+        if (!siteInfoText) return;
+        var info = textToSiteInfo(siteInfoText);
+        if (info) {
+          list.push(info);
+        }
+      });
+      setupSites(list);
+      return list;
+    }
+    function textToSiteInfo(siteDef) {
+      if (!siteDef) return null;
+      var info = JSON.parse(siteDef);
+      if (info && info.key && info.type && info.base_url) {
+        return info;
+      }
+      return null;
+    }
+    function startsWith(s, searchString) {
+      if (String.prototype.startsWith) {
+        return s.startsWith(searchString);
+      }
+      return s.substr(0, searchString.length) === searchString;
+    }
+    function getRegex(list) {
+      var reText = '';
+      for (var i = 0, length = list.length; i < length; i++) {
+        if (reText.length > 0) {
+          reText += '|'
+        }
+        reText += list[i].reText;
+      }
+      return new RegExp(headReText + '(' + reText + ')' + tailReText);
+    }
+    function makeTicketLink(element) {
+      var siteList = getSiteList();
+      if (!siteList || siteList.length === 0) {
+        return;
+      }
+      var re = getRegex(siteList);
+      var f, m, text = element.nodeValue;
+      while (m = text.match(re)) {
+        // m[1]: head, m[2]: keyText
+        f || (f = document.createDocumentFragment());
+        if (m.index > 0 || m[1].length > 0) {
+          f.appendChild(document.createTextNode(text.substr(0, m.index) + m[1]));
+        }
+        var a = document.createElement('a');
+        a.textContent = m[2];
+        var linkInfo = ticketToLink(a.textContent);
+        a.href = linkInfo.url;
+        a.title = linkInfo.title;
+        f.appendChild(a);
+        text = text.substr(m.index + m[0].length);
+      }
+      if (f) {
+        text.length > 0 && f.appendChild(document.createTextNode(text));
+        element.parentNode.replaceChild(f, element)
+      }
+    }
+    var ignoreTags = ['A', 'INPUT', 'TEXTAREA', 'BUTTON',
+      'SCRIPT', 'FRAME', 'IFRAME'];
+    function walkElement(element) {
+      var e = element.firstChild;
+      while (e) {
+        if (e.nodeType == 3 && e.nodeValue &&
+            e.nodeValue.length > 5 && /\S/.test(e.nodeValue)) {
+          var next = e.nextSibling;
+          makeTicketLink(e);
+          e = next;
+        } else {
+          if (e.nodeType == 1 && ignoreTags.indexOf(e.tagName) == -1) {
+            walkElement(e);
+          }
+          e = e.nextSibling;
+        }
+      }
+    }
+    var target = document.getElementById('body');
+    walkElement(target);
+  });
+}
index 1c53681..0d49b43 100644 (file)
@@ -2,7 +2,7 @@
 // PukiWiki - Yet another WikiWikiWeb clone.
 // pukiwiki.skin.php
 // Copyright
-//   2002-2016 PukiWiki Development Team
+//   2002-2017 PukiWiki Development Team
 //   2001-2002 Originally written by yu-ji
 // License: GPL v2 or (at your option) any later version
 //
@@ -68,6 +68,7 @@ header('Content-Type: text/html; charset=' . CONTENT_CHARSET);
  <link rel="SHORTCUT ICON" href="<?php echo $image['favicon'] ?>" />
  <link rel="stylesheet" type="text/css" href="<?php echo SKIN_DIR ?>pukiwiki.css" />
  <link rel="alternate" type="application/rss+xml" title="RSS" href="<?php echo $link['rss'] ?>" /><?php // RSS auto-discovery ?>
+ <script type="text/javascript" src="skin/main.js" defer></script>
 
 <?php echo $head_tag ?>
 </head>
@@ -267,6 +268,7 @@ function _toolbar($key, $x = 20, $y = 20){
  <?php echo S_COPYRIGHT ?>.
  Powered by PHP <?php echo PHP_VERSION ?>. HTML convert time: <?php echo elapsedtime() ?> sec.
 </div>
+<?php echo $html_scripting_data ?>
 
 </body>
 </html>