Security Fixes
This commit is contained in:
parent
aa7a7bc9fc
commit
a0eebfcb3f
14 changed files with 279 additions and 37 deletions
|
|
@ -519,7 +519,7 @@
|
|||
|
||||
function renderActivity(activity) {
|
||||
// Header
|
||||
document.getElementById('activityTitle').textContent = activity.title || 'Untitled Activity';
|
||||
document.getElementById('activityTitle').innerHTML = linkifyHashtags(activity.title || 'Untitled Activity');
|
||||
document.getElementById('activityType').textContent = activity.activityType;
|
||||
document.getElementById('activityType').className = `activity-type-badge activity-type-${activity.activityType.toLowerCase()}`;
|
||||
// Format date with timezone awareness
|
||||
|
|
@ -570,7 +570,7 @@
|
|||
|
||||
// Description
|
||||
if (activity.description) {
|
||||
document.getElementById('activityDescription').textContent = activity.description;
|
||||
document.getElementById('activityDescription').innerHTML = linkifyHashtags(activity.description);
|
||||
} else {
|
||||
document.getElementById('activityDescription').style.display = 'none';
|
||||
}
|
||||
|
|
@ -1721,6 +1721,14 @@
|
|||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function linkifyHashtags(text) {
|
||||
if (!text) return '';
|
||||
const escaped = escapeHtml(text);
|
||||
return escaped.replace(/(^|\s)#(\w+)/g, (match, lead, tag) =>
|
||||
`${lead}<a href="/timeline?hashtag=${encodeURIComponent(tag.toLowerCase())}" class="hashtag-link">#${tag}</a>`
|
||||
);
|
||||
}
|
||||
|
||||
// Delete functionality
|
||||
document.getElementById('deleteBtn').addEventListener('click', function() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
|
||||
|
|
|
|||
|
|
@ -152,9 +152,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h5 class="card-title">
|
||||
<a href="/activities/${activity.id}" class="text-decoration-none">
|
||||
${escapeHtml(activity.title || 'Untitled Activity')}
|
||||
</a>
|
||||
${renderTitleLinkWithHashtags(activity.title, `/activities/${activity.id}`)}
|
||||
</h5>
|
||||
<p class="text-muted mb-2">
|
||||
<span class="activity-type-badge activity-type-${activity.activityType.toLowerCase()}${activity.race ? ' race-activity' : ''}">
|
||||
|
|
@ -181,7 +179,7 @@
|
|||
${activity.visibility}
|
||||
</span>
|
||||
</p>
|
||||
${activity.description ? `<p class="card-text">${escapeHtml(activity.description).substring(0, 150)}${activity.description.length > 150 ? '...' : ''}</p>` : ''}
|
||||
${activity.description ? `<p class="card-text">${linkifyHashtags(activity.description.length > 150 ? activity.description.substring(0, 150) + '...' : activity.description)}</p>` : ''}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<small class="text-muted">
|
||||
|
|
@ -332,6 +330,35 @@
|
|||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function linkifyHashtags(text) {
|
||||
if (!text) return '';
|
||||
const escaped = escapeHtml(text);
|
||||
return escaped.replace(/(^|\s)#(\w+)/g, (match, lead, tag) =>
|
||||
`${lead}<a href="/timeline?hashtag=${encodeURIComponent(tag.toLowerCase())}" class="hashtag-link">#${tag}</a>`
|
||||
);
|
||||
}
|
||||
|
||||
function renderTitleLinkWithHashtags(text, activityHref) {
|
||||
const safeText = text || 'Untitled Activity';
|
||||
const wrap = (chunk) => chunk
|
||||
? `<a href="${activityHref}" class="text-decoration-none activity-title-link">${chunk}</a>`
|
||||
: '';
|
||||
const parts = [];
|
||||
const regex = /(^|\s)#(\w+)/g;
|
||||
let last = 0;
|
||||
let m;
|
||||
while ((m = regex.exec(safeText)) !== null) {
|
||||
const before = safeText.substring(last, m.index) + m[1];
|
||||
if (before) parts.push(wrap(escapeHtml(before)));
|
||||
const tag = m[2];
|
||||
parts.push(`<a href="/timeline?hashtag=${encodeURIComponent(tag.toLowerCase())}" class="hashtag-link">#${escapeHtml(tag)}</a>`);
|
||||
last = m.index + m[0].length;
|
||||
}
|
||||
const tail = safeText.substring(last);
|
||||
if (tail) parts.push(wrap(escapeHtml(tail)));
|
||||
return parts.join('');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
|
|
|
|||
|
|
@ -62,6 +62,18 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active hashtag filter -->
|
||||
<div id="hashtagFilterBadge" class="alert alert-info d-flex justify-content-between align-items-center d-none" role="alert">
|
||||
<span>
|
||||
<i class="bi bi-hash"></i>
|
||||
Filtering by <strong id="hashtagFilterLabel">#tag</strong>
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="FitPubTimeline.clearHashtagFilter(); return false;">
|
||||
<i class="bi bi-x-lg"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading Indicator -->
|
||||
<div id="loadingIndicator" class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue