mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-09-16 15:17:17 +00:00
Add some option to Global Chat
1. Report send to chat 2. Mentions in chat
This commit is contained in:
@@ -97,7 +97,8 @@ trait DatabaseGlobalChatQueries {
|
||||
|
||||
// Faza 2: c.type/c.poll_id/c.deleted/c.edited - vezi editGlobalChatMessage(),
|
||||
// deleteGlobalChatMessage() si createGlobalChatPoll() mai jos
|
||||
$q = "SELECT c.id, c.id_user, c.date, c.msg, c.type, c.poll_id, c.deleted, c.edited,
|
||||
// Faza 3: c.report_id - vezi shareGlobalChatReport() mai jos
|
||||
$q = "SELECT c.id, c.id_user, c.date, c.msg, c.type, c.poll_id, c.report_id, c.deleted, c.edited,
|
||||
u.username, u.access, a.tag AS ally_tag, a.id AS ally_id
|
||||
FROM " . TB_PREFIX . "chat_global c
|
||||
LEFT JOIN " . TB_PREFIX . "users u ON u.id = c.id_user
|
||||
@@ -475,4 +476,118 @@ trait DatabaseGlobalChatQueries {
|
||||
"DELETE FROM " . TB_PREFIX . "chat_mutes WHERE id_user = $targetUid"
|
||||
) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Faza 3 (09.09.2026): cautare useri dupa inceputul username-ului, pentru
|
||||
* autocomplete la @mentiuni. Exclude id 1-3 (Nature/Natars/conturi de
|
||||
* sistem - vezi addNotice() din DatabaseMessageQueries.php, care le trateaza
|
||||
* deja ca speciale). Scapam manual '%'/'_' din query ca sa nu functioneze
|
||||
* ca wildcard-uri LIKE neintentionate daca cineva tasteaza chiar aceste
|
||||
* caractere dupa @.
|
||||
*/
|
||||
function searchGlobalChatUsers($prefix, $limit = 8) {
|
||||
$prefix = trim((string) $prefix);
|
||||
$limit = (int) $limit;
|
||||
|
||||
if ($prefix === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$likeSafe = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $prefix);
|
||||
list($eprefix) = $this->escape_input($likeSafe);
|
||||
|
||||
return $this->mysqli_fetch_all($this->query(
|
||||
"SELECT id, username FROM " . TB_PREFIX . "users
|
||||
WHERE username LIKE '$eprefix%' AND id > 3
|
||||
ORDER BY username ASC
|
||||
LIMIT $limit"
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Faza 3 (09.09.2026): distribuie un raport de lupta propriu in chat-ul
|
||||
* general - de atac SAU de aparare (cand te ataca altcineva pe tine,
|
||||
* raportul TAU are tot ntype in acelasi set - vezi
|
||||
* AutomationBattleResolution::[cod care apeleaza addNotice], unde
|
||||
* atacatorul si apararea primesc fiecare propriul rand in `ndata`, cu
|
||||
* ntype-uri complementare din SETUL de mai jos - t=3/TZ_ATTACKS din
|
||||
* Message::noticeType() include deja ambele roluri, nu doar atacul).
|
||||
* Include si perechea 22/23 (scenariu de atac separat, gasit tot in
|
||||
* AutomationBattleResolution.php - nu e in intervalul 1-7 dar e tot
|
||||
* un raport de lupta atac+aparare).
|
||||
*
|
||||
* Exclude intentionat intariri (8,15,16,17) si comert (10-13): acelea
|
||||
* dezvaluie logistica/loturi, nepotrivite pentru difuzare pe tot
|
||||
* serverul (chat-ul general nu are granita de alianta, deci un raport
|
||||
* distribuit aici e vizibil oricui, inclusiv unui inamic) - acelasi
|
||||
* filtru ca share-ul de alianta din berichte.php.
|
||||
*
|
||||
* NU atingem tabela ndata (folosita de tot sistemul de mesaje) - in loc,
|
||||
* adaugam id-ul raportului intr-o allowlist separata (chat_global_shared_reports),
|
||||
* verificata suplimentar in berichte.php la afisarea unui raport individual.
|
||||
*
|
||||
* @return array ['ok'=>bool, 'reason'=>string|null]
|
||||
*/
|
||||
function shareGlobalChatReport($uid, $noticeId) {
|
||||
$uid = (int) $uid;
|
||||
$noticeId = (int) $noticeId;
|
||||
|
||||
if ($uid <= 0 || $noticeId <= 0) {
|
||||
return ['ok' => false, 'reason' => 'invalid'];
|
||||
}
|
||||
|
||||
$notice = $this->getNotice2($noticeId, null);
|
||||
|
||||
if (!$notice) {
|
||||
return ['ok' => false, 'reason' => 'notfound'];
|
||||
}
|
||||
|
||||
// tine sincron cu acelasi set din berichte.php ($tzShareableNtypes)
|
||||
$shareableNtypes = [1, 2, 3, 4, 5, 6, 7, 22, 23];
|
||||
if ((int) $notice['uid'] !== $uid || !in_array((int) $notice['ntype'], $shareableNtypes, true)) {
|
||||
return ['ok' => false, 'reason' => 'forbidden'];
|
||||
}
|
||||
|
||||
$mutedUntil = $this->getGlobalChatMuteStatus($uid);
|
||||
if ($mutedUntil !== null) {
|
||||
return ['ok' => false, 'reason' => 'muted', 'mutedUntil' => $mutedUntil];
|
||||
}
|
||||
|
||||
$now = time();
|
||||
list($eNoticeId, $euid) = $this->escape_input($noticeId, $uid);
|
||||
|
||||
// allowlist: acest raport devine vizibil oricui e logat (vezi berichte.php)
|
||||
$this->query(
|
||||
"INSERT INTO " . TB_PREFIX . "chat_global_shared_reports (notice_id, shared_by, created)
|
||||
VALUES ($eNoticeId, $euid, $now)
|
||||
ON DUPLICATE KEY UPDATE shared_by = $euid, created = $now"
|
||||
);
|
||||
|
||||
// mesajul-ancora in chat - topicul raportului (deja text afisabil,
|
||||
// acelasi camp folosit si in lista de rapoarte din berichte.php)
|
||||
$topic = trim((string) ($notice['topic'] ?? ''));
|
||||
$topic = function_exists('mb_substr') ? mb_substr($topic, 0, 250) : substr($topic, 0, 250);
|
||||
list($eTopic) = $this->escape_input($topic);
|
||||
|
||||
$this->query(
|
||||
"INSERT INTO " . TB_PREFIX . "chat_global (id_user, date, msg, type, report_id)
|
||||
VALUES ($euid, $now, '$eTopic', 'report', $eNoticeId)"
|
||||
);
|
||||
|
||||
return ['ok' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica daca un raport a fost distribuit in chat-ul general - folosita
|
||||
* de berichte.php ca o conditie suplimentara de acces (vezi acolo).
|
||||
*/
|
||||
function isGlobalChatSharedReport($noticeId) {
|
||||
$noticeId = (int) $noticeId;
|
||||
|
||||
$row = mysqli_fetch_assoc($this->query(
|
||||
"SELECT notice_id FROM " . TB_PREFIX . "chat_global_shared_reports WHERE notice_id = $noticeId"
|
||||
));
|
||||
|
||||
return $row ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4439,3 +4439,7 @@ tz_def('GCHAT_POLL_CREATE', 'Create poll');
|
||||
tz_def('GCHAT_POLL_VOTES_WORD', 'votes');
|
||||
tz_def('GCHAT_EMOJI_TITLE', 'Emoji');
|
||||
tz_def('GCHAT_ERROR_GENERIC', 'Something went wrong.');
|
||||
// Phase 3 (09.09.2026): @username mentions, share battle reports
|
||||
tz_def('GCHAT_SHARE_REPORT', 'Share to chat');
|
||||
tz_def('GCHAT_SHARE_REPORT_OK', 'Shared to chat!');
|
||||
tz_def('GCHAT_REPORT_FALLBACK', 'Battle report');
|
||||
|
||||
@@ -4176,3 +4176,7 @@ tz_def('GCHAT_POLL_CREATE', 'Creeaza sondaj');
|
||||
tz_def('GCHAT_POLL_VOTES_WORD', 'voturi');
|
||||
tz_def('GCHAT_EMOJI_TITLE', 'Emoji');
|
||||
tz_def('GCHAT_ERROR_GENERIC', 'A aparut o eroare.');
|
||||
// Faza 3 (09.09.2026): mentiuni @username, distribuire rapoarte de lupta
|
||||
tz_def('GCHAT_SHARE_REPORT', 'Distribuie in chat');
|
||||
tz_def('GCHAT_SHARE_REPORT_OK', 'Distribuit in chat!');
|
||||
tz_def('GCHAT_REPORT_FALLBACK', 'Raport de lupta');
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
#################################################################################
|
||||
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||||
## --------------------------------------------------------------------------- ##
|
||||
## Project: TravianZ ##
|
||||
## Filename: Templates/GlobalChat/share_report_button.tpl ##
|
||||
## Purpose: Buton "Distribuie in chat" pe pagina unui raport propriu de ##
|
||||
## atac (berichte.php) - Faza 3 Global Chat. ##
|
||||
## ##
|
||||
## Inclus DOAR de berichte.php, DOAR cand $tzShareEligible e true (proprietar ##
|
||||
## + ntype 1-7) - vezi acolo. id-ul raportului vine ca $_GET['id'], deja ##
|
||||
## curatat in berichte.php inainte de a ajunge aici. ##
|
||||
## ##
|
||||
## Cerut de Catalin, 09.09.2026 (Faza 3 Global Chat). ##
|
||||
## ##
|
||||
## License: TravianZ Project ##
|
||||
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||||
#################################################################################
|
||||
|
||||
$tzShareNoticeId = (int) preg_replace("/[^0-9]/", "", (string) ($_GET['id'] ?? 0));
|
||||
?>
|
||||
<div id="gchat_share_report_wrap" style="margin:8px 0;">
|
||||
<button type="button" id="gchat_share_report_btn" data-id="<?php echo $tzShareNoticeId; ?>"
|
||||
style="padding:4px 10px;border:1px solid #6b8f47;border-radius:4px;background:#6b8f47;color:#fff;cursor:pointer;font-size:12px;">
|
||||
<?php echo GCHAT_SHARE_REPORT; ?>
|
||||
</button>
|
||||
<span id="gchat_share_report_status" style="margin-left:6px;font-size:12px;color:#666;"></span>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
"use strict";
|
||||
var btn = document.getElementById('gchat_share_report_btn');
|
||||
var status = document.getElementById('gchat_share_report_status');
|
||||
if (!btn) { return; }
|
||||
|
||||
var OK_TXT = <?php echo json_encode(GCHAT_SHARE_REPORT_OK); ?>;
|
||||
var ERR_TXT = <?php echo json_encode(GCHAT_ERROR_GENERIC); ?>;
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
btn.disabled = true;
|
||||
status.textContent = '';
|
||||
|
||||
fetch('ajax.php?f=gchat_share_report', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'id=' + encodeURIComponent(btn.getAttribute('data-id'))
|
||||
}).then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
btn.disabled = false;
|
||||
status.textContent = (data && data.ok) ? OK_TXT : ERR_TXT;
|
||||
})
|
||||
.catch(function () {
|
||||
btn.disabled = false;
|
||||
status.textContent = ERR_TXT;
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -24,7 +24,7 @@
|
||||
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||||
#################################################################################
|
||||
?>
|
||||
<div id="gchat_root" data-uid="<?php echo (int) $session->uid; ?>">
|
||||
<div id="gchat_root" data-uid="<?php echo (int) $session->uid; ?>" data-username="<?php echo htmlspecialchars($session->username, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<button id="gchat_bubble" type="button" title="<?php echo GCHAT_TITLE; ?>">
|
||||
💬<span id="gchat_badge" style="display:none">0</span>
|
||||
</button>
|
||||
@@ -50,6 +50,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="gchat_mention_list" style="display:none"></div>
|
||||
|
||||
<form id="gchat_form" autocomplete="off">
|
||||
<button id="gchat_emoji_btn" type="button" class="gchat_toolbar_btn" title="<?php echo GCHAT_EMOJI_TITLE; ?>">🙂</button>
|
||||
<button id="gchat_poll_btn" type="button" class="gchat_toolbar_btn" title="<?php echo GCHAT_POLL_TITLE; ?>">📊</button>
|
||||
@@ -335,6 +337,50 @@
|
||||
#gchat_poll_add_option { background: #ddd; }
|
||||
#gchat_poll_submit { background: #6b8f47; color: #fff; }
|
||||
|
||||
/* Faza 3: mentiuni @username */
|
||||
#gchat_mention_list {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
bottom: 40px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,.15);
|
||||
z-index: 10;
|
||||
}
|
||||
.gchat_mention_item {
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
.gchat_mention_item:hover, .gchat_mention_item.active { background: #eef2e8; }
|
||||
|
||||
.gchat_mention { color: #1a5aa8; font-weight: bold; }
|
||||
.gchat_msg_mentioned {
|
||||
background: #fff6d5;
|
||||
border-radius: 3px;
|
||||
padding: 2px 3px;
|
||||
margin: -2px -3px 4px -3px;
|
||||
}
|
||||
|
||||
/* Faza 3: rapoarte de lupta distribuite in chat */
|
||||
.gchat_report_block { margin-top: 2px; }
|
||||
.gchat_report_link {
|
||||
display: inline-block;
|
||||
padding: 3px 6px;
|
||||
background: #f2f0e6;
|
||||
border: 1px solid #ddd6bd;
|
||||
border-radius: 4px;
|
||||
color: #6b4a1f;
|
||||
text-decoration: none;
|
||||
font-size: 11px;
|
||||
}
|
||||
.gchat_report_link:hover { text-decoration: underline; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
#gchat_panel { right: 8px; bottom: 66px; }
|
||||
#gchat_bubble { right: 8px; bottom: 8px; }
|
||||
@@ -367,7 +413,8 @@
|
||||
deletedPlaceholder: <?php echo json_encode(GCHAT_DELETED_PLACEHOLDER); ?>,
|
||||
pollOptionPlaceholder: <?php echo json_encode(GCHAT_POLL_OPTION_PLACEHOLDER); ?>,
|
||||
pollVotesWord: <?php echo json_encode(GCHAT_POLL_VOTES_WORD); ?>,
|
||||
errorGeneric: <?php echo json_encode(GCHAT_ERROR_GENERIC); ?>
|
||||
errorGeneric: <?php echo json_encode(GCHAT_ERROR_GENERIC); ?>,
|
||||
reportFallback: <?php echo json_encode(GCHAT_REPORT_FALLBACK); ?>
|
||||
};
|
||||
|
||||
var ACCESS_ADMIN = 9, ACCESS_MH = 8;
|
||||
@@ -390,8 +437,10 @@
|
||||
var pollForm = document.getElementById('gchat_poll_form');
|
||||
var pollQuestionInput = document.getElementById('gchat_poll_question');
|
||||
var pollOptionsBox = document.getElementById('gchat_poll_options');
|
||||
var mentionList = document.getElementById('gchat_mention_list');
|
||||
|
||||
var myUid = parseInt(root.getAttribute('data-uid'), 10) || 0;
|
||||
var myUsername = root.getAttribute('data-username') || '';
|
||||
var lastId = 0;
|
||||
var unread = 0;
|
||||
var isOpen = false;
|
||||
@@ -407,6 +456,12 @@
|
||||
var MAX_POLL_OPTIONS = 6;
|
||||
var EMOJI_LIST = ['😀','😂','😅','😊','😍','😎','🤔','😴','😭','😡','👍','👎','👏','🙏','💪','🔥','⭐','❤️','💯','🎉','⚔️','🛡️','🏰','🌾','🪵','⛏️','🧱','⏳','🐎','🏆'];
|
||||
|
||||
// Faza 3: stare pentru autocomplete la @mentiuni
|
||||
var mentionMatches = [];
|
||||
var mentionActiveIndex = -1;
|
||||
var mentionTokenStart = -1;
|
||||
var mentionTimer = null;
|
||||
|
||||
function fmtTime(unixTs) {
|
||||
var d = new Date(unixTs * 1000);
|
||||
function pad(n) { return (n < 10 ? '0' : '') + n; }
|
||||
@@ -587,6 +642,44 @@
|
||||
return wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Faza 3: evidentiaza "@cuvant" in text (stil, nu neaparat un user real -
|
||||
* verificare simpla, client-side, fara sa validam impotriva bazei de date
|
||||
* la fiecare randare). Daca vreunul dintre ele e chiar username-ul
|
||||
* vizualizatorului, intoarce true - renderMessage() adauga atunci un fundal
|
||||
* pe tot randul, ca "notificare" vizuala (nu exista notificari separate,
|
||||
* gen sunet/browser - in linie cu restul widget-ului, minimalist).
|
||||
*/
|
||||
function renderTextWithMentions(container, text) {
|
||||
var re = /@([A-Za-z0-9_.\-]{2,32})/g;
|
||||
var lastIndex = 0;
|
||||
var match;
|
||||
var mentionsMe = false;
|
||||
|
||||
while ((match = re.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
container.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
|
||||
}
|
||||
|
||||
var mSpan = document.createElement('span');
|
||||
mSpan.className = 'gchat_mention';
|
||||
mSpan.textContent = '@' + match[1];
|
||||
container.appendChild(mSpan);
|
||||
|
||||
if (myUsername && match[1].toLowerCase() === myUsername.toLowerCase()) {
|
||||
mentionsMe = true;
|
||||
}
|
||||
|
||||
lastIndex = re.lastIndex;
|
||||
}
|
||||
|
||||
if (lastIndex < text.length) {
|
||||
container.appendChild(document.createTextNode(text.slice(lastIndex)));
|
||||
}
|
||||
|
||||
return mentionsMe;
|
||||
}
|
||||
|
||||
function renderMessage(row) {
|
||||
var line = document.createElement('div');
|
||||
line.className = 'gchat_msg';
|
||||
@@ -646,9 +739,34 @@
|
||||
return line;
|
||||
}
|
||||
|
||||
// Faza 3: raport de lupta distribuit - link catre berichte.php, cu
|
||||
// topicul raportului (deja in row.msg) ca text vizibil
|
||||
if (row.type === 'report') {
|
||||
var reportBlock = document.createElement('div');
|
||||
reportBlock.className = 'gchat_report_block';
|
||||
|
||||
var reportLink = document.createElement('a');
|
||||
reportLink.href = 'berichte.php?id=' + encodeURIComponent(row.report_id);
|
||||
reportLink.target = '_blank';
|
||||
reportLink.rel = 'noopener';
|
||||
reportLink.className = 'gchat_report_link';
|
||||
reportLink.textContent = '\u2694\ufe0f ' + (row.msg || GCHAT_TXT.reportFallback);
|
||||
reportBlock.appendChild(reportLink);
|
||||
line.appendChild(reportBlock);
|
||||
|
||||
var reportActions = document.createElement('span');
|
||||
reportActions.className = 'gchat_actions';
|
||||
appendModActionLinks(reportActions, row);
|
||||
if (reportActions.childNodes.length) { line.appendChild(reportActions); }
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
var text = document.createElement('span');
|
||||
text.className = 'gchat_text';
|
||||
text.textContent = row.msg;
|
||||
if (renderTextWithMentions(text, row.msg)) {
|
||||
line.classList.add('gchat_msg_mentioned');
|
||||
}
|
||||
line.appendChild(text);
|
||||
|
||||
var editedTag = document.createElement('span');
|
||||
@@ -712,7 +830,11 @@
|
||||
|
||||
var textEl2 = line.querySelector('.gchat_text');
|
||||
var editedTagEl2 = line.querySelector('.gchat_edited_tag');
|
||||
if (textEl2) { textEl2.textContent = row.msg; }
|
||||
if (textEl2) {
|
||||
textEl2.textContent = '';
|
||||
var mentionsMe2 = renderTextWithMentions(textEl2, row.msg);
|
||||
line.classList.toggle('gchat_msg_mentioned', mentionsMe2);
|
||||
}
|
||||
if (editedTagEl2 && parseInt(row.edited, 10) === 1) {
|
||||
editedTagEl2.style.display = 'inline';
|
||||
}
|
||||
@@ -878,6 +1000,125 @@
|
||||
if (field.setSelectionRange) { field.setSelectionRange(pos, pos); }
|
||||
}
|
||||
|
||||
// Faza 3: autocomplete la @mentiuni
|
||||
function currentMentionToken() {
|
||||
var pos = input.selectionStart;
|
||||
var value = input.value;
|
||||
if (pos == null) { return null; }
|
||||
|
||||
var atPos = value.lastIndexOf('@', pos - 1);
|
||||
if (atPos === -1) { return null; }
|
||||
|
||||
// "@" trebuie sa fie inceput de cuvant (spatiu inainte, sau chiar
|
||||
// inceputul mesajului) - altfel un email sau "cuvant@altceva" ar
|
||||
// declansa gresit autocomplete-ul
|
||||
if (atPos > 0 && !/\s/.test(value.charAt(atPos - 1))) { return null; }
|
||||
|
||||
var token = value.slice(atPos + 1, pos);
|
||||
if (/[\s@]/.test(token)) { return null; }
|
||||
|
||||
return { start: atPos, query: token };
|
||||
}
|
||||
|
||||
function closeMentionList() {
|
||||
mentionList.style.display = 'none';
|
||||
mentionList.innerHTML = '';
|
||||
mentionMatches = [];
|
||||
mentionActiveIndex = -1;
|
||||
mentionTokenStart = -1;
|
||||
}
|
||||
|
||||
function renderMentionList() {
|
||||
mentionList.innerHTML = '';
|
||||
|
||||
mentionMatches.forEach(function (u, idx) {
|
||||
var item = document.createElement('div');
|
||||
item.className = 'gchat_mention_item' + (idx === mentionActiveIndex ? ' active' : '');
|
||||
item.textContent = u.username;
|
||||
// mousedown (nu click) - trebuie sa apuce inaintea lui 'blur' de pe input
|
||||
item.addEventListener('mousedown', function (e) {
|
||||
e.preventDefault();
|
||||
selectMention(u.username);
|
||||
});
|
||||
mentionList.appendChild(item);
|
||||
});
|
||||
|
||||
mentionList.style.display = mentionMatches.length ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function selectMention(username) {
|
||||
if (mentionTokenStart === -1) { return; }
|
||||
|
||||
var pos = input.selectionStart;
|
||||
var value = input.value;
|
||||
var before = value.slice(0, mentionTokenStart);
|
||||
var after = value.slice(pos);
|
||||
var inserted = '@' + username + ' ';
|
||||
|
||||
input.value = before + inserted + after;
|
||||
var newPos = (before + inserted).length;
|
||||
input.focus();
|
||||
if (input.setSelectionRange) { input.setSelectionRange(newPos, newPos); }
|
||||
|
||||
closeMentionList();
|
||||
}
|
||||
|
||||
input.addEventListener('input', function () {
|
||||
var token = currentMentionToken();
|
||||
if (!token || token.query.length < 1) {
|
||||
closeMentionList();
|
||||
return;
|
||||
}
|
||||
|
||||
mentionTokenStart = token.start;
|
||||
|
||||
if (mentionTimer) { window.clearTimeout(mentionTimer); }
|
||||
mentionTimer = window.setTimeout(function () {
|
||||
fetch(AJAX_URL + '?f=gchat_search_users&q=' + encodeURIComponent(token.query), { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!data || !data.ok) { return; }
|
||||
|
||||
// token-ul s-ar fi putut schimba intre timp (cerere async) -
|
||||
// verificam ca inca suntem pe acelasi "@cuvant" inainte sa
|
||||
// afisam rezultate posibil deja irelevante
|
||||
var stillSame = currentMentionToken();
|
||||
if (!stillSame || stillSame.start !== mentionTokenStart) { return; }
|
||||
|
||||
mentionMatches = data.users || [];
|
||||
mentionActiveIndex = mentionMatches.length ? 0 : -1;
|
||||
renderMentionList();
|
||||
})
|
||||
.catch(function () { closeMentionList(); });
|
||||
}, 150);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function (e) {
|
||||
if (!mentionMatches.length) { return; }
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
mentionActiveIndex = (mentionActiveIndex + 1) % mentionMatches.length;
|
||||
renderMentionList();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
mentionActiveIndex = (mentionActiveIndex - 1 + mentionMatches.length) % mentionMatches.length;
|
||||
renderMentionList();
|
||||
} else if (e.key === 'Enter' || e.key === 'Tab') {
|
||||
if (mentionActiveIndex >= 0) {
|
||||
e.preventDefault();
|
||||
selectMention(mentionMatches[mentionActiveIndex].username);
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
closeMentionList();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('blur', function () {
|
||||
// delay ca sa apuce mousedown-ul pe un item din lista inainte sa o inchidem
|
||||
window.setTimeout(closeMentionList, 150);
|
||||
});
|
||||
|
||||
function addPollOptionRow(value) {
|
||||
if (pollOptionsBox.children.length >= MAX_POLL_OPTIONS) { return; }
|
||||
|
||||
|
||||
@@ -248,6 +248,40 @@ switch(isset($_GET['f']) ? $_GET['f'] : '') {
|
||||
echo json_encode($database->voteGlobalChatPoll($uid, $_POST['pollId'] ?? 0, $_POST['option'] ?? -1));
|
||||
break;
|
||||
|
||||
// Faza 3 (09.09.2026): autocomplete la @mentiuni - cautare useri dupa
|
||||
// inceputul username-ului. GET, nu necesita alta permisiune decat login.
|
||||
case 'gchat_search_users':
|
||||
header('Content-Type: application/json');
|
||||
if (!isset($_SESSION)) {
|
||||
session_start();
|
||||
}
|
||||
include_once($autoprefix.'GameEngine/Database.php');
|
||||
$uid = (int) ($_SESSION['id_user'] ?? 0);
|
||||
if (!$uid) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['ok' => 0, 'reason' => 'notloggedin']);
|
||||
break;
|
||||
}
|
||||
echo json_encode(['ok' => 1, 'users' => $database->searchGlobalChatUsers($_GET['q'] ?? '')]);
|
||||
break;
|
||||
|
||||
// Faza 3 (09.09.2026): distribuie un raport de lupta propriu in chat.
|
||||
// Verificarea de tip+proprietate se face in Database::shareGlobalChatReport().
|
||||
case 'gchat_share_report':
|
||||
header('Content-Type: application/json');
|
||||
if (!isset($_SESSION)) {
|
||||
session_start();
|
||||
}
|
||||
include_once($autoprefix.'GameEngine/Database.php');
|
||||
$uid = (int) ($_SESSION['id_user'] ?? 0);
|
||||
if (!$uid) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['ok' => 0, 'reason' => 'notloggedin']);
|
||||
break;
|
||||
}
|
||||
echo json_encode($database->shareGlobalChatReport($uid, $_POST['id'] ?? 0));
|
||||
break;
|
||||
|
||||
case 'gchat_mute':
|
||||
case 'gchat_block':
|
||||
case 'gchat_unmute':
|
||||
|
||||
@@ -157,6 +157,8 @@ if (isset($_GET['t']) && (int) $_GET['t'] === 3) {
|
||||
<?php
|
||||
if (isset($_GET['id']))
|
||||
{
|
||||
$tzShareEligible = false;
|
||||
|
||||
if (isset($_GET['aid']) && $_GET['aid'] > 0 && $_GET['aid'] == $session->alliance && $database->getNotice2($_GET['id'], 'ally') == $session->alliance)
|
||||
{
|
||||
$type = $database->getNotice2($_GET['id'], 'ntype');
|
||||
@@ -170,9 +172,40 @@ if (isset($_GET['id']))
|
||||
elseif($database->getNotice2(preg_replace("/[^a-zA-Z0-9_-]/", "", $_GET['id']), 'uid') == $session->uid)
|
||||
{
|
||||
$type = ($message->readingNotice['ntype'] == 9) ? $message->readingNotice['archive'] : $message->readingNotice['ntype'];
|
||||
|
||||
// Faza 3 Global Chat (09.09.2026): doar proprietarul vazand propriul
|
||||
// raport poate primi butonul de distribuire, si doar rapoarte de
|
||||
// LUPTA - atac SAU aparare (cand te ataca altcineva pe tine, raportul
|
||||
// TAU are tot un ntype din acelasi set - t=3/TZ_ATTACKS din
|
||||
// Message::noticeType() include deja ambele roluri). Verificam ntype-ul
|
||||
// ORIGINAL, nefiltrat (nu $type, care mai sus poate fi deja remapat pe
|
||||
// arhiva). Tine sincron cu $shareableNtypes din
|
||||
// Database::shareGlobalChatReport().
|
||||
$tzShareableNtypes = [1, 2, 3, 4, 5, 6, 7, 22, 23];
|
||||
$tzOwnNtype = $message->readingNotice['ntype'] ?? null;
|
||||
if ($tzOwnNtype !== null && in_array((int) $tzOwnNtype, $tzShareableNtypes, true)) {
|
||||
$tzShareEligible = true;
|
||||
}
|
||||
}
|
||||
elseif ($database->isGlobalChatSharedReport($_GET['id']))
|
||||
{
|
||||
// Faza 3 Global Chat (09.09.2026): raport distribuit explicit in
|
||||
// chat-ul general de catre proprietarul lui - vizibil oricui e logat,
|
||||
// indiferent de alianta. Acelasi filtru de siguranta ca la share-ul
|
||||
// de alianta mai sus (10-17 raman ascunse), desi in practica
|
||||
// Database::shareGlobalChatReport() deja refuza sa distribuie orice
|
||||
// in afara de ntype 1-7 - dublam verificarea aici din prudenta.
|
||||
$type = $database->getNotice2($_GET['id'], 'ntype');
|
||||
if ($type >= 10 && $type <= 17) unset($type);
|
||||
}
|
||||
|
||||
if(isset($type)) include("Templates/Notice/".$message->getReportType($type).".tpl");
|
||||
|
||||
// Faza 3 Global Chat: butonul "Distribuie in chat", doar cand eligibil (vezi mai sus)
|
||||
if ($tzShareEligible) {
|
||||
include("Templates/GlobalChat/share_report_button.tpl");
|
||||
}
|
||||
|
||||
unset($type);
|
||||
}
|
||||
else include("Templates/Notice/all.tpl");
|
||||
|
||||
@@ -595,6 +595,7 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%chat_global` (
|
||||
`msg` varchar(250) NOT NULL,
|
||||
`type` varchar(10) NOT NULL DEFAULT 'text',
|
||||
`poll_id` int(20) NULL DEFAULT NULL,
|
||||
`report_id` int(20) NULL DEFAULT NULL,
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`edited` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`updated_at` int(11) NULL DEFAULT NULL,
|
||||
@@ -658,6 +659,24 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%chat_global_poll_votes` (
|
||||
KEY `poll_id` (`poll_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Faza 3 (09.09.2026): allowlist de rapoarte distribuite in chat-ul general.
|
||||
-- NU atingem tabela `ndata` (folosita de tot sistemul de mesaje/rapoarte) -
|
||||
-- in loc, un raport prezent aici e vizibil oricui e logat, indiferent de
|
||||
-- alianta (vezi berichte.php, verificarea de acces). notice_id e PRIMARY
|
||||
-- KEY: un raport se distribuie o singura data in allowlist (re-distribuirea
|
||||
-- doar posteaza un mesaj nou in chat, vezi Database::shareGlobalChatReport).
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `%PREFIX%chat_global_shared_reports` (
|
||||
`notice_id` int(11) NOT NULL,
|
||||
`shared_by` int(11) NOT NULL,
|
||||
`created` int(11) NOT NULL,
|
||||
PRIMARY KEY (`notice_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user