Ticket #7153: 7153.3.diff
File 7153.3.diff, 14.3 KB (added by , 14 years ago) |
---|
-
django/contrib/auth/__init__.py
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 3d76a42..cca1b70 100644
a b import datetime 2 2 from warnings import warn 3 3 from django.core.exceptions import ImproperlyConfigured 4 4 from django.utils.importlib import import_module 5 from django.contrib.auth import signals 5 6 6 7 SESSION_KEY = '_auth_user_id' 7 8 BACKEND_SESSION_KEY = '_auth_user_backend' … … def login(request, user): 62 63 if user is None: 63 64 user = request.user 64 65 # TODO: It would be nice to support different login methods, like signed cookies. 65 user.last_login = datetime.datetime.now()66 user.save()66 signals.user_logged_in.send(sender=user.__class__, request=request, 67 user=user) 67 68 68 69 if SESSION_KEY in request.session: 69 70 if request.session[SESSION_KEY] != user.id: … … def logout(request): 83 84 Removes the authenticated user's ID from the request and flushes their 84 85 session data. 85 86 """ 87 # Dispatch the signal before the user is logged out so the receivers have a 88 # chance to find out *who* logged out. 89 user = getattr(request, 'user', None) 90 if hasattr(user, 'is_authenticated') and not user.is_authenticated(): 91 user = None 92 signals.user_logged_out.send(sender=user.__class__, request=request, 93 user=user) 94 86 95 request.session.flush() 87 96 if hasattr(request, 'user'): 88 97 from django.contrib.auth.models import AnonymousUser -
django/contrib/auth/models.py
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 16a8b99..e37c5b3 100644
a b import datetime 2 2 import urllib 3 3 4 4 from django.contrib import auth 5 # Import the signals module to register the signal listener. 6 from django.contrib.auth import signals 5 7 from django.core.exceptions import ImproperlyConfigured 6 8 from django.db import models 7 9 from django.db.models.manager import EmptyManager -
new file django/contrib/auth/signals.py
diff --git a/django/contrib/auth/signals.py b/django/contrib/auth/signals.py new file mode 100644 index 0000000..ae66b6d
- + 1 import datetime 2 import django.dispatch 3 4 user_logged_in = django.dispatch.Signal(providing_args=['request', 'user']) 5 user_logged_out = django.dispatch.Signal(providing_args=['request', 'user']) 6 7 8 def update_last_login(sender, user, **kwargs): 9 """ 10 A signal receiver which updates the last_login date for the user logging 11 in. 12 13 """ 14 user.last_login = datetime.datetime.now() 15 user.save() 16 17 18 user_logged_in.connect(update_last_login) -
django/contrib/auth/tests/__init__.py
diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py index 98061a1..9f8b49f 100644
a b from django.contrib.auth.tests.forms import UserCreationFormTest, Authentication 5 5 from django.contrib.auth.tests.remote_user \ 6 6 import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest 7 7 from django.contrib.auth.tests.models import ProfileTestCase 8 from django.contrib.auth.tests.signals import SignalTestCase 8 9 from django.contrib.auth.tests.tokens import TokenGeneratorTest 9 10 from django.contrib.auth.tests.views \ 10 11 import PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest -
new file django/contrib/auth/tests/signals.py
diff --git a/django/contrib/auth/tests/signals.py b/django/contrib/auth/tests/signals.py new file mode 100644 index 0000000..3806021
- + 1 from django.test import TestCase 2 from django.contrib.auth import signals 3 4 5 class SignalTestCase(TestCase): 6 urls = 'django.contrib.auth.tests.urls' 7 fixtures = ['authtestdata.json'] 8 9 def listener_login(self, user, **kwargs): 10 self.logged_in.append(user) 11 12 def listener_logout(self, user, **kwargs): 13 self.logged_out.append(user) 14 15 def setUp(self): 16 """Set up the listeners and reset the logged in/logged out counters""" 17 self.logged_in = [] 18 self.logged_out = [] 19 signals.user_logged_in.connect(self.listener_login) 20 signals.user_logged_out.connect(self.listener_logout) 21 22 def tearDown(self): 23 """Disconnect the listeners""" 24 signals.user_logged_in.disconnect(self.listener_login) 25 signals.user_logged_out.disconnect(self.listener_logout) 26 27 def test_login(self): 28 # Only a successful login will trigger the signal. 29 self.client.login(username='testclient', password='bad') 30 self.assertEqual(len(self.logged_in), 0) 31 # Like this: 32 self.client.login(username='testclient', password='password') 33 self.assertEqual(len(self.logged_in), 1) 34 self.assertEqual(self.logged_in[0].username, 'testclient') 35 36 def test_logout_anonymous(self): 37 # The log_out function will still trigger the signal for anonymous 38 # users. 39 self.client.get('/logout/next_page/') 40 self.assertEqual(len(self.logged_out), 1) 41 self.assertEqual(self.logged_out[0], None) 42 43 def test_logout(self): 44 self.client.login(username='testclient', password='password') 45 self.client.get('/logout/next_page/') 46 self.assertEqual(len(self.logged_out), 1) 47 self.assertEqual(self.logged_out[0].username, 'testclient') -
django/template/__init__.py
diff --git a/django/template/__init__.py b/django/template/__init__.py index c316786..fc6ce07 100644
a b class Variable(object): 717 717 instead. 718 718 """ 719 719 current = context 720 for bit in self.lookups: 721 try: # dictionary lookup 722 current = current[bit] 723 except (TypeError, AttributeError, KeyError): 724 try: # attribute lookup 725 current = getattr(current, bit) 726 if callable(current): 727 if getattr(current, 'alters_data', False): 728 current = settings.TEMPLATE_STRING_IF_INVALID 729 else: 730 try: # method call (assuming no args required) 731 current = current() 732 except TypeError: # arguments *were* required 733 # GOTCHA: This will also catch any TypeError 734 # raised in the function itself. 735 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call 736 except Exception, e: 737 if getattr(e, 'silent_variable_failure', False): 738 current = settings.TEMPLATE_STRING_IF_INVALID 739 else: 740 raise 741 except (TypeError, AttributeError): 742 try: # list-index lookup 743 current = current[int(bit)] 744 except (IndexError, # list index out of range 745 ValueError, # invalid literal for int() 746 KeyError, # current is a dict without `int(bit)` key 747 TypeError, # unsubscriptable object 748 ): 749 raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute 750 except Exception, e: 751 if getattr(e, 'silent_variable_failure', False): 752 current = settings.TEMPLATE_STRING_IF_INVALID 753 else: 754 raise 755 except Exception, e: 756 if getattr(e, 'silent_variable_failure', False): 757 current = settings.TEMPLATE_STRING_IF_INVALID 758 else: 759 raise 720 try: # catch-all for silent variable failures 721 for bit in self.lookups: 722 try: # dictionary lookup 723 current = current[bit] 724 except (TypeError, AttributeError, KeyError): 725 try: # attribute lookup 726 current = getattr(current, bit) 727 except (TypeError, AttributeError): 728 try: # list-index lookup 729 current = current[int(bit)] 730 except (IndexError, # list index out of range 731 ValueError, # invalid literal for int() 732 KeyError, # current is a dict without `int(bit)` key 733 TypeError, # unsubscriptable object 734 ): 735 raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute 736 if callable(current): 737 if getattr(current, 'alters_data', False): 738 return settings.TEMPLATE_STRING_IF_INVALID 739 try: # method call (assuming no args required) 740 current = current() 741 except TypeError: # arguments *were* required 742 # GOTCHA: This will also catch any TypeError 743 # raised in the function itself. 744 return settings.TEMPLATE_STRING_IF_INVALID # invalid method call 745 except Exception, e: 746 if getattr(e, 'silent_variable_failure', False): 747 return settings.TEMPLATE_STRING_IF_INVALID 748 else: 749 raise 760 750 761 751 return current 762 752 -
docs/ref/signals.txt
diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt index 4c1db33..36ad071 100644
a b A list of all the signals that Django sends. 12 12 The :doc:`comment framework </ref/contrib/comments/index>` sends a :doc:`set 13 13 of comment-related signals </ref/contrib/comments/signals>`. 14 14 15 The :ref:`authentication framework <topics-auth>` sends :ref:`signals when 16 a user is logged in / out <topics-auth-signals>`. 17 15 18 Model signals 16 19 ============= 17 20 -
docs/topics/auth.txt
diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index e0856e8..d69fd91 100644
a b How to log a user out 664 664 immediately after logging out, do that *after* calling 665 665 :func:`django.contrib.auth.logout()`. 666 666 667 .. _topics-auth-signals: 668 669 Login and logout signals 670 ------------------------ 671 672 The auth framework uses two :ref:`signals <topic-signals>` that can be used for 673 notification when a user logs in or out. 674 675 **:data:`django.contrib.auth.signals.user_logged_in`** 676 677 Sent when a user logs in successfully. 678 679 Arguments sent with this signal: 680 681 ``sender`` 682 As above: the class of the user that just logged in. 683 684 ``request`` 685 The current :class:`~django.http.HttpRequest` instance. 686 687 ``user`` 688 The user instance that just logged in. 689 690 **:data:`django.contrib.auth.signals.user_logged_out`** 691 692 Sent when the logout method is called. 693 694 ``sender`` 695 As above: the class of the user that just logged out or ``None`` 696 if the user was not authenticated. 697 698 ``request`` 699 The current :class:`~django.http.HttpRequest` instance. 700 701 ``user`` 702 The user instance that just logged out or ``None`` if the 703 user was not authenticated. 704 667 705 Limiting access to logged-in users 668 706 ---------------------------------- 669 707 -
tests/regressiontests/templates/tests.py
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 6683ebb..fba5c02 100644
a b class SomeClass: 94 94 def method4(self): 95 95 raise SomeOtherException 96 96 97 def __getitem__(self, key): 98 if key == 'silent_fail_key': 99 raise SomeException 100 elif key == 'noisy_fail_key': 101 raise SomeOtherException 102 raise KeyError 103 104 def silent_fail_attribute(self): 105 raise SomeException 106 silent_fail_attribute = property(silent_fail_attribute) 107 108 def noisy_fail_attribute(self): 109 raise SomeOtherException 110 noisy_fail_attribute = property(noisy_fail_attribute) 111 97 112 class OtherClass: 98 113 def method(self): 99 114 return "OtherClass.method" … … class Templates(unittest.TestCase): 499 514 'basic-syntax25': ('{{ "fred" }}', {}, "fred"), 500 515 'basic-syntax26': (r'{{ "\"fred\"" }}', {}, "\"fred\""), 501 516 'basic-syntax27': (r'{{ _("\"fred\"") }}', {}, "\"fred\""), 517 518 # Call methods in the top level of the context 519 'basic-syntax28': (r'{{ callable }}', {"callable": lambda: "foo bar"}, "foo bar"), 520 521 # Call methods returned from dictionary lookups 522 'basic-syntax29': (r'{{ var.callable }}', {"var": {"callable": lambda: "foo bar"}}, "foo bar"), 502 523 503 524 # regression test for ticket #12554 504 525 # make sure a silent_variable_failure Exception is supressed … … class Templates(unittest.TestCase): 604 625 #filters should accept empty string constants 605 626 'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""), 606 627 628 # Fail silently for non-callable attribute and dict lookups which 629 # raise an exception with a "silent_variable_failure" attribute 630 'filter-syntax21': (r'1{{ var.silent_fail_key }}2', {"var": SomeClass()}, ("12", "1INVALID2")), 631 'filter-syntax22': (r'1{{ var.silent_fail_attribute }}2', {"var": SomeClass()}, ("12", "1INVALID2")), 632 633 # In attribute and dict lookups that raise an unexpected exception 634 # without a "silent_variable_attribute" set to True, the exception 635 # propagates 636 'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, SomeOtherException), 637 'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, SomeOtherException), 638 607 639 ### COMMENT SYNTAX ######################################################## 608 640 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), 609 641 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),