@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 PhabricatorSubscriptionsCurtainExtension
4 extends PHUICurtainExtension {
5
6 const EXTENSIONKEY = 'subscriptions.subscribers';
7
8 public function shouldEnableForObject($object) {
9 return ($object instanceof PhabricatorSubscribableInterface);
10 }
11
12 public function getExtensionApplication() {
13 return new PhabricatorSubscriptionsApplication();
14 }
15
16 public function buildCurtainPanel($object) {
17 $viewer = $this->getViewer();
18 $viewer_phid = $viewer->getPHID();
19 $object_phid = $object->getPHID();
20
21 $max_handles = 100;
22 $max_visible = 8;
23
24 // TODO: We should limit the number of subscriber PHIDs we'll load, so
25 // we degrade gracefully when objects have thousands of subscribers.
26
27 $subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
28 $object_phid);
29 $subscriber_count = count($subscriber_phids);
30
31 $subscriber_phids = $this->sortSubscriberPHIDs(
32 $subscriber_phids,
33 null);
34
35 // If we have fewer subscribers than the maximum number of handles we're
36 // willing to load, load all the handles and then sort the list based on
37 // complete handle data.
38
39 // If we have too many PHIDs, we'll skip this step and accept a less
40 // useful ordering.
41 $handles = null;
42 if ($subscriber_count <= $max_handles) {
43 $handles = $viewer->loadHandles($subscriber_phids);
44
45 $subscriber_phids = $this->sortSubscriberPHIDs(
46 $subscriber_phids,
47 $handles);
48 }
49
50 // If we have more PHIDs to show than visible slots, slice the list.
51 if ($subscriber_count > $max_visible) {
52 $visible_phids = array_slice($subscriber_phids, 0, $max_visible - 1);
53 $show_all = true;
54 } else {
55 $visible_phids = $subscriber_phids;
56 $show_all = false;
57 }
58
59 // If we didn't load handles earlier because we had too many PHIDs,
60 // load them now.
61 if ($handles === null) {
62 $handles = $viewer->loadHandles($visible_phids);
63 }
64
65 PhabricatorPolicyFilterSet::loadHandleViewCapabilities(
66 $viewer,
67 $handles,
68 array($object));
69
70 $ref_list = id(new PHUICurtainObjectRefListView())
71 ->setViewer($viewer)
72 ->setEmptyMessage(pht('None'));
73
74 foreach ($visible_phids as $phid) {
75 $handle = $handles[$phid];
76
77 $ref = $ref_list->newObjectRefView()
78 ->setHandle($handle);
79
80 if ($phid === $viewer_phid) {
81 $ref->setHighlighted(true);
82 }
83
84 if ($handle->hasCapabilities()) {
85 if (!$handle->hasViewCapability($object)) {
86 $ref->setExiled(true);
87 }
88 }
89 }
90
91 if ($show_all) {
92 $view_all_uri = urisprintf(
93 '/subscriptions/list/%s/',
94 $object_phid);
95
96 $ref_list->newTailLink()
97 ->setURI($view_all_uri)
98 ->setText(pht('View All %d Subscriber(s)', $subscriber_count))
99 ->setWorkflow(true);
100 }
101
102 return $this->newPanel()
103 ->setHeaderText(pht('Subscribers'))
104 ->setOrder(20000)
105 ->appendChild($ref_list);
106 }
107
108 private function sortSubscriberPHIDs(array $subscriber_phids, $handles) {
109
110 // Sort subscriber PHIDs with or without handle data. If we have handles,
111 // we can sort results more comprehensively.
112
113 $viewer = $this->getViewer();
114
115 $user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
116 $viewer_phid = $viewer->getPHID();
117
118 $type_order_map = array(
119 PhabricatorPeopleUserPHIDType::TYPECONST => 0,
120 PhabricatorProjectProjectPHIDType::TYPECONST => 1,
121 PhabricatorOwnersPackagePHIDType::TYPECONST => 2,
122 );
123 $default_type_order = count($type_order_map);
124
125 $subscriber_map = array();
126 foreach ($subscriber_phids as $subscriber_phid) {
127 $is_viewer = ($viewer_phid === $subscriber_phid);
128
129 $subscriber_type = phid_get_type($subscriber_phid);
130 $type_order = idx($type_order_map, $subscriber_type, $default_type_order);
131
132 $sort_name = '';
133 $is_complete = false;
134 if ($handles) {
135 if (isset($handles[$subscriber_phid])) {
136 $handle = $handles[$subscriber_phid];
137 if ($handle->isComplete()) {
138 $is_complete = true;
139
140 $sort_name = $handle->getLinkName();
141 $sort_name = phutil_utf8_strtolower($sort_name);
142 }
143 }
144 }
145
146 $subscriber_map[$subscriber_phid] = id(new PhutilSortVector())
147 ->addInt($is_viewer ? 0 : 1)
148 ->addInt($is_complete ? 0 : 1)
149 ->addInt($type_order)
150 ->addString($sort_name);
151 }
152
153 $subscriber_map = msortv($subscriber_map, 'getSelf');
154
155 return array_keys($subscriber_map);
156 }
157
158}