Ticket #13376: messages_expirity_capabilities.4.diff

File messages_expirity_capabilities.4.diff, 35.7 KB (added by marekw2143, 13 years ago)
  • django/contrib/messages/restrictions.py

     
     1import time as time_provider
     2
     3
     4class Restriction (object):
     5    JSON_SEPARATOR = '@'
     6
     7    def __init__(self, name):
     8        self.type = name
     9
     10    def is_expired(self):
     11        """
     12        indicates whether given message should be removed
     13        """
     14        raise NotImplementedError ()
     15
     16    def on_display (self):
     17        """
     18        called when iterated - does nothing by default
     19        """
     20        pass
     21
     22    def to_json(self):
     23        """
     24        returns json representation of restriction
     25        """
     26        raise NotImplementedError
     27
     28    def from_json_param(self, *args):
     29        """
     30        returns restriction on the basis of data encoded in json
     31        """
     32        raise NotImplementedError
     33
     34
     35    def __cmp__(self, other):
     36        if self.__eq__(other): return 0
     37        return -1
     38
     39class TimeRestriction (Restriction):
     40    JSON_TYPE_CODE = 't'
     41
     42    def __init__(self, seconds):
     43        """
     44        seconds - expiration time since now
     45        """
     46        Restriction.__init__(self, 'time')
     47        created = time_provider.time()
     48        self.expires = created + int(seconds)
     49
     50    def set_expirity_time(self, expiration_time):
     51        """
     52        Sets expilcity expiration time
     53        """
     54        self.expires = int(expiration_time)
     55
     56    def is_expired (self):
     57        return self.expires < time_provider.time()
     58
     59    def __eq__(self, other):
     60        return self.type == other.type and not bool(self.expires ^ other.expires)
     61
     62    def __hash__(self):
     63        return self.expires
     64
     65    def to_json(self):
     66        return '%s%s%s' % (self.JSON_TYPE_CODE, self.JSON_SEPARATOR, self.expires)
     67
     68    @classmethod
     69    def from_json_param(cls, expirity_time):
     70        ret = TimeRestriction(0)
     71        ret.set_expirity_time(expirity_time)
     72        return ret
     73
     74
     75
     76class AmountRestriction (Restriction):
     77    JSON_TYPE_CODE = 'a'
     78
     79    def __init__(self, amount):
     80        assert int(amount) >= 0
     81        Restriction.__init__(self, 'amount')
     82        self.can_be_shown = int(amount)
     83
     84    def on_display (self):
     85        self.can_be_shown -= 1
     86
     87    def is_expired(self):
     88        return int(self.can_be_shown) <= 0
     89
     90    def __eq__(self, other):
     91        return self.type == other.type and not bool(self.can_be_shown ^ other.can_be_shown)
     92
     93
     94    def __hash__(self):
     95        return self.can_be_shown
     96
     97    def to_json(self):
     98        return '%s%s%s' % (self.JSON_TYPE_CODE, self.JSON_SEPARATOR, self.can_be_shown)
     99
     100    @classmethod
     101    def from_json_param(cls, amount):
     102        return AmountRestriction(amount)
  • django/contrib/messages/api.py

     
    1212    pass
    1313
    1414
    15 def add_message(request, level, message, extra_tags='', fail_silently=False):
     15def add_message(request, level, message, extra_tags='', fail_silently=False, restrictions=[]):
    1616    """
    1717    Attempts to add a message to the request using the 'messages' app.
    1818    """
    1919    if hasattr(request, '_messages'):
    20         return request._messages.add(level, message, extra_tags)
     20        return request._messages.add(level, message, extra_tags, restrictions = restrictions)
    2121    if not fail_silently:
    2222        raise MessageFailure('You cannot add messages without installing '
    2323                    'django.contrib.messages.middleware.MessageMiddleware')
  • django/contrib/messages/tests/restrictions.py

     
     1from django.test import TestCase
     2from django.contrib.messages import restrictions
     3from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
     4from django.contrib.messages.tests.time_provider import TestTimeProvider
     5
     6restrictions.time_provider = TestTimeProvider ()
     7
     8class RestrictionsTest(TestCase):
     9    def __check_expired(self, amount_restriction, iterations_amount):
     10        """
     11        Checks whether after iterations_amount of on_displayate given restriction will become expired
     12        But before iterations_amount given amount_restriction must not indicate is_expired
     13        """
     14        for i in range(iterations_amount):
     15            self.assertFalse(amount_restriction.is_expired())
     16            amount_restriction.on_display()
     17        self.assertTrue(amount_restriction.is_expired())
     18
     19    def test_amount_restrictions(self):
     20        res = AmountRestriction(4)
     21        self.__check_expired(res, 4)
     22
     23    def test_amount_restrictions_invalid_argument(self):
     24        self.assertRaises(AssertionError, AmountRestriction, -1)
     25
     26    def test_equal(self):
     27        self.assertEqual(AmountRestriction(5), AmountRestriction(5))
     28        self.assertFalse(AmountRestriction(1) == AmountRestriction(3))
     29        self.assertEqual(TimeRestriction(2), TimeRestriction(2))
     30        self.assertFalse(TimeRestriction(3) == TimeRestriction(4))
  • django/contrib/messages/tests/time_provider.py

     
     1class TestTimeProvider(object):
     2    def __init__(self, act_time = 0):
     3        self.act_time = act_time
     4    def set_act_time(self, act_time):
     5        self.act_time = act_time
     6    def time(self):
     7        return self.act_time
     8    def inc_act_time(self):
     9        self.act_time += 1
  • django/contrib/messages/tests/cookie.py

     
    11from django.contrib.messages import constants
    22from django.contrib.messages.tests.base import BaseTest
     3from django.contrib.messages.tests.time_provider import TestTimeProvider
     4from django.contrib.messages import restrictions
     5from django.contrib.messages.restrictions import TimeRestriction, AmountRestriction
    36from django.contrib.messages.storage.cookie import (CookieStorage,
    47    MessageEncoder, MessageDecoder)
    58from django.contrib.messages.storage.base import Message
     
    5659    def test_get(self):
    5760        storage = self.storage_class(self.get_request())
    5861        # Set initial data.
    59         example_messages = ['test', 'me']
     62        example_messages = [Message(constants.INFO, 'test'), Message(constants.INFO, 'me')]
     63        expected_messages = [Message(constants.INFO, 'test', restrictions = [AmountRestriction(0),]),\
     64                                     Message(constants.INFO, 'me', restrictions = [AmountRestriction(0),])]
    6065        set_cookie_data(storage, example_messages)
    6166        # Test that the message actually contains what we expect.
    62         self.assertEqual(list(storage), example_messages)
     67        self.assertEqual(list(storage), expected_messages)
    6368
    6469    def test_domain(self):
    6570        """
     
    126131        instances is properly encoded/decoded by the custom JSON
    127132        encoder/decoder classes.
    128133        """
     134        restrictions.time_provider = TestTimeProvider()
     135        restrictions.time_provider.set_act_time(0)
    129136        messages = [
    130137            {
    131138                'message': Message(constants.INFO, 'Test message'),
    132                 'message_list': [Message(constants.INFO, 'message %s') \
     139                'message_list': [Message(constants.INFO, 'message %s', \
     140                                 restrictions = [TimeRestriction(5), AmountRestriction(3), TimeRestriction(2), AmountRestriction(10)]) \
    133141                                 for x in xrange(5)] + [{'another-message': \
    134142                                 Message(constants.ERROR, 'error')}],
    135143            },
     
    139147        value = encoder.encode(messages)
    140148        decoded_messages = json.loads(value, cls=MessageDecoder)
    141149        self.assertEqual(messages, decoded_messages)
     150
     151    def transport_storage(self, response, request):
     152        request.COOKIES['messages'] = response.cookies['messages'].value
  • django/contrib/messages/tests/base.py

     
    33from django.conf import settings
    44from django.utils.translation import ugettext_lazy
    55from django.utils.unittest import skipIf
    6 from django.contrib.messages import constants, utils, get_level, set_level
     6from django.contrib.messages import constants, utils, get_level, set_level, restrictions
    77from django.contrib.messages.api import MessageFailure
     8from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
    89from django.contrib.messages.storage import default_storage, base
    910from django.contrib.messages.storage.base import Message
     11from django.contrib.messages.tests.time_provider import TestTimeProvider
    1012from django.core.urlresolvers import reverse
    1113
     14restrictions.time_provider = TestTimeProvider()
    1215
    1316def skipUnlessAuthIsInstalled(func):
    1417    return skipIf(
     
    9598        storage._loaded_data = data or []
    9699        return storage
    97100
     101    def read_storage(self, storage):
     102        """
     103        Simulates reading all messages from given storage
     104        Returns list of read messages
     105        """
     106        return list(storage)
     107
    98108    def test_add(self):
    99109        storage = self.get_storage()
    100110        self.assertFalse(storage.added_new)
     
    170180            response = self.client.post(add_url, data, follow=True)
    171181            self.assertRedirects(response, show_url)
    172182            self.assertTrue('messages' in response.context)
    173             messages = [Message(self.levels[level], msg) for msg in
     183            messages = [Message(self.levels[level], msg, restrictions = [AmountRestriction(0),]) for msg in
    174184                                                         data['messages']]
    175185            self.assertEqual(list(response.context['messages']), messages)
    176186            for msg in data['messages']:
     
    203213        """
    204214        settings.MESSAGE_LEVEL = constants.DEBUG
    205215        data = {
    206             'messages': ['Test message %d' % x for x in xrange(10)],
     216            'messages': ['%d' % x for x in xrange(10)],
    207217        }
    208218        show_url = reverse('django.contrib.messages.tests.urls.show')
    209219        messages = []
     
    214224                              args=(level,))
    215225            self.client.post(add_url, data)
    216226        response = self.client.get(show_url)
     227        new_messages = []
     228        for level in ('debug', 'info', 'success', 'warning', 'error'):
     229            new_messages.extend([Message(self.levels[level], msg, \
     230                                         restrictions = [AmountRestriction(0),]) for msg in data['messages']])
    217231        self.assertTrue('messages' in response.context)
    218         self.assertEqual(list(response.context['messages']), messages)
     232        self.assertEqual(list(response.context['messages']), new_messages)
    219233        for msg in data['messages']:
    220234            self.assertContains(response, msg)
    221235
     
    392406            # Ensure the level tags constant is put back like we found it.
    393407            self.restore_setting('MESSAGE_TAGS')
    394408            base.LEVEL_TAGS = utils.get_level_tags()
     409
     410    def test_storing_restrictoins(self):
     411        storage = self.get_storage()
     412        response = self.get_response()
     413
     414        get_res = lambda ar1, ar2, tr1, tr2: [AmountRestriction(ar1), AmountRestriction(ar2), TimeRestriction(tr1), TimeRestriction(tr2)]
     415
     416        restrictions.time_provider.set_act_time(0)
     417        storage.add(constants.WARNING, 'First msg', restrictions = get_res(10, 20, 30, 40))
     418        messages = self.__read_messages(storage, response)
     419        self.assertEqual(len(messages), 1)
     420        rest = messages[0].restrictions
     421        for a in (9, 19):
     422            self.assertTrue(a in [r.can_be_shown for r in rest if r.type == 'amount'])
     423        for t in (30, 40):
     424            self.assertTrue(t in [r.expires for r in rest if r.type == 'time'])
     425
     426        restrictions.time_provider.set_act_time(4)
     427        storage = self.get_storage()
     428        response = self.get_response()
     429        storage.add(constants.WARNING, 'First msg', restrictions = get_res(10, 20, 30, 40))
     430        messages = self.__read_messages(storage, response)
     431        rest = messages[0].restrictions
     432        for a in (9, 19):
     433            self.assertTrue(a in [r.can_be_shown for r in rest if r.type == 'amount'])
     434        for t in (34, 44):
     435            self.assertTrue(t in [r.expires for r in rest if r.type == 'time'])
     436
     437    def test_expire_default(self):
     438        """
     439        Tests against message expiration in default behaviour (no explicity defined restrictions on expire -> show only once)
     440        """
     441        storage = self.get_existing_storage()
     442        response = self.get_response()
     443
     444        list (storage)
     445        storage.update(response)
     446
     447        storing = self.stored_messages_count(storage, response)
     448        self.assertEqual(storing, 0)
     449
     450    def test_expire_after_showing_given_amount_of_times(self):
     451        """
     452        Tests against expiring message after beeing shown given amount of times
     453        """
     454        storage = self.get_existing_storage()
     455        response = self.get_response()
     456
     457        read_messages = lambda: self.__read_messages(storage, response)
     458
     459        storage.add(constants.WARNING, 'Third message, should be shown four times', restrictions = [AmountRestriction(4),])
     460        storage.add(constants.ERROR, "Four'th message, should be shown five times", restrictions = [AmountRestriction(5),])
     461
     462
     463        messages = read_messages ()
     464        self.assertEqual(len(messages), 4)
     465        storing = self.stored_messages_count(storage, response)
     466        self.assertEqual(storing, 2)
     467
     468
     469        for i in range(2):
     470            messages = read_messages ()
     471            self.assertEqual(len(messages), 2)
     472            storing = self.stored_messages_count(storage, response)
     473            self.assertEqual(storing, 2)
     474            [self.assertTrue(level in [x.level for x in messages]) for level in (constants.ERROR, constants.WARNING)]
     475
     476
     477        messages = read_messages ()
     478        self.assertEqual(len(messages), 2)
     479        storing = self.stored_messages_count(storage, response)
     480        self.assertEqual(storing, 1)
     481
     482
     483        messages = read_messages()
     484        self.assertEqual(len(messages), 1)
     485        self.assertEqual(messages[0].level, constants.ERROR)
     486        storing = self.stored_messages_count(storage, response)
     487        self.assertEqual(storing, 0)
     488
     489
     490        messages = read_messages()
     491        self.assertEqual(len(messages), 0)
     492        storing = self.stored_messages_count(storage, response)
     493        self.assertEqual(storing, 0)
     494
     495    def test_expire_after_time(self):
     496        storage = self.get_storage()
     497        response = self.get_response()
     498        res = restrictions
     499        res.time_provider.set_act_time(0)
     500
     501        storage.add(constants.WARNING, 'Third message, should be shown four times', restrictions = [TimeRestriction(4),])
     502        storage.add(constants.ERROR, "Four'th message, should be shown five times", restrictions = [TimeRestriction(6),])
     503
     504        for i in range(10):
     505            messages = self.__read_messages(storage, response)
     506            self.assertEqual(len(messages), 2)
     507            storing = self.stored_messages_count(storage, response)
     508            self.assertEqual(storing, 2)
     509
     510        res.time_provider.set_act_time(5)
     511        messages = self.__read_messages(storage, response)
     512        self.assertEqual(len(messages), 1)
     513        self.assertEqual(messages[0].level, constants.ERROR)
     514        storing = self.stored_messages_count(storage, response)
     515        self.assertEqual(storing, 1)
     516
     517        res.time_provider.set_act_time(7)
     518        messages = self.__read_messages(storage, response)
     519        self.assertEqual(len(messages), 0)
     520        storing = self.stored_messages_count(storage, response)
     521        self.assertEqual(storing, 0)
     522
     523    def test_expire_fixed_restrictions(self):
     524        """
     525        Check message expirity when defining few sort of restrictions
     526        of different kind
     527        """
     528        get_storage_and_response = lambda: (self.get_storage(), self.get_response())
     529        storage, response = get_storage_and_response()
     530        get_res = lambda ar1, ar2, tr1, tr2: [AmountRestriction(ar1), AmountRestriction(ar2), TimeRestriction(tr1), TimeRestriction(tr2)]
     531
     532
     533        def check_storage(storage, iterations, expected_amount, fnct = None):
     534            """
     535            Checks whether given storage would contain expected_amount of messages before each of iterations iterations
     536            Performs iterations amount of iterations
     537            Calls fnct at the end of each iteration
     538            """
     539            for i in range(iterations): #read 2 times
     540                messages = self.__read_messages(storage, response)
     541                self.assertEqual(len(messages), expected_amount)
     542                storing = self.stored_messages_count(storage, response)
     543                self.assertEqual(storing, expected_amount)
     544                if fnct: fnct()
     545
     546        # first - expires after amount of showing
     547        restrictions.time_provider.set_act_time(0)
     548        storage.add(constants.ERROR, "Some message", restrictions = get_res(7, 30, 5, 20))
     549        storage.add(constants.INFO, "another message", restrictions = get_res(3, 10, 4, 11))
     550        check_storage(storage, 2, 2)
     551        messages = self.__read_messages(storage, response) # 3 times read
     552        self.assertEqual(len(messages), 2)
     553        storing = self.stored_messages_count(storage, response)
     554        self.assertEqual(storing, 1)
     555        check_storage(storage, 3, 1)
     556
     557
     558        storage, response = get_storage_and_response()
     559        restrictions.time_provider.set_act_time(0)
     560        storage.add(constants.ERROR, "Some message", restrictions = get_res(7, 30, 5, 20))
     561        storage.add(constants.INFO, "another message", restrictions = get_res(3, 10, 4, 11))
     562        restrictions.time_provider.set_act_time(2)
     563        check_storage(storage, 2, 2, lambda:restrictions.time_provider.set_act_time(3))
     564
     565        # second - expires after amount of time
     566        storage, response = get_storage_and_response()
     567        restrictions.time_provider.set_act_time(0)
     568        storage.add(constants.ERROR, "Some message", restrictions = get_res(10, 30, 5, 20))
     569        storage.add(constants.INFO, "another message", restrictions = get_res(10, 20, 3, 11))
     570        check_storage(storage, 2, 2)
     571        restrictions.time_provider.set_act_time(2)
     572        check_storage(storage, 2, 2)
     573        restrictions.time_provider.set_act_time(4)
     574        check_storage(storage, 2, 1)
     575
     576    def transport_storage(self, response, request):
     577        """
     578        Transports storage from response to request
     579        This method can be overriden by subclasses
     580        """
     581
     582    def __read_messages(self, storage, response):#simulate next call to render messages
     583        """
     584        Simulates iterating storage in template
     585        Returns messages from that iteration that would be returned
     586        """
     587        messages = self.read_storage(storage)
     588        storage.update(response)
     589        self.transport_storage (response, storage.request)
     590        storage.used = False
     591        storage._queued_messages = []
     592        del storage._loaded_data
     593        return messages
  • django/contrib/messages/tests/__init__.py

     
    22from django.contrib.messages.tests.fallback import FallbackTest
    33from django.contrib.messages.tests.middleware import MiddlewareTest
    44from django.contrib.messages.tests.session import SessionTest
     5from django.contrib.messages.tests.restrictions import RestrictionsTest
     6from django.contrib.messages.tests.message import MessageTest
  • django/contrib/messages/tests/fallback.py

     
    11from django.contrib.messages import constants
     2from django.contrib.messages.storage.base import Message
     3from django.contrib.messages.restrictions import AmountRestriction
    24from django.contrib.messages.storage.fallback import (FallbackStorage,
    35    CookieStorage)
    46from django.contrib.messages.tests.base import BaseTest
     
    4446        cookie_storage = self.get_cookie_storage(storage)
    4547
    4648        # Set initial cookie data.
    47         example_messages = [str(i) for i in range(5)]
     49        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
     50        expected_messages = [Message(constants.INFO, str(i), restrictions = [AmountRestriction(0),]) for i in range(5)]
    4851        set_cookie_data(cookie_storage, example_messages)
    4952
    5053        # Overwrite the _get method of the fallback storage to prove it is not
     
    5255        self.get_session_storage(storage)._get = None
    5356
    5457        # Test that the message actually contains what we expect.
    55         self.assertEqual(list(storage), example_messages)
     58        self.assertEqual(list(storage), expected_messages)
    5659
    5760    def test_get_empty(self):
    5861        request = self.get_request()
     
    7275        session_storage = self.get_session_storage(storage)
    7376
    7477        # Set initial cookie and session data.
    75         example_messages = [str(i) for i in range(5)]
     78        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
     79        expected_messages = [Message(constants.INFO, str(i), restrictions = [AmountRestriction(0),]) for i in range(5)]
    7680        set_cookie_data(cookie_storage, example_messages[:4] +
    7781                        [CookieStorage.not_finished])
    7882        set_session_data(session_storage, example_messages[4:])
    7983
    8084        # Test that the message actually contains what we expect.
    81         self.assertEqual(list(storage), example_messages)
     85        self.assertEqual(list(storage), expected_messages)
    8286
    8387    def test_get_fallback_only(self):
    8488        request = self.get_request()
     
    8791        session_storage = self.get_session_storage(storage)
    8892
    8993        # Set initial cookie and session data.
    90         example_messages = [str(i) for i in range(5)]
     94        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
    9195        set_cookie_data(cookie_storage, [CookieStorage.not_finished],
    9296                        encode_empty=True)
    9397        set_session_data(session_storage, example_messages)
     
    102106        session_storage = self.get_session_storage(storage)
    103107
    104108        # Set initial cookie and session data.
    105         set_cookie_data(cookie_storage, ['cookie', CookieStorage.not_finished])
    106         set_session_data(session_storage, ['session'])
     109        set_cookie_data(cookie_storage, [Message(constants.INFO, 'cookie'), CookieStorage.not_finished])
     110        set_session_data(session_storage, [Message(constants.INFO, 'session')])
    107111
    108112        # When updating, previously used but no longer needed backends are
    109113        # flushed.
     
    173177        self.assertEqual(cookie_storing, 0)
    174178        session_storing = self.stored_session_messages_count(storage, response)
    175179        self.assertEqual(session_storing, 1)
     180
     181    def transport_storage(self, response, request):
     182        request.COOKIES['messages'] = response.cookies['messages'].value
  • django/contrib/messages/tests/session.py

     
    11from django.contrib.messages.tests.base import BaseTest
    22from django.contrib.messages.storage.session import SessionStorage
     3from django.contrib.messages.storage.base import Message
    34
    45
    56def set_session_data(storage, messages):
     
    3233    def test_get(self):
    3334        storage = self.storage_class(self.get_request())
    3435        # Set initial data.
    35         example_messages = ['test', 'me']
     36        example_messages = [Message(1, 'test') , Message(1, 'me')]
    3637        set_session_data(storage, example_messages)
    3738        # Test that the message actually contains what we expect.
    3839        self.assertEqual(list(storage), example_messages)
  • django/contrib/messages/tests/message.py

     
     1from django.test import TestCase
     2from django.contrib.messages.storage.base import Message
     3from django.contrib.messages import restrictions
     4from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction, time_provider
     5from django.contrib.messages.tests.time_provider import TestTimeProvider
     6from django.contrib.messages import constants
     7
     8
     9class MessageTest(TestCase):
     10    def setUp(self):
     11        self.tp = restrictions.time_provider = TestTimeProvider ()
     12    def __check_active(self, msg, iterations):
     13        """
     14        Reads msg given amount of iterations, and after each read
     15        checks whether before each read message is active
     16        """
     17        for i in range(iterations):
     18            self.assertTrue(msg.active())
     19            msg.on_display()
     20        self.assertFalse(msg.active())
     21        msg.on_display()
     22        self.assertFalse(msg.active())
     23
     24    def test_active_default(self):
     25        msg = Message(constants.INFO, "Test message")
     26        self.__check_active(msg, 1)
     27
     28    def test_active_custom_one_amount_restriction(self):
     29        msg = Message(constants.INFO, "Test message", restrictions = [AmountRestriction(3),])
     30        self.__check_active(msg, 3)
     31
     32    def test_active_custom_few_amount_restriction(self):
     33        msg = Message(constants.INFO, "Test message", restrictions = [AmountRestriction(x) for x in (2, 3, 5)])
     34        self.__check_active(msg, 2)
     35
     36    def test_active_custom_one_time_restriction(self):
     37        msg = Message(constants.INFO, "Test message", restrictions = [TimeRestriction(3),])
     38        def check_iter():
     39            for i in range(10): # iteration doesn't have direct impact for TimeRestriction
     40                self.assertTrue(msg.active())
     41                msg.on_display()
     42        check_iter()
     43        self.tp.set_act_time(3)
     44        check_iter()
     45        self.tp.set_act_time(4)
     46        self.assertFalse(msg.active())
     47
     48
     49    def test_mixed_restrictions(self):
     50        get_restrictions = lambda:[TimeRestriction(3), TimeRestriction(5), AmountRestriction(2), AmountRestriction(3)]
     51        get_msg = lambda:Message(constants.INFO, "Test message", restrictions = get_restrictions())
     52
     53        msg = get_msg()
     54        for i in range(2):
     55            self.assertTrue(msg.active())
     56            msg.on_display()
     57        self.assertFalse(msg.active())
     58
     59        msg = get_msg()
     60        self.assertTrue(msg.active())
     61        msg.on_display()
     62        self.assertTrue(msg.active())
     63        self.tp.set_act_time(4)
     64        self.assertFalse(msg.active())
     65        for i in range(10):
     66            self.assertFalse(msg.active())
     67            msg.on_display()
  • django/contrib/messages/storage/cookie.py

     
    11from django.conf import settings
    2 from django.contrib.messages.storage.base import BaseStorage, Message
     2from django.contrib.messages.storage.base import BaseStorage, Message, RestrictionsContainer
    33from django.http import SimpleCookie
    44from django.utils import simplejson as json
    55from django.utils.crypto import salted_hmac, constant_time_compare
     6from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
    67
    78
    89class MessageEncoder(json.JSONEncoder):
     
    1112    """
    1213    message_key = '__json_message'
    1314
     15
    1416    def default(self, obj):
    1517        if isinstance(obj, Message):
    1618            message = [self.message_key, obj.level, obj.message]
    1719            if obj.extra_tags:
    1820                message.append(obj.extra_tags)
     21            message.append (obj.restrictions.to_json_obj())
    1922            return message
    2023        return super(MessageEncoder, self).default(obj)
    2124
     
    2528    Decodes JSON that includes serialized ``Message`` instances.
    2629    """
    2730
     31
     32    def create_message(self, vars):
     33        ''' creates message on the basis of encoded data '''
     34        args = vars[:-1]
     35        restrictions = vars[-1]
     36        restrictions = RestrictionsContainer.create_from_josn(restrictions)
     37        return Message(*args, **{'restrictions': restrictions})
     38
    2839    def process_messages(self, obj):
    2940        if isinstance(obj, list) and obj:
    3041            if obj[0] == MessageEncoder.message_key:
    31                 return Message(*obj[1:])
     42                return self.create_message (obj[1:])
    3243            return [self.process_messages(item) for item in obj]
    3344        if isinstance(obj, dict):
    3445            return dict([(key, self.process_messages(value))
  • django/contrib/messages/storage/base.py

     
    11from django.conf import settings
    22from django.utils.encoding import force_unicode, StrAndUnicode
    3 from django.contrib.messages import constants, utils
     3from django.contrib.messages import constants, utils, restrictions as res
    44
    55
    66LEVEL_TAGS = utils.get_level_tags()
    77
    88
     9class RestrictionsContainer(list):
     10    res_map = {res.AmountRestriction.JSON_TYPE_CODE: res.AmountRestriction,
     11                res.TimeRestriction.JSON_TYPE_CODE: res.TimeRestriction}
     12
     13    def to_json_obj(self):
     14        return [r.to_json() for r in self]
     15
     16    @classmethod
     17    def create_from_josn(cls, enc_restrictions):
     18        #set_trace()
     19        ret = []
     20        for r in enc_restrictions:
     21            restriction_type, values = r.split(res.Restriction.JSON_SEPARATOR)
     22            ret.append(cls.res_map[restriction_type].from_json_param(values))
     23        return RestrictionsContainer(ret)
     24
     25    def __eq__(self, other):
     26        return set(self) == set(other)
     27
    928class Message(StrAndUnicode):
    1029    """
    1130    Represents an actual message that can be stored in any of the supported
     
    1332    or template.
    1433    """
    1534
    16     def __init__(self, level, message, extra_tags=None):
     35    def __init__(self, level, message, extra_tags=None, restrictions = []):
    1736        self.level = int(level)
    1837        self.message = message
    1938        self.extra_tags = extra_tags
    2039
    21     def _prepare(self):
     40        self.restrictions = restrictions or list([res.AmountRestriction(1)])
     41        self.restrictions = RestrictionsContainer(self.restrictions)
     42        # if not given any restriction - one show by default
     43
     44    def _prepare(self): # todo: slef.restrictions =
    2245        """
    2346        Prepares the message for serialization by forcing the ``message``
    2447        and ``extra_tags`` to unicode in case they are lazy translations.
     
    3154
    3255    def __eq__(self, other):
    3356        return isinstance(other, Message) and self.level == other.level and \
    34                                               self.message == other.message
     57                                              self.message == other.message and \
     58                                              self.restrictions == other.restrictions
    3559
    3660    def __unicode__(self):
    3761        return force_unicode(self.message)
    3862
     63
    3964    def _get_tags(self):
    4065        label_tag = force_unicode(LEVEL_TAGS.get(self.level, ''),
    4166                                  strings_only=True)
     
    5075    tags = property(_get_tags)
    5176
    5277
     78    def active (self):
     79        for r in self.restrictions:
     80            if r.is_expired(): return False
     81        return True
     82
     83
     84    def on_display (self):
     85        for r in self.restrictions:
     86            r.on_display ()
     87
     88
    5389class BaseStorage(object):
    5490    """
    5591    This is the base backend for temporary message storage.
     
    6096
    6197    def __init__(self, request, *args, **kwargs):
    6298        self.request = request
     99        self.used = False
    63100        self._queued_messages = []
    64         self.used = False
    65101        self.added_new = False
    66102        super(BaseStorage, self).__init__(*args, **kwargs)
    67103
    68104    def __len__(self):
    69         return len(self._loaded_messages) + len(self._queued_messages)
     105        # in case that there was a call for render template which would cause iterating throught messages,
     106        # and then (e.g. in some middleware, would be call for iterating through messages (e.g. by iterating of context['messages'])
     107        # TODO: implement a way to access messages without affecting callign __iter__ method
     108        all_msgs = set(self._loaded_messages + self._queued_messages)
     109        return len(all_msgs)
     110        #return len(self._loaded_messages) + len(self._queued_messages)
    70111
    71112    def __iter__(self):
    72         self.used = True
    73         if self._queued_messages:
    74             self._loaded_messages.extend(self._queued_messages)
    75             self._queued_messages = []
    76         return iter(self._loaded_messages)
     113        if not self.used:
     114            self.used = True
     115            if self._queued_messages:
     116                self._loaded_messages.extend(self._queued_messages)
     117                self._queued_messages = []
    77118
     119            active_messages = [x for x in self._loaded_messages if x.active ()]
     120            for x in active_messages:
     121                x.on_display ()
     122            self._queued_messages.extend (m for m in active_messages if m not in self._queued_messages)
     123        return iter(self._queued_messages)
     124
     125
    78126    def __contains__(self, item):
    79127        return item in self._loaded_messages or item in self._queued_messages
    80128
     
    104152        """
    105153        raise NotImplementedError()
    106154
     155    def filter_store(self, messages, response, *args, **kwargs):
     156        ''' stores only active messages from given messages in storage '''
     157        filtered_messages = [x for x in messages if x.active ()]
     158        return self._store(filtered_messages, response, *args, **kwargs)
     159
     160
    107161    def _store(self, messages, response, *args, **kwargs):
    108162        """
    109163        Stores a list of messages, returning a list of any messages which could
     
    130184        be stored again. Otherwise, only messages added after the last
    131185        iteration will be stored.
    132186        """
     187        # if used or used and added, then _queued_messages contains all messages that should be saved
     188        # if added, then save: all messages currently stored and added ones
    133189        self._prepare_messages(self._queued_messages)
    134190        if self.used:
    135             return self._store(self._queued_messages, response)
     191            return self.filter_store(self._queued_messages, response)
    136192        elif self.added_new:
    137193            messages = self._loaded_messages + self._queued_messages
    138             return self._store(messages, response)
     194            return self.filter_store(messages, response)
    139195
    140     def add(self, level, message, extra_tags=''):
     196    def add(self, level, message, extra_tags='', restrictions = []):
    141197        """
    142198        Queues a message to be stored.
    143199
     
    152208            return
    153209        # Add the message.
    154210        self.added_new = True
    155         message = Message(level, message, extra_tags=extra_tags)
     211        message = Message(level, message, extra_tags=extra_tags, restrictions = restrictions)
    156212        self._queued_messages.append(message)
    157213
    158214    def _get_level(self):
Back to Top