1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 00:42:41 +01:00

Basic image thumbnailing

Summary:
This is still very rough but provides basic support for generating image
thumbnails. I need to separate stuff out a bit but I'm going to integrate into
Maniphest before I hit the profile stuff so this seems like a reasonable
starting point.

Test Plan:
Generated some image thumbnails in various sizes.

Reviewed By: aran
Reviewers: jungejason, tuomaspelkonen, aran
CC: aran
Differential Revision: 333
This commit is contained in:
epriestley 2011-05-22 14:40:51 -07:00
parent dbedb012eb
commit 8af5bb117d
10 changed files with 242 additions and 1 deletions

View file

@ -0,0 +1,10 @@
CREATE TABLE phabricator_file.file_transformedfile (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
originalPHID varchar(64) BINARY NOT NULL,
transform varchar(255) BINARY NOT NULL,
unique key (originalPHID, transform),
transformedPHID varchar(64) BINARY NOT NULL,
key (transformedPHID),
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL
);

View file

@ -321,6 +321,7 @@ phutil_register_library_map(array(
'PhabricatorFileProxyController' => 'applications/files/controller/proxy',
'PhabricatorFileProxyImage' => 'applications/files/storage/proxyimage',
'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
'PhabricatorFileTransformController' => 'applications/files/controller/transform',
'PhabricatorFileURI' => 'applications/files/uri',
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
'PhabricatorFileViewController' => 'applications/files/controller/view',
@ -465,6 +466,7 @@ phutil_register_library_map(array(
'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event',
'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/eventdata',
'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator',
'PhabricatorTransformedFile' => 'applications/files/storage/transformed',
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
'PhabricatorUIExample' => 'applications/uiexample/examples/base',
@ -760,6 +762,7 @@ phutil_register_library_map(array(
'PhabricatorFileProxyController' => 'PhabricatorFileController',
'PhabricatorFileProxyImage' => 'PhabricatorFileDAO',
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
'PhabricatorFileTransformController' => 'PhabricatorFileController',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileViewController' => 'PhabricatorFileController',
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
@ -883,6 +886,7 @@ phutil_register_library_map(array(
'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorUIExampleController' => 'PhabricatorController',

View file

@ -62,6 +62,8 @@ class AphrontDefaultApplicationConfiguration
'delete/(?P<id>\d+)/$' => 'PhabricatorFileMacroDeleteController',
),
'proxy/$' => 'PhabricatorFileProxyController',
'xform/(?P<transform>[^/]+)/(?P<phid>[^/]+)/'
=> 'PhabricatorFileTransformController',
),
'/phid/' => array(
'$' => 'PhabricatorPHIDLookupController',

View file

@ -0,0 +1,131 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileTransformController extends PhabricatorFileController {
private $transform;
private $phid;
public function willProcessRequest(array $data) {
$this->transform = $data['transform'];
$this->phid = $data['phid'];
}
public function processRequest() {
$xform = id(new PhabricatorTransformedFile())
->loadOneWhere(
'originalPHID = %s AND transform = %s',
$this->phid,
$this->transform);
if ($xform) {
return $this->buildTransformedFileResponse($xform);
}
$file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $this->phid);
if (!$file) {
return new Aphront404Response();
}
if (!$file->isViewableInBrowser()) {
return new Aphront400Response();
}
if (!$file->isTransformableImage()) {
return new Aphront400Response();
}
switch ($this->transform) {
case 'thumb-160x120':
$xformed_file = $this->executeThumbTransform($file, 160, 120);
break;
case 'thumb-60x45':
$xformed_file = $this->executeThumbTransform($file, 60, 45);
break;
case 'profile-50x50':
$xformed_file = $this->executeProfile50x50Transform($file);
break;
default:
return new Aphront400Response();
}
if (!$xformed_file) {
return new Aphront400Response();
}
$xform = new PhabricatorTransformedFile();
$xform->setOriginalPHID($this->phid);
$xform->setTransform($this->transform);
$xform->setTransformedPHID($xformed_file->getPHID());
$xform->save();
return $this->buildTransformedFileResponse($xform);
}
private function buildTransformedFileResponse(
PhabricatorTransformedFile $xform) {
// TODO: We could just delegate to the file view controller instead,
// which would save the client a roundtrip, but is slightly more complex.
return id(new AphrontRedirectResponse())->setURI(
PhabricatorFileURI::getViewURIForPHID($xform->getTransformedPHID()));
}
private function executeProfile50x50Transform(PhabricatorFile $file) {
$data = $file->loadFileData();
$jpeg = $this->crudelyScaleTo($data, 50, 50);
return PhabricatorFile::newFromFileData($jpeg, array(
'name' => 'profile-'.$file->getName(),
));
}
private function executeThumbTransform(PhabricatorFile $file, $x, $y) {
$data = $file->loadFileData();
$jpeg = $this->crudelyScaleTo($data, $x, $y);
return PhabricatorFile::newFromFileData($jpeg, array(
'name' => 'thumb-'.$file->getName(),
));
}
/**
* Very crudely scale an image up or down to an exact size.
*/
private function crudelyScaleTo($data, $dx, $dy) {
$src = imagecreatefromstring($data);
$x = imagesx($src);
$y = imagesy($src);
$scale = min($x / $dx, $y / $dy);
$dst = imagecreatetruecolor($dx, $dy);
imagecopyresampled(
$dst,
$src,
0, 0,
0, 0,
$dx, $dy,
$scale * $dx, $scale * $dy);
ob_start();
imagejpeg($dst);
return ob_get_clean();
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/400');
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/storage/transformed');
phutil_require_module('phabricator', 'applications/files/uri');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFileTransformController.php');

View file

@ -129,8 +129,37 @@ class PhabricatorFileViewController extends PhabricatorFileController {
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$transformations = id(new PhabricatorTransformedFile())->loadAllWhere(
'originalPHID = %s',
$file->getPHID());
$rows = array();
foreach ($transformations as $transformed) {
$phid = $transformed->getTransformedPHID();
$rows[] = array(
phutil_escape_html($transformed->getTransform()),
phutil_render_tag(
'a',
array(
'href' => PhabricatorFileURI::getViewURIForPHID($phid),
),
$phid));
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Transform',
'File',
));
$xform_panel = new AphrontPanelView();
$xform_panel->appendChild($table);
$xform_panel->setWidth(AphrontPanelView::WIDTH_FORM);
$xform_panel->setHeader('Transformations');
return $this->buildStandardPageResponse(
array($panel),
array($panel, $xform_panel),
array(
'title' => 'File Info - '.$file->getName(),
));

View file

@ -11,11 +11,15 @@ phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/file');
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/storage/transformed');
phutil_require_module('phabricator', 'applications/files/uri');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -206,6 +206,10 @@ class PhabricatorFile extends PhabricatorFileDAO {
return ($this->getViewableMimeType() !== null);
}
public function isTransformableImage() {
return preg_match('@^image/(gif|png|jpe?g)@', $this->getViewableMimeType());
}
public function getViewableMimeType() {
$mime_map = PhabricatorEnv::getEnvConfig('files.viewable-mime-types');

View file

@ -0,0 +1,25 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorTransformedFile extends PhabricatorFileDAO {
protected $originalPHID;
protected $transform;
protected $transformedPHID;
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/files/storage/base');
phutil_require_source('PhabricatorTransformedFile.php');