Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/files/markup/PhabricatorImageRemarkupRule.php
12241 views
1
<?php
2
3
final 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
$uri_arg = $args['uri'];
53
if ($uri_arg === null || !strlen($uri_arg)) {
54
return $matches[0];
55
}
56
57
// Make sure this is something that looks roughly like a real URI. We'll
58
// validate it more carefully before proxying it, but if whatever the user
59
// has typed isn't even close, just decline to activate the rule behavior.
60
try {
61
$uri = new PhutilURI($uri_arg);
62
63
if ($uri->getProtocol() === null || !strlen($uri->getProtocol())) {
64
return $matches[0];
65
}
66
67
$args['uri'] = (string)$uri;
68
} catch (Exception $ex) {
69
return $matches[0];
70
}
71
72
$engine = $this->getEngine();
73
$metadata_key = self::KEY_RULE_EXTERNAL_IMAGE;
74
$metadata = $engine->getTextMetadata($metadata_key, array());
75
76
$token = $engine->storeText('<img>');
77
78
$metadata[] = array(
79
'token' => $token,
80
'args' => $args,
81
);
82
83
$engine->setTextMetadata($metadata_key, $metadata);
84
85
return $token;
86
}
87
88
public function didMarkupText() {
89
$engine = $this->getEngine();
90
$metadata_key = self::KEY_RULE_EXTERNAL_IMAGE;
91
$images = $engine->getTextMetadata($metadata_key, array());
92
$engine->setTextMetadata($metadata_key, array());
93
94
if (!$images) {
95
return;
96
}
97
98
// Look for images we've already successfully fetched that aren't about
99
// to get eaten by the GC. For any we find, we can just emit a normal
100
// "<img />" tag pointing directly to the file.
101
102
// For files which we don't hit in the cache, we emit a placeholder
103
// instead and use AJAX to actually perform the fetch.
104
105
$digests = array();
106
foreach ($images as $image) {
107
$uri = $image['args']['uri'];
108
$digests[] = PhabricatorHash::digestForIndex($uri);
109
}
110
111
$caches = id(new PhabricatorFileExternalRequest())->loadAllWhere(
112
'uriIndex IN (%Ls) AND isSuccessful = 1 AND ttl > %d',
113
$digests,
114
PhabricatorTime::getNow() + phutil_units('1 hour in seconds'));
115
116
$file_phids = array();
117
foreach ($caches as $cache) {
118
$file_phids[$cache->getFilePHID()] = $cache->getURI();
119
}
120
121
$file_map = array();
122
if ($file_phids) {
123
$files = id(new PhabricatorFileQuery())
124
->setViewer(PhabricatorUser::getOmnipotentUser())
125
->withPHIDs(array_keys($file_phids))
126
->execute();
127
foreach ($files as $file) {
128
$phid = $file->getPHID();
129
130
$file_remote_uri = $file_phids[$phid];
131
$file_view_uri = $file->getViewURI();
132
133
$file_map[$file_remote_uri] = $file_view_uri;
134
}
135
}
136
137
foreach ($images as $image) {
138
$args = $image['args'];
139
$uri = $args['uri'];
140
141
$direct_uri = idx($file_map, $uri);
142
if ($direct_uri) {
143
$img = phutil_tag(
144
'img',
145
array(
146
'src' => $direct_uri,
147
'alt' => $args['alt'],
148
'width' => $args['width'],
149
'height' => $args['height'],
150
));
151
} else {
152
$src_uri = id(new PhutilURI('/file/imageproxy/'))
153
->replaceQueryParam('uri', $uri);
154
155
$img = id(new PHUIRemarkupImageView())
156
->setURI($src_uri)
157
->setAlt($args['alt'])
158
->setWidth($args['width'])
159
->setHeight($args['height']);
160
}
161
162
$engine->overwriteStoredText($image['token'], $img);
163
}
164
}
165
166
private function isURI($uri_string) {
167
// Very simple check to make sure it starts with either http or https.
168
// If it does, we'll try to treat it like a valid URI
169
return preg_match('~^https?\:\/\/.*\z~i', $uri_string);
170
}
171
172
}
173
174