3 final class PhabricatorFileViewController
extends PhabricatorFileController
{
5 public function shouldAllowPublic() {
9 public function handleRequest(AphrontRequest
$request) {
10 $viewer = $request->getViewer();
11 $id = $request->getURIData('id');
12 $phid = $request->getURIData('phid');
15 $file = id(new PhabricatorFileQuery())
17 ->withPHIDs(array($phid))
18 ->withIsDeleted(false)
22 return new Aphront404Response();
24 return id(new AphrontRedirectResponse())->setURI($file->getInfoURI());
27 $file = id(new PhabricatorFileQuery())
30 ->withIsDeleted(false)
33 return new Aphront404Response();
36 $phid = $file->getPHID();
38 $header = id(new PHUIHeaderView())
40 ->setPolicyObject($file)
41 ->setHeader($file->getName())
42 ->setHeaderIcon('fa-file-o');
44 $ttl = $file->getTTL();
46 $ttl_tag = id(new PHUITagView())
47 ->setType(PHUITagView
::TYPE_SHADE
)
48 ->setColor(PHUITagView
::COLOR_YELLOW
)
49 ->setName(pht('Temporary'));
50 $header->addTag($ttl_tag);
53 $partial = $file->getIsPartial();
55 $partial_tag = id(new PHUITagView())
56 ->setType(PHUITagView
::TYPE_SHADE
)
57 ->setColor(PHUITagView
::COLOR_ORANGE
)
58 ->setName(pht('Partial Upload'));
59 $header->addTag($partial_tag);
62 $curtain = $this->buildCurtainView($file);
63 $timeline = $this->buildTransactionView($file);
64 $crumbs = $this->buildApplicationCrumbs();
65 $crumbs->addTextCrumb(
68 $crumbs->setBorder(true);
70 $object_box = id(new PHUIObjectBoxView())
71 ->setHeaderText(pht('File Metadata'))
72 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
);
74 $this->buildPropertyViews($object_box, $file);
75 $title = $file->getName();
77 $file_content = $this->newFileContent($file);
79 $view = id(new PHUITwoColumnView())
81 ->setCurtain($curtain)
89 return $this->newPage()
92 ->setPageObjectPHIDs(array($file->getPHID()))
96 private function buildTransactionView(PhabricatorFile
$file) {
97 $viewer = $this->getViewer();
99 $timeline = $this->buildTransactionTimeline(
101 new PhabricatorFileTransactionQuery());
103 $comment_view = id(new PhabricatorFileEditEngine())
105 ->buildEditEngineCommentView($file);
107 $monogram = $file->getMonogram();
109 $timeline->setQuoteRef($monogram);
110 $comment_view->setTransactionTimeline($timeline);
118 private function buildCurtainView(PhabricatorFile
$file) {
119 $viewer = $this->getViewer();
121 $id = $file->getID();
123 $can_edit = PhabricatorPolicyFilter
::hasCapability(
126 PhabricatorPolicyCapability
::CAN_EDIT
);
128 $curtain = $this->newCurtainView($file);
130 $can_download = !$file->getIsPartial();
132 if ($file->isViewableInBrowser()) {
134 id(new PhabricatorActionView())
135 ->setName(pht('View File'))
136 ->setIcon('fa-file-o')
137 ->setHref($file->getViewURI())
138 ->setDisabled(!$can_download)
139 ->setWorkflow(!$can_download));
142 id(new PhabricatorActionView())
144 ->setDownload($can_download)
145 ->setName(pht('Download File'))
146 ->setIcon('fa-download')
147 ->setHref($file->getDownloadURI())
148 ->setDisabled(!$can_download)
149 ->setWorkflow(!$can_download));
153 id(new PhabricatorActionView())
154 ->setName(pht('Edit File'))
155 ->setIcon('fa-pencil')
156 ->setHref($this->getApplicationURI("/edit/{$id}/"))
157 ->setWorkflow(!$can_edit)
158 ->setDisabled(!$can_edit));
161 id(new PhabricatorActionView())
162 ->setName(pht('Delete File'))
163 ->setIcon('fa-times')
164 ->setHref($this->getApplicationURI("/delete/{$id}/"))
166 ->setDisabled(!$can_edit));
169 id(new PhabricatorActionView())
170 ->setName(pht('View Transforms'))
172 ->setHref($this->getApplicationURI("/transforms/{$id}/")));
176 $viewer_phid = $viewer->getPHID();
177 $author_phid = $file->getAuthorPHID();
179 $phids[] = $author_phid;
182 $handles = $viewer->loadHandles($phids);
185 $author_refs = id(new PHUICurtainObjectRefListView())
186 ->setViewer($viewer);
188 $author_ref = $author_refs->newObjectRefView()
189 ->setHandle($handles[$author_phid])
190 ->setEpoch($file->getDateCreated())
191 ->setHighlighted($author_phid === $viewer_phid);
194 ->setHeaderText(pht('Authored By'))
195 ->appendChild($author_refs);
199 ->setHeaderText(pht('Size'))
200 ->appendChild(phutil_format_bytes($file->getByteSize()));
202 $width = $file->getImageWidth();
203 $height = $file->getImageHeight();
205 if ($width ||
$height) {
207 ->setHeaderText(pht('Dimensions'))
210 "%spx \xC3\x97 %spx",
211 new PhutilNumber($width),
212 new PhutilNumber($height)));
218 private function buildPropertyViews(
219 PHUIObjectBoxView
$box,
220 PhabricatorFile
$file) {
221 $request = $this->getRequest();
222 $viewer = $request->getUser();
224 $tab_group = id(new PHUITabGroupView());
225 $box->addTabGroup($tab_group);
227 $finfo = new PHUIPropertyListView();
230 id(new PHUITabView())
231 ->setName(pht('Details'))
233 ->appendChild($finfo));
237 $file->getMimeType());
239 $ttl = $file->getTtl();
241 $delta = $ttl - PhabricatorTime
::getNow();
247 phabricator_datetime($ttl, $viewer),
248 phutil_format_relative_time_detailed($delta)));
251 $is_image = $file->isViewableImage();
253 $image_string = pht('Yes');
254 $cache_string = $file->getCanCDN() ?
pht('Yes') : pht('No');
256 $image_string = pht('No');
257 $cache_string = pht('Not Applicable');
261 if ($file->isViewableImage()) {
262 $types[] = pht('Image');
265 if ($file->isVideo()) {
266 $types[] = pht('Video');
269 if ($file->isAudio()) {
270 $types[] = pht('Audio');
273 if ($file->getCanCDN()) {
274 $types[] = pht('Can CDN');
277 $builtin = $file->getBuiltinName();
278 if ($builtin !== null) {
279 $types[] = pht('Builtin ("%s")', $builtin);
282 if ($file->getIsProfileImage()) {
283 $types[] = pht('Profile');
287 $types = implode(', ', $types);
288 $finfo->addProperty(pht('Attributes'), $types);
292 pht('Storage Engine'),
293 $file->getStorageEngine());
295 $engine = $this->loadStorageEngine($file);
296 if ($engine && $engine->isChunkEngine()) {
297 $format_name = pht('Chunks');
299 $format_key = $file->getStorageFormat();
300 $format = PhabricatorFileStorageFormat
::getFormat($format_key);
302 $format_name = $format->getStorageFormatName();
304 $format_name = pht('Unknown ("%s")', $format_key);
307 $finfo->addProperty(pht('Storage Format'), $format_name);
310 pht('Storage Handle'),
311 $file->getStorageHandle());
313 $custom_alt = $file->getCustomAltText();
314 if ($custom_alt !== null && strlen($custom_alt)) {
315 $finfo->addProperty(pht('Custom Alt Text'), $custom_alt);
318 $default_alt = $file->getDefaultAltText();
319 if ($default_alt !== null && strlen($default_alt)) {
320 $finfo->addProperty(pht('Default Alt Text'), $default_alt);
323 $attachments_table = $this->newAttachmentsView($file);
326 id(new PHUITabView())
327 ->setName(pht('Attached'))
329 ->appendChild($attachments_table));
331 $engine = $this->loadStorageEngine($file);
333 if ($engine->isChunkEngine()) {
334 $chunkinfo = new PHUIPropertyListView();
337 id(new PHUITabView())
338 ->setName(pht('Chunks'))
340 ->appendChild($chunkinfo));
342 $chunks = id(new PhabricatorFileChunkQuery())
344 ->withChunkHandles(array($file->getStorageHandle()))
346 $chunks = msort($chunks, 'getByteStart');
349 $completed = array();
350 foreach ($chunks as $chunk) {
351 $is_complete = $chunk->getDataFilePHID();
354 $chunk->getByteStart(),
355 $chunk->getByteEnd(),
356 ($is_complete ?
pht('Yes') : pht('No')),
360 $completed[] = $chunk;
364 $table = id(new AphrontTableView($rows))
378 $chunkinfo->addProperty(
382 $chunkinfo->addProperty(
383 pht('Completed Chunks'),
386 $chunkinfo->addRawContent($table);
392 private function loadStorageEngine(PhabricatorFile
$file) {
396 $engine = $file->instantiateStorageEngine();
397 } catch (Exception
$ex) {
398 // Don't bother raising this anywhere for now.
404 private function newFileContent(PhabricatorFile
$file) {
405 $request = $this->getRequest();
407 $ref = id(new PhabricatorDocumentRef())
410 $engine = id(new PhabricatorFileDocumentRenderingEngine())
411 ->setRequest($request);
413 return $engine->newDocumentView($ref);
416 private function newAttachmentsView(PhabricatorFile
$file) {
417 $viewer = $this->getViewer();
419 $attachments = id(new PhabricatorFileAttachmentQuery())
421 ->withFilePHIDs(array($file->getPHID()))
424 $handles = $viewer->loadHandles(mpull($attachments, 'getObjectPHID'));
428 $mode_map = PhabricatorFileAttachment
::getModeNameMap();
429 $mode_attach = PhabricatorFileAttachment
::MODE_ATTACH
;
431 foreach ($attachments as $attachment) {
432 $object_phid = $attachment->getObjectPHID();
433 $handle = $handles[$object_phid];
435 $attachment_mode = $attachment->getAttachmentMode();
437 $mode_name = idx($mode_map, $attachment_mode);
438 if ($mode_name === null) {
439 $mode_name = pht('Unknown ("%s")', $attachment_mode);
442 $detach_uri = urisprintf(
443 '/file/ui/detach/%s/%s/',
447 $is_disabled = !$attachment->canDetach();
449 $detach_button = id(new PHUIButtonView())
450 ->setHref($detach_uri)
453 ->setDisabled($is_disabled)
454 ->setColor(PHUIButtonView
::GREY
)
455 ->setSize(PHUIButtonView
::SMALL
)
456 ->setText(pht('Detach File'));
461 'href' => $detach_uri,
462 'sigil' => 'workflow',
464 'class' => 'small button button-grey disabled',
469 $handle->renderLink(),
475 $table = id(new AphrontTableView($rows))