[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class ManiphestTransactionEditor 4 extends PhabricatorApplicationTransactionEditor { 5 6 private $heraldEmailPHIDs = array(); 7 8 public function getEditorApplicationClass() { 9 return 'PhabricatorManiphestApplication'; 10 } 11 12 public function getEditorObjectsDescription() { 13 return pht('Maniphest Tasks'); 14 } 15 16 public function getTransactionTypes() { 17 $types = parent::getTransactionTypes(); 18 19 $types[] = PhabricatorTransactions::TYPE_COMMENT; 20 $types[] = PhabricatorTransactions::TYPE_EDGE; 21 $types[] = ManiphestTransaction::TYPE_PRIORITY; 22 $types[] = ManiphestTransaction::TYPE_STATUS; 23 $types[] = ManiphestTransaction::TYPE_TITLE; 24 $types[] = ManiphestTransaction::TYPE_DESCRIPTION; 25 $types[] = ManiphestTransaction::TYPE_OWNER; 26 $types[] = ManiphestTransaction::TYPE_CCS; 27 $types[] = ManiphestTransaction::TYPE_SUBPRIORITY; 28 $types[] = ManiphestTransaction::TYPE_PROJECT_COLUMN; 29 $types[] = ManiphestTransaction::TYPE_MERGED_INTO; 30 $types[] = ManiphestTransaction::TYPE_MERGED_FROM; 31 $types[] = ManiphestTransaction::TYPE_UNBLOCK; 32 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 33 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 34 35 return $types; 36 } 37 38 protected function getCustomTransactionOldValue( 39 PhabricatorLiskDAO $object, 40 PhabricatorApplicationTransaction $xaction) { 41 42 switch ($xaction->getTransactionType()) { 43 case ManiphestTransaction::TYPE_PRIORITY: 44 if ($this->getIsNewObject()) { 45 return null; 46 } 47 return (int)$object->getPriority(); 48 case ManiphestTransaction::TYPE_STATUS: 49 if ($this->getIsNewObject()) { 50 return null; 51 } 52 return $object->getStatus(); 53 case ManiphestTransaction::TYPE_TITLE: 54 if ($this->getIsNewObject()) { 55 return null; 56 } 57 return $object->getTitle(); 58 case ManiphestTransaction::TYPE_DESCRIPTION: 59 if ($this->getIsNewObject()) { 60 return null; 61 } 62 return $object->getDescription(); 63 case ManiphestTransaction::TYPE_OWNER: 64 return nonempty($object->getOwnerPHID(), null); 65 case ManiphestTransaction::TYPE_CCS: 66 return array_values(array_unique($object->getCCPHIDs())); 67 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 68 // These are pre-populated. 69 return $xaction->getOldValue(); 70 case ManiphestTransaction::TYPE_SUBPRIORITY: 71 return $object->getSubpriority(); 72 case ManiphestTransaction::TYPE_MERGED_INTO: 73 case ManiphestTransaction::TYPE_MERGED_FROM: 74 return null; 75 } 76 } 77 78 protected function getCustomTransactionNewValue( 79 PhabricatorLiskDAO $object, 80 PhabricatorApplicationTransaction $xaction) { 81 82 switch ($xaction->getTransactionType()) { 83 case ManiphestTransaction::TYPE_PRIORITY: 84 return (int)$xaction->getNewValue(); 85 case ManiphestTransaction::TYPE_CCS: 86 return array_values(array_unique($xaction->getNewValue())); 87 case ManiphestTransaction::TYPE_OWNER: 88 return nonempty($xaction->getNewValue(), null); 89 case ManiphestTransaction::TYPE_STATUS: 90 case ManiphestTransaction::TYPE_TITLE: 91 case ManiphestTransaction::TYPE_DESCRIPTION: 92 case ManiphestTransaction::TYPE_SUBPRIORITY: 93 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 94 case ManiphestTransaction::TYPE_MERGED_INTO: 95 case ManiphestTransaction::TYPE_MERGED_FROM: 96 case ManiphestTransaction::TYPE_UNBLOCK: 97 return $xaction->getNewValue(); 98 } 99 } 100 101 protected function transactionHasEffect( 102 PhabricatorLiskDAO $object, 103 PhabricatorApplicationTransaction $xaction) { 104 105 $old = $xaction->getOldValue(); 106 $new = $xaction->getNewValue(); 107 108 switch ($xaction->getTransactionType()) { 109 case ManiphestTransaction::TYPE_CCS: 110 sort($old); 111 sort($new); 112 return ($old !== $new); 113 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 114 $new_column_phids = $new['columnPHIDs']; 115 $old_column_phids = $old['columnPHIDs']; 116 sort($new_column_phids); 117 sort($old_column_phids); 118 return ($old !== $new); 119 } 120 121 return parent::transactionHasEffect($object, $xaction); 122 } 123 124 protected function applyCustomInternalTransaction( 125 PhabricatorLiskDAO $object, 126 PhabricatorApplicationTransaction $xaction) { 127 128 switch ($xaction->getTransactionType()) { 129 case ManiphestTransaction::TYPE_PRIORITY: 130 return $object->setPriority($xaction->getNewValue()); 131 case ManiphestTransaction::TYPE_STATUS: 132 return $object->setStatus($xaction->getNewValue()); 133 case ManiphestTransaction::TYPE_TITLE: 134 return $object->setTitle($xaction->getNewValue()); 135 case ManiphestTransaction::TYPE_DESCRIPTION: 136 return $object->setDescription($xaction->getNewValue()); 137 case ManiphestTransaction::TYPE_OWNER: 138 $phid = $xaction->getNewValue(); 139 140 // Update the "ownerOrdering" column to contain the full name of the 141 // owner, if the task is assigned. 142 143 $handle = null; 144 if ($phid) { 145 $handle = id(new PhabricatorHandleQuery()) 146 ->setViewer($this->getActor()) 147 ->withPHIDs(array($phid)) 148 ->executeOne(); 149 } 150 151 if ($handle) { 152 $object->setOwnerOrdering($handle->getName()); 153 } else { 154 $object->setOwnerOrdering(null); 155 } 156 157 return $object->setOwnerPHID($phid); 158 case ManiphestTransaction::TYPE_CCS: 159 return $object->setCCPHIDs($xaction->getNewValue()); 160 case ManiphestTransaction::TYPE_SUBPRIORITY: 161 $data = $xaction->getNewValue(); 162 $new_sub = $this->getNextSubpriority( 163 $data['newPriority'], 164 $data['newSubpriorityBase'], 165 $data['direction']); 166 $object->setSubpriority($new_sub); 167 return; 168 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 169 // these do external (edge) updates 170 return; 171 case ManiphestTransaction::TYPE_MERGED_INTO: 172 $object->setStatus(ManiphestTaskStatus::getDuplicateStatus()); 173 return; 174 case ManiphestTransaction::TYPE_MERGED_FROM: 175 return; 176 } 177 } 178 179 protected function expandTransaction( 180 PhabricatorLiskDAO $object, 181 PhabricatorApplicationTransaction $xaction) { 182 183 $xactions = parent::expandTransaction($object, $xaction); 184 switch ($xaction->getTransactionType()) { 185 case ManiphestTransaction::TYPE_SUBPRIORITY: 186 $data = $xaction->getNewValue(); 187 $new_pri = $data['newPriority']; 188 if ($new_pri != $object->getPriority()) { 189 $xactions[] = id(new ManiphestTransaction()) 190 ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) 191 ->setNewValue($new_pri); 192 } 193 break; 194 default: 195 break; 196 } 197 198 return $xactions; 199 } 200 201 protected function applyCustomExternalTransaction( 202 PhabricatorLiskDAO $object, 203 PhabricatorApplicationTransaction $xaction) { 204 205 switch ($xaction->getTransactionType()) { 206 case ManiphestTransaction::TYPE_PROJECT_COLUMN: 207 $board_phid = idx($xaction->getNewValue(), 'projectPHID'); 208 if (!$board_phid) { 209 throw new Exception( 210 pht("Expected 'projectPHID' in column transaction.")); 211 } 212 213 $old_phids = idx($xaction->getOldValue(), 'columnPHIDs', array()); 214 $new_phids = idx($xaction->getNewValue(), 'columnPHIDs', array()); 215 if (count($new_phids) !== 1) { 216 throw new Exception( 217 pht("Expected exactly one 'columnPHIDs' in column transaction.")); 218 } 219 220 $columns = id(new PhabricatorProjectColumnQuery()) 221 ->setViewer($this->requireActor()) 222 ->withPHIDs($new_phids) 223 ->execute(); 224 $columns = mpull($columns, null, 'getPHID'); 225 226 $positions = id(new PhabricatorProjectColumnPositionQuery()) 227 ->setViewer($this->requireActor()) 228 ->withObjectPHIDs(array($object->getPHID())) 229 ->withBoardPHIDs(array($board_phid)) 230 ->execute(); 231 232 $before_phid = idx($xaction->getNewValue(), 'beforePHID'); 233 $after_phid = idx($xaction->getNewValue(), 'afterPHID'); 234 235 if (!$before_phid && !$after_phid && ($old_phids == $new_phids)) { 236 // If we are not moving the object between columns and also not 237 // reordering the position, this is a move on some other order 238 // (like priority). We can leave the positions untouched and just 239 // bail, there's no work to be done. 240 return; 241 } 242 243 // Otherwise, we're either moving between columns or adjusting the 244 // object's position in the "natural" ordering, so we do need to update 245 // some rows. 246 247 // Remove all existing column positions on the board. 248 249 foreach ($positions as $position) { 250 $position->delete(); 251 } 252 253 // Add the new column positions. 254 255 foreach ($new_phids as $phid) { 256 $column = idx($columns, $phid); 257 if (!$column) { 258 throw new Exception( 259 pht('No such column "%s" exists!', $phid)); 260 } 261 262 // Load the other object positions in the column. Note that we must 263 // skip implicit column creation to avoid generating a new position 264 // if the target column is a backlog column. 265 266 $other_positions = id(new PhabricatorProjectColumnPositionQuery()) 267 ->setViewer($this->requireActor()) 268 ->withColumns(array($column)) 269 ->withBoardPHIDs(array($board_phid)) 270 ->setSkipImplicitCreate(true) 271 ->execute(); 272 $other_positions = msort($other_positions, 'getOrderingKey'); 273 274 // Set up the new position object. We're going to figure out the 275 // right sequence number and then persist this object with that 276 // sequence number. 277 $new_position = id(new PhabricatorProjectColumnPosition()) 278 ->setBoardPHID($board_phid) 279 ->setColumnPHID($column->getPHID()) 280 ->setObjectPHID($object->getPHID()); 281 282 $updates = array(); 283 $sequence = 0; 284 285 // If we're just dropping this into the column without any specific 286 // position information, put it at the top. 287 if (!$before_phid && !$after_phid) { 288 $new_position->setSequence($sequence)->save(); 289 $sequence++; 290 } 291 292 foreach ($other_positions as $position) { 293 $object_phid = $position->getObjectPHID(); 294 295 // If this is the object we're moving before and we haven't 296 // saved yet, insert here. 297 if (($before_phid == $object_phid) && !$new_position->getID()) { 298 $new_position->setSequence($sequence)->save(); 299 $sequence++; 300 } 301 302 // This object goes here in the sequence; we might need to update 303 // the row. 304 if ($sequence != $position->getSequence()) { 305 $updates[$position->getID()] = $sequence; 306 } 307 $sequence++; 308 309 // If this is the object we're moving after and we haven't saved 310 // yet, insert here. 311 if (($after_phid == $object_phid) && !$new_position->getID()) { 312 $new_position->setSequence($sequence)->save(); 313 $sequence++; 314 } 315 } 316 317 // We should have found a place to put it. 318 if (!$new_position->getID()) { 319 throw new Exception( 320 pht('Unable to find a place to insert object on column!')); 321 } 322 323 // If we changed other objects' column positions, bulk reorder them. 324 325 if ($updates) { 326 $position = new PhabricatorProjectColumnPosition(); 327 $conn_w = $position->establishConnection('w'); 328 329 $pairs = array(); 330 foreach ($updates as $id => $sequence) { 331 // This is ugly because MySQL gets upset with us if it is 332 // configured strictly and we attempt inserts which can't work. 333 // We'll never actually do these inserts since they'll always 334 // collide (triggering the ON DUPLICATE KEY logic), so we just 335 // provide dummy values in order to get there. 336 337 $pairs[] = qsprintf( 338 $conn_w, 339 '(%d, %d, "", "", "")', 340 $id, 341 $sequence); 342 } 343 344 queryfx( 345 $conn_w, 346 'INSERT INTO %T (id, sequence, boardPHID, columnPHID, objectPHID) 347 VALUES %Q ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)', 348 $position->getTableName(), 349 implode(', ', $pairs)); 350 } 351 } 352 break; 353 default: 354 break; 355 } 356 } 357 358 protected function applyFinalEffects( 359 PhabricatorLiskDAO $object, 360 array $xactions) { 361 362 // When we change the status of a task, update tasks this tasks blocks 363 // with a message to the effect of "alincoln resolved blocking task Txxx." 364 $unblock_xaction = null; 365 foreach ($xactions as $xaction) { 366 switch ($xaction->getTransactionType()) { 367 case ManiphestTransaction::TYPE_STATUS: 368 $unblock_xaction = $xaction; 369 break; 370 } 371 } 372 373 if ($unblock_xaction !== null) { 374 $blocked_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 375 $object->getPHID(), 376 PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK); 377 if ($blocked_phids) { 378 // In theory we could apply these through policies, but that seems a 379 // little bit surprising. For now, use the actor's vision. 380 $blocked_tasks = id(new ManiphestTaskQuery()) 381 ->setViewer($this->getActor()) 382 ->withPHIDs($blocked_phids) 383 ->execute(); 384 385 $old = $unblock_xaction->getOldValue(); 386 $new = $unblock_xaction->getNewValue(); 387 388 foreach ($blocked_tasks as $blocked_task) { 389 $unblock_xactions = array(); 390 391 $unblock_xactions[] = id(new ManiphestTransaction()) 392 ->setTransactionType(ManiphestTransaction::TYPE_UNBLOCK) 393 ->setOldValue(array($object->getPHID() => $old)) 394 ->setNewValue(array($object->getPHID() => $new)); 395 396 id(new ManiphestTransactionEditor()) 397 ->setActor($this->getActor()) 398 ->setActingAsPHID($this->getActingAsPHID()) 399 ->setContentSource($this->getContentSource()) 400 ->setContinueOnNoEffect(true) 401 ->setContinueOnMissingFields(true) 402 ->applyTransactions($blocked_task, $unblock_xactions); 403 } 404 } 405 } 406 407 return $xactions; 408 } 409 410 protected function shouldSendMail( 411 PhabricatorLiskDAO $object, 412 array $xactions) { 413 414 $xactions = mfilter($xactions, 'shouldHide', true); 415 return $xactions; 416 } 417 418 protected function getMailSubjectPrefix() { 419 return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix'); 420 } 421 422 protected function getMailThreadID(PhabricatorLiskDAO $object) { 423 return 'maniphest-task-'.$object->getPHID(); 424 } 425 426 protected function getMailTo(PhabricatorLiskDAO $object) { 427 return array( 428 $object->getOwnerPHID(), 429 $this->getActingAsPHID(), 430 ); 431 } 432 433 protected function getMailCC(PhabricatorLiskDAO $object) { 434 $phids = array(); 435 436 foreach ($object->getCCPHIDs() as $phid) { 437 $phids[] = $phid; 438 } 439 440 foreach (parent::getMailCC($object) as $phid) { 441 $phids[] = $phid; 442 } 443 444 foreach ($this->heraldEmailPHIDs as $phid) { 445 $phids[] = $phid; 446 } 447 448 return $phids; 449 } 450 451 public function getMailTagsMap() { 452 return array( 453 ManiphestTransaction::MAILTAG_STATUS => 454 pht("A task's status changes."), 455 ManiphestTransaction::MAILTAG_OWNER => 456 pht("A task's owner changes."), 457 ManiphestTransaction::MAILTAG_PRIORITY => 458 pht("A task's priority changes."), 459 ManiphestTransaction::MAILTAG_CC => 460 pht("A task's subscribers change."), 461 ManiphestTransaction::MAILTAG_PROJECTS => 462 pht("A task's associated projects change."), 463 ManiphestTransaction::MAILTAG_UNBLOCK => 464 pht('One of the tasks a task is blocked by changes status.'), 465 ManiphestTransaction::MAILTAG_COLUMN => 466 pht('A task is moved between columns on a workboard.'), 467 ManiphestTransaction::MAILTAG_COMMENT => 468 pht('Someone comments on a task.'), 469 ManiphestTransaction::MAILTAG_OTHER => 470 pht('Other task activity not listed above occurs.'), 471 ); 472 } 473 474 protected function buildReplyHandler(PhabricatorLiskDAO $object) { 475 return id(new ManiphestReplyHandler()) 476 ->setMailReceiver($object); 477 } 478 479 protected function buildMailTemplate(PhabricatorLiskDAO $object) { 480 $id = $object->getID(); 481 $title = $object->getTitle(); 482 483 return id(new PhabricatorMetaMTAMail()) 484 ->setSubject("T{$id}: {$title}") 485 ->addHeader('Thread-Topic', "T{$id}: ".$object->getOriginalTitle()); 486 } 487 488 protected function buildMailBody( 489 PhabricatorLiskDAO $object, 490 array $xactions) { 491 492 $body = parent::buildMailBody($object, $xactions); 493 494 if ($this->getIsNewObject()) { 495 $body->addTextSection( 496 pht('TASK DESCRIPTION'), 497 $object->getDescription()); 498 } 499 500 $body->addLinkSection( 501 pht('TASK DETAIL'), 502 PhabricatorEnv::getProductionURI('/T'.$object->getID())); 503 504 505 $board_phids = array(); 506 $type_column = ManiphestTransaction::TYPE_PROJECT_COLUMN; 507 foreach ($xactions as $xaction) { 508 if ($xaction->getTransactionType() == $type_column) { 509 $new = $xaction->getNewValue(); 510 $project_phid = idx($new, 'projectPHID'); 511 if ($project_phid) { 512 $board_phids[] = $project_phid; 513 } 514 } 515 } 516 517 if ($board_phids) { 518 $projects = id(new PhabricatorProjectQuery()) 519 ->setViewer($this->requireActor()) 520 ->withPHIDs($board_phids) 521 ->execute(); 522 523 foreach ($projects as $project) { 524 $body->addLinkSection( 525 pht('WORKBOARD'), 526 PhabricatorEnv::getProductionURI( 527 '/project/board/'.$project->getID().'/')); 528 } 529 } 530 531 532 return $body; 533 } 534 535 protected function shouldPublishFeedStory( 536 PhabricatorLiskDAO $object, 537 array $xactions) { 538 return $this->shouldSendMail($object, $xactions); 539 } 540 541 protected function supportsSearch() { 542 return true; 543 } 544 545 protected function shouldApplyHeraldRules( 546 PhabricatorLiskDAO $object, 547 array $xactions) { 548 return true; 549 } 550 551 protected function buildHeraldAdapter( 552 PhabricatorLiskDAO $object, 553 array $xactions) { 554 555 return id(new HeraldManiphestTaskAdapter()) 556 ->setTask($object); 557 } 558 559 protected function didApplyHeraldRules( 560 PhabricatorLiskDAO $object, 561 HeraldAdapter $adapter, 562 HeraldTranscript $transcript) { 563 564 // TODO: Convert these to transactions. The way Maniphest deals with these 565 // transactions is currently unconventional and messy. 566 567 $save_again = false; 568 $cc_phids = $adapter->getCcPHIDs(); 569 if ($cc_phids) { 570 $existing_cc = $object->getCCPHIDs(); 571 $new_cc = array_unique(array_merge($cc_phids, $existing_cc)); 572 $object->setCCPHIDs($new_cc); 573 $object->save(); 574 } 575 576 $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 577 578 $xactions = array(); 579 580 $assign_phid = $adapter->getAssignPHID(); 581 if ($assign_phid) { 582 $xactions[] = id(new ManiphestTransaction()) 583 ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 584 ->setNewValue($assign_phid); 585 } 586 587 $project_phids = $adapter->getProjectPHIDs(); 588 if ($project_phids) { 589 $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; 590 $xactions[] = id(new ManiphestTransaction()) 591 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 592 ->setMetadataValue('edge:type', $project_type) 593 ->setNewValue( 594 array( 595 '+' => array_fuse($project_phids), 596 )); 597 } 598 599 return $xactions; 600 } 601 602 protected function requireCapabilities( 603 PhabricatorLiskDAO $object, 604 PhabricatorApplicationTransaction $xaction) { 605 606 parent::requireCapabilities($object, $xaction); 607 608 $app_capability_map = array( 609 ManiphestTransaction::TYPE_PRIORITY => 610 ManiphestEditPriorityCapability::CAPABILITY, 611 ManiphestTransaction::TYPE_STATUS => 612 ManiphestEditStatusCapability::CAPABILITY, 613 ManiphestTransaction::TYPE_OWNER => 614 ManiphestEditAssignCapability::CAPABILITY, 615 PhabricatorTransactions::TYPE_EDIT_POLICY => 616 ManiphestEditPoliciesCapability::CAPABILITY, 617 PhabricatorTransactions::TYPE_VIEW_POLICY => 618 ManiphestEditPoliciesCapability::CAPABILITY, 619 ); 620 621 622 $transaction_type = $xaction->getTransactionType(); 623 624 $app_capability = null; 625 if ($transaction_type == PhabricatorTransactions::TYPE_EDGE) { 626 switch ($xaction->getMetadataValue('edge:type')) { 627 case PhabricatorProjectObjectHasProjectEdgeType::EDGECONST: 628 $app_capability = ManiphestEditProjectsCapability::CAPABILITY; 629 break; 630 } 631 } else { 632 $app_capability = idx($app_capability_map, $transaction_type); 633 } 634 635 if ($app_capability) { 636 $app = id(new PhabricatorApplicationQuery()) 637 ->setViewer($this->getActor()) 638 ->withClasses(array('PhabricatorManiphestApplication')) 639 ->executeOne(); 640 PhabricatorPolicyFilter::requireCapability( 641 $this->getActor(), 642 $app, 643 $app_capability); 644 } 645 } 646 647 protected function adjustObjectForPolicyChecks( 648 PhabricatorLiskDAO $object, 649 array $xactions) { 650 651 $copy = parent::adjustObjectForPolicyChecks($object, $xactions); 652 foreach ($xactions as $xaction) { 653 switch ($xaction->getTransactionType()) { 654 case ManiphestTransaction::TYPE_OWNER: 655 $copy->setOwnerPHID($xaction->getNewValue()); 656 break; 657 default: 658 continue; 659 } 660 } 661 662 return $copy; 663 } 664 665 private function getNextSubpriority($pri, $sub, $dir = '>') { 666 switch ($dir) { 667 case '>': 668 $order = 'ASC'; 669 break; 670 case '<': 671 $order = 'DESC'; 672 break; 673 default: 674 throw new Exception('$dir must be ">" or "<".'); 675 break; 676 } 677 678 if ($sub === null) { 679 $base = 0; 680 } else { 681 $base = $sub; 682 } 683 684 if ($sub === null) { 685 $next = id(new ManiphestTask())->loadOneWhere( 686 'priority = %d ORDER BY subpriority %Q LIMIT 1', 687 $pri, 688 $order); 689 if ($next) { 690 if ($dir == '>') { 691 return $next->getSubpriority() - ((double)(2 << 16)); 692 } else { 693 return $next->getSubpriority() + ((double)(2 << 16)); 694 } 695 } 696 } else { 697 $next = id(new ManiphestTask())->loadOneWhere( 698 'priority = %d AND subpriority %Q %f ORDER BY subpriority %Q LIMIT 1', 699 $pri, 700 $dir, 701 $sub, 702 $order); 703 if ($next) { 704 return ($sub + $next->getSubpriority()) / 2; 705 } 706 } 707 708 if ($dir == '>') { 709 return $base + (double)(2 << 32); 710 } else { 711 return $base - (double)(2 << 32); 712 } 713 } 714 715 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |