Skip to content

Commit 712e222

Browse files
omailsonepriestley
authored andcommitted
Store width and height metadata of image files
Summary: Also provide a way to update old files metadata. Test Plan: Create a revision which includes a image file. Check whether the widht, height metadata exists. Run `scripts/files/manage_files.php metadata --all` to update previously uploaded files. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T2101 Differential Revision: https://secure.phabricator.com/D4347
1 parent ae0773b commit 712e222

File tree

7 files changed

+212
-0
lines changed

7 files changed

+212
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ALTER TABLE {$NAMESPACE}_file.file
2+
ADD metadata LONGTEXT COLLATE utf8_bin NOT NULL;
3+
4+
UPDATE {$NAMESPACE}_file.file
5+
SET metadata = '{}' WHERE metadata = '';

scripts/files/manage_files.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
new PhabricatorFilesManagementEnginesWorkflow(),
1919
new PhabricatorFilesManagementMigrateWorkflow(),
2020
new PhutilHelpArgumentWorkflow(),
21+
new PhabricatorFilesManagementMetadataWorkflow(),
2122
);
2223

2324
$args->parseWorkflows($workflows);

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@
830830
'PhabricatorFileUploadController' => 'applications/files/controller/PhabricatorFileUploadController.php',
831831
'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php',
832832
'PhabricatorFilesManagementEnginesWorkflow' => 'applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php',
833+
'PhabricatorFilesManagementMetadataWorkflow' => 'applications/files/management/PhabricatorFilesManagementMetadataWorkflow.php',
833834
'PhabricatorFilesManagementMigrateWorkflow' => 'applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php',
834835
'PhabricatorFilesManagementWorkflow' => 'applications/files/management/PhabricatorFilesManagementWorkflow.php',
835836
'PhabricatorFlag' => 'applications/flag/storage/PhabricatorFlag.php',
@@ -2177,6 +2178,7 @@
21772178
'PhabricatorFileUploadController' => 'PhabricatorFileController',
21782179
'PhabricatorFileUploadException' => 'Exception',
21792180
'PhabricatorFilesManagementEnginesWorkflow' => 'PhabricatorFilesManagementWorkflow',
2181+
'PhabricatorFilesManagementMetadataWorkflow' => 'PhabricatorFilesManagementWorkflow',
21802182
'PhabricatorFilesManagementMigrateWorkflow' => 'PhabricatorFilesManagementWorkflow',
21812183
'PhabricatorFilesManagementWorkflow' => 'PhutilArgumentWorkflow',
21822184
'PhabricatorFlag' => 'PhabricatorFlagDAO',

src/applications/files/controller/PhabricatorFileInfoController.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ private function buildPropertyView(PhabricatorFile $file) {
125125
pht('Handle'),
126126
phutil_escape_html($file->getStorageHandle()));
127127

128+
$metadata = $file->getMetadata();
129+
if (!empty($metadata)) {
130+
$view->addSectionHeader(pht('Metadata'));
131+
132+
foreach ($metadata as $key => $value) {
133+
$view->addProperty(
134+
PhabricatorFile::getMetadataName($key),
135+
phutil_escape_html($value));
136+
}
137+
}
138+
128139
if ($file->isViewableInBrowser()) {
129140

130141
// TODO: Clean this up after Pholio (dark backgrounds, standardization,
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
final class PhabricatorFilesManagementMetadataWorkflow
4+
extends PhabricatorFilesManagementWorkflow {
5+
6+
public function didConstruct() {
7+
$this
8+
->setName('metadata')
9+
->setSynopsis('Update metadata of old files.')
10+
->setArguments(
11+
array(
12+
array(
13+
'name' => 'all',
14+
'help' => 'Update all files.',
15+
),
16+
array(
17+
'name' => 'names',
18+
'wildcard' => true,
19+
'help' => 'Update the given files.',
20+
),
21+
array(
22+
'name' => 'dry-run',
23+
'help' => 'Show what would be updated.',
24+
),
25+
)
26+
);
27+
}
28+
29+
public function execute(PhutilArgumentParser $args) {
30+
$console = PhutilConsole::getConsole();
31+
32+
if ($args->getArg('all')) {
33+
if ($args->getArg('names')) {
34+
throw new PhutilArgumentUsageException(
35+
"Specify either a list of files or `--all`, but not both.");
36+
}
37+
$iterator = new LiskMigrationIterator(new PhabricatorFile());
38+
} else if ($args->getArg('names')) {
39+
$iterator = array();
40+
41+
foreach ($args->getArg('names') as $name) {
42+
$name = trim($name);
43+
44+
$id = preg_replace('/^F/i', '', $name);
45+
if (ctype_digit($id)) {
46+
$file = id(new PhabricatorFile())->loadOneWhere(
47+
'id = %d',
48+
$id);
49+
if (!$file) {
50+
throw new PhutilArgumentUsageException(
51+
"No file exists with id '{$name}'.");
52+
}
53+
} else {
54+
$file = id(new PhabricatorFile())->loadOneWhere(
55+
'phid = %d',
56+
$name);
57+
if (!$file) {
58+
throw new PhutilArgumentUsageException(
59+
"No file exists with PHID '{$name}'.");
60+
}
61+
}
62+
$iterator[] = $file;
63+
}
64+
} else {
65+
throw new PhutilArgumentUsageException(
66+
"Either specify a list of files to update, or use `--all` ".
67+
"to update all files.");
68+
}
69+
70+
$is_dry_run = $args->getArg('dry-run');
71+
72+
$failed = array();
73+
74+
foreach ($iterator as $file) {
75+
$fid = 'F'.$file->getID();
76+
77+
if (!$file->isViewableImage()) {
78+
$console->writeOut(
79+
"%s: Not an image file.\n",
80+
$fid);
81+
continue;
82+
}
83+
84+
$metadata = $file->getMetadata();
85+
$image_width = idx($metadata, PhabricatorFile::METADATA_IMAGE_WIDTH);
86+
$image_height = idx($metadata, PhabricatorFile::METADATA_IMAGE_HEIGHT);
87+
if ($image_width && $image_height) {
88+
$console->writeOut(
89+
"%s: Already updated\n",
90+
$fid);
91+
continue;
92+
}
93+
94+
if ($is_dry_run) {
95+
$console->writeOut(
96+
"%s: Would update file (dry run)\n",
97+
$fid);
98+
continue;
99+
}
100+
101+
$console->writeOut(
102+
"%s: Updating metadata... ",
103+
$fid);
104+
105+
try {
106+
$file->updateDimensions();
107+
$console->writeOut("done.\n");
108+
} catch (Exception $ex) {
109+
$console->writeOut("failed!\n");
110+
$console->writeErr("%s\n", (string)$ex);
111+
$failed[] = $file;
112+
}
113+
}
114+
115+
if ($failed) {
116+
$console->writeOut("**Failures!**\n");
117+
$ids = array();
118+
foreach ($failed as $file) {
119+
$ids[] = 'F'.$file->getID();
120+
}
121+
$console->writeOut("%s\n", implode(', ', $ids));
122+
123+
return 1;
124+
} else {
125+
$console->writeOut("**Success!**\n");
126+
return 0;
127+
}
128+
129+
return 0;
130+
}
131+
}

src/applications/files/storage/PhabricatorFile.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ final class PhabricatorFile extends PhabricatorFileDAO
55

66
const STORAGE_FORMAT_RAW = 'raw';
77

8+
const METADATA_IMAGE_WIDTH = 'width';
9+
const METADATA_IMAGE_HEIGHT = 'height';
10+
811
protected $phid;
912
protected $name;
1013
protected $mimeType;
1114
protected $byteSize;
1215
protected $authorPHID;
1316
protected $secretKey;
1417
protected $contentHash;
18+
protected $metadata = array();
1519

1620
protected $storageEngine;
1721
protected $storageFormat;
@@ -20,6 +24,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
2024
public function getConfiguration() {
2125
return array(
2226
self::CONFIG_AUX_PHID => true,
27+
self::CONFIG_SERIALIZATION => array(
28+
'metadata' => self::SERIALIZATION_JSON,
29+
),
2330
) + parent::getConfiguration();
2431
}
2532

@@ -196,6 +203,12 @@ public static function newFromFileData($data, array $params = array()) {
196203
$file->setMimeType(Filesystem::getMimeType($tmp));
197204
}
198205

206+
try {
207+
$file->updateDimensions(false);
208+
} catch (Exception $ex) {
209+
// Do nothing
210+
}
211+
199212
$file->save();
200213

201214
return $file;
@@ -485,6 +498,51 @@ public function generateSecretKey() {
485498
return Filesystem::readRandomCharacters(20);
486499
}
487500

501+
public function updateDimensions($save = true) {
502+
if (!$this->isViewableImage()) {
503+
throw new Exception(
504+
"This file is not a viewable image.");
505+
}
506+
507+
if (!function_exists("imagecreatefromstring")) {
508+
throw new Exception(
509+
"Cannot retrieve image information.");
510+
}
511+
512+
$data = $this->loadFileData();
513+
514+
$img = imagecreatefromstring($data);
515+
if ($img === false) {
516+
throw new Exception(
517+
"Error when decoding image.");
518+
}
519+
520+
$this->metadata[self::METADATA_IMAGE_WIDTH] = imagesx($img);
521+
$this->metadata[self::METADATA_IMAGE_HEIGHT] = imagesy($img);
522+
523+
if ($save) {
524+
$this->save();
525+
}
526+
527+
return $this;
528+
}
529+
530+
public static function getMetadataName($metadata) {
531+
switch ($metadata) {
532+
case self::METADATA_IMAGE_WIDTH:
533+
$name = pht('Width');
534+
break;
535+
case self::METADATA_IMAGE_HEIGHT:
536+
$name = pht('Height');
537+
break;
538+
default:
539+
$name = ucfirst($metadata);
540+
break;
541+
}
542+
543+
return $name;
544+
}
545+
488546

489547
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
490548

src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,10 @@ public function getPatches() {
10811081
'name' =>
10821082
$this->getPatchPath('20130102.metamtareceivedmailmessageidhash.sql'),
10831083
),
1084+
'20130103.filemetadata.sql' => array(
1085+
'type' => 'sql',
1086+
'name' => $this->getPatchPath('20130103.filemetadata.sql'),
1087+
),
10841088
);
10851089
}
10861090

0 commit comments

Comments
 (0)