Roughly modularize document rendering in Files
Summary: Ref T13105. This change begins modularizing document rendering. I'm starting in Files since it's the use case with the smallest amount of complexity. Currently, we hard-coding the inline rendering for images, audio, and video. Instead, use the modular engine pattern to make rendering flexible and extensible. There aren't any options for switching modes yet and none of the renderers do anything fancy. This API is also probably very unstable. Test Plan: Viewwed images, audio, video, and other files. Saw reasonable renderings, with "nothing can render this" for any other file type. Maniphest Tasks: T13105 Differential Revision: https://secure.phabricator.com/D19237
This commit is contained in:
		@@ -9,7 +9,7 @@ return array(
 | 
				
			|||||||
  'names' => array(
 | 
					  'names' => array(
 | 
				
			||||||
    'conpherence.pkg.css' => 'e68cf1fa',
 | 
					    'conpherence.pkg.css' => 'e68cf1fa',
 | 
				
			||||||
    'conpherence.pkg.js' => '15191c65',
 | 
					    'conpherence.pkg.js' => '15191c65',
 | 
				
			||||||
    'core.pkg.css' => 'c218ed53',
 | 
					    'core.pkg.css' => '6a8ba174',
 | 
				
			||||||
    'core.pkg.js' => '8581cd02',
 | 
					    'core.pkg.js' => '8581cd02',
 | 
				
			||||||
    'differential.pkg.css' => '113e692c',
 | 
					    'differential.pkg.css' => '113e692c',
 | 
				
			||||||
    'differential.pkg.js' => 'f6d809c0',
 | 
					    'differential.pkg.js' => 'f6d809c0',
 | 
				
			||||||
@@ -168,7 +168,7 @@ return array(
 | 
				
			|||||||
    'rsrc/css/phui/phui-object-box.css' => '9cff003c',
 | 
					    'rsrc/css/phui/phui-object-box.css' => '9cff003c',
 | 
				
			||||||
    'rsrc/css/phui/phui-pager.css' => 'edcbc226',
 | 
					    'rsrc/css/phui/phui-pager.css' => 'edcbc226',
 | 
				
			||||||
    'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
 | 
					    'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
 | 
				
			||||||
    'rsrc/css/phui/phui-property-list-view.css' => '2dc7993f',
 | 
					    'rsrc/css/phui/phui-property-list-view.css' => '79fc3a02',
 | 
				
			||||||
    'rsrc/css/phui/phui-remarkup-preview.css' => '54a34863',
 | 
					    'rsrc/css/phui/phui-remarkup-preview.css' => '54a34863',
 | 
				
			||||||
    'rsrc/css/phui/phui-segment-bar-view.css' => 'b1d1b892',
 | 
					    'rsrc/css/phui/phui-segment-bar-view.css' => 'b1d1b892',
 | 
				
			||||||
    'rsrc/css/phui/phui-spacing.css' => '042804d6',
 | 
					    'rsrc/css/phui/phui-spacing.css' => '042804d6',
 | 
				
			||||||
@@ -848,7 +848,7 @@ return array(
 | 
				
			|||||||
    'phui-oi-simple-ui-css' => 'a8beebea',
 | 
					    'phui-oi-simple-ui-css' => 'a8beebea',
 | 
				
			||||||
    'phui-pager-css' => 'edcbc226',
 | 
					    'phui-pager-css' => 'edcbc226',
 | 
				
			||||||
    'phui-pinboard-view-css' => '2495140e',
 | 
					    'phui-pinboard-view-css' => '2495140e',
 | 
				
			||||||
    'phui-property-list-view-css' => '2dc7993f',
 | 
					    'phui-property-list-view-css' => '79fc3a02',
 | 
				
			||||||
    'phui-remarkup-preview-css' => '54a34863',
 | 
					    'phui-remarkup-preview-css' => '54a34863',
 | 
				
			||||||
    'phui-segment-bar-view-css' => 'b1d1b892',
 | 
					    'phui-segment-bar-view-css' => 'b1d1b892',
 | 
				
			||||||
    'phui-spacing-css' => '042804d6',
 | 
					    'phui-spacing-css' => '042804d6',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2066,6 +2066,7 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorAsanaConfigOptions' => 'applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php',
 | 
					    'PhabricatorAsanaConfigOptions' => 'applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php',
 | 
				
			||||||
    'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php',
 | 
					    'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php',
 | 
				
			||||||
    'PhabricatorAsanaTaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaTaskHasObjectEdgeType.php',
 | 
					    'PhabricatorAsanaTaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaTaskHasObjectEdgeType.php',
 | 
				
			||||||
 | 
					    'PhabricatorAudioDocumentEngine' => 'applications/files/document/PhabricatorAudioDocumentEngine.php',
 | 
				
			||||||
    'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php',
 | 
					    'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php',
 | 
				
			||||||
    'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php',
 | 
					    'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php',
 | 
				
			||||||
    'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php',
 | 
					    'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php',
 | 
				
			||||||
@@ -2808,6 +2809,8 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorDividerEditField' => 'applications/transactions/editfield/PhabricatorDividerEditField.php',
 | 
					    'PhabricatorDividerEditField' => 'applications/transactions/editfield/PhabricatorDividerEditField.php',
 | 
				
			||||||
    'PhabricatorDividerProfileMenuItem' => 'applications/search/menuitem/PhabricatorDividerProfileMenuItem.php',
 | 
					    'PhabricatorDividerProfileMenuItem' => 'applications/search/menuitem/PhabricatorDividerProfileMenuItem.php',
 | 
				
			||||||
    'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php',
 | 
					    'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php',
 | 
				
			||||||
 | 
					    'PhabricatorDocumentEngine' => 'applications/files/document/PhabricatorDocumentEngine.php',
 | 
				
			||||||
 | 
					    'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php',
 | 
				
			||||||
    'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php',
 | 
					    'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php',
 | 
				
			||||||
    'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php',
 | 
					    'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php',
 | 
				
			||||||
    'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php',
 | 
					    'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php',
 | 
				
			||||||
@@ -3155,6 +3158,7 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
 | 
					    'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
 | 
				
			||||||
    'PhabricatorIconSetEditField' => 'applications/transactions/editfield/PhabricatorIconSetEditField.php',
 | 
					    'PhabricatorIconSetEditField' => 'applications/transactions/editfield/PhabricatorIconSetEditField.php',
 | 
				
			||||||
    'PhabricatorIconSetIcon' => 'applications/files/iconset/PhabricatorIconSetIcon.php',
 | 
					    'PhabricatorIconSetIcon' => 'applications/files/iconset/PhabricatorIconSetIcon.php',
 | 
				
			||||||
 | 
					    'PhabricatorImageDocumentEngine' => 'applications/files/document/PhabricatorImageDocumentEngine.php',
 | 
				
			||||||
    'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php',
 | 
					    'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php',
 | 
				
			||||||
    'PhabricatorImageRemarkupRule' => 'applications/files/markup/PhabricatorImageRemarkupRule.php',
 | 
					    'PhabricatorImageRemarkupRule' => 'applications/files/markup/PhabricatorImageRemarkupRule.php',
 | 
				
			||||||
    'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php',
 | 
					    'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php',
 | 
				
			||||||
@@ -4481,7 +4485,9 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php',
 | 
					    'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php',
 | 
				
			||||||
    'PhabricatorVersionedDraft' => 'applications/draft/storage/PhabricatorVersionedDraft.php',
 | 
					    'PhabricatorVersionedDraft' => 'applications/draft/storage/PhabricatorVersionedDraft.php',
 | 
				
			||||||
    'PhabricatorVeryWowEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorVeryWowEnglishTranslation.php',
 | 
					    'PhabricatorVeryWowEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorVeryWowEnglishTranslation.php',
 | 
				
			||||||
 | 
					    'PhabricatorVideoDocumentEngine' => 'applications/files/document/PhabricatorVideoDocumentEngine.php',
 | 
				
			||||||
    'PhabricatorViewerDatasource' => 'applications/people/typeahead/PhabricatorViewerDatasource.php',
 | 
					    'PhabricatorViewerDatasource' => 'applications/people/typeahead/PhabricatorViewerDatasource.php',
 | 
				
			||||||
 | 
					    'PhabricatorVoidDocumentEngine' => 'applications/files/document/PhabricatorVoidDocumentEngine.php',
 | 
				
			||||||
    'PhabricatorWatcherHasObjectEdgeType' => 'applications/transactions/edges/PhabricatorWatcherHasObjectEdgeType.php',
 | 
					    'PhabricatorWatcherHasObjectEdgeType' => 'applications/transactions/edges/PhabricatorWatcherHasObjectEdgeType.php',
 | 
				
			||||||
    'PhabricatorWebContentSource' => 'infrastructure/contentsource/PhabricatorWebContentSource.php',
 | 
					    'PhabricatorWebContentSource' => 'infrastructure/contentsource/PhabricatorWebContentSource.php',
 | 
				
			||||||
    'PhabricatorWebServerSetupCheck' => 'applications/config/check/PhabricatorWebServerSetupCheck.php',
 | 
					    'PhabricatorWebServerSetupCheck' => 'applications/config/check/PhabricatorWebServerSetupCheck.php',
 | 
				
			||||||
@@ -7497,6 +7503,7 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
 | 
					    'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
 | 
				
			||||||
    'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
					    'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
				
			||||||
    'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
					    'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
				
			||||||
 | 
					    'PhabricatorAudioDocumentEngine' => 'PhabricatorDocumentEngine',
 | 
				
			||||||
    'PhabricatorAuditActionConstants' => 'Phobject',
 | 
					    'PhabricatorAuditActionConstants' => 'Phobject',
 | 
				
			||||||
    'PhabricatorAuditApplication' => 'PhabricatorApplication',
 | 
					    'PhabricatorAuditApplication' => 'PhabricatorApplication',
 | 
				
			||||||
    'PhabricatorAuditCommentEditor' => 'PhabricatorEditor',
 | 
					    'PhabricatorAuditCommentEditor' => 'PhabricatorEditor',
 | 
				
			||||||
@@ -8362,6 +8369,8 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorDividerEditField' => 'PhabricatorEditField',
 | 
					    'PhabricatorDividerEditField' => 'PhabricatorEditField',
 | 
				
			||||||
    'PhabricatorDividerProfileMenuItem' => 'PhabricatorProfileMenuItem',
 | 
					    'PhabricatorDividerProfileMenuItem' => 'PhabricatorProfileMenuItem',
 | 
				
			||||||
    'PhabricatorDivinerApplication' => 'PhabricatorApplication',
 | 
					    'PhabricatorDivinerApplication' => 'PhabricatorApplication',
 | 
				
			||||||
 | 
					    'PhabricatorDocumentEngine' => 'Phobject',
 | 
				
			||||||
 | 
					    'PhabricatorDocumentRef' => 'Phobject',
 | 
				
			||||||
    'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication',
 | 
					    'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication',
 | 
				
			||||||
    'PhabricatorDraft' => 'PhabricatorDraftDAO',
 | 
					    'PhabricatorDraft' => 'PhabricatorDraftDAO',
 | 
				
			||||||
    'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
 | 
					    'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
 | 
				
			||||||
@@ -8756,6 +8765,7 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorIconSet' => 'Phobject',
 | 
					    'PhabricatorIconSet' => 'Phobject',
 | 
				
			||||||
    'PhabricatorIconSetEditField' => 'PhabricatorEditField',
 | 
					    'PhabricatorIconSetEditField' => 'PhabricatorEditField',
 | 
				
			||||||
    'PhabricatorIconSetIcon' => 'Phobject',
 | 
					    'PhabricatorIconSetIcon' => 'Phobject',
 | 
				
			||||||
 | 
					    'PhabricatorImageDocumentEngine' => 'PhabricatorDocumentEngine',
 | 
				
			||||||
    'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule',
 | 
					    'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule',
 | 
				
			||||||
    'PhabricatorImageRemarkupRule' => 'PhutilRemarkupRule',
 | 
					    'PhabricatorImageRemarkupRule' => 'PhutilRemarkupRule',
 | 
				
			||||||
    'PhabricatorImageTransformer' => 'Phobject',
 | 
					    'PhabricatorImageTransformer' => 'Phobject',
 | 
				
			||||||
@@ -10330,7 +10340,9 @@ phutil_register_library_map(array(
 | 
				
			|||||||
    'PhabricatorVCSResponse' => 'AphrontResponse',
 | 
					    'PhabricatorVCSResponse' => 'AphrontResponse',
 | 
				
			||||||
    'PhabricatorVersionedDraft' => 'PhabricatorDraftDAO',
 | 
					    'PhabricatorVersionedDraft' => 'PhabricatorDraftDAO',
 | 
				
			||||||
    'PhabricatorVeryWowEnglishTranslation' => 'PhutilTranslation',
 | 
					    'PhabricatorVeryWowEnglishTranslation' => 'PhutilTranslation',
 | 
				
			||||||
 | 
					    'PhabricatorVideoDocumentEngine' => 'PhabricatorDocumentEngine',
 | 
				
			||||||
    'PhabricatorViewerDatasource' => 'PhabricatorTypeaheadDatasource',
 | 
					    'PhabricatorViewerDatasource' => 'PhabricatorTypeaheadDatasource',
 | 
				
			||||||
 | 
					    'PhabricatorVoidDocumentEngine' => 'PhabricatorDocumentEngine',
 | 
				
			||||||
    'PhabricatorWatcherHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
					    'PhabricatorWatcherHasObjectEdgeType' => 'PhabricatorEdgeType',
 | 
				
			||||||
    'PhabricatorWebContentSource' => 'PhabricatorContentSource',
 | 
					    'PhabricatorWebContentSource' => 'PhabricatorContentSource',
 | 
				
			||||||
    'PhabricatorWebServerSetupCheck' => 'PhabricatorSetupCheck',
 | 
					    'PhabricatorWebServerSetupCheck' => 'PhabricatorSetupCheck',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return id(new AphrontRedirectResponse())->setURI($file->getInfoURI());
 | 
					      return id(new AphrontRedirectResponse())->setURI($file->getInfoURI());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $file = id(new PhabricatorFileQuery())
 | 
					    $file = id(new PhabricatorFileQuery())
 | 
				
			||||||
      ->setViewer($viewer)
 | 
					      ->setViewer($viewer)
 | 
				
			||||||
      ->withIDs(array($id))
 | 
					      ->withIDs(array($id))
 | 
				
			||||||
@@ -62,31 +63,34 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
 | 
				
			|||||||
    $timeline = $this->buildTransactionView($file);
 | 
					    $timeline = $this->buildTransactionView($file);
 | 
				
			||||||
    $crumbs = $this->buildApplicationCrumbs();
 | 
					    $crumbs = $this->buildApplicationCrumbs();
 | 
				
			||||||
    $crumbs->addTextCrumb(
 | 
					    $crumbs->addTextCrumb(
 | 
				
			||||||
      'F'.$file->getID(),
 | 
					      $file->getMonogram(),
 | 
				
			||||||
      $this->getApplicationURI("/info/{$phid}/"));
 | 
					      $file->getInfoURI());
 | 
				
			||||||
    $crumbs->setBorder(true);
 | 
					    $crumbs->setBorder(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $object_box = id(new PHUIObjectBoxView())
 | 
					    $object_box = id(new PHUIObjectBoxView())
 | 
				
			||||||
      ->setHeaderText(pht('File'))
 | 
					      ->setHeaderText(pht('File Metadata'))
 | 
				
			||||||
      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
 | 
					      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $this->buildPropertyViews($object_box, $file);
 | 
					    $this->buildPropertyViews($object_box, $file);
 | 
				
			||||||
    $title = $file->getName();
 | 
					    $title = $file->getName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $file_content = $this->newFileContent($file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $view = id(new PHUITwoColumnView())
 | 
					    $view = id(new PHUITwoColumnView())
 | 
				
			||||||
      ->setHeader($header)
 | 
					      ->setHeader($header)
 | 
				
			||||||
      ->setCurtain($curtain)
 | 
					      ->setCurtain($curtain)
 | 
				
			||||||
      ->setMainColumn(array(
 | 
					      ->setMainColumn(
 | 
				
			||||||
        $object_box,
 | 
					        array(
 | 
				
			||||||
        $timeline,
 | 
					          $object_box,
 | 
				
			||||||
      ));
 | 
					          $file_content,
 | 
				
			||||||
 | 
					          $timeline,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $this->newPage()
 | 
					    return $this->newPage()
 | 
				
			||||||
      ->setTitle($title)
 | 
					      ->setTitle($title)
 | 
				
			||||||
      ->setCrumbs($crumbs)
 | 
					      ->setCrumbs($crumbs)
 | 
				
			||||||
      ->setPageObjectPHIDs(array($file->getPHID()))
 | 
					      ->setPageObjectPHIDs(array($file->getPHID()))
 | 
				
			||||||
      ->appendChild($view);
 | 
					      ->appendChild($view);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private function buildTransactionView(PhabricatorFile $file) {
 | 
					  private function buildTransactionView(PhabricatorFile $file) {
 | 
				
			||||||
@@ -325,61 +329,6 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
 | 
				
			|||||||
        $viewer->renderHandleList($phids));
 | 
					        $viewer->renderHandleList($phids));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ($file->isViewableImage()) {
 | 
					 | 
				
			||||||
      $image = phutil_tag(
 | 
					 | 
				
			||||||
        'img',
 | 
					 | 
				
			||||||
        array(
 | 
					 | 
				
			||||||
          'src' => $file->getViewURI(),
 | 
					 | 
				
			||||||
          'class' => 'phui-property-list-image',
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      $linked_image = phutil_tag(
 | 
					 | 
				
			||||||
        'a',
 | 
					 | 
				
			||||||
        array(
 | 
					 | 
				
			||||||
          'href' => $file->getViewURI(),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        $image);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      $media = id(new PHUIPropertyListView())
 | 
					 | 
				
			||||||
        ->addImageContent($linked_image);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      $box->addPropertyList($media);
 | 
					 | 
				
			||||||
    } else if ($file->isVideo()) {
 | 
					 | 
				
			||||||
      $video = phutil_tag(
 | 
					 | 
				
			||||||
        'video',
 | 
					 | 
				
			||||||
        array(
 | 
					 | 
				
			||||||
          'controls' => 'controls',
 | 
					 | 
				
			||||||
          'class' => 'phui-property-list-video',
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        phutil_tag(
 | 
					 | 
				
			||||||
          'source',
 | 
					 | 
				
			||||||
          array(
 | 
					 | 
				
			||||||
            'src' => $file->getViewURI(),
 | 
					 | 
				
			||||||
            'type' => $file->getMimeType(),
 | 
					 | 
				
			||||||
          )));
 | 
					 | 
				
			||||||
      $media = id(new PHUIPropertyListView())
 | 
					 | 
				
			||||||
        ->addImageContent($video);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      $box->addPropertyList($media);
 | 
					 | 
				
			||||||
    } else if ($file->isAudio()) {
 | 
					 | 
				
			||||||
      $audio = phutil_tag(
 | 
					 | 
				
			||||||
        'audio',
 | 
					 | 
				
			||||||
        array(
 | 
					 | 
				
			||||||
          'controls' => 'controls',
 | 
					 | 
				
			||||||
          'class' => 'phui-property-list-audio',
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        phutil_tag(
 | 
					 | 
				
			||||||
          'source',
 | 
					 | 
				
			||||||
          array(
 | 
					 | 
				
			||||||
            'src' => $file->getViewURI(),
 | 
					 | 
				
			||||||
            'type' => $file->getMimeType(),
 | 
					 | 
				
			||||||
          )));
 | 
					 | 
				
			||||||
      $media = id(new PHUIPropertyListView())
 | 
					 | 
				
			||||||
        ->addImageContent($audio);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      $box->addPropertyList($media);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    $engine = $this->loadStorageEngine($file);
 | 
					    $engine = $this->loadStorageEngine($file);
 | 
				
			||||||
    if ($engine) {
 | 
					    if ($engine) {
 | 
				
			||||||
      if ($engine->isChunkEngine()) {
 | 
					      if ($engine->isChunkEngine()) {
 | 
				
			||||||
@@ -453,5 +402,52 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
 | 
				
			|||||||
    return $engine;
 | 
					    return $engine;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private function newFileContent(PhabricatorFile $file) {
 | 
				
			||||||
 | 
					    $viewer = $this->getViewer();
 | 
				
			||||||
 | 
					    $engines = PhabricatorDocumentEngine::getAllEngines();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ref = id(new PhabricatorDocumentRef())
 | 
				
			||||||
 | 
					      ->setFile($file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    foreach ($engines as $key => $engine) {
 | 
				
			||||||
 | 
					      $engine = id(clone $engine)
 | 
				
			||||||
 | 
					        ->setViewer($viewer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!$engine->canRenderDocument($ref)) {
 | 
				
			||||||
 | 
					        unset($engines[$key]);
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $engines[$key] = $engine;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!$engines) {
 | 
				
			||||||
 | 
					      throw new Exception(pht('No engine can render this document.'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $vectors = array();
 | 
				
			||||||
 | 
					    foreach ($engines as $key => $usable_engine) {
 | 
				
			||||||
 | 
					      $vectors[$key] = $usable_engine->newSortVector($ref);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    $vectors = msortv($vectors, 'getSelf');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $engine = $engines[head_key($vectors)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $content = $engine->newDocument($ref);
 | 
				
			||||||
 | 
					    if (!$content) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $icon = $engine->newDocumentIcon($ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $header = id(new PHUIHeaderView())
 | 
				
			||||||
 | 
					      ->setHeaderIcon($icon)
 | 
				
			||||||
 | 
					      ->setHeader($ref->getName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return id(new PHUIObjectBoxView())
 | 
				
			||||||
 | 
					      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
 | 
				
			||||||
 | 
					      ->setHeader($header)
 | 
				
			||||||
 | 
					      ->appendChild($content);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class PhabricatorAudioDocumentEngine
 | 
				
			||||||
 | 
					  extends PhabricatorDocumentEngine {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ENGINEKEY = 'audio';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return 'fa-file-sound-o';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function canRenderDocumentType(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      return $file->isAudio();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $viewable_types = PhabricatorEnv::getEnvConfig('files.viewable-mime-types');
 | 
				
			||||||
 | 
					    $viewable_types = array_keys($viewable_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $audio_types = PhabricatorEnv::getEnvConfig('files.audio-mime-types');
 | 
				
			||||||
 | 
					    $audio_types = array_keys($audio_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($viewable_types) &&
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($audio_types);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function newDocumentContent(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      $source_uri = $file->getViewURI();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw new PhutilMethodNotImplementedException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $mime_type = $ref->getMimeType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $audio = phutil_tag(
 | 
				
			||||||
 | 
					      'audio',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'controls' => 'controls',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      phutil_tag(
 | 
				
			||||||
 | 
					        'source',
 | 
				
			||||||
 | 
					        array(
 | 
				
			||||||
 | 
					          'src' => $source_uri,
 | 
				
			||||||
 | 
					          'type' => $mime_type,
 | 
				
			||||||
 | 
					        )));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $container = phutil_tag(
 | 
				
			||||||
 | 
					      'div',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'class' => 'document-engine-audio',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      $audio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return $container;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class PhabricatorDocumentEngine
 | 
				
			||||||
 | 
					  extends Phobject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private $viewer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function setViewer(PhabricatorUser $viewer) {
 | 
				
			||||||
 | 
					    $this->viewer = $viewer;
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function getViewer() {
 | 
				
			||||||
 | 
					    return $this->viewer;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function canRenderDocument(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return $this->canRenderDocumentType($ref);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract protected function canRenderDocumentType(
 | 
				
			||||||
 | 
					    PhabricatorDocumentRef $ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function newDocument(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return $this->newDocumentContent($ref);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function newDocumentIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return id(new PHUIIconView())
 | 
				
			||||||
 | 
					      ->setIcon($this->getDocumentIconIcon($ref));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract protected function newDocumentContent(
 | 
				
			||||||
 | 
					    PhabricatorDocumentRef $ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return 'fa-file-o';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function getDocumentEngineKey() {
 | 
				
			||||||
 | 
					    return $this->getPhobjectClassConstant('ENGINEKEY');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public static function getAllEngines() {
 | 
				
			||||||
 | 
					    return id(new PhutilClassMapQuery())
 | 
				
			||||||
 | 
					      ->setAncestorClass(__CLASS__)
 | 
				
			||||||
 | 
					      ->setUniqueMethod('getDocumentEngineKey')
 | 
				
			||||||
 | 
					      ->execute();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final public function newSortVector(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $content_score = $this->getContentScore($ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return id(new PhutilSortVector())
 | 
				
			||||||
 | 
					      ->addInt(-$content_score);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getContentScore() {
 | 
				
			||||||
 | 
					    return 2000;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/applications/files/document/PhabricatorDocumentRef.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/applications/files/document/PhabricatorDocumentRef.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class PhabricatorDocumentRef
 | 
				
			||||||
 | 
					  extends Phobject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private $name;
 | 
				
			||||||
 | 
					  private $mimeType;
 | 
				
			||||||
 | 
					  private $file;
 | 
				
			||||||
 | 
					  private $byteLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function setFile(PhabricatorFile $file) {
 | 
				
			||||||
 | 
					    $this->file = $file;
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function getFile() {
 | 
				
			||||||
 | 
					    return $this->file;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function setMimeType($mime_type) {
 | 
				
			||||||
 | 
					    $this->mimeType = $mime_type;
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function getMimeType() {
 | 
				
			||||||
 | 
					    if ($this->mimeType !== null) {
 | 
				
			||||||
 | 
					      return $this->mimeType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($this->file) {
 | 
				
			||||||
 | 
					      return $this->file->getMimeType();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function setName($name) {
 | 
				
			||||||
 | 
					    $this->name = $name;
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function getName() {
 | 
				
			||||||
 | 
					    if ($this->name !== null) {
 | 
				
			||||||
 | 
					      return $this->name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($this->file) {
 | 
				
			||||||
 | 
					      return $this->file->getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function setByteLength($length) {
 | 
				
			||||||
 | 
					    $this->byteLength = $length;
 | 
				
			||||||
 | 
					    return $this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function getLength() {
 | 
				
			||||||
 | 
					    if ($this->byteLength !== null) {
 | 
				
			||||||
 | 
					      return $this->byteLength;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($this->file) {
 | 
				
			||||||
 | 
					      return (int)$this->file->getByteSize();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function hasAnyMimeType(array $candidate_types) {
 | 
				
			||||||
 | 
					    $mime_full = $this->getMimeType();
 | 
				
			||||||
 | 
					    $mime_parts = explode(';', $mime_full);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $mime_type = head($mime_parts);
 | 
				
			||||||
 | 
					    $mime_type = $this->normalizeMimeType($mime_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    foreach ($candidate_types as $candidate_type) {
 | 
				
			||||||
 | 
					      if ($this->normalizeMimeType($candidate_type) === $mime_type) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private function normalizeMimeType($mime_type) {
 | 
				
			||||||
 | 
					    $mime_type = trim($mime_type);
 | 
				
			||||||
 | 
					    $mime_type = phutil_utf8_strtolower($mime_type);
 | 
				
			||||||
 | 
					    return $mime_type;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class PhabricatorImageDocumentEngine
 | 
				
			||||||
 | 
					  extends PhabricatorDocumentEngine {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ENGINEKEY = 'image';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return 'fa-file-image-o';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function canRenderDocumentType(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      return $file->isViewableImage();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $viewable_types = PhabricatorEnv::getEnvConfig('files.viewable-mime-types');
 | 
				
			||||||
 | 
					    $viewable_types = array_keys($viewable_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $image_types = PhabricatorEnv::getEnvConfig('files.image-mime-types');
 | 
				
			||||||
 | 
					    $image_types = array_keys($image_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($viewable_types) &&
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($image_types);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function newDocumentContent(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      $source_uri = $file->getViewURI();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // We could use a "data:" URI here. It's not yet clear if or when we'll
 | 
				
			||||||
 | 
					      // have a ref but no backing file.
 | 
				
			||||||
 | 
					      throw new PhutilMethodNotImplementedException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $image = phutil_tag(
 | 
				
			||||||
 | 
					      'img',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'src' => $source_uri,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $linked_image = phutil_tag(
 | 
				
			||||||
 | 
					      'a',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'href' => $source_uri,
 | 
				
			||||||
 | 
					        'rel' => 'noreferrer',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      $image);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $container = phutil_tag(
 | 
				
			||||||
 | 
					      'div',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'class' => 'document-engine-image',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      $linked_image);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return $container;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class PhabricatorVideoDocumentEngine
 | 
				
			||||||
 | 
					  extends PhabricatorDocumentEngine {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ENGINEKEY = 'video';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return 'fa-film';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function canRenderDocumentType(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      return $file->isVideo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $viewable_types = PhabricatorEnv::getEnvConfig('files.viewable-mime-types');
 | 
				
			||||||
 | 
					    $viewable_types = array_keys($viewable_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $video_types = PhabricatorEnv::getEnvConfig('files.video-mime-types');
 | 
				
			||||||
 | 
					    $video_types = array_keys($video_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($viewable_types) &&
 | 
				
			||||||
 | 
					      $ref->hasAnyMimeType($video_types);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function newDocumentContent(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $file = $ref->getFile();
 | 
				
			||||||
 | 
					    if ($file) {
 | 
				
			||||||
 | 
					      $source_uri = $file->getViewURI();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw new PhutilMethodNotImplementedException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $mime_type = $ref->getMimeType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $video = phutil_tag(
 | 
				
			||||||
 | 
					      'video',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'controls' => 'controls',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      phutil_tag(
 | 
				
			||||||
 | 
					        'source',
 | 
				
			||||||
 | 
					        array(
 | 
				
			||||||
 | 
					          'src' => $source_uri,
 | 
				
			||||||
 | 
					          'type' => $mime_type,
 | 
				
			||||||
 | 
					        )));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $container = phutil_tag(
 | 
				
			||||||
 | 
					      'div',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'class' => 'document-engine-video',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      $video);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return $container;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class PhabricatorVoidDocumentEngine
 | 
				
			||||||
 | 
					  extends PhabricatorDocumentEngine {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ENGINEKEY = 'void';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return 'fa-file';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function getContentScore() {
 | 
				
			||||||
 | 
					    return 1000;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function canRenderDocumentType(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected function newDocumentContent(PhabricatorDocumentRef $ref) {
 | 
				
			||||||
 | 
					    $message = pht(
 | 
				
			||||||
 | 
					      'No document engine can render the contents of this file.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $container = phutil_tag(
 | 
				
			||||||
 | 
					      'div',
 | 
				
			||||||
 | 
					      array(
 | 
				
			||||||
 | 
					        'class' => 'document-engine-message',
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      $message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return $container;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -307,9 +307,14 @@ final class PHUIHeaderView extends AphrontTagView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    $icon = null;
 | 
					    $icon = null;
 | 
				
			||||||
    if ($this->headerIcon) {
 | 
					    if ($this->headerIcon) {
 | 
				
			||||||
      $icon = id(new PHUIIconView())
 | 
					      if ($this->headerIcon instanceof PHUIIconView) {
 | 
				
			||||||
        ->setIcon($this->headerIcon)
 | 
					        $icon = id(clone $this->headerIcon)
 | 
				
			||||||
        ->addClass('phui-header-icon');
 | 
					          ->addClass('phui-header-icon');
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        $icon = id(new PHUIIconView())
 | 
				
			||||||
 | 
					          ->setIcon($this->headerIcon)
 | 
				
			||||||
 | 
					          ->addClass('phui-header-icon');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $header_content = $this->header;
 | 
					    $header_content = $this->header;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -149,24 +149,6 @@ div.phui-property-list-stacked .phui-property-list-properties
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.phui-property-list-image {
 | 
					 | 
				
			||||||
  margin: auto;
 | 
					 | 
				
			||||||
  max-width: 95%;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.phui-property-list-audio {
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  margin: 16px auto;
 | 
					 | 
				
			||||||
  width: 50%;
 | 
					 | 
				
			||||||
  min-width: 240px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.phui-property-list-video {
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  margin: 0 auto;
 | 
					 | 
				
			||||||
  max-width: 95%;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* When tags appear in property lists, give them a little more vertical
 | 
					/* When tags appear in property lists, give them a little more vertical
 | 
				
			||||||
   spacing. */
 | 
					   spacing. */
 | 
				
			||||||
.phui-property-list-value .phui-tag-view {
 | 
					.phui-property-list-value .phui-tag-view {
 | 
				
			||||||
@@ -220,3 +202,32 @@ div.phui-property-list-stacked .phui-property-list-properties
 | 
				
			|||||||
  border-right: 1px solid {$lightblueborder};
 | 
					  border-right: 1px solid {$lightblueborder};
 | 
				
			||||||
  border-bottom: 1px solid {$blueborder};
 | 
					  border-bottom: 1px solid {$blueborder};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.document-engine-image img {
 | 
				
			||||||
 | 
					  margin: 20px auto;
 | 
				
			||||||
 | 
					  background: url('/rsrc/image/checker_light.png');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.device-desktop .document-engine-image img:hover {
 | 
				
			||||||
 | 
					  background: url('/rsrc/image/checker_dark.png');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.document-engine-video video {
 | 
				
			||||||
 | 
					  margin: 20px auto;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  max-width: 95%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.document-engine-audio audio {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  margin: 16px auto;
 | 
				
			||||||
 | 
					  width: 50%;
 | 
				
			||||||
 | 
					  min-width: 240px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.document-engine-message {
 | 
				
			||||||
 | 
					  margin: 20px auto;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  color: {$greytext};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user