]> git.ipfire.org Git - ipfire.org.git/commitdiff
talk: Update for Kamailio
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 19 Apr 2015 11:14:54 +0000 (13:14 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 19 Apr 2015 11:14:54 +0000 (13:14 +0200)
12 files changed:
templates/modules/menu.html
templates/talk/diagnosis.html
templates/talk/index.html
templates/talk/modules/call-log.html
templates/talk/modules/lines.html [new file with mode: 0644]
templates/talk/modules/ongoing-calls.html
templates/talk/phonebook-contact.html
templates/talk/trunks.html [deleted file]
webapp/__init__.py
webapp/backend/talk.py
webapp/handlers_talk.py
webapp/ui_modules.py

index 66df0d2fb527cd98d62787b0687318de3d8af68e..a36f338bf4176577a5b0f0a1ee7824fc2fce2203 100644 (file)
                        <a href="/conferences">{{ _("Conferences") }}</a>
                </li>
 
-               {% if current_user and current_user.is_admin() %}
-                       <li>
-                               <a href="/trunks">{{ _("Trunks") }}</a>
-                       </li>
-               {% end %}
+               <li>
+                       <a href="http://wiki.ipfire.org/en/community/talk.ipfire.org/start">
+                               {{ _("Documentation") }}
+                       </a>
+               </li>
 
                <li>
                        <a href="/diagnosis">{{ _("Diagnosis") }}</a>
index a56ff244b6be5b3c79479831add17d9de451fed4..a609e7bae0c20da7a2ec15a3dafabea8d54fef06 100644 (file)
@@ -4,11 +4,11 @@
 
 {% block body %}
        <div class="page-header">
-               <h3>{{ _("Diagnosis") }}</h3>
+               <h2>{{ _("Diagnosis") }}</h2>
        </div>
 
        <section id="test-call">
-               <h4>{{ _("Test Call") }}</h4>
+               <h3>{{ _("Test Call") }}</h3>
 
                <div class="well">
                        <span class="glyphicon glyphicon-earphone text-success"></span>
                        - {{ _("A music playing service") }}
                </div>
        </section>
+
+       {% if current_user.is_admin() %}
+               <section id="lines">
+                       {% module TalkLines(show_account=True) %}
+               </section>
+       {% end %}
 {% end block %}
index 1c79b1856edf8221934d8e524fb2ea17da812c06..e2bed372abb331bcd8b0d1ddb7903bfccb8b7dd8 100644 (file)
                </div>
        {% end %}
 
-       {% if ongoing_calls %}
-               <h4>{{ _("Ongoing Calls") }}</h4>
-               {% module TalkOngoingCalls(ongoing_calls) %}
-       {% end %}
+       {% module TalkOngoingCalls(current_user) %}
 
-       {% if call_log %}
-               <h4>{{ _("Call Log") }}</h4>
-               {% module TalkCallLog(call_log) %}
-       {% end %}
+       {% module TalkCallLog(current_user) %}
 {% end block %}
index d3e8b5ace5a14d5a9d44d324f0bbee899f9e3af6..b39a139b1a8561ea7f9b70fb1b394c6f6f6f4d61 100644 (file)
@@ -1,3 +1,5 @@
+<h3>{{ _("Call Log") }}</h3>
+
 <table class="table table-condensed table-hover table-striped">
        <thead>
                <tr>
        </thead>
        <tbody>
                {% for entry in calls %}
-                       {% if entry.status == "ringing" and entry.reason == "hangup" %}
-                               <tr class="info">
-                       {% elif entry.status == "ringing" and entry.reason == "Request Terminated" %}
-                               <tr class="danger">                             
-                       {% else %}
+                       {% if entry.sip_code == "200" %}
                                <tr>
+                       {% elif entry.caller == viewer.sip_id %}
+                               <tr class="warning">
+                       {% else %}
+                               <tr class="info">
                        {% end %}
                                <td>{{ locale.format_date(entry.time, full_format=True, relative=False) }}</td>
                                <td>
-                                       {% if entry.direction == "incoming" %}
+                                       {% if entry.called == viewer.sip_id %}
                                                <span class="glyphicon glyphicon-arrow-left text-success" title="{{ _("incoming") }}"></span>
                                                {% if entry.caller_account %}
                                                        <a href="/phonebook/{{ entry.caller_account.uid }}">{{ entry.caller_account.name }}</a>
@@ -25,7 +27,7 @@
                                                {% else %}
                                                        {{ entry.caller }}
                                                {% end %}
-                                       {% elif entry.direction == "outgoing" %}
+                                       {% elif entry.caller == viewer.sip_id %}
                                                <span class="glyphicon glyphicon-arrow-right text-info" title="{{ _("outgoing") }}"></span>
                                                {% if entry.called_account %}
                                                        <a href="/phonebook/{{ entry.called_account.uid }}">{{ entry.called_account.name }}</a>
                                        {% end %}
                                </td>
                                <td>
-                                       {% if entry.status == "ringing" %}
-                                               {% if entry.reason == "hangup" %}
-                                                       {{ _("no answer") }}
-                                               {% elif entry.reason == "Request Terminated" %}
-                                                       {{ _("missed call") }}
-                                               {% end %}
+                                       {% if entry.sip_code == "200" %}
+                                               {{ entry.duration }}
                                        {% else %}
-                                               {{ entry.duration.strftime("%H:%M:%S") }}
+                                               {% if entry.reason == "Busy Here" %}
+                                                       {{ _("Busy") }}
+                                               {% elif entry.reason in ("Cancelled", "Request Cancelled", "Request Timeout") %}
+                                                       {% if entry.caller == viewer.sip_id %}
+                                                               {{ _("no answer") }}
+                                                       {% else %}
+                                                               {{ _("missed call") }}
+                                                       {% end %}
+                                               {% else %}
+                                                       {{ entry.sip_code }} {{ entry.reason }}
+                                               {% end %}
                                        {% end %}
                                </td>
                        </tr>
diff --git a/templates/talk/modules/lines.html b/templates/talk/modules/lines.html
new file mode 100644 (file)
index 0000000..722ee13
--- /dev/null
@@ -0,0 +1,40 @@
+<h3>{{ _("Lines") }}</h3>
+
+<table class="table table-condensed table-hover table-striped">
+       <thead>
+               <tr>
+                       {% if show_account %}
+                               <th>{{ _("Account") }}</th>
+                       {% end %}
+                       <th>{{ _("Expires") }}</th>
+                       <th>{{ _("Location") }}</th>
+                       <th>{{ _("User Agent") }}</th>
+               </tr>
+       </thead>
+       <tbody>
+               {% for line in lines %}
+                       <tr>
+                               {% if show_account %}
+                                       <td>
+                                               {% if line.account %}
+                                                       <a href="/phonebook/{{ line.account.uid }}">{{ line.account.name }}</a>
+                                                               <span class="text-muted">({{ line.account.sip_id }})</span>
+                                                       </a>
+                                               {% else %}
+                                                       {{ line.username }}@{{ line.domain }}
+                                               {% end %}
+                                       </td>
+                               {% end %}
+
+                               <td>{{ locale.format_date(line.expires) }}</td>
+                               <td>
+                                       {% if line.tls_enabled %}
+                                               <span class="glyphicon glyphicon-lock" title="{{ _("TLS") }}"></span>
+                                       {% end %}
+                                       {{ line.location }}
+                               </td>
+                               <td>{{ line.user_agent }}</td>
+                       </tr>
+               {% end %}
+       </tbody>
+</table>
index 885b42b40b35a9f3634cce7ff24b61b71347735b..59b468328318e8e179034e4c56f275a32483a12d 100644 (file)
@@ -1,3 +1,5 @@
+<h3>{{ _("Ongoing Calls") }}</h3>
+
 <table class="table table-hover table-striped">
        <thead>
                <tr>
index 7318e3c673b09f7e7f119bef031d9fd7c933a02f..1fa535e3ea35bea1aa72dd4af53278211c4bd803 100644 (file)
                </div>
        {% end %}
 
-       {% if lines %}
-               <h3>{{ _("Lines") }}</h3>
-
-               <table class="table table-condensed table-hover table-striped">
-                       <thead>
-                               <tr>
-                                       <th>{{ _("Expires") }}</th>
-                                       <th>{{ _("Location") }}</th>
-                                       <th>{{ _("User Agent") }}</th>
-                               </tr>
-                       </thead>
-                       <tbody>
-                               {% for line in lines %}
-                                       <tr>
-                                               <td>{{ locale.format_date(line.expires) }}</td>
-                                               <td>
-                                                       {% if line.tls_enabled %}
-                                                               <span class="glyphicon glyphicon-lock" title="{{ _("TLS") }}"></span>
-                                                       {% end %}
-                                                       {{ line.location }}
-                                               </td>
-                                               <td>{{ line.user_agent }}</td>
-                                       </tr>
-                               {% end %}
-                       </tbody>
-               </table>
-       {% end %}
-
-       {% if ongoing_calls %}
-               <h3>{{ _("Ongoing Calls") }}</h3>
-               {% module TalkOngoingCalls(ongoing_calls) %}
-       {% end %}
-
-       {% if call_log %}
-               <h3>{{ _("Call Log") }}</h3>
-               {% module TalkCallLog(call_log) %}
+       {% if current_user == account or current_user.is_admin() %}
+               {% module TalkLines(account) %}
+               {% module TalkOngoingCalls(account) %}
+               {% module TalkCallLog(account) %}
        {% end %}
 {% end block %}
diff --git a/templates/talk/trunks.html b/templates/talk/trunks.html
deleted file mode 100644 (file)
index bfa9a91..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-{% extends "../base.html" %}
-
-{% block title %}{{ _("Trunks") }}{% end block %}
-
-{% block body %}
-       <h3>{{ _("Trunks") }}</h3>
-       <table class="table table-hover table-striped">
-               <thead>
-                       <tr>
-                               <th></th>
-                               <th>{{ _("Name") }}</th>
-                               <th>{{ _("Registrar") }}</th>
-                       </tr>
-               </thead>
-               <tbody>
-                       {% for trunk in trunks %}
-                               <tr>
-                                       <td>
-                                               {% if trunk.status == "online" %}
-                                                       <span class="glyphicon glyphicon-ok text-success" title="{{ _("online") }}"></span>
-                                               {% elif trunk.status == "offline" or trunk.status is None %}
-                                                       <span class="glyphicon glyphicon-remove text-danger" title="{{ _("offline") }}"></span>
-                                               {% else %}
-                                                       {{ trunk.status }}
-                                               {% end %}
-                                       </td>
-                                       <td>{{ trunk.description or trunk.account }}</td>
-                                       <td>{{ trunk.username }}@{{ trunk.registrar }}</td>
-                               </tr>
-                       {% end %}
-               </tbody>
-       </table>
-{% end block %}
index de574b9113e887d55e9dc7b6594a269f0ddd6390..a8d78163a64d994a212ee1de2a94456973e479fe 100644 (file)
@@ -61,6 +61,7 @@ class Application(tornado.web.Application):
                                "FireinfoDeviceAndGroupsTable" : FireinfoDeviceAndGroupsTableModule,
                                "FireinfoGeoTable"        : FireinfoGeoTableModule,
                                "TalkCallLog"          : TalkCallLogModule,
+                               "TalkLines"            : TalkLinesModule,
                                "TalkOngoingCalls"     : TalkOngoingCallsModule,
                                "TrackerPeerList"      : TrackerPeerListModule,
                                "Wish"                 : WishModule,
@@ -255,7 +256,6 @@ class Application(tornado.web.Application):
                        (r"/phonebook/(\w+)", TalkPhonebookAccountHandler),
                        (r"/phonebook", TalkPhonebookHandler),
                        (r"/profile", TalkProfileHandler),
-                       (r"/trunks", TalkTrunksHandler),
                ] + authentication_handlers + static_handlers)
 
                # accounts.ipfire.org
index 70a4172437f3d5bbbaec69a4442c06be55e67de0..cad51d984e96942791ffd321f7d13d999fad598b 100644 (file)
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+import re
+
 import database
 
 from misc import Object
@@ -8,7 +10,7 @@ class Talk(Object):
        def init(self):
                db_args = self.backend.db._db_args
                db_args.update({
-                       "database" : "yate",
+                       "database" : "kamailio",
                })
 
                self._db = database.Connection(**db_args)
@@ -31,7 +33,7 @@ class Talk(Object):
                return accounts
 
        def user_is_online(self, sip_id):
-               res = self.db.get("SELECT 1 FROM lines WHERE username = %s \
+               res = self.db.get("SELECT 1 FROM location WHERE username = %s \
                        AND expires >= NOW() LIMIT 1", sip_id)
 
                if res:
@@ -39,28 +41,35 @@ class Talk(Object):
 
                return False
 
-       def get_trunks(self):
-               res = self.db.query("SELECT * FROM accounts WHERE enabled = TRUE \
-                       ORDER BY description, account")
-
-               return res
+       def get_lines(self, account=None):
+               accounts_cache = {}
 
-       def get_lines(self, account):
-               if not account.is_talk_enabled():
+               if account and not account.is_talk_enabled():
                        return []
 
-               res = self.db.query("SELECT location, expires, user_agent FROM lines \
-                       WHERE username = %s ORDER BY expires DESC", account.sip_id)
+               if account:
+                       res = self.db.query("SELECT contact AS location, expires, user_agent, socket \
+                               FROM location WHERE domain = %s AND username = %s ORDER BY expires DESC",
+                               "ipfire.org", account.sip_id)
+               else:
+                       res = self.db.query("SELECT username, domain, contact AS location, \
+                               expires, user_agent, socket FROM location ORDER BY username, expires DESC")
 
                result = []
                for row in res:
-                       # Check if TLS is used
-                       row.tls_enabled = row.location.startswith("sip/sips:") or \
-                               "transport=TLS" in row.location
+                       if account:
+                               row.account = account
+                       elif row.username:
+                               try:
+                                       row.account = accounts_cache[row.username]
+                               except KeyError:
+                                       row.account = accounts_cache[row.username] = \
+                                               self.accounts.get_by_sip_id(row.username)
+                       else:
+                               row.account = None
 
-                       # Strip a leading sip/
-                       if row.location.startswith("sip/"):
-                               row.location = row.location[4:]
+                       # Check if TLS is used
+                       row.tls_enabled = row.socket.startswith("tls:")
 
                        # Cut off everything after ;
                        row.location, sep, rest = row.location.partition(";")
@@ -69,39 +78,62 @@ class Talk(Object):
 
                return result
 
-       def _process_cdr(self, entries):
+       def _process_sip_uri(self, sip_uri):
+               sip_uri, delimiter, rest = sip_uri.partition(";")
+
+               m = re.match(r"^sip:([0-9]{4})@ipfire\.org", sip_uri)
+               if m:
+                       return m.group(1)
+
+               # Remove trailing default port
+               if sip_uri.endswith(":5060"):
+                       sip_uri = sip_uri.replace(":5060", "")
+
+               return sip_uri
+
+       def _process_cdr(self, entries, replace_sip_uris=False):
                accounts_cache = {}
 
                result = []
                for row in entries:
+                       if replace_sip_uris:
+                               row["caller"] = self._process_sip_uri(row.caller)
+
                        try:
-                               row.caller_account = accounts_cache[row.caller]
+                               row["caller_account"] = accounts_cache[row.caller]
                        except KeyError:
-                               row.caller_account = accounts_cache[row.caller] = \
+                               row["caller_account"] = accounts_cache[row.caller] = \
                                        self.accounts.get_by_sip_id(row.caller)
 
+                       if replace_sip_uris:
+                               row["called"] = self._process_sip_uri(row.called)
+
                        try:
-                               row.called_account = accounts_cache[row.called]
+                               row["called_account"] = accounts_cache[row.called]
                        except KeyError:
-                               row.called_account = accounts_cache[row.called] = \
+                               row["called_account"] = accounts_cache[row.called] = \
                                        self.accounts.get_by_sip_id(row.called)
 
                        if not row.called_account:
-                               row.called_conference = self.get_conference(row.called)
+                               row["called_conference"] = self.get_conference(row.called)
                        else:
-                               row.called_conference = None
+                               row["called_conference"] = None
+
+                       try:
+                               row["time"] = datetime.datetime.fromutctimestamp(row.time)
+                       except:
+                               pass
 
                        result.append(row)
 
                return result
 
        def get_call_log(self, account, limit=25):
-               res = self.db.query("SELECT * FROM cdr \
-                       WHERE ((direction = 'incoming' AND called = %s) OR (direction = 'outgoing' AND caller = %s)) \
-                       AND (status = 'answered' OR \
-                               (status = 'ringing' AND (reason = 'hangup' OR reason = 'Request Terminated'))) \
-                       AND NOT (status = 'outgoing' AND reason = 'pickup') \
-                       AND ended = TRUE ORDER BY time DESC LIMIT %s", account.sip_id, account.sip_id, limit)
+               if account:
+                       res = self.db.query("SELECT * FROM cdr WHERE (caller = %s OR called = %s) \
+                               ORDER BY time DESC LIMIT %s", account.sip_id, account.sip_id, limit)
+               else:
+                       res = self.db.query("SELECT * FROM cdr ORDER BY time DESC LIMIT %s", limit)
 
                return self._process_cdr(res)
 
@@ -109,18 +141,23 @@ class Talk(Object):
                if account and sip_id is None:
                        sip_id = account.sip_id
 
+                       # If the given account does not have a SIP ID,
+                       # we not need to search for any active sessions
+                       if sip_id is None:
+                               return []
+
                if sip_id:
-                       res = self.db.query("SELECT * FROM cdr \
-                               WHERE direction = 'incoming' AND (caller = %s OR called = %s) \
-                                       AND status = 'answered' AND NOT ended \
-                               ORDER BY time", sip_id, sip_id)
+                       sip_id = "sip:%s@%%" % sip_id
+
+                       res = self.db.query("SELECT from_uri AS caller, \
+                               to_uri AS called, start_time AS time FROM dialog \
+                               WHERE from_uri LIKE %s OR to_uri LIKE %s ORDER BY time",
+                               sip_id, sip_id)
                else:
-                       res = self.db.query("SELECT * FROM cdr \
-                               WHERE direction = 'incoming' AND \
-                                       status = 'answered' AND NOT ended \
-                               ORDER BY time")
+                       res = self.db.query("SELECT from_uri AS caller, to_uri AS called, \
+                               start_time AS time FROM dialog ORDER BY time")
 
-               return self._process_cdr(res)
+               return self._process_cdr(res, replace_sip_uris=True)
 
        def initiate_call(self, caller, called, when=None):
                self.db.execute("INSERT INTO dialout(caller, called, not_before) \
@@ -129,12 +166,11 @@ class Talk(Object):
        # Favourites
 
        def get_favourite_contacts(self, account, limit=6):
-               ignored_numbers = ["9999", account.sip_id, ""]
-
-               res = self.db.query("SELECT called, %s AS caller, COUNT(*) AS count FROM cdr \
-                       WHERE direction = 'outgoing' AND caller = %s AND status = 'answered' \
-                       AND NOT called = ANY(%s) GROUP BY called ORDER BY count DESC LIMIT %s",
-                       account.sip_id, account.sip_id, ignored_numbers, limit)
+               res = self.db.query("SELECT src_user AS caller, dst_ouser AS called, \
+                       COUNT(*) AS count FROM acc WHERE method = %s AND src_user = %s AND \
+                       dst_ouser BETWEEN %s AND %s AND time >= NOW() - INTERVAL '1 year' \
+                       GROUP BY caller, called ORDER BY count DESC LIMIT %s",
+                       "INVITE", account.sip_id, "1000", "1999", limit)
 
                return self._process_cdr(res)
 
@@ -191,6 +227,9 @@ class Conference(Object):
                participants = [p.caller for p in self.participants]
 
                for invitee in self.backend.talk.get_phonebook():
+                       if not invitee.sip_id:
+                               continue
+
                        if invitee.sip_id in participants:
                                continue
 
index e422a0cc95a33a8b4768941708d8ec3b02777f8d..27602e48a5568626bddfc9da891c65f086216717 100644 (file)
@@ -34,29 +34,7 @@ class TalkPhonebookAccountHandler(BaseHandler):
                if not account:
                        raise tornado.web.HTTPError(404, "Account not found: %s" % uid)
 
-               # Get the call log, currently registered lines and ongoing calls
-               if self.current_user == account or self.current_user.is_admin():
-                       call_log = self.talk.get_call_log(account)
-                       lines = self.talk.get_lines(account)
-                       ongoing_calls = self.talk.get_ongoing_calls(account)
-               else:
-                       call_log = None
-                       lines = None
-                       ongoing_calls = None
-
-               self.render("talk/phonebook-contact.html", account=account,
-                       ongoing_calls=ongoing_calls, call_log=call_log, lines=lines)
-
-
-class TalkTrunksHandler(BaseHandler):
-       @tornado.web.authenticated
-       def get(self):
-               if not self.current_user.is_admin():
-                       raise tornado.web.HTTPError(403)
-
-               trunks = self.talk.get_trunks()
-
-               self.render("talk/trunks.html", trunks=trunks)
+               self.render("talk/phonebook-contact.html", account=account)
 
 
 class TalkDiagnosisHandler(BaseHandler):
index f61cfca89ca554edcbf2dc0da145b903969c18c4..4b4e9c9c86504aff4b91ab11ac5506c78f2eae77 100644 (file)
@@ -48,6 +48,10 @@ class UIModule(tornado.web.UIModule):
        def planet(self):
                return self.handler.planet
 
+       @property
+       def talk(self):
+               return self.handler.talk
+
        @property
        def wishlist(self):
                return self.handler.wishlist
@@ -323,13 +327,45 @@ class ProgressBarModule(UIModule):
 
 
 class TalkCallLogModule(UIModule):
-       def render(self, calls):
-               return self.render_string("talk/modules/call-log.html", calls=calls)
+       def render(self, account=None, viewer=None):
+               if (account is None or not self.current_user == account) \
+                               and not self.current_user.is_admin():
+                       raise RuntimeException("Insufficient permissions")
+
+               if viewer is None:
+                       viewer = self.current_user
+
+               calls = self.talk.get_call_log(account)
+
+               return self.render_string("talk/modules/call-log.html",
+                       calls=calls, viewer=viewer)
+
+
+class TalkLinesModule(UIModule):
+       def render(self, account=None, show_account=False):
+               if (account is None or not self.current_user == account) \
+                               and not self.current_user.is_admin():
+                       raise RuntimeException("Insufficient permissions")
+
+               lines = self.talk.get_lines(account)
+
+               return self.render_string("talk/modules/lines.html",
+                       show_account=show_account, lines=lines)
 
 
 class TalkOngoingCallsModule(UIModule):
-       def render(self, calls):
-               return self.render_string("talk/modules/ongoing-calls.html", calls=calls)
+       def render(self, account=None):
+               if (account is None or not self.current_user == account) \
+                               and not self.current_user.is_admin():
+                       raise RuntimeException("Insufficient permissions")
+
+               calls = self.talk.get_ongoing_calls(account)
+
+               if calls:
+                       return self.render_string("talk/modules/ongoing-calls.html",
+                               calls=calls)
+
+               return ""
 
 
 class TrackerPeerListModule(UIModule):