Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/people/markup/PhabricatorMentionRemarkupRule.php
12256 views
1
<?php
2
3
final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule {
4
5
const KEY_RULE_MENTION = 'rule.mention';
6
const KEY_RULE_MENTION_ORIGINAL = 'rule.mention.original';
7
8
const KEY_MENTIONED = 'phabricator.mentioned-user-phids';
9
10
11
// NOTE: The negative lookbehind prevents matches like "mail@lists", while
12
// allowing constructs like "@tomo/@mroch". Since we now allow periods in
13
// usernames, we can't reasonably distinguish that "@company.com" isn't a
14
// username, so we'll incorrectly pick it up, but there's little to be done
15
// about that. We forbid terminal periods so that we can correctly capture
16
// "@joe" instead of "@joe." in "Hey, @joe.".
17
//
18
// We disallow "@@joe" because it creates a false positive in the common
19
// construction "l@@k", made popular by eBay.
20
const REGEX = '/(?<!\w|@)@([a-zA-Z0-9._-]*[a-zA-Z0-9_-])/';
21
22
public function apply($text) {
23
return preg_replace_callback(
24
self::REGEX,
25
array($this, 'markupMention'),
26
$text);
27
}
28
29
protected function markupMention(array $matches) {
30
$engine = $this->getEngine();
31
32
if ($engine->isTextMode()) {
33
return $engine->storeText($matches[0]);
34
}
35
36
$token = $engine->storeText('');
37
38
// Store the original text exactly so we can preserve casing if it doesn't
39
// resolve into a username.
40
$original_key = self::KEY_RULE_MENTION_ORIGINAL;
41
$original = $engine->getTextMetadata($original_key, array());
42
$original[$token] = $matches[1];
43
$engine->setTextMetadata($original_key, $original);
44
45
$metadata_key = self::KEY_RULE_MENTION;
46
$metadata = $engine->getTextMetadata($metadata_key, array());
47
$username = strtolower($matches[1]);
48
if (empty($metadata[$username])) {
49
$metadata[$username] = array();
50
}
51
$metadata[$username][] = $token;
52
$engine->setTextMetadata($metadata_key, $metadata);
53
54
return $token;
55
}
56
57
public function didMarkupText() {
58
$engine = $this->getEngine();
59
60
$metadata_key = self::KEY_RULE_MENTION;
61
$metadata = $engine->getTextMetadata($metadata_key, array());
62
if (empty($metadata)) {
63
// No mentions, or we already processed them.
64
return;
65
}
66
67
$original_key = self::KEY_RULE_MENTION_ORIGINAL;
68
$original = $engine->getTextMetadata($original_key, array());
69
70
$usernames = array_keys($metadata);
71
72
$users = id(new PhabricatorPeopleQuery())
73
->setViewer($this->getEngine()->getConfig('viewer'))
74
->withUsernames($usernames)
75
->needAvailability(true)
76
->execute();
77
78
$actual_users = array();
79
80
$mentioned_key = self::KEY_MENTIONED;
81
$mentioned = $engine->getTextMetadata($mentioned_key, array());
82
foreach ($users as $row) {
83
$actual_users[strtolower($row->getUserName())] = $row;
84
$mentioned[$row->getPHID()] = $row->getPHID();
85
}
86
87
$engine->setTextMetadata($mentioned_key, $mentioned);
88
$context_object = $engine->getConfig('contextObject');
89
90
$policy_object = null;
91
if ($context_object) {
92
if ($context_object instanceof PhabricatorPolicyInterface) {
93
$policy_object = $context_object;
94
}
95
}
96
97
if ($policy_object) {
98
$policy_set = new PhabricatorPolicyFilterSet();
99
foreach ($actual_users as $user) {
100
$policy_set->addCapability(
101
$user,
102
$policy_object,
103
PhabricatorPolicyCapability::CAN_VIEW);
104
}
105
}
106
107
foreach ($metadata as $username => $tokens) {
108
$exists = isset($actual_users[$username]);
109
$user_can_not_view = false;
110
111
if ($exists) {
112
$user = $actual_users[$username];
113
114
// Check if the user has view access to the object she was mentioned in
115
if ($policy_object) {
116
$user_can_not_view = !$policy_set->hasCapability(
117
$user,
118
$policy_object,
119
PhabricatorPolicyCapability::CAN_VIEW);
120
}
121
122
$user_href = '/p/'.$user->getUserName().'/';
123
124
if ($engine->isHTMLMailMode()) {
125
$user_href = PhabricatorEnv::getProductionURI($user_href);
126
127
if ($user_can_not_view) {
128
$colors = '
129
border-color: #92969D;
130
color: #92969D;
131
background-color: #F7F7F7;';
132
} else {
133
$colors = '
134
border-color: #f1f7ff;
135
color: #19558d;
136
background-color: #f1f7ff;';
137
}
138
139
$tag = phutil_tag(
140
'a',
141
array(
142
'href' => $user_href,
143
'style' => $colors.'
144
border: 1px solid transparent;
145
border-radius: 3px;
146
font-weight: bold;
147
padding: 0 4px;',
148
),
149
'@'.$user->getUserName());
150
} else {
151
if ($engine->getConfig('uri.full')) {
152
$user_href = PhabricatorEnv::getURI($user_href);
153
}
154
155
$tag = id(new PHUITagView())
156
->setType(PHUITagView::TYPE_PERSON)
157
->setPHID($user->getPHID())
158
->setName('@'.$user->getUserName())
159
->setHref($user_href);
160
161
if ($context_object) {
162
$tag->setContextObject($context_object);
163
}
164
165
if ($user_can_not_view) {
166
$tag->setIcon('fa-eye-slash red');
167
$tag->setIsExiled(true);
168
}
169
170
if ($user->getIsDisabled()) {
171
$tag->setDotColor(PHUITagView::COLOR_GREY);
172
} else if (!$user->isResponsive()) {
173
$tag->setDotColor(PHUITagView::COLOR_VIOLET);
174
} else {
175
if ($user->getAwayUntil()) {
176
$away = PhabricatorCalendarEventInvitee::AVAILABILITY_AWAY;
177
if ($user->getDisplayAvailability() == $away) {
178
$tag->setDotColor(PHUITagView::COLOR_RED);
179
} else {
180
$tag->setDotColor(PHUITagView::COLOR_ORANGE);
181
}
182
}
183
}
184
}
185
186
foreach ($tokens as $token) {
187
$engine->overwriteStoredText($token, $tag);
188
}
189
} else {
190
// NOTE: The structure here is different from the 'exists' branch,
191
// because we want to preserve the original text capitalization and it
192
// may differ for each token.
193
foreach ($tokens as $token) {
194
$tag = phutil_tag(
195
'span',
196
array(
197
'class' => 'phabricator-remarkup-mention-unknown',
198
),
199
'@'.idx($original, $token, $username));
200
$engine->overwriteStoredText($token, $tag);
201
}
202
}
203
}
204
205
// Don't re-process these mentions.
206
$engine->setTextMetadata($metadata_key, array());
207
}
208
209
}
210
211