• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

Revisionbdd91ef0e3092b4b29f539571625a3e6f6b8d8d1 (tree)
Zeit2017-05-28 05:43:20
Autorumorigu <umorigu@gmai...>
Commiterumorigu

Log Message

BugTrack/2420 AutoTicketLink

Ändern Zusammenfassung

Diff

--- a/lib/html.php
+++ b/lib/html.php
@@ -186,12 +186,47 @@ 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+/**
199+ * Get data used by JavaScript modules
200+ */
201+function get_html_scripting_data()
202+{
203+ global $ticket_link_sites;
204+ if (!isset($ticket_link_sites) || !is_array($ticket_link_sites)) {
205+ return '';
206+ }
207+ // Require: PHP 5.4+
208+ if (!defined('JSON_UNESCAPED_UNICODE')) {
209+ return '';
210+ };
211+ $text = '';
212+ foreach ($ticket_link_sites as $s) {
213+ if (!preg_match('/^([a-zA-Z0-9]+)([\.\-][a-zA-Z0-9]+)*$/', $s['key'])) {
214+ continue;
215+ }
216+ $site_info_json = htmlsc(json_encode($s, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
217+ $text .= <<<EOS
218+ <span class="pukiwiki-ticketlink-site" data-site="$site_info_json"></span>
219+EOS;
220+ $text .= "\n";
221+ }
222+ $data = <<<EOS
223+<div class="pukiwiki-ticketlink-def" style="display:none;">
224+$text
225+</div>
226+EOS;
227+ return $data;
228+}
229+
195230 // Show 'edit' form
196231 function edit_form($page, $postdata, $digest = FALSE, $b_template = TRUE)
197232 {
--- a/pukiwiki.ini.php
+++ b/pukiwiki.ini.php
@@ -288,6 +288,31 @@ $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, jira or git
297+ 'title' => 'PHP :: Bug #$1',
298+ 'base_url' => 'https://bugs.php.net/bug.php?id=',
299+ ),
300+ array(
301+ 'key' => 'asfjira',
302+ 'type' => 'jira',
303+ 'title' => 'ASF JIRA [$1]',
304+ 'base_url' => 'https://issues.apache.org/jira/browse/',
305+ ),
306+ array(
307+ 'key' => 'pukiwiki-commit',
308+ 'type' => 'git',
309+ 'title' => 'PukiWiki revision $1',
310+ 'base_url' => 'https://ja.osdn.net/projects/pukiwiki/scm/git/pukiwiki/commits/',
311+ ),
312+*/
313+);
314+
315+/////////////////////////////////////////////////
291316 // $whatsnew: Max number of RecentChanges
292317 $maxshow = 60;
293318
--- /dev/null
+++ b/skin/main.js
@@ -0,0 +1,147 @@
1+// PukiWiki - Yet another WikiWikiWeb clone.
2+// main.js
3+// Copyright
4+// 2017 PukiWiki Development Team
5+// License: GPL v2 or (at your option) any later version
6+//
7+// PukiWiki JavaScript client script
8+if (window.addEventListener) {
9+ window.addEventListener('DOMContentLoaded', function() {
10+ if (!Array.prototype.indexOf || !document.createDocumentFragment) {
11+ return;
12+ }
13+ var headReText = '([\\s\\b]|^)';
14+ var tailReText = '\\b';
15+ var _siteList = getSiteListFromBody();
16+ function ticketToLink(keyText) {
17+ var siteList = getSiteList();
18+ for (var i = 0; i < siteList.length; i++) {
19+ var site = siteList[i];
20+ var m = keyText.match(site.re);
21+ if (m) {
22+ var title = site.title;
23+ var ticketKey = m[3]
24+ if (title) {
25+ title = title.replace(/\$1/g, ticketKey);
26+ }
27+ return {
28+ url: site.base_url + m[3],
29+ title: title
30+ };
31+ }
32+ }
33+ return null;
34+ }
35+ function regexEscape(key) {
36+ return key.replace(/[\-\.]/g, function (m) {
37+ return '\\' + m;
38+ });
39+ }
40+ function setupSites(siteList) {
41+ for (var i = 0, length = siteList.length; i < length; i++) {
42+ var site = siteList[i];
43+ var reText = '';
44+ switch (site.type) {
45+ case 'jira':
46+ reText = '(' + regexEscape(site.key) + '):' + '([A-Z][A-Z0-9_]+-\\d+)';
47+ break;
48+ case 'redmine':
49+ reText = '(' + regexEscape(site.key) + '):' + '(\\d+)';
50+ break;
51+ default:
52+ continue;
53+ }
54+ site.reText = reText;
55+ site.re = new RegExp(headReText + reText + tailReText);
56+ }
57+ }
58+ function getSiteList() {
59+ return _siteList;
60+ }
61+ function getSiteListFromBody() {
62+ var list = [];
63+ var defRoot = document.querySelector('.pukiwiki-ticketlink-def');
64+ var siteNodes = defRoot.querySelectorAll('.pukiwiki-ticketlink-site');
65+ Array.prototype.forEach.call(siteNodes, function (e) {
66+ var siteInfoText = e.dataset && e.dataset.site;
67+ if (!siteInfoText) return;
68+ var info = textToSiteInfo(siteInfoText);
69+ if (info) {
70+ list.push(info);
71+ }
72+ });
73+ setupSites(list);
74+ return list;
75+ }
76+ function textToSiteInfo(siteDef) {
77+ if (!siteDef) return null;
78+ var info = JSON.parse(siteDef);
79+ if (info && info.key && info.type && info.base_url) {
80+ return info;
81+ }
82+ return null;
83+ }
84+ function startsWith(s, searchString) {
85+ if (String.prototype.startsWith) {
86+ return s.startsWith(searchString);
87+ }
88+ return s.substr(0, searchString.length) === searchString;
89+ }
90+ function getRegex(list) {
91+ var reText = '';
92+ for (var i = 0, length = list.length; i < length; i++) {
93+ if (reText.length > 0) {
94+ reText += '|'
95+ }
96+ reText += list[i].reText;
97+ }
98+ return new RegExp(headReText + '(' + reText + ')' + tailReText);
99+ }
100+ function makeTicketLink(element) {
101+ var siteList = getSiteList();
102+ if (!siteList || siteList.length === 0) {
103+ return;
104+ }
105+ var re = getRegex(siteList);
106+ var f, m, text = element.nodeValue;
107+ while (m = text.match(re)) {
108+ // m[1]: head, m[2]: keyText
109+ f || (f = document.createDocumentFragment());
110+ if (m.index > 0 || m[1].length > 0) {
111+ f.appendChild(document.createTextNode(text.substr(0, m.index) + m[1]));
112+ }
113+ var a = document.createElement('a');
114+ a.textContent = m[2];
115+ var linkInfo = ticketToLink(a.textContent);
116+ a.href = linkInfo.url;
117+ a.title = linkInfo.title;
118+ f.appendChild(a);
119+ text = text.substr(m.index + m[0].length);
120+ }
121+ if (f) {
122+ text.length > 0 && f.appendChild(document.createTextNode(text));
123+ element.parentNode.replaceChild(f, element)
124+ }
125+ }
126+ var ignoreTags = ['A', 'INPUT', 'TEXTAREA', 'BUTTON',
127+ 'SCRIPT', 'FRAME', 'IFRAME'];
128+ function walkElement(element) {
129+ var e = element.firstChild;
130+ while (e) {
131+ if (e.nodeType == 3 && e.nodeValue &&
132+ e.nodeValue.length > 5 && /\S/.test(e.nodeValue)) {
133+ var next = e.nextSibling;
134+ makeTicketLink(e);
135+ e = next;
136+ } else {
137+ if (e.nodeType == 1 && ignoreTags.indexOf(e.tagName) == -1) {
138+ walkElement(e);
139+ }
140+ e = e.nextSibling;
141+ }
142+ }
143+ }
144+ var target = document.getElementById('body');
145+ walkElement(target);
146+ });
147+}
--- a/skin/pukiwiki.skin.php
+++ b/skin/pukiwiki.skin.php
@@ -2,7 +2,7 @@
22 // PukiWiki - Yet another WikiWikiWeb clone.
33 // pukiwiki.skin.php
44 // Copyright
5-// 2002-2016 PukiWiki Development Team
5+// 2002-2017 PukiWiki Development Team
66 // 2001-2002 Originally written by yu-ji
77 // License: GPL v2 or (at your option) any later version
88 //
@@ -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 type="text/javascript" src="skin/main.js" defer></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>