@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 PhabricatorPeopleProfileViewController
4 extends PhabricatorPeopleProfileController {
5
6 public function shouldAllowPublic() {
7 return true;
8 }
9
10 public function handleRequest(AphrontRequest $request) {
11 $viewer = $this->getViewer();
12 $username = $request->getURIData('username');
13
14 $user = id(new PhabricatorPeopleQuery())
15 ->setViewer($viewer)
16 ->withUsernames(array($username))
17 ->needProfileImage(true)
18 ->needAvailability(true)
19 ->executeOne();
20 if (!$user) {
21 return new Aphront404Response();
22 }
23
24 $this->setUser($user);
25 $header = $this->buildProfileHeader();
26
27 $properties = $this->buildPropertyView($user);
28 $name = $user->getUsername();
29
30 $feed = $this->buildPeopleFeed($user, $viewer);
31
32 $view_all = id(new PHUIButtonView())
33 ->setTag('a')
34 ->setIcon('fa-list-ul')
35 ->setText(pht('View All'))
36 ->setHref('/feed/?userPHIDs='.$user->getPHID());
37
38 $feed_header = id(new PHUIHeaderView())
39 ->setHeader(pht('Recent Activity'))
40 ->addActionLink($view_all);
41
42 $feed = id(new PHUIObjectBoxView())
43 ->setHeader($feed_header)
44 ->addClass('project-view-feed')
45 ->appendChild($feed);
46
47 $projects = $this->buildProjectsView($user);
48 $calendar = $this->buildCalendarDayView($user);
49
50 $home = id(new PHUITwoColumnView())
51 ->setHeader($header)
52 ->addClass('project-view-home')
53 ->addClass('project-view-people-home')
54 ->setMainColumn(
55 array(
56 $properties,
57 $feed,
58 ))
59 ->setSideColumn(
60 array(
61 $projects,
62 $calendar,
63 ));
64
65 $navigation = $this->newNavigation(
66 $user,
67 PhabricatorPeopleProfileMenuEngine::ITEM_PROFILE);
68
69 $crumbs = $this->buildApplicationCrumbs();
70 $crumbs->setBorder(true);
71
72 return $this->newPage()
73 ->setTitle($user->getUsername())
74 ->setNavigation($navigation)
75 ->setCrumbs($crumbs)
76 ->setPageObjectPHIDs(
77 array(
78 $user->getPHID(),
79 ))
80 ->appendChild(
81 array(
82 $home,
83 ));
84 }
85
86 /**
87 * @return PHUIObjectBoxView|null
88 */
89 private function buildPropertyView(
90 PhabricatorUser $user) {
91
92 $viewer = $this->getRequest()->getUser();
93 $view = id(new PHUIPropertyListView())
94 ->setViewer($viewer)
95 ->setObject($user);
96
97 $field_list = PhabricatorCustomField::getObjectFields(
98 $user,
99 PhabricatorCustomField::ROLE_VIEW);
100 $field_list->appendFieldsToPropertyList($user, $viewer, $view);
101
102 if (!$view->hasAnyProperties()) {
103 return null;
104 }
105
106 $header = id(new PHUIHeaderView())
107 ->setHeader(pht('User Details'));
108
109 $view = id(new PHUIObjectBoxView())
110 ->appendChild($view)
111 ->setHeader($header)
112 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
113 ->addClass('project-view-properties');
114
115 return $view;
116 }
117
118 /**
119 * @return PHUIObjectBoxView
120 */
121 private function buildProjectsView(
122 PhabricatorUser $user) {
123
124 $viewer = $this->getViewer();
125 $projects = id(new PhabricatorProjectQuery())
126 ->setViewer($viewer)
127 ->withMemberPHIDs(array($user->getPHID()))
128 ->needImages(true)
129 ->withStatuses(
130 array(
131 PhabricatorProjectStatus::STATUS_ACTIVE,
132 ))
133 ->execute();
134
135 $header = id(new PHUIHeaderView())
136 ->setHeader(pht('Projects'));
137
138 if (!empty($projects)) {
139 $limit = 5;
140 $render_phids = array_slice($projects, 0, $limit);
141 $list = id(new PhabricatorProjectListView())
142 ->setViewer($viewer)
143 ->setProjects($render_phids);
144
145 $header_text = pht(
146 'Projects (%s)',
147 phutil_count($projects));
148
149 $header = id(new PHUIHeaderView())
150 ->setHeader($header_text)
151 ->addActionLink(
152 id(new PHUIButtonView())
153 ->setTag('a')
154 ->setIcon('fa-list-ul')
155 ->setText(pht('View All'))
156 ->setHref('/project/?member='.$user->getPHID().'#R'));
157
158 } else {
159 $list = id(new PHUIInfoView())
160 ->setSeverity(PHUIInfoView::SEVERITY_NODATA)
161 ->appendChild(pht('User is not a member of any projects.'));
162 }
163
164 $box = id(new PHUIObjectBoxView())
165 ->setHeader($header)
166 ->appendChild($list)
167 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
168
169 return $box;
170 }
171
172 /**
173 * @return PHUIObjectBoxView|null
174 */
175 private function buildCalendarDayView(PhabricatorUser $user) {
176 $viewer = $this->getViewer();
177 $class = PhabricatorCalendarApplication::class;
178
179 if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
180 return null;
181 }
182
183 // Don't show calendar information for disabled users, since it's probably
184 // not useful or accurate and may be misleading.
185 if ($user->getIsDisabled()) {
186 return null;
187 }
188
189 $midnight = PhabricatorTime::getTodayMidnightDateTime($viewer);
190 $week_end = clone $midnight;
191 $week_end = $week_end->modify('+3 days');
192
193 $range_start = $midnight->format('U');
194 $range_end = $week_end->format('U');
195
196 $events = id(new PhabricatorCalendarEventQuery())
197 ->setViewer($viewer)
198 ->withDateRange($range_start, $range_end)
199 ->withInvitedPHIDs(array($user->getPHID()))
200 ->withIsCancelled(false)
201 ->needRSVPs(array($viewer->getPHID()))
202 ->execute();
203
204 $event_views = array();
205 foreach ($events as $event) {
206 $viewer_is_invited = $event->isRSVPInvited($viewer->getPHID());
207
208 $can_edit = PhabricatorPolicyFilter::hasCapability(
209 $viewer,
210 $event,
211 PhabricatorPolicyCapability::CAN_EDIT);
212
213 $epoch_min = $event->getStartDateTimeEpoch();
214 $epoch_max = $event->getEndDateTimeEpoch();
215
216 $event_view = id(new AphrontCalendarEventView())
217 ->setCanEdit($can_edit)
218 ->setEventID($event->getID())
219 ->setEpochRange($epoch_min, $epoch_max)
220 ->setIsAllDay($event->getIsAllDay())
221 ->setIcon($event->getIcon())
222 ->setViewerIsInvited($viewer_is_invited)
223 ->setName($event->getName())
224 ->setDatetimeSummary($event->renderEventDate($viewer, true))
225 ->setURI($event->getURI());
226
227 $event_views[] = $event_view;
228 }
229
230 $event_views = msort($event_views, 'getEpochStart');
231
232 $day_view = id(new PHUICalendarWeekView())
233 ->setViewer($viewer)
234 ->setView('week')
235 ->setEvents($event_views)
236 ->setWeekLength(3)
237 ->render();
238
239 $header = id(new PHUIHeaderView())
240 ->setHeader(pht('Calendar'))
241 ->setHref(
242 urisprintf(
243 '/calendar/?invited=%s#R',
244 $user->getUsername()));
245
246 $box = id(new PHUIObjectBoxView())
247 ->setHeader($header)
248 ->appendChild($day_view)
249 ->addClass('calendar-profile-box')
250 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
251
252 return $box;
253 }
254
255 private function buildPeopleFeed(
256 PhabricatorUser $user,
257 $viewer) {
258
259 $query = id(new PhabricatorFeedQuery())
260 ->setViewer($viewer)
261 ->withFilterPHIDs(array($user->getPHID()))
262 ->setLimit(100)
263 ->setReturnPartialResultsOnOverheat(true);
264
265 $stories = $query->execute();
266
267 $overheated_view = null;
268 $is_overheated = $query->getIsOverheated();
269 if ($is_overheated) {
270 $overheated_message =
271 PhabricatorApplicationSearchController::newOverheatedError(
272 (bool)$stories);
273
274 $overheated_view = id(new PHUIInfoView())
275 ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
276 ->setTitle(pht('Query Overheated'))
277 ->setErrors(
278 array(
279 $overheated_message,
280 ));
281 }
282
283 $builder = new PhabricatorFeedBuilder($stories);
284 $builder->setUser($viewer);
285 $builder->setShowHovercards(true);
286 $builder->setNoDataString(pht('To begin on such a grand journey, '.
287 'requires but just a single step.'));
288 $view = $builder->buildView();
289
290 return array(
291 $overheated_view,
292 $view->render(),
293 );
294 }
295
296}