Commit 5474a729 authored by Dan Pascu's avatar Dan Pascu

Automatically transform URLs in the chat window into clickable links

parent 4f7b4dbf
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
__all__ = ['ChatWindow'] __all__ = ['ChatWindow']
import os import os
import re
from PyQt4 import uic from PyQt4 import uic
from PyQt4.QtCore import Qt, QEasingCurve, QEvent, QPointF, QPropertyAnimation, QRect, QSettings, QTimer, pyqtSignal from PyQt4.QtCore import Qt, QEasingCurve, QEvent, QPointF, QPropertyAnimation, QRect, QSettings, QTimer, pyqtSignal
...@@ -17,7 +18,8 @@ from application.python import Null ...@@ -17,7 +18,8 @@ from application.python import Null
from application.python.types import MarkerType from application.python.types import MarkerType
from collections import MutableSet from collections import MutableSet
from datetime import datetime, timedelta from datetime import datetime, timedelta
from lxml import etree from lxml import etree, html
from lxml.html.clean import autolink
from weakref import proxy from weakref import proxy
from zope.interface import implements from zope.interface import implements
...@@ -607,7 +609,8 @@ class ChatWidget(base_class, ui_class): ...@@ -607,7 +609,8 @@ class ChatWidget(base_class, ui_class):
uri = account.id uri = account.id
icon = IconManager().get('avatar') or self.default_user_icon icon = IconManager().get('avatar') or self.default_user_icon
sender = ChatSender(display_name, uri, icon.filename) sender = ChatSender(display_name, uri, icon.filename)
self.add_message(ChatMessage(text, sender, 'outgoing')) content = HtmlProcessor.autolink(text)
self.add_message(ChatMessage(content, sender, 'outgoing'))
def _SH_ComposingTimerTimeout(self): def _SH_ComposingTimerTimeout(self):
self.composing_timer.stop() self.composing_timer.stop()
...@@ -1243,7 +1246,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -1243,7 +1246,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
sender = ChatSender(message.sender.display_name or contact.name, uri, contact.icon.filename) sender = ChatSender(message.sender.display_name or contact.name, uri, contact.icon.filename)
else: else:
sender = ChatSender(message.sender.display_name or session.name, uri, session.icon.filename) sender = ChatSender(message.sender.display_name or session.name, uri, session.icon.filename)
content = message.body if message.content_type=='text/html' else QTextDocument(message.body).toHtml() content = HtmlProcessor.autolink(message.body if message.content_type=='text/html' else QTextDocument(message.body).toHtml())
session.chat_widget.add_message(ChatMessage(content, sender, 'incoming')) session.chat_widget.add_message(ChatMessage(content, sender, 'incoming'))
session.remote_composing = False session.remote_composing = False
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
...@@ -1485,6 +1488,36 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -1485,6 +1488,36 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
del ui_class, base_class del ui_class, base_class
# Helpers
#
class HtmlProcessor(object):
_autolink_re = [#re.compile(r"(?P<body>https?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)?(?:/[\w/%!$@*&='~():;,.+-]*(?:\?[\w%!$@*&='~():;,.+-]*)?)?)", re.I|re.U),
re.compile(r"""
(?P<body>
https?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)? # scheme :// [ user [ : password ] @ ] host [ : port ]
(?:/(?:[\w/%!$@*&='~:;,.+-]*(?:\([\w/%!$@*&='~:;,.+-]*\))?)*)? # [ / path]
(?:\?(?:[\w%!$@*&='~:;,.+-]*(?:\([\w%!$@*&='~:;,.+-]*\))?)*)? # [ ? query]
)
""", re.I|re.U|re.X),
re.compile(r"(?P<body>ftps?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)?(?:/(?:[\w/%!?$@*&='~:,.+-]*(?:\([\w/%!?$@*&='~:,.+-]*\))?)*(?:;type=[aid])?)?)", re.I|re.U),
re.compile(r'mailto:(?P<body>[\w.-]+@(?P<host>[a-z0-9.-]+))', re.I|re.U)]
@classmethod
def autolink(cls, content):
if isinstance(content, basestring):
doc = html.fromstring(content)
autolink(doc, link_regexes=cls._autolink_re)
return html.tostring(doc, encoding='unicode') # add method='xml' to get <br/> xhtml style tags and doctype=doc.getroottree().docinfo.doctype for prepending the DOCTYPE line
else:
autolink(content, link_regexes=cls._autolink_re)
return content
@classmethod
def normalize(cls, content):
return content
class TrafficNormalizer(object): class TrafficNormalizer(object):
boundaries = [( 1024, '%d%ss', 1), boundaries = [( 1024, '%d%ss', 1),
( 10*1024, '%.2fk%ss', 1024.0), ( 1024*1024, '%.1fk%ss', 1024.0), ( 10*1024, '%.2fk%ss', 1024.0), ( 1024*1024, '%.1fk%ss', 1024.0),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment