@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 363 lines 9.7 kB view raw
1<?php 2 3final class ConpherenceThread extends ConpherenceDAO 4 implements 5 PhabricatorPolicyInterface, 6 PhabricatorApplicationTransactionInterface, 7 PhabricatorMentionableInterface, 8 PhabricatorDestructibleInterface, 9 PhabricatorNgramsInterface { 10 11 protected $title; 12 protected $topic; 13 protected $profileImagePHID; 14 protected $messageCount; 15 protected $mailKey; 16 protected $viewPolicy; 17 protected $editPolicy; 18 protected $joinPolicy; 19 20 private $participants = self::ATTACHABLE; 21 private $transactions = self::ATTACHABLE; 22 private $profileImageFile = self::ATTACHABLE; 23 private $handles = self::ATTACHABLE; 24 25 public static function initializeNewRoom(PhabricatorUser $sender) { 26 $default_policy = id(new ConpherenceThreadMembersPolicyRule()) 27 ->getObjectPolicyFullKey(); 28 return id(new ConpherenceThread()) 29 ->setMessageCount(0) 30 ->setTitle('') 31 ->setTopic('') 32 ->attachParticipants(array()) 33 ->setViewPolicy($default_policy) 34 ->setEditPolicy($default_policy) 35 ->setJoinPolicy(''); 36 } 37 38 protected function getConfiguration() { 39 return array( 40 self::CONFIG_AUX_PHID => true, 41 self::CONFIG_COLUMN_SCHEMA => array( 42 'title' => 'text255?', 43 'topic' => 'text255', 44 'messageCount' => 'uint64', 45 'mailKey' => 'text20', 46 'joinPolicy' => 'policy', 47 'profileImagePHID' => 'phid?', 48 ), 49 self::CONFIG_KEY_SCHEMA => array( 50 'key_phid' => null, 51 'phid' => array( 52 'columns' => array('phid'), 53 'unique' => true, 54 ), 55 ), 56 ) + parent::getConfiguration(); 57 } 58 59 public function generatePHID() { 60 return PhabricatorPHID::generateNewPHID( 61 PhabricatorConpherenceThreadPHIDType::TYPECONST); 62 } 63 64 public function save() { 65 if (!$this->getMailKey()) { 66 $this->setMailKey(Filesystem::readRandomCharacters(20)); 67 } 68 return parent::save(); 69 } 70 71 public function getMonogram() { 72 return 'Z'.$this->getID(); 73 } 74 75 public function getURI() { 76 return '/'.$this->getMonogram(); 77 } 78 79 /** 80 * @param array<ConpherenceParticipant> $participants 81 */ 82 public function attachParticipants(array $participants) { 83 assert_instances_of($participants, ConpherenceParticipant::class); 84 $this->participants = $participants; 85 return $this; 86 } 87 88 public function getParticipants() { 89 return $this->assertAttached($this->participants); 90 } 91 92 public function getParticipant($phid) { 93 $participants = $this->getParticipants(); 94 return $participants[$phid]; 95 } 96 97 public function getParticipantIfExists($phid, $default = null) { 98 $participants = $this->getParticipants(); 99 return idx($participants, $phid, $default); 100 } 101 102 public function getParticipantPHIDs() { 103 $participants = $this->getParticipants(); 104 return array_keys($participants); 105 } 106 107 /** 108 * @param array<PhabricatorObjectHandle> $handles 109 */ 110 public function attachHandles(array $handles) { 111 assert_instances_of($handles, PhabricatorObjectHandle::class); 112 $this->handles = $handles; 113 return $this; 114 } 115 116 public function getHandles() { 117 return $this->assertAttached($this->handles); 118 } 119 120 /** 121 * @param array<ConpherenceTransaction> $transactions 122 */ 123 public function attachTransactions(array $transactions) { 124 assert_instances_of($transactions, ConpherenceTransaction::class); 125 $this->transactions = $transactions; 126 return $this; 127 } 128 129 public function getTransactions($assert_attached = true) { 130 return $this->assertAttached($this->transactions); 131 } 132 133 public function hasAttachedTransactions() { 134 return $this->transactions !== self::ATTACHABLE; 135 } 136 137 public function getTransactionsFrom($begin = 0, $amount = null) { 138 $length = count($this->transactions); 139 140 return array_slice( 141 $this->getTransactions(), 142 $length - $begin - $amount, 143 $amount); 144 } 145 146 public function getProfileImageURI() { 147 return $this->getProfileImageFile()->getBestURI(); 148 } 149 150 public function attachProfileImageFile(PhabricatorFile $file) { 151 $this->profileImageFile = $file; 152 return $this; 153 } 154 155 public function getProfileImageFile() { 156 return $this->assertAttached($this->profileImageFile); 157 } 158 159 /** 160 * Get a thread title which doesn't require handles to be attached. 161 * 162 * This is a less rich title than @{method:getDisplayTitle}, but does not 163 * require handles to be attached. We use it to build thread handles without 164 * risking cycles or recursion while querying. 165 * 166 * @return string Lower quality human-readable title. 167 */ 168 public function getStaticTitle() { 169 $title = $this->getTitle(); 170 if (strlen($title)) { 171 return $title; 172 } 173 174 return pht('Private Room'); 175 } 176 177 public function getDisplayData(PhabricatorUser $viewer) { 178 $handles = $this->getHandles(); 179 180 if ($this->hasAttachedTransactions()) { 181 $transactions = $this->getTransactions(); 182 } else { 183 $transactions = array(); 184 } 185 186 $img_src = $this->getProfileImageURI(); 187 188 $message_transaction = null; 189 foreach ($transactions as $transaction) { 190 if ($message_transaction) { 191 break; 192 } 193 switch ($transaction->getTransactionType()) { 194 case PhabricatorTransactions::TYPE_COMMENT: 195 $message_transaction = $transaction; 196 break; 197 default: 198 break; 199 } 200 } 201 if ($message_transaction) { 202 $message_handle = $handles[$message_transaction->getAuthorPHID()]; 203 $subtitle = sprintf( 204 '%s: %s', 205 $message_handle->getName(), 206 id(new PhutilUTF8StringTruncator()) 207 ->setMaximumGlyphs(60) 208 ->truncateString( 209 $message_transaction->getComment()->getContent())); 210 } else { 211 // Kinda lame, but maybe add last message to cache? 212 $subtitle = pht('No recent messages'); 213 } 214 215 $user_participation = $this->getParticipantIfExists($viewer->getPHID()); 216 $theme = ConpherenceRoomSettings::COLOR_LIGHT; 217 if ($user_participation) { 218 $user_seen_count = $user_participation->getSeenMessageCount(); 219 $participant = $this->getParticipant($viewer->getPHID()); 220 $settings = $participant->getSettings(); 221 $theme = idx($settings, 'theme', $theme); 222 } else { 223 $user_seen_count = 0; 224 } 225 226 $unread_count = $this->getMessageCount() - $user_seen_count; 227 $theme_class = ConpherenceRoomSettings::getThemeClass($theme); 228 229 $title = $this->getTitle(); 230 $topic = $this->getTopic(); 231 232 return array( 233 'title' => $title, 234 'topic' => $topic, 235 'subtitle' => $subtitle, 236 'unread_count' => $unread_count, 237 'epoch' => $this->getDateModified(), 238 'image' => $img_src, 239 'theme' => $theme_class, 240 ); 241 } 242 243 244/* -( PhabricatorPolicyInterface Implementation )-------------------------- */ 245 246 247 public function getCapabilities() { 248 return array( 249 PhabricatorPolicyCapability::CAN_VIEW, 250 PhabricatorPolicyCapability::CAN_EDIT, 251 ); 252 } 253 254 public function getPolicy($capability) { 255 switch ($capability) { 256 case PhabricatorPolicyCapability::CAN_VIEW: 257 return $this->getViewPolicy(); 258 case PhabricatorPolicyCapability::CAN_EDIT: 259 return $this->getEditPolicy(); 260 } 261 return PhabricatorPolicies::POLICY_NOONE; 262 } 263 264 public function hasAutomaticCapability($capability, PhabricatorUser $user) { 265 // this bad boy isn't even created yet so go nuts $user 266 if (!$this->getID()) { 267 return true; 268 } 269 270 switch ($capability) { 271 case PhabricatorPolicyCapability::CAN_EDIT: 272 return false; 273 } 274 275 if (!$user->getPHID()) { 276 return false; 277 } 278 279 $participants = $this->getParticipants(); 280 return isset($participants[$user->getPHID()]); 281 } 282 283 public function describeAutomaticCapability($capability) { 284 switch ($capability) { 285 case PhabricatorPolicyCapability::CAN_VIEW: 286 return pht('Participants in a room can always view it.'); 287 } 288 } 289 290 public static function loadViewPolicyObjects( 291 PhabricatorUser $viewer, 292 array $conpherences) { 293 294 assert_instances_of($conpherences, self::class); 295 296 $policies = array(); 297 foreach ($conpherences as $room) { 298 $policies[$room->getViewPolicy()] = 1; 299 } 300 $policy_objects = array(); 301 if ($policies) { 302 $policy_objects = id(new PhabricatorPolicyQuery()) 303 ->setViewer($viewer) 304 ->withPHIDs(array_keys($policies)) 305 ->execute(); 306 } 307 308 return $policy_objects; 309 } 310 311 /** 312 * @param array<PhabricatorPolicy> $policy_objects 313 */ 314 public function getPolicyIconName(array $policy_objects) { 315 assert_instances_of($policy_objects, PhabricatorPolicy::class); 316 317 $icon = $policy_objects[$this->getViewPolicy()]->getIcon(); 318 return $icon; 319 } 320 321 322/* -( PhabricatorApplicationTransactionInterface )------------------------- */ 323 324 325 public function getApplicationTransactionEditor() { 326 return new ConpherenceEditor(); 327 } 328 329 public function getApplicationTransactionTemplate() { 330 return new ConpherenceTransaction(); 331 } 332 333 334/* -( PhabricatorNgramInterface )------------------------------------------ */ 335 336 337 public function newNgrams() { 338 return array( 339 id(new ConpherenceThreadTitleNgrams()) 340 ->setValue($this->getTitle()), 341 ); 342 } 343 344 345/* -( PhabricatorDestructibleInterface )----------------------------------- */ 346 347 348 public function destroyObjectPermanently( 349 PhabricatorDestructionEngine $engine) { 350 351 $this->openTransaction(); 352 $this->delete(); 353 354 $participants = id(new ConpherenceParticipant()) 355 ->loadAllWhere('conpherencePHID = %s', $this->getPHID()); 356 foreach ($participants as $participant) { 357 $participant->delete(); 358 } 359 360 $this->saveTransaction(); 361 362 } 363}