1<?php
2/**
3 * DokuWiki Plugin directorylist (Syntax Component)
4 *
5 * @author  alexwenzel <alexander.wenzel.berlin@gmail.com>
6 */
7
8// must be run within Dokuwiki
9if (!defined('DOKU_INC')) die();
10
11class Syntax_Plugin_Directorylist_Directorylist extends DokuWiki_Syntax_Plugin
12{
13	/**
14	 * Doku_Renderer
15	 * @var Doku_Renderer
16	 */
17	private $renderer;
18
19	/**
20	 * @return string Syntax mode type
21	 */
22	public function getType()
23	{
24		return 'disabled';
25	}
26
27	/**
28	 * @return string Paragraph type
29	 */
30	public function getPType()
31	{
32		return 'stack';
33	}
34
35	/**
36	 * @return int Sort order - Low numbers go before high numbers
37	 */
38	public function getSort()
39	{
40		return 10;
41	}
42
43	/**
44	 * Connect lookup pattern to lexer
45	 * @param  string $mode Parser mode
46	 * @return void
47	 */
48	public function connectTo($mode)
49	{
50		$this->Lexer->addSpecialPattern('<directorylist.+?>',$mode,'plugin_directorylist_directorylist');
51	}
52
53	/**
54	 * Handle matches of the directorylist syntax
55	 * @param  string $match The match of the syntax
56	 * @param  int    $state The state of the handler
57	 * @param  int    $pos The position in the document
58	 * @param  Doku_Handler    $handler The handler
59	 * @return array Data for the renderer
60	 */
61	public function handle($match, $state, $pos, Doku_Handler $handler)
62	{
63		// default value
64		$parameters = array();
65
66		// regex
67		preg_match_all('#(\w+)\s*=\s*"(.*?)"#', $match, $return);
68
69		if (is_array($return) && isset($return[1]) && is_array($return[1]))
70		foreach($return[1] as $index => $name)
71		{
72			$parameters[$name] = $return[2][$index];
73		}
74
75		return $parameters;
76	}
77
78	/**
79	 * Render xhtml output or metadata
80	 * @param  string         $mode      Renderer mode (supported modes: xhtml)
81	 * @param  Doku_Renderer  $renderer  The renderer
82	 * @param  array          $data      The data from the handler() function
83	 * @return bool 					If rendering was successful.
84	 */
85	public function render($mode, Doku_Renderer $renderer, $data)
86	{
87		// do not render if not in xhtml mode
88		if($mode != 'xhtml') return false;
89
90		// safe reference
91		$this->renderer = $renderer;
92
93		try {
94
95			// check and set default: recursive argument
96			if ( ! isset($data['recursive']) || empty($data['recursive']) )
97				$data['recursive'] = true;
98
99			// check and set default: ignore argument
100			if ( ! isset($data['ignore']) || empty($data['ignore']) )
101				$data['ignore'] = '';
102
103			// check and set default: fileorder argument
104			if ( ! isset($data['fileorder']) || empty($data['fileorder']) )
105				$data['fileorder'] = 'asc';
106
107			// check and set default: type argument
108			if ( ! isset($data['type']) || empty($data['type']) )
109				$data['type'] = 'download';
110
111			// check: path argument
112			if ( ! isset($data['path']) || empty($data['path']) )
113				throw new Exception("A path is missing!");
114
115			// get all directories and files
116			require_once "SplFileArray.php";
117			$fs = new SplFileArray($data['path'], (bool)$data['recursive'], $data['fileorder']);
118
119			// get files
120			$files = $fs->get();
121
122
123			// start walking down
124			$this->renderer->doc .= '<ul class="directorylist">';
125			$this->walkDirArray( $files , $data);
126			$this->renderer->doc .= '</ul>';
127
128		} catch (Exception $e) {
129
130			$this->renderer->doc .= '<strong>directorylist error:</strong> ';
131			$this->renderer->doc .= $e->getMessage();
132		}
133
134		// finished
135		return true;
136	}
137
138	/**
139	 * Walks down the directory array
140	 * @param  array         $dirArray
141	 * @return void
142	 */
143	private function walkDirArray(array $dirArray, array $data)
144	{
145		foreach ($dirArray as $key => $value) {
146
147			// check if is ignored
148			if ( ! $this->isIgnored($data['ignored'], $key) ) {
149
150				// check if is directory
151				if ( is_array($value) ) {
152
153					// this is the start of a new sub directory
154					$this->renderer->doc .= '<li class="folder">'.$key.'<ul>';
155					$this->walkDirArray($value, $data);
156					$this->renderer->doc .= '</ul></li>';
157				}
158				else if ( $value instanceof SplFileInfo ) {
159
160					// not directory, but file
161					$this->renderer->doc .= '<li class="file">'.$this->formatLink($value,$data).$this->formatBytes($value).'</li>';
162				}
163			}
164		}
165	}
166
167	/**
168	 * Returns, whether the file is ignored or not
169	 * @see    http://www.php.net/manual/en/function.fnmatch.php
170	 * @param  string $ignorePattern
171	 * @param  string $name
172	 * @return bool
173	 */
174	private function isIgnored($ignorePattern, $name)
175	{
176		// not ignored if emtpy
177		if ( empty($ignorePattern) )
178			return false;
179
180		// explode the ignore argument
181		$patternArray = explode(',', $ignorePattern);
182
183		// iterate through all given patterns
184		foreach ($patternArray as $pattern) {
185
186			// is there a match
187			if ( fnmatch($pattern, $name) )
188				return true;
189		}
190
191		return false;
192	}
193
194	/**
195	 * Returns the filesize for a given file
196	 * @param  SplFileInfo $file
197	 * @param  integer     $precision
198	 * @return string
199	 */
200	private function formatBytes(SplFileInfo $file, $precision = 2)
201	{
202		$base = log($file->getSize()) / log(1024);
203		$suffixes = array('B', 'kB', 'MB', 'GB', 'TB');
204
205		$return = round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
206
207		return '<span class="size">'.$return.'</span>';
208	}
209
210
211
212
213	/**
214	 * Returns the link tag for a given file
215	 * @param  string $filepath
216	 * @return string
217	 */
218	private function formatLink(SplFileInfo $file, array $data)
219	{
220		global $conf;
221
222		$link  = '<a href="';
223		if( $data['type']=='download' ) {
224		    $link .= '?do=download&file='.rawurlencode($file->getRealPath());
225		} else if( $data['type']=='direct' ) {
226		    $link .= 'file://'.$file->getRealPath();
227		} else if ($data['type']=='link'){
228		    $startpos = strpos( $file->getRealPath(), $conf['basedir'] );
229		    $link .= $conf['baseurl'].substr( $file->getRealPath(), $startpos);
230		} else {
231		    $link .= '';
232		}
233		$link .= '"';
234		if( $data['type']=='download' ) {
235		    $link .= ' target="_blank"';
236		}
237		$link .= ' title="'.$file->getFilename().'"';
238		$link .= '>';
239		$link .= utf8_encode($file->getFilename());
240		$link .= '</a>';
241
242		return $link;
243	}
244}
245
246// vim:ts=4:sw=4:et:
247