Changes in / [e9ec5e4:fbda20e] in Klonkt
- Files:
-
- 1 added
- 1 edited
-
src/services/ActivityPubService.js (modified) (11 diffs)
-
test/summary-not-a-warning.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
re9ec5e4 rfbda20e 280 280 * Funkwhale hoeft te weten. 281 281 */ 282 /** 283 * De waarschuwingstekst van een object, of niets. 284 * 285 * `summary` IS in AS2 een SAMENVATTING -- "a natural language summarization of 286 * the object". Dat Mastodon dat veld hergebruikt als waarschuwing is Mastodons 287 * conventie, en die zet er `sensitive` bij. Zonder `sensitive` is een summary 288 * dus gewoon een samenvatting. 289 * 290 * WordPress + ActivityPub stuurt daar de EXCERPT van een artikel in, netjes 291 * afgekapt voor Mastodon. Wij lazen dat als waarschuwing en verborgen de post 292 * daarmee achter zijn eigen eerste alinea (Barts melding, 13-8: 293 * europeanpirates.eu). Niemand krijgt dan te zien wat er staat, en de 294 * waarschuwing waarschuwt nergens voor. 295 */ 296 export function contentWarning(o) { 297 if (!o || !o.sensitive) return null; 298 const s = typeof o.summary === 'string' ? o.summary.trim() : ''; 299 return s || null; 300 } 301 282 302 export function timelineFields(o) { 283 303 // De hoes: een `image` op het object. Bij een Note alleen als terugval (daar … … 308 328 } 309 329 310 // Note / Article / Question -- ongewijzigd gedrag. 330 // Een ARTIKEL heeft een titel, en die is het eerste wat je wilt zien. Zonder 331 // dit kwam een WordPress-post binnen als kale body: de titel zit in `name` en 332 // die gooiden we weg, terwijl de excerpt in `summary` ten onrechte als 333 // waarschuwing dienstdeed. Nu allebei goed -- en dit is dezelfde greep die 334 // resolveRemoteNote al doet voor niet-Note-objecten, dus de tijdlijn en het 335 // antwoordpad zeggen eindelijk hetzelfde. 336 if (o.type && o.type !== 'Note' && typeof o.name === 'string' && o.name.trim()) { 337 const kop = `<p><strong>${HtmlSanitizerService.escape ? HtmlSanitizerService.escape(o.name) : o.name}</strong></p>`; 338 const atts = mediaFromNote(o); 339 const pagina = pickLink(o.url, (mt) => !mt || /html/i.test(mt)); 340 return { 341 html: HtmlSanitizerService.sanitize(kop + (o.content || '')), 342 atts, 343 url: pagina ? pagina.href : null, 344 }; 345 } 346 347 // Note / Question -- ongewijzigd gedrag. 311 348 const atts = (Array.isArray(o.attachment) ? o.attachment : []) 312 349 .map((a) => ({ url: safeUrl(a && a.url), type: (a && a.mediaType) || '' })) … … 2463 2500 // the timeline). 2464 2501 for (const s of subs) { 2465 tlStmts().ins.run(o.id, s.slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, _url, o.published || null, media, o.sensitive ? 1 : 0, o.summary || null);2502 tlStmts().ins.run(o.id, s.slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, _url, o.published || null, media, o.sensitive ? 1 : 0, contentWarning(o)); 2466 2503 // FEP-633c ยง2.2: register the ward hint on the stored object (no action yet). 2467 2504 if (Guardianship.objectHasGuardians(o)) { try { db.prepare('UPDATE ap_timeline SET has_guardians = 1 WHERE id = ? AND slug = ?').run(o.id, s.slug); } catch { /* ignore */ } } … … 2615 2652 // post would keep linking to the old, now-dead URL. 2616 2653 const r = db.prepare('UPDATE ap_timeline SET content = ?, media_json = ?, nsfw = ?, cw = ?, url = COALESCE(?, url) WHERE id = ? AND author_uri = ?') 2617 .run(html, media, o.sensitive ? 1 : 0, o.summary || null, o.url || null, o.id, claimedActor);2654 .run(html, media, o.sensitive ? 1 : 0, contentWarning(o), o.url || null, o.id, claimedActor); 2618 2655 if (r.changes) console.log('[AP] timeline update', claimedActor, 'โ', o.id); 2619 2656 // A poll's Update carries the fresh vote counts / closed state. Refresh per-row so each … … 2679 2716 // have the note (e.g. we also follow the author), keep it and DON'T relabel it. 2680 2717 let inserted = false; 2681 try { const r = tlStmts().ins.run(bn.id, s.slug, origUri || '', oai.name, oai.handle, oai.icon, oai.url, html, bn.url || null, new Date().toISOString(), media, bn.sensitive ? 1 : 0, bn.summary || null); inserted = r.changes > 0; } catch { /* ignore */ }2718 try { const r = tlStmts().ins.run(bn.id, s.slug, origUri || '', oai.name, oai.handle, oai.icon, oai.url, html, bn.url || null, new Date().toISOString(), media, bn.sensitive ? 1 : 0, contentWarning(bn)); inserted = r.changes > 0; } catch { /* ignore */ } 2682 2719 if (inserted) { try { db.prepare('UPDATE ap_timeline SET reblog_name = ?, reblog_handle = ?, reblog_icon = ?, reblog_emoji_json = ? WHERE slug = ? AND id = ?').run(booster.name, booster.handle, booster.icon, (booster.emojis && Object.keys(booster.emojis).length) ? JSON.stringify(booster.emojis) : null, s.slug, bn.id); } catch { /* ignore */ } } 2683 2720 storeAuthorEmoji(bn.id, s.slug, oai); // custom-emoji display name for the byline … … 3842 3879 published: typeof o.published === 'string' ? o.published : undefined, 3843 3880 sensitive: !!o.sensitive, 3844 summary: typeof o.summary === 'string' ? o.summary.slice(0, 500) :undefined,3881 summary: (contentWarning(o) || '').slice(0, 500) || undefined, 3845 3882 attachment: (() => { 3846 3883 const arr = Array.isArray(o.attachment) ? o.attachment : (o.attachment ? [o.attachment] : []); … … 3958 3995 content: HtmlSanitizerService.sanitize(rawHtml), // full, sanitized 3959 3996 sensitive: !!note.sensitive, // remote CW โ blur in the Cirkel 3960 cw: note.summary|| '',3997 cw: contentWarning(note) || '', 3961 3998 images, 3962 3999 // Full typed media (incl. video/mp4) for the timeline cache. `images` above is … … 4988 5025 // during a flux window, e.g. a fleet-wide update), and drops notes that are gone 4989 5026 // (404/410). Bump SELFHEAL_VERSION only on a release that warrants a re-sync. 4990 const SELFHEAL_VERSION = 2 1; // v21: drop direct notes (๐ help requests, waves) that were cached as timeline posts5027 const SELFHEAL_VERSION = 22; // v22: summary is pas een waarschuwing MET sensitive, en een artikel houdt zijn titel 4991 5028 async function fetchNoteAP(url) { 4992 5029 try { … … 5375 5412 const poll = parsePoll(o); // a Question (poll) โ carry its options/counts on backfill too 5376 5413 try { 5377 const r = tlStmts().ins.run(o.id, slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, o.url || null, o.published || null, mediaFromNote(o), o.sensitive ? 1 : 0, o.summary || null);5414 const r = tlStmts().ins.run(o.id, slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, o.url || null, o.published || null, mediaFromNote(o), o.sensitive ? 1 : 0, contentWarning(o)); 5378 5415 if (r && r.changes > 0) added++; 5379 5416 // FEP-9098: keep custom-emoji tags from backfilled posts too. … … 5534 5571 if (note === 404) { db.prepare('DELETE FROM ap_timeline WHERE id = ?').run(r.id); healed++; continue; } 5535 5572 if (!note || typeof note !== 'object') { failed++; continue; } // origin unreachable right now 5536 const html = HtmlSanitizerService.sanitize(note.content || ''); 5537 const media = mediaFromNote(note); 5573 // Door DEZELFDE bouwer als de innamekant (v22). Hij bouwde de inhoud 5574 // hier zelf op, en daardoor miste een gerepareerde rij precies wat de 5575 // inname wel doet -- de titel van een artikel bijvoorbeeld. Een 5576 // zelfherstel dat een andere vorm oplevert dan de inname repareert naar 5577 // een derde toestand. 5578 const velden = timelineFields(note); 5579 const html = velden.html; 5580 const media = velden.atts.length ? JSON.stringify(velden.atts) : mediaFromNote(note); 5538 5581 const nsfw = note.sensitive ? 1 : 0; // re-sync NSFW/sensitive + CW onto already-cached posts 5539 const cw = note.summary || null;5582 const cw = contentWarning(note); 5540 5583 const url = note.url || null; // re-sync the human url (catches a remote slug rename) 5541 5584 const emoji = extractEmojiTags(note.tag); // FEP-9098: re-capture custom-emoji tags (v8) … … 6590 6633 getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, buildReplyNote, getOutboxNote, getSentNotes, deliverReply, resolveRemoteNote, noteAudience, mayReadNote, 6591 6634 listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote, 6592 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, isoStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll,6635 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, contentWarning, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, isoStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll, 6593 6636 acceptGatedFollow, rejectGatedFollow, isWardGuardian, outboxAudience, sendFollowDecision, 6594 6637 gateOutgoingFollow, performApprovedFollow, recordGuardianEvent, listGuardianEvents, GUARDIAN_EVENT_KEEP,
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)