Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / almanac / storage / AlmanacDevice.php
blob87704d89d8352e281f3bcb7a7a335fdcc7c08d17
1 <?php
3 final class AlmanacDevice
4 extends AlmanacDAO
5 implements
6 PhabricatorPolicyInterface,
7 PhabricatorApplicationTransactionInterface,
8 PhabricatorProjectInterface,
9 PhabricatorSSHPublicKeyInterface,
10 AlmanacPropertyInterface,
11 PhabricatorDestructibleInterface,
12 PhabricatorNgramsInterface,
13 PhabricatorConduitResultInterface,
14 PhabricatorExtendedPolicyInterface {
16 protected $name;
17 protected $nameIndex;
18 protected $viewPolicy;
19 protected $editPolicy;
20 protected $status;
21 protected $isBoundToClusterService;
23 private $almanacProperties = self::ATTACHABLE;
25 public static function initializeNewDevice() {
26 return id(new AlmanacDevice())
27 ->setViewPolicy(PhabricatorPolicies::POLICY_USER)
28 ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
29 ->setStatus(AlmanacDeviceStatus::ACTIVE)
30 ->attachAlmanacProperties(array())
31 ->setIsBoundToClusterService(0);
34 protected function getConfiguration() {
35 return array(
36 self::CONFIG_AUX_PHID => true,
37 self::CONFIG_COLUMN_SCHEMA => array(
38 'name' => 'text128',
39 'nameIndex' => 'bytes12',
40 'status' => 'text32',
41 'isBoundToClusterService' => 'bool',
43 self::CONFIG_KEY_SCHEMA => array(
44 'key_name' => array(
45 'columns' => array('nameIndex'),
46 'unique' => true,
48 'key_nametext' => array(
49 'columns' => array('name'),
52 ) + parent::getConfiguration();
55 public function getPHIDType() {
56 return AlmanacDevicePHIDType::TYPECONST;
59 public function save() {
60 AlmanacNames::validateName($this->getName());
62 $this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
64 return parent::save();
67 public function getURI() {
68 return urisprintf(
69 '/almanac/device/view/%s/',
70 $this->getName());
73 public function rebuildClusterBindingStatus() {
74 $services = id(new AlmanacServiceQuery())
75 ->setViewer(PhabricatorUser::getOmnipotentUser())
76 ->withDevicePHIDs(array($this->getPHID()))
77 ->execute();
79 $is_cluster = false;
80 foreach ($services as $service) {
81 if ($service->isClusterService()) {
82 $is_cluster = true;
83 break;
87 if ($is_cluster != $this->getIsBoundToClusterService()) {
88 $this->setIsBoundToClusterService((int)$is_cluster);
89 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
90 queryfx(
91 $this->establishConnection('w'),
92 'UPDATE %R SET isBoundToClusterService = %d WHERE id = %d',
93 $this,
94 $this->getIsBoundToClusterService(),
95 $this->getID());
96 unset($unguarded);
99 return $this;
102 public function isClusterDevice() {
103 return $this->getIsBoundToClusterService();
106 public function getStatusObject() {
107 return $this->newStatusObject();
110 private function newStatusObject() {
111 return AlmanacDeviceStatus::newStatusFromValue($this->getStatus());
114 public function isDisabled() {
115 return $this->getStatusObject()->isDisabled();
119 /* -( AlmanacPropertyInterface )------------------------------------------- */
122 public function attachAlmanacProperties(array $properties) {
123 assert_instances_of($properties, 'AlmanacProperty');
124 $this->almanacProperties = mpull($properties, null, 'getFieldName');
125 return $this;
128 public function getAlmanacProperties() {
129 return $this->assertAttached($this->almanacProperties);
132 public function hasAlmanacProperty($key) {
133 $this->assertAttached($this->almanacProperties);
134 return isset($this->almanacProperties[$key]);
137 public function getAlmanacProperty($key) {
138 return $this->assertAttachedKey($this->almanacProperties, $key);
141 public function getAlmanacPropertyValue($key, $default = null) {
142 if ($this->hasAlmanacProperty($key)) {
143 return $this->getAlmanacProperty($key)->getFieldValue();
144 } else {
145 return $default;
149 public function getAlmanacPropertyFieldSpecifications() {
150 return array();
153 public function newAlmanacPropertyEditEngine() {
154 return new AlmanacDevicePropertyEditEngine();
157 public function getAlmanacPropertySetTransactionType() {
158 return AlmanacDeviceSetPropertyTransaction::TRANSACTIONTYPE;
161 public function getAlmanacPropertyDeleteTransactionType() {
162 return AlmanacDeviceDeletePropertyTransaction::TRANSACTIONTYPE;
166 /* -( PhabricatorPolicyInterface )----------------------------------------- */
169 public function getCapabilities() {
170 return array(
171 PhabricatorPolicyCapability::CAN_VIEW,
172 PhabricatorPolicyCapability::CAN_EDIT,
176 public function getPolicy($capability) {
177 switch ($capability) {
178 case PhabricatorPolicyCapability::CAN_VIEW:
179 return $this->getViewPolicy();
180 case PhabricatorPolicyCapability::CAN_EDIT:
181 return $this->getEditPolicy();
185 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
186 return false;
190 /* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
193 public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
194 switch ($capability) {
195 case PhabricatorPolicyCapability::CAN_EDIT:
196 if ($this->isClusterDevice()) {
197 return array(
198 array(
199 new PhabricatorAlmanacApplication(),
200 AlmanacManageClusterServicesCapability::CAPABILITY,
204 break;
207 return array();
211 /* -( PhabricatorApplicationTransactionInterface )------------------------- */
214 public function getApplicationTransactionEditor() {
215 return new AlmanacDeviceEditor();
218 public function getApplicationTransactionTemplate() {
219 return new AlmanacDeviceTransaction();
223 /* -( PhabricatorSSHPublicKeyInterface )----------------------------------- */
226 public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) {
227 return $this->getURI();
230 public function getSSHKeyDefaultName() {
231 return $this->getName();
234 public function getSSHKeyNotifyPHIDs() {
235 // Devices don't currently have anyone useful to notify about SSH key
236 // edits, and they're usually a difficult vector to attack since you need
237 // access to a cluster host. However, it would be nice to make them
238 // subscribable at some point.
239 return array();
243 /* -( PhabricatorDestructibleInterface )----------------------------------- */
246 public function destroyObjectPermanently(
247 PhabricatorDestructionEngine $engine) {
249 $interfaces = id(new AlmanacInterfaceQuery())
250 ->setViewer($engine->getViewer())
251 ->withDevicePHIDs(array($this->getPHID()))
252 ->execute();
253 foreach ($interfaces as $interface) {
254 $engine->destroyObject($interface);
257 $this->delete();
261 /* -( PhabricatorNgramsInterface )----------------------------------------- */
264 public function newNgrams() {
265 return array(
266 id(new AlmanacDeviceNameNgrams())
267 ->setValue($this->getName()),
272 /* -( PhabricatorConduitResultInterface )---------------------------------- */
275 public function getFieldSpecificationsForConduit() {
276 return array(
277 id(new PhabricatorConduitSearchFieldSpecification())
278 ->setKey('name')
279 ->setType('string')
280 ->setDescription(pht('The name of the device.')),
281 id(new PhabricatorConduitSearchFieldSpecification())
282 ->setKey('status')
283 ->setType('map<string, wild>')
284 ->setDescription(pht('Device status information.')),
285 id(new PhabricatorConduitSearchFieldSpecification())
286 ->setKey('disabled')
287 ->setType('bool')
288 ->setDescription(pht('True if device is disabled.')),
292 public function getFieldValuesForConduit() {
293 $status = $this->getStatusObject();
295 return array(
296 'name' => $this->getName(),
297 'status' => array(
298 'value' => $status->getValue(),
299 'name' => $status->getName(),
301 'disabled' => $this->isDisabled(),
305 public function getConduitSearchAttachments() {
306 return array(
307 id(new AlmanacPropertiesSearchEngineAttachment())
308 ->setAttachmentKey('properties'),