1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 21:40:55 +01:00

Provide default image alt text in more contexts and support custom alt text

Summary:
Ref T13629.

  - Allow files to have custom alt text.
  - If a file doesn't have alt text, try to generate a plausible default alt text with the information we have.

Test Plan:
  - Viewed image files in DocumentEngine diffs, files, `{Fxxx}` embeds, and lightboxes.
  - Saw default alt text in all cases, or custom alt text if provided.
  - Set, modified, and removed file alt text. Viewed timeline and feed.
  - Pulled alt text with "conduit.search".

Maniphest Tasks: T13629

Differential Revision: https://secure.phabricator.com/D21596
This commit is contained in:
epriestley 2021-03-04 16:40:19 -08:00
parent fceb9a3f9e
commit bfe7cdc5a2
9 changed files with 211 additions and 21 deletions

View file

@ -10,7 +10,7 @@ return array(
'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.css' => '0e3cf785',
'conpherence.pkg.js' => '020aebcf', 'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '0ae696de', 'core.pkg.css' => '0ae696de',
'core.pkg.js' => '079198f6', 'core.pkg.js' => 'ab3502fe',
'dark-console.pkg.js' => '187792c2', 'dark-console.pkg.js' => '187792c2',
'differential.pkg.css' => '5c459f92', 'differential.pkg.css' => '5c459f92',
'differential.pkg.js' => '5080baf4', 'differential.pkg.js' => '5080baf4',
@ -489,7 +489,7 @@ return array(
'rsrc/js/core/behavior-hovercard.js' => '183738e6', 'rsrc/js/core/behavior-hovercard.js' => '183738e6',
'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731', 'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731',
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '42c44e8b', 'rsrc/js/core/behavior-keyboard-shortcuts.js' => '42c44e8b',
'rsrc/js/core/behavior-lightbox-attachments.js' => 'c7e748bf', 'rsrc/js/core/behavior-lightbox-attachments.js' => '14c7ab36',
'rsrc/js/core/behavior-line-linker.js' => '0d915ff5', 'rsrc/js/core/behavior-line-linker.js' => '0d915ff5',
'rsrc/js/core/behavior-linked-container.js' => '74446546', 'rsrc/js/core/behavior-linked-container.js' => '74446546',
'rsrc/js/core/behavior-more.js' => '506aa3f4', 'rsrc/js/core/behavior-more.js' => '506aa3f4',
@ -642,7 +642,7 @@ return array(
'javelin-behavior-history-install' => '6a1583a8', 'javelin-behavior-history-install' => '6a1583a8',
'javelin-behavior-icon-composer' => '38a6cedb', 'javelin-behavior-icon-composer' => '38a6cedb',
'javelin-behavior-launch-icon-composer' => 'a17b84f1', 'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf', 'javelin-behavior-lightbox-attachments' => '14c7ab36',
'javelin-behavior-line-chart' => 'ad258e28', 'javelin-behavior-line-chart' => 'ad258e28',
'javelin-behavior-linked-container' => '74446546', 'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => '139ef688', 'javelin-behavior-maniphest-batch-selector' => '139ef688',
@ -1039,6 +1039,15 @@ return array(
'javelin-stratcom', 'javelin-stratcom',
'javelin-util', 'javelin-util',
), ),
'14c7ab36' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-mask',
'javelin-util',
'phuix-icon-view',
'phabricator-busy',
),
'183738e6' => array( '183738e6' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-behavior-device', 'javelin-behavior-device',
@ -2091,15 +2100,6 @@ return array(
'javelin-workflow', 'javelin-workflow',
'javelin-json', 'javelin-json',
), ),
'c7e748bf' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-mask',
'javelin-util',
'phuix-icon-view',
'phabricator-busy',
),
'cc2c5de5' => array( 'cc2c5de5' => array(
'javelin-install', 'javelin-install',
'phuix-button-view', 'phuix-button-view',

View file

@ -3428,6 +3428,7 @@ phutil_register_library_map(array(
'PhabricatorFerretSearchEngineExtension' => 'applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php', 'PhabricatorFerretSearchEngineExtension' => 'applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php',
'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php', 'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php',
'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php',
'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php',
'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php',
'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php', 'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php',
@ -9956,6 +9957,7 @@ phutil_register_library_map(array(
'PhabricatorNgramsInterface', 'PhabricatorNgramsInterface',
), ),
'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat', 'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat',
'PhabricatorFileAltTextTransaction' => 'PhabricatorFileTransactionType',
'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileBundleLoader' => 'Phobject',
'PhabricatorFileChunk' => array( 'PhabricatorFileChunk' => array(
'PhabricatorFileDAO', 'PhabricatorFileDAO',

View file

@ -310,6 +310,15 @@ final class PhabricatorFileViewController extends PhabricatorFileController {
pht('Storage Handle'), pht('Storage Handle'),
$file->getStorageHandle()); $file->getStorageHandle());
$custom_alt = $file->getCustomAltText();
if (strlen($custom_alt)) {
$finfo->addProperty(pht('Custom Alt Text'), $custom_alt);
}
$default_alt = $file->getDefaultAltText();
if (strlen($default_alt)) {
$finfo->addProperty(pht('Default Alt Text'), $default_alt);
}
$phids = $file->getObjectPHIDs(); $phids = $file->getObjectPHIDs();
if ($phids) { if ($phids) {

View file

@ -86,6 +86,7 @@ final class PhabricatorImageDocumentEngine
phutil_tag( phutil_tag(
'img', 'img',
array( array(
'alt' => $file->getAltText(),
'src' => $file->getBestURI(), 'src' => $file->getBestURI(),
))); )));
@ -129,6 +130,7 @@ final class PhabricatorImageDocumentEngine
$image = phutil_tag( $image = phutil_tag(
'img', 'img',
array( array(
'alt' => $file->getAltText(),
'src' => $source_uri, 'src' => $source_uri,
)); ));

View file

@ -75,6 +75,14 @@ final class PhabricatorFileEditEngine
->setConduitDescription(pht('Rename the file.')) ->setConduitDescription(pht('Rename the file.'))
->setConduitTypeDescription(pht('New file name.')) ->setConduitTypeDescription(pht('New file name.'))
->setValue($object->getName()), ->setValue($object->getName()),
id(new PhabricatorTextEditField())
->setKey('alt')
->setLabel(pht('Alt Text'))
->setTransactionType(PhabricatorFileAltTextTransaction::TRANSACTIONTYPE)
->setDescription(pht('Human-readable file description.'))
->setConduitDescription(pht('Set the file alt text.'))
->setConduitTypeDescription(pht('New alt text.'))
->setValue($object->getCustomAltText()),
); );
} }

View file

@ -161,10 +161,17 @@ final class PhabricatorEmbedFileRemarkupRule
} }
} }
$alt = null;
if (isset($options['alt'])) { if (isset($options['alt'])) {
$attrs['alt'] = $options['alt']; $alt = $options['alt'];
} }
if (!strlen($alt)) {
$alt = $file->getAltText();
}
$attrs['alt'] = $alt;
$img = phutil_tag('img', $attrs); $img = phutil_tag('img', $attrs);
$embed = javelin_tag( $embed = javelin_tag(
@ -177,6 +184,7 @@ final class PhabricatorEmbedFileRemarkupRule
'phid' => $file->getPHID(), 'phid' => $file->getPHID(),
'uri' => $file->getBestURI(), 'uri' => $file->getBestURI(),
'dUri' => $file->getDownloadURI(), 'dUri' => $file->getDownloadURI(),
'alt' => $alt,
'viewable' => true, 'viewable' => true,
'monogram' => $file->getMonogram(), 'monogram' => $file->getMonogram(),
), ),

View file

@ -41,6 +41,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
const METADATA_STORAGE = 'storage'; const METADATA_STORAGE = 'storage';
const METADATA_INTEGRITY = 'integrity'; const METADATA_INTEGRITY = 'integrity';
const METADATA_CHUNK = 'chunk'; const METADATA_CHUNK = 'chunk';
const METADATA_ALT_TEXT = 'alt';
const STATUS_ACTIVE = 'active'; const STATUS_ACTIVE = 'active';
const STATUS_DELETED = 'deleted'; const STATUS_DELETED = 'deleted';
@ -1274,6 +1275,72 @@ final class PhabricatorFile extends PhabricatorFileDAO
return idx($this->metadata, self::METADATA_IMAGE_WIDTH); return idx($this->metadata, self::METADATA_IMAGE_WIDTH);
} }
public function getAltText() {
$alt = $this->getCustomAltText();
if (strlen($alt)) {
return $alt;
}
return $this->getDefaultAltText();
}
public function getCustomAltText() {
return idx($this->metadata, self::METADATA_ALT_TEXT);
}
public function setCustomAltText($value) {
$value = phutil_string_cast($value);
if (!strlen($value)) {
$value = null;
}
if ($value === null) {
unset($this->metadata[self::METADATA_ALT_TEXT]);
} else {
$this->metadata[self::METADATA_ALT_TEXT] = $value;
}
return $this;
}
public function getDefaultAltText() {
$parts = array();
$name = $this->getName();
if (strlen($name)) {
$parts[] = $name;
}
$stats = array();
$image_x = $this->getImageHeight();
$image_y = $this->getImageWidth();
if ($image_x && $image_y) {
$stats[] = pht(
"%d\xC3\x97%d px",
new PhutilNumber($image_x),
new PhutilNumber($image_y));
}
$bytes = $this->getByteSize();
if ($bytes) {
$stats[] = phutil_format_bytes($bytes);
}
if ($stats) {
$parts[] = pht('(%s)', implode(', ', $stats));
}
if (!$parts) {
return null;
}
return implode(' ', $parts);
}
public function getCanCDN() { public function getCanCDN() {
if (!$this->isViewableImage()) { if (!$this->isViewableImage()) {
return false; return false;
@ -1676,6 +1743,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
'uri' => PhabricatorEnv::getURI($this->getURI()), 'uri' => PhabricatorEnv::getURI($this->getURI()),
'dataURI' => $this->getCDNURI('data'), 'dataURI' => $this->getCDNURI('data'),
'size' => (int)$this->getByteSize(), 'size' => (int)$this->getByteSize(),
'alt' => array(
'custom' => $this->getCustomAltText(),
'default' => $this->getDefaultAltText(),
),
); );
} }

View file

@ -0,0 +1,94 @@
<?php
final class PhabricatorFileAltTextTransaction
extends PhabricatorFileTransactionType {
const TRANSACTIONTYPE = 'file:alt';
public function generateOldValue($object) {
return $object->getCustomAltText();
}
public function generateNewValue($object, $value) {
$value = phutil_string_cast($value);
if (!strlen($value)) {
$value = null;
}
return $value;
}
public function applyInternalEffects($object, $value) {
$object->setCustomAltText($value);
}
public function getTitle() {
$old_value = $this->getOldValue();
$new_value = $this->getNewValue();
if (!strlen($old_value)) {
return pht(
'%s set the alternate text for this file to %s.',
$this->renderAuthor(),
$this->renderNewValue());
} else if (!strlen($new_value)) {
return pht(
'%s removed the alternate text for this file (was %s).',
$this->renderAuthor(),
$this->renderOldValue());
} else {
return pht(
'%s changed the alternate text for this file from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
}
public function getTitleForFeed() {
$old_value = $this->getOldValue();
$new_value = $this->getNewValue();
if (!strlen($old_value)) {
return pht(
'%s set the alternate text for %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderNewValue());
} else if (!strlen($new_value)) {
return pht(
'%s removed the alternate text for %s (was %s).',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue());
} else {
return pht(
'%s changed the alternate text for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue(),
$this->renderNewValue());
}
}
public function validateTransactions($object, array $xactions) {
$errors = array();
$max_length = 1024;
foreach ($xactions as $xaction) {
$new_value = $xaction->getNewValue();
$new_length = strlen($new_value);
if ($new_length > $max_length) {
$errors[] = $this->newInvalidError(
pht(
'File alternate text must not be longer than %s character(s).',
new PhutilNumber($max_length)));
}
}
return $errors;
}
}

View file

@ -94,16 +94,12 @@ JX.behavior('lightbox-attachments', function() {
if (target_data.viewable) { if (target_data.viewable) {
img_uri = target_data.uri; img_uri = target_data.uri;
var alt_name = '';
if (typeof target_data.name != 'undefined') {
alt_name = target_data.name;
}
img = img =
JX.$N('img', JX.$N('img',
{ {
className : 'loading', className : 'loading',
alt : alt_name alt : target_data.alt || null
} }
); );
} else { } else {