$replyto = !empty($_REQUEST['replyto']) ? Strings::escapeTags(trim($_REQUEST['replyto'])) : '';
$subject = !empty($_REQUEST['subject']) ? Strings::escapeTags(trim($_REQUEST['subject'])) : '';
$body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
- $recipient = !empty($_REQUEST['messageto']) ? intval($_REQUEST['messageto']) : 0;
+ $recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0;
$ret = Mail::send($recipient, $body, $subject, $replyto);
$norecip = false;
'$linkurl' => DI::l10n()->t('Please enter a link URL:')
]);
- $preselect = isset($a->argv[2]) ? [$a->argv[2]] : [];
+ $recipientId = $a->argv[2] ?? null;
- $prename = $preurl = $preid = '';
-
- if ($preselect) {
- $r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1",
- intval(local_user()),
- intval($a->argv[2])
- );
- if (!DBA::isResult($r)) {
- $r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1",
- intval(local_user()),
- DBA::escape(Strings::normaliseLink(base64_decode($a->argv[2])))
- );
- }
-
- if (!DBA::isResult($r)) {
- $r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `addr` = '%s' LIMIT 1",
- intval(local_user()),
- DBA::escape(base64_decode($a->argv[2]))
- );
- }
-
- if (DBA::isResult($r)) {
- $prename = $r[0]['name'];
- $preid = $r[0]['id'];
- $preselect = [$preid];
- } else {
- $preselect = [];
- }
- }
-
- $prefill = $preselect ? $prename : '';
-
- // the ugly select box
- $select = ACL::getMessageContactSelectHTML('messageto', 'message-to-select', $preselect, 4, 10);
+ $select = ACL::getMessageContactSelectHTML($recipientId);
$tpl = Renderer::getMarkupTemplate('prv_message.tpl');
$o .= Renderer::replaceMacros($tpl, [
'$header' => DI::l10n()->t('Send Private Message'),
'$to' => DI::l10n()->t('To:'),
- '$showinputs' => 'true',
- '$prefill' => $prefill,
- '$preid' => $preid,
'$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $_REQUEST['subject'] ?? '',
'$text' => $_REQUEST['body'] ?? '',
$seen = $message['seen'];
}
- $select = $message['name'] . '<input type="hidden" name="messageto" value="' . $contact_id . '" />';
+ $select = $message['name'] . '<input type="hidden" name="recipient" value="' . $contact_id . '" />';
$parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />';
$tpl = Renderer::getMarkupTemplate('mail_display.tpl');
// reply
'$header' => DI::l10n()->t('Send Reply'),
'$to' => DI::l10n()->t('To:'),
- '$showinputs' => '',
'$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $message['title'],
'$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
class ACL
{
/**
- * Returns a select input tag with all the contact of the local user
+ * Returns a select input tag for private message recipient
*
- * @param string $selname Name attribute of the select input tag
- * @param string $selclass Class attribute of the select input tag
- * @param array $preselected Contact IDs that should be already selected
- * @param int $size Length of the select box
- * @param int $tabindex Select input tag tabindex attribute
+ * @param int $selected Existing recipien contact ID
* @return string
* @throws \Exception
*/
- public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
+ public static function getMessageContactSelectHTML(int $selected = null)
{
- $a = DI::app();
-
$o = '';
- // When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
- // to one recipient. By default our selector allows multiple selects amongst all contacts.
- $sql_extra = sprintf(" AND `rel` = %d ", intval(Contact::FRIEND));
- $sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", Protocol::DFRN, Protocol::DIASPORA);
-
- $tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
+ $page = DI::page();
- $hidepreselected = '';
- if ($preselected) {
- $sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
- $hidepreselected = ' style="display: none;"';
- }
+ $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
+ $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
- $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
+ // When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
+ // to one recipient. By default our selector allows multiple selects amongst all contacts.
+ $condition = [
+ 'uid' => local_user(),
+ 'self' => false,
+ 'blocked' => false,
+ 'pending' => false,
+ 'archive' => false,
+ 'deleted' => false,
+ 'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
+ 'network' => Protocol::FEDERATED,
+ ];
- $stmt = DBA::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
- WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
- $sql_extra
- ORDER BY `name` ASC ", intval(local_user())
+ $contacts = Contact::selectToArray(
+ ['id', 'name', 'addr', 'micro'],
+ DBA::mergeConditions($condition, ["`notify` != ''"])
);
- $contacts = DBA::toArray($stmt);
-
$arr = ['contact' => $contacts, 'entry' => $o];
- // e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
- Hook::callAll(DI::module()->getName() . '_pre_' . $selname, $arr);
+ Hook::callAll(DI::module()->getName() . '_pre_recipient', $arr);
- $receiverlist = [];
-
- if (DBA::isResult($contacts)) {
- foreach ($contacts as $contact) {
- if (in_array($contact['id'], $preselected)) {
- $selected = ' selected="selected"';
- } else {
- $selected = '';
- }
-
- $trimmed = Protocol::formatMention($contact['url'], $contact['name']);
-
- $receiverlist[] = $trimmed;
-
- $o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
- }
- }
-
- $o .= '</select>' . PHP_EOL;
-
- if ($preselected) {
- $o .= implode(', ', $receiverlist);
- }
+ $tpl = Renderer::getMarkupTemplate('acl/message_recipient.tpl');
+ $o = Renderer::replaceMacros($tpl, [
+ '$contacts' => $contacts,
+ '$selected' => $selected,
+ ]);
- Hook::callAll(DI::module()->getName() . '_post_' . $selname, $o);
+ Hook::callAll(DI::module()->getName() . '_post_recipient', $o);
return $o;
}
--- /dev/null
+<select name="recipient" class="form-control input-lg" id="recipient">
+ {{foreach $contacts as $contact}}
+ <option value="{{$contact.id}}"{{if $contact.id == $selected}} selected{{/if}}>{{$contact.name}}</option>
+ {{/foreach}}
+</select>
+<script type="text/javascript">
+ $(function() {
+ let $recipient_input = $('[name="recipient"]');
+
+ let acl = new Bloodhound({
+ local: {{$contacts|@json_encode nofilter}},
+ identify: function(obj) { return obj.id.toString(); },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ sorter: function (itemA, itemB) {
+ if (itemA.name === itemB.name) {
+ return 0;
+ } else if (itemA.name > itemB.name) {
+ return 1;
+ } else {
+ return -1;
+ }
+ },
+ });
+ acl.initialize();
+
+ let suggestionTemplate = function (item) {
+ return '<div><img src="' + item.micro + '" alt="" style="float: left; width: auto; height: 2.8em; margin-right: 0.5em;"><p style="margin-left: 3.3em;"><strong>' + item.name + '</strong><br /><em>' + item.addr + '</em></p></div>';
+ };
+
+ $recipient_input.tagsinput({
+ confirmKeys: [13, 44],
+ freeInput: false,
+ tagClass: 'label label-info',
+ itemValue: function (item) { return item.id.toString(); },
+ itemText: 'name',
+ itemThumb: 'micro',
+ itemTitle: function(item) {
+ return item.addr;
+ },
+ typeaheadjs: {
+ name: 'contacts',
+ displayKey: 'name',
+ templates: {
+ suggestion: suggestionTemplate
+ },
+ source: acl.ttAdapter()
+ }
+ });
+
+ // Import existing ACL into the tags input fields.
+ $recipient_input.tagsinput('add', acl.get({{$selected}})[0]);
+ });
+</script>
-
-<script>
-$(document).ready(function() {
- $("#recip").name_autocomplete(baseurl + '/search/acl', 'm', false, function(data) {
- $("#recip-complete").val(data.id);
- });
-});
-</script>
width:100px;
}
-#recip {
-
-}
.autocomplete-w1 { background: #ffffff no-repeat bottom right; position:absolute; top:0px; left:0px; margin:6px 0 0 6px; /* IE6 fix: */ _background:none; _margin:1px 0 0 0; }
.autocomplete { color:#000; border:1px solid #999; background:#FFF; cursor:default; text-align:left; max-height:350px; overflow:auto; margin:-6px 6px 6px -6px; /* IE6 specific: */ _height:350px; _margin:0; _overflow-x:hidden; }
.autocomplete .selected { background:#F0F0F0; }
+++ /dev/null
-
-
-<h3>{{$header}}</h3>
-
-<div id="prvmail-wrapper" >
-<form id="prvmail-form" action="message" method="post" >
-
-{{$parent nofilter}}
-
-<div id="prvmail-to-label">{{$to}}</div>
-
-{{if $showinputs}}
-<input type="text" id="recip" name="messagerecip" value="{{$prefill}}" maxlength="255" size="64" tabindex="10" />
-<input type="hidden" id="recip-complete" name="messageto" value="{{$preid}}">
-{{else}}
-{{$select nofilter}}
-{{/if}}
-
-<div id="prvmail-subject-label">{{$subject}}</div>
-<input type="text" size="64" maxlength="255" id="prvmail-subject" name="subject" value="{{$subjtxt}}" {{$readonly}} tabindex="11" />
-
-<div id="prvmail-message-label">{{$yourmessage}}</div>
-<textarea rows="8" cols="72" class="prvmail-text" id="prvmail-text" name="body" tabindex="12">{{$text}}</textarea>
-
-
-<div id="prvmail-submit-wrapper" >
- <input type="submit" id="prvmail-submit" name="submit" value="{{$submit}}" tabindex="13" />
- <div id="prvmail-upload-wrapper" >
- <div id="prvmail-upload" class="icon border camera" title="{{$upload}}" ></div>
- </div>
- <div id="prvmail-link-wrapper" >
- <div id="prvmail-link" class="icon border link" title="{{$insert}}" onclick="jotGetLink();" ></div>
- </div>
- <div id="prvmail-rotator-wrapper" >
- <img id="prvmail-rotator" src="images/rotator.gif" alt="{{$wait}}" title="{{$wait}}" style="display: none;" />
- </div>
-</div>
-<div id="prvmail-end"></div>
-</form>
-</div>
<div id="prvmail-wrapper">
<form id="prvmail-form" action="message" method="post" >
- {{* Disable the header. We will see if we will need it
- <h3>{{$header}}</h3>
- *}}
-
{{$parent nofilter}}
{{* The To: form-group which contains the message recipient *}}
<div id="prvmail-to-label" class="form-group">
- <label for="recip">{{$to}}</label><br>
- {{if $showinputs}}
- <input type="text" id="recip" class="form-control" name="messagerecip" value="{{$prefill}}" tabindex="10" {{if $prefill}}disabled{{else}}aria-required="true"{{/if}} />
- <input type="hidden" id="recip-complete" name="messageto" value="{{$preid}}">
- {{else}}
+ <label for="recipient">{{$to}}</label><br>
{{$select nofilter}}
- {{/if}}
</div>
{{* The subject input field *}}
+++ /dev/null
-<h3>{{$header}}</h3>
-
-<div id="prvmail-wrapper" >
-<form id="prvmail-form" action="message" method="post" >
-
-{{$parent nofilter}}
-
-<div id="prvmail-to-label">{{$to}}</div>
-{{if $showinputs}}
-<input type="text" id="recip" name="messagerecip" value="{{$prefill}}" maxlength="255" size="64" tabindex="10" />
-<input type="hidden" id="recip-complete" name="messageto" value="{{$preid}}">
-{{else}}
-{{$select nofilter}}
-{{/if}}
-
-<div id="prvmail-subject-label">{{$subject}}</div>
-<input type="text" size="64" maxlength="255" id="prvmail-subject" name="subject" value="{{$subjtxt}}" {{$readonly}} tabindex="11" />
-
-<div id="prvmail-message-label">{{$yourmessage}}</div>
-<textarea rows="20" cols="72" class="prvmail-text" id="prvmail-text" name="body" tabindex="12">{{$text}}</textarea>
-
-
-<div id="prvmail-submit-wrapper" >
- <input type="submit" id="prvmail-submit" name="submit" value="{{$submit}}" tabindex="13" />
- <div id="prvmail-upload-wrapper" >
- <div id="prvmail-upload" class="icon border camera" title="{{$upload}}" ></div>
- </div>
- <div id="prvmail-link-wrapper" >
- <div id="prvmail-link" class="icon border link" title="{{$insert}}" onclick="jotGetLink();" ></div>
- </div>
- <div id="prvmail-rotator-wrapper" >
- <img id="prvmail-rotator" src="images/rotator.gif" alt="{{$wait}}" title="{{$wait}}" style="display: none;" />
- </div>
-</div>
-<div id="prvmail-end"></div>
-</form>
-</div>