Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/view/form/control/PhabricatorRemarkupControl.php
12256 views
1
<?php
2
3
final class PhabricatorRemarkupControl
4
extends AphrontFormTextAreaControl {
5
6
private $disableFullScreen = false;
7
private $canPin;
8
private $sendOnEnter = false;
9
private $remarkupMetadata = array();
10
11
public function setDisableFullScreen($disable) {
12
$this->disableFullScreen = $disable;
13
return $this;
14
}
15
16
public function setCanPin($can_pin) {
17
$this->canPin = $can_pin;
18
return $this;
19
}
20
21
public function getCanPin() {
22
return $this->canPin;
23
}
24
25
public function setSendOnEnter($soe) {
26
$this->sendOnEnter = $soe;
27
return $this;
28
}
29
30
public function getSendOnEnter() {
31
return $this->sendOnEnter;
32
}
33
34
public function setRemarkupMetadata(array $value) {
35
$this->remarkupMetadata = $value;
36
return $this;
37
}
38
39
public function getRemarkupMetadata() {
40
return $this->remarkupMetadata;
41
}
42
43
public function setValue($value) {
44
if ($value instanceof RemarkupValue) {
45
$this->setRemarkupMetadata($value->getMetadata());
46
$value = $value->getCorpus();
47
}
48
49
return parent::setValue($value);
50
}
51
52
protected function renderInput() {
53
$id = $this->getID();
54
if (!$id) {
55
$id = celerity_generate_unique_node_id();
56
$this->setID($id);
57
}
58
59
$viewer = $this->getUser();
60
if (!$viewer) {
61
throw new PhutilInvalidStateException('setUser');
62
}
63
64
// NOTE: Metadata is passed to Javascript in a structured way, and also
65
// dumped directly into the form as an encoded string. This makes it less
66
// likely that we'll lose server-provided metadata (for example, from a
67
// saved draft) if there is a client-side error.
68
69
$metadata_name = $this->getName().'_metadata';
70
$metadata_value = (object)$this->getRemarkupMetadata();
71
$metadata_string = phutil_json_encode($metadata_value);
72
73
$metadata_id = celerity_generate_unique_node_id();
74
$metadata_input = phutil_tag(
75
'input',
76
array(
77
'type' => 'hidden',
78
'id' => $metadata_id,
79
'name' => $metadata_name,
80
'value' => $metadata_string,
81
));
82
83
// We need to have this if previews render images, since Ajax can not
84
// currently ship JS or CSS.
85
require_celerity_resource('phui-lightbox-css');
86
87
if (!$this->getDisabled()) {
88
Javelin::initBehavior(
89
'aphront-drag-and-drop-textarea',
90
array(
91
'target' => $id,
92
'remarkupMetadataID' => $metadata_id,
93
'remarkupMetadataValue' => $metadata_value,
94
'activatedClass' => 'aphront-textarea-drag-and-drop',
95
'uri' => '/file/dropupload/',
96
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
97
));
98
}
99
100
$root_id = celerity_generate_unique_node_id();
101
102
$user_datasource = new PhabricatorPeopleDatasource();
103
$emoji_datasource = new PhabricatorEmojiDatasource();
104
$proj_datasource = id(new PhabricatorProjectDatasource())
105
->setParameters(
106
array(
107
'autocomplete' => 1,
108
));
109
110
$phriction_datasource = new PhrictionDocumentDatasource();
111
$phurl_datasource = new PhabricatorPhurlURLDatasource();
112
113
Javelin::initBehavior(
114
'phabricator-remarkup-assist',
115
array(
116
'pht' => array(
117
'bold text' => pht('bold text'),
118
'italic text' => pht('italic text'),
119
'monospaced text' => pht('monospaced text'),
120
'List Item' => pht('List Item'),
121
'Quoted Text' => pht('Quoted Text'),
122
'data' => pht('data'),
123
'name' => pht('name'),
124
'URL' => pht('URL'),
125
'key-help' => pht('Pin or unpin the comment form.'),
126
),
127
'canPin' => $this->getCanPin(),
128
'disabled' => $this->getDisabled(),
129
'sendOnEnter' => $this->getSendOnEnter(),
130
'rootID' => $root_id,
131
'autocompleteMap' => (object)array(
132
64 => array( // "@"
133
'datasourceURI' => $user_datasource->getDatasourceURI(),
134
'headerIcon' => 'fa-user',
135
'headerText' => pht('Find User:'),
136
'hintText' => $user_datasource->getPlaceholderText(),
137
),
138
35 => array( // "#"
139
'datasourceURI' => $proj_datasource->getDatasourceURI(),
140
'headerIcon' => 'fa-briefcase',
141
'headerText' => pht('Find Project:'),
142
'hintText' => $proj_datasource->getPlaceholderText(),
143
),
144
58 => array( // ":"
145
'datasourceURI' => $emoji_datasource->getDatasourceURI(),
146
'headerIcon' => 'fa-smile-o',
147
'headerText' => pht('Find Emoji:'),
148
'hintText' => $emoji_datasource->getPlaceholderText(),
149
150
// Cancel on emoticons like ":3".
151
'ignore' => array(
152
'3',
153
')',
154
'(',
155
'-',
156
'/',
157
),
158
),
159
91 => array( // "["
160
'datasourceURI' => $phriction_datasource->getDatasourceURI(),
161
'headerIcon' => 'fa-book',
162
'headerText' => pht('Find Document:'),
163
'hintText' => $phriction_datasource->getPlaceholderText(),
164
'cancel' => array(
165
':', // Cancel on "http:" and similar.
166
'|',
167
']',
168
),
169
'prefix' => '^\\[',
170
),
171
40 => array( // "("
172
'datasourceURI' => $phurl_datasource->getDatasourceURI(),
173
'headerIcon' => 'fa-compress',
174
'headerText' => pht('Find Phurl:'),
175
'hintText' => $phurl_datasource->getPlaceholderText(),
176
'cancel' => array(
177
')',
178
),
179
'prefix' => '^\\(',
180
),
181
),
182
));
183
Javelin::initBehavior('phabricator-tooltips', array());
184
185
$actions = array(
186
'fa-bold' => array(
187
'tip' => pht('Bold'),
188
'nodevice' => true,
189
),
190
'fa-italic' => array(
191
'tip' => pht('Italics'),
192
'nodevice' => true,
193
),
194
'fa-text-width' => array(
195
'tip' => pht('Monospaced'),
196
'nodevice' => true,
197
),
198
'fa-link' => array(
199
'tip' => pht('Link'),
200
'nodevice' => true,
201
),
202
array(
203
'spacer' => true,
204
'nodevice' => true,
205
),
206
'fa-list-ul' => array(
207
'tip' => pht('Bulleted List'),
208
'nodevice' => true,
209
),
210
'fa-list-ol' => array(
211
'tip' => pht('Numbered List'),
212
'nodevice' => true,
213
),
214
'fa-code' => array(
215
'tip' => pht('Code Block'),
216
'nodevice' => true,
217
),
218
'fa-quote-right' => array(
219
'tip' => pht('Quote'),
220
'nodevice' => true,
221
),
222
'fa-table' => array(
223
'tip' => pht('Table'),
224
'nodevice' => true,
225
),
226
'fa-cloud-upload' => array(
227
'tip' => pht('Upload File'),
228
),
229
);
230
231
$can_use_macros = function_exists('imagettftext');
232
233
if ($can_use_macros) {
234
$can_use_macros = PhabricatorApplication::isClassInstalledForViewer(
235
'PhabricatorMacroApplication',
236
$viewer);
237
}
238
239
if ($can_use_macros) {
240
$actions[] = array(
241
'spacer' => true,
242
);
243
$actions['fa-meh-o'] = array(
244
'tip' => pht('Meme'),
245
);
246
}
247
248
$actions['fa-eye'] = array(
249
'tip' => pht('Preview'),
250
'align' => 'right',
251
);
252
253
$actions['fa-book'] = array(
254
'tip' => pht('Help'),
255
'align' => 'right',
256
'href' => PhabricatorEnv::getDoclink('Remarkup Reference'),
257
);
258
259
$mode_actions = array();
260
261
if (!$this->disableFullScreen) {
262
$mode_actions['fa-arrows-alt'] = array(
263
'tip' => pht('Fullscreen Mode'),
264
'align' => 'right',
265
);
266
}
267
268
if ($this->getCanPin()) {
269
$mode_actions['fa-thumb-tack'] = array(
270
'tip' => pht('Pin Form On Screen'),
271
'align' => 'right',
272
);
273
}
274
275
if ($mode_actions) {
276
$actions += $mode_actions;
277
}
278
279
$buttons = array();
280
foreach ($actions as $action => $spec) {
281
282
$classes = array();
283
284
if (idx($spec, 'align') == 'right') {
285
$classes[] = 'remarkup-assist-right';
286
}
287
288
if (idx($spec, 'nodevice')) {
289
$classes[] = 'remarkup-assist-nodevice';
290
}
291
292
if (idx($spec, 'spacer')) {
293
$classes[] = 'remarkup-assist-separator';
294
$buttons[] = phutil_tag(
295
'span',
296
array(
297
'class' => implode(' ', $classes),
298
),
299
'');
300
continue;
301
} else {
302
$classes[] = 'remarkup-assist-button';
303
}
304
305
if ($action == 'fa-cloud-upload') {
306
$classes[] = 'remarkup-assist-upload';
307
}
308
309
$href = idx($spec, 'href', '#');
310
if ($href == '#') {
311
$meta = array('action' => $action);
312
$mustcapture = true;
313
$target = null;
314
} else {
315
$meta = array();
316
$mustcapture = null;
317
$target = '_blank';
318
}
319
320
$content = null;
321
322
$tip = idx($spec, 'tip');
323
if ($tip) {
324
$meta['tip'] = $tip;
325
$content = javelin_tag(
326
'span',
327
array(
328
'aural' => true,
329
),
330
$tip);
331
}
332
333
$sigils = array();
334
$sigils[] = 'remarkup-assist';
335
if (!$this->getDisabled()) {
336
$sigils[] = 'has-tooltip';
337
}
338
339
$buttons[] = javelin_tag(
340
'a',
341
array(
342
'class' => implode(' ', $classes),
343
'href' => $href,
344
'sigil' => implode(' ', $sigils),
345
'meta' => $meta,
346
'mustcapture' => $mustcapture,
347
'target' => $target,
348
'tabindex' => -1,
349
),
350
phutil_tag(
351
'div',
352
array(
353
'class' =>
354
'remarkup-assist phui-icon-view phui-font-fa bluegrey '.$action,
355
),
356
$content));
357
}
358
359
$buttons = phutil_tag(
360
'div',
361
array(
362
'class' => 'remarkup-assist-bar',
363
),
364
$buttons);
365
366
$use_monospaced = $viewer->compareUserSetting(
367
PhabricatorMonospacedTextareasSetting::SETTINGKEY,
368
PhabricatorMonospacedTextareasSetting::VALUE_TEXT_MONOSPACED);
369
370
if ($use_monospaced) {
371
$monospaced_textareas_class = 'PhabricatorMonospaced';
372
} else {
373
$monospaced_textareas_class = null;
374
}
375
376
$this->setCustomClass(
377
'remarkup-assist-textarea '.$monospaced_textareas_class);
378
379
return javelin_tag(
380
'div',
381
array(
382
'sigil' => 'remarkup-assist-control',
383
'class' => $this->getDisabled() ? 'disabled-control' : null,
384
'id' => $root_id,
385
),
386
array(
387
$buttons,
388
parent::renderInput(),
389
$metadata_input,
390
));
391
}
392
393
}
394
395