1<?php 2/** 3 * DokuWiki Plugin youtrack (Helper Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Dominic Pöllath <dominic.poellath@ils-gmbh.net> 7 * @author Anika Henke <anika@zopa.com> 8 */ 9 10// must be run within Dokuwiki 11if(!defined('DOKU_INC')) die(); 12 13class helper_plugin_youtrack extends DokuWiki_Plugin { 14 15 protected $cookie = false; 16 17 /** 18 * Return info about supported methods in this Helper Plugin 19 * 20 * @return array of public methods 21 */ 22 public function getMethods() { 23 return array( 24 array( 25 'name' => 'request', 26 'desc' => 'Send request to REST API', 27 'params' => array( 28 'method' => 'string', 29 'endpoint' => 'string', 30 'params (optional)' => 'array' 31 ), 32 'return' => array('Response' => 'mixed') 33 ), 34 array( 35 'name' => 'getBaseUrl', 36 'desc' => 'Get YouTrack base URL', 37 'return' => array('URL' => 'string') 38 ), 39 array( 40 'name' => 'login', 41 'desc' => 'Login to YouTrack', 42 'return' => array('If logged in or not' => 'bool') 43 ), 44 array( 45 'name' => 'logout', 46 'desc' => 'Logout of YouTrack by deleting cookie', 47 ), 48 array( 49 'name' => 'getIssue', 50 'desc' => 'Get issue by ID', 51 'params' => array( 52 'id' => 'string', 53 ), 54 'return' => array('Issue data' => 'SimpleXMLElement') 55 ), 56 array( 57 'name' => 'getIssues', 58 'desc' => 'Get issues by filter', 59 'params' => array( 60 'filter' => 'string', 61 ), 62 'return' => array('Issues data' => 'SimpleXMLElement') 63 ), 64 array( 65 'name' => 'getIssueUrl', 66 'desc' => 'Get issues by filter', 67 'params' => array( 68 'id' => 'string', 69 ), 70 'return' => array('URL' => 'string') 71 ), 72 array( 73 'name' => 'renderIssueTable', 74 'desc' => 'Render table of issues', 75 'params' => array( 76 'R' => 'Doku_Renderer', 77 'issues' => 'string', 78 'cols' => 'string' 79 ), 80 ), 81 ); 82 } 83 84 /** 85 * Send request to REST API 86 * 87 * @param string $method HTTP methods: POST, PUT, GET etc 88 * @param string $endpoint API endpoint 89 * @param array $params Request params: array("param" => "value") ==> ?param=value 90 * @return SimpleXMLElement Response 91 */ 92 function request($method, $endpoint, $params = false) { 93 if (!extension_loaded('curl')) { 94 msg('You need to have curl installed and enabled to use the YouTrack plugin', -1); 95 return false; 96 } 97 98 $url = $this->getBaseUrl().$endpoint; 99 100 $curl = curl_init(); 101 curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 1); 102 curl_setopt($curl, CURLOPT_TIMEOUT, 1); 103 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 104 105 switch ($method) { 106 case "POST": 107 curl_setopt($curl, CURLOPT_POST, 1); 108 if ($params) { 109 curl_setopt($curl, CURLOPT_POSTFIELDS, $params); 110 } 111 break; 112 case "PUT": 113 curl_setopt($curl, CURLOPT_PUT, 1); 114 break; 115 default: 116 if ($params) { 117 $url = sprintf("%s?%s", $url, http_build_query($params, '', '&')); 118 } 119 } 120 121 curl_setopt($curl, CURLOPT_URL, $url); 122 123 // Optional Authentication: 124 // curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 125 // curl_setopt($curl, CURLOPT_USERPWD, "username:password"); 126 // curl_setopt($curl, CURLOPT_COOKIEJAR, $ckfile); 127 if ($this->cookie) { 128 curl_setopt($curl, CURLOPT_COOKIEFILE, $this->cookie); 129 curl_setopt($curl, CURLOPT_COOKIEJAR, $this->cookie); 130 } 131 132 $content = curl_exec($curl); 133 curl_close($curl); 134 return $content ? $content : null; 135 } 136 137 138 /** 139 * Get YouTrack base URL (without ending slash) 140 * 141 * @return string URL 142 */ 143 function getBaseUrl() { 144 $url = $this->getConf('url'); 145 if (empty($url)) { 146 msg('YouTrack URL is not defined.', -1); 147 } 148 return substr($url, -1) === '/' ? substr($url, 0, strlen($url)-1) : $url; 149 } 150 151 152 /** 153 * Login to YouTrack 154 * 155 * @return bool If logged in or not 156 */ 157 function login() { 158 $user = $this->getConf('user'); 159 $password = $this->getConf('password'); 160 $url = $this->getBaseUrl(); 161 162 if (empty($user) || empty($password) || empty($url)) { 163 $this->cookie = false; 164 return false; 165 } 166 167 $this->cookie = tempnam(sys_get_temp_dir(), "CURLCOOKIE"); 168 169 $content = $this->request( 170 "POST", 171 "/rest/user/login", 172 array( 173 "login" => $user, 174 "password" => $password 175 ) 176 ); 177 178 if ($content && simplexml_load_string($content) != "ok") { 179 msg('Login data not correct, or REST login is not enabled.', -1); 180 return false; 181 } 182 return true; 183 } 184 185 /** 186 * Logout of YouTrack by deleting cookie 187 */ 188 function logout() { 189 if ($this->cookie) { 190 unlink($this->cookie) or die("Can't unlink $this->cookie"); 191 } 192 } 193 194 /** 195 * Get issue by ID 196 * 197 * @param string $id ID of issue 198 * @return SimpleXMLElement Issue data 199 */ 200 function getIssue($id) { 201 $this->login(); 202 $xml = $this->request("GET", "/rest/issue/$id"); 203 $this->logout(); 204 return simplexml_load_string($xml); 205 } 206 207 /** 208 * Get issues by filter 209 * 210 * @param string $filter Filter in YouTrack query language 211 * @return SimpleXMLElement Issues data 212 */ 213 function getIssues($filter) { 214 $this->login(); 215 $xml = $this->request( 216 "GET", 217 "/rest/issue/", 218 array( 219 'filter' => $filter, 220 'max' => 100 // TODO: set max via config? 221 ) 222 ); 223 $this->logout(); 224 return simplexml_load_string($xml); 225 } 226 227 /** 228 * Get link to issue 229 * 230 * @param string $id ID of issue 231 * @return string 232 */ 233 function getIssueUrl($id) { 234 if (empty($id)) { 235 return ''; 236 } 237 return $this->getBaseUrl().'/issue/'.$id; 238 } 239 240 /** 241 * Render table of issues 242 * 243 * @param Doku_Renderer $R Renderer 244 * @param array $issues Issues with their relevant values 245 * @param array $cols Columns to show 246 */ 247 function renderIssueTable(Doku_Renderer &$R, $issues, $cols) { 248 $R->table_open(count($cols)); 249 $R->tablethead_open(); 250 $R->tablerow_open(); 251 foreach($cols as $col) { 252 $R->tableheader_open(); 253 $R->cdata($col); 254 $R->tableheader_close(); 255 } 256 $R->tablerow_close(); 257 $R->tablethead_close(); 258 if (method_exists($R, 'tabletbody_open')) { 259 $R->tabletbody_open(); 260 } 261 262 foreach ($issues as $issue) { 263 $R->tablerow_open(); 264 foreach($cols as $col) { 265 $R->tablecell_open(); 266 if ($col == 'ID') { 267 $R->externallink($this->getIssueUrl($issue[$col]), $issue[$col]); 268 } else { 269 $R->cdata($issue[$col]); 270 } 271 $R->tablecell_close(); 272 } 273 $R->tablerow_close(); 274 } 275 276 if (method_exists($R, 'tabletbody_close')) { 277 $R->tabletbody_close(); 278 } 279 $R->table_close(); 280 } 281 282} 283// vim:ts=4:sw=4:et: 284