1<?php
2/**
3 * DokuWiki Plugin bible (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Allen Stetson <allen.stetson@gmail.com>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
15
16require_once DOKU_PLUGIN.'syntax.php';
17
18class syntax_plugin_bible extends DokuWiki_Syntax_Plugin {
19    # Required. Define plugin type (see https://www.dokuwiki.org/devel:syntax_plugins)
20    public function getType() {
21        return 'substition';
22    }
23
24	# Required. Define paragraph handling (see https://www.dokuwiki.org/devel:syntax_plugins)
25    public function getPType() {
26        return 'normal';
27    }
28
29	# Required. Define sort order to determine which plugin trumps which. This value was somewhat arbitrary
30	## And can be changed without really affecting the plugin. (see https://www.dokuwiki.org/devel:syntax_plugins)
31    public function getSort() {
32        return 275;
33    }
34
35	#Plugin Part 1 of 3. This is the actual plugin. This part defines how to invoke the plugin
36    public function connectTo($mode) {
37		# Define all 73 books of the Catholic Bible.
38		## There are 66 books in the Protestant Bible, so this list will need altering if you plan to use that.
39		$validBibleBooksArr = array("Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Josue", "Judges", "Ruth",
40		 "1 Kings", "2 Kings", "3 Kings", "4 Kings", "1 Paralipomenon", "2 Paralipomenon", "1 Esdras", "2 Esdras", "Tobias",
41		 "Judith", "Esther", "Job", "Psalms", "Proverbs", "Ecclesiastes", "Canticles", "Wisdom", "Ecclesiasticus",
42		 "Isaias", "Jeremias", "Lamentations", "Baruch", "Ezechiel", "Daniel", "Osee", "Joel", "Amos", "Abdias", "Jonas",
43		 "Micheas", "Nahum", "Habacuc", "Sophonias", "Aggeus", "Zacharias", "Malachias", "1 Machabees", "2 Machabees",
44	     "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians",
45		 "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus",
46		 "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Apocalypse");
47
48		# Now, for each of those books, register two regular expressions:
49		## One for "through verses" like chapter 2 verses 19 through 24 (Mark 2:19-24)
50		## One for just a single verse (Mark 2:19)
51		## The order of registry is important! Make sure the through-verse is first or else the single verse will gobble everything.
52		foreach ($validBibleBooksArr as $bibleBook){
53			$this->Lexer->addSpecialPattern($bibleBook . ' \d+:\d+-\d+',$mode,'plugin_bible');
54			$this->Lexer->addSpecialPattern($bibleBook . ' \d+:\d+',$mode,'plugin_bible');
55		}
56    }
57
58	#Plugin Part 2 of 3. This is the handler - the workhorse - which takes the matched result from part 1 and modifies it.
59    public function handle($match, $state, $pos, &$handler){
60        $data = array(); # Blank array
61		# Make sure that we're dealing with the match from our regexp in part 1, which is in the DOKU_LEXER_SPECIAL context.
62		switch ($state){
63			case DOKU_LEXER_SPECIAL :
64				# Okay awesome, lets process that regexp match. Call my custom function called _fetchBibleVerse().
65				$bibleLink = $this->_fetchBibleVerse($match);
66				# Modified match obtained! Now return that to Dokuwiki for collection in Part 3.
67				return array($bibleLink, $state, $pos);
68		}
69        return $data; # Upon failure, return that blank array
70    }
71
72	#Plugin part 3 of 3. This takes that result from part 2 and actually renders it to the page.
73    public function render($mode, &$renderer, $data) {
74        if($mode != 'xhtml') return false; # If mode is not html, like if it is metadata, just return.
75        $renderer->doc .= $data[0]; # Otherwise, fetch that $bibleLink (stored in $data[0]) and pass it to the dokuwiki renderer.
76        return true;
77    }
78
79	# This custom function takes a matched input value (like Mark 2:19-24 or Amos 1:1) and returns HTML code
80	## which turns that input into an <acronym> with the bible verse as the hover-text.
81	function _fetchBibleVerse($inputVerse) {
82		# Set up variables for later
83		$thisChapterName = "";
84		$thisChapterNum = "";
85		$thisVerse = "";
86		$throughVerse = "";
87		$matchingVerses = array();
88		$inputVerses = array();
89
90  		# Have a look at that input and try to determine chapter name, number, verse number, and through-verse if it exists.
91		## Groups are defined by parenthesis.  Group one is catching the Chapter Name, accounting for the fact that some chapters
92		## begin with a number.
93		## Group two is catching the chapter number
94		## Group three is catching the verse number
95		## Group 4 is required so that I can put a * after it, indicating that it may or may not be present
96		## Group 5 catches the "through verse" if it should exist.
97		$result = preg_match("/^.*?(\d*?\s*?\w+) (\d+):(\d+)(-(\d+))*$/", $inputVerse, $matches);
98		if ($result){
99		  $chapterName = trim($matches[1]);
100		  $thisChapterName = $chapterName;
101		  $thisChapterNum = $matches[2];
102		  $thisVerseNum = $matches[3];
103		  $throughVerse = $matches[5];
104
105		  # Define which bible text file matches the result
106		  ## Here is where you'll plug in a different version of the bible, should you desire.
107		  ## Make sure that the syntax of the text files matches that of the Douay Rhiems bible
108		  ## provided with this plugin.
109		  ## PLEASE NOTE -- The bible cannot be all in one text file or the memory limit imposed upon
110		  ## plugins by Dokuwiki will be reached, and the plugin will fail. You MUST separate by chapter.
111		  $filename = DOKU_PLUGIN . "bible/bible_douayRheims/" . $thisChapterName . ".txt";
112		  $filename = str_replace(' ','_',$filename); # The user entered spaces, but filenames use underscores.
113
114		  if(!file_exists($filename)){
115    		echo "ERROR, can't find matching bible file: " . $filename . "<br />\n";
116    		return;
117		  }
118
119		  # Check for a through verse (like, Mark 2:7-9), expand it if found
120		  if ($throughVerse != ""){
121			# Through Verse supplied
122			foreach (range($thisVerseNum, $throughVerse) as $verseNum){
123				# Push each valid verse defined by the range provided onto the inputVerses array
124				array_push($inputVerses, $thisChapterName . " " . $thisChapterNum . ":" . $verseNum);
125			}
126		  } else {
127		    # No through verse, just take the input
128			array_push($inputVerses, $inputVerse);
129		  }
130
131		  # Now for each of the verses supplied (multiples are from through-verses),
132		  ## look for a match in the bible file, collect it for return.
133		  foreach ($inputVerses as $thisInputVerse){
134			foreach (file($filename) as $linenum => $line){
135		  		# Find matching verse
136				## Here is where the syntax of the bible text files is important. Every line must be:
137				## "CHAPTER:VERSE. CONTENT"
138				$result = preg_match("/^.*?(\d+:\d+)\.(.*?)$/", $line, $matches);
139		    	if ($result){
140			  		# looks like a verse
141		      		$thisVerse = $matches[1];
142			  		$content = $matches[2];
143			  		$constructedVerse = $thisChapterName . " " . $thisVerse; # Form the verse into something similar to user input
144			  		$constructedVerse = strtolower($constructedVerse); # Force it to lower case for comparison
145			  		$lowInputVerse = strtolower($thisInputVerse); # Force user input to lower case for comparison
146			  		if($lowInputVerse == $constructedVerse){  # Do they match?
147						# This verse matches one of the verses that the user asked for! Push its contents into our matchingVerses array
148						array_push($matchingVerses, $content);
149			  		}
150				}
151				#Not a chapter or verse? Skip it.
152		  	}
153		  }
154		  $matchingVersesOutput = implode(" ", $matchingVerses); # Flatten our matching verses array into a string joined by spaces.
155		  $matchingVersesOutput = trim($matchingVersesOutput); # Make it look pretty by trimming off any leading or trailing whitespace
156
157		  # FINALLY, return all matching verses smooshed together as the "title" or hover-text to an <acronym>.
158		  return "<acronym title=\"" . $matchingVersesOutput . "\">" . $inputVerse . "</acronym>";
159		} else {
160			# Warn the user that some match is not a valid bible verse (Mark 445:9833)
161		  echo "Input does not appear to be a valid bible verse: " . $inputVerse . "<br />\n";
162		}
163	}
164}
165
166// vim:ts=4:sw=4:et:
167