1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Martin Schulte <lebowski[at]corvus[dot]uberspace[dot]de>
5 */
6// must be run within Dokuwiki
7if (!defined('DOKU_INC')) die();
8
9//constants
10if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
11if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
12if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
13
14
15
16class helper_plugin_authorlist extends DokuWiki_Plugin
17{
18    protected $authors = array();  // Contributors-Array (loginname => fullname)
19    // ID for the page, the authors should be diesplayed. Not implemented yet, you can't choose another
20    // page, can only handle the current rendered page. But maybe later.
21    protected $pageID;
22
23    protected $output;
24
25
26    protected $displaystyle;    // loginname/fullname or both
27    protected $displayaslist;   // List or everything in one line?
28    protected $tooltip;         // none, username or fullname.
29    protected $linkto;          // can be none, userpage, eMail
30	protected $showheading;     // We want a heading?
31    protected $showcreator;     // show Creator separat
32
33	// Can't be set using ~~AUTHORS~~ only via adminmenu
34    protected $creatortext;     // template for the creatortext  (contains %CREATOR% and %DATE% as wildcard)
35	protected $intro;			// text before authorlist.
36	protected $userpageid;      // Template for the pageID of the userhomepages (contains %USER% as wildcard for the loginname)
37
38	// Some state variables
39	private $cssClass;			// classes author, authoruserpage, authoremail are possible depending on the displaystyle.
40	private $openTag;           // <li> or <span>
41	private $closeTag;			// </li> or </span>
42	private $printempty;        // Print everything even if the authorlist is empty
43	private $creatorisauthor;  // creator in the authorlist.
44
45    /**
46     * Constructor gets default preferences (calling setOptions(...))
47     */
48    public function __construct() {
49		global $INFO;
50
51		// This options can only set using the admin menu. Because you can use html-Syntax for this options.
52		$this->creatortext = $this->getConf('creatortext');
53		$this->intro = $this->getConf('intro');
54		$this->userpageid = $this->getConf('userpageid');
55		$this->linkto = $this->getConf('linkto');
56		$this->showheading = $this->getConf('showheading');
57		$this->setOptions($INFO['id'] , null);		// set default options
58    }
59
60
61   /**
62    * Set the options.
63    *
64    * @param $pageID String The page the authorlist should be displayed for. (Not implemented yet, only the current rendered page will be handled)
65    * @param $data Array Optionsarray (option => value)
66    */
67    public function setOptions($pageID, $data){
68		$this->output='';
69		$this->pageID = $pageID;
70		$options = array('displayaslist','displaystyle','tooltip','showcreator','printempty', 'creatorisauthor');// possible options
71		foreach($options as $option){
72			if(isset($data[$option])){
73				$this->$option = $data[$option];
74			}else{
75				$this->$option = $this->getConf($option);
76			}
77		}
78		// find right css-class (author, authoeruserpage, authoremail -> see *.css), sets state variables $this->cssClass
79        $this->findCssClass();
80        // sets state variables $this->openTag, This->closeTag
81        $this->findOpenAndCloseTags();
82	}
83
84   /**
85    * Renders the heading
86    */
87	public function renderHeading(){
88		// heading?
89		if($this->showheading) $this->output .= "<h2 class='sectionedit1'><a name='authorlist'>".strip_tags($this->getConf('heading'))."</a></h2>".DOKU_LF;
90		return true;
91	}
92
93
94   /**
95    * Starts to render the authorlist: Creatortext, <div class='authorlist'> and maybe <ul class='authorlist'> will be add to $this->output
96    */
97    public function startList(){
98        //Show creator separate (before authorlist)
99        if($this->showcreator== "before" ) $this->output .= $this->renderCreator();
100        // Text before authorslist
101        if(!$this->printempty && empty($this->authors)) return false;
102        $this->output .= $this->intro.DOKU_LF;
103        // open div.authorlist
104        $this->output .= "<div class='authorlist'>".DOKU_LF;
105        if($this->displayaslist) $this->output .= "<ul class='authorlist'>".DOKU_LF; // open <ul>
106
107        return true;
108    }
109
110    /**
111    * Add an author to the $this->authors array
112    *
113    * @param $loginname String Login name of an user.
114    * @param $fullname String (optional) fullname of an user, will be found automatically if empty.
115    */
116    public function addAuthor($loginname, $fullname=''){
117		// Get fullname from users.auth
118		if($fullname == '') $fullname = $this-> getFullname($loginname);
119		// add them to the authors-array
120		$this->authors[$loginname] = $fullname;
121		return true;
122	}
123
124
125
126	/**
127    * Get's the authors of an article from the metadata and add them to $this->authors
128    */
129	public function fetchAuthorsFromMetadata(){
130		global $INFO;
131		if($this->creatorisauthor || !$this->showcreator){
132			// Creator is an author (creator not in $INFO['meta']['contributor'] if he/she made no changes
133			list($creator,$creatorfullname) = $this->getCreator();
134			if(!empty($creator) ) $this->addAuthor($creator,$creatorfullname);
135		}
136		// Authors from metadata
137		if(isset($INFO) && array_key_exists('contributor',$INFO['meta']))	$this->authors =  array_merge($INFO['meta']['contributor'],$this->authors);
138	}
139
140	/**
141    * Sort authors in alphabetical order. After calling this function $this->authors ist ordered.
142    */
143	public function sortAuthors(){
144		if($this->displaystyle != 'fullname' && $this->displaystyle != 'fullname (loginname)'){
145			// sort by key
146			ksort($this->authors);
147		}else{
148			// sort by value
149			asort($this->authors);
150		}
151	}
152
153	/**
154    * Renders all authors and add them to $this->output
155    */
156	public function renderAllAuthors(){
157		 foreach($this->authors as $loginname => $fullname){
158				$this->output .= $this->renderOneAuthor($loginname, $fullname);
159		 }
160	}
161
162    /**
163    * Finish to render the authorlist: close all open tags.
164    */
165    public function finishList(){
166		if($this->printempty || !empty($this->authors)){
167			// close <ul>
168			if($this->displayaslist) $this->output .= "</ul>".DOKU_LF;
169			// close div
170			$this->output .= "</div>".DOKU_LF;
171		}
172		if($this->showcreator == "below" ) $this->output .= $this->renderCreator();
173		return true;
174	}
175
176	/**
177    * Returns the current output. Makes sense after calling startlist() renderAllAuthors and finishList()
178    */
179	public function getOutput(){
180		return $this->output;
181	}
182
183	/**
184    * Builds the html-code for one author (called for each author by renderAllAuthors())
185    */
186	public function renderOneAuthor($loginname, $fullname=''){
187		$loginname = htmlspecialchars($loginname);
188		$fullname = htmlspecialchars($fullname);
189		// Find text to display on the site.
190		switch($this->displaystyle){
191				case "fullname": $display = $fullname; break;
192				case "loginname (fullname)": $display = $loginname.($fullname != ''?" (":"").$fullname.($fullname != ''?")":""); break;
193				case "fullname (loginname)": $display = $fullname != ''?"$fullname ($loginname)":$loginname; break;
194				default: $display = $loginname;
195		}
196		$inner = ">"; // $this->openTag has no closing > (so it's possible to add an title="..."
197		// Find title
198		if($this->linkto != 'email'){
199			switch($this->tooltip){
200				case "loginname": $inner = "title=\"$loginname\">"; break;
201				case "fullname": $inner = "title=\"$fullname\">"; break;
202			}
203		}
204		global $auth; // if we need to get the eMail-adress.
205		// build a link if necessary
206		switch($this->linkto){
207				case 'email': $userdata = $auth->getUserData($loginname); $display= $this->email($userdata['mail'], $display, 'authormail'); break;
208				case 'userhomepage': $display = $this->linkToUserhomepage($loginname, $display); break;
209		}
210		// Return the htmlcode for one author.
211		return $this->openTag.$inner.$display.$this->closeTag;
212	}
213
214	/**
215    * Builds the html-code for a link to a userhompage
216    *
217    * @param $loginname String Login name of an user.
218    * @param $display String The Text should be displayed as link.
219    */
220        private function linkToUserhomepage($loginname, $display){
221			$userpageid = str_replace("%USER%",$loginname, $this->userpageid);
222			$userpageid = htmlspecialchars($userpageid);
223                        if(page_exists($userpageid)) {
224                            return '<a href="'.wl($userpageid).'" rel="author" class="wikilink1" title="'.($this->tooltip = 'none'?$userpageid:'').'">'.$display.'</a>';
225                        } else {
226                            return '<a href="'.wl($userpageid).'" rel="nofollow" class="wikilink2" title="'.($this->tooltip = 'none'?$userpageid:'').'">'.$display.'</a>';
227                        }
228	}
229
230	/**
231    * Find's the right css-class, depending on on the linkto option.
232    */
233	private function findCssClass(){
234		switch($this->linkto){
235                case "userhomepage": $this->cssClass = "'authoruserpage'"; break;
236                case "email": $this->cssClass = "'authoremail'"; break;
237                default: $this->cssClass = "'author'";
238            }
239        return true;
240	}
241
242	/**
243    * If displayed as list, we need a <li>-Tag, else we need a <span>-Tag
244    */
245	private function findOpenAndCloseTags(){
246		if($this->displayaslist){
247			$this->openTag = DOKU_TAB."<li class='level1'><div class=".$this->cssClass." ";
248			$this->closeTag = "</div></li>".DOKU_LF;
249        }else{
250			$this->openTag = DOKU_TAB."<span class=".$this->cssClass." ";
251			$this->closeTag = "</span>".DOKU_LF;
252        }
253		return true;
254	}
255
256	/**
257    * Get the creator from the metadata (https://www.dokuwiki.org/devel:metadata)
258    */
259	private function getCreator(){
260		global $INFO;
261		return array($INFO['meta']['user'],$INFO['meta']['creator']);
262	}
263
264	/**
265    * Builds the html-code for the creatorline.
266    */
267	private function renderCreator($creator = '', $cTime = ''){
268		$creator = htmlspecialchars($creator);
269		$cTime = htmlspecialchars($cTime);
270		global $INFO;
271		// Get metadata if parameters are empty
272		if($creator == '' && $cTime == ''){
273			list($creator,$creatorfullname) = $this->getCreator();
274			$ctime = $INFO['meta']['date'];
275			$ctime = $ctime['created'];
276		}
277	   //$creator = "<span class=".$this->cssClass."";
278       // Handle template for this line
279       if($creator =='') return "";
280       switch($this->displaystyle){
281			case "fullname": $creator = $creatorfullname; break;
282			case "loginname (fullname)": $creator = $creator.($creatorfullname != ''?" (":"").$creatorfullname.($creatorfullname != ''?")":""); break;
283			case "fullname (loginname)": $creator = $creatorfullname != ''?"$creatorfullname ($creator)":$creator; break;
284		}
285	   global $conf;
286	   // build text from template
287       $output = str_replace("%CREATOR%",$creator, $this->creatortext);
288       $output = str_replace("%DATE%",strftime($conf['dformat'],$ctime), $output);
289       return $output.DOKU_LF;
290    }
291
292    /**
293    * Get the fullname for a given login name.
294    */
295    public function getFullname($loginname){
296		global $auth;
297		$userdata = $auth->getUserData($loginname);
298		return $userdata['name'];
299	}
300}
301