OSDN Git Service

BugTrack/2247 External link cushion page - Control Referer etc.
authorumorigu <umorigu@gmail.com>
Wed, 21 Mar 2018 17:14:44 +0000 (02:14 +0900)
committerumorigu <umorigu@gmail.com>
Wed, 21 Mar 2018 17:14:44 +0000 (02:14 +0900)
* New plugin: external_link.inc.php
* New config: $external_link_cushion_page, $external_link_cushion

You can set "internal domains", on these host, you don't need to
see cushion page.

en.lng.php
image/external-link.png [new file with mode: 0644]
ja.lng.php
lib/html.php
pukiwiki.ini.php
skin/main.js
skin/pukiwiki.css
skin/tdiary.css

index 3d6dfb4..2789c09 100644 (file)
@@ -446,3 +446,11 @@ $_loginform_messages = array(
        'login' => 'Log in',
        'invalid_username_or_password' => 'The username or password you entered is incorrect'
 );
+
+///////////////////////////////////////
+// external_link.inc.php
+$_external_link_messages = array(
+       'page_title' => 'External link: %s',
+       'desc' => 'The selected URL is not the contents of this site.',
+       'wait_n_seconds' => 'It will move to the page automatically after %s seconds.',
+);
diff --git a/image/external-link.png b/image/external-link.png
new file mode 100644 (file)
index 0000000..04f5be1
Binary files /dev/null and b/image/external-link.png differ
index e0fb800..dc5c310 100644 (file)
@@ -448,3 +448,11 @@ $_loginform_messages = array(
        'login' => 'ログイン',
        'invalid_username_or_password' => 'ユーザー名またはパスワードが違います'
 );
+
+///////////////////////////////////////
+// external_link.inc.php
+$_external_link_messages = array(
+       'page_title' => '外部リンク: %s',
+       'desc' => '選択されたURLは本サイトのコンテンツではありません。',
+       'wait_n_seconds' => '%s 秒後に自動的に移動します。',
+);
index a05455a..e448123 100644 (file)
@@ -219,6 +219,7 @@ function _decorate_Nth_word($matches)
 function get_html_scripting_data()
 {
        global $ticket_link_sites, $plugin;
+       global $external_link_cushion_page, $external_link_cushion;
        if (!isset($ticket_link_sites) || !is_array($ticket_link_sites)) {
                return '';
        }
@@ -261,11 +262,21 @@ EOS;
        $ticketlink_data = <<<EOS
 <input type="hidden" class="ticketlink-def" value="$h_ticket_link_sites" />
 EOS;
+       // External link cushion page
+       $external_link_cushion_data = '';
+       if ($external_link_cushion_page) {
+               $h_cushion = htmlsc(json_encode($external_link_cushion,
+                       JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
+               $external_link_cushion_data = <<<EOS
+<input type="hidden" class="external-link-cushion" value="$h_cushion" />
+EOS;
+       }
        $data = <<<EOS
 <div id="pukiwiki-site-properties" style="display:none;">
 $site_props
 $plugin_prop
 $ticketlink_data
+$external_link_cushion_data
 </div>
 EOS;
        return $data;
index 30dbf3d..288f24c 100644 (file)
@@ -314,6 +314,26 @@ $ticket_link_sites = array(
 );
 
 /////////////////////////////////////////////////
+// Show External Link Cushion Page
+// 0: Disabled
+// 1: Enabled
+$external_link_cushion_page = 0;
+$external_link_cushion = array(
+       // Wait N seconds before jumping to an external site
+       'wait_seconds' => 5,
+       // Internal site domain list
+       'internal_domains' => array(
+               'localhost',
+               // '*.example.com',
+       ),
+       // Don't show extenal link icons on these domains
+       'silent_external_domains' => array(
+               'pukiwiki.osdn.jp',
+               'pukiwiki.example.com',
+       ),
+);
+
+/////////////////////////////////////////////////
 // $whatsnew: Max number of RecentChanges
 $maxshow = 500;
 
index c6619c8..82344ef 100644 (file)
@@ -370,8 +370,105 @@ window.addEventListener && window.addEventListener('DOMContentLoaded', function(
       }
     });
   }
+  function convertExternalLinkToCushionPageLink() {
+    function domainQuote(domain) {
+      return domain.replace(/\./g, '\\.');
+    }
+    function domainsToRegex(domains) {
+      var regexList = [];
+      domains.forEach(function(domain) {
+        if (domain.substr(0, 2) === '*.') {
+          // Wildcard domain
+          var apex = domain.substr(2);
+          var r = new RegExp('((^.*\\.)|^)' + domainQuote(apex) + '$', 'i');
+          regexList.push(r);
+        } else {
+          // Normal domain
+          regexList.push(new RegExp('^' + domainQuote(domain) + '$', 'i'));
+        }
+      });
+      return regexList;
+    }
+    function domainMatch(domain, regexList) {
+      for (var i = 0, n = regexList.length; i < n; i++) {
+        if (regexList[i].test(domain)) {
+          return true;
+        }
+      }
+      return false;
+    }
+    function removeCushionPageLinks() {
+      var links = document.querySelectorAll('a.external-link');
+      forEach(links, function(link) {
+        var originalUrl = link.getAttribute('data-original-url');
+        if (originalUrl) {
+          link.setAttribute('href', originalUrl);
+        }
+      });
+    }
+    if (!document.querySelector || !JSON) return;
+    if (!Array || !Array.prototype || !Array.prototype.indexOf) return;
+    var extLinkDef = document.querySelector('#pukiwiki-site-properties .external-link-cushion');
+    if (!extLinkDef || !extLinkDef.value) return;
+    var extLinkInfo = JSON.parse(extLinkDef.value);
+    if (!extLinkInfo) return;
+    var refInternalDomains = extLinkInfo.internal_domains;
+    var silentExternalDomains = extLinkInfo.silent_external_domains;
+    if (!Array.isArray(refInternalDomains)) {
+      refInternalDomains = [];
+    }
+    var internalDomains = refInternalDomains.slice();
+    var location = document.location;
+    if (location.protocol === 'file:') {
+      removeCushionPageLinks();
+      return;
+    }
+    if (location.protocol !== 'http:' && location.protocol !== 'https:') return;
+    if (internalDomains.indexOf(location.hostname) < 0) {
+      internalDomains.push(location.hostname);
+    }
+    if (!Array.isArray(silentExternalDomains)) {
+      silentExternalDomains = [];
+    }
+    var propsE = document.querySelector('#pukiwiki-site-properties .site-props');
+    if (!propsE || !propsE.value) return;
+    var siteProps = JSON.parse(propsE.value);
+    var sitePathname = siteProps && siteProps.base_uri_pathname;
+    if (!sitePathname) return;
+    var internalDomainsR = domainsToRegex(internalDomains);
+    var silentExternalDomainsR = domainsToRegex(silentExternalDomains);
+    var links = document.querySelectorAll('a:not(.external-link):not(.internal-link)');
+    var classListEnabled = null;
+    forEach(links, function(link) {
+      if (classListEnabled === null) {
+        classListEnabled = link.classList && link.classList.add && true;
+      }
+      if (!classListEnabled) return;
+      var href = link.getAttribute('href');
+      var m = href.match(/^https?:\/\/([0-9a-zA-Z.-]+)(:\d+)?/);
+      if (m) {
+        var host = m[1];
+        if (domainMatch(host, internalDomainsR)) {
+          link.classList.add('internal-link');
+        } else {
+          if (domainMatch(host, silentExternalDomainsR) ||
+            link.innerText.replace(/\s+/g, '') === '') {
+            // Don't show extenal link icons on these domains
+            link.classList.add('external-link-silent');
+          }
+          link.classList.add('external-link');
+          link.setAttribute('title', href);
+          link.setAttribute('data-original-url', href);
+          link.setAttribute('href', sitePathname + '?cmd=external_link&url=' + encodeURIComponent(href));
+        }
+      } else {
+        link.classList.add('internal-link');
+      }
+    });
+  }
   setYourName();
   autoTicketLink();
   confirmEditFormLeaving();
   showPagePassage();
+  convertExternalLinkToCushionPageLink();
 });
index 3c332bd..58b2dca 100644 (file)
@@ -47,6 +47,11 @@ a:hover {
   text-decoration:underline;
 }
 
+a.external-link:not(.external-link-silent)::after {
+  content:url(../image/external-link.png);
+  margin:3px;
+}
+
 h1, h2 {
   font-family:verdana, arial, helvetica, Sans-Serif;
   color:inherit;
index 0ec60ed..34816d0 100644 (file)
@@ -30,6 +30,11 @@ img { border: 0 }
 /* --------------------- */
 /* PukiWiki original CSS */
 
+a.external-link:not(.external-link-silent)::after {
+  content:url(../image/external-link.png);
+  margin:3px;
+}
+
 pre {
   white-space:pre-wrap;
   word-wrap:break-word;