@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.

at recaptime-dev/main 171 lines 4.6 kB view raw
1<?php 2 3final class PhabricatorImageRemarkupRule extends PhutilRemarkupRule { 4 5 const KEY_RULE_EXTERNAL_IMAGE = 'rule.external-image'; 6 7 public function getPriority() { 8 return 200.0; 9 } 10 11 public function apply($text) { 12 return preg_replace_callback( 13 '@{(image|img) ((?:[^}\\\\]+|\\\\.)*)}@m', 14 array($this, 'markupImage'), 15 $text); 16 } 17 18 public function markupImage(array $matches) { 19 if (!$this->isFlatText($matches[0])) { 20 return $matches[0]; 21 } 22 23 $args = array(); 24 $defaults = array( 25 'uri' => null, 26 'alt' => null, 27 'width' => null, 28 'height' => null, 29 ); 30 31 $trimmed_match = trim($matches[2]); 32 if ($this->isURI($trimmed_match)) { 33 $args['uri'] = $trimmed_match; 34 } else { 35 $parser = new PhutilSimpleOptions(); 36 $keys = $parser->parse($trimmed_match); 37 38 $uri_key = ''; 39 foreach (array('src', 'uri', 'url') as $key) { 40 if (array_key_exists($key, $keys)) { 41 $uri_key = $key; 42 } 43 } 44 if ($uri_key) { 45 $args['uri'] = $keys[$uri_key]; 46 } 47 $args += $keys; 48 } 49 50 $args += $defaults; 51 52 if (!strlen($args['uri'])) { 53 return $matches[0]; 54 } 55 56 // Make sure this is something that looks roughly like a real URI. We'll 57 // validate it more carefully before proxying it, but if whatever the user 58 // has typed isn't even close, just decline to activate the rule behavior. 59 try { 60 $uri = new PhutilURI($args['uri']); 61 62 if (!strlen($uri->getProtocol())) { 63 return $matches[0]; 64 } 65 66 $args['uri'] = (string)$uri; 67 } catch (Exception $ex) { 68 return $matches[0]; 69 } 70 71 $engine = $this->getEngine(); 72 $metadata_key = self::KEY_RULE_EXTERNAL_IMAGE; 73 $metadata = $engine->getTextMetadata($metadata_key, array()); 74 75 $token = $engine->storeText('<img>'); 76 77 $metadata[] = array( 78 'token' => $token, 79 'args' => $args, 80 ); 81 82 $engine->setTextMetadata($metadata_key, $metadata); 83 84 return $token; 85 } 86 87 public function didMarkupText() { 88 $engine = $this->getEngine(); 89 $metadata_key = self::KEY_RULE_EXTERNAL_IMAGE; 90 $images = $engine->getTextMetadata($metadata_key, array()); 91 $engine->setTextMetadata($metadata_key, array()); 92 93 if (!$images) { 94 return; 95 } 96 97 // Look for images we've already successfully fetched that aren't about 98 // to get eaten by the GC. For any we find, we can just emit a normal 99 // "<img />" tag pointing directly to the file. 100 101 // For files which we don't hit in the cache, we emit a placeholder 102 // instead and use AJAX to actually perform the fetch. 103 104 $digests = array(); 105 foreach ($images as $image) { 106 $uri = $image['args']['uri']; 107 $digests[] = PhabricatorHash::digestForIndex($uri); 108 } 109 110 $caches = id(new PhabricatorFileExternalRequest())->loadAllWhere( 111 'uriIndex IN (%Ls) AND isSuccessful = 1 AND ttl > %d', 112 $digests, 113 PhabricatorTime::getNow() + phutil_units('1 hour in seconds')); 114 115 $file_phids = array(); 116 foreach ($caches as $cache) { 117 $file_phids[$cache->getFilePHID()] = $cache->getURI(); 118 } 119 120 $file_map = array(); 121 if ($file_phids) { 122 $files = id(new PhabricatorFileQuery()) 123 ->setViewer(PhabricatorUser::getOmnipotentUser()) 124 ->withPHIDs(array_keys($file_phids)) 125 ->execute(); 126 foreach ($files as $file) { 127 $phid = $file->getPHID(); 128 129 $file_remote_uri = $file_phids[$phid]; 130 $file_view_uri = $file->getViewURI(); 131 132 $file_map[$file_remote_uri] = $file_view_uri; 133 } 134 } 135 136 foreach ($images as $image) { 137 $args = $image['args']; 138 $uri = $args['uri']; 139 140 $direct_uri = idx($file_map, $uri); 141 if ($direct_uri) { 142 $img = phutil_tag( 143 'img', 144 array( 145 'src' => $direct_uri, 146 'alt' => $args['alt'], 147 'width' => $args['width'], 148 'height' => $args['height'], 149 )); 150 } else { 151 $src_uri = id(new PhutilURI('/file/imageproxy/')) 152 ->replaceQueryParam('uri', $uri); 153 154 $img = id(new PHUIRemarkupImageView()) 155 ->setURI($src_uri) 156 ->setAlt($args['alt']) 157 ->setWidth($args['width']) 158 ->setHeight($args['height']); 159 } 160 161 $engine->overwriteStoredText($image['token'], $img); 162 } 163 } 164 165 private function isURI($uri_string) { 166 // Very simple check to make sure it starts with either http or https. 167 // If it does, we'll try to treat it like a valid URI 168 return preg_match('~^https?\:\/\/.*\z~i', $uri_string); 169 } 170 171}