1<?php 2 3/** 4 * DokuWiki Plugin authorstats (Syntax Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author George Chatzisofroniou <sophron@latthi.com> 8 * @author Constantinos Xanthopoulos <conx@xanthopoulos.info> 9 */ 10 11// must be run within Dokuwiki 12if (!defined("DOKU_INC")) die(); 13if (!defined("DOKU_LF")) define("DOKU_LF", "\n"); 14if (!defined("DOKU_TAB")) define("DOKU_TAB", "\t"); 15if (!defined("DOKU_PLUGIN")) define("DOKU_PLUGIN", DOKU_INC . "lib/plugins/"); 16 17class syntax_plugin_authorstats extends DokuWiki_Syntax_Plugin 18{ 19 var $helpers = null; 20 21 public function __construct() 22 { 23 $this->helpers = $this->loadHelper("authorstats", true); 24 } 25 26 public function getType() 27 { 28 return "substition"; 29 } 30 31 public function getPType() 32 { 33 return "stack"; 34 } 35 36 public function getSort() 37 { 38 return 371; 39 } 40 41 public function connectTo($mode) 42 { 43 $this->Lexer->addSpecialPattern("<AUTHORSTATS>", $mode, "plugin_authorstats"); 44 $this->Lexer->addSpecialPattern("<AUTHORSTATS [0-9]+>", $mode, "plugin_authorstats"); 45 $this->Lexer->addSpecialPattern("<AUTHORSTATS YEARGRAPH>", $mode, "plugin_authorstats"); 46 $this->Lexer->addSpecialPattern("<AUTHORSTATS YEARGRAPH\s+\d*\s*\w*>", $mode, "plugin_authorstats"); 47 } 48 49 public function handle($match, $state, $pos, Doku_Handler $handler) 50 { 51 return array($match); 52 } 53 54 public function render($mode, Doku_Renderer $renderer, $data) 55 { 56 57 if ($mode == "metadata") { 58 $renderer->meta["authorstats-enabled"] = 1; 59 return true; 60 } 61 62 if ($mode == "xhtml") { 63 if (preg_match("/<AUTHORSTATS (?P<months>[0-9]+)>/", $data[0], $matches)) { 64 $renderer->doc .= $this->_getMonthlyStatsTable(intval($matches[1])); 65 } else if (preg_match("/<AUTHORSTATS YEARGRAPH\s*(?P<years>[0-9]+)*\s*(?P<sort>(:?asc|ascending|desc|descending|rev|reverse)*)>/", $data[0], $matches)) { 66 $renderer->doc .= $this->getYearGraph($matches); 67 } else { 68 $renderer->doc .= $this->_getStatsTable(); 69 } 70 } 71 } 72 73 // Returns the number of author"s Contrib for a number of months 74 function _getLastMonthsContrib($author, $months) 75 { 76 $m = array(); 77 $sum = 0; 78 // Get an array of months in the format used eg. 201208, 201209, 201210 79 for ($i = $months - 1; $i >= 0; $i--) 80 array_push($m, date("Ym", strtotime("-" . $i . " Months"))); 81 82 // Sum the Contrib 83 foreach ($m as $month) { 84 if (array_key_exists($month, $author["pm"])) { 85 $sum += intval($author["pm"][$month]); 86 } 87 } 88 return $sum; 89 } 90 91 function _sortByContrib($a, $b) 92 { 93 return $this->_getTotalContrib($a) <= $this->_getTotalContrib($b) ? 1 : -1; 94 } 95 96 function _getTotalContrib($a) 97 { 98 return (intval($a["C"]) + intval($a["E"]) + intval($a["e"]) + intval($a["D"]) + intval($a["R"])); 99 } 100 101 function _sortByLastMonthsContrib($a, $b) 102 { 103 return $a["lmc"] >= $b["lmc"] ? -1 : 1; 104 } 105 106 function _getMonthlyContrib($authors, $yearmonth) 107 { 108 $sum = 0; 109 foreach ($authors as $author) { 110 if (array_key_exists($yearmonth, $author["pm"])) { 111 $sum += intval($author["pm"][$yearmonth]); 112 } 113 } 114 return $sum; 115 } 116 117 function getYearGraph($inopts) 118 { 119 global $conf; 120 if ($conf['allowdebug']) $start_time = microtime(true); 121 $output = "<h3>" . $this->getLang("yearly-contrib") . "</h3>"; 122 $data = $this->helpers->readJSON(); 123 $authors = $data["authors"]; 124 if (!$authors) return $this->getLang("no-stats"); 125 $totalpm = array(); 126 $labels = array(); 127 128 $max_months = 12; 129 if (isset($inopts["years"]) && $inopts["years"] > 0) { 130 $max_months = 12 * $inopts["years"]; 131 } 132 for ($i = 0; $i <= $max_months; $i++) { 133 array_push($totalpm, $this->_getMonthlyContrib($authors, date("Ym", strtotime("-$i months")))); 134 array_push($labels, date("Y-M", strtotime("-$i months"))); 135 } 136 137 $totalpm = array_reverse($totalpm); // For some odd reason the charting tool needs this is the reverse order of the labels... 138 if (isset($inopts["sort"])) { 139 if (preg_match("/^(desc|descending|rev|reverse)$/", $inopts["sort"])) { // Reverse the sort order from the default 140 $totalpm = array_reverse($totalpm); 141 $labels = array_reverse($labels); 142 } 143 } 144 145 // Append the parameters for the Axes Titles 146 $url = "https://chart.googleapis.com/chart"; 147 $url .= "?cht=bhs"; // Chart type; https://developers.google.com/chart/image/docs/gallery/chart_gall 148 $url .= "&chs=500x600"; // Chart size (width x height); The overall size is very limited, max total pixel has to be less than 300k. 149 $url .= "&chxt=y,y,x,x"; // Visible axes 150 $url .= "&chco=0000F0"; // Series colors 151 $url .= "&chds=a"; // Scale for text format with custom range; a == automatic scaling 152 $url .= "&chbh=a"; // Bar Width and Spacing; a == bars will resize to fit in the chart 153 $url .= "&chxr=0,1,12|1,0,100|3,0,100"; // Axis ranges 154 $url .= "&chxp=1,2,3,4,5,6,7,8,9,10,11,12|1,50|3,50"; // Axis label positions 155 $url .= "&chxl=0:|" . implode("|", $labels) . "|1:|Yr-Mon|3:|Num_of_Contributions"; // Axis labels 156 $url .= "&chd=t:" . implode(",", $totalpm); // Chart data string 157 if ($conf['allowdebug']) { 158 $end_time = microtime(true); 159 $execution_time = ($end_time - $start_time); 160 dbglog(__FUNCTION__ . " time:" . $execution_time, "AUTHORSTATS PLUGIN"); 161 } 162 return $output . "<img src=\"" . $url . "\">"; 163 } 164 165 function _makeAuthorLink($author, $name, $type) 166 { 167 if (!$this->getConf("enable-pagelist")) { 168 return $author[$type]; 169 } 170 $url = wl("authorstats:" . $name, array("do" => "authorstats_pages", "name" => $name, "type" => $type)); 171 $link = array( 172 "href" => $url, 173 "class" => "wikilink1", 174 "tooltip" => hsc($name), 175 "title" => hsc($author[$type]) 176 ); 177 $link = "<a href='" . $link["href"] . "' class='" . $link["class"] . "' title='" . $link["tooltip"] . "' rel='tag'>" . $link["title"] . "</a>"; 178 return $link; 179 } 180 181 // Returns the HTML table with the authors and their stats 182 function _getStatsTable() 183 { 184 global $conf; 185 if ($conf['allowdebug']) $start_time = microtime(true); 186 $output = "<h3>" . $this->getLang("gen-stats") . "</h3><table class=\"authorstats-table\"><tr><th>" . $this->getLang("name") . "</th><th>" . $this->getLang("creates") . "</th><th>" . $this->getLang("edits") . "</th><th>" . $this->getLang("minor") . "</th><th>" . $this->getLang("deletes") . "</th><th>" . $this->getLang("reverts") . "</th><th>" . $this->getLang("contrib") . "</th></tr>"; 187 $authors = $this->helpers->readJSON(); 188 $authors = $authors["authors"]; 189 if (!$authors) return $this->getLang("no-stats"); 190 uasort($authors, array($this, "_sortByContrib")); 191 foreach ($authors as $name => $author) { 192 $dname = $this->_getUser($name); 193 if ($dname == null) continue; 194 $output .= "<tr><th>" . 195 $dname . "</th><td>" . 196 $this->_makeAuthorLink($author, $name, "C") . "</td><td>" . 197 $this->_makeAuthorLink($author, $name, "E") . "</td><td>" . 198 $this->_makeAuthorLink($author, $name, "e") . "</td><td>" . 199 $this->_makeAuthorLink($author, $name, "D") . "</td><td>" . 200 $this->_makeAuthorLink($author, $name, "R") . "</td><td>" . 201 strval($this->_getTotalContrib($author)) . "</td></tr>"; 202 } 203 $output .= "</table>"; 204 if ($conf['allowdebug']) { 205 $end_time = microtime(true); 206 $execution_time = ($end_time - $start_time); 207 dbglog(__FUNCTION__ . " time:" . $execution_time, "AUTHORSTATS PLUGIN"); 208 } 209 return $output; 210 } 211 212 // Returns the HTML table with the authors and their Contrib for the 213 // last <$months> months 214 function _getMonthlyStatsTable($months) 215 { 216 global $conf; 217 if ($conf['allowdebug']) $start_time = microtime(true); 218 $output = "<h3>" . $this->getLang("contrib-months") . " " . $months . " " . $this->getLang("months") . "</h3><table class=\"authorstats-table\"><tr><th>" . $this->getLang("name") . "</th><th>" . $this->getLang("contrib") . "</th></tr>"; 219 $authors = $this->helpers->readJSON(); 220 $authors = $authors["authors"]; 221 if (!$authors) return $this->getLang("no-stats"); 222 foreach ($authors as $name => $author) { 223 $authors[$name]["lmc"] = $this->_getLastMonthsContrib($author, $months); 224 } 225 uasort($authors, array($this, "_sortByLastMonthsContrib")); 226 foreach ($authors as $name => $author) { 227 if ($authors[$name]["lmc"] > 0) { 228 $dname = $this->_getUser($name); 229 if ($dname == null) continue; 230 $output .= "<tr><th>" . 231 $dname . "</th><td>" . 232 strval($authors[$name]["lmc"]) . "</td></tr>"; 233 } 234 } 235 $output .= "</table>"; 236 if ($conf['allowdebug']) { 237 $end_time = microtime(true); 238 $execution_time = ($end_time - $start_time); 239 dbglog(__FUNCTION__ . " time:" . $execution_time, "AUTHORSTATS PLUGIN"); 240 } 241 return $output; 242 } 243 244 function _getUser($name) 245 { 246 global $auth; 247 $user = $auth->getUserData($name); 248 if ($user !== false and $this->getConf("show-realname")) { 249 $dname = $user["name"]; 250 } else if ($this->getConf("show-profile-links")) { 251 } else if ($user !== false) { 252 $dname = $name; 253 } else { 254 // Deleted user? 255 if (!$this->getConf("show-deleted-users")) return null; 256 $dname = "<i>($name)</i>"; 257 } 258 return $dname; 259 } 260} 261