mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-21 13:00:56 +01:00
Modularize file transforms and provide a "transforms" UI
Summary: Ref T7707. Available transforms are currently relatively hard-coded and don't really have any support UI. Modularize them so we can build some support UI. This doesn't actually //use// any of the new stuff yet: I want to make a clean cutover once I fix the aspect ratio stuff so I can pick up a cachekey/URI change as a side effect. Test Plan: {F400524} Reviewers: btrahan Reviewed By: btrahan Subscribers: chad, epriestley Maniphest Tasks: T7707 Differential Revision: https://secure.phabricator.com/D12808
This commit is contained in:
parent
ae32d1afb8
commit
c998e44b5a
9 changed files with 319 additions and 16 deletions
|
@ -1860,6 +1860,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileFilePHIDType' => 'applications/files/phid/PhabricatorFileFilePHIDType.php',
|
||||
'PhabricatorFileHasObjectEdgeType' => 'applications/files/edge/PhabricatorFileHasObjectEdgeType.php',
|
||||
'PhabricatorFileImageMacro' => 'applications/macro/storage/PhabricatorFileImageMacro.php',
|
||||
'PhabricatorFileImageTransform' => 'applications/files/transform/PhabricatorFileImageTransform.php',
|
||||
'PhabricatorFileInfoController' => 'applications/files/controller/PhabricatorFileInfoController.php',
|
||||
'PhabricatorFileLinkListView' => 'view/layout/PhabricatorFileLinkListView.php',
|
||||
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
||||
|
@ -1873,10 +1874,13 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
|
||||
'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
|
||||
'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
|
||||
'PhabricatorFileThumbnailTransform' => 'applications/files/transform/PhabricatorFileThumbnailTransform.php',
|
||||
'PhabricatorFileTransaction' => 'applications/files/storage/PhabricatorFileTransaction.php',
|
||||
'PhabricatorFileTransactionComment' => 'applications/files/storage/PhabricatorFileTransactionComment.php',
|
||||
'PhabricatorFileTransactionQuery' => 'applications/files/query/PhabricatorFileTransactionQuery.php',
|
||||
'PhabricatorFileTransform' => 'applications/files/transform/PhabricatorFileTransform.php',
|
||||
'PhabricatorFileTransformController' => 'applications/files/controller/PhabricatorFileTransformController.php',
|
||||
'PhabricatorFileTransformListController' => 'applications/files/controller/PhabricatorFileTransformListController.php',
|
||||
'PhabricatorFileUploadController' => 'applications/files/controller/PhabricatorFileUploadController.php',
|
||||
'PhabricatorFileUploadDialogController' => 'applications/files/controller/PhabricatorFileUploadDialogController.php',
|
||||
'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php',
|
||||
|
@ -5262,6 +5266,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorFileImageTransform' => 'PhabricatorFileTransform',
|
||||
'PhabricatorFileInfoController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileLinkListView' => 'AphrontView',
|
||||
'PhabricatorFileLinkView' => 'AphrontView',
|
||||
|
@ -5274,10 +5279,13 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorFileTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||
'PhabricatorFileThumbnailTransform' => 'PhabricatorFileImageTransform',
|
||||
'PhabricatorFileTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorFileTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'PhabricatorFileTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorFileTransform' => 'Phobject',
|
||||
'PhabricatorFileTransformController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileTransformListController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileUploadController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileUploadDialogController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileUploadException' => 'Exception',
|
||||
|
|
|
@ -91,6 +91,8 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
|||
'(?P<phid>[^/]+)/'.
|
||||
'(?P<key>[^/]+)/'
|
||||
=> 'PhabricatorFileTransformController',
|
||||
'transforms/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorFileTransformListController',
|
||||
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
|
||||
'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
|
||||
),
|
||||
|
|
|
@ -170,6 +170,12 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit));
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('View Transforms'))
|
||||
->setIcon('fa-crop')
|
||||
->setHref($this->getApplicationURI("/transforms/{$id}/")));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
@ -267,7 +273,6 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
$user->renderHandleList($phids));
|
||||
}
|
||||
|
||||
|
||||
if ($file->isViewableImage()) {
|
||||
$image = phutil_tag(
|
||||
'img',
|
||||
|
|
|
@ -48,21 +48,33 @@ final class PhabricatorFileTransformController
|
|||
// protection.
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
|
||||
switch ($transform) {
|
||||
case 'thumb-profile':
|
||||
$xformed_file = $this->executeThumbTransform($file, 50, 50);
|
||||
break;
|
||||
case 'thumb-280x210':
|
||||
$xformed_file = $this->executeThumbTransform($file, 280, 210);
|
||||
break;
|
||||
case 'preview-100':
|
||||
$xformed_file = $this->executePreviewTransform($file, 100);
|
||||
break;
|
||||
case 'preview-220':
|
||||
$xformed_file = $this->executePreviewTransform($file, 220);
|
||||
break;
|
||||
default:
|
||||
return new Aphront400Response();
|
||||
$xformed_file = null;
|
||||
|
||||
$xforms = PhabricatorFileTransform::getAllTransforms();
|
||||
if (isset($xforms[$transform])) {
|
||||
$xform = $xforms[$transform];
|
||||
if ($xform->canApplyTransform($file)) {
|
||||
$xformed_file = $xforms[$transform]->applyTransform($file);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$xformed_file) {
|
||||
switch ($transform) {
|
||||
case 'thumb-profile':
|
||||
$xformed_file = $this->executeThumbTransform($file, 50, 50);
|
||||
break;
|
||||
case 'thumb-280x210':
|
||||
$xformed_file = $this->executeThumbTransform($file, 280, 210);
|
||||
break;
|
||||
case 'preview-100':
|
||||
$xformed_file = $this->executePreviewTransform($file, 100);
|
||||
break;
|
||||
case 'preview-220':
|
||||
$xformed_file = $this->executePreviewTransform($file, 220);
|
||||
break;
|
||||
default:
|
||||
return new Aphront400Response();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$xformed_file) {
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileTransformListController
|
||||
extends PhabricatorFileController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$monogram = $file->getMonogram();
|
||||
|
||||
$xdst = id(new PhabricatorTransformedFile())->loadAllWhere(
|
||||
'transformedPHID = %s',
|
||||
$file->getPHID());
|
||||
|
||||
$dst_rows = array();
|
||||
foreach ($xdst as $source) {
|
||||
$dst_rows[] = array(
|
||||
$source->getTransform(),
|
||||
$viewer->renderHandle($source->getOriginalPHID()),
|
||||
);
|
||||
}
|
||||
$dst_table = id(new AphrontTableView($dst_rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Key'),
|
||||
pht('Source'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'wide',
|
||||
))
|
||||
->setNoDataString(
|
||||
pht(
|
||||
'This file was not created by transforming another file.'));
|
||||
|
||||
$xsrc = id(new PhabricatorTransformedFile())->loadAllWhere(
|
||||
'originalPHID = %s',
|
||||
$file->getPHID());
|
||||
$xsrc = mpull($xsrc, 'getTransformedPHID', 'getTransform');
|
||||
|
||||
$src_rows = array();
|
||||
$xforms = PhabricatorFileTransform::getAllTransforms();
|
||||
foreach ($xforms as $xform) {
|
||||
$dst_phid = idx($xsrc, $xform->getTransformKey());
|
||||
|
||||
if ($xform->canApplyTransform($file)) {
|
||||
$can_apply = pht('Yes');
|
||||
$view_href = $file->getURIForTransform($xform);
|
||||
if ($dst_phid) {
|
||||
$view_text = pht('View Transform');
|
||||
} else {
|
||||
$view_text = pht('Generate Transform');
|
||||
}
|
||||
$view_link = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => 'small grey button',
|
||||
'href' => $view_href,
|
||||
),
|
||||
$view_text);
|
||||
} else {
|
||||
$can_apply = phutil_tag('em', array(), pht('No'));
|
||||
$view_link = phutil_tag('em', array(), pht('None'));
|
||||
}
|
||||
|
||||
if ($dst_phid) {
|
||||
$dst_link = $viewer->renderHandle($dst_phid);
|
||||
} else {
|
||||
$dst_link = phutil_tag('em', array(), pht('None'));
|
||||
}
|
||||
|
||||
$src_rows[] = array(
|
||||
$xform->getTransformName(),
|
||||
$xform->getTransformKey(),
|
||||
$can_apply,
|
||||
$dst_link,
|
||||
$view_link,
|
||||
);
|
||||
}
|
||||
|
||||
$src_table = id(new AphrontTableView($src_rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Name'),
|
||||
pht('Key'),
|
||||
pht('Supported'),
|
||||
pht('Transform'),
|
||||
pht('View'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'wide',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'action',
|
||||
));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($monogram, '/'.$monogram);
|
||||
$crumbs->addTextCrumb(pht('Transforms'));
|
||||
|
||||
$dst_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('File Sources'))
|
||||
->appendChild($dst_table);
|
||||
|
||||
$src_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Available Transforms'))
|
||||
->appendChild($src_table);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$dst_box,
|
||||
$src_box,
|
||||
),
|
||||
array(
|
||||
'title' => array(
|
||||
pht('%s %s', $monogram, $file->getName()),
|
||||
pht('Tranforms'),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -760,6 +760,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return (string) $uri;
|
||||
}
|
||||
|
||||
public function getURIForTransform(PhabricatorFileTransform $transform) {
|
||||
return $this->getTransformedURI($transform->getTransformKey());
|
||||
}
|
||||
|
||||
private function getTransformedURI($transform) {
|
||||
$parts = array();
|
||||
$parts[] = 'file';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorFileImageTransform extends PhabricatorFileTransform {
|
||||
|
||||
public function canApplyTransform(PhabricatorFile $file) {
|
||||
if (!$file->isViewableImage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$file->isTransformableImage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileThumbnailTransform
|
||||
extends PhabricatorFileImageTransform {
|
||||
|
||||
const TRANSFORM_PROFILE = 'profile';
|
||||
const TRANSFORM_PINBOARD = 'pinboard';
|
||||
const TRANSFORM_THUMBGRID = 'thumbgrid';
|
||||
const TRANSFORM_PREVIEW = 'preview';
|
||||
|
||||
private $name;
|
||||
private $key;
|
||||
private $dstX;
|
||||
private $dstY;
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDimensions($x, $y) {
|
||||
$this->dstX = $x;
|
||||
$this->dstY = $y;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTransformName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getTransformKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function generateTransforms() {
|
||||
return array(
|
||||
id(new PhabricatorFileThumbnailTransform())
|
||||
->setName(pht("Profile (100px \xC3\x97 100px)"))
|
||||
->setKey(self::TRANSFORM_PROFILE)
|
||||
->setDimensions(100, 100),
|
||||
id(new PhabricatorFileThumbnailTransform())
|
||||
->setName(pht("Pinboard (280px \xC3\x97 210px)"))
|
||||
->setKey(self::TRANSFORM_PINBOARD)
|
||||
->setDimensions(280, 210),
|
||||
id(new PhabricatorFileThumbnailTransform())
|
||||
->setName(pht('Thumbgrid (100px)'))
|
||||
->setKey(self::TRANSFORM_THUMBGRID)
|
||||
->setDimensions(100, null),
|
||||
id(new PhabricatorFileThumbnailTransform())
|
||||
->setName(pht('Preview (220px)'))
|
||||
->setKey(self::TRANSFORM_PREVIEW)
|
||||
->setDimensions(220, null),
|
||||
);
|
||||
}
|
||||
|
||||
public function applyTransform(PhabricatorFile $file) {
|
||||
$x = $this->dstX;
|
||||
$y = $this->dstY;
|
||||
|
||||
$xformer = new PhabricatorImageTransformer();
|
||||
|
||||
if ($y === null) {
|
||||
return $xformer->executePreviewTransform($file, $x);
|
||||
} else {
|
||||
return $xformer->executeThumbTransform($file, $x, $y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorFileTransform extends Phobject {
|
||||
|
||||
abstract public function getTransformName();
|
||||
abstract public function getTransformKey();
|
||||
abstract public function canApplyTransform(PhabricatorFile $file);
|
||||
abstract public function applyTransform(PhabricatorFile $file);
|
||||
|
||||
public function generateTransforms() {
|
||||
return array($this);
|
||||
}
|
||||
|
||||
public static function getAllTransforms() {
|
||||
static $map;
|
||||
|
||||
if ($map === null) {
|
||||
$xforms = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->loadObjects();
|
||||
|
||||
$result = array();
|
||||
foreach ($xforms as $xform_template) {
|
||||
foreach ($xform_template->generateTransforms() as $xform) {
|
||||
$key = $xform->getTransformKey();
|
||||
if (isset($result[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Two %s objects define the same transform key ("%s"), but '.
|
||||
'each transform must have a unique key.',
|
||||
__CLASS__,
|
||||
$key));
|
||||
}
|
||||
$result[$key] = $xform;
|
||||
}
|
||||
}
|
||||
|
||||
$map = $result;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue