Index: trunk/extensions/BugzillaReports/BugzillaQuery.php |
— | — | @@ -0,0 +1,872 @@ |
| 2 | +<?php |
| 3 | +require_once("$IP/extensions/BugzillaReports/BSQLQuery.php"); |
| 4 | +/** |
| 5 | + * A bugzilla query |
| 6 | + */ |
| 7 | +class BugzillaQuery extends BSQLQuery { |
| 8 | + var $supportedParameters=array ( |
| 9 | + 'alias' => 'field', |
| 10 | + 'bar' => 'column', |
| 11 | + 'blocks' => 'field-depends', |
| 12 | + 'cc' => 'field', |
| 13 | + 'columns' => 'columns', |
| 14 | + 'component' => 'field', |
| 15 | + 'deadline' => 'field-date', |
| 16 | + 'depends' => 'field-depends', |
| 17 | + 'detailsrow' => 'columns', |
| 18 | + 'detailsrowprepend' => 'free', |
| 19 | + 'flag' => 'field-special', |
| 20 | + 'format' => 'value', # table (default) or list |
| 21 | + 'from' => 'field', |
| 22 | + 'group' => 'sort', |
| 23 | + 'groupformat' => 'value', |
| 24 | + 'heading' => 'free', |
| 25 | + 'headers' => 'value', |
| 26 | + 'id' => 'field', |
| 27 | + 'lastcomment' => 'boolean', |
| 28 | + 'maxrows' => 'value', |
| 29 | + 'maxrowsbar' => 'value', |
| 30 | + 'milestone' => 'field', |
| 31 | + 'modified' => 'field-date', |
| 32 | + 'nameformat' => 'value', # real (default),tla or login |
| 33 | + 'order' => 'value', |
| 34 | + 'priority' => 'field', |
| 35 | + 'product' => 'field', |
| 36 | + 'quickflag' => 'value', |
| 37 | + 'noresultsmessage' => 'free', |
| 38 | + 'search' => 'field-text', |
| 39 | + 'severity' => 'field', |
| 40 | + 'sort' => 'sort', |
| 41 | + 'status' => 'field', |
| 42 | + 'to' => 'field', |
| 43 | + 'url' => 'field', |
| 44 | + 'version' => 'field', |
| 45 | + 'votes' => 'field-int', |
| 46 | + ); |
| 47 | + var $defaultParameters=array ( |
| 48 | + 'columns' => 'id,priority,status,severity,version,product,summary,url', |
| 49 | + 'noresultsmessage' => 'no bugzilla tickets were found', |
| 50 | + 'order' => 'asc', |
| 51 | + 'status' => '!CLOSED', |
| 52 | + 'sort' => 'priority' |
| 53 | + ); |
| 54 | + var $columnName=array ( |
| 55 | + 'alias' => 'Alias', |
| 56 | + 'blocks' => 'Blocks', |
| 57 | + 'component' => 'Component', |
| 58 | + 'cc' => 'CC', |
| 59 | + 'deadline' => 'Deadline', |
| 60 | + 'depends' => 'Depends', |
| 61 | + 'flag' => 'Flagged For', |
| 62 | + 'flagdate' => 'Flag Date', |
| 63 | + 'flagfrom' => 'Flagged By', |
| 64 | + 'flagname' => 'Flag', |
| 65 | + 'from' => 'Requester', |
| 66 | + 'id' => 'ID', |
| 67 | + 'severity' => 'Severity', |
| 68 | + 'status' => 'Status', |
| 69 | + 'milestone' => 'Milestone', |
| 70 | + 'modified' => 'Modified', |
| 71 | + 'product' => 'Product', |
| 72 | + 'priority' => 'P', |
| 73 | + 'summary' => 'Summary', |
| 74 | + 'to' => 'Assignee', |
| 75 | + 'url' => ' ', |
| 76 | + 'version' => 'Version', |
| 77 | + 'votes' => 'Votes', |
| 78 | + ); |
| 79 | + # Fields and their mapping to the value in the results sets |
| 80 | + var $fieldMapping=array ( |
| 81 | + 'cc' => 'cc', |
| 82 | + 'from' => 'raisedby', |
| 83 | + 'to' => 'assignedto', |
| 84 | + ); |
| 85 | + var $fieldSQLColumn=array ( |
| 86 | + 'cc' => 'ccprofiles.login_name', |
| 87 | + 'component' => 'components.name', |
| 88 | + 'from' => 'reporterprofiles.login_name', |
| 89 | + 'milestone' => 'target_milestone', |
| 90 | + 'modified' => 'lastdiffed', |
| 91 | + 'product' => 'products.name', |
| 92 | + 'severity' => 'bug_severity', |
| 93 | + 'status' => 'bug_status', |
| 94 | + 'to' => 'profiles.login_name', |
| 95 | + 'url' => 'bug_file_loc', |
| 96 | + ); |
| 97 | + var $fieldDefaultSort=array ( |
| 98 | + 'modified' => 'desc', |
| 99 | + 'votes' => 'desc' |
| 100 | + ); |
| 101 | + var $formats=array ( |
| 102 | + 'alias' => 'id', |
| 103 | + 'blocks' => 'id', |
| 104 | + 'cc' => 'name', |
| 105 | + 'deadline' => 'date', |
| 106 | + 'depends' => 'id', |
| 107 | + 'flagdate' => 'date', |
| 108 | + 'flagfrom' => 'name', |
| 109 | + 'from' => 'name', |
| 110 | + 'id' => 'id', |
| 111 | + 'modified' => 'date', |
| 112 | + 'to' => 'name', |
| 113 | + 'url' => 'url', |
| 114 | + ); |
| 115 | + var $fieldValues=array ( |
| 116 | + 'priority' => 'P1,P2,P3,P4,P5', |
| 117 | + 'status' => 'ASSIGNED,NEW,RESOLVED,CLOSED', |
| 118 | + 'severity' => 'blocker,critical,major,normal,minor,trivial,enhancement' |
| 119 | + ); |
| 120 | + var $sortMapping=array ( |
| 121 | + 'deadline' => "COALESCE(deadline, '2100-01-01')", |
| 122 | + 'milestone' => "COALESCE(NULLIF(milestone,'---'),'XXXXX')", |
| 123 | + 'id' => 'bugs.bug_id' |
| 124 | + ); |
| 125 | + var $dependsRowColumns=array ( |
| 126 | + 'depends' => "block", |
| 127 | + 'dependsto' => "title", # Output in the title |
| 128 | + 'dependsstatus' => "title", # Output in the title |
| 129 | + 'dependssummary' => "block", |
| 130 | + ); |
| 131 | + var $blocksRowColumns=array ( |
| 132 | + 'blocks' => "block", |
| 133 | + 'blocksto' => "title", # Output in the title |
| 134 | + 'blocksstatus' => "title", |
| 135 | + 'blockssummary' => "block" |
| 136 | + ); |
| 137 | + # |
| 138 | + # Title for a given value rendering |
| 139 | + # |
| 140 | + var $valueTitle=array ( |
| 141 | + 'alias' => "id,alias", |
| 142 | + 'blocks' => "blocks,blocksalias", |
| 143 | + 'depends' => "depends,dependsalias", |
| 144 | + 'id' => "id,alias" |
| 145 | + ); |
| 146 | + |
| 147 | + private $output; |
| 148 | + |
| 149 | + # WHY IS THIS NEEDED? |
| 150 | + #var $dbhost="localhost"; |
| 151 | + |
| 152 | + # |
| 153 | + # Parse in a context object which implements the following |
| 154 | + # |
| 155 | + # Public Variables |
| 156 | + # - debug, bzserver, interwiki, |
| 157 | + # - database, host, dbuser, password; |
| 158 | + # |
| 159 | + # Functions |
| 160 | + # - debug |
| 161 | + # - warn, |
| 162 | + # - getErrorMessage |
| 163 | + # |
| 164 | + function BugzillaQuery( $context ) { |
| 165 | + $this->setContext($context); |
| 166 | + } |
| 167 | + |
| 168 | + # |
| 169 | + # Get rendering formats |
| 170 | + # |
| 171 | + function getFormats() { |
| 172 | + return $this->formats; |
| 173 | + } |
| 174 | + |
| 175 | + # |
| 176 | + # Get default priority |
| 177 | + # |
| 178 | + function getDefaultSort() { |
| 179 | + return $this->defaultSort; |
| 180 | + } |
| 181 | + |
| 182 | + |
| 183 | + # |
| 184 | + # Render the results |
| 185 | + # |
| 186 | + function render() { |
| 187 | + $this->context->debug && |
| 188 | + $this->context->debug("Rendering BugzillaQuery"); |
| 189 | + |
| 190 | + $where=""; |
| 191 | + # |
| 192 | + # If ID is set then don't do any other query since we're being pretty |
| 193 | + # Specific |
| 194 | + # |
| 195 | + |
| 196 | + if ($this->get('id')) { |
| 197 | + $where.=$this->getWhereClause($this->get('id'),"bug_id"); |
| 198 | + } else { |
| 199 | + # |
| 200 | + # Now process other fields and make sure we have SQL and implicit |
| 201 | + # usage built up |
| 202 | + # |
| 203 | + foreach (array_keys($this->supportedParameters) as $column) { |
| 204 | + if ($this->get($column)) { |
| 205 | + switch ($this->supportedParameters[$column]) { |
| 206 | + case "field" : |
| 207 | + case "field-int" : |
| 208 | + if (preg_match("/[,!+]/",$this->get($column))) { |
| 209 | + $this->implictlyAddColumn($column); |
| 210 | + } else { |
| 211 | + $this->implictlyRemoveColumn($column); |
| 212 | + } |
| 213 | + case "field-depends" : |
| 214 | + $sqlColumn=$column; |
| 215 | + if (array_key_exists($column, |
| 216 | + $this->fieldSQLColumn)) { |
| 217 | + $sqlColumn=$this->fieldSQLColumn[$column]; |
| 218 | + } |
| 219 | + switch ($this->supportedParameters[$column]) { |
| 220 | + case "field" : |
| 221 | + $where.=$this-> |
| 222 | + getWhereClause($this->get($column), |
| 223 | + $sqlColumn); |
| 224 | + break; |
| 225 | + case "field-int" : |
| 226 | + $where.=$this-> |
| 227 | + getIntWhereClause($this->get($column), |
| 228 | + $sqlColumn); |
| 229 | + break; |
| 230 | + } |
| 231 | + $this->requireField($column); |
| 232 | + if (array_key_exists($column, |
| 233 | + $this->fieldDefaultSort)) { |
| 234 | + $this->setImplicit('sort',"$column"); |
| 235 | + $this->setImplicit('order', |
| 236 | + $this->fieldDefaultSort[$column]); |
| 237 | + } |
| 238 | + break; |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + if ($this->get('format') == "list") { |
| 243 | + $this->requireField("to"); |
| 244 | + $this->requireField("deadline"); |
| 245 | + } |
| 246 | + if ($this->get('deadline')) { |
| 247 | + $where.=$this->getDateWhereClause($this->get('deadline'),"deadline"); |
| 248 | + $this->requireField("deadline"); |
| 249 | + $this->setImplicit('sort',"deadline"); |
| 250 | + if (preg_match("/[,!+]/",$this->get("deadline"))) { |
| 251 | + $this->implictlyAddColumn("deadline"); |
| 252 | + } else { |
| 253 | + $this->implictlyRemoveColumn("deadline"); |
| 254 | + } |
| 255 | + |
| 256 | + } |
| 257 | + if ($this->get('flag')) { |
| 258 | + $this->requireField("flag"); |
| 259 | + $this->implictlyAddColumn("flagfrom"); |
| 260 | + $this->implictlyAddColumn("flagname"); |
| 261 | + $this->implictlyAddColumn("flagdate"); |
| 262 | + } |
| 263 | + if ($this->get('lastcomment')) { |
| 264 | + $this->requireField("lastcomment"); |
| 265 | + } |
| 266 | + if ($this->get('modified')) { |
| 267 | + $where.= |
| 268 | + $this->getDateWhereClause($this->get('modified'),"lastdiffed"); |
| 269 | + $this->requireField("modified"); |
| 270 | + $this->setImplicit('sort',"modified"); |
| 271 | + $this->setImplicit('order',"desc"); |
| 272 | + preg_match("/[+]/",$this->get('modified')) && |
| 273 | + $this->implictlyAddColumn("modified"); |
| 274 | + } |
| 275 | + if ($this->get('search')) { |
| 276 | + $where.=" and short_desc like '%".$this->search.="%'"; |
| 277 | + } |
| 278 | + } |
| 279 | + |
| 280 | + # |
| 281 | + # Quick flag enabled by default |
| 282 | + # |
| 283 | + $this->requireField("quickflag"); |
| 284 | + |
| 285 | + # |
| 286 | + # Alias enabled by default |
| 287 | + # |
| 288 | + $this->requireField("alias"); |
| 289 | + |
| 290 | + # |
| 291 | + # Prepare the query; |
| 292 | + # |
| 293 | + $this->preSQLGenerate(); |
| 294 | + |
| 295 | + $this->context->debug && |
| 296 | + $this->context->debug("Columns required are " |
| 297 | + .join(",",array_keys($this->fieldsRequired))); |
| 298 | + $sql="SELECT *,bugs.bug_id as id"; |
| 299 | + if ($this->isRequired("blocks")) { |
| 300 | + $sql.=", blockstab.blocks as blocks, blockstab.blocksalias as blocksalias, blockstab.blockssummary as blockssummary,blockstab.blocksstatus as blocksstatus, blockstab.blockspriority as blockspriority, blockstab.realname as blocksto"; |
| 301 | + } |
| 302 | + if ($this->isRequired("cc")) { |
| 303 | + if ($this->get('nameformat')=='login') { |
| 304 | + $sql.=", ccprofiles.login_name as cc"; |
| 305 | + } else { |
| 306 | + $sql.=", ccprofiles.realname as cc"; |
| 307 | + } |
| 308 | + } |
| 309 | + if ($this->isRequired("component")) { |
| 310 | + $sql.=", components.name as component"; |
| 311 | + } |
| 312 | + if ($this->isRequired("depends")) { |
| 313 | + $sql.=", dependstab.depends as depends, dependstab.dependsalias as dependsalias, dependstab.dependssummary as dependssummary,dependstab.dependsstatus as dependsstatus, dependstab.dependspriority as dependspriority, dependstab.realname as dependsto"; |
| 314 | + } |
| 315 | + if ($this->isRequired("flag")) { |
| 316 | + if ($this->get('nameformat')=='login') { |
| 317 | + $sql.=", flagprofiles.flagfrom_login as flagfrom"; |
| 318 | + $sql.=", flagprofiles.flag_login as flag"; |
| 319 | + } else { |
| 320 | + $sql.=", flagprofiles.flagfrom_realname as flagfrom"; |
| 321 | + $sql.=", flagprofiles.flag_realname as flag"; |
| 322 | + } |
| 323 | + $sql.=", flagprofiles.flagname as flagname"; |
| 324 | + $sql.=", flagprofiles.flagdate as flagdate"; |
| 325 | + } else if ($this->isRequired("quickflag")) { |
| 326 | + $sql.=", quickflag.flagdate as flagdate"; |
| 327 | + } |
| 328 | + if ($this->isRequired("from")) { |
| 329 | + if ($this->get('nameformat')=='login') { |
| 330 | + $sql.=", reporterprofiles.login_name as raisedby"; |
| 331 | + } else { |
| 332 | + $sql.=", reporterprofiles.realname as raisedby"; |
| 333 | + } |
| 334 | + } |
| 335 | + if ($this->isRequired("milestone")) { |
| 336 | + $sql.=", target_milestone as milestone"; |
| 337 | + } |
| 338 | + if ($this->isRequired("modified")) { |
| 339 | + $sql.=", lastdiffed as modified"; |
| 340 | + } |
| 341 | + if ($this->isRequired("product")) { |
| 342 | + $sql.=", products.name as product"; |
| 343 | + } |
| 344 | + if ($this->isRequired("severity")) { |
| 345 | + $sql.=", bug_severity as severity"; |
| 346 | + } |
| 347 | + if ($this->isRequired("status")) { |
| 348 | + $sql.=", bug_status as status"; |
| 349 | + } |
| 350 | + if ($this->isRequired("summary")) { |
| 351 | + $sql.=", short_desc as summary"; |
| 352 | + } |
| 353 | + if ($this->isRequired("to")) { |
| 354 | + if ($this->get('nameformat')=='login') { |
| 355 | + $sql.=", profiles.login_name as assignedto"; |
| 356 | + } else { |
| 357 | + $sql.=", profiles.realname as assignedto"; |
| 358 | + } |
| 359 | + } |
| 360 | + if ($this->isRequired("url")) { |
| 361 | + $sql.=", bug_file_loc as url"; |
| 362 | + } |
| 363 | + $sql.=" FROM ".$this->context->database. |
| 364 | + ".bugs"; |
| 365 | + if ($this->isRequired("blocks")) { |
| 366 | + $sql.=" LEFT JOIN (SELECT dependson,blocked as blocks, blockedbugs.alias as blocksalias, blockedbugs.short_desc as blockssummary, blockedbugs.bug_status as blocksstatus, blockedbugs.priority as blockspriority,login_name,realname from ". |
| 367 | + $this->context->database. |
| 368 | + ".dependencies" |
| 369 | + ." INNER JOIN ".$this->context->database.".bugs as blockedbugs ON dependencies.blocked=blockedbugs.bug_id" |
| 370 | + ." INNER JOIN ". |
| 371 | + $this->context->database. |
| 372 | + ".profiles ON blockedbugs.assigned_to=profiles.userid" |
| 373 | + ." where blockedbugs.bug_status <> 'CLOSED' order by blockedbugs.priority) as blockstab ON blockstab.dependson=bugs.bug_id"; |
| 374 | + } |
| 375 | + if ($this->isRequired("component")) { |
| 376 | + $sql.=" LEFT JOIN ". |
| 377 | + $this->context->database. |
| 378 | + ".components on bugs.component_id=components.id"; |
| 379 | + } |
| 380 | + if ($this->isRequired("cc")) { |
| 381 | + $sql.=" INNER JOIN (SELECT bug_id,login_name,realname from ". |
| 382 | + $this->context->database. |
| 383 | + ".cc INNER JOIN ". |
| 384 | + $this->context->database. |
| 385 | + ".profiles ON cc.who=profiles.userid"; |
| 386 | + if ($this->get('cc')) { |
| 387 | + $sql.=$this |
| 388 | + ->getWhereClause($this->get('cc'),"profiles.login_name"); |
| 389 | + } |
| 390 | + $sql.=") as ". |
| 391 | + "ccprofiles on ccprofiles.bug_id=bugs.bug_id"; |
| 392 | + } |
| 393 | + if ($this->isRequired("depends")) { |
| 394 | + $sql.=" LEFT JOIN (SELECT blocked,dependson as depends, dependsonbugs.alias as dependsalias, dependsonbugs.short_desc as dependssummary, dependsonbugs.bug_status as dependsstatus, dependsonbugs.priority as dependspriority, login_name, realname from ". |
| 395 | + $this->context->database. |
| 396 | + ".dependencies" |
| 397 | + ." INNER JOIN ".$this->context->database.".bugs as dependsonbugs ON dependencies.dependson=dependsonbugs.bug_id" |
| 398 | + ." INNER JOIN ". |
| 399 | + $this->context->database. |
| 400 | + ".profiles ON dependsonbugs.assigned_to=profiles.userid" |
| 401 | + ." where dependsonbugs.bug_status <> 'CLOSED' order by dependsonbugs.priority) as dependstab ON dependstab.blocked=bugs.bug_id"; |
| 402 | + } |
| 403 | + if ($this->isRequired("flag")) { |
| 404 | + $sql.=" INNER JOIN (SELECT bug_id,creation_date as flagdate,flagsto.login_name as flag_login,flagsto.realname as flag_realname,flagsfrom.login_name as flagfrom_login, flagsfrom.realname as flagfrom_realname,flagtypes.name as flagname from ". |
| 405 | + $this->context->database. |
| 406 | + ".flags INNER JOIN ". |
| 407 | + $this->context->database. |
| 408 | + ".flagtypes ON flags.type_id=flagtypes.id INNER JOIN ". |
| 409 | + $this->context->database. |
| 410 | + ".profiles as flagsto ON flags.requestee_id=flagsto.userid INNER JOIN ". |
| 411 | + $this->context->database. |
| 412 | + ".profiles as flagsfrom ON flags.setter_id=flagsfrom.userid where status='?'"; |
| 413 | + if ($this->get('flag')) { |
| 414 | + $sql.=$this |
| 415 | + ->getWhereClause($this->get('flag'), |
| 416 | + "flagsto.login_name"); |
| 417 | + } |
| 418 | + $sql.=") as ". |
| 419 | + "flagprofiles on flagprofiles.bug_id=bugs.bug_id"; |
| 420 | + } else if ($this->isRequired("quickflag")) { |
| 421 | + $sql.=" LEFT JOIN (SELECT bug_id as quickflagbugid, MAX(creation_date) as flagdate from ". |
| 422 | + $this->context->database. |
| 423 | + ".flags where status='?' group by quickflagbugid) as ". |
| 424 | + "quickflag on quickflag.quickflagbugid=bugs.bug_id"; |
| 425 | + } |
| 426 | + if ($this->isRequired("from")) { |
| 427 | + $sql.=" LEFT JOIN ". |
| 428 | + $this->context->database. |
| 429 | + ".profiles as reporterprofiles on bugs.reporter=reporterprofiles.userid"; |
| 430 | + } |
| 431 | + if ($this->isRequired("lastcomment")) { |
| 432 | + $sql.=" LEFT JOIN (SELECT MAX(longdescs.comment_id) as sub_comment_id, ". |
| 433 | + "longdescs.bug_id as sub_bug_id FROM ".$this->context->database. |
| 434 | + ".longdescs GROUP BY longdescs.bug_id) ". |
| 435 | + "descs ON bugs.bug_id=descs.sub_bug_id LEFT JOIN longdescs ON ". |
| 436 | + "longdescs.comment_id=sub_comment_id"; |
| 437 | + } |
| 438 | + if ($this->isRequired("product")) { |
| 439 | + $sql.=" LEFT JOIN ". |
| 440 | + $this->context->database. |
| 441 | + ".products on bugs.product_id=products.id"; |
| 442 | + } |
| 443 | + if ($this->isRequired("to")) { |
| 444 | + $sql.=" LEFT JOIN ". |
| 445 | + $this->context->database. |
| 446 | + ".profiles on bugs.assigned_to=profiles.userid"; |
| 447 | + } |
| 448 | + $sql.=" where 1=1 ".$where." order by ". |
| 449 | + $this->getMappedSort()." ".$this->getOrder(); |
| 450 | + $sql.=";"; |
| 451 | + $this->context->debug && $this->context->debug("SQL : ".$sql); |
| 452 | + |
| 453 | + $link = mysql_connect($this->context->host, |
| 454 | + $this->context->dbuser, $this->context->password); |
| 455 | + |
| 456 | + if (!$link) |
| 457 | + return ""; $this->context->getErrorMessage('bReport_noconnection', |
| 458 | + $this->context->dbuser,$this->context->host,mysql_error()); |
| 459 | + |
| 460 | + if (!mysql_select_db($this->context->database, $link)) { |
| 461 | + mysql_close($link); |
| 462 | + return $this->context->getErrorMessage('bReport_nodb'); |
| 463 | + } |
| 464 | + $result = mysql_query($sql, $link); |
| 465 | + |
| 466 | + # |
| 467 | + # Check that the record set is open |
| 468 | + # |
| 469 | + if ($result) { |
| 470 | + if (mysql_num_rows($result) > 0) { |
| 471 | + $output=$this->renderHTML($result); |
| 472 | + } else { |
| 473 | + $output=$this->renderNoResultsHTML(); |
| 474 | + } |
| 475 | + |
| 476 | + mysql_free_result($result); |
| 477 | + } else { |
| 478 | + return $this->context->getErrorMessage('bReport_sqlerror',$sql); |
| 479 | + } |
| 480 | + |
| 481 | + mysql_close($link); |
| 482 | + |
| 483 | + if ($this->context->debug) { |
| 484 | + $output.="<div>SQL = ".$sql."</div>"; |
| 485 | + } |
| 486 | + return $output; |
| 487 | + } |
| 488 | + |
| 489 | + /** |
| 490 | + * Extract options from a blob of text |
| 491 | + * |
| 492 | + * @param recordset |
| 493 | + * @return rendered markup |
| 494 | + */ |
| 495 | + public function renderHTML($result) { |
| 496 | + $this->output = ""; |
| 497 | + $nRows=mysql_num_rows($result); |
| 498 | + |
| 499 | + if ($this->get('heading')) { |
| 500 | + $this->output.="<h1>".$this->get('heading')."</h1>"; |
| 501 | + } |
| 502 | + # Table start |
| 503 | + $this->output .= "<table class=\"bugzilla"; |
| 504 | + if ($this->get('bar')) { |
| 505 | + $this->output.=" bz_bar"; |
| 506 | + } |
| 507 | + $this->output.="\">"; |
| 508 | + |
| 509 | + # Initialise details row logic |
| 510 | + $detailsRowColumns=array(); |
| 511 | + $arrayOfDetailRowColumns=explode(",",$this->get('detailsrow')); |
| 512 | + foreach ($arrayOfDetailRowColumns as $detailRowColumn) { |
| 513 | + $detailsRowColumns[$detailRowColumn]=1; |
| 514 | + } |
| 515 | + $this->numberOfMainRowColumns=0; |
| 516 | + if ($this->get('bar')) { |
| 517 | + $this->numberOfMainRowColumns=2; |
| 518 | + } else { |
| 519 | + foreach ($this->getColumns() as $column) { |
| 520 | + if (!array_key_exists($column,$detailsRowColumns)) { |
| 521 | + $this->numberOfMainRowColumns++; |
| 522 | + } |
| 523 | + } |
| 524 | + } |
| 525 | + |
| 526 | + # Display table header |
| 527 | + if (!$this->get('bar') && |
| 528 | + $this->get('format')!='list' && |
| 529 | + $this->get('headers')!="hide") { |
| 530 | + $this->output .= "<tr>"; |
| 531 | + foreach ($this->getColumns() as $column) { |
| 532 | + $name=trim($column); |
| 533 | + if (array_key_exists($column,$this->columnName)) { |
| 534 | + $name=$this->columnName[$column]; |
| 535 | + } |
| 536 | + if (($column=="summary") && ($nRows > 1)) { |
| 537 | + $name.=" ($nRows tasks)"; |
| 538 | + } |
| 539 | + $this->output .= "<th><b>$name</b></th>"; |
| 540 | + } |
| 541 | + $this->output .= "</tr>"; |
| 542 | + } |
| 543 | + |
| 544 | + # Create Table Data Rows |
| 545 | + $even=true; |
| 546 | + $count=0; |
| 547 | + $localMaxRows=$this->getMaxRows(); |
| 548 | + $localMaxRowsForBarChart=$this->getMaxRowsForBarChart(); |
| 549 | + $groupValue=""; |
| 550 | + $groupTotal=0; |
| 551 | + $groups; |
| 552 | + $doGrouping=0; |
| 553 | + if ($this->getGroup()) { |
| 554 | + $doGrouping=1; |
| 555 | + $groups=split(",",$this->getGroup()); |
| 556 | + } |
| 557 | + $barArray=$this->getBarArray(); |
| 558 | + $currentId=0; |
| 559 | + $currentBlocksId=0; |
| 560 | + $currentDependsId=0; |
| 561 | + while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) { |
| 562 | + # |
| 563 | + # Add group heading |
| 564 | + # |
| 565 | + if ($doGrouping) { |
| 566 | + $groupTotal++; |
| 567 | + $newGroupValue=$this->formatForHeading( |
| 568 | + $line[$this->mapField( |
| 569 | + $groups[0])],$groups[0]); |
| 570 | + if ($newGroupValue != $groupValue) { |
| 571 | + $colspan=$this->numberOfMainRowColumns; |
| 572 | + if ($this->get('bar')) { |
| 573 | + if ($groupValue) { |
| 574 | + $this->renderBar($barArray); |
| 575 | + foreach (array_keys($barArray) as $key) { |
| 576 | + $barArray[$key]=0; |
| 577 | + } |
| 578 | + } |
| 579 | + $colspan-=1; |
| 580 | + } |
| 581 | + $groupValue=$newGroupValue; |
| 582 | + $this->output.="<tr class=\"bz_group\"><td colspan=\"" |
| 583 | + .$colspan. |
| 584 | + "\">".$groupValue. |
| 585 | + "</td>"; |
| 586 | + if ($this->get('bar')) { |
| 587 | + $this->output.="<td class=\"total\"> </td>"; |
| 588 | + } |
| 589 | + $this->output.="</tr>"; |
| 590 | + $groupTotal=0; |
| 591 | + } |
| 592 | + } |
| 593 | + ++$count; |
| 594 | + # Bar counter |
| 595 | + if ($this->get('bar')) { |
| 596 | + if ($count > $localMaxRowsForBarChart) { |
| 597 | + $this->context->warn("Bar count greater than max allowed ". |
| 598 | + "$count > $localMaxRowsForBarChart"); |
| 599 | + break; |
| 600 | + } |
| 601 | + $barArray[$line[$this->mapField($this->get('bar'))]]+=1; |
| 602 | + continue; |
| 603 | + } |
| 604 | + # Safety check break out of loop if there's too much |
| 605 | + if ($count > $localMaxRows) { |
| 606 | + $this->context->warn("Report truncated - count greater than max allowed ". |
| 607 | + "$count > $localMaxRows"); |
| 608 | + break; |
| 609 | + } |
| 610 | + # |
| 611 | + # Only render the row if the ID has changed from previous row |
| 612 | + # to support LEFT JOINS |
| 613 | + # |
| 614 | + if ($line["id"] != $currentId) { |
| 615 | + $currentId=$line["id"]; |
| 616 | + $firstcolumn=true; |
| 617 | + $even=!$even; |
| 618 | + $class="bz_bug ".$line["priority"]." ".$line["bug_status"]." "; |
| 619 | + if ($even) { |
| 620 | + $class.="bz_row_even"; |
| 621 | + } else { |
| 622 | + $class.="bz_row_odd"; |
| 623 | + } |
| 624 | + $this->output .= "<tr class=\"".$class."\">"; |
| 625 | + if ($this->get('format') == "list") { |
| 626 | + $this->output.="<td class=\"bz_list\" colspan=\"". |
| 627 | + $this->numberOfMainRowColumns."\">"; |
| 628 | + $this->output.= |
| 629 | + "[".$this->format($line[$this->mapField('to')], |
| 630 | + 'to')."] "; |
| 631 | + $deadline=$line[$this->mapField('deadline')]; |
| 632 | + if ($deadline) { |
| 633 | + $this->output.= |
| 634 | + "(".$this->format($deadline,"deadline").") "; |
| 635 | + } |
| 636 | + $this->output.= |
| 637 | + $this->format($line[$this->mapField('summary')], |
| 638 | + 'summary')." (#". |
| 639 | + $this->format($line[$this->mapField('id')], |
| 640 | + 'id').")"; |
| 641 | + $this->output.="</td>"; |
| 642 | + } else { |
| 643 | + foreach ($this->getColumns() as $column) { |
| 644 | + $dbColumn=$this->mapField($column); |
| 645 | + if (!$this->get('detailsrow') |
| 646 | + or !array_key_exists($dbColumn, |
| 647 | + $detailsRowColumns)) { |
| 648 | + $title=$this->getValueTitle($line,$column); |
| 649 | + $value=$this->format($line[$dbColumn],$column,$title); |
| 650 | + $this->output.="<td"; |
| 651 | + if ($title) { |
| 652 | + $this->output.=" title=\"$title\""; |
| 653 | + } |
| 654 | + $this->output.=">$value"; |
| 655 | + if ($firstcolumn) { |
| 656 | + $firstcolumn=false; |
| 657 | + /** |
| 658 | + * Start with a carriage return so that comments starting with |
| 659 | + * list characters, e.g. *, # render in wiki style |
| 660 | + */ |
| 661 | + if ($this->get('lastcomment')) { |
| 662 | + $lastcomment=trim(htmlentities($line["thetext"], ENT_QUOTES, 'UTF-8')); |
| 663 | + if (strlen($lastcomment) > 0) { |
| 664 | + $this->output.="*<div class=\"bz_comment\"><span class=\"bug_id\">". |
| 665 | + $line["bug_id"]."</span>\n".$lastcomment. |
| 666 | + "</div>"; |
| 667 | + } |
| 668 | + } |
| 669 | + # |
| 670 | + # Render quick flag |
| 671 | + # |
| 672 | + if ($this->isRequired('quickflag')) { |
| 673 | + if ($line["flagdate"]) { |
| 674 | + $this->output.="<span class=\"flag\" title=\"Flag : ". |
| 675 | + $line["flagdate"]."\">?</span>"; |
| 676 | + } |
| 677 | + } |
| 678 | + } |
| 679 | + $this->output.="</td>"; |
| 680 | + } |
| 681 | + } |
| 682 | + } |
| 683 | + $this->output .= "</tr>"; |
| 684 | + } |
| 685 | + if ($this->get('detailsrow')) { |
| 686 | + $this->renderDetailsRow($detailsRowColumns,$line, |
| 687 | + $this->get('detailsrowprepend')); |
| 688 | + } |
| 689 | + # |
| 690 | + # We have LEFT JOIN so we need to ignore repeats |
| 691 | + # |
| 692 | + if ($this->get('blocks') && |
| 693 | + ($line["blocks"] != $currentBlocksId)) { |
| 694 | + $currentBlocksId=$line["blocks"]; |
| 695 | + $this->renderDetailsRow( |
| 696 | + $this->blocksRowColumns,$line,"⇒ "); |
| 697 | + } |
| 698 | + # |
| 699 | + # We have LEFT JOIN so we need to ignore repeats |
| 700 | + # |
| 701 | + if ($this->get('depends') && |
| 702 | + ($line["depends"] != $currentDependsId)) { |
| 703 | + $currentDependsId=$line["depends"]; |
| 704 | + $this->renderDetailsRow( |
| 705 | + $this->dependsRowColumns,$line,"⇐ "); |
| 706 | + } |
| 707 | + } |
| 708 | + |
| 709 | + # |
| 710 | + # Display bar |
| 711 | + # |
| 712 | + if ($this->get('bar')) { |
| 713 | + $this->renderBar($barArray); |
| 714 | + $this->output.="<tr class=\"bz_bar_total\"><td colspan=\"" |
| 715 | + .($this->numberOfMainRowColumns-1). |
| 716 | + "\">total</td><td class=\"total\">$nRows</td></tr>"; |
| 717 | + } |
| 718 | + |
| 719 | + # Table end |
| 720 | + $this->output .= "</table>"; |
| 721 | + |
| 722 | + return $this->output; |
| 723 | + } |
| 724 | + |
| 725 | + # |
| 726 | + # Render a details row |
| 727 | + # |
| 728 | + private function renderDetailsRow($array,$line,$prepend) { |
| 729 | + $details=""; |
| 730 | + $title=""; |
| 731 | + foreach (array_keys($array) as $column) { |
| 732 | + $dbColumn=$this->mapField($column); |
| 733 | + if ($line[$column]) { |
| 734 | + $valueTitle=$this->getValueTitle($line,$column); |
| 735 | + if ($valueTitle) { |
| 736 | + $this->context->debug && |
| 737 | + $this->context |
| 738 | + ->debug("Setting title for $column to $valueTitle"); |
| 739 | + } |
| 740 | + switch ($array[$column]) { |
| 741 | + case "title" : |
| 742 | + $title.=$this |
| 743 | + ->format($line[$dbColumn], |
| 744 | + $column,$valueTitle)." "; |
| 745 | + break; |
| 746 | + default : |
| 747 | + $details.=$this |
| 748 | + ->format($line[$dbColumn], |
| 749 | + $column,$valueTitle)." "; |
| 750 | + } |
| 751 | + } |
| 752 | + } |
| 753 | + if (trim($details)) { |
| 754 | + $this->output.="<tr class=\"bz_details\"><td title=\"$title\" class=\"bz_details\" colspan=\"". |
| 755 | + $this->numberOfMainRowColumns."\">"; |
| 756 | + $this->output.=$prepend.$details; |
| 757 | + $this->ouput.="</td></tr>"; |
| 758 | + } |
| 759 | + } |
| 760 | + |
| 761 | + private function renderBar($barArray) { |
| 762 | + $total=0; |
| 763 | + $nonZeroKeyCount=0; |
| 764 | + $arrayKeys=array_keys($barArray); |
| 765 | + foreach ($arrayKeys as $key) { |
| 766 | + $total+=$barArray[$key]; |
| 767 | + $barArray[$key]>0 && $nonZeroKeyCount++; |
| 768 | + } |
| 769 | + $classColourCode="few"; |
| 770 | + $keyCount=count($arrayKeys); |
| 771 | + if ($keyCount > 6) { |
| 772 | + $classColourCode="some"; |
| 773 | + } else if ($keyCount > 10) { |
| 774 | + $classColourCode="many"; |
| 775 | + } |
| 776 | + $this->output.="<tr class=\"bz_bar $classColourCode\"><td colspan=\"" |
| 777 | + .($this->numberOfMainRowColumns-1). |
| 778 | + "\">"; |
| 779 | + $i=0; |
| 780 | + $iNonZero=0; |
| 781 | + sort($arrayKeys); |
| 782 | + $rowTotal=0; |
| 783 | + $rowWidthTotal=0; |
| 784 | + foreach ($arrayKeys as $key) { |
| 785 | + $count=$barArray[$key]; |
| 786 | + $i++; |
| 787 | + if ($count > 0) { |
| 788 | + $iNonZero++; |
| 789 | + $widthString=""; |
| 790 | + $rowTotal+=$count; |
| 791 | + if ($iNonZero==$nonZeroKeyCount) { |
| 792 | + $class="C$i last"; |
| 793 | + # Make sure we don't get caught by rounding error |
| 794 | + $width=100-$rowWidthTotal; |
| 795 | + } else { |
| 796 | + $width=number_format((100*$count)/$total,0); |
| 797 | + $rowWidthTotal+=$width; |
| 798 | + $class="C$i notlast"; |
| 799 | + } |
| 800 | + $content; |
| 801 | + if (strlen($key) > ($width /2) ) { |
| 802 | + $content=" "; |
| 803 | + } else { |
| 804 | + $keyAndCount="$key ($count)"; |
| 805 | + if (strlen($keyAndCount) < ($width /2) ) { |
| 806 | + $content=$keyAndCount; |
| 807 | + } else { |
| 808 | + $content=$key; |
| 809 | + } |
| 810 | + } |
| 811 | + $widthString=" style=\"width:".$width."%\""; |
| 812 | + $this->output.="<div title=\"$key = $count"; |
| 813 | + if ($this->context->debug) { |
| 814 | + $this->output.="class=$class / width=$width / key count=$keyCount / nz key count=$nonZeroKeyCount / i=$i / inz=$iNonZero"; |
| 815 | + } |
| 816 | + $this->output.= |
| 817 | + "\"$widthString class=\"$class\"><div>". |
| 818 | + "$content</div></div>"; |
| 819 | + } |
| 820 | + } |
| 821 | + $this->output.="</td><td class=\"total\">$rowTotal</td></tr>"; |
| 822 | + } |
| 823 | + |
| 824 | + /** |
| 825 | + * Render output for no results |
| 826 | + */ |
| 827 | + public function renderNoResultsHTML() { |
| 828 | + if ($this->get('noresultsmessage')) { |
| 829 | + return "<div class=\"bz_noresults\">". |
| 830 | + $this->get('noresultsmessage')."</div>"; |
| 831 | + } else { |
| 832 | + return ""; |
| 833 | + } |
| 834 | + } |
| 835 | + |
| 836 | + public function getMatchExpression($match,$name,$negate) { |
| 837 | + $timmedMatch=trim($match); |
| 838 | + $pos=strpos($timmedMatch, "!"); |
| 839 | + if ($pos === false) { |
| 840 | + if ($negate) { |
| 841 | + return $name."<>'".$timmedMatch."'"; |
| 842 | + } else { |
| 843 | + return $name."='".$timmedMatch."'"; |
| 844 | + } |
| 845 | + } else if ($pos==0) { |
| 846 | + if ($negate) { |
| 847 | + return $name."='".substr($timmedMatch,1)."'"; |
| 848 | + } else { |
| 849 | + return $name."<>'".substr($timmedMatch,1)."'"; |
| 850 | + } |
| 851 | + } else { |
| 852 | + $this->context->debug && $this->context->warn("The '!' operator must be the first character -> ignoring $name=$match"); |
| 853 | + } |
| 854 | + } |
| 855 | + |
| 856 | + # |
| 857 | + # Initialise the bar array |
| 858 | + # |
| 859 | + private function getBarArray() { |
| 860 | + $barArray=array(); |
| 861 | + if (array_key_exists($this->get('bar'),$this->fieldValues)) { |
| 862 | + foreach (split(",", |
| 863 | + $this->fieldValues[$this->get('bar')]) as $key) { |
| 864 | + $barArray[$key]=0; |
| 865 | + } |
| 866 | + $this->context->debug && |
| 867 | + $this->context->debug("Initialised bar keys (". |
| 868 | + $this->get('bar')."): ". |
| 869 | + join(",",$arrayKeys)); |
| 870 | + } |
| 871 | + return $barArray; |
| 872 | + } |
| 873 | +} |
Property changes on: trunk/extensions/BugzillaReports/BugzillaQuery.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 874 | + Id |
Added: svn:eol-style |
2 | 875 | + native |
Index: trunk/extensions/BugzillaReports/RELEASE-NOTES |
— | — | @@ -0,0 +1,76 @@ |
| 2 | +=Release Notes= |
| 3 | + |
| 4 | +==v0.8 == |
| 5 | + |
| 6 | +* Support heading which includes the heading (as an H1) above the report (only if there is content) |
| 7 | +* Support for empty noresultsmessage, if it's set to nothing then no no-results message will be displayed |
| 8 | +* Support for milestone search query and column name |
| 9 | +* Refactoring to improve supportability and future extensions |
| 10 | +* Support for "allow_call_time_pass_reference = Off" in php.ini without warnings |
| 11 | +* Clear up all warnings with "error_reporting = E_ALL" set in php.ini |
| 12 | +* Display tasks which a task is dependent on with "depends=*" and anything which blocks with "blocks=*" - assignee and status displayed on roll over (in the td title) |
| 13 | +* By default the real name is display for cc,to, from , etc names. If you want the login name to be displayed set "nameformat=login" |
| 14 | +* Support for name format as a succinct three letter acronym, e.g. Joe Blogs would be JBL and Jane Doe would be JDO, set "nameformat=tla" to enable this |
| 15 | +* Provided a simple list format configured by setting "format=list" which generates a list of tasks handy for cutting and pasting into an email |
| 16 | +* Support for alias in column and query |
| 17 | +* A little faint "+" appears next to any value if there's extra info when you roll over the value, for example if an alias is set then you see a little "+" and see the alias name when you roll over |
| 18 | + |
| 19 | +==v0.7 - 4th July 2008 == |
| 20 | + |
| 21 | +* Regression Warning : There were some inconsistencies and bugs relating to the naming fields in query field parameters, sort parameters and column parameters. This naming has been simplified and made consistent, however it does mean that the use of functions should be adjusted inline with this release. Naming is now as follows: |
| 22 | +*: status (instead of bug_status), severity (instead of bug_severity), id (instead of bug_id), summary (instead of short_desc) |
| 23 | +* Warnings now output to page to make troubleshooting easier |
| 24 | +* Implicitly add column to provide url associated with task. Column doesn't have a header so if no rows have urls then nothing shows up. |
| 25 | +* Provide support for last modified with the parameter, modified=+ or sort=modified|order=desc |
| 26 | +* Added group feature, e.g. group=product to group the report into different products, including groupformat=radar to render headers for dates as today, tomorrow, this week etc ... |
| 27 | +* Added experimental bar chart feature - e.g. bar=priority groups all rows into a bar chart of different priorities. I say experimental, since doesn't look great yet, but it's functional. |
| 28 | + |
| 29 | +==v0.6 - 1st July 2008 == |
| 30 | + |
| 31 | +* Fix setting of implicit sorting when some query fields set |
| 32 | +* Fix bug relating to array_fill_keys mandating two parameters |
| 33 | + |
| 34 | +==v0.5 - 1st July 2008 == |
| 35 | + |
| 36 | +* Optimise SQL to only do joins when needed |
| 37 | +* Support search on component name, component=mycomponent |
| 38 | +* Support for vote reporting, columns=+votes to include votes column and votes=+ to display only tickets with votes against them |
| 39 | +* Significant refactoring to make extension easier to maintain and to allow further DB integrations to be developed (including abstractions from mediawiki) - I expect to do be doing a vTiger MediaWiki extension in the coming months, and I've had feedback that people would like to run this functionality outside of mediawiki. |
| 40 | +* Implicit sort and ordering based on query parameters if no explit sort and ordering set, for example if you say you want to list all rows with deadline set, then sorting will be on deadline (unless explicitly set otherwise) |
| 41 | +* Support for "from","to" and "cc" meaning respectively who raised the ticket, who's it assigned to and who's cc'd |
| 42 | +** Renamed column and query field login_name to "to" (requires changes to any function calls which make reference to login_name) |
| 43 | +** Support for reporter query field and column, e.g. from=me@bemoko.com and columns=+from |
| 44 | +** Support for query field on cc, but note that comma separated and not operators are not supported for this field. |
| 45 | +* Implicity remove columns if query set for a single value, i.e. all values in the column would be the same |
| 46 | +* Implicity add columns if query set for not null, e.g. votes=+ or deadline=+ would implicitly add the deadline and votes column |
| 47 | +* Support query for flagged tasks, e.g. flag=+me@bemoko.com |
| 48 | +* Make sort on deadline put earliest date first and nulls at end |
| 49 | + |
| 50 | +==v0.4 - 30th June 2008== |
| 51 | + |
| 52 | +* Fix query for free text match - using '''search''' parameter |
| 53 | +* Changed default status to !CLOSED |
| 54 | +* Default maxrows changed to 100 |
| 55 | +* Support maxrows as parameter into function call (as well as server config max) - note function call value can't be higher than server config value |
| 56 | +* Support spaces in comma separated query list |
| 57 | +* Default columns changed to "bug_id,priority,bug_status,bug_severity,version,name,short_desc" with login_name removed |
| 58 | +* Support adding and removing columns from the default by using "column=+deadline" or "column=-name", or "column=+deadline,-name" |
| 59 | +* Support sort, e.g "sort=deadline" and ordering "order=desc" |
| 60 | +* Support search for deadline set "deadline=+" and deadline not set "deadline=-" |
| 61 | +* Change "name" column to "product" to make it more obvious and consistent |
| 62 | + |
| 63 | +==v0.3 - 26th June 2008== |
| 64 | + |
| 65 | +* Improve UI rendering of lastcomment bubbles |
| 66 | +* Fix the css to get tables rendered better |
| 67 | +* Improved error handling of mysql_query |
| 68 | + |
| 69 | +==v0.2 - 26th June 2008== |
| 70 | + |
| 71 | +* Render last comment for a bug in jQuery bubble - use the '''lastcomment''' parameter |
| 72 | +* Support for negation of query fields |
| 73 | +* Installation download bundle provided |
| 74 | + |
| 75 | +==v0.1 - 25th June 2008== |
| 76 | + |
| 77 | +* Baseline reporting functionality |
\ No newline at end of file |
Property changes on: trunk/extensions/BugzillaReports/RELEASE-NOTES |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 78 | + native |
Index: trunk/extensions/BugzillaReports/scripts/jquery-1.2.6.min.js |
— | — | @@ -0,0 +1,32 @@ |
| 2 | +/* |
| 3 | + * jQuery 1.2.6 - New Wave Javascript |
| 4 | + * |
| 5 | + * Copyright (c) 2008 John Resig (jquery.com) |
| 6 | + * Dual licensed under the MIT (MIT-LICENSE.txt) |
| 7 | + * and GPL (GPL-LICENSE.txt) licenses. |
| 8 | + * |
| 9 | + * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ |
| 10 | + * $Rev: 5685 $ |
| 11 | + */ |
| 12 | +(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else |
| 13 | +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else |
| 14 | +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else |
| 15 | +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else |
| 16 | +return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else |
| 17 | +this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else |
| 18 | +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else |
| 19 | +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&©&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else |
| 20 | +script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else |
| 21 | +for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else |
| 22 | +for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else |
| 23 | +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else |
| 24 | +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else |
| 25 | +while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else |
| 26 | +while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else |
| 27 | +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else |
| 28 | +jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else |
| 29 | +xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else |
| 30 | +jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else |
| 31 | +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else |
| 32 | +s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else |
| 33 | +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); |
\ No newline at end of file |
Property changes on: trunk/extensions/BugzillaReports/scripts/jquery-1.2.6.min.js |
___________________________________________________________________ |
Added: svn:keywords |
1 | 34 | + Id |
Index: trunk/extensions/BugzillaReports/skins/bz_main.css |
— | — | @@ -0,0 +1,179 @@ |
| 2 | +table.bugzilla, div.bz_noresults { |
| 3 | + border-radius:8px; |
| 4 | + border: 1px solid #f0f0f0; |
| 5 | + border-spacing:0px; |
| 6 | + border-collapse: collapse; |
| 7 | +} |
| 8 | +span.flag { |
| 9 | + font-size: x-small; |
| 10 | + color: #999; |
| 11 | +} |
| 12 | +div.bz_noresults { |
| 13 | + padding: 5px; |
| 14 | + font-weight:bold; |
| 15 | + color: #909090; |
| 16 | + text-align:center; |
| 17 | +} |
| 18 | +div.warning { |
| 19 | + padding: 5px; |
| 20 | + font-weight:bold; |
| 21 | + color: #f00; |
| 22 | +} |
| 23 | +div.bz_comment { |
| 24 | + position: absolute; |
| 25 | + -moz-border-radius:8px; |
| 26 | + border: 1px solid #909090; |
| 27 | + background-color: #fff; |
| 28 | + padding:10px; |
| 29 | + margin:5px 5px 5px 20px; |
| 30 | + width:50%; |
| 31 | +} |
| 32 | +div.bz_comment span.bug_id { |
| 33 | + -moz-border-radius:4px; |
| 34 | + position: relative; |
| 35 | + top:-5px; |
| 36 | + left: -5px; |
| 37 | + padding: 2px; |
| 38 | + background-color:#e0e0e0; |
| 39 | + font-size: x-small; |
| 40 | +} |
| 41 | +table.bugzilla td{ |
| 42 | + padding:2px 5px 2px 5px; |
| 43 | +} |
| 44 | +table.bugzilla td.bz_details{ |
| 45 | + padding:2px 5px 2px 30px; |
| 46 | + color: #222; |
| 47 | + font-style: italic; |
| 48 | +} |
| 49 | +table.bugzilla tr.bz_group { |
| 50 | + background-color: #fff; |
| 51 | +} |
| 52 | +table.bugzilla tr.bz_group:hover { |
| 53 | + background-color: #e0e0e0; |
| 54 | +} |
| 55 | +table.bugzilla tr.bz_group td { |
| 56 | + text-align: right; |
| 57 | + border-bottom:2px solid #555; |
| 58 | + padding: 2px 5px 2px 2px; |
| 59 | + color: #555; |
| 60 | + font-weight: bold; |
| 61 | +} |
| 62 | +table.bugzilla tr.bz_group td.total { |
| 63 | + border-bottom:0px solid #555; |
| 64 | +} |
| 65 | +table.bugzilla td.total { |
| 66 | + border: 1px solid #f0f0f0; |
| 67 | +} |
| 68 | +table.bugzilla td.total:hover,table.bugzilla tr.bz_bar td.total:hover { |
| 69 | + background-color: #dde; |
| 70 | +} |
| 71 | +table.bugzilla tr.bz_row_odd { |
| 72 | + background-color: #f0f0f0; |
| 73 | +} |
| 74 | +table.bugzilla tr.P1 { |
| 75 | + color: #b30000; |
| 76 | + font-weight: bold; |
| 77 | +} |
| 78 | +table.bugzilla tr.P4 { |
| 79 | + color: #57585b; |
| 80 | +} |
| 81 | +table.bugzilla tr.P5 { |
| 82 | + color: #a7a8ab; |
| 83 | +} |
| 84 | +table.bugzilla tr.RESOLVED td { |
| 85 | + font-style: italic; |
| 86 | +} |
| 87 | +table.bugzilla tr.CLOSED td { |
| 88 | + text-decoration: line-through; |
| 89 | +} |
| 90 | +table.bugzilla tr { |
| 91 | + vertical-align:top; |
| 92 | +} |
| 93 | +table.bugzilla tr:hover { |
| 94 | + background-color: #e0e0e0; |
| 95 | +} |
| 96 | +table.bz_bar { |
| 97 | + width:500px; |
| 98 | +} |
| 99 | +table.bugzilla tr.bz_bar { |
| 100 | + background-color: #ddd; |
| 101 | +} |
| 102 | +table.bugzilla tr.bz_bar td { |
| 103 | + margin: 0px; |
| 104 | + padding: 0px; |
| 105 | + border-right:2px solid #555; |
| 106 | + border-bottom:2px solid #555; |
| 107 | +} |
| 108 | +table.bugzilla tr.bz_bar td.total { |
| 109 | + border-bottom:0px; |
| 110 | + border-right:0px; |
| 111 | + text-align:right; |
| 112 | + padding-right:2px; |
| 113 | + background-color:#fff; |
| 114 | +} |
| 115 | +table.bugzilla tr.bz_bar_total td { |
| 116 | + font-weight: bold; |
| 117 | + border-right:0px; |
| 118 | + text-align:right; |
| 119 | + padding-right:2px; |
| 120 | + background-color:#eee; |
| 121 | +} |
| 122 | +table.bugzilla tr.bz_bar td div { |
| 123 | + text-align:right; |
| 124 | + color:#000; |
| 125 | +} |
| 126 | +table.bugzilla tr.bz_bar td div div { |
| 127 | + text-align:right; |
| 128 | + color:#000; |
| 129 | + font-size:small; |
| 130 | + border-left:2px solid #555; |
| 131 | + padding-right:4px; |
| 132 | +} |
| 133 | +table.bugzilla tr.bz_bar td div.C1{ |
| 134 | + background-color: #aae; |
| 135 | +} |
| 136 | +table.bugzilla tr.bz_bar td div.C2{ |
| 137 | + background-color: #bbe; |
| 138 | +} |
| 139 | +table.bugzilla tr.bz_bar td div.C3{ |
| 140 | + background-color: #cce; |
| 141 | +} |
| 142 | +table.bugzilla tr.bz_bar td div.C4{ |
| 143 | + background-color: #dde; |
| 144 | +} |
| 145 | +table.bugzilla tr.bz_bar td div.C5{ |
| 146 | + background-color: #eee; |
| 147 | +} |
| 148 | +table.bugzilla tr.bz_bar td div.notlast{ |
| 149 | + float: left; |
| 150 | +} |
| 151 | +table.bugzilla tr.bz_bar td div.last{ |
| 152 | + float: right; |
| 153 | +} |
| 154 | +table.bugzilla tr.some td div.C1{ |
| 155 | + background-color: #aae; |
| 156 | +} |
| 157 | +table.bugzilla tr.some td div.C2{ |
| 158 | + background-color: #abe; |
| 159 | +} |
| 160 | +table.bugzilla tr.some td div.C3{ |
| 161 | + background-color: #bbe; |
| 162 | +} |
| 163 | +table.bugzilla tr.some td div.C4{ |
| 164 | + background-color: #bce; |
| 165 | +} |
| 166 | +table.bugzilla tr.some td div.C5{ |
| 167 | + background-color: #cce; |
| 168 | +} |
| 169 | +table.bugzilla tr.some td div.C6{ |
| 170 | + background-color: #cde; |
| 171 | +} |
| 172 | +table.bugzilla tr.some td div.C7{ |
| 173 | + background-color: #dde; |
| 174 | +} |
| 175 | +table.bugzilla tr.some td div.C8{ |
| 176 | + background-color: #dee; |
| 177 | +} |
| 178 | +table.bugzilla tr.some td div.C9{ |
| 179 | + background-color: #eee; |
| 180 | +} |
Property changes on: trunk/extensions/BugzillaReports/skins/bz_main.css |
___________________________________________________________________ |
Added: svn:keywords |
1 | 181 | + Id |
Added: svn:eol-style |
2 | 182 | + native |
Index: trunk/extensions/BugzillaReports/BMWExtension.php |
— | — | @@ -0,0 +1,74 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | +Bemoko MediaWiki Extension |
| 5 | +*/ |
| 6 | + |
| 7 | +/** |
| 8 | + * The bugzilla report objects |
| 9 | + */ |
| 10 | +abstract class BMWExtension { |
| 11 | + public $debug; |
| 12 | + var $warnings; |
| 13 | + |
| 14 | + abstract protected function set($name,$value); |
| 15 | + |
| 16 | + /** |
| 17 | + * Extract options from a arguments |
| 18 | + * |
| 19 | + * @param string $args function arguments |
| 20 | + */ |
| 21 | + public function extractOptions( $args ) { |
| 22 | + $this->debug && $this->debug("Calling extractOptions"); |
| 23 | + foreach( $args as $line ) { |
| 24 | + if( strpos( $line, '=' ) === false ) |
| 25 | + continue; |
| 26 | + list( $name, $value ) = explode( '=', $line, 2 ); |
| 27 | + $value=trim($value); |
| 28 | + $match=$this->getParameterRegex($name); |
| 29 | + if (!$match) { |
| 30 | + /** |
| 31 | + * Safe parameter reading by default |
| 32 | + * only allowing alphanumeric |
| 33 | + */ |
| 34 | + $match="/^[\w]*$/"; |
| 35 | + } |
| 36 | + if (preg_match($match, $value, $matches)) { |
| 37 | + $this->set($name,$matches[0]); |
| 38 | + $this->debug && |
| 39 | + $this->debug("Parameter [".$name."=".$matches[0]."]"); |
| 40 | + } else { |
| 41 | + $this->warn("Parameter ". |
| 42 | + $name."=".$value." is invalid using regex $match"); |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + /** |
| 48 | + * MediaWiki debug message |
| 49 | + */ |
| 50 | + public function debug($message) { |
| 51 | + if ($this->debug) { |
| 52 | + if ($message) { |
| 53 | + wfDebugLog("BMWExtension",$message); |
| 54 | + } else { |
| 55 | + $this->warn("Null message sent to debug statement"); |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + /** |
| 60 | + * MediaWiki warn message |
| 61 | + */ |
| 62 | + public function warn($message) { |
| 63 | + wfDebugLog("BMWExtension","WARN: *** ".$message); |
| 64 | + $this->warnings.="<li>".$message."</li>"; |
| 65 | + } |
| 66 | + |
| 67 | + public function getWarnings() { |
| 68 | + if ($this->warnings) { |
| 69 | + return "<div class=\"warning\"><b>Warnings were generated during the execution of function</b><ol>".$this->warnings."</ol></div>"; |
| 70 | + } else { |
| 71 | + return NULL; |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | +} |
Property changes on: trunk/extensions/BugzillaReports/BMWExtension.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 76 | + Id |
Added: svn:eol-style |
2 | 77 | + native |
Index: trunk/extensions/BugzillaReports/BugzillaReports.i18n.php |
— | — | @@ -0,0 +1,13 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension BugzillaReport. |
| 5 | + */ |
| 6 | + |
| 7 | +$messages = array(); |
| 8 | + |
| 9 | +$messages['en'] = array( |
| 10 | + 'bReport-desc' => 'Provide bugzilla reports using the parser function #bugzilla', |
| 11 | + 'bReport_noconnection' => 'Cannot get database connection ($1@$2) : $3', |
| 12 | + 'bReport_nodb' => 'Database not found', |
| 13 | + 'bReport_sqlerror' => 'Error running the sql : $1' |
| 14 | +); |
Property changes on: trunk/extensions/BugzillaReports/BugzillaReports.i18n.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 15 | + Id |
Added: svn:eol-style |
2 | 16 | + native |
Index: trunk/extensions/BugzillaReports/BSQLQuery.php |
— | — | @@ -0,0 +1,829 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * A general SQL query |
| 5 | + */ |
| 6 | +abstract class BSQLQuery { |
| 7 | + protected $context; |
| 8 | + |
| 9 | + # |
| 10 | + # Parameter values |
| 11 | + # |
| 12 | + private $parameters=array(); |
| 13 | + |
| 14 | + # |
| 15 | + # Arbitary cached values for private use |
| 16 | + # |
| 17 | + private $bsql_cache=array(); |
| 18 | + |
| 19 | + # |
| 20 | + # Date long time in the future, useful for sorting purposes |
| 21 | + # when null mapped to this |
| 22 | + # |
| 23 | + var $futureDate='2100-01-01'; |
| 24 | + # |
| 25 | + # Work out what db data is required and record in |
| 26 | + # $fieldsRequired array so we can optimise the SQL |
| 27 | + # ... no point in wasting energy |
| 28 | + # |
| 29 | + |
| 30 | + var $fieldsRequired=array(); |
| 31 | + |
| 32 | + # Cached array of columns that we actually want to render |
| 33 | + var $columnsToRender; |
| 34 | + |
| 35 | + # Number of columns in a report |
| 36 | + var $numberOfMainRowColumns; |
| 37 | + |
| 38 | + # Columns implicitly removed or added, note that explict setting |
| 39 | + # overrides this |
| 40 | + private $implicityAddedColumns=array(); |
| 41 | + private $implicityRemovedColumns=array(); |
| 42 | + |
| 43 | + # Columns implicitly sort and order, note that explict setting |
| 44 | + # overrides this |
| 45 | + var $implicitParameters=array(); |
| 46 | + |
| 47 | + # Cached versions so we only calculate once |
| 48 | + var $cache=array(); |
| 49 | + |
| 50 | + abstract protected function getFormats(); |
| 51 | + abstract protected function getDefaultSort(); |
| 52 | + |
| 53 | + # |
| 54 | + # Override this to map sort value to appropriate SQL |
| 55 | + # |
| 56 | + protected function getSortMapping($column) { |
| 57 | + if (array_key_exists($column,$this->sortMapping)) { |
| 58 | + return $this->sortMapping[$column]; |
| 59 | + } else { |
| 60 | + return $column; |
| 61 | + } |
| 62 | + } |
| 63 | + protected function setSortMapping($column,$mapping) { |
| 64 | + $this->sortMapping[$column]=$mapping; |
| 65 | + } |
| 66 | + |
| 67 | + public function setContext($context) { |
| 68 | + $this->context=$context; |
| 69 | + } |
| 70 | + |
| 71 | + # |
| 72 | + # Set parameter value |
| 73 | + # |
| 74 | + public function set($name,$value) { |
| 75 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 76 | + switch ($this->supportedParameters[$name]) { |
| 77 | + case 'field' : |
| 78 | + $this->parameters[$name]= |
| 79 | + $this->tidyCommaSeparated($value); |
| 80 | + break; |
| 81 | + case 'columns' : |
| 82 | + $this->parameters[$name]= |
| 83 | + $this->tidyCommaSeparated($value); |
| 84 | + break; |
| 85 | + default: |
| 86 | + $this->parameters[$name]=$value; |
| 87 | + } |
| 88 | + $this->context->debug && |
| 89 | + $this->context->debug("BSQLQuery parameter set $name=". |
| 90 | + $this->parameters[$name]); |
| 91 | + } else { |
| 92 | + $this->context->warn("Setting parameter $name is not supported"); |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + # |
| 97 | + # Tidy a field value, which essentially means removing the spaces next |
| 98 | + # to the columns |
| 99 | + # |
| 100 | + private function tidyCommaSeparated($value) { |
| 101 | + $newValue; |
| 102 | + foreach (split(",",$value) as $singleValue) { |
| 103 | + if (!isset($newValue)) { |
| 104 | + $newValue=trim($singleValue); |
| 105 | + } else { |
| 106 | + $newValue.=",".trim($singleValue); |
| 107 | + } |
| 108 | + } |
| 109 | + return $newValue; |
| 110 | + } |
| 111 | + |
| 112 | + # |
| 113 | + # Return supported regex for each parameter |
| 114 | + # |
| 115 | + public function getParameterRegex($name) { |
| 116 | + $regex; |
| 117 | + switch ($this->supportedParameters[$name]) { |
| 118 | + case 'column' : |
| 119 | + $regex="/^[\w,+-]*$/"; |
| 120 | + break; |
| 121 | + case 'columns' : |
| 122 | + $regex="/^[\w,\s+-]*$/"; |
| 123 | + break; |
| 124 | + case 'field-date' : |
| 125 | + $regex="/^[\*+-]*$/"; |
| 126 | + break; |
| 127 | + case 'free' : |
| 128 | + $regex="/^.*$/"; |
| 129 | + break; |
| 130 | + default : |
| 131 | + if (substr($this->supportedParameters[$name],0,5) |
| 132 | + == "field") { |
| 133 | + $regex="/^[\w,@\.\s\*!()+-]*$/"; |
| 134 | + } else { |
| 135 | + $regex="/^[\w]*$/"; |
| 136 | + } |
| 137 | + } |
| 138 | + return $regex; |
| 139 | + } |
| 140 | + |
| 141 | + # |
| 142 | + # Set implicit parameter value |
| 143 | + # |
| 144 | + protected function setImplicit($name,$value) { |
| 145 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 146 | + $this->implicitParameters[$name]=$value; |
| 147 | + } else { |
| 148 | + $this->context->warn("Setting parameter $name is not supported"); |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + # |
| 153 | + # Get parameter value |
| 154 | + # |
| 155 | + protected function get($name) { |
| 156 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 157 | + if (array_key_exists($name,$this->parameters)) { |
| 158 | + return $this->parameters[$name]; |
| 159 | + } else { |
| 160 | + if (array_key_exists($name,$this->defaultParameters)) { |
| 161 | + return $this->defaultParameters[$name]; |
| 162 | + } else { |
| 163 | + return NULL; |
| 164 | + } |
| 165 | + } |
| 166 | + } else { |
| 167 | + $this->context->warn("Getting parameter $name is not supported"); |
| 168 | + return NULL; |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + # |
| 173 | + # Get implicit parameter value |
| 174 | + # |
| 175 | + protected function getImplicit($name) { |
| 176 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 177 | + if (array_key_exists($name,$this->implicitParameters)) { |
| 178 | + return $this->implicitParameters[$name]; |
| 179 | + } else { |
| 180 | + return NULL; |
| 181 | + } |
| 182 | + } else { |
| 183 | + $this->context->warn( |
| 184 | + "Getting implicit parameter $name is not supported"); |
| 185 | + return NULL; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + # |
| 190 | + # Get explicit parameter value |
| 191 | + # |
| 192 | + protected function getExplicit($name) { |
| 193 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 194 | + if (array_key_exists($name,$this->parameters)) { |
| 195 | + return $this->parameters[$name]; |
| 196 | + } else { |
| 197 | + return NULL; |
| 198 | + } |
| 199 | + } else { |
| 200 | + $this->context->warn( |
| 201 | + "Getting explicit parameter $name is not supported"); |
| 202 | + return NULL; |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + # |
| 207 | + # Get default parameter value |
| 208 | + # |
| 209 | + protected function getDefault($name) { |
| 210 | + if (array_key_exists($name,$this->supportedParameters)) { |
| 211 | + if (array_key_exists($name,$this->defaultParameters)) { |
| 212 | + return $this->defaultParameters[$name]; |
| 213 | + } else { |
| 214 | + return NULL; |
| 215 | + } |
| 216 | + } else { |
| 217 | + $this->context->warn( |
| 218 | + "Getting default parameter $name is not supported"); |
| 219 | + return NULL; |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + # |
| 224 | + # Identify a field as required |
| 225 | + # |
| 226 | + public function requireField($column) { |
| 227 | + $this->context->debug && |
| 228 | + $this->context |
| 229 | + ->debug("Field required : ".$column); |
| 230 | + $this->fieldsRequired[$column]=1; |
| 231 | + } |
| 232 | + |
| 233 | + # |
| 234 | + # Determine whether a field is required |
| 235 | + # |
| 236 | + public function isRequired($column) { |
| 237 | + if (array_key_exists($column,$this->fieldsRequired)) { |
| 238 | + return TRUE; |
| 239 | + } else { |
| 240 | + return FALSE; |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + # |
| 245 | + # Convert date to nice words |
| 246 | + # |
| 247 | + private function getRadarFormat($value) { |
| 248 | + if (!$this->bsql_cache["today"]) { |
| 249 | + $this->bsql_cache["yesterday"]=date("Y-m-d",strtotime("-1 day")); |
| 250 | + $this->bsql_cache["today"]=date("Y-m-d"); |
| 251 | + $this->bsql_cache["tomorrow"]=date("Y-m-d",strtotime("+1 day")); |
| 252 | + $this->bsql_cache["thisweek"]=date("Y-W"); |
| 253 | + $this->bsql_cache["nextweek"]=date("Y-W",strtotime("+1 week")); |
| 254 | + $this->bsql_cache["thismonth"]=date("Y-m"); |
| 255 | + $this->bsql_cache["nextmonth"]=date("Y-m",strtotime("+1 month")); |
| 256 | + $this->bsql_cache["thisyear"]=date("Y"); |
| 257 | + $this->bsql_cache["nextyear"]=date("Y",strtotime("+1 year")); |
| 258 | + } |
| 259 | + if (date("Y-m-d",$value) == $this->bsql_cache["yesterday"]) { |
| 260 | + return "yesterday"; |
| 261 | + } else if ($value < (time()-86400)) { |
| 262 | + return "overdue"; |
| 263 | + } else if (date("Y-m-d",$value) == $this->bsql_cache["today"]) { |
| 264 | + return "today"; |
| 265 | + } else if (date("Y-m-d",$value) == $this->bsql_cache["tomorrow"]) { |
| 266 | + return "tomorrow"; |
| 267 | + } else if (date("Y-W",$value) == $this->bsql_cache["thisweek"]) { |
| 268 | + return "this week"; |
| 269 | + } else if (date("Y-W",$value) == $this->bsql_cache["nextweek"]) { |
| 270 | + return "next week"; |
| 271 | + } else if (date("Y-m",$value) == $this->bsql_cache["thismonth"]) { |
| 272 | + return "this month"; |
| 273 | + } else if (date("Y-m",$value) == $this->bsql_cache["nextmonth"]) { |
| 274 | + return "next month"; |
| 275 | + } else if (date("Y",$value) == $this->bsql_cache["thisyear"]) { |
| 276 | + return "this year"; |
| 277 | + } else if (date("Y",$value) == $this->bsql_cache["nextyear"]) { |
| 278 | + return "next year"; |
| 279 | + } else { |
| 280 | + return "years away"; |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + private function formatForExplicitFormat($value,$format,$title) { |
| 285 | + switch ($format) { |
| 286 | + case "date" : |
| 287 | + if ($value) { |
| 288 | + $time=strtotime($value); |
| 289 | + if ($time == strtotime($this->futureDate)) { |
| 290 | + return ""; |
| 291 | + } else { |
| 292 | + $formattedDate=date("Y-m-d",strtotime($value)); |
| 293 | + } |
| 294 | + return $formattedDate; |
| 295 | + } else { |
| 296 | + return ""; |
| 297 | + } |
| 298 | + case "radar" : |
| 299 | + if ($value) { |
| 300 | + $time=strtotime($value); |
| 301 | + if ($time == strtotime($this->futureDate)) { |
| 302 | + return ""; |
| 303 | + } else { |
| 304 | + $formattedDate= |
| 305 | + $this->getRadarFormat(strtotime($value)); |
| 306 | + } |
| 307 | + return $formattedDate; |
| 308 | + } else { |
| 309 | + return ""; |
| 310 | + } |
| 311 | + case "url" : |
| 312 | + if ($value) { |
| 313 | + return "[$value]"; |
| 314 | + } else { |
| 315 | + return " "; |
| 316 | + } |
| 317 | + case "id" : |
| 318 | + # Render as interwiki or external link |
| 319 | + if ($value) { |
| 320 | + $text; |
| 321 | + if ($title) { |
| 322 | + $flag=""; |
| 323 | + if ($title!=$value) { |
| 324 | + $flag="<span class=\"flag\">+</span>"; |
| 325 | + } |
| 326 | + $text="<span title=\"$title\">$value$flag</span>"; |
| 327 | + } else { |
| 328 | + $text=$value; |
| 329 | + } |
| 330 | + if ($this->context->interwiki) { |
| 331 | + return "[[".$this->context->interwiki. |
| 332 | + ":".$value. |
| 333 | + "|".$text."]]"; |
| 334 | + } else { |
| 335 | + return "[".$this->context->bzserver. |
| 336 | + "/show_bug.cgi?id=".$value. |
| 337 | + " ".$text."]"; |
| 338 | + } |
| 339 | + } else { |
| 340 | + return " "; |
| 341 | + } |
| 342 | + case "name" : |
| 343 | + if ($value) { |
| 344 | + if ($this->get("nameformat") == "tla") { |
| 345 | + return $this->convertNameToTla($value); |
| 346 | + } else { |
| 347 | + return $value; |
| 348 | + } |
| 349 | + } else { |
| 350 | + return " "; |
| 351 | + } |
| 352 | + default : |
| 353 | + $this->context->warn("Format ". |
| 354 | + $format. |
| 355 | + " not recognised"); |
| 356 | + return $value; |
| 357 | + } |
| 358 | + } |
| 359 | + |
| 360 | + |
| 361 | + /** |
| 362 | + * Convert a name to a TLA |
| 363 | + */ |
| 364 | + |
| 365 | + public function convertNameToTla($value) { |
| 366 | + $names=split(" ",$value); |
| 367 | + $tla; |
| 368 | + if (sizeof($names) ==1) { |
| 369 | + if (strlen($value) > 3) { |
| 370 | + $tla=substring($value,0,3); |
| 371 | + } else { |
| 372 | + $tla=$value; |
| 373 | + } |
| 374 | + } else { |
| 375 | + if (strlen($names[1]) > 1) { |
| 376 | + $tla=substr($names[0],0,1).substr($names[1],0,2); |
| 377 | + } else if (strlen($name[0] > 1)) { |
| 378 | + $tla=substr($names[0],0,2).substr($names[1],0,1); |
| 379 | + } else { |
| 380 | + $tla=$names[0].$names[1]."A"; |
| 381 | + } |
| 382 | + } |
| 383 | + return strtoupper($tla); |
| 384 | + } |
| 385 | + /** |
| 386 | + * Format a value |
| 387 | + */ |
| 388 | + public function format($value,$column,$title) { |
| 389 | + $formats=$this->getFormats(); |
| 390 | + if (array_key_exists($column,$formats)) { |
| 391 | + return $this->formatForExplicitFormat($value, |
| 392 | + $formats[$column],$title); |
| 393 | + } else { |
| 394 | + return $value; |
| 395 | + } |
| 396 | + } |
| 397 | + |
| 398 | + /** |
| 399 | + * Get a title for a given value |
| 400 | + */ |
| 401 | + public function getValueTitle($line,$column) { |
| 402 | + $title=""; |
| 403 | + if (array_key_exists($column,$this->valueTitle)) { |
| 404 | + $columns=split(",",$this->valueTitle[$column]); |
| 405 | + foreach ($columns as $column) { |
| 406 | + $title.=$line[$column]." "; |
| 407 | + } |
| 408 | + $title=trim($title); |
| 409 | + } |
| 410 | + return $title; |
| 411 | + } |
| 412 | + |
| 413 | + |
| 414 | + # |
| 415 | + # Formatting with null mapped to a string, useful for headings |
| 416 | + # |
| 417 | + public function formatForHeading($value,$column) { |
| 418 | + if ($this->get('groupformat')) { |
| 419 | + $this->context->debug && |
| 420 | + $this->context |
| 421 | + ->debug("Group format set : ".$this->get('groupformat')); |
| 422 | + $value=$this->formatForExplicitFormat($value, |
| 423 | + $this->get('groupformat')); |
| 424 | + } else { |
| 425 | + $value=$this->format($value,$column); |
| 426 | + } |
| 427 | + if ($value) { |
| 428 | + return $value; |
| 429 | + } else { |
| 430 | + return "not set"; |
| 431 | + } |
| 432 | + } |
| 433 | + |
| 434 | + /** |
| 435 | + * Get a where clause from a named field matching a comma separated list |
| 436 | + */ |
| 437 | + public function getWhereClause($match,$name) { |
| 438 | + $this->context->debug && |
| 439 | + $this->context->debug("Generating where clause for $name=$match"); |
| 440 | + if (preg_match("/[\*+-]/",$match)) { |
| 441 | + return $this->getWhereClauseSpecial($match,$name); |
| 442 | + } else if (strstr($match,",")) { |
| 443 | + $pos=strpos($match,"!("); |
| 444 | + $where=" and ("; |
| 445 | + $operator; |
| 446 | + $negate; |
| 447 | + if ($pos===false) { |
| 448 | + $operator="OR"; |
| 449 | + $negate=false; |
| 450 | + } else { |
| 451 | + $match=substr($match,2,-1); |
| 452 | + $operator="AND"; |
| 453 | + $negate=true; |
| 454 | + } |
| 455 | + $first=true; |
| 456 | + foreach (explode(",", $match) as $value) { |
| 457 | + if ($first) { |
| 458 | + $first=false; |
| 459 | + } else { |
| 460 | + $where.=" $operator "; |
| 461 | + } |
| 462 | + $where.=$this->getMatchExpression($value,$name,$negate); |
| 463 | + } |
| 464 | + $where.=") "; |
| 465 | + return $where; |
| 466 | + } else { |
| 467 | + return " and ".$this->getMatchExpression($match,$name,false); |
| 468 | + } |
| 469 | + } |
| 470 | + |
| 471 | + /** |
| 472 | + * Get a int where clause |
| 473 | + * ... very simple for now |
| 474 | + */ |
| 475 | + public function getIntWhereClause($match,$name) { |
| 476 | + if (preg_match("/[\*+-]/",$match)) { |
| 477 | + switch ($match) { |
| 478 | + case "+": |
| 479 | + return " and $name > 0"; |
| 480 | + default: |
| 481 | + $this->context->warn("Int match not recognised $name=$match"); |
| 482 | + } |
| 483 | + } else { |
| 484 | + $this->context->warn("Int match not recognised $name=$match"); |
| 485 | + return ""; |
| 486 | + } |
| 487 | + } |
| 488 | + |
| 489 | + /** |
| 490 | + * Get a date where clause |
| 491 | + * ... very simple for now |
| 492 | + */ |
| 493 | + public function getDateWhereClause($match,$name) { |
| 494 | + if (preg_match("/[\*+-]/",$match)) { |
| 495 | + return $this->getWhereClauseSpecial($match,$name); |
| 496 | + } else { |
| 497 | + $this->context->warn("Date match not recognised $name=$match"); |
| 498 | + return ""; |
| 499 | + } |
| 500 | + } |
| 501 | + |
| 502 | + public function getWhereClauseSpecial($match,$name) { |
| 503 | + switch ($match) { |
| 504 | + case "+": |
| 505 | + return " and $name IS NOT NULL"; |
| 506 | + case "-": |
| 507 | + return " and $name IS NULL"; |
| 508 | + case "*": |
| 509 | + return ""; |
| 510 | + default: |
| 511 | + $this->context->warn("Special match not recognised $name=$match"); |
| 512 | + return ""; |
| 513 | + } |
| 514 | + return " and $name IS NOT NULL"; |
| 515 | + } |
| 516 | + |
| 517 | + /** |
| 518 | + * Get maximum number of rows |
| 519 | + */ |
| 520 | + public function getMaxRows() { |
| 521 | + if ($this->get('maxrows')) { |
| 522 | + if ($this->get('maxrows') > $this->context->maxrowsFromConfig) { |
| 523 | + $this->context->warn("Max rows in function parameter greater than in config -> ignoring"); |
| 524 | + return $this->context->maxrowsFromConfig; |
| 525 | + } else { |
| 526 | + return $this->get('maxrows'); |
| 527 | + } |
| 528 | + } else { |
| 529 | + return $this->context->maxrowsFromConfig; |
| 530 | + } |
| 531 | + } |
| 532 | + |
| 533 | + /** |
| 534 | + * Get maximum number of rows |
| 535 | + */ |
| 536 | + public function getMaxRowsForBarChart() { |
| 537 | + if ($this->get('maxrowsbar')) { |
| 538 | + if ($this->get('maxrowsbar') > $this->context->maxrowsForBarChartFromConfig) { |
| 539 | + $this->context->warn("Max rows bar in function parameter greater than in config -> ignoring"); |
| 540 | + return $this->context->maxrowsForBarChartFromConfig; |
| 541 | + } else { |
| 542 | + return $this->get('maxrowsbar'); |
| 543 | + } |
| 544 | + } else { |
| 545 | + return $this->context->maxrowsForBarChartFromConfig; |
| 546 | + } |
| 547 | + } |
| 548 | + |
| 549 | + /** |
| 550 | + * Get sort |
| 551 | + */ |
| 552 | + public function getSort() { |
| 553 | + # Not explicit on function call or notcached |
| 554 | + if (!array_key_exists('sort',$this->cache)) { |
| 555 | + $sort; |
| 556 | + if ($this->getExplicit('sort')) { |
| 557 | + $sort=$this->getExplicit('sort'); |
| 558 | + } else if ($this->getImplicit('sort')) { |
| 559 | + # Implicit on usage and other function call parameters |
| 560 | + $sort=$this->getImplicit('sort'); |
| 561 | + } else { |
| 562 | + # Default behaviour |
| 563 | + $sort=$this->getDefault('sort'); |
| 564 | + } |
| 565 | + # |
| 566 | + # Prepend with group (if set) |
| 567 | + # |
| 568 | + if ($this->getGroup()) { |
| 569 | + $sort=$this->getGroup().",".$sort; |
| 570 | + } |
| 571 | + $this->context->debug && |
| 572 | + $this->context->debug("Sort set to $sort"); |
| 573 | + $this->cache['sort']=$sort; |
| 574 | + } |
| 575 | + return $this->cache['sort']; |
| 576 | + } |
| 577 | + |
| 578 | + # |
| 579 | + # Return sort with mapping |
| 580 | + # |
| 581 | + public function getMappedSort() { |
| 582 | + $mappedSort=array(); |
| 583 | + foreach(explode(",",$this->getSort()) as $column) { |
| 584 | + array_push($mappedSort,$this->getSortMapping($column)); |
| 585 | + } |
| 586 | + return join(",",$mappedSort); |
| 587 | + } |
| 588 | + /** |
| 589 | + * Get order |
| 590 | + */ |
| 591 | + public function getOrder() { |
| 592 | + if (!array_key_exists('order',$this->cache)) { |
| 593 | + $order; |
| 594 | + if ($this->getExplicit('order')) { |
| 595 | + $order=$this->getExplicit('order'); |
| 596 | + } else if ($this->getImplicit('order')) { |
| 597 | + $order=$this->getImplicit('order'); |
| 598 | + } else { |
| 599 | + $order=$this->getDefault('order'); |
| 600 | + } |
| 601 | + if ($order == "desc") { |
| 602 | + $this->cache['order']="DESC"; |
| 603 | + } else { |
| 604 | + $this->cache['order']="ASC"; |
| 605 | + } |
| 606 | + $this->context->debug && |
| 607 | + $this->context->debug("Order set to ".$this->cache['order']); |
| 608 | + } |
| 609 | + return $this->cache['order']; |
| 610 | + } |
| 611 | + |
| 612 | + /** |
| 613 | + * Get group |
| 614 | + */ |
| 615 | + public function getGroup() { |
| 616 | + if (!array_key_exists('group',$this->cache)) { |
| 617 | + $group; |
| 618 | + if ($this->getExplicit('group')) { |
| 619 | + # Explicit on function call |
| 620 | + $group=$this->getExplicit('group'); |
| 621 | + } else if ($this->getImplicit('group')) { |
| 622 | + # Implicit on usage and other function call parameters |
| 623 | + $group=$this->getImplicit('group'); |
| 624 | + } else { |
| 625 | + # Default behaviour is nothing |
| 626 | + $group=FALSE; |
| 627 | + } |
| 628 | + $this->cache['group']=$group; |
| 629 | + } |
| 630 | + return $this->cache['group']; |
| 631 | + } |
| 632 | + |
| 633 | + public function implictlyRemoveColumn($column) { |
| 634 | + $this->implicityRemovedColumns[$column]=$column; |
| 635 | + $this->context->debug("Registering column for implicit removal : ". |
| 636 | + $column); |
| 637 | + } |
| 638 | + |
| 639 | + public function implictlyAddColumn($column) { |
| 640 | + $this->implicityAddedColumns[$column]=$column; |
| 641 | + $this->context->debug("Registering column for implicit addition : ". |
| 642 | + $column); |
| 643 | + } |
| 644 | + |
| 645 | + /** |
| 646 | + * Get columns |
| 647 | + */ |
| 648 | + public function getColumns() { |
| 649 | + if ($this->columnsToRender) { |
| 650 | + return $this->columnsToRender; |
| 651 | + } |
| 652 | + if ($this->getExplicit('columns')) { |
| 653 | + if (preg_match("/^([+-])(.*)$/", |
| 654 | + $this->getExplicit('columns'), $array)) { |
| 655 | + $this->context->debug && |
| 656 | + $this->context-> |
| 657 | + debug("Adjusting columns (comma separated) : ". |
| 658 | + $array[1].":".$array[2]); |
| 659 | + $newColumns=explode(",",$this->getDefault('columns')); |
| 660 | + $deltaColumns=explode(",",$array[2]); |
| 661 | + $defaultOperation=$array[1]; |
| 662 | + foreach ($deltaColumns as $deltaColumn) { |
| 663 | + $newColumn=$deltaColumn; |
| 664 | + $operation=$defaultOperation; |
| 665 | + /** |
| 666 | + * Support operations on subsequent columns (not just the first) |
| 667 | + */ |
| 668 | + if (preg_match("/^([+-])(.*)$/", |
| 669 | + $deltaColumn, $pregDeltaColumn)) { |
| 670 | + $operation=$pregDeltaColumn[1]; |
| 671 | + $newColumn=$pregDeltaColumn[2]; |
| 672 | + } |
| 673 | + $this->context->debug && |
| 674 | + $this->context-> |
| 675 | + debug("Adjusting columns (single string): ". |
| 676 | + $operation.":".$newColumn); |
| 677 | + /** |
| 678 | + * Add or remove column |
| 679 | + */ |
| 680 | + if ($operation == "+") { |
| 681 | + $this->context->debug && |
| 682 | + $this->context-> |
| 683 | + debug("Adding column [$newColumn]"); |
| 684 | + array_push($newColumns,$newColumn); |
| 685 | + if (array_key_exists($newColumn, |
| 686 | + $this->implicityRemovedColumns)) { |
| 687 | + $this->context->debug && |
| 688 | + $this->context-> |
| 689 | + debug("Removing implicit removal of column : $newColumn"); |
| 690 | + unset($this->implicityRemovedColumns[$newColumn]); |
| 691 | + } |
| 692 | + } else if ($operation == "-") { |
| 693 | + $found=-1; |
| 694 | + $i=0; |
| 695 | + foreach ($newColumns as $search) { |
| 696 | + if ($search==$newColumn) { |
| 697 | + $found=$i; |
| 698 | + break; |
| 699 | + } |
| 700 | + $i++; |
| 701 | + } |
| 702 | + if ($found > -1) { |
| 703 | + $this->context->debug && |
| 704 | + $this->context->debug("Removing column [$newColumn,$found]"); |
| 705 | + unset($newColumns[$found]); |
| 706 | + if (array_key_exists($newColumn, |
| 707 | + $this->implicityAddedColumns)) { |
| 708 | + unset($this |
| 709 | + ->implicityAddedColumns[$newColumn]); |
| 710 | + } |
| 711 | + } else { |
| 712 | + $this->context |
| 713 | + ->warn("Can't remove column [$newColumn] it doesn't exists"); |
| 714 | + } |
| 715 | + } else { |
| 716 | + $this->context |
| 717 | + ->warn("Operation not recognised in column " |
| 718 | + .$operation); |
| 719 | + } |
| 720 | + } |
| 721 | + /** |
| 722 | + * We may have removed values from the columns so we need to |
| 723 | + * recreate array by calling array_values function |
| 724 | + */ |
| 725 | + $this->columnsToRender=$this-> |
| 726 | + applyImplicitColumns(array_values($newColumns)); |
| 727 | + $this->context->debug && |
| 728 | + $this->context->debug("Columns to display adjusted to " |
| 729 | + .join(",",$this->columnsToRender)); |
| 730 | + } else { |
| 731 | + $this->context->debug && |
| 732 | + $this->context->debug("Columns explicitly set to ". |
| 733 | + $this->get('columns')); |
| 734 | + # Explicit columns - so don't apply implicit rules |
| 735 | + $this->columnsToRender=explode(",", |
| 736 | + $this->getExplicit('columns')); |
| 737 | + } |
| 738 | + } else { |
| 739 | + $this->columnsToRender=$this-> |
| 740 | + applyImplicitColumns(explode(",", |
| 741 | + $this->getDefault('columns'))); |
| 742 | + $this->context->debug && |
| 743 | + $this->context->debug("Columns set to default " |
| 744 | + .join(",",$this->columnsToRender)); |
| 745 | + } |
| 746 | + return $this->columnsToRender; |
| 747 | + } |
| 748 | + |
| 749 | + # |
| 750 | + # Apply implicit column rules |
| 751 | + # |
| 752 | + private function applyImplicitColumns($columns) { |
| 753 | + $newColumns=$this->array_fill_keys(array_merge( |
| 754 | + $columns, |
| 755 | + array_keys($this->implicityAddedColumns) |
| 756 | + )); |
| 757 | + $this->context->debug && |
| 758 | + $this->context->debug("Implictly adding column " |
| 759 | + .join(",",array_keys($this->implicityAddedColumns))); |
| 760 | + foreach ($this->implicityRemovedColumns as $column) { |
| 761 | + if (array_key_exists($column,$newColumns)) { |
| 762 | + $this->context->debug && |
| 763 | + $this->context-> |
| 764 | + debug("Implictly removing column ".$column); |
| 765 | + unset($newColumns[$column]); |
| 766 | + } |
| 767 | + } |
| 768 | + return array_keys($newColumns); |
| 769 | + } |
| 770 | + |
| 771 | + # |
| 772 | + # Local implementation of array_fill_keys since it's only supported |
| 773 | + # from php 5.2 |
| 774 | + # |
| 775 | + private function array_fill_keys($array,$value=NULL) { |
| 776 | + $newArray=array(); |
| 777 | + foreach ($array as $key) { |
| 778 | + if ($value) { |
| 779 | + $newArray[$key]=$value; |
| 780 | + } else { |
| 781 | + $newArray[$key]=$key; |
| 782 | + } |
| 783 | + } |
| 784 | + return $newArray; |
| 785 | + } |
| 786 | + |
| 787 | + # |
| 788 | + # Initialisation prior to generating the SQL |
| 789 | + # |
| 790 | + protected function preSQLGenerate() { |
| 791 | + # |
| 792 | + # Process sort variable and add implicit columns |
| 793 | + # |
| 794 | + foreach (explode(",",$this->getSort()) as $column) { |
| 795 | + $this->implictlyAddColumn($column); |
| 796 | + } |
| 797 | + # |
| 798 | + # But remove group |
| 799 | + # |
| 800 | + foreach (explode(",",$this->getGroup()) as $column) { |
| 801 | + $this->implictlyRemoveColumn($column); |
| 802 | + # Although we still need the field in the SQL |
| 803 | + $this->requireField($column); |
| 804 | + } |
| 805 | + # |
| 806 | + # Require fields listed in columns |
| 807 | + # |
| 808 | + foreach ($this->getColumns() as $column) { |
| 809 | + $this->requireField($column); |
| 810 | + } |
| 811 | + |
| 812 | + # |
| 813 | + # Require the bar field |
| 814 | + # |
| 815 | + if ($this->get('bar')) { |
| 816 | + $this->context->debug && |
| 817 | + $this->context->debug("Requiring bar field ". |
| 818 | + $this->get('bar')); |
| 819 | + $this->requireField($this->get('bar')); |
| 820 | + } |
| 821 | + } |
| 822 | + |
| 823 | + public function mapField($column) { |
| 824 | + if (array_key_exists($column,$this->fieldMapping)) { |
| 825 | + return $this->fieldMapping[$column]; |
| 826 | + } else { |
| 827 | + return $column; |
| 828 | + } |
| 829 | + } |
| 830 | +} |
Property changes on: trunk/extensions/BugzillaReports/BSQLQuery.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 831 | + Id |
Added: svn:eol-style |
2 | 832 | + native |
Index: trunk/extensions/BugzillaReports/README |
— | — | @@ -0,0 +1,320 @@ |
| 2 | +{ {extension |
| 3 | +|name=BugzillaReports |
| 4 | +|author=[[User:Ian Homer|Ian Homer]] |
| 5 | +|type=parser function |
| 6 | +|version=0.8 |
| 7 | +|update=July 4, 2008 |
| 8 | +|status=beta |
| 9 | +|mediawiki=1.12 |
| 10 | +|description=Generate bugzilla reports |
| 11 | +|download=[http://blog.bemoko.com/bugzillareports/ BugzillaReports] |
| 12 | +}} |
| 13 | +'''BugzillaReports''' generates great looking reports from [[Bugzilla]] which you can include in your [[MediaWiki]] pages. Great to provide access to Bugzilla info, either from an access control point of view or providing reports easier for non-techies to read. Supports interactions with voting & flagging and helps you to see what needs to be done next. It's like your controlled window onto Bugzilla ... |
| 14 | +[[Category:Bugzilla]] |
| 15 | + |
| 16 | +=Installation= |
| 17 | + |
| 18 | +# Download the extension bundle from [http://blog.bemoko.com/bugzillareports/ BugzillaReports] |
| 19 | +# Unpack bundle into your '''extensions''' directory. This should give you |
| 20 | +#: BugzillaReports/BugzillaReports.i18n.php |
| 21 | +#: BugzillaReports/BugzillaReports.php |
| 22 | +#: BugzillaReports/skins/bz_main.css |
| 23 | +#: etc |
| 24 | + |
| 25 | +Add the following to your LocalSettings.php file: |
| 26 | + |
| 27 | +<source lang="php"> |
| 28 | +require_once("$IP/extensions/BugzillaReports/BugzillaReports.php"); |
| 29 | +$wgBugzillaReports = array( |
| 30 | + 'database' => "bugzilla", |
| 31 | + 'user' => "user", |
| 32 | + 'password' => "(your password)", |
| 33 | + 'bzserver' => "http://host" |
| 34 | +); |
| 35 | +</source> |
| 36 | + |
| 37 | +or if you want to use [[interwiki]] links |
| 38 | + |
| 39 | +<source lang="php"> |
| 40 | +... |
| 41 | + 'interwiki' => "bugzilla" |
| 42 | +... |
| 43 | +</source> |
| 44 | + |
| 45 | +where parameters are defined as follows: |
| 46 | + |
| 47 | +# '''host''' ⇒ defaults to localhost, but if Bugzilla not on same machine as your mediawiki install then set the hostname here |
| 48 | +# Use one of |
| 49 | +## '''bzserver''' ⇒ point to bugzilla server e.g. http://host". Note that the path "/show_bug.cgi?id=" is automatically added for you by the extension |
| 50 | +## '''interwiki''' ⇒ [[interwiki]] prefix linking to bugzilla instance |
| 51 | +##: For example set up interwiki prefix of '''bugzilla''' to point to URL http://host/show_bug.cgi?id=$1 |
| 52 | +# '''maxrows''' ⇒ set maximum number of rows to display (defaults to 200) |
| 53 | + |
| 54 | +=Example Output= |
| 55 | + |
| 56 | +[[Image:bugzilla-reports-screenshot3.png|Bugzilla Report]] |
| 57 | + |
| 58 | +Note that the bubble appears as you roll over a bug that has a last comment set. |
| 59 | + |
| 60 | +=Usage= |
| 61 | + |
| 62 | +==By Example== |
| 63 | +Generate a report from Bugzilla based on search criteria, e.g. |
| 64 | + |
| 65 | +[[Image:bugzilla-reports-screenshot1.png|Bugzilla Report|right]] |
| 66 | + |
| 67 | +List all open bugs - note that status defaults to NEW,ASSIGNED,UNCONFIRMED,REOPENED and maximum of 200 rows (by default) are returned. Results are sorted by priority. |
| 68 | + |
| 69 | +<source lang="php"> |
| 70 | +{{#bugzilla:}} |
| 71 | +</source> |
| 72 | + |
| 73 | +Report on P3 and higher bugs |
| 74 | + |
| 75 | +<source lang="php"> |
| 76 | +{{#bugzilla: |
| 77 | + |priority=P1,P2,P3 |
| 78 | +}} |
| 79 | +</source> |
| 80 | + |
| 81 | +Report on a particular product |
| 82 | + |
| 83 | +<source lang="php"> |
| 84 | +{{#bugzilla: |
| 85 | + |product=myproduct |
| 86 | + |version=1.3 |
| 87 | + |lastcomment=1 |
| 88 | +}} |
| 89 | +</source> |
| 90 | + |
| 91 | +Report on specific bug ids |
| 92 | +<source lang="php"> |
| 93 | +{{#bugzilla:id=30,56,78,93}} |
| 94 | +</source> |
| 95 | + |
| 96 | +[[Image:bugzilla-reports-screenshot2.png|Last Comment Bubbles|right]] |
| 97 | +Report on all enhancements with the text wiki in the summary with last comment bubbles |
| 98 | + |
| 99 | +<source lang="php"> |
| 100 | +{{#bugzilla: |
| 101 | + |search=wiki |
| 102 | + |severity=enhancement |
| 103 | + |header=hide |
| 104 | + |lastcomment=1 |
| 105 | +}} |
| 106 | +</source> |
| 107 | + |
| 108 | +Setting the '''lastcomment''' parameter will generate a report which renders the last comment in bundles in bubbles as you hover each row (see image left) This has been tested on Firefox 3 and Safari 3, but this option may have issues with other and older browser. I'd be happy to hear feedback on this feature. |
| 109 | + |
| 110 | +Report on all bugs that are not new |
| 111 | + |
| 112 | +<source lang="php"> |
| 113 | +{{#bugzilla:status=!NEW}} |
| 114 | +</source> |
| 115 | + |
| 116 | +Everything except P1 and P2 bugs |
| 117 | + |
| 118 | +<source lang="php"> |
| 119 | +{{#bugzilla: |
| 120 | + |priority=!(P1,P2) |
| 121 | + |status=* |
| 122 | +}} |
| 123 | +</source> |
| 124 | + |
| 125 | +Set the message when no results are returned |
| 126 | + |
| 127 | +<source lang="php"> |
| 128 | +{{#bugzilla: |
| 129 | + |product=doesntexist |
| 130 | + |noresultsmessage=all tasks complete |
| 131 | +}} |
| 132 | +</source> |
| 133 | + |
| 134 | +Tasks with votes against them |
| 135 | + |
| 136 | +<source lang="php"> |
| 137 | +{{#bugzilla:columns=+votes|votes=+}} |
| 138 | +</source> |
| 139 | + |
| 140 | +[[Image:bugzilla-reports-screenshot4.png]] |
| 141 | + |
| 142 | +<source lang="php"> |
| 143 | +{{#bugzilla |
| 144 | + |group=deadline |
| 145 | + |groupformat=radar |
| 146 | +}} |
| 147 | +</source> |
| 148 | + |
| 149 | +[[Image:bugzilla-reports-screenshot5.png]] |
| 150 | + |
| 151 | +<source lang="php"> |
| 152 | +{{#bugzilla |
| 153 | + |group=version |
| 154 | + |bar=status |
| 155 | +}} |
| 156 | +</source> |
| 157 | + |
| 158 | +All tasks created by me but not assigned to me |
| 159 | + |
| 160 | +<source lang="php"> |
| 161 | +{{#bugzilla:columns=+to|from=me@bemoko.com|to=!me@bemoko.com}} |
| 162 | +</source> |
| 163 | + |
| 164 | +All tasks I'm cc'd on but not created by me and not assigned to me |
| 165 | + |
| 166 | +<source lang="php"> |
| 167 | +{{#bugzilla:columns=+to,+from |
| 168 | + |to=!me@bemoko.com |
| 169 | + |from=!me@bemoko.com |
| 170 | + |cc=me@bemoko.com}} |
| 171 | +</source> |
| 172 | + |
| 173 | +===Task Radar=== |
| 174 | + |
| 175 | +<source lang="php"> |
| 176 | +{{task radar|me@bemoko.com}} |
| 177 | +</source> |
| 178 | + |
| 179 | +where task radar is defined as: |
| 180 | + |
| 181 | +<source lang="php"> |
| 182 | +=Flagged= |
| 183 | +{{#bugzilla:flag={{{1}}}|lastcomment=1|noresultsmessage=No tasks flagged}} |
| 184 | +=Up & Coming= |
| 185 | +{{#bugzilla:to={{{1}}}|columns=-version,+deadline|deadline=+|group=deadline|groupformat=radar|lastcomment=1|noresultsmessage=Nothing up & coming}} |
| 186 | +=My Product Priority Radar= |
| 187 | +{{#bugzilla:to={{{1}}}|lastcomment=1|product=myproduct|noresultsmessage=Nothing to do on myproduct}} |
| 188 | +=Voted Tasks= |
| 189 | +{{#bugzilla:to={{{1}}}|columns=+deadline|group=product|votes=+|noresultsmessage=None of your tasks have received votes}} |
| 190 | +=Raised= |
| 191 | +{{#bugzilla:from={{{1}}}|to=!{{{1}}}|noresultsmessage=You have not raised anything}} |
| 192 | +=CC= |
| 193 | +{{#bugzilla:to=!{{{1}}}|from=!{{{1}}}|cc={{{1}}}|noresultsmessage=No ccs}} |
| 194 | +=Other Priority Radar= |
| 195 | +{{#bugzilla:to={{{1}}}|product=!myproduct|group=product|noresultsmessage=Nothing else to do}} |
| 196 | +</source> |
| 197 | + |
| 198 | +==Parameter Usage== |
| 199 | + |
| 200 | +{| style="-moz-border-radius:8px;border: 1px solid #f0f0f0;border-spacing:0px;width: 80%;margin:auto" |
| 201 | +|- style="vertical-align:top; background-color: #c0c0c0;" |
| 202 | +! parameter |
| 203 | +! style="width:30%" |values |
| 204 | +! description |
| 205 | +|- style="vertical-align:top;" |
| 206 | +| '''bar''' |
| 207 | +| column name |
| 208 | +| field to summarise in a bar chart - note that this also works in conjunction with the group parameter to provide a set of bar charts. |
| 209 | +|- style="vertical-align:top; background-color: #f0f0f0;" |
| 210 | +| '''cc''' |
| 211 | +| single username |
| 212 | +| Query for any task cc'd to the specified username, only supports single value |
| 213 | +|- style="vertical-align:top;" |
| 214 | +| '''columns''' |
| 215 | +| id, cc, deadline, modified, priority, product, severity, status, summary, to, version |
| 216 | +| comma separated list of columns to display in the specified order |
| 217 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 218 | +| '''debug''' |
| 219 | +| ''anything'' |
| 220 | +| Set to enable debugging - outputs SQL at end of table |
| 221 | +|- style="vertical-align:top;" |
| 222 | +| '''component''' |
| 223 | +| component name |
| 224 | +| component query field |
| 225 | +|- style="vertical-align:top; background-color: #f0f0f0;" |
| 226 | +| '''detailsrow''' |
| 227 | +| ''same as values for columns'' |
| 228 | +| comma separated list of columns to display in the specified order on a second row |
| 229 | +|- style="vertical-align:top;" |
| 230 | +| '''flag''' |
| 231 | +| username, e.g. me@bemoko.com |
| 232 | +| search for all tasks flag for the specified user |
| 233 | +|- style="vertical-align:top;" |
| 234 | +| '''from''' |
| 235 | +| username, e.g. me@bemoko.com |
| 236 | +| reported by query field |
| 237 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 238 | +| '''group''' |
| 239 | +| any column name as described in the columns field |
| 240 | +| field to group report on |
| 241 | +|- style="vertical-align:top;" |
| 242 | +| '''groupformat''' |
| 243 | +| radar (headings set to relative date names, e.g. today, tomorrow, next week) |
| 244 | +| format to be applied to group headings |
| 245 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 246 | +| '''header''' |
| 247 | +| hide |
| 248 | +| Set to '''"hide"''' to hide the header row |
| 249 | +|- style="vertical-align:top;" |
| 250 | +| '''id''' |
| 251 | +| ''bugzilla id'' |
| 252 | +| ID query field |
| 253 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 254 | +| '''lastcomments''' |
| 255 | +| 1 |
| 256 | +| display the last comment for each bug |
| 257 | +|- style="vertical-align:top;" |
| 258 | +| '''maxrows''' |
| 259 | +| any integer less than that configure in the LocalSettings.php configuration |
| 260 | +| Maximum number of rows to return |
| 261 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 262 | +| '''noresultsmessage''' |
| 263 | +| any text messag |
| 264 | +| Set the message when there are no results matching the query |
| 265 | +|- style="vertical-align:top;" |
| 266 | +| '''order''' |
| 267 | +| desc (or asc) |
| 268 | +| sort order, defaults to asc |
| 269 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 270 | +| '''priority''' |
| 271 | +| P1,P2,P3,P4,P5 |
| 272 | +| priority query field |
| 273 | +|- style="vertical-align:top;" |
| 274 | +| '''product''' |
| 275 | +| ''bugzilla product names'' |
| 276 | +| product name query field |
| 277 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 278 | +| '''search''' |
| 279 | +| ''anything'' |
| 280 | +| free text search - only searches short_desc at the moment |
| 281 | +|- style="vertical-align:top;" |
| 282 | +| '''severity''' |
| 283 | +| blocker, critical, major, normal, minor, trivial, enhancement |
| 284 | +| severity query field |
| 285 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 286 | +| '''sort''' |
| 287 | +| any column name (or comma separated list of column names) as described in the columns field |
| 288 | +| field to sort on |
| 289 | +|- style="vertical-align:top;" |
| 290 | +| '''status''' |
| 291 | +| NEW,ASSIGNED,UNCONFIRMED,REOPENED,RESOLVED,VERIFIED,CLOSED |
| 292 | +| status query field, note that this defaults to !CLOSED |
| 293 | +|- style="vertical-align:top;background-color: #f0f0f0;" |
| 294 | +| '''to''' |
| 295 | +| a valid username, e.g. me@bemoko.com |
| 296 | +| assigned to query field |
| 297 | +|- style="vertical-align:top;" |
| 298 | +| '''version''' |
| 299 | +| ''bugzilla version name/number'' |
| 300 | +| version query field |
| 301 | +|} |
| 302 | + |
| 303 | + |
| 304 | +All query fields can take the following constructs |
| 305 | + |
| 306 | +* value - single value to match |
| 307 | +* value1,value2, value3 - comma separate list of value to match |
| 308 | +* !value - match on everything except value |
| 309 | +* !(value1, value2, value3) - match on everything except the listed values |
| 310 | +* "*" - match on everything (useful to override inbuilt default of status) |
| 311 | +* "+" - match on not null |
| 312 | +* "-" - match on not set |
| 313 | + |
| 314 | +==With Semantic MediaWiki== |
| 315 | + |
| 316 | +This extension works well with the [http://semantic-mediawiki.org/wiki/Semantic_MediaWiki Semantic MediaWiki] by |
| 317 | + |
| 318 | +# tagging your page with a property, e.g. <nowiki>[[task::52]]</nowiki> |
| 319 | +# and then passing the results of semantic search into the bugzilla reports function, e.g. |
| 320 | + |
| 321 | +<nowiki>{{#bugzilla:|headers=hide|id={{#ask: [[{{PAGENAME}}]] [[task::+]] | ? task | link=none | headers=hide}}}}</nowiki> |
\ No newline at end of file |
Property changes on: trunk/extensions/BugzillaReports/README |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 322 | + native |
Index: trunk/extensions/BugzillaReports/BugzillaReports.php |
— | — | @@ -0,0 +1,166 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | +See README for installation and usage |
| 5 | +*/ |
| 6 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 7 | + die( 'This file is a MediaWiki extension, it is not a valid entry point' ); |
| 8 | +} |
| 9 | +require_once("$IP/extensions/BugzillaReports/BMWExtension.php"); |
| 10 | +require_once("$IP/extensions/BugzillaReports/BugzillaQuery.php"); |
| 11 | + |
| 12 | +$wgExtensionCredits['parserhook'][] = array( |
| 13 | + 'name' => 'BugzillaReports', |
| 14 | + 'version' => '0.8-SNAPSHOT', |
| 15 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:Bugzilla_Reports', |
| 16 | + 'author' => '[http://blog.bemoko.com Ian Homer]', |
| 17 | + 'description' => 'Provide bugzilla reports' |
| 18 | + 'descriptionmsg' => 'bReport-desc' |
| 19 | +); |
| 20 | + |
| 21 | +$wgExtensionFunctions[] = 'efBugzillaReportsSetup'; |
| 22 | +$wgExtensionMessagesFiles['BugzillaReports'] = dirname(__FILE__) . '/BugzillaReports.i18n.php'; |
| 23 | + |
| 24 | +$wgHooks['LanguageGetMagic'][] = 'efBugzillaReportsMagic'; |
| 25 | + |
| 26 | +$bzScriptPath = $wgScriptPath . '/extensions/BugzillaReports'; |
| 27 | +$bzHeadIncluded=false; // flag to record whether the head has been included already so that we only include it once |
| 28 | + |
| 29 | +/** |
| 30 | + * Register the function hook |
| 31 | + */ |
| 32 | +function efBugzillaReportsSetup() { |
| 33 | + global $wgParser; |
| 34 | + $wgParser->setFunctionHook( 'bugzilla', 'efBugzillaReportsRender' ); |
| 35 | +} |
| 36 | + |
| 37 | +/** |
| 38 | + * Register the magic word |
| 39 | + */ |
| 40 | +function efBugzillaReportsMagic( &$magicWords, $langCode ) { |
| 41 | + $magicWords['bugzilla'] = array( 0, 'bugzilla' ); |
| 42 | + return true; |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * Call to render the bugzilla report |
| 47 | + */ |
| 48 | +function efBugzillaReportsRender( &$parser) { |
| 49 | + $bugzillaReport = new BugzillaReport( $parser ); |
| 50 | + $args = func_get_args(); |
| 51 | + array_shift( $args ); |
| 52 | + return $bugzillaReport->render($args); |
| 53 | +} |
| 54 | + |
| 55 | +/** |
| 56 | + * The bugzilla report objects |
| 57 | + */ |
| 58 | +class BugzillaReport extends BMWExtension { |
| 59 | + |
| 60 | + # The handle on the query object |
| 61 | + var $query; |
| 62 | + |
| 63 | + # Default max rows for a report |
| 64 | + var $maxrowsFromConfig; |
| 65 | + var $maxrowsFromConfigDefault=100; |
| 66 | + |
| 67 | + # Default max rows which are used for aggregation of a bar chart report |
| 68 | + var $maxrowsForBarChartFromConfig; |
| 69 | + var $maxrowsForBarChartFromConfigDefault=500; |
| 70 | + |
| 71 | + public $dbuser,$bzserver,$interwiki; |
| 72 | + public $database,$host,$password; |
| 73 | + |
| 74 | + function BugzillaReport( &$parser ) { |
| 75 | + $this->parser =& $parser; |
| 76 | + |
| 77 | + } |
| 78 | + |
| 79 | + public function render($args) { |
| 80 | + global $wgBugzillaReports; |
| 81 | + global $bzScriptPath; |
| 82 | + global $wgDBserver,$wgDBname,$wgDBuser,$wgDBpassword; |
| 83 | + global $bzHeadIncluded; |
| 84 | + |
| 85 | + # Initialise query |
| 86 | + $this->query=new BugzillaQuery($this); |
| 87 | + $this->extractOptions($args); |
| 88 | + |
| 89 | + if (!$bzHeadIncluded) { |
| 90 | + $bzHeadIncluded=true; |
| 91 | + $this->parser->mOutput->addHeadItem('<link rel="stylesheet" type="text/css" media="screen, projection" href="' . $bzScriptPath . '/skins/bz_main.css" />'); |
| 92 | + $this->parser->mOutput->addHeadItem('<script type="text/javascript" src="' . $bzScriptPath . '/scripts/jquery-1.2.6.min.js" ></script>'); |
| 93 | + $script=<<< EOH |
| 94 | +<script type= "text/javascript"> |
| 95 | +$(document).ready(function(){ |
| 96 | + $("div.bz_comment").hide(); |
| 97 | + $("tr.bz_bug").hover( |
| 98 | + function () { |
| 99 | + $(this).find("td div.bz_comment").show(); |
| 100 | + }, |
| 101 | + function () { |
| 102 | + $(this).find("td div.bz_comment").hide(); |
| 103 | + } |
| 104 | + ) |
| 105 | +}); |
| 106 | +</script> |
| 107 | +EOH; |
| 108 | + $this->parser->mOutput->addHeadItem($script); |
| 109 | + } |
| 110 | + |
| 111 | + $this->dbuser=$this->getProperty("user",$wgDBuser); |
| 112 | + $this->bzserver=$this->getProperty("bzserver","bzserver-property-not-set"); |
| 113 | + $this->interwiki=$this->getProperty("interwiki",null); |
| 114 | + $this->database=$wgBugzillaReports['database']; |
| 115 | + $this->host=$wgBugzillaReports['host']; |
| 116 | + $this->password=$wgBugzillaReports['password']; |
| 117 | + $this->maxrowsFromConfig= |
| 118 | + $this->getProperty("maxrows",$this->maxrowsFromConfigDefault); |
| 119 | + $this->maxrowsForBarChartFromConfig= |
| 120 | + $this->getProperty("maxrowsbar", |
| 121 | + $this->maxrowsForBarChartFromConfigDefault); |
| 122 | + |
| 123 | + $this->debug && $this->debug("Rendering BugzillaReport"); |
| 124 | + return $this->query->render().$this->getWarnings(); |
| 125 | + } |
| 126 | + |
| 127 | + # |
| 128 | + # Set value - implementation of the abstract function from BMWExtension |
| 129 | + # |
| 130 | + protected function set($name,$value) { |
| 131 | + # debug variable is store on this object |
| 132 | + if ($name=="debug") { |
| 133 | + $this->$name=$value; |
| 134 | + } else { |
| 135 | + $this->query->set($name,$value); |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + protected function getParameterRegex($name) { |
| 140 | + if ($name=="debug") { |
| 141 | + return "/^1$/"; |
| 142 | + } else { |
| 143 | + return $this->query->getParameterRegex($name); |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + function getProperty($name,$default) { |
| 148 | + global $wgBugzillaReports; |
| 149 | + $value; |
| 150 | + if (array_key_exists($name,$wgBugzillaReports)) { |
| 151 | + $value=$wgBugzillaReports[$name]; |
| 152 | + } else { |
| 153 | + $value=$default; |
| 154 | + } |
| 155 | + $this->debug && |
| 156 | + $this->debug("Env property $name=$value"); |
| 157 | + return $value; |
| 158 | + } |
| 159 | + |
| 160 | + public function getErrorMessage($key) { |
| 161 | + $args = func_get_args(); |
| 162 | + array_shift( $args ); |
| 163 | + wfLoadExtensionMessages( 'BugzillaReports' ); |
| 164 | + return '<strong class="error">BugzillaReports : '. |
| 165 | + wfMsgForContent($key,$args) . '</strong>'; |
| 166 | + } |
| 167 | +} |
Property changes on: trunk/extensions/BugzillaReports/BugzillaReports.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 168 | + Id |
Added: svn:eol-style |
2 | 169 | + native |