Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php
12241 views
1
<?php
2
3
final class DiffusionSourceLinkRemarkupRule
4
extends PhutilRemarkupRule {
5
6
const KEY_SOURCELINKS = 'diffusion.links';
7
8
public function getPriority() {
9
return 200.0;
10
}
11
12
public function apply($text) {
13
return preg_replace_callback(
14
'@{(?:src|source)\b((?:[^}\\\\]+|\\\\.)*)}@m',
15
array($this, 'markupSourceLink'),
16
$text);
17
}
18
19
public function markupSourceLink(array $matches) {
20
$engine = $this->getEngine();
21
$text_mode = $engine->isTextMode();
22
$mail_mode = $engine->isHTMLMailMode();
23
24
if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
25
// We could do better than this in text mode and mail mode, but focus
26
// on web mode first.
27
return $matches[0];
28
}
29
30
$metadata_key = self::KEY_SOURCELINKS;
31
$metadata = $engine->getTextMetadata($metadata_key, array());
32
33
$token = $engine->storeText($matches[0]);
34
35
$metadata[] = array(
36
'token' => $token,
37
'raw' => $matches[0],
38
'input' => $matches[1],
39
);
40
41
$engine->setTextMetadata($metadata_key, $metadata);
42
43
return $token;
44
}
45
46
public function didMarkupText() {
47
$engine = $this->getEngine();
48
$metadata_key = self::KEY_SOURCELINKS;
49
$metadata = $engine->getTextMetadata($metadata_key, array());
50
51
if (!$metadata) {
52
return;
53
}
54
55
$viewer = $engine->getConfig('viewer');
56
if (!$viewer) {
57
return;
58
}
59
60
$defaults = array(
61
'repository' => null,
62
'line' => null,
63
'commit' => null,
64
'ref' => null,
65
);
66
67
$tags = array();
68
foreach ($metadata as $ref) {
69
$token = $ref['token'];
70
$raw = $ref['raw'];
71
$input = $ref['input'];
72
73
$pattern =
74
'(^'.
75
'[\s,]*'.
76
'(?:"(?P<quotedpath>(?:[^\\\\"]+|\\.)+)"|(?P<rawpath>[^\s,]+))'.
77
'[\s,]*'.
78
'(?P<options>.*)'.
79
'\z)';
80
$matches = null;
81
if (!preg_match($pattern, $input, $matches)) {
82
$hint_text = pht(
83
'Missing path, expected "{src path ...}" in: %s',
84
$raw);
85
$hint = $this->newSyntaxHint($hint_text);
86
87
$engine->overwriteStoredText($token, $hint);
88
continue;
89
}
90
91
$path = idx($matches, 'rawpath');
92
if (!strlen($path)) {
93
$path = idx($matches, 'quotedpath');
94
$path = stripcslashes($path);
95
}
96
97
$parts = explode(':', $path, 2);
98
if (count($parts) == 2) {
99
$repository = nonempty($parts[0], null);
100
$path = $parts[1];
101
} else {
102
$repository = null;
103
$path = $parts[0];
104
}
105
106
$options = $matches['options'];
107
108
$parser = new PhutilSimpleOptions();
109
$options = $parser->parse($options) + $defaults;
110
111
foreach ($options as $key => $value) {
112
if (!array_key_exists($key, $defaults)) {
113
$hint_text = pht(
114
'Unknown option "%s" in: %s',
115
$key,
116
$raw);
117
$hint = $this->newSyntaxHint($hint_text);
118
119
$engine->overwriteStoredText($token, $hint);
120
continue 2;
121
}
122
}
123
124
if ($options['repository'] !== null) {
125
$repository = $options['repository'];
126
}
127
128
if ($repository === null) {
129
$hint_text = pht(
130
'Missing repository, expected "{src repository:path ...}" '.
131
'or "{src path repository=...}" in: %s',
132
$raw);
133
$hint = $this->newSyntaxHint($hint_text);
134
135
$engine->overwriteStoredText($token, $hint);
136
continue;
137
}
138
139
$tags[] = array(
140
'token' => $token,
141
'raw' => $raw,
142
'identifier' => $repository,
143
'path' => $path,
144
'options' => $options,
145
);
146
}
147
148
if (!$tags) {
149
return;
150
}
151
152
$query = id(new PhabricatorRepositoryQuery())
153
->setViewer($viewer)
154
->withIdentifiers(ipull($tags, 'identifier'));
155
156
$query->execute();
157
158
$repository_map = $query->getIdentifierMap();
159
160
foreach ($tags as $tag) {
161
$token = $tag['token'];
162
163
$identifier = $tag['identifier'];
164
$repository = idx($repository_map, $identifier);
165
if (!$repository) {
166
// For now, just bail out here. Ideally, we should distingiush between
167
// restricted and invalid repositories.
168
continue;
169
}
170
171
$drequest = DiffusionRequest::newFromDictionary(
172
array(
173
'user' => $viewer,
174
'repository' => $repository,
175
));
176
177
$options = $tag['options'];
178
179
$line = $options['line'];
180
$commit = $options['commit'];
181
$ref_name = $options['ref'];
182
183
$link_uri = $drequest->generateURI(
184
array(
185
'action' => 'browse',
186
'path' => $tag['path'],
187
'commit' => $commit,
188
'line' => $line,
189
'branch' => $ref_name,
190
));
191
192
$view = id(new DiffusionSourceLinkView())
193
->setRepository($repository)
194
->setPath($tag['path'])
195
->setURI($link_uri);
196
197
if ($line !== null) {
198
$view->setLine($line);
199
}
200
201
if ($commit !== null) {
202
$view->setCommit($commit);
203
}
204
205
if ($ref_name !== null) {
206
$view->setRefName($ref_name);
207
}
208
209
$engine->overwriteStoredText($token, $view);
210
}
211
}
212
213
private function newSyntaxHint($text) {
214
return id(new PHUITagView())
215
->setType(PHUITagView::TYPE_SHADE)
216
->setColor('red')
217
->setIcon('fa-exclamation-triangle')
218
->setName($text);
219
}
220
221
}
222
223