@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Videos to Remarkup

Summary: Ref T6916. Added video to remarkup using D7156 as reference.

Test Plan:
- Viewed video files (MP4, Ogg) in Safari, Chrome, Firefox (some don't work, e.g., OGG in Safari, but nothing we can really do about that).
- Used `alt`.
- Used `autoplay`.
- Used `loop`.
- Used `media=audio`.
- Viewed file detail page.

Reviewers: nateguchi2, chad, #blessed_reviewers

Reviewed By: chad, #blessed_reviewers

Subscribers: asherkin, ivo, joshuaspence, Korvin, epriestley

Tags: #remarkup

Maniphest Tasks: T6916

Differential Revision: https://secure.phabricator.com/D11297

+172 -29
+38 -5
src/applications/files/config/PhabricatorFilesConfigOptions.php
··· 34 34 'image/x-icon' => 'image/x-icon', 35 35 'image/vnd.microsoft.icon' => 'image/x-icon', 36 36 37 - 'audio/x-wav' => 'audio/x-wav', 37 + // This is a generic type for both OGG video and OGG audio. 38 38 'application/ogg' => 'application/ogg', 39 - 'audio/mpeg' => 'audio/mpeg', 39 + 40 + 'audio/x-wav' => 'audio/x-wav', 41 + 'audio/mpeg' => 'audio/mpeg', 42 + 'audio/ogg' => 'audio/ogg', 43 + 44 + 'video/mp4' => 'video/mp4', 45 + 'video/ogg' => 'video/ogg', 46 + 'video/webm' => 'video/webm', 40 47 ); 41 48 42 49 $image_default = array( ··· 49 56 'image/vnd.microsoft.icon' => true, 50 57 ); 51 58 59 + 60 + // The "application/ogg" type is listed as both an audio and video type, 61 + // because it may contain either type of content. 62 + 52 63 $audio_default = array( 53 - 'audio/x-wav' => true, 64 + 'audio/x-wav' => true, 65 + 'audio/mpeg' => true, 66 + 'audio/ogg' => true, 67 + 68 + // These are video or ambiguous types, but can be forced to render as 69 + // audio with `media=audio`, which seems to work properly in browsers. 70 + // (For example, you can embed a music video as audio if you just want 71 + // to set the mood for your task without distracting viewers.) 72 + 'video/mp4' => true, 73 + 'video/ogg' => true, 54 74 'application/ogg' => true, 55 - 'audio/mpeg' => true, 75 + ); 76 + 77 + $video_default = array( 78 + 'video/mp4' => true, 79 + 'video/ogg' => true, 80 + 'video/webm' => true, 81 + 'application/ogg' => true, 56 82 ); 57 83 58 84 // largely lifted from http://en.wikipedia.org/wiki/Internet_media_type ··· 70 96 // movie file icon 71 97 'video/mpeg' => 'fa-file-movie-o', 72 98 'video/mp4' => 'fa-file-movie-o', 99 + 'application/ogg' => 'fa-file-movie-o', 73 100 'video/ogg' => 'fa-file-movie-o', 74 101 'video/quicktime' => 'fa-file-movie-o', 75 102 'video/webm' => 'fa-file-movie-o', ··· 122 149 ->setSummary(pht('Configure which MIME types are audio.')) 123 150 ->setDescription( 124 151 pht( 125 - 'List of MIME types which can be used to render an `%s` tag.', 152 + 'List of MIME types which can be rendered with an `%s` tag.', 126 153 '<audio />')), 154 + $this->newOption('files.video-mime-types', 'set', $video_default) 155 + ->setSummary(pht('Configure which MIME types are video.')) 156 + ->setDescription( 157 + pht( 158 + 'List of MIME types which can be rendered with a `%s` tag.', 159 + '<video />')), 127 160 $this->newOption('files.icon-mime-types', 'wild', $icon_default) 128 161 ->setLocked(true) 129 162 ->setSummary(pht('Configure which MIME types map to which icons.'))
+40 -12
src/applications/files/controller/PhabricatorFileInfoController.php
··· 230 230 $cache_string = pht('Not Applicable'); 231 231 } 232 232 233 - $finfo->addProperty(pht('Viewable Image'), $image_string); 234 - $finfo->addProperty(pht('Cacheable'), $cache_string); 233 + $types = array(); 234 + if ($file->isViewableImage()) { 235 + $types[] = pht('Image'); 236 + } 235 237 236 - $builtin = $file->getBuiltinName(); 237 - if ($builtin === null) { 238 - $builtin_string = pht('No'); 239 - } else { 240 - $builtin_string = $builtin; 238 + if ($file->isVideo()) { 239 + $types[] = pht('Video'); 241 240 } 242 241 243 - $finfo->addProperty(pht('Builtin'), $builtin_string); 242 + if ($file->isAudio()) { 243 + $types[] = pht('Audio'); 244 + } 244 245 245 - $is_profile = $file->getIsProfileImage() 246 - ? pht('Yes') 247 - : pht('No'); 246 + if ($file->getCanCDN()) { 247 + $types[] = pht('Can CDN'); 248 + } 248 249 249 - $finfo->addProperty(pht('Profile'), $is_profile); 250 + $builtin = $file->getBuiltinName(); 251 + if ($builtin !== null) { 252 + $types[] = pht('Builtin ("%s")', $builtin); 253 + } 254 + 255 + if ($file->getIsProfileImage()) { 256 + $types[] = pht('Profile'); 257 + } 258 + 259 + $types = implode(', ', $types); 260 + $finfo->addProperty(pht('Attributes'), $types); 250 261 251 262 $storage_properties = new PHUIPropertyListView(); 252 263 $box->addPropertyList($storage_properties, pht('Storage')); ··· 291 302 292 303 $media = id(new PHUIPropertyListView()) 293 304 ->addImageContent($linked_image); 305 + 306 + $box->addPropertyList($media); 307 + } else if ($file->isVideo()) { 308 + $video = phutil_tag( 309 + 'video', 310 + array( 311 + 'controls' => 'controls', 312 + 'class' => 'phui-property-list-video', 313 + ), 314 + phutil_tag( 315 + 'source', 316 + array( 317 + 'src' => $file->getViewURI(), 318 + 'type' => $file->getMimeType(), 319 + ))); 320 + $media = id(new PHUIPropertyListView()) 321 + ->addImageContent($video); 294 322 295 323 $box->addPropertyList($media); 296 324 } else if ($file->isAudio()) {
+51 -8
src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
··· 43 43 44 44 $is_viewable_image = $object->isViewableImage(); 45 45 $is_audio = $object->isAudio(); 46 + $is_video = $object->isVideo(); 46 47 $force_link = ($options['layout'] == 'link'); 47 48 48 - $options['viewable'] = ($is_viewable_image || $is_audio); 49 + // If a file is both audio and video, as with "application/ogg" by default, 50 + // render it as video but allow the user to specify `media=audio` if they 51 + // want to force it to render as audio. 52 + if ($is_audio && $is_video) { 53 + $media = $options['media']; 54 + if ($media == 'audio') { 55 + $is_video = false; 56 + } else { 57 + $is_audio = false; 58 + } 59 + } 60 + 61 + $options['viewable'] = ($is_viewable_image || $is_audio || $is_video); 49 62 50 63 if ($is_viewable_image && !$force_link) { 51 64 return $this->renderImageFile($object, $handle, $options); 65 + } else if ($is_video && !$force_link) { 66 + return $this->renderVideoFile($object, $handle, $options); 52 67 } else if ($is_audio && !$force_link) { 53 68 return $this->renderAudioFile($object, $handle, $options); 54 69 } else { ··· 58 73 59 74 private function getFileOptions($option_string) { 60 75 $options = array( 61 - 'size' => null, 62 - 'layout' => 'left', 63 - 'float' => false, 64 - 'width' => null, 65 - 'height' => null, 76 + 'size' => null, 77 + 'layout' => 'left', 78 + 'float' => false, 79 + 'width' => null, 80 + 'height' => null, 66 81 'alt' => null, 82 + 'media' => null, 83 + 'autoplay' => null, 84 + 'loop' => null, 67 85 ); 68 86 69 87 if ($option_string) { ··· 201 219 PhabricatorFile $file, 202 220 PhabricatorObjectHandle $handle, 203 221 array $options) { 222 + return $this->renderMediaFile('audio', $file, $handle, $options); 223 + } 224 + 225 + private function renderVideoFile( 226 + PhabricatorFile $file, 227 + PhabricatorObjectHandle $handle, 228 + array $options) { 229 + return $this->renderMediaFile('video', $file, $handle, $options); 230 + } 231 + 232 + private function renderMediaFile( 233 + $tag, 234 + PhabricatorFile $file, 235 + PhabricatorObjectHandle $handle, 236 + array $options) { 237 + 238 + $is_video = ($tag == 'video'); 204 239 205 240 if (idx($options, 'autoplay')) { 206 241 $preload = 'auto'; 207 242 $autoplay = 'autoplay'; 208 243 } else { 209 - $preload = 'none'; 244 + // If we don't preload video, the user can't see the first frame and 245 + // has no clue what they're looking at, so always preload. 246 + if ($is_video) { 247 + $preload = 'auto'; 248 + } else { 249 + $preload = 'none'; 250 + } 210 251 $autoplay = null; 211 252 } 212 253 213 254 return $this->newTag( 214 - 'audio', 255 + $tag, 215 256 array( 216 257 'controls' => 'controls', 217 258 'preload' => $preload, 218 259 'autoplay' => $autoplay, 219 260 'loop' => idx($options, 'loop') ? 'loop' : null, 261 + 'alt' => $options['alt'], 262 + 'class' => 'phabricator-media', 220 263 ), 221 264 $this->newTag( 222 265 'source',
+10
src/applications/files/storage/PhabricatorFile.php
··· 802 802 return idx($mime_map, $mime_type); 803 803 } 804 804 805 + public function isVideo() { 806 + if (!$this->isViewableInBrowser()) { 807 + return false; 808 + } 809 + 810 + $mime_map = PhabricatorEnv::getEnvConfig('files.video-mime-types'); 811 + $mime_type = $this->getMimeType(); 812 + return idx($mime_map, $mime_type); 813 + } 814 + 805 815 public function isTransformableImage() { 806 816 // NOTE: The way the 'gd' extension works in PHP is that you can install it 807 817 // with support for only some file types, so it might be able to handle
+15 -4
src/docs/user/userguide/remarkup.diviner
··· 410 410 411 411 {F123, layout=left, float, size=full, alt="a duckling"} 412 412 413 - Valid options are: 413 + Valid options for all files are: 414 414 415 415 - **layout** left (default), center, right, inline, link (render a link 416 416 instead of a thumbnail for images) 417 + - **name** with `layout=link` or for non-images, use this name for the link 418 + text 419 + - **alt** Provide alternate text for assistive technologies. 420 + 421 + Image files support these options: 422 + 417 423 - **float** If layout is set to left or right, the image will be floated so 418 424 text wraps around it. 419 425 - **size** thumb (default), full 420 - - **name** with `layout=link` or for non-images, use this name for the link 421 - text 422 426 - **width** Scale image to a specific width. 423 427 - **height** Scale image to a specific height. 424 - - **alt** Provide alternate text for assistive technologies. 428 + 429 + Audio and video files support these options: 430 + 431 + - **media**: Specify the media type as `audio` or `video`. This allows you 432 + to disambiguate how file format which may contain either audio or video 433 + should be rendered. 434 + - **loop**: Loop this media. 435 + - **autoplay**: Automatically begin playing this media. 425 436 426 437 == Embedding Countdowns 427 438
+11
webroot/rsrc/css/core/remarkup.css
··· 218 218 width: 50%; 219 219 } 220 220 221 + video.phabricator-media { 222 + background: {$greybackground}; 223 + } 224 + 225 + .phabricator-remarkup video { 226 + display: block; 227 + margin: 0 auto; 228 + min-width: 240px; 229 + width: 90%; 230 + } 231 + 221 232 .phabricator-remarkup-mention-exists { 222 233 font-weight: bold; 223 234 background: #e6f3ff;
+7
webroot/rsrc/css/phui/phui-property-list-view.css
··· 160 160 min-width: 240px; 161 161 } 162 162 163 + .phui-property-list-video { 164 + display: block; 165 + margin: 0 auto; 166 + width: 90%; 167 + min-width: 240px; 168 + } 169 + 163 170 /* When tags appear in property lists, give them a little more vertical 164 171 spacing. */ 165 172 .phui-property-list-view .phui-tag-view {