@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 PhrictionDiffController extends PhrictionController {
4
5 public function shouldAllowPublic() {
6 return true;
7 }
8
9 public function handleRequest(AphrontRequest $request) {
10 $viewer = $request->getViewer();
11 $id = $request->getURIData('id');
12
13 $document = id(new PhrictionDocumentQuery())
14 ->setViewer($viewer)
15 ->withIDs(array($id))
16 ->needContent(true)
17 ->executeOne();
18 if (!$document) {
19 return new Aphront404Response();
20 }
21
22 $current = $document->getContent();
23
24 $l = $request->getInt('l');
25 $r = $request->getInt('r');
26
27 $ref = $request->getStr('ref');
28 if ($ref) {
29 list($l, $r) = explode(',', $ref);
30 }
31
32 $content = id(new PhrictionContentQuery())
33 ->setViewer($viewer)
34 ->withDocumentPHIDs(array($document->getPHID()))
35 ->withVersions(array($l, $r))
36 ->execute();
37 $content = mpull($content, null, 'getVersion');
38
39 $content_l = idx($content, $l, null);
40 $content_r = idx($content, $r, null);
41
42 if (!$content_l || !$content_r) {
43 return new Aphront404Response();
44 }
45
46 $text_l = $content_l->getContent();
47 $text_r = $content_r->getContent();
48
49 $diff_view = id(new PhabricatorApplicationTransactionTextDiffDetailView())
50 ->setOldText($text_l)
51 ->setNewText($text_r);
52
53 $changes = id(new PHUIObjectBoxView())
54 ->setHeaderText(pht('Content Changes'))
55 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
56 ->appendChild(
57 phutil_tag(
58 'div',
59 array(
60 'class' => 'prose-diff-frame',
61 ),
62 $diff_view));
63
64 require_celerity_resource('phriction-document-css');
65
66 $slug = $document->getSlug();
67
68 $revert_l = $this->renderRevertButton($document, $content_l, $current);
69 $revert_r = $this->renderRevertButton($document, $content_r, $current);
70
71 $crumbs = $this->buildApplicationCrumbs();
72 $crumb_views = $this->renderBreadcrumbs($slug);
73 foreach ($crumb_views as $view) {
74 $crumbs->addCrumb($view);
75 }
76
77 $crumbs->addTextCrumb(
78 pht('History'),
79 PhrictionDocument::getSlugURI($slug, 'history'));
80
81 $title = pht('Version %s vs %s', $l, $r);
82
83 $header = id(new PHUIHeaderView())
84 ->setHeader($title)
85 ->setHeaderIcon('fa-history');
86
87 $crumbs->addTextCrumb($title, $request->getRequestURI());
88
89 $comparison_table = $this->renderComparisonTable(
90 array(
91 $content_r,
92 $content_l,
93 ));
94
95 $navigation_table = null;
96 if ($l + 1 == $r) {
97 $nav_l = ($l > 1);
98 $nav_r = ($r != $document->getMaxVersion());
99
100 $uri = $request->getRequestURI();
101
102 if ($nav_l) {
103 $link_l = phutil_tag(
104 'a',
105 array(
106 'href' => $uri->alter('l', $l - 1)->alter('r', $r - 1),
107 'class' => 'button button-grey',
108 ),
109 pht("\xC2\xAB Previous Change"));
110 } else {
111 $link_l = phutil_tag(
112 'a',
113 array(
114 'href' => '#',
115 'class' => 'button button-grey disabled',
116 ),
117 pht('Original Change'));
118 }
119
120 $link_r = null;
121 if ($nav_r) {
122 $link_r = phutil_tag(
123 'a',
124 array(
125 'href' => $uri->alter('l', $l + 1)->alter('r', $r + 1),
126 'class' => 'button button-grey',
127 ),
128 pht("Next Change \xC2\xBB"));
129 } else {
130 $link_r = phutil_tag(
131 'a',
132 array(
133 'href' => '#',
134 'class' => 'button button-grey disabled',
135 ),
136 pht('Most Recent Change'));
137 }
138
139 $navigation_table = phutil_tag(
140 'table',
141 array('class' => 'phriction-history-nav-table'),
142 phutil_tag('tr', array(), array(
143 phutil_tag('td', array('class' => 'nav-prev'), $link_l),
144 phutil_tag('td', array('class' => 'nav-next'), $link_r),
145 )));
146 }
147
148 $output = hsprintf(
149 '<div class="phriction-document-history-diff">'.
150 '%s%s'.
151 '<table class="phriction-revert-table">'.
152 '<tr><td>%s</td><td>%s</td>'.
153 '</table>'.
154 '</div>',
155 $comparison_table->render(),
156 $navigation_table,
157 $revert_l,
158 $revert_r);
159
160 $object_box = id(new PHUIObjectBoxView())
161 ->setHeaderText(pht('Edits'))
162 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
163 ->appendChild($output);
164
165 $crumbs->setBorder(true);
166
167 $view = id(new PHUITwoColumnView())
168 ->setHeader($header)
169 ->setFooter(array(
170 $object_box,
171 $changes,
172 ));
173
174 return $this->newPage()
175 ->setTitle(pht('Document History'))
176 ->setCrumbs($crumbs)
177 ->appendChild($view);
178
179 }
180
181 private function renderRevertButton(
182 PhrictionDocument $document,
183 PhrictionContent $content,
184 PhrictionContent $current) {
185
186 $document_id = $document->getID();
187 $version = $content->getVersion();
188
189 $hidden_statuses = array(
190 PhrictionChangeType::CHANGE_DELETE => true, // Silly
191 PhrictionChangeType::CHANGE_MOVE_AWAY => true, // Plain silly
192 PhrictionChangeType::CHANGE_STUB => true, // Utterly silly
193 );
194
195 if (isset($hidden_statuses[$content->getChangeType()])) {
196 // Don't show an edit/revert button for changes which deleted, moved or
197 // stubbed the content since it's silly.
198 return null;
199 }
200
201 if ($version == $current->getVersion()) {
202 $label = pht('Edit Current Version %s...', new PhutilNumber($version));
203 } else if ($version < $current->getVersion()) {
204 $label = pht('Edit Older Version %s...', new PhutilNumber($version));
205 } else {
206 $label = pht('Edit Draft Version %s...', new PhutilNumber($version));
207 }
208
209 return phutil_tag(
210 'a',
211 array(
212 'href' => '/phriction/edit/'.$document_id.'/?revert='.$version,
213 'class' => 'button button-grey',
214 ),
215 $label);
216 }
217
218 /**
219 * @param array<PhrictionContent> $content
220 */
221 private function renderComparisonTable(array $content) {
222 assert_instances_of($content, PhrictionContent::class);
223
224 $viewer = $this->getViewer();
225
226 $phids = mpull($content, 'getAuthorPHID');
227 $handles = $this->loadViewerHandles($phids);
228
229 $list = new PHUIObjectItemListView();
230
231 $first = true;
232 foreach ($content as $c) {
233 $author = $handles[$c->getAuthorPHID()]->renderLink();
234 $item = id(new PHUIObjectItemView())
235 ->setHeader(pht('%s by %s, %s',
236 PhrictionChangeType::getChangeTypeLabel($c->getChangeType()),
237 $author,
238 pht('Version %s', $c->getVersion())))
239 ->addAttribute(pht('%s %s',
240 phabricator_date($c->getDateCreated(), $viewer),
241 phabricator_time($c->getDateCreated(), $viewer)));
242
243 if ($c->getDescription()) {
244 $item->addAttribute($c->getDescription());
245 }
246
247 if ($first == true) {
248 $item->setStatusIcon('fa-file green');
249 $first = false;
250 } else {
251 $item->setStatusIcon('fa-file red');
252 }
253
254 $list->addItem($item);
255 }
256
257 return $list;
258 }
259
260}