@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 374 lines 9.8 kB view raw
1<?php 2 3final class DiffusionRepositoryStorageManagementPanel 4 extends DiffusionRepositoryManagementPanel { 5 6 const PANELKEY = 'storage'; 7 8 public function getManagementPanelLabel() { 9 return pht('Storage'); 10 } 11 12 public function getManagementPanelOrder() { 13 return 600; 14 } 15 16 public function getManagementPanelIcon() { 17 $repository = $this->getRepository(); 18 19 if ($repository->getAlmanacServicePHID()) { 20 return 'fa-sitemap'; 21 } else if ($repository->isHosted()) { 22 return 'fa-database'; 23 } else { 24 return 'fa-download'; 25 } 26 } 27 28 public function buildManagementPanelCurtain() { 29 $repository = $this->getRepository(); 30 $viewer = $this->getViewer(); 31 $action_list = $this->newActionList(); 32 33 $doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories'); 34 35 $action_list->addAction( 36 id(new PhabricatorActionView()) 37 ->setIcon('fa-book') 38 ->setHref($doc_href) 39 ->setName(pht('Cluster Documentation'))); 40 41 return $this->newCurtainView() 42 ->setActionList($action_list); 43 } 44 45 public function buildManagementPanelContent() { 46 return array( 47 $this->buildStorageStatusPanel(), 48 $this->buildClusterStatusPanel(), 49 $this->buildRefsStatusPanels(), 50 ); 51 } 52 53 private function buildStorageStatusPanel() { 54 $repository = $this->getRepository(); 55 $viewer = $this->getViewer(); 56 57 $view = id(new PHUIPropertyListView()) 58 ->setViewer($viewer); 59 60 if ($repository->usesLocalWorkingCopy()) { 61 $storage_path = $repository->getLocalPath(); 62 } else { 63 $storage_path = phutil_tag('em', array(), pht('No Local Working Copy')); 64 } 65 66 $service_phid = $repository->getAlmanacServicePHID(); 67 if ($service_phid) { 68 $storage_service = $viewer->renderHandle($service_phid); 69 } else { 70 $storage_service = phutil_tag('em', array(), pht('Local')); 71 } 72 73 $view->addProperty(pht('Storage Path'), $storage_path); 74 $view->addProperty(pht('Storage Cluster'), $storage_service); 75 76 return $this->newBox(pht('Storage'), $view); 77 } 78 79 private function buildClusterStatusPanel() { 80 $repository = $this->getRepository(); 81 $viewer = $this->getViewer(); 82 83 $service_phid = $repository->getAlmanacServicePHID(); 84 if ($service_phid) { 85 $service = id(new AlmanacServiceQuery()) 86 ->setViewer($viewer) 87 ->withServiceTypes( 88 array( 89 AlmanacClusterRepositoryServiceType::SERVICETYPE, 90 )) 91 ->withPHIDs(array($service_phid)) 92 ->needActiveBindings(true) 93 ->executeOne(); 94 if (!$service) { 95 // TODO: Viewer may not have permission to see the service, or it may 96 // be invalid? Raise some more useful error here? 97 throw new Exception(pht('Unable to load cluster service.')); 98 } 99 } else { 100 $service = null; 101 } 102 103 Javelin::initBehavior('phabricator-tooltips'); 104 105 $rows = array(); 106 if ($service) { 107 $bindings = $service->getActiveBindings(); 108 $bindings = mgroup($bindings, 'getDevicePHID'); 109 110 // This is an unusual read which always comes from the master. 111 if (PhabricatorEnv::isReadOnly()) { 112 $versions = array(); 113 } else { 114 $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions( 115 $repository->getPHID()); 116 } 117 118 $versions = mpull($versions, null, 'getDevicePHID'); 119 120 $sort = array(); 121 foreach ($bindings as $key => $binding_group) { 122 $sort[$key] = id(new PhutilSortVector()) 123 ->addString(head($binding_group)->getDevice()->getName()); 124 } 125 $sort = msortv($sort, 'getSelf'); 126 $bindings = array_select_keys($bindings, array_keys($sort)) + $bindings; 127 128 foreach ($bindings as $binding_group) { 129 $any_binding = head($binding_group); 130 131 $binding_icon = 'fa-folder-open green'; 132 $binding_tip = pht('Active'); 133 134 $binding_icon = id(new PHUIIconView()) 135 ->setIcon($binding_icon) 136 ->addSigil('has-tooltip') 137 ->setMetadata( 138 array( 139 'tip' => $binding_tip, 140 )); 141 142 $device = $any_binding->getDevice(); 143 144 $version = idx($versions, $device->getPHID()); 145 if ($version) { 146 $version_number = $version->getRepositoryVersion(); 147 148 $href = null; 149 if ($repository->isHosted()) { 150 $href = "/diffusion/pushlog/view/{$version_number}/"; 151 } else { 152 $commit = id(new DiffusionCommitQuery()) 153 ->setViewer($viewer) 154 ->withIDs(array($version_number)) 155 ->executeOne(); 156 if ($commit) { 157 $href = $commit->getURI(); 158 } 159 } 160 161 if ($href) { 162 $version_number = phutil_tag( 163 'a', 164 array( 165 'href' => $href, 166 ), 167 $version_number); 168 } 169 } else { 170 $version_number = '-'; 171 } 172 173 if ($version && $version->getIsWriting()) { 174 $is_writing = id(new PHUIIconView()) 175 ->setIcon('fa-pencil green'); 176 } else { 177 $is_writing = id(new PHUIIconView()) 178 ->setIcon('fa-pencil grey'); 179 } 180 181 $write_properties = null; 182 if ($version) { 183 $write_properties = $version->getWriteProperties(); 184 if ($write_properties) { 185 try { 186 $write_properties = phutil_json_decode($write_properties); 187 } catch (Exception $ex) { 188 $write_properties = null; 189 } 190 } 191 } 192 193 $last_writer = null; 194 $writer_epoch = null; 195 if ($write_properties) { 196 $writer_phid = idx($write_properties, 'userPHID'); 197 198 if ($writer_phid) { 199 $last_writer = $viewer->renderHandle($writer_phid); 200 } 201 202 $writer_epoch = idx($write_properties, 'epoch'); 203 if ($writer_epoch) { 204 $writer_epoch = phabricator_datetime($writer_epoch, $viewer); 205 } 206 } 207 208 $rows[] = array( 209 $binding_icon, 210 phutil_tag( 211 'a', 212 array( 213 'href' => $device->getURI(), 214 ), 215 $device->getName()), 216 $version_number, 217 $is_writing, 218 $last_writer, 219 $writer_epoch, 220 ); 221 } 222 } 223 224 $table = id(new AphrontTableView($rows)) 225 ->setNoDataString(pht('This is not a cluster repository.')) 226 ->setHeaders( 227 array( 228 null, 229 pht('Device'), 230 pht('Version'), 231 pht('Writing'), 232 pht('Last Writer'), 233 pht('Last Write At'), 234 )) 235 ->setColumnClasses( 236 array( 237 null, 238 null, 239 null, 240 'right wide', 241 null, 242 'date', 243 )); 244 245 return $this->newBox(pht('Cluster Status'), $table); 246 } 247 248 private function buildRefsStatusPanels() { 249 $repository = $this->getRepository(); 250 251 $service_phid = $repository->getAlmanacServicePHID(); 252 if (!$service_phid) { 253 // If this repository isn't clustered, don't bother rendering anything. 254 // There are enough other context clues that another empty panel isn't 255 // useful. 256 return; 257 } 258 259 $all_protocols = array( 260 'http', 261 'https', 262 'ssh', 263 ); 264 265 $readable_panel = $this->buildRefsStatusPanel( 266 pht('Readable Service Refs'), 267 array( 268 'neverProxy' => false, 269 'protocols' => $all_protocols, 270 'writable' => false, 271 )); 272 273 $writable_panel = $this->buildRefsStatusPanel( 274 pht('Writable Service Refs'), 275 array( 276 'neverProxy' => false, 277 'protocols' => $all_protocols, 278 'writable' => true, 279 )); 280 281 return array( 282 $readable_panel, 283 $writable_panel, 284 ); 285 } 286 287 private function buildRefsStatusPanel( 288 $title, 289 $options) { 290 291 $repository = $this->getRepository(); 292 $viewer = $this->getViewer(); 293 294 $caught = null; 295 $refs = array(); 296 297 try { 298 $refs = $repository->getAlmanacServiceRefs($viewer, $options); 299 } catch (Exception $ex) { 300 $caught = $ex; 301 } catch (Throwable $ex) { 302 $caught = $ex; 303 } 304 305 $info_view = null; 306 if ($caught) { 307 $info_view = id(new PHUIInfoView()) 308 ->setErrors( 309 array( 310 phutil_escape_html_newlines($caught->getMessage()), 311 )); 312 } 313 314 $phids = array(); 315 foreach ($refs as $ref) { 316 $phids[] = $ref->getDevicePHID(); 317 } 318 319 $handles = $viewer->loadHandles($phids); 320 321 $icon_writable = id(new PHUIIconView()) 322 ->setIcon('fa-pencil', 'green'); 323 324 $icon_unwritable = id(new PHUIIconView()) 325 ->setIcon('fa-times', 'grey'); 326 327 $rows = array(); 328 foreach ($refs as $ref) { 329 $device_phid = $ref->getDevicePHID(); 330 $device_handle = $handles[$device_phid]; 331 332 if ($ref->isWritable()) { 333 $writable_icon = $icon_writable; 334 $writable_text = pht('Read/Write'); 335 } else { 336 $writable_icon = $icon_unwritable; 337 $writable_text = pht('Read Only'); 338 } 339 340 $rows[] = array( 341 $device_handle->renderLink(), 342 $ref->getURI(), 343 $writable_icon, 344 $writable_text, 345 ); 346 } 347 348 $table = id(new AphrontTableView($rows)) 349 ->setNoDataString(pht('No repository service refs available.')) 350 ->setHeaders( 351 array( 352 pht('Device'), 353 pht('Internal Service URI'), 354 null, 355 pht('I/O'), 356 )) 357 ->setColumnClasses( 358 array( 359 null, 360 'wide', 361 'icon', 362 null, 363 )); 364 365 $box_view = $this->newBox($title, $table); 366 367 if ($info_view) { 368 $box_view->setInfoView($info_view); 369 } 370 371 return $box_view; 372 } 373 374}