Commit 2c02d7ee6dd357c98c4517bbe0d8fb07667584bf
1 parent
a7d5f7a6
Exists in
master
and in
2 other branches
User status change propagation via notification
Showing
8 changed files
with
79 additions
and
6 deletions
Show diff stats
amadeus/static/js/socket.js
@@ -21,6 +21,8 @@ socket.onmessage = function(e) { | @@ -21,6 +21,8 @@ socket.onmessage = function(e) { | ||
21 | } | 21 | } |
22 | } else if (content.type == "chat") { | 22 | } else if (content.type == "chat") { |
23 | messageReceived(content); | 23 | messageReceived(content); |
24 | + } else if (content.type == "user_status") { | ||
25 | + changeUserStatus(content); | ||
24 | } | 26 | } |
25 | } | 27 | } |
26 | // Call onopen directly if socket is already open | 28 | // Call onopen directly if socket is already open |
@@ -297,4 +299,18 @@ function messageReceived(content) { | @@ -297,4 +299,18 @@ function messageReceived(content) { | ||
297 | setTimeout(notification.close.bind(notification), 3000); | 299 | setTimeout(notification.close.bind(notification), 3000); |
298 | } | 300 | } |
299 | } | 301 | } |
302 | +} | ||
303 | + | ||
304 | +function changeUserStatus(content) { | ||
305 | + var elem = $(".user_" + content.user_id + "_status"); | ||
306 | + | ||
307 | + elem.removeClass(content.remove_class); | ||
308 | + | ||
309 | + if (content.status_class == "") { | ||
310 | + elem.removeClass('active'); | ||
311 | + } else { | ||
312 | + elem.addClass(content.status_class); | ||
313 | + } | ||
314 | + | ||
315 | + elem.attr('data-original-title', content.status); | ||
300 | } | 316 | } |
301 | \ No newline at end of file | 317 | \ No newline at end of file |
chat/templates/chat/_profile.html
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | <img src="{{ participant.image_url }}" /> | 14 | <img src="{{ participant.image_url }}" /> |
15 | </span> | 15 | </span> |
16 | <h4> | 16 | <h4> |
17 | - <a class="status {{ status }}" title="{{ status|status_text }}"></a> | 17 | + <a class="user_{{ participant.id }}_status status {{ status }}" title="{{ status|status_text }}"></a> |
18 | <b>{{ participant }}</b> | 18 | <b>{{ participant }}</b> |
19 | </h4> | 19 | </h4> |
20 | <a href="#" onclick="getModalInfo($(this), '{{ space }}', '{{ space_type }}'); return false;" data-url='{% url "chat:talk" participant.email %}' class="btn btn-raised btn-success btn-block">{% trans 'Send Message' %}</a> | 20 | <a href="#" onclick="getModalInfo($(this), '{{ space }}', '{{ space_type }}'); return false;" data-url='{% url "chat:talk" participant.email %}' class="btn btn-raised btn-success btn-block">{% trans 'Send Message' %}</a> |
chat/templates/chat/_view.html
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | <img src="{{ talking_to.image_url }}" class="img-responsive" /> | 9 | <img src="{{ talking_to.image_url }}" class="img-responsive" /> |
10 | </div> | 10 | </div> |
11 | <div class="col-md-6 user-info"> | 11 | <div class="col-md-6 user-info"> |
12 | - <h4 class='talking-header'><a class="status {{ status }}" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ talking_to }} (<span class="chat_notify">{{ chat|notifies:request.user }}</span>)</h4> | 12 | + <h4 class='talking-header'><a class="user_{{ talking_to.id }}_status status {{ status }}" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ talking_to }} (<span class="chat_notify">{{ chat|notifies:request.user }}</span>)</h4> |
13 | <p class="talk-last_msg">{% trans 'Last message in' %} {{ chat|last_message }}</p> | 13 | <p class="talk-last_msg">{% trans 'Last message in' %} {{ chat|last_message }}</p> |
14 | </div> | 14 | </div> |
15 | <div class="col-md-4 buttons pull-right text-center"> | 15 | <div class="col-md-4 buttons pull-right text-center"> |
chat/templates/chat/_view_participant.html
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | <img src="{{ participant.image_url }}" class="img-responsive" /> | 7 | <img src="{{ participant.image_url }}" class="img-responsive" /> |
8 | </div> | 8 | </div> |
9 | <div class="col-md-6 user-info"> | 9 | <div class="col-md-6 user-info"> |
10 | - <h4><a class="status {{ status }}" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ participant }}</h4> | 10 | + <h4><a class="user_{{ participant.id }}_status status {{ status }}" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ participant }}</h4> |
11 | </div> | 11 | </div> |
12 | <div class="col-md-4 buttons pull-right text-center"> | 12 | <div class="col-md-4 buttons pull-right text-center"> |
13 | <a href="#" onclick="getModalInfo($(this), '{{ space }}', '{{ space_type }}'); return false;" data-url='{% url "chat:profile" participant.email %}' class="btn btn-raised btn-default">{% trans 'See Profile' %}</a> | 13 | <a href="#" onclick="getModalInfo($(this), '{{ space }}', '{{ space_type }}'); return false;" data-url='{% url "chat:profile" participant.email %}' class="btn btn-raised btn-default">{% trans 'See Profile' %}</a> |
chat/templates/chat/talk.html
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | <img src="{{ participant.image_url }}" /> | 11 | <img src="{{ participant.image_url }}" /> |
12 | </span> | 12 | </span> |
13 | <h4 class="pull-left" data-breadcrumb="{% trans 'Talk with ' %}{{ participant }}"> | 13 | <h4 class="pull-left" data-breadcrumb="{% trans 'Talk with ' %}{{ participant }}"> |
14 | - <a class="status {{ status }}" title="{{ status|status_text }}"></a> | 14 | + <a class="user_{{ participant.id }}_status status {{ status }}" title="{{ status|status_text }}"></a> |
15 | <b>{{ participant }}</b> | 15 | <b>{{ participant }}</b> |
16 | </h4> | 16 | </h4> |
17 | </div> | 17 | </div> |
subjects/templates/subjects/view.html
@@ -93,7 +93,7 @@ | @@ -93,7 +93,7 @@ | ||
93 | 93 | ||
94 | <div class="participants-container"> | 94 | <div class="participants-container"> |
95 | <div class="col-md-12 sub-user" data-toggle="popover" data-container="body" data-placement="left"> | 95 | <div class="col-md-12 sub-user" data-toggle="popover" data-container="body" data-placement="left"> |
96 | - <h4><a class="status {{ status }}" data-placement="right" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ participant }}</h4> | 96 | + <h4><a class="user_{{ participant.id }}_status status {{ status }}" data-placement="right" data-toggle="tooltip" title="{{ status|status_text }}"></a> {{ participant }}</h4> |
97 | </div> | 97 | </div> |
98 | 98 | ||
99 | <div class="popover"> | 99 | <div class="popover"> |
users/middleware.py
@@ -9,6 +9,11 @@ from session_security.utils import get_last_activity, set_last_activity | @@ -9,6 +9,11 @@ from session_security.utils import get_last_activity, set_last_activity | ||
9 | 9 | ||
10 | from log.models import Log | 10 | from log.models import Log |
11 | 11 | ||
12 | +from .models import User | ||
13 | +from django.utils.translation import ugettext as _u | ||
14 | +from channels import Group | ||
15 | +import json | ||
16 | + | ||
12 | class SessionExpireMiddleware(object): | 17 | class SessionExpireMiddleware(object): |
13 | 18 | ||
14 | def process_request(self, request): | 19 | def process_request(self, request): |
@@ -33,4 +38,19 @@ class SessionExpireMiddleware(object): | @@ -33,4 +38,19 @@ class SessionExpireMiddleware(object): | ||
33 | log.action = "logout" | 38 | log.action = "logout" |
34 | log.resource = "system" | 39 | log.resource = "system" |
35 | 40 | ||
36 | - log.save() | ||
37 | \ No newline at end of file | 41 | \ No newline at end of file |
42 | + log.save() | ||
43 | + | ||
44 | + users = User.objects.all().exclude(email = request.user.email) | ||
45 | + | ||
46 | + notification = { | ||
47 | + "type": "user_status", | ||
48 | + "user_id": str(request.user.id), | ||
49 | + "status": _u("Offline"), | ||
50 | + "status_class": "", | ||
51 | + "remove_class": "away" | ||
52 | + } | ||
53 | + | ||
54 | + notification = json.dumps(notification) | ||
55 | + | ||
56 | + for u in users: | ||
57 | + Group("user-%s" % u.id).send({'text': notification}) | ||
38 | \ No newline at end of file | 58 | \ No newline at end of file |
users/views.py
@@ -5,6 +5,7 @@ from django.contrib.auth import authenticate, login as login_user, logout as log | @@ -5,6 +5,7 @@ from django.contrib.auth import authenticate, login as login_user, logout as log | ||
5 | from django.contrib.auth.mixins import LoginRequiredMixin | 5 | from django.contrib.auth.mixins import LoginRequiredMixin |
6 | from django.core.urlresolvers import reverse, reverse_lazy | 6 | from django.core.urlresolvers import reverse, reverse_lazy |
7 | from django.utils.translation import ugettext_lazy as _ | 7 | from django.utils.translation import ugettext_lazy as _ |
8 | +from django.utils.translation import ugettext as _u | ||
8 | from django.db.models import Q, Count | 9 | from django.db.models import Q, Count |
9 | 10 | ||
10 | from braces import views as braces_mixins | 11 | from braces import views as braces_mixins |
@@ -19,6 +20,10 @@ from .models import User | @@ -19,6 +20,10 @@ from .models import User | ||
19 | from .utils import has_dependencies | 20 | from .utils import has_dependencies |
20 | from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm, PassResetRequest, SetPasswordForm | 21 | from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm, PassResetRequest, SetPasswordForm |
21 | 22 | ||
23 | +#USER STATUS NOTIFICATION | ||
24 | +from channels import Group | ||
25 | +import json | ||
26 | + | ||
22 | #RECOVER PASS IMPORTS | 27 | #RECOVER PASS IMPORTS |
23 | from django.contrib.auth.tokens import default_token_generator | 28 | from django.contrib.auth.tokens import default_token_generator |
24 | from django.core.mail import send_mail | 29 | from django.core.mail import send_mail |
@@ -501,6 +506,21 @@ def login(request): | @@ -501,6 +506,21 @@ def login(request): | ||
501 | if not security.maintence or user.is_staff: | 506 | if not security.maintence or user.is_staff: |
502 | login_user(request, user) | 507 | login_user(request, user) |
503 | 508 | ||
509 | + users = User.objects.all().exclude(email = username) | ||
510 | + | ||
511 | + notification = { | ||
512 | + "type": "user_status", | ||
513 | + "user_id": str(user.id), | ||
514 | + "status": _u("Online"), | ||
515 | + "status_class": "active", | ||
516 | + "remove_class": "away" | ||
517 | + } | ||
518 | + | ||
519 | + notification = json.dumps(notification) | ||
520 | + | ||
521 | + for u in users: | ||
522 | + Group("user-%s" % u.id).send({'text': notification}) | ||
523 | + | ||
504 | next_url = request.GET.get('next', None) | 524 | next_url = request.GET.get('next', None) |
505 | 525 | ||
506 | if next_url: | 526 | if next_url: |
@@ -519,8 +539,25 @@ def login(request): | @@ -519,8 +539,25 @@ def login(request): | ||
519 | 539 | ||
520 | @log_decorator('user', 'logout', 'system') | 540 | @log_decorator('user', 'logout', 'system') |
521 | def logout(request, next_page = None): | 541 | def logout(request, next_page = None): |
542 | + user = request.user | ||
543 | + | ||
522 | logout_user(request) | 544 | logout_user(request) |
523 | 545 | ||
546 | + users = User.objects.all().exclude(email = user.email) | ||
547 | + | ||
548 | + notification = { | ||
549 | + "type": "user_status", | ||
550 | + "user_id": str(user.id), | ||
551 | + "status": _u("Offline"), | ||
552 | + "status_class": "", | ||
553 | + "remove_class": "away" | ||
554 | + } | ||
555 | + | ||
556 | + notification = json.dumps(notification) | ||
557 | + | ||
558 | + for u in users: | ||
559 | + Group("user-%s" % u.id).send({'text': notification}) | ||
560 | + | ||
524 | if next_page: | 561 | if next_page: |
525 | return redirect(next_page) | 562 | return redirect(next_page) |
526 | 563 |