Changeset View
Changeset View
Standalone View
Standalone View
src/applications/auth/storage/PhabricatorAuthContactNumber.php
<?php | <?php | ||||
final class PhabricatorAuthContactNumber | final class PhabricatorAuthContactNumber | ||||
extends PhabricatorAuthDAO | extends PhabricatorAuthDAO | ||||
implements | implements | ||||
PhabricatorApplicationTransactionInterface, | PhabricatorApplicationTransactionInterface, | ||||
PhabricatorPolicyInterface, | PhabricatorPolicyInterface, | ||||
PhabricatorDestructibleInterface { | PhabricatorDestructibleInterface { | ||||
protected $objectPHID; | protected $objectPHID; | ||||
protected $contactNumber; | protected $contactNumber; | ||||
protected $uniqueKey; | protected $uniqueKey; | ||||
protected $status; | protected $status; | ||||
protected $isPrimary; | |||||
protected $properties = array(); | protected $properties = array(); | ||||
const STATUS_ACTIVE = 'active'; | const STATUS_ACTIVE = 'active'; | ||||
const STATUS_DISABLED = 'disabled'; | const STATUS_DISABLED = 'disabled'; | ||||
protected function getConfiguration() { | protected function getConfiguration() { | ||||
return array( | return array( | ||||
self::CONFIG_SERIALIZATION => array( | self::CONFIG_SERIALIZATION => array( | ||||
'properties' => self::SERIALIZATION_JSON, | 'properties' => self::SERIALIZATION_JSON, | ||||
), | ), | ||||
self::CONFIG_AUX_PHID => true, | self::CONFIG_AUX_PHID => true, | ||||
self::CONFIG_COLUMN_SCHEMA => array( | self::CONFIG_COLUMN_SCHEMA => array( | ||||
'contactNumber' => 'text255', | 'contactNumber' => 'text255', | ||||
'status' => 'text32', | 'status' => 'text32', | ||||
'uniqueKey' => 'bytes12?', | 'uniqueKey' => 'bytes12?', | ||||
'isPrimary' => 'bool', | |||||
), | ), | ||||
self::CONFIG_KEY_SCHEMA => array( | self::CONFIG_KEY_SCHEMA => array( | ||||
'key_object' => array( | 'key_object' => array( | ||||
'columns' => array('objectPHID'), | 'columns' => array('objectPHID'), | ||||
), | ), | ||||
'key_unique' => array( | 'key_unique' => array( | ||||
'columns' => array('uniqueKey'), | 'columns' => array('uniqueKey'), | ||||
'unique' => true, | 'unique' => true, | ||||
), | ), | ||||
), | ), | ||||
) + parent::getConfiguration(); | ) + parent::getConfiguration(); | ||||
} | } | ||||
public static function initializeNewContactNumber($object) { | public static function initializeNewContactNumber($object) { | ||||
return id(new self()) | return id(new self()) | ||||
->setStatus(self::STATUS_ACTIVE) | ->setStatus(self::STATUS_ACTIVE) | ||||
->setObjectPHID($object->getPHID()); | ->setObjectPHID($object->getPHID()) | ||||
->setIsPrimary(0); | |||||
} | } | ||||
public function getPHIDType() { | public function getPHIDType() { | ||||
return PhabricatorAuthContactNumberPHIDType::TYPECONST; | return PhabricatorAuthContactNumberPHIDType::TYPECONST; | ||||
} | } | ||||
public function getURI() { | public function getURI() { | ||||
return urisprintf('/auth/contact/%s/', $this->getID()); | return urisprintf('/auth/contact/%s/', $this->getID()); | ||||
Show All 13 Lines | final class PhabricatorAuthContactNumber | ||||
public function newIconView() { | public function newIconView() { | ||||
if ($this->isDisabled()) { | if ($this->isDisabled()) { | ||||
return id(new PHUIIconView()) | return id(new PHUIIconView()) | ||||
->setIcon('fa-ban', 'grey') | ->setIcon('fa-ban', 'grey') | ||||
->setTooltip(pht('Disabled')); | ->setTooltip(pht('Disabled')); | ||||
} | } | ||||
if ($this->getIsPrimary()) { | |||||
return id(new PHUIIconView()) | return id(new PHUIIconView()) | ||||
->setIcon('fa-mobile', 'green') | ->setIcon('fa-certificate', 'blue') | ||||
->setTooltip(pht('Primary Number')); | |||||
} | |||||
return id(new PHUIIconView()) | |||||
->setIcon('fa-hashtag', 'bluegrey') | |||||
->setTooltip(pht('Active Phone Number')); | ->setTooltip(pht('Active Phone Number')); | ||||
} | } | ||||
public function newUniqueKey() { | public function newUniqueKey() { | ||||
$parts = array( | $parts = array( | ||||
// This is future-proofing for a world where we have multiple types | // This is future-proofing for a world where we have multiple types | ||||
// of contact numbers, so we might be able to avoid re-hashing | // of contact numbers, so we might be able to avoid re-hashing | ||||
// everything. | // everything. | ||||
Show All 10 Lines | public function save() { | ||||
// We require that active contact numbers be unique, but it's okay to | // We require that active contact numbers be unique, but it's okay to | ||||
// disable a number and then reuse it somewhere else. | // disable a number and then reuse it somewhere else. | ||||
if ($this->isDisabled()) { | if ($this->isDisabled()) { | ||||
$this->uniqueKey = null; | $this->uniqueKey = null; | ||||
} else { | } else { | ||||
$this->uniqueKey = $this->newUniqueKey(); | $this->uniqueKey = $this->newUniqueKey(); | ||||
} | } | ||||
return parent::save(); | parent::save(); | ||||
return $this->updatePrimaryContactNumber(); | |||||
} | |||||
private function updatePrimaryContactNumber() { | |||||
// Update the "isPrimary" column so that at most one number is primary for | |||||
// each user, and no disabled number is primary. | |||||
$conn = $this->establishConnection('w'); | |||||
$this_id = (int)$this->getID(); | |||||
if ($this->getIsPrimary() && !$this->isDisabled()) { | |||||
// If we're trying to make this number primary and it's active, great: | |||||
// make this number the primary number. | |||||
$primary_id = $this_id; | |||||
} else { | |||||
// If we aren't trying to make this number primary or it is disabled, | |||||
// pick another number to make primary if we can. A number must be active | |||||
// to become primary. | |||||
// If there are multiple active numbers, pick the oldest one currently | |||||
// marked primary (usually, this should mean that we just keep the | |||||
// current primary number as primary). | |||||
// If none are marked primary, just pick the oldest one. | |||||
$primary_row = queryfx_one( | |||||
$conn, | |||||
'SELECT id FROM %R | |||||
WHERE objectPHID = %s AND status = %s | |||||
ORDER BY isPrimary DESC, id ASC | |||||
LIMIT 1', | |||||
$this, | |||||
$this->getObjectPHID(), | |||||
self::STATUS_ACTIVE); | |||||
if ($primary_row) { | |||||
$primary_id = (int)$primary_row['id']; | |||||
} else { | |||||
$primary_id = -1; | |||||
} | |||||
} | |||||
// Set the chosen number to primary, and all other numbers to nonprimary. | |||||
queryfx( | |||||
$conn, | |||||
'UPDATE %R SET isPrimary = IF(id = %d, 1, 0) | |||||
WHERE objectPHID = %s', | |||||
$this, | |||||
$primary_id, | |||||
$this->getObjectPHID()); | |||||
$this->setIsPrimary((int)($primary_id === $this_id)); | |||||
return $this; | |||||
} | } | ||||
public static function getStatusNameMap() { | public static function getStatusNameMap() { | ||||
return ipull(self::getStatusPropertyMap(), 'name'); | return ipull(self::getStatusPropertyMap(), 'name'); | ||||
} | } | ||||
private static function getStatusPropertyMap() { | private static function getStatusPropertyMap() { | ||||
return array( | return array( | ||||
self::STATUS_ACTIVE => array( | self::STATUS_ACTIVE => array( | ||||
'name' => pht('Active'), | 'name' => pht('Active'), | ||||
), | ), | ||||
self::STATUS_DISABLED => array( | self::STATUS_DISABLED => array( | ||||
'name' => pht('Disabled'), | 'name' => pht('Disabled'), | ||||
), | ), | ||||
); | ); | ||||
} | } | ||||
public function getSortVector() { | |||||
// Sort the primary number first, then active numbers, then disabled | |||||
// numbers. In each group, sort from oldest to newest. | |||||
return id(new PhutilSortVector()) | |||||
->addInt($this->getIsPrimary() ? 0 : 1) | |||||
->addInt($this->isDisabled() ? 1 : 0) | |||||
->addInt($this->getID()); | |||||
} | |||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */ | /* -( PhabricatorPolicyInterface )----------------------------------------- */ | ||||
public function getCapabilities() { | public function getCapabilities() { | ||||
return array( | return array( | ||||
PhabricatorPolicyCapability::CAN_VIEW, | PhabricatorPolicyCapability::CAN_VIEW, | ||||
PhabricatorPolicyCapability::CAN_EDIT, | PhabricatorPolicyCapability::CAN_EDIT, | ||||
Show All 34 Lines |