@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
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}