1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 12:52:42 +01:00

Converting badge recipients from Edge to BadgeAward table

Summary: Ref T8996, Convert badge recipients from Edges to actual BadgeAward objects

Test Plan: Create badge, award it to recipient. Make sure adding/removing recipients works. (Still need to migrate exisiting recipients to new table and need to create activity feed blurbs)

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: chad, Korvin

Maniphest Tasks: T8996

Differential Revision: https://secure.phabricator.com/D15014
This commit is contained in:
lkassianik 2016-01-13 11:33:46 -08:00
parent c286d2b441
commit 0330ea575d
15 changed files with 296 additions and 74 deletions

View file

@ -0,0 +1,10 @@
CREATE TABLE {$NAMESPACE}_badges.badges_award (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
badgePHID VARBINARY(64) NOT NULL,
recipientPHID VARBINARY(64) NOT NULL,
awarderPHID varbinary(64) NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_badge` (badgePHID, recipientPHID),
KEY `key_recipient` (recipientPHID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,6 @@
/* PhabricatorBadgeHasRecipientEdgeType::TYPECONST = 59 */
INSERT IGNORE INTO {$NAMESPACE}_badges.badges_award
(badgePHID, recipientPHID, awarderPHID, dateCreated, dateModified)
SELECT src, dst, 'PHID-VOID-00000000000000000000', dateCreated, dateCreated
FROM {$NAMESPACE}_badges.edge WHERE type = 59;

View file

@ -1870,6 +1870,8 @@ phutil_register_library_map(array(
'PhabricatorBadgeHasRecipientEdgeType' => 'applications/badges/edge/PhabricatorBadgeHasRecipientEdgeType.php', 'PhabricatorBadgeHasRecipientEdgeType' => 'applications/badges/edge/PhabricatorBadgeHasRecipientEdgeType.php',
'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php', 'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php',
'PhabricatorBadgesArchiveController' => 'applications/badges/controller/PhabricatorBadgesArchiveController.php', 'PhabricatorBadgesArchiveController' => 'applications/badges/controller/PhabricatorBadgesArchiveController.php',
'PhabricatorBadgesAward' => 'applications/badges/storage/PhabricatorBadgesAward.php',
'PhabricatorBadgesAwardQuery' => 'applications/badges/query/PhabricatorBadgesAwardQuery.php',
'PhabricatorBadgesBadge' => 'applications/badges/storage/PhabricatorBadgesBadge.php', 'PhabricatorBadgesBadge' => 'applications/badges/storage/PhabricatorBadgesBadge.php',
'PhabricatorBadgesCommentController' => 'applications/badges/controller/PhabricatorBadgesCommentController.php', 'PhabricatorBadgesCommentController' => 'applications/badges/controller/PhabricatorBadgesCommentController.php',
'PhabricatorBadgesController' => 'applications/badges/controller/PhabricatorBadgesController.php', 'PhabricatorBadgesController' => 'applications/badges/controller/PhabricatorBadgesController.php',
@ -6231,6 +6233,12 @@ phutil_register_library_map(array(
'PhabricatorBadgeHasRecipientEdgeType' => 'PhabricatorEdgeType', 'PhabricatorBadgeHasRecipientEdgeType' => 'PhabricatorEdgeType',
'PhabricatorBadgesApplication' => 'PhabricatorApplication', 'PhabricatorBadgesApplication' => 'PhabricatorApplication',
'PhabricatorBadgesArchiveController' => 'PhabricatorBadgesController', 'PhabricatorBadgesArchiveController' => 'PhabricatorBadgesController',
'PhabricatorBadgesAward' => array(
'PhabricatorBadgesDAO',
'PhabricatorDestructibleInterface',
'PhabricatorPolicyInterface',
),
'PhabricatorBadgesAwardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorBadgesBadge' => array( 'PhabricatorBadgesBadge' => array(
'PhabricatorBadgesDAO', 'PhabricatorBadgesDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',

View file

@ -2,7 +2,6 @@
final class PhabricatorBadgesEditController extends final class PhabricatorBadgesEditController extends
PhabricatorBadgesController { PhabricatorBadgesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorBadgesEditEngine()) return id(new PhabricatorBadgesEditEngine())
->setController($this) ->setController($this)

View file

@ -6,6 +6,7 @@ final class PhabricatorBadgesEditRecipientsController
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$id = $request->getURIData('id'); $id = $request->getURIData('id');
$xactions = array();
$badge = id(new PhabricatorBadgesQuery()) $badge = id(new PhabricatorBadgesQuery())
->setViewer($viewer) ->setViewer($viewer)
@ -21,30 +22,23 @@ final class PhabricatorBadgesEditRecipientsController
return new Aphront404Response(); return new Aphront404Response();
} }
$recipient_phids = $badge->getRecipientPHIDs();
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/'); $view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
$awards = $badge->getAwards();
$recipient_phids = mpull($awards, 'getRecipientPHID');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$recipient_spec = array(); $award_phids = array();
$remove = $request->getStr('remove');
if ($remove) {
$recipient_spec['-'] = array_fuse(array($remove));
}
$add_recipients = $request->getArr('phids'); $add_recipients = $request->getArr('phids');
if ($add_recipients) { if ($add_recipients) {
$recipient_spec['+'] = array_fuse($add_recipients); foreach ($add_recipients as $phid) {
$award_phids[] = $phid;
}
} }
$type_recipient = PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
$xactions = array();
$xactions[] = id(new PhabricatorBadgesTransaction()) $xactions[] = id(new PhabricatorBadgesTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_AWARD)
->setMetadataValue('edge:type', $type_recipient) ->setNewValue($award_phids);
->setNewValue($recipient_spec);
$editor = id(new PhabricatorBadgesEditor($badge)) $editor = id(new PhabricatorBadgesEditor($badge))
->setActor($viewer) ->setActor($viewer)

View file

@ -21,7 +21,8 @@ final class PhabricatorBadgesRemoveRecipientsController
return new Aphront404Response(); return new Aphront404Response();
} }
$recipient_phids = $badge->getRecipientPHIDs(); $awards = $badge->getAwards();
$recipient_phids = mpull($awards, 'getRecipientPHID');
$remove_phid = $request->getStr('phid'); $remove_phid = $request->getStr('phid');
if (!in_array($remove_phid, $recipient_phids)) { if (!in_array($remove_phid, $recipient_phids)) {
@ -31,17 +32,10 @@ final class PhabricatorBadgesRemoveRecipientsController
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/'); $view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$recipient_spec = array();
$recipient_spec['-'] = array($remove_phid => $remove_phid);
$type_recipient = PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
$xactions = array(); $xactions = array();
$xactions[] = id(new PhabricatorBadgesTransaction()) $xactions[] = id(new PhabricatorBadgesTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_REVOKE)
->setMetadataValue('edge:type', $type_recipient) ->setNewValue(array($remove_phid));
->setNewValue($recipient_spec);
$editor = id(new PhabricatorBadgesEditor($badge)) $editor = id(new PhabricatorBadgesEditor($badge))
->setActor($viewer) ->setActor($viewer)

View file

@ -50,7 +50,8 @@ final class PhabricatorBadgesViewController
$badge, $badge,
new PhabricatorBadgesTransactionQuery()); new PhabricatorBadgesTransactionQuery());
$recipient_phids = $badge->getRecipientPHIDs(); $awards = $badge->getAwards();
$recipient_phids = mpull($awards, 'getRecipientPHID');
$recipient_phids = array_reverse($recipient_phids); $recipient_phids = array_reverse($recipient_phids);
$handles = $this->loadViewerHandles($recipient_phids); $handles = $this->loadViewerHandles($recipient_phids);

View file

@ -20,6 +20,8 @@ final class PhabricatorBadgesEditor
$types[] = PhabricatorBadgesTransaction::TYPE_ICON; $types[] = PhabricatorBadgesTransaction::TYPE_ICON;
$types[] = PhabricatorBadgesTransaction::TYPE_STATUS; $types[] = PhabricatorBadgesTransaction::TYPE_STATUS;
$types[] = PhabricatorBadgesTransaction::TYPE_QUALITY; $types[] = PhabricatorBadgesTransaction::TYPE_QUALITY;
$types[] = PhabricatorBadgesTransaction::TYPE_AWARD;
$types[] = PhabricatorBadgesTransaction::TYPE_REVOKE;
$types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_EDGE;
@ -44,6 +46,11 @@ final class PhabricatorBadgesEditor
return $object->getQuality(); return $object->getQuality();
case PhabricatorBadgesTransaction::TYPE_STATUS: case PhabricatorBadgesTransaction::TYPE_STATUS:
return $object->getStatus(); return $object->getStatus();
case PhabricatorBadgesTransaction::TYPE_AWARD:
$award_phids = mpull($object->getAwards(), 'getRecipientPHID');
return $award_phids;
case PhabricatorBadgesTransaction::TYPE_REVOKE:
return null;
} }
return parent::getCustomTransactionOldValue($object, $xaction); return parent::getCustomTransactionOldValue($object, $xaction);
@ -60,6 +67,8 @@ final class PhabricatorBadgesEditor
case PhabricatorBadgesTransaction::TYPE_ICON: case PhabricatorBadgesTransaction::TYPE_ICON:
case PhabricatorBadgesTransaction::TYPE_STATUS: case PhabricatorBadgesTransaction::TYPE_STATUS:
case PhabricatorBadgesTransaction::TYPE_QUALITY: case PhabricatorBadgesTransaction::TYPE_QUALITY:
case PhabricatorBadgesTransaction::TYPE_AWARD:
case PhabricatorBadgesTransaction::TYPE_REVOKE:
return $xaction->getNewValue(); return $xaction->getNewValue();
} }
@ -90,6 +99,9 @@ final class PhabricatorBadgesEditor
case PhabricatorBadgesTransaction::TYPE_STATUS: case PhabricatorBadgesTransaction::TYPE_STATUS:
$object->setStatus($xaction->getNewValue()); $object->setStatus($xaction->getNewValue());
return; return;
case PhabricatorBadgesTransaction::TYPE_AWARD:
case PhabricatorBadgesTransaction::TYPE_REVOKE:
return;
} }
return parent::applyCustomInternalTransaction($object, $xaction); return parent::applyCustomInternalTransaction($object, $xaction);
@ -108,6 +120,34 @@ final class PhabricatorBadgesEditor
case PhabricatorBadgesTransaction::TYPE_STATUS: case PhabricatorBadgesTransaction::TYPE_STATUS:
case PhabricatorBadgesTransaction::TYPE_QUALITY: case PhabricatorBadgesTransaction::TYPE_QUALITY:
return; return;
case PhabricatorBadgesTransaction::TYPE_REVOKE:
$revoked_recipient_phids = $xaction->getNewValue();
$awards = $object->getAwards();
$awards = mpull($awards, null, 'getRecipientPHID');
foreach ($revoked_recipient_phids as $phid) {
$awards[$phid]->delete();
}
$object->attachAwards($awards);
return;
case PhabricatorBadgesTransaction::TYPE_AWARD:
$recipient_phids = $xaction->getNewValue();
$awards = $object->getAwards();
$awards = mpull($awards, null, 'getRecipientPHID');
foreach ($recipient_phids as $phid) {
$award = idx($awards, $phid);
if (!$award) {
$award = PhabricatorBadgesAward::initializeNewBadgesAward(
$this->getActor(),
$object,
$phid);
$award->save();
$awards[] = $award;
}
}
$object->attachAwards($awards);
return;
} }
return parent::applyCustomExternalTransaction($object, $xaction); return parent::applyCustomExternalTransaction($object, $xaction);

View file

@ -0,0 +1,87 @@
<?php
final class PhabricatorBadgesAwardQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $badgePHIDs;
private $recipientPHIDs;
private $awarderPHIDs;
protected function willFilterPage(array $awards) {
$badges = id(new PhabricatorBadgesQuery())
->setViewer($this->getViewer())
->withRecipientPHIDs(mpull($awards, null, 'getRecipientPHID'))
->execute();
$badges = mpull($badges, null, 'getPHID');
foreach ($awards as $key => $award) {
$award_badge = idx($badges, $award->getBadgePHID());
if ($award_badge === null) {
$this->didRejectResult($award);
unset($awards[$key]);
continue;
}
$award->attachBadge($award_badge);
}
return $awards;
}
public function withBadgePHIDs(array $phids) {
$this->badgePHIDs = $phids;
return $this;
}
public function withRecipientPHIDs(array $phids) {
$this->recipientPHIDs = $phids;
return $this;
}
public function withAwarderPHIDs(array $phids) {
$this->awarderPHIDs = $phids;
return $this;
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
public function newResultObject() {
return new PhabricatorBadgesAward();
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->badgePHIDs !== null) {
$where[] = qsprintf(
$conn,
'badgePHID IN (%Ls)',
$this->badgePHIDs);
}
if ($this->recipientPHIDs !== null) {
$where[] = qsprintf(
$conn,
'recipientPHID IN (%Ls)',
$this->recipientPHIDs);
}
if ($this->awarderPHIDs !== null) {
$where[] = qsprintf(
$conn,
'awarderPHID IN (%Ls)',
$this->awarderPHIDs);
}
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorBadgesApplication';
}
}

View file

@ -50,22 +50,17 @@ final class PhabricatorBadgesQuery
} }
protected function didFilterPage(array $badges) { protected function didFilterPage(array $badges) {
if ($this->needRecipients) { if ($this->needRecipients) {
$edge_query = id(new PhabricatorEdgeQuery()) $query = id(new PhabricatorBadgesAwardQuery())
->withSourcePHIDs(mpull($badges, 'getPHID')) ->setViewer($this->getViewer())
->withEdgeTypes( ->withBadgePHIDs(mpull($badges, 'getPHID'))
array( ->execute();
PhabricatorBadgeHasRecipientEdgeType::EDGECONST,
)); $awards = mgroup($query, 'getBadgePHID');
$edge_query->execute();
foreach ($badges as $badge) { foreach ($badges as $badge) {
$phids = $edge_query->getDestinationPHIDs( $badge_awards = idx($awards, $badge->getPHID(), array());
array( $badge->attachAwards($badge_awards);
$badge->getPHID(),
));
$badge->attachRecipientPHIDs($phids);
} }
} }

View file

@ -0,0 +1,83 @@
<?php
final class PhabricatorBadgesAward extends PhabricatorBadgesDAO
implements
PhabricatorDestructibleInterface,
PhabricatorPolicyInterface {
protected $badgePHID;
protected $recipientPHID;
protected $awarderPHID;
private $badge = self::ATTACHABLE;
public static function initializeNewBadgesAward(
PhabricatorUser $actor,
PhabricatorBadgesBadge $badge,
$recipient_phid) {
return id(new self())
->setRecipientPHID($recipient_phid)
->setBadgePHID($badge->getPHID())
->setAwarderPHID($actor->getPHID())
->attachBadge($badge);
}
protected function getConfiguration() {
return array(
self::CONFIG_KEY_SCHEMA => array(
'key_badge' => array(
'columns' => array('badgePHID', 'recipientPHID'),
'unique' => true,
),
'key_recipient' => array(
'columns' => array('recipientPHID'),
),
),
) + parent::getConfiguration();
}
public function attachBadge(PhabricatorBadgesBadge $badge) {
$this->badge = $badge;
return $this;
}
public function getBadge() {
return $this->assertAttached($this->badge);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getBadge()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
}

View file

@ -19,7 +19,7 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO
protected $status; protected $status;
protected $creatorPHID; protected $creatorPHID;
private $recipientPHIDs = self::ATTACHABLE; private $awards = self::ATTACHABLE;
const STATUS_ACTIVE = 'open'; const STATUS_ACTIVE = 'open';
const STATUS_ARCHIVED = 'closed'; const STATUS_ARCHIVED = 'closed';
@ -102,13 +102,13 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO
return ($this->getStatus() == self::STATUS_ARCHIVED); return ($this->getStatus() == self::STATUS_ARCHIVED);
} }
public function attachRecipientPHIDs(array $phids) { public function attachAwards(array $awards) {
$this->recipientPHIDs = $phids; $this->awards = $awards;
return $this; return $this;
} }
public function getRecipientPHIDs() { public function getAwards() {
return $this->assertAttached($this->recipientPHIDs); return $this->assertAttached($this->awards);
} }
public function getViewURI() { public function getViewURI() {
@ -197,6 +197,15 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO
public function destroyObjectPermanently( public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) { PhabricatorDestructionEngine $engine) {
$awards = id(new PhabricatorBadgesAwardQuery())
->setViewer($engine->getViewer())
->withBadgePHIDs(array($this->getPHID()))
->execute();
foreach ($awards as $award) {
$engine->destroyObjectPermanently($award);
}
$this->openTransaction(); $this->openTransaction();
$this->delete(); $this->delete();
$this->saveTransaction(); $this->saveTransaction();

View file

@ -9,6 +9,8 @@ final class PhabricatorBadgesTransaction
const TYPE_ICON = 'badges:icon'; const TYPE_ICON = 'badges:icon';
const TYPE_STATUS = 'badges:status'; const TYPE_STATUS = 'badges:status';
const TYPE_FLAVOR = 'badges:flavor'; const TYPE_FLAVOR = 'badges:flavor';
const TYPE_AWARD = 'badges:award';
const TYPE_REVOKE = 'badges:revoke';
const MAILTAG_DETAILS = 'badges:details'; const MAILTAG_DETAILS = 'badges:details';
const MAILTAG_COMMENT = 'badges:comment'; const MAILTAG_COMMENT = 'badges:comment';

View file

@ -155,20 +155,17 @@ final class PhabricatorPeopleQuery
} }
if ($this->needBadges) { if ($this->needBadges) {
$edge_query = id(new PhabricatorEdgeQuery()) $awards = id(new PhabricatorBadgesAwardQuery())
->withSourcePHIDs(mpull($users, 'getPHID')) ->setViewer($this->getViewer())
->withEdgeTypes( ->withRecipientPHIDs(mpull($users, 'getPHID'))
array( ->execute();
PhabricatorRecipientHasBadgeEdgeType::EDGECONST,
)); $awards = mgroup($awards, 'getRecipientPHID');
$edge_query->execute();
foreach ($users as $user) { foreach ($users as $user) {
$phids = $edge_query->getDestinationPHIDs( $user_awards = idx($awards, $user->getPHID(), array());
array( $badge_phids = mpull($user_awards, 'getBadgePHID');
$user->getPHID(), $user->attachBadgePHIDs($badge_phids);
));
$user->attachBadgePHIDs($phids);
} }
} }

View file

@ -250,31 +250,28 @@ final class PHUITimelineView extends AphrontView {
return; return;
} }
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($user_phids)
->withEdgeTypes(array($badge_edge_type));
$edges->execute();
$badge_phids = $edges->getDestinationPHIDs(); $awards = id(new PhabricatorBadgesAwardQuery())
if (!$badge_phids) { ->setViewer($this->getViewer())
return; ->withRecipientPHIDs($user_phids)
}
$all_badges = id(new PhabricatorBadgesQuery())
->setViewer($viewer)
->withPHIDs($badge_phids)
->withStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE))
->execute(); ->execute();
$all_badges = mpull($all_badges, null, 'getPHID');
$awards = mgroup($awards, 'getRecipientPHID');
foreach ($events as $event) { foreach ($events as $event) {
$author_phid = $event->getAuthorPHID(); $author_awards = idx($awards, $event->getAuthorPHID(), array());
$event_phids = $edges->getDestinationPHIDs(array($author_phid)); $badges = array();
$badges = array_select_keys($all_badges, $event_phids); foreach ($author_awards as $award) {
$badge = $award->getBadge();
if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) {
$badges[$award->getBadgePHID()] = $badge;
}
}
// TODO: Pick the "best" badges in some smart way. For now, just pick // TODO: Pick the "best" badges in some smart way. For now, just pick
// the first two. // the first two.
$badges = array_slice($badges, 0, 2); $badges = array_slice($badges, 0, 2);
foreach ($badges as $badge) { foreach ($badges as $badge) {
$badge_view = id(new PHUIBadgeMiniView()) $badge_view = id(new PHUIBadgeMiniView())
->setIcon($badge->getIcon()) ->setIcon($badge->getIcon())