1<?php
2/**
3 * This class is a backend to use the ownCloud database while running DokuWiki
4 * You can have a look to <ownCloud-Path>/lib/db.php for functions provided.
5 * Here is a description for developer to using the ownCloud database
6 * http://doc.owncloud.org/server/5.0/developer_manual/app/appframework/database.html
7 *
8 * @license    GPL 3 (http://www.gnu.org/licenses/gpl.html)
9 * @author     Martin Schulte <lebowski[at]corvus[dot]uberspace[dot]de>, 2013
10 */
11// must be run within Dokuwiki
12if (!defined('DOKU_INC')) die();
13
14//constants
15if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
16if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
17if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
18if(!defined('DOKU_CHANGE_TYPE_MOVE')) define('DOKU_CHANGE_TYPE_MOVE','M');
19if(!defined('DOKU_CHANGE_TYPE_REPLACE')) define('DOKU_CHANGE_TYPE_REPLACE','R');
20
21
22class helper_plugin_owncloud extends DokuWiki_Plugin
23{
24	// The last query of type PDOStatementWrapper (see <ownCloud-Path>/lib/db.php)
25	protected $lastQuery;
26	protected $storageNr;
27	protected $lastfileid;
28	public $wiki = 'wiki';
29
30	// id => filename
31	protected $fileIDCache = array();
32	// filename => id
33	protected $filenameCache = array();
34
35    /**
36     * Constructor to connect to db and check if ownCloud is installed
37     */
38    public function helper_plugin_owncloud($db=true) {
39		if($db){
40			global $conf;
41			include_once($this->getConf('pathtoowncloud').'/lib/base.php');
42			// Check if ownCloud is installed or in maintenance (update) mode
43			if (!OC_Config::getValue('installed', false)) {
44				global $conf;
45				require_once('lang/'.$conf['lang'].'/settings.php');
46				echo $lang['owncloudNotInstalled'];
47				exit();
48			}
49                        // This really should be replaced by public OC methods. Oh well.
50                        $storageId = 'local::'.$conf['mediadir'].'/';
51                        if (strlen($storageId) > 64) {
52                          $storageId = md5($storageId);
53                        }
54			// Find Storage ID
55			$this->dbQuery('SELECT numeric_id FROM `*PREFIX*storages` WHERE `id` LIKE ?', array($storageId));
56			$this->storageNr = $this->lastQuery->fetchOne();
57		}
58    }
59
60    /**
61    * Prepares and executes a SQL query
62    *
63    * @param $sql String The SQL-Query (for syntax see http://doc.owncloud.org/server/5.0/developer_manual/app/appframework/database.html)
64    * @param $params array The parameters should be used in the sql-query
65    */
66    public function dbQuery($sql, $params){
67		$db = \OC_DB::prepare($sql);
68		$this->lastQuery = $db->execute($params);
69
70	}
71
72
73	// Returns the last query (last call of dbQuery)
74	public function getLastQuery(){
75		return $this->lastQuery;
76	}
77
78	// Returns the last fileid
79	public function getLastfileid(){
80		return $this->lastfileid;
81	}
82
83	/**
84    * Returns the path for the correspondig fileID (from table OC_filecache)
85    *
86    * @param $id integer The fileID
87    * @param $wikiID bool If true, slashes (/) will be replaced by colons (:)
88    */
89	public function getFilenameForID($id, $wikiID=false){
90		if(isset($this->fileidCache[$id])){// save db query
91			 $path = $this->fileidCache[$id];
92		}else{
93			$this->dbQuery('SELECT `path` FROM `*PREFIX*filecache` WHERE fileid = ? AND storage = ?', array($id, $this->storageNr));
94			if($this->lastQuery->numRows() == 0) return NULL;
95			$path = $this->lastQuery->fetchOne();
96		}
97		$this->fileidCache[$id] = $path;
98		if($wikiID) return $this->pathToWikiID($path);
99		return $path;
100	}
101
102	/** replace / with / */
103	public function pathToWikiID($path){
104		return str_replace('/',':',$path);
105	}
106
107	/** replace : with / */
108	public function wikiIDToPath($id){
109		return str_replace(':','/',$id);
110	}
111
112	/** Returns true if the given path is a directory */
113	public function isMediaDir($path){
114		global $conf;
115		return is_dir($conf['mediadir'].'/'.trim($path,'/'));
116	}
117
118
119	/**
120    * Returns the fileid for the correspondig path (from table OC_filecache)
121    *
122    * @param $file string The path
123    * @return $id the fileID
124    */
125	public function getIDForFilename($file){
126		$file = trim($file,'/'); //Remove slashes at the beginning
127		// save db query
128		if(isset($this->filenameCache[$file])) return $this->filenameCache[$file];
129		$this->dbQuery('SELECT `fileid` FROM `*PREFIX*filecache` WHERE path = ? AND storage = ?', array($file, $this->storageNr));
130		$id = $this->lastQuery->fetchOne();
131		$this->filenameCache[$file] = $id;
132		$this->lastfileid = $id;
133		return $id;
134	}
135
136	/**
137    * Returns the mimetype for the correspondig id (from table OC_filecache)
138    *
139    * @param $file string The path
140    */
141	public function getMimetypeForID($id){
142		$this->dbQuery('SELECT *PREFIX*mimetypes.mimetype FROM *PREFIX*filecache JOIN *PREFIX*mimetypes ON *PREFIX*filecache.mimetype = *PREFIX*mimetypes.id WHERE fileid = ? AND storage = ?', array($id, $this->storageNr));
143		return $this->lastQuery->fetchOne();
144	}
145
146	/**
147    * Returns the content of a folder specified by its id (from oc database)
148    *
149    * @param $id string the folderID
150    * @return @folderAndFiles folderID and filesID in the given dir
151    */
152	public function getFolderContent($id){
153		$this->dbQuery('SELECT `fileid`, `path`,*PREFIX*mimetypes.mimetype FROM *PREFIX*filecache  JOIN *PREFIX*mimetypes ON *PREFIX*filecache.mimetype = *PREFIX*mimetypes.id WHERE parent=? AND storage = ? ORDER BY *PREFIX*filecache.path ASC', array($id, $this->storageNr));
154		$rows = $this->lastQuery->numRows();
155		$files = array();
156		$folders = array();
157		for($i = 1; $i <= $rows; $i++){
158				$row = $this->lastQuery->fetchRow();
159				if($row['mimetype'] == 'httpd/unix-directory'){
160					array_push($folders, $row);
161				}else{
162					array_push($files, $row);
163				}
164		}
165		return array($folders,$files);
166	}
167
168	/**
169    * Returns the media used on the page specified by $wikiid (from
170    * table OC_dokuwiki_media_used). It will be rendered as a orderd list
171    *
172    * @param $file string wikiid
173    * @return $string orderd list of media used on the page
174    */
175	public function getMediaOfThisPage($wikiid){
176		$this->dbQuery('SELECT `fileid`,`path` FROM `*PREFIX*dokuwiki_media_use` JOIN `*PREFIX*filecache` USING(`fileid`) WHERE `wikipage_hash` = ? ORDER BY `fileid` ASC', array(md5($wikiid)));
177		$rows = $this->lastQuery->numRows();
178		if(empty($rows) || $rows == 0) return false;
179		$ret = '<p style="float:right;"><a href="#headingusedmedia" id="usemediadetail">'.$this->getLang('showfileinfo').'</a></p>';
180		$ret .= DOKU_LF.'<ol id="usedmedia">'.DOKU_LF;
181		$ids = $this->lastQuery;
182		for($i = 1; $i <= $rows; $i++){
183			$row = $ids->fetchRow();
184			$ret .= DOKU_TAB.'<li class="mediaitem" fileid="'.$row['fileid'].'">';
185			$ret .= $this->internalmedia($row['fileid'],"",(($row['path']!='')?$row['path']:'/'),NULL,16,NULL,NULL,'linkonly');
186			$ret .= '</li>'.DOKU_LF;
187		}
188		$ret .= '</ol>'.DOKU_LF;
189		return $ret;
190
191	}
192
193	/**
194    * Returns formatted informations about the given $file or $id. The
195    * informations contain the time, the number of versions, the authors
196    * and the description.
197    *
198    * @param $file full filename or id
199    * @param $isid choose true, if the first parameter is a fileid
200    * @return $string html with file informations
201    */
202	public function fileInfoToString($file, $isID = false){
203		global $conf;
204		if($isID) $file = $this->getFilenameForID($file);
205		if($this->isMediaDir($file)) return '';
206		list($authorsString,$desc,$count,$time) = $this->getAuthorsAndDescOfMediafile($file);
207		if(empty($count)) $count = 0;
208		return '<span class="filedesc" style=" font-size:90%;">'.
209			   '<p style="margin-bottom:0px;padding-left:16px;"><span><b>'.$this->getLang('historyVersion').':</b>&nbsp;'.strftime($conf['dformat'],intval($time)).'&nbsp;('.$count.'&nbsp;'.($count == 1?$this->getLang('version'):$this->getLang('versions')).')</span></b>'.
210			   '<p style="margin-bottom:0px;padding-left:16px;"><span><b>'.$this->getLang('filelistAuthor').'</b>:&nbsp;'.$authorsString.'</span></p>'.
211		       (($desc != '' && $desc != $lang['created'])?'<p style="margin-bottom:2px;padding-left:16px;"><b>'.$this->getLang('historyComment').'</b>:&nbsp;'.$desc.'</p>':'').'</span>';
212	}
213
214
215
216
217	/** Returns the authors of the given mediafile as string. If plugin authorlist is enabled,
218	 *  authors will be linked as configured in this plugin.
219	 */
220	public function getAuthorsAndDescOfMediafile($file){
221		global $ID;
222		if($this->getConf('linkAuthor') && !plugin_isdisabled('authorlist')){
223			$authorlist = $this->loadHelper('authorlist',true);
224			$authorlist->setOptions($ID,array('displayaslist'=>false));
225		}
226		$meta = $this->getMediaMeta($file);
227		if(!empty($meta)){
228			$authors = array();
229			$line =array();
230			foreach($meta as $onemeta){
231				$line = explode("\t", $onemeta);
232				if($line[4] != "" && !in_array($line[4],$authors)) array_push($authors,$line[4]);
233			}
234			if($authorlist){
235				foreach($authors as &$author){
236					$author=$authorlist->renderOneAuthor($author,$authorlist->getFullname($author));
237				}
238			}
239			return array(implode(", ", $authors),$line[5],count($meta),$line[0]); // $line[5] is the latest filedescription
240		}
241		return '';
242	}
243
244	/** Returns the meta data of the given mediafile */
245	public function getMediaMeta($file){
246		global $conf;
247		if(file_exists($conf['mediametadir'].'/'.$file.'.changes')) return file($conf['mediametadir'].'/'.$file.'.changes');
248		return array();
249	}
250
251	/**
252    * Builds a table for the mediameta information. The rest is done by
253    * jQuery
254    *
255    * @param $file file to find the metadata for
256    * @return $ret htmltable with included javascript
257    */
258	public function mediaMetaStart($file){
259		global $lang;
260		$ret = '<div class="historyOC">'.DOKU_LF;
261		$ret .= DOKU_TAB.'<div class="table"><table  width="100%" class="inline">'.DOKU_LF;
262		$ret .= DOKU_TAB.DOKU_TAB.'<tr class="row0">'.DOKU_LF;
263		$ret .= DOKU_TAB.DOKU_TAB.DOKU_TAB.'<th class="col0" width="20%" >'.($this->getLang('historyVersion')).'</th><th  width="20%" class="col1">'.($this->getLang('historyAuthor')).'</th><th width="10%" class="col2">'.($this->getLang('filelistSize')).'</th><th class="col3" width="45%">'.($this->getLang('historyComment')).'</th>'.DOKU_LF;
264		$ret .= DOKU_TAB.DOKU_TAB.'</tr>'.DOKU_LF;
265		$ret .= DOKU_TAB.DOKU_TAB.'<tr><td colspan="4" class="load"></td></tr>'.DOKU_LF;
266		$ret .= DOKU_TAB.'</table></div></div>'.DOKU_LF;
267		// To run javascript only if filelist is on this side
268		$ret .= DOKU_TAB.'<script type="text/javascript">filehistory.start("'.$file.'");</script>'.DOKU_LF;
269		$wikiid = $this->pathToWikiID($file);
270		$ns = getNS($wikiid);
271		$mediamanager = '<a class="mmLink" href="'.DOKU_URL.'doku.php?ns='.$ns.'&image='.$this->pathToWikiID($file).'&do=media&tab_details=history">'.$this->getLang('compare').'</a>';
272		$ret .= '<p>'.$this->getLang('mediamanager_info').' '.$mediamanager.'</p>';
273		return $ret;
274
275
276	}
277
278	/**
279    * Builds the innards for the table generated by mediaMetaStart()
280    * is called via jQuery.
281    *
282    * @param $file file to find the metadata for
283    * @return $ret html-table-rows with media meta
284    */
285	public function mediaMetaAsTable($file){
286		$ret = "";
287		global $conf;
288		global $ID;
289		global $lang;
290		$meta = $this->getMediaMeta($file);
291		if(empty($meta)) return '<tr><td colspan="4" align="center">'.($this->getLang('noVersion')).'</td></tr>';
292		$meta =  array_reverse($meta); // Newest first.
293		$authorlist = false;
294		if($this->getConf('linkAuthor') && !plugin_isdisabled('authorlist')){
295			$authorlist = $this->loadHelper('authorlist',true);
296			$authorlist->setOptions($ID,array('displayaslist'=>false));
297		}
298		$oldmedia = $conf['mediaolddir'];
299		$nr = 1;
300		$fetch = DOKU_BASE.'lib/exe/fetch.php';
301		foreach($meta as $onemeta){
302			$line = explode("\t", $onemeta);
303			$time = strftime($conf['dformat'],intval($line[0]));
304			if($nr > 1){ // in attic
305				list($name, $ext) =  $this->filenameAndExtension($file);
306				$path = $oldmedia.'/'.$name.'.'.$line[0].'.'.$ext;
307				$link = '<a title="'.$time.'" href="'.$fetch.'?media='.($this->pathToWikiID($file)).'&rev='.$line[0].'" target="_blank">'.$time.'</a>';
308			}else{
309				$path = $conf['mediadir'].'/'.$file;
310				$link = $time.' <small>('.$lang['current'].')<small>';;
311
312			}
313			$size = (file_exists($path)) ? filesize_h(filesize($path)) : '--';
314			if(empty($line[4]))	$author = $line[1]; // IP if no author
315			else $author = ($authorlist)?$authorlist->renderOneAuthor($line[4],$authorlist->getFullname($line[4])) : $line[4];
316			//$author .= '<span class="ip">('.$line[1].')</span>';
317			if($line[2] == DOKU_CHANGE_TYPE_MOVE) $extra = ' ('.$this->getLang('movedfrom').' '.hsc($line[6]).')';
318			else $extra = '';
319
320
321			$ret .= '<tr class=" row'.$nr.'">';
322			$ret .= '<td class="col0" > '.$link.' </td><td class="col1">'.$author.'</td><td class="col2">'.$size.'</td><td class="col3">'.$line[5].$extra.'</td>';
323			$ret .= '</tr>';
324			$nr++;
325		}
326		return $ret;
327
328
329	}
330
331	/**
332    * Returns the filename and extension (Can be replaced by php nativ
333    * function pathinfo, only for compatibility
334    *
335    * @param $file file
336    * @return @array filename and extension
337    */
338	public function filenameAndExtension($file){
339			$extPos  = strrpos($file, '.');
340			$ext = substr($file, $extPos + 1);
341			$name = substr($file, 0, $extPos);
342			return array($name, $ext);
343	}
344
345
346    /**
347    * Returns true, if the given source starts with http, https or ftp
348    * otherwise false
349    *
350    * @param $src string
351    * @return bool true if http(s)/ftp otherwise false
352    */
353    public function isExternal($src){
354		if ( preg_match('#^(https?|ftp)#i',$src) ) {
355			return  true;
356		}
357		return false;
358	}
359
360	/**
361    * Tests if an url match the the urls or pattern specified in the
362    * plugin configuration.
363    *
364    * @param $url URL
365    * @return bool true if allowed
366    */
367	public function isAllowedExternalImage($url){
368		if($this->getConf('allowExternalImages') == 1) return true; // all external images are allowed
369		$allowedImagesURL = explode(',',$this->getConf('allowedImagesURL'));
370		$match = false;
371		foreach($allowedImagesURL as $allowedURL){
372			$allowedURL = trim($allowedURL);
373			if(empty($allowedURL)) continue;
374			if(strpos($url, $allowedURL) === 0 ){
375				$match = true;
376				break;
377			}
378		}
379		if($match) return true; // save some time, if we have a match until here
380		$allowedImagesURLregexp = explode(',',$this->getConf('allowedImagesURLregexp'));
381		foreach($allowedImagesURLregexp as $pattern){
382			$pattern = trim($pattern);
383			if(empty($pattern)) continue;
384			if(preg_match('#'.str_replace('#','\\#',$pattern).'#i',$url)){
385				$match = true;
386				break;
387			}
388		}
389		return $match;
390	}
391
392    /**
393     * Renders an internal media link. This is nearly the same function as internalmedia(...)
394     *  in inc/parser/xhtml.php extended to getting the filename from fileid.
395     *
396     * @author Harry Fuecks <hfuecks@gmail.com>
397     * @author Andreas Gohr <andi@splitbrain.org>
398     * @author Martin Schulte <lebowski[at]corvus[dot]uberspace[dot]de>
399     */
400    public function internalmedia($fileid, $src, $title=NULL, $align=NULL, $width=NULL,$height=NULL, $cache=NULL, $linking=NULL) {
401        global $ID;
402        $filelist = false;
403        list($src,$hash) = explode('#',$src,2);
404        if($fileid != '' && $fileid > 0){
405				$res = $this -> getFilenameForID($fileid, true);
406		}
407		if(isset($res)){
408				$src = $res;
409				if($src == ''){ // we are at the top
410					$src = '/';
411					if(empty($title)) $title = '/';
412				}
413				$exists = true;
414		}else{
415			$oldsrc = $src;
416			resolve_mediaid(getNS($ID),$src, $exists);
417			if($exists){
418				$fileid = $this->fileIDForWikiID($src);
419			}else{// Maybe directory
420				$fileid = $this->fileIDForWikiID($oldsrc);
421				if($fileid != '' && $fileid > 0){
422					$src = $oldsrc;
423					$exists = true;
424				}
425			}
426		}
427        $noLink = false;
428        $render = ($linking == 'linkonly') ? false : true;
429        // + fileid first parameter
430        $link = $this->_getMediaLinkConf($fileid,$src, $title, $align, $width, $height, $cache, $render);
431
432        list($ext,$mime,$dl) = mimetype($src,false);
433        if(substr($mime,0,5) == 'image' && $render){
434			// + fileid  & $this->
435            $link['url'] = $this->ml($src,array('id'=>$ID,'cache'=>$cache,'fileid'=>$fileid),($linking=='direct'));
436        }elseif($mime == 'application/x-shockwave-flash' && $render){
437            // don't link flash movies
438            $noLink = true;
439        }else{
440            // add file icons
441            $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
442            // + mimetype folder
443            if(empty($ext)){
444					if($this->getMimetypeForID($fileid) == 'httpd/unix-directory') {
445							$class = 'folder';
446							if($linking =='direct') $filelist = true;
447					}
448			}
449            $link['class'] .= ' mediafile mf_'.$class;
450            // + fileid  & $this->
451            $link['url'] = $this->ml($src,array('id'=>$ID,'cache'=>$cache,'fileid'=>$fileid),($linking=='direct'));
452            // + no size if directory
453            if ($exists && $class != 'folder') $link['title'] .= ' (' . filesize_h(filesize(mediaFN($src))).')';
454        }
455
456        if($hash) $link['url'] .= '#'.$hash;
457
458        //markup non existing files
459        if (!$exists) {
460            $link['class'] .= ' wikilink2';
461        }
462        //output formatted
463        // + return instead of .=
464        if ($linking == 'nolink' || $noLink){
465			 return $link['name'];
466		}else{
467			if(!$filelist){
468				return $this->_formatLink($link);
469			}else{
470				$link['name'] = $this->wikiIDToPath($src);
471				return $this->filelist($fileid);
472			}
473		}
474
475
476	}
477
478	/**
479     * Renders an external media link. This is the same function as externalmedia(...)
480     * in inc/parser/xhtml.php, here it returns the link and don't add it to the render doc
481     *
482     * @author Harry Fuecks <hfuecks@gmail.com>
483     * @author Andreas Gohr <andi@splitbrain.org>
484     */
485	function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
486                            $height=NULL, $cache=NULL, $linking=NULL) {
487        list($src,$hash) = explode('#',$src,2);
488        $noLink = false;
489        $render = ($linking == 'linkonly') ? false : true;
490        // + -1 as fileid
491        $link = $this->_getMediaLinkConf(-1,$src, $title, $align, $width, $height, $cache, $render);
492
493		// use the originally ml function
494        $link['url'] = ml($src,array('cache'=>$cache));
495
496        list($ext,$mime,$dl) = mimetype($src,false);
497        if(substr($mime,0,5) == 'image' && $render){
498            // link only jpeg images
499            // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true;
500        }elseif($mime == 'application/x-shockwave-flash' && $render){
501            // don't link flash movies
502            $noLink = true;
503        }else{
504            // add file icons
505            $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
506            $link['class'] .= ' mediafile mf_'.$class;
507        }
508
509        if($hash) $link['url'] .= '#'.$hash;
510
511        //output formatted
512        if ($linking == 'nolink' || $noLink) return  $link['name'];
513        else return  $this->_formatLink($link);
514    }
515
516    /** Renders a list with all files of the given folder. Using jQuery (see script.js).
517     */
518    public function filelist($folderid, $link){
519		if(!isset($link)) $link = '<div class="filelistheader">'.($this->getLang('filelistHeader')).' '.$this->internalmedia($folderid,NULL,NULL, NULL, NULL,NULL, NULL, 'details').'</div>';
520		$ret = '<div class="filelistOC fileid'.$folderid.'">'.$link.DOKU_LF;
521		$ret .= DOKU_TAB.'<div class="table"><table  width="100%" class="inline">'.DOKU_LF;
522		$ret .= DOKU_TAB.DOKU_TAB.'<tr class="row0">'.DOKU_LF;
523		$ret .= DOKU_TAB.DOKU_TAB.DOKU_TAB.'<th class="col0">'.($this->getLang('filelistName')).'</th><th class="col1">'.($this->getLang('filelistAuthor')).'</th><th class="col2">'.($this->getLang('filelistDate')).'</th><th class="col3">'.($this->getLang('filelistSize')).'</th><th class="col4"></th>'.DOKU_LF;
524		$ret .= DOKU_TAB.DOKU_TAB.'</tr>'.DOKU_LF;
525		$ret .= DOKU_TAB.DOKU_TAB.'<tr><td colspan="5" class="load"></td></tr>'.DOKU_LF;
526		$ret .= DOKU_TAB.'</table></div></div>'.DOKU_LF;
527		// To run javascript only if filelist is on this side
528		$ret .= DOKU_TAB.'<script type="text/javascript"> window.filelistOnThisSide = true;</script>'.DOKU_LF;
529		return $ret;
530	}
531
532
533	/**
534	 * This is nearly the same function as ml(...) in inc/common.php
535	 * extended to getting the filename from fileid.
536	 *
537	 * Build a link to a media file
538	 *
539	 * Will return a link to the detail page if $direct is false
540	 *
541	 * The $more parameter should always be given as array, the function then
542	 * will strip default parameters to produce even cleaner URLs
543	 *
544	 * This is nearly the code from inc/common.php. Replaced lib/exe/fetch.php
545	 * with lib/plugins/owncloud/exe/fetch.php.
546	 *
547	 * @author Andreas Gohr <andi@splitbrain.org>
548	 * @author Martin Schulte <lebowski[at]corvus[dot]uberspace[dot]de>
549	 *
550	 * @param string  $id     the media file id or URL
551	 * @param mixed   $more   string or array with additional parameters
552	 * @param bool    $direct link to detail page if false
553	 * @param string  $sep    URL parameter separator
554	 * @param bool    $abs    Create an absolute URL
555	 * @return string
556	 */
557	function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false) {
558
559		global $conf;
560		$isexternalimage = media_isexternal($id);
561		if(is_array($more)) {
562			// add token for resized images
563			if(!empty($more['w']) || !empty($more['h']) || $isexternalimage ){
564				$more['tok'] = media_get_token($id,$more['w'],$more['h']);
565			}
566			// strip defaults for shorter URLs
567			if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']);
568			if(empty($more['w'])) unset($more['w']);
569			if(empty($more['h'])) unset($more['h']);
570			//+ fileid
571			if($more['fileid'] <1 || $more['fileid'] == '') unset($more['fileid']);
572			if(isset($more['id']) && $direct) unset($more['id']);
573			$more = buildURLparams($more, $sep);
574		} else {
575			$matches = array();
576			if (preg_match_all('/\b(w|h)=(\d*)\b/',$more,$matches,PREG_SET_ORDER) || $isexternalimage){
577				$resize = array('w'=>0, 'h'=>0);
578				foreach ($matches as $match){
579					$resize[$match[1]] = $match[2];
580				}
581				$more .= $more === '' ? '' : $sep;
582				$more .= 'tok='.media_get_token($id,$resize['w'],$resize['h']);
583			}
584			$more = str_replace('cache=cache', '', $more); //skip default
585			$more = str_replace(',,', ',', $more);
586			$more = str_replace(',', $sep, $more);
587		}
588
589		if($abs) {
590			$xlink = DOKU_URL;
591		} else {
592			$xlink = DOKU_BASE;
593		}
594
595		// external URLs are always direct without rewriting
596		if(preg_match('#^(https?|ftp)://#i', $id)) {
597			// + changed lib/exe to lib/plugins/owncloud/
598			$xlink .= 'lib/plugins/owncloud/exe/fetch.php';
599			// add hash:
600			$xlink .= '?hash='.substr(md5(auth_cookiesalt().$id), 0, 6);
601			if($more) {
602				$xlink .= $sep.$more;
603				$xlink .= $sep.'media='.rawurlencode($id);
604			} else {
605				$xlink .= $sep.'media='.rawurlencode($id);
606			}
607			return $xlink;
608		}
609
610		$id = idfilter($id);
611
612		// decide on scriptname
613		if($direct) {
614			if($conf['userewrite'] == 1) {
615				$script = '_media';
616			} else {
617				// + changed lib/exe to lib/plugins/owncloud/
618				$script = 'lib/plugins/owncloud/exe/fetch.php';
619			}
620		} else {
621			if($conf['userewrite'] == 1) {
622				$script = '_detail';
623			} else {
624				// + changed lib/exe to lib/plugins/owncloud/
625				$script = 'lib/plugins/owncloud/exe/detail.php';
626			}
627		}
628
629		// build URL based on rewrite mode
630		if($conf['userewrite']) {
631			$xlink .= $script.'/'.$id;
632			if($more) $xlink .= '?'.$more;
633		} else {
634			if($more) {
635				$xlink .= $script.'?'.$more;
636				$xlink .= $sep.'media='.$id;
637			} else {
638				$xlink .= $script.'?media='.$id;
639			}
640		}
641
642		return $xlink;
643	}
644
645
646	/**
647     * _getMediaLinkConf is a helperfunction to internalmedia() and externalmedia()
648     * which returns a basic link to a media (from inc/parser/xhtml.php).
649     *
650     * + fileid as first parameter
651     *
652     * @author Pierre Spring <pierre.spring@liip.ch>
653     * @param string $src
654     * @param string $title
655     * @param string $align
656     * @param string $width
657     * @param string $height
658     * @param string $cache
659     * @param string $render
660     * @access protected
661     * @return array
662     */
663	function _getMediaLinkConf($fileid,$src, $title, $align, $width, $height, $cache, $render)
664    {
665        global $conf;
666
667        $link = array();
668        $link['class']  = 'media';
669        $link['style']  = '';
670        $link['pre']    = '';
671        $link['suf']    = '';
672        $link['more']   = '';
673        $link['target'] = $conf['target']['media'];
674        $link['title']  = $this->_xmlEntities($src);
675        // + fileid first parameter
676        $link['name']   = $this->_media($fileid, $src, $title, $align, $width, $height, $cache, $render);
677
678        return $link;
679    }
680
681    /**
682     * Renders internal and external media (from inc/parser/xhtml.php)
683     *
684     * add fileid as first parameter
685     *
686     * @author Andreas Gohr <andi@splitbrain.org>
687     */
688    function _media($fileid, $src, $title=NULL, $align=NULL, $width=NULL,
689                      $height=NULL, $cache=NULL, $render = true) {
690
691        $ret = '';
692
693        list($ext,$mime,$dl) = mimetype($src);
694        if(substr($mime,0,5) == 'image'){
695            // first get the $title
696            if (!is_null($title)) {
697                $title  = $this->_xmlEntities($title);
698            }elseif($ext == 'jpg' || $ext == 'jpeg'){
699                //try to use the caption from IPTC/EXIF
700                require_once(DOKU_INC.'inc/JpegMeta.php');
701                $jpeg =new JpegMeta(mediaFN($src));
702                if($jpeg !== false) $cap = $jpeg->getTitle();
703                if($cap){
704                    $title = $this->_xmlEntities($cap);
705                }
706            }
707            if (!$render) {
708                // if the picture is not supposed to be rendered
709                // return the title of the picture
710                if (!$title) {
711                    // just show the sourcename
712                    $title = $this->_xmlEntities(utf8_basename(noNS($src)));
713                }
714                return $title;
715            }
716            //add image tag
717            // + $this
718            $ret .= '<img src="'.($this->ml($src,array('fileid'=>$fileid,'w'=>$width,'h'=>$height,'cache'=>$cache))).'"';
719            $ret .= ' class="media'.$align.'"';
720
721            if ($title) {
722                $ret .= ' title="' . $title . '"';
723                $ret .= ' alt="'   . $title .'"';
724            }else{
725                $ret .= ' alt=""';
726            }
727
728            if ( !is_null($width) )
729                $ret .= ' width="'.$this->_xmlEntities($width).'"';
730
731            if ( !is_null($height) )
732                $ret .= ' height="'.$this->_xmlEntities($height).'"';
733
734            $ret .= ' />';
735
736        }elseif($mime == 'application/x-shockwave-flash'){
737            if (!$render) {
738                // if the flash is not supposed to be rendered
739                // return the title of the flash
740                if (!$title) {
741                    // just show the sourcename
742                    $title = utf8_basename(noNS($src));
743                }
744                return $this->_xmlEntities($title);
745            }
746
747            $att = array();
748            $att['class'] = "media$align";
749            if($align == 'right') $att['align'] = 'right';
750            if($align == 'left')  $att['align'] = 'left';
751            $ret .= html_flashobject(ml($src,array('cache'=>$cache),true,'&'),$width,$height,
752                                     array('quality' => 'high'),
753                                     null,
754                                     $att,
755                                     $this->_xmlEntities($title));
756        }elseif($title){
757            // well at least we have a title to display
758            $ret .= $this->_xmlEntities($title);
759        }else{
760            // just show the sourcename
761            $ret .= $this->_xmlEntities(utf8_basename(noNS($src)));
762        }
763
764        return $ret;
765    }
766
767
768
769
770	 /**
771     * (from inc/parser/xhtml.php)
772     */
773    function _xmlEntities($string) {
774        return htmlspecialchars($string,ENT_QUOTES,'UTF-8');
775    }
776
777    /**
778     * Build a link (from inc/xhtml)
779     *
780     * Assembles all parts defined in $link returns HTML for the link
781     *
782     * @author Andreas Gohr <andi@splitbrain.org>
783     */
784    function _formatLink($link){
785        //make sure the url is XHTML compliant (skip mailto)
786        if(substr($link['url'],0,7) != 'mailto:'){
787            $link['url'] = str_replace('&','&amp;',$link['url']);
788            $link['url'] = str_replace('&amp;amp;','&amp;',$link['url']);
789        }
790        //remove double encodings in titles
791        $link['title'] = str_replace('&amp;amp;','&amp;',$link['title']);
792
793        // be sure there are no bad chars in url or title
794        // (we can't do this for name because it can contain an img tag)
795        $link['url']   = strtr($link['url'],array('>'=>'%3E','<'=>'%3C','"'=>'%22'));
796        $link['title'] = strtr($link['title'],array('>'=>'&gt;','<'=>'&lt;','"'=>'&quot;'));
797
798        $ret  = '';
799        $ret .= $link['pre'];
800        $ret .= '<a href="'.$link['url'].'"';
801        if(!empty($link['class']))  $ret .= ' class="'.$link['class'].'"';
802        if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"';
803        if(!empty($link['title']))  $ret .= ' title="'.$link['title'].'"';
804        if(!empty($link['style']))  $ret .= ' style="'.$link['style'].'"';
805        if(!empty($link['rel']))    $ret .= ' rel="'.$link['rel'].'"';
806        if(!empty($link['more']))   $ret .= ' '.$link['more'];
807        $ret .= '>';
808        $ret .= $link['name'];
809        $ret .= '</a>';
810        $ret .= $link['suf'];
811        return $ret;
812    }
813
814	/* Looking for a fileid when a wikiid is given
815	 *
816	 * @param String wikiid of the mediafile
817	 * @return fileid for a wikiid
818	 */
819	public function fileIDForWikiID($src){
820			$path = str_replace(':','/',$src);
821			return $this->getIDForFilename($path);
822	}
823
824
825	/* Returns a list with all pages using the given media
826	 *
827	 * @param String fileid of the mediafile
828	 * @return String List with all pages using the specified media.
829	 */
830	public function mediaInUse($fileID){
831		$this->dbQuery('SELECT `firstheading`,`wikipage` FROM `*PREFIX*dokuwiki_media_use` WHERE fileid = ?', array($fileID));
832		$rows = $this->lastQuery->numRows();
833		$ret = '<h3 class="sectionedit3">'.$this->getLang('mediaUsedIn').'</h3>';
834		if(empty($rows) || $rows == 0) return '<h3 class="sectionedit3">'.$this->getLang('noUsage').'</h3>';
835		$ret .= '<ul>'.DOKU_TAB;
836		for($i = 1; $i <= $rows; $i++){
837				$row = $this->lastQuery->fetchRow();
838				$title = ($row['firstheading']!='')?$row['firstheading'].' ('.$row['wikipage'].')':$row['wikipage'];
839				$ret .= DOKU_TAB.'<li><span class="curid"><a href="'.wl($row['wikipage']).'" class="wikilink1" title="'.$title.'">'.$title.'</a></span></li>'.DOKU_LF;
840		}
841		$ret .= '</ul>'.DOKU_LF;
842		return $ret;
843	}
844
845
846}
847