• R/O
  • R/O (HTTP)
  • R/W (SSH)
  • R/W (HTTPS)

pukiwiki: コミット


コミットメタ情報

リビジョンb351d0f9660e1729f8d982e8902a940c1077aa94 (tree)
日時2017-03-12 19:08:53
作者umorigu <umorigu@gmai...>
コミッターumorigu

ログメッセージ

BugTrack/2420 AutoTicketLink

変更サマリ

差分

--- a/lib/html.php
+++ b/lib/html.php
@@ -186,12 +186,41 @@ function catbody($title, $page, $body)
186186 }
187187 }
188188
189+ // Embed Scripting data
190+ $html_scripting_data = get_html_scripting_data();
191+
189192 // Compat: 'HTML convert time' without time about MenuBar and skin
190193 $taketime = elapsedtime();
191194
192195 require(SKIN_FILE);
193196 }
194197
198+function get_html_scripting_data()
199+{
200+ global $ticket_link_sites;
201+ if (!isset($ticket_link_sites) || !is_array($ticket_link_sites)) {
202+ return '';
203+ }
204+ $text = '';
205+ foreach ($ticket_link_sites as $s) {
206+ $key = htmlsc($s['key']);
207+ $type = htmlsc($s['type']);
208+ $name = htmlsc($s['name']);
209+ $detail = htmlsc($s['detail']);
210+ $base_url = htmlsc($s['base_url']);
211+ $text .= <<<EOS
212+ <span class="pukiwiki-ticketlink-site" data-site="key=$key,type=$type,detail=$detail,name=$name,baseUrl=$base_url" />
213+EOS;
214+ $text .= "\n";
215+ }
216+ $data = <<<EOS
217+<div class="pukiwiki-ticketlink-def" style="display:none;">
218+$text
219+</div>
220+EOS;
221+ return $data;
222+}
223+
195224 // Show 'edit' form
196225 function edit_form($page, $postdata, $digest = FALSE, $b_template = TRUE)
197226 {
--- a/pukiwiki.ini.php
+++ b/pukiwiki.ini.php
@@ -288,6 +288,19 @@ $edit_auth_pages = array(
288288 $search_auth = 0;
289289
290290 /////////////////////////////////////////////////
291+// AutoTicketLink
292+$ticket_link_sites = array(
293+/*
294+ array(
295+ 'key' => 'phpbug',
296+ 'type' => 'redmine', // type: redmine or jira
297+ 'name' => 'PHP :: Bug #$1',
298+ 'base_url' => 'https://bugs.php.net/bug.php?id=',
299+ ),
300+ */
301+);
302+
303+/////////////////////////////////////////////////
291304 // $whatsnew: Max number of RecentChanges
292305 $maxshow = 60;
293306
--- a/skin/pukiwiki.skin.php
+++ b/skin/pukiwiki.skin.php
@@ -68,6 +68,7 @@ header('Content-Type: text/html; charset=' . CONTENT_CHARSET);
6868 <link rel="SHORTCUT ICON" href="<?php echo $image['favicon'] ?>" />
6969 <link rel="stylesheet" type="text/css" href="<?php echo SKIN_DIR ?>pukiwiki.css" />
7070 <link rel="alternate" type="application/rss+xml" title="RSS" href="<?php echo $link['rss'] ?>" /><?php // RSS auto-discovery ?>
71+ <script src="skin/ticketlink.js"></script>
7172
7273 <?php echo $head_tag ?>
7374 </head>
@@ -267,6 +268,7 @@ function _toolbar($key, $x = 20, $y = 20){
267268 <?php echo S_COPYRIGHT ?>.
268269 Powered by PHP <?php echo PHP_VERSION ?>. HTML convert time: <?php echo elapsedtime() ?> sec.
269270 </div>
271+<?php echo $html_scripting_data ?>
270272
271273 </body>
272274 </html>
--- /dev/null
+++ b/skin/ticketlink.js
@@ -0,0 +1,140 @@
1+// ticketlink.js
2+if (window.addEventListener) {
3+ window.addEventListener('DOMContentLoaded', function() {
4+ if (!Array.prototype.indexOf || !document.createDocumentFragment) {
5+ return;
6+ }
7+ var headReText = '([\\s\\b]|^)';
8+ var tailReText = '\\b';
9+ var _siteList = getSiteListFromBody();
10+ function ticketToUrl(keyText) {
11+ var siteList = getSiteList();
12+ for (var i = 0; i < siteList.length; i++) {
13+ var site = siteList[i];
14+ var m = keyText.match(site.re);
15+ if (m) {
16+ return site.baseUrl + m[3];
17+ }
18+ }
19+ return '';
20+ }
21+ function setupSites(siteList) {
22+ for (var i = 0, length = siteList.length; i < length; i++) {
23+ var site = siteList[i];
24+ var reText = '';
25+ switch (site.type) {
26+ case 'jira':
27+ reText = '(' + site.key + '):' + '([A-Z][A-Z0-9]+-\\d+)';
28+ break;
29+ case 'redmine':
30+ reText = '(' + site.key + '):' + '(\\d+)';
31+ break;
32+ default:
33+ continue;
34+ }
35+ site.reText = reText;
36+ site.re = new RegExp(headReText + reText + tailReText);
37+ }
38+ }
39+ function getSiteList() {
40+ return _siteList;
41+ }
42+ function getSiteListFromBody() {
43+ var list = [];
44+ var defRoot = document.querySelector('.pukiwiki-ticketlink-def');
45+ var siteNodes = defRoot.querySelectorAll('.pukiwiki-ticketlink-site');
46+ Array.prototype.forEach.call(siteNodes, function (e) {
47+ var siteInfoText = e.dataset && e.dataset.site;
48+ if (!siteInfoText) return;
49+ var info = textToSiteInfo(siteInfoText);
50+ if (info) {
51+ list.push(info);
52+ }
53+ });
54+ setupSites(list);
55+ return list;
56+ }
57+ function textToSiteInfo(siteDef) {
58+ if (!siteDef) return null;
59+ var sp = siteDef.split(',');
60+ var info = {};
61+ sp.forEach(function(s) {
62+ if (startsWith(s, 'key=')) {
63+ info.key = s.substr('key='.length);
64+ } else if (startsWith(s, 'type=')) {
65+ info.type = s.substr('type='.length);
66+ } else if (startsWith(s, 'name=')) {
67+ info.name = s.substr('name='.length);
68+ } else if (startsWith(s, 'detail=')) {
69+ info.detail = s.substr('detail='.length);
70+ } else if (startsWith(s, 'baseUrl=')) {
71+ info.baseUrl = s.substr('baseUrl='.length);
72+ }
73+ });
74+ if (info.key && info.type && info.name && info.baseUrl) {
75+ return info;
76+ }
77+ return null;
78+ }
79+ function startsWith(s, searchString) {
80+ if (String.prototype.startsWith) {
81+ return s.startsWith(searchString);
82+ }
83+ return s.substr(0, searchString.length) === searchString;
84+ }
85+ function getRegex(list) {
86+ var reText = '';
87+ for (var i = 0, length = list.length; i < length; i++) {
88+ if (reText.length > 0) {
89+ reText += '|'
90+ }
91+ reText += list[i].reText;
92+ }
93+ return new RegExp(headReText + '(' + reText + ')' + tailReText);
94+ }
95+ function makeTicketLink(element) {
96+ var siteList = getSiteList();
97+ if (!siteList || siteList.length === 0) {
98+ return;
99+ }
100+ var re = getRegex(siteList);
101+ var f, m, text = element.nodeValue;
102+ while (m = text.match(re)) {
103+ // m[1]: head, m[2]: keyText
104+ f || (f = document.createDocumentFragment());
105+ if (m.index > 0 || m[1].length > 0) {
106+ f.appendChild(document.createTextNode(text.substr(0, m.index) + m[1]));
107+ }
108+ var a = document.createElement('a');
109+ a.textContent = m[2];
110+ a.href = ticketToUrl(a.textContent);
111+ f.appendChild(a);
112+ text = text.substr(m.index + m[0].length);
113+ }
114+ if (f) {
115+ text.length > 0 && f.appendChild(document.createTextNode(text));
116+ element.parentNode.replaceChild(f, element)
117+ }
118+ }
119+ var ignoreTags = ['A', 'INPUT', 'TEXTAREA', 'BUTTON',
120+ 'SCRIPT', 'FRAME', 'IFRAME'];
121+ function walkElement(element) {
122+ var e = element.firstChild;
123+ while (e) {
124+ if (e.nodeType == 3 && e.nodeValue &&
125+ e.nodeValue.length > 5 && /\S/.test(e.nodeValue)) {
126+ var next = e.nextSibling;
127+ makeTicketLink(e);
128+ e = next;
129+ } else {
130+ if (e.nodeType == 1 && ignoreTags.indexOf(e.tagName) == -1) {
131+ walkElement(e);
132+ }
133+ e = e.nextSibling;
134+ }
135+ }
136+ }
137+ var target = document.getElementById('body');
138+ walkElement(target);
139+ });
140+}
旧リポジトリブラウザで表示