1<?php
2/**
3 * Prolog plug-in : Rule-based System for Groupware.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Paweł Kupka <pawel.kupka@gmail.com>
7 */
8
9// must be run within Dokuwiki
10$webroot = reset(split('lib',dirname(__FILE__)));
11if(!defined('DOKU_INC')) define('DOKU_INC', $webroot);
12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins');
13if(!defined('DOKU_PAGES')) define('DOKU_PAGES',DOKU_INC.'data/pages');
14if(!defined('DOKU_TMP')) define('DOKU_TMP',DOKU_INC.'data/tmp');
15//require_once (DOKU_INC.'inc/pageutils.php');
16//require_once (DOKU_INC.'inc/utf8.php');
17require_once('prolog_tag.php');
18
19class AttributeInclude extends PrologTag {
20
21	var $name = 'include';
22
23	/**
24	 * Constructor
25	 */
26	function AttributeInclude()
27	{
28		$this->setAttributePattern($this->name);
29	}
30
31	/**
32	 * Gets the list of files to be included
33	 * @param string $includeValue the attribute include value
34	 * @param string $fileID the file ID in the DokuWiki notation
35	 * @param array $filesList list of files
36	 * @return array $filesList list of files
37	 */
38	function getFilesList($includeValue = null, $fileID = null, &$filesList = array())
39	{
40		if(empty($fileID))
41			$fileID = getID();
42
43		$includedIDs = $this->sliceIncludeValue($includeValue);
44		$joinedIDs = array();
45		foreach($includedIDs as $includeID)
46			array_push($joinedIDs, $this->joinID($fileID,$includeID));
47
48		foreach($joinedIDs as $ID)
49		{
50			$files = $this->scan($ID);
51			foreach($files as $file)
52			{
53				if(in_array($file, $filesList) || $file == wikiFN(getID()))
54					continue;
55				else
56				{
57					array_push($filesList, $file);
58					$tags = $this->getEntryTags($file);
59					foreach($tags as $tag)
60						$this->getFilesList($this->getAttributeValue($tag), $this->abs2ID($file), $filesList);
61				}
62			}
63		}
64		return $filesList;
65	}
66
67	/**
68	 * Gets the files paths from the place designated by the ID
69	 * @param string $ID ID of the page or namespace
70	 * @return array $foundFiles list of files paths
71	 */
72	function scan($ID = null)
73	{
74		$path = $this->ID2abs($ID);
75
76		if(!$this->isReadable($path))
77			return array();
78
79		if($this->getScanType($ID) == 'SINGLE_PAGE')
80			return array($path);
81		elseif($this->getScanType($ID) == 'RECURSIVE_DIRECTORY')
82			return  $this->scanDirectory($path, true);
83		elseif($this->getScanType($ID) == 'SINGLE_DIRECTORY')
84			return $this->scanDirectory($path);
85		else
86			return array();
87	}
88
89	/**
90	 * cuts the include attribute value
91	 * @param string $value attribute include value
92	 * @return array $exValues array of ID values from the include attribute
93	 */
94	function sliceIncludeValue($value = null)
95	{
96		$parts = array();
97		$exValues = explode(',', $value);
98		foreach($exValues as $part) {
99			$part = trim($part);
100			if(!empty($part))
101				array_push($parts, $part);
102		}
103		return $parts;
104	}
105
106	/**
107	 * Checks if the file/directory exists and is readable
108	 * @param $file file path
109	 * @return bool
110	 */
111	function isReadable($file = null)
112	{
113		if((is_file($file) || is_dir($file)) && file_exists($file) && is_readable($file))
114			return true;
115		else
116			return false;
117	}
118
119	/**
120	 * Returns the scan type based on the given ID
121	 * @param string $ID
122	 * @return string scan type
123	 */
124	function getScanType($ID = null)
125	{
126		$IDparts = explode(':', $ID);
127		if(preg_match('/[\w]+/', end($IDparts)))
128			return 'SINGLE_PAGE';
129		elseif(preg_match('/\*\*/', end($IDparts)))
130			return 'RECURSIVE_DIRECTORY';
131		elseif(preg_match('/\*/', end($IDparts)))
132			return 'SINGLE_DIRECTORY';
133		else
134			return 'NONE';
135	}
136
137	/**
138	 * Reads the code from all Prolog tags in the given text files
139	 * @param array $files list of files path
140	 * @return string $readCode prolog source code read from the prolog tags
141	 */
142	function readCode($files = array())
143	{
144		$readCode = '';
145		foreach($files as $file)
146		{
147			$fileContent = file_get_contents($file);
148			$splitedByEntry = preg_split($this->entryTagPattern,$fileContent);
149			array_shift($splitedByEntry);
150			foreach($splitedByEntry as $splited)
151			{
152				if(!preg_match($this->exitTagPattern,$splited))
153					continue;
154				$splitedByExit = preg_split($this->exitTagPattern, $splited,-1,PREG_SPLIT_NO_EMPTY);
155				$readCode .= $splitedByExit[0];
156			}
157		}
158		return $readCode;
159	}
160
161	/**
162	 * Joins two IDs into one
163	 * @param string $headID base namespace
164	 * @param string $tailID additional namespace
165	 * @return string $joinedID
166	 */
167	function joinID($headID = null, $tailID = null) {
168		$headNS = getNS($headID);
169		$tailIDParts = explode(':',$tailID);
170		$joinedIDParts = explode(':',$headNS);
171
172		if(empty($headNS))
173			return $tailID;
174
175		foreach($tailIDParts as $tailIDPart)
176		{
177			if(empty($tailIDPart))
178				return implode(':',$tailIDParts);
179
180			if($tailIDPart == '.')
181			{
182				array_shift($tailIDParts);
183				$joinedIDParts = $tailIDParts;
184				break;
185			}
186			elseif($tailIDPart == '..')
187				array_pop($joinedIDParts);
188			else
189				array_push($joinedIDParts, $tailIDPart);
190		}
191		$joinedID = implode(':',$joinedIDParts);
192
193		return $joinedID;
194	}
195
196	/**
197	 * Converts ID into the absolute path to the file/directory
198	 * @param string $ID page/namespace ID
199	 * @return string $abs absolute path to file/directory
200	 */
201	function ID2abs($ID = null) {
202		if(noNS($ID) == '*' || noNS($ID) == '**')
203			$abs = DOKU_PAGES.'/'.str_replace(':','/',getNS($ID));
204		else
205			$abs = DOKU_PAGES.'/'.str_replace(':','/',$ID.'.txt');
206
207		return str_replace('\\', '/', $abs);
208	}
209
210	/**
211	 * Converts the file/directory absolute path into ID
212	 * @param string $abs absolute path to file/directory
213	 * @return string $ID page/namespace ID
214	 */
215	function abs2ID($abs = null) {
216		$abs = str_replace(realpath(DOKU_PAGES), '', realpath($abs));
217
218		if(substr($abs,-4) == '.txt')
219			$abs = substr($abs, 0, -4);
220		else
221			$abs .= '/*';
222
223		$ID = preg_replace('/[\/\\\]/',':',$abs);
224
225		if(substr($ID,0,1) == ':')
226			$ID = substr($ID,1);
227
228		return $ID;
229	}
230
231	/**
232	 * Scans the given directory in search of text files
233	 * @param string $directory path to the directory
234	 * @param bool $recursive if set to "true", it also scans all sub-directories, if set to "false", only the main directory (parameter value)
235	 * @param array $file_list list of found text files
236	 * @return array $file_list list of found text files
237	 */
238	function scanDirectory($directory = null, $recursive = false, &$file_list = array())
239	{
240		if(substr($directory,-1) == '/' || substr($directory,-1) == '\\')
241			$directory = substr($directory,0,-1);
242
243		$directory_list = opendir($directory);
244
245		while (false !== ($file = readdir($directory_list)))
246		{
247			if($file != '.' && $file != '..')
248			{
249				$path = $directory.'/'.$file;
250				if(is_readable($path))
251				{
252					$subdirectories = explode('/',$path);
253					if(is_dir($path) && $recursive)
254							$this->scanDirectory($path, $recursive, $file_list);
255					elseif(is_file($path))
256					{
257						$extension = end(explode('.',end($subdirectories)));
258						if($extension == 'txt')
259							array_push($file_list,$path);
260					}
261				}
262			}
263		}
264		closedir($directory_list);
265		return $file_list;
266	}
267}
268?>