Compare commits
9 Commits
abce2e03d1
...
6337350b60
| Author | SHA1 | Date | |
|---|---|---|---|
| 6337350b60 | |||
| 986acfac38 | |||
| b27149b5ba | |||
| eb1284060a | |||
| 4266ac1f12 | |||
| 6b4709d011 | |||
| ef8d3068a8 | |||
|
|
a23c14389b | ||
|
|
6dfd8db0a6 |
@ -1,5 +1,5 @@
|
||||
from snek.system.service import BaseService
|
||||
from snek.system.template import whitelist_attributes
|
||||
from snek.system.template import sanitize_html
|
||||
import time
|
||||
|
||||
class ChannelMessageService(BaseService):
|
||||
@ -69,10 +69,10 @@ class ChannelMessageService(BaseService):
|
||||
"color": user["color"],
|
||||
}
|
||||
)
|
||||
context['message'] = whitelist_attributes(context['message'])
|
||||
try:
|
||||
template = self.app.jinja2_env.get_template("message.html")
|
||||
model["html"] = template.render(**context)
|
||||
model['html'] = sanitize_html(model['html'])
|
||||
except Exception as ex:
|
||||
print(ex, flush=True)
|
||||
|
||||
@ -118,7 +118,6 @@ class ChannelMessageService(BaseService):
|
||||
async def save(self, model):
|
||||
context = {}
|
||||
context.update(model.record)
|
||||
context['message'] = whitelist_attributes(context['message'])
|
||||
user = await self.app.services.user.get(model["user_uid"])
|
||||
context.update(
|
||||
{
|
||||
@ -130,6 +129,7 @@ class ChannelMessageService(BaseService):
|
||||
)
|
||||
template = self.app.jinja2_env.get_template("message.html")
|
||||
model["html"] = template.render(**context)
|
||||
model['html'] = sanitize_html(model['html'])
|
||||
return await super().save(model)
|
||||
|
||||
async def offset(self, channel_uid, page=0, timestamp=None, page_size=30):
|
||||
|
||||
@ -3,8 +3,15 @@ export class EventHandler {
|
||||
this.subscribers = {};
|
||||
}
|
||||
|
||||
addEventListener(type, handler) {
|
||||
addEventListener(type, handler, { once = false } = {}) {
|
||||
if (!this.subscribers[type]) this.subscribers[type] = [];
|
||||
if (once) {
|
||||
const originalHandler = handler;
|
||||
handler = (...args) => {
|
||||
originalHandler(...args);
|
||||
this.removeEventListener(type, handler);
|
||||
};
|
||||
}
|
||||
this.subscribers[type].push(handler);
|
||||
}
|
||||
|
||||
@ -12,4 +19,15 @@ export class EventHandler {
|
||||
if (this.subscribers[type])
|
||||
this.subscribers[type].forEach((handler) => handler(...data));
|
||||
}
|
||||
|
||||
removeEventListener(type, handler) {
|
||||
if (!this.subscribers[type]) return;
|
||||
this.subscribers[type] = this.subscribers[type].filter(
|
||||
(h) => h !== handler
|
||||
);
|
||||
|
||||
if (this.subscribers[type].length === 0) {
|
||||
delete this.subscribers[type];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,17 +57,6 @@ export class ReplyEvent extends Event {
|
||||
class MessageElement extends HTMLElement {
|
||||
// static observedAttributes = ['data-uid', 'data-color', 'data-channel_uid', 'data-user_nick', 'data-created_at', 'data-user_uid'];
|
||||
|
||||
isVisible() {
|
||||
if (!this) return false;
|
||||
const rect = this.getBoundingClientRect();
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
if (this._originalChildren === undefined) {
|
||||
const { color, user_nick, created_at, user_uid} = this.dataset;
|
||||
@ -104,8 +93,8 @@ class MessageElement extends HTMLElement {
|
||||
})
|
||||
}
|
||||
|
||||
if (!this.siblingGenerated && this.nextElementSibling) {
|
||||
this.siblingGenerated = true;
|
||||
if ((!this.siblingGenerated || this.siblingGenerated !== this.nextElementSibling) && this.nextElementSibling) {
|
||||
this.siblingGenerated = this.nextElementSibling;
|
||||
if (this.nextElementSibling?.dataset?.user_uid !== this.dataset.user_uid) {
|
||||
this.classList.add('switch-user');
|
||||
} else {
|
||||
@ -177,6 +166,10 @@ class MessageList extends HTMLElement {
|
||||
threshold: 0,
|
||||
})
|
||||
|
||||
this.endOfMessages = document.createElement('div');
|
||||
this.endOfMessages.classList.add('message-list-bottom');
|
||||
this.prepend(this.endOfMessages);
|
||||
|
||||
for(const c of this.children) {
|
||||
this._observer.observe(c);
|
||||
if (c instanceof MessageElement) {
|
||||
@ -184,10 +177,6 @@ class MessageList extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
this.endOfMessages = document.createElement('div');
|
||||
this.endOfMessages.classList.add('message-list-bottom');
|
||||
this.prepend(this.endOfMessages);
|
||||
|
||||
this.scrollToBottom(true);
|
||||
}
|
||||
|
||||
@ -243,13 +232,13 @@ class MessageList extends HTMLElement {
|
||||
);
|
||||
}
|
||||
isScrolledToBottom() {
|
||||
return this.isElementVisible(this.endOfMessages);
|
||||
return this.visibleSet.has(this.endOfMessages)
|
||||
}
|
||||
scrollToBottom(force = false, behavior= 'instant') {
|
||||
if (force || !this.isScrolledToBottom()) {
|
||||
this.firstElementChild.scrollIntoView({ behavior, block: 'end' });
|
||||
this.endOfMessages.scrollIntoView({ behavior, block: 'end' });
|
||||
setTimeout(() => {
|
||||
this.firstElementChild.scrollIntoView({ behavior, block: 'end' });
|
||||
this.endOfMessages.scrollIntoView({ behavior, block: 'end' });
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
@ -302,7 +291,7 @@ class MessageList extends HTMLElement {
|
||||
}
|
||||
|
||||
const scrolledToBottom = this.isScrolledToBottom();
|
||||
this.prepend(message);
|
||||
this.endOfMessages.after(message);
|
||||
if (scrolledToBottom) this.scrollToBottom(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,10 +142,9 @@ export class Socket extends EventHandler {
|
||||
method,
|
||||
args,
|
||||
};
|
||||
const me = this;
|
||||
return new Promise((resolve) => {
|
||||
me.addEventListener(call.callId, (data) => resolve(data));
|
||||
me.sendJson(call);
|
||||
this.addEventListener(call.callId, (data) => resolve(data), { once: true});
|
||||
this.sendJson(call);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +82,32 @@ emoji.EMOJI_DATA[
|
||||
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + ["picture"]
|
||||
|
||||
def sanitize_html(value):
|
||||
|
||||
soup = BeautifulSoup(value, 'html.parser')
|
||||
|
||||
for script in soup.find_all('script'):
|
||||
script.decompose()
|
||||
|
||||
#for iframe in soup.find_all('iframe'):
|
||||
#iframe.decompose()
|
||||
|
||||
for tag in soup.find_all(['object', 'embed']):
|
||||
tag.decompose()
|
||||
|
||||
for tag in soup.find_all():
|
||||
event_attributes = ['onclick', 'onerror', 'onload', 'onmouseover', 'onfocus']
|
||||
for attr in event_attributes:
|
||||
if attr in tag.attrs:
|
||||
del tag[attr]
|
||||
|
||||
for img in soup.find_all('img'):
|
||||
if 'onerror' in img.attrs:
|
||||
img.decompose()
|
||||
|
||||
return soup.prettify()
|
||||
|
||||
|
||||
def sanitize_html2(value):
|
||||
return bleach.clean(
|
||||
value,
|
||||
protocols=list(bleach.sanitizer.ALLOWED_PROTOCOLS) + ["data"],
|
||||
|
||||
@ -12,7 +12,7 @@ function showTerm(options){
|
||||
|
||||
|
||||
class StarField {
|
||||
constructor({ count = 100, container = document.body } = {}) {
|
||||
constructor({ count = 50, container = document.body } = {}) {
|
||||
this.container = container;
|
||||
this.starCount = count;
|
||||
this.stars = [];
|
||||
@ -567,7 +567,7 @@ const count = Array.from(messages).filter(el => el.textContent.trim() === text).
|
||||
|
||||
|
||||
|
||||
const starField = new StarField({starCount: 100});
|
||||
const starField = new StarField({starCount: 50});
|
||||
app.starField = starField;
|
||||
|
||||
class DemoSequence {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user