From ba324ed814ec4875fb504209233f50b5cd20a260 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sat, 5 Jan 2019 17:48:27 +0000
Subject: [PATCH] Document the internal link syntax.

Next up: Testing it all I think!
---
 docs/06.5-Interwiki-Links.md        | 26 ++++++++
 modules/feature-interwiki-links.php |  2 +-
 modules/parser-parsedown.php        | 94 +++++++++++++++++++----------
 peppermint.guiconfig.json           |  6 +-
 4 files changed, 91 insertions(+), 37 deletions(-)
 create mode 100644 docs/06.5-Interwiki-Links.md

diff --git a/docs/06.5-Interwiki-Links.md b/docs/06.5-Interwiki-Links.md
new file mode 100644
index 0000000..9271d16
--- /dev/null
+++ b/docs/06.5-Interwiki-Links.md
@@ -0,0 +1,26 @@
+# Inter-wiki Links
+Pepperminty Wiki, as of v0.18, supports _inter-wiki links_. Such a link sends the user to another wiki elsewhere on the internet. This is configured via the `interwiki_index_location` setting, which should point to a CSV file (either as a file path or an absolute URL) that specifies the wikis that can be linked to. Here are some example inter-wiki links:
+
+```markdown
+ - [[another_wiki:Apples]]
+ - [[trees:Apple Trees]]
+ - In the [[history:The Great Rainforest|rainforest]], lots of trees can be found.
+ - .... Text text text [[any prefix here:page name|Display text]] text text text ....
+```
+
+Note that the automatic page name correction doesn't work with inter-wiki links, so you'll need to get the casing exactly right (try using the vertical-bar `|` display text feature if you want to the display text to use a different casing to the actual page name.
+
+The CSV file format is best explained with an example:
+
+```csv
+Name,Prefix,Root URL
+Another Wiki,another_wiki,https://wiki.example.com/?page=%s
+Tree Field Guide,trees,https://trees.bobsrockets.io/wiki/%s
+History Wiki,history,https://history.sanssatellites.co.uk/#%s
+Wiki Display Name,internal link prefix,url with %s in place of page name
+Apt Link,apt,apt://%s
+```
+
+The top line is a header and is discarded by _Pepperminty Wiki_ (so you can put anything you like here).
+
+The url can be any URI supported by the user's browser. The string `%s` will be replaced with the (url encoded) page name. To this end, you can use this feature for _any_ external service you like - it doesn't have to be a Pepperminty Wiki installation (or even a wiki at all)!
diff --git a/modules/feature-interwiki-links.php b/modules/feature-interwiki-links.php
index 0db8e71..b93da8e 100644
--- a/modules/feature-interwiki-links.php
+++ b/modules/feature-interwiki-links.php
@@ -96,7 +96,7 @@ function interwiki_get_pagename_url($interwiki_pagename) {
 	[$prefix, $pagename] = interwiki_pagename_parse($interwiki_pagename);
 	
 	return str_replace(
-		"{{page_name}}", rawurlencode($pagename),
+		"%s", rawurlencode($pagename),
 		$interwiki_def->root_url
 	);
 }
diff --git a/modules/parser-parsedown.php b/modules/parser-parsedown.php
index 647377b..2ecf178 100644
--- a/modules/parser-parsedown.php
+++ b/modules/parser-parsedown.php
@@ -446,67 +446,95 @@ class PeppermintParsedown extends ParsedownExtra
 	{
 		global $pageindex, $env;
 		
-		if(preg_match('/^\[\[([^\]]*)\]\]([^\s!?",;.()\[\]{}*=+\/]*)/u', $fragment["text"], $matches))
-		{
-			$linkPage = trim($matches[1]);
-			$display = $linkPage . trim($matches[2]);
+		if(preg_match('/^\[\[([^\]]*)\]\]([^\s!?",;.()\[\]{}*=+\/]*)/u', $fragment["text"], $matches)) {
+			// 1: Parse parameters out
+			// -------------------------------
+			$link_page = trim($matches[1]);
+			$display = $link_page . trim($matches[2]);
 			if(strpos($matches[1], "|") !== false || strpos($matches[1], "¦") !== false)
 			{
 				// We have a bar character
 				$parts = preg_split("/\\||¦/", $matches[1], 2);
-				$linkPage = trim($parts[0]); // The page to link to
+				$link_page = trim($parts[0]); // The page to link to
 				$display = trim($parts[1]); // The text to display
 			}
 			
-			$hashCode = "";
-			if(strpos($linkPage, "#") !== false)
+			
+			// 2: Parse the hash out
+			// -------------------------------
+			$hash_code = "";
+			if(strpos($link_page, "#") !== false)
 			{
 				// We want to link to a subsection of a page
-				$hashCode = substr($linkPage, strpos($linkPage, "#") + 1);
-				$linkPage = substr($linkPage, 0, strpos($linkPage, "#"));
+				$hash_code = substr($link_page, strpos($link_page, "#") + 1);
+				$link_page = substr($link_page, 0, strpos($link_page, "#"));
 				
-				// If $linkPage is empty then we want to link to the current page
-				if(strlen($linkPage) === 0)
-					$linkPage = $env->page;
-			}
-			
-			// If the page doesn't exist, check varying different
-			// capitalisations to see if it exists under some variant.
-			if(empty($pageindex->$linkPage))
-			{
-				if(!empty($pageindex->{ucfirst($linkPage)}))
-					$linkPage = ucfirst($linkPage);
-				else if(!empty($pageindex->{ucwords($linkPage)}))
-					$linkPage = ucwords($linkPage);
+				// If $link_page is empty then we want to link to the current page
+				if(strlen($link_page) === 0)
+					$link_page = $env->page;
 			}
 			
 			
-			// Construct the full url
-			$linkUrl = str_replace(
-				"%s", rawurlencode($linkPage),
-				$this->internalLinkBase
-			);
 			
-			if(strlen($hashCode) > 0)
-				$linkUrl .= "#$hashCode";
+			// 3: Page name auto-correction
+			// -------------------------------
+			$is_interwiki_link = module_exists("feature-interwiki-links") && is_interwiki_link($link_page);
+			if(!is_interwiki_link && empty($pageindex->$link_page)) {
+				// If the page doesn't exist, check varying different
+				// capitalisations to see if it exists under some variant.
+				if(!empty($pageindex->{ucfirst($link_page)}))
+					$link_page = ucfirst($link_page);
+				else if(!empty($pageindex->{ucwords($link_page)}))
+					$link_page = ucwords($link_page);
+			}
 			
+			
+			// 4: Construct the full url
+			// -------------------------------
+			$link_url = null;
+			// If it's an interwiki link, then handle it as such
+			if($is_interwiki_link)
+				$link_url = interwiki_get_pagename_url($link_page);
+			
+			// If it isn't (or it failed), then try it as a normal link instead
+			if(empty($link_url)) {
+				$link_url = str_replace(
+					"%s", rawurlencode($link_page),
+					$this->internalLinkBase
+				);
+				// We failed to handle it as an interwiki link, so we should 
+				// tell everyone that
+				$is_interwiki_link = false;
+			}
+			
+			if(strlen($hash_code) > 0)
+				$link_url .= "#$hash_code";
+			
+			// 6: Result encoding
+			// -------------------------------
 			$result = [
 				"extent" => strlen($matches[0]),
 				"element" => [
 					"name" => "a",
 					"text" => $display,
 					"attributes" => [
-						"href" => $linkUrl
+						"href" => $link_url
 					]
 				]
 			];
 			
-			if(empty($pageindex->{makepathsafe($linkPage)}))
-				$result["element"]["attributes"]["class"] = "redlink";
+			// Attach some useful classes based on how we handled it
+			$class_list = [];
+			// Interwiki links can never be redlinks
+			if(!$is_interwiki_link && empty($pageindex->{makepathsafe($link_page)}))
+				$class_list[] = "redlink";
+			if($is_interwiki_link)
+				$class_list[] = "interwiki_link";
+			
+			$result["element"]["attributes"]["class"] = implode(" ", $class_list);
 			
 			return $result;
 		}
-		return;
 	}
 	
 	/*
diff --git a/peppermint.guiconfig.json b/peppermint.guiconfig.json
index b92c0f9..18581a5 100644
--- a/peppermint.guiconfig.json
+++ b/peppermint.guiconfig.json
@@ -15,10 +15,10 @@
 	"admindisplaychar": { "type": "text", "description": "The string that is prepended before an admin's name on the nav bar. Defaults to a diamond shape (&#9670;).", "default": "&#9670;" },
 	"protectedpagechar": { "type": "text", "description": "The string that is prepended a page's name in the page title if it is protected. Defaults to a lock symbol. (&#128274;)", "default": "&#128274;" },
 	"editing": { "type": "checkbox", "description": "Whether editing is enabled.", "default": true},
-	"anonedits": { "type": "checkbox", "description": "Whether users who aren't logged in are allowed to edit your wiki.", "default": false},
-	"maxpagesize": { "type": "number", "description": "The maximum page size in characters.", "default": 135000},
+	"anonedits": { "type": "checkbox", "description": "Whether users who aren't logged in are allowed to edit your wiki.", "default": false },
+	"maxpagesize": { "type": "number", "description": "The maximum page size in characters.", "default": 135000 },
 	"parser": { "type": "text", "description": "The parser to use when rendering pages. Defaults to an extended version of parsedown (http://parsedown.org/)", "default": "parsedown" },
-	"interwiki_index_location": { "type": "url", "description": "The location to find the interwiki wiki definition file, which contains a list of wikis along with their names, prefixes, and root urls. May be a URL, or simply a file path - as it's passed to file_get_contents()." },
+	"interwiki_index_location": { "type": "url", "description": "The location to find the interwiki wiki definition file, which contains a list of wikis along with their names, prefixes, and root urls. May be a URL, or simply a file path - as it's passed to file_get_contents().", "default": null },
 	"clean_raw_html": { "type": "checkbox", "description": "Whether page sources should be cleaned of HTML before rendering. It is STRONGLY recommended that you keep this option turned on.", "default": true},
 	"enable_math_rendering": { "type": "checkbox", "description": "Whether to enable client side rendering of mathematical expressions with MathJax (https://www.mathjax.org/). Math expressions should be enclosed inside of dollar signs ($). Turn off if you don't use it.", "default": true},
 	"users": { "type": "usertable", "description": "An array of usernames and passwords - passwords should be hashed with password_hash() (the hash action can help here)", "default": {
