Index: contrib/messages/restrictions.py
===================================================================
--- contrib/messages/restrictions.py	(revision 0)
+++ contrib/messages/restrictions.py	(revision 0)
@@ -0,0 +1,52 @@
+import time as time_provider
+
+
+class Restriction (object):
+    def __init__(self, name):
+        self.type = name 
+
+    def to_remove(self):
+        ''' indicates whether given message should be removed '''
+        raise NotImplementedError ()
+
+    def on_iter (self):
+        ''' called when iterated - defaultly do nothing '''
+
+
+class TimeRestriction (Restriction):
+    def __init__(self, seconds):
+        """
+        seconds - expiration time since now
+        """
+        Restriction.__init__(self, 'time')
+        created = time_provider.time()
+        self.expires = created + int(seconds)
+
+    def set_expirity_time(self, expiration_time):
+        """
+        Sets expilcity expiration time
+        """
+        self.expires = int(expiration_time)
+
+    def to_remove (self):
+        return self.expires < time_provider.time()
+
+    def __eq__(self, other):
+        return self.expires == other.expires
+
+
+class AmountRestriction (Restriction):
+    def __init__(self, amount):
+        assert int(amount) >= 0
+        Restriction.__init__(self, 'amount')
+        self.can_be_shown = int(amount)
+
+    def on_iter (self):
+        self.can_be_shown -= 1
+
+    def to_remove(self):
+        return int(self.can_be_shown) <= 0
+
+    def __eq__(self, other):
+        return self.can_be_shown == other.can_be_shown
+
Index: contrib/messages/api.py
===================================================================
--- contrib/messages/api.py	(revision 14213)
+++ contrib/messages/api.py	(working copy)
@@ -13,13 +13,13 @@
     pass
 
 
-def add_message(request, level, message, extra_tags='', fail_silently=False):
+def add_message(request, level, message, extra_tags='', fail_silently=False, restrictions = []):
     """
     Attempts to add a message to the request using the 'messages' app, falling
     back to the user's message_set if MessageMiddleware hasn't been enabled.
     """
     if hasattr(request, '_messages'):
-        return request._messages.add(level, message, extra_tags)
+        return request._messages.add(level, message, extra_tags, restrictions = restrictions)
     if hasattr(request, 'user') and request.user.is_authenticated():
         return request.user.message_set.create(message=message)
     if not fail_silently:
Index: contrib/messages/tests/restrictions.py
===================================================================
--- contrib/messages/tests/restrictions.py	(revision 0)
+++ contrib/messages/tests/restrictions.py	(revision 0)
@@ -0,0 +1,31 @@
+from django.test import TestCase
+from django.contrib.messages import restrictions
+from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
+from django.contrib.messages.tests.time_provider import TestTimeProvider
+
+restrictions.time_provider = TestTimeProvider ()
+
+class RestrictionsTest(TestCase):
+    def __check_expired(self, amount_restriction, iterations_amount):
+        """
+        Checks whether after iterations_amount of on_iterate given restriction will become expired
+        But before iterations_amount given amount_restriction must not indicate to_remove
+        """
+        for i in range(iterations_amount):
+            self.assertFalse(amount_restriction.to_remove())
+            amount_restriction.on_iter()
+        self.assertTrue(amount_restriction.to_remove())
+
+    def test_amount_restrictions(self):
+        res = AmountRestriction(4)
+        self.__check_expired(res, 4)
+
+    def test_amount_restrictions_invalid_argument(self):
+        self.assertRaises(AssertionError, AmountRestriction, -1)
+
+    def test_equal(self):
+        self.assertEqual(AmountRestriction(5), AmountRestriction(5))
+        self.assertFalse(AmountRestriction(1) == AmountRestriction(3))
+        self.assertEqual(TimeRestriction(2), TimeRestriction(2))
+        self.assertFalse(TimeRestriction(3) == TimeRestriction(4))
+
Index: contrib/messages/tests/time_provider.py
===================================================================
--- contrib/messages/tests/time_provider.py	(revision 0)
+++ contrib/messages/tests/time_provider.py	(revision 0)
@@ -0,0 +1,9 @@
+class TestTimeProvider(object):
+    def __init__(self, act_time = 0):
+        self.act_time = act_time
+    def set_act_time(self, act_time):
+        self.act_time = act_time
+    def time(self):
+        return self.act_time
+    def inc_act_time(self):
+        self.act_time += 1
Index: contrib/messages/tests/cookie.py
===================================================================
--- contrib/messages/tests/cookie.py	(revision 14213)
+++ contrib/messages/tests/cookie.py	(working copy)
@@ -1,5 +1,8 @@
 from django.contrib.messages import constants
 from django.contrib.messages.tests.base import BaseTest
+from django.contrib.messages.tests.time_provider import TestTimeProvider
+from django.contrib.messages import restrictions
+from django.contrib.messages.restrictions import TimeRestriction, AmountRestriction
 from django.contrib.messages.storage.cookie import CookieStorage, \
                                             MessageEncoder, MessageDecoder
 from django.contrib.messages.storage.base import Message
@@ -46,10 +49,12 @@
     def test_get(self):
         storage = self.storage_class(self.get_request())
         # Set initial data.
-        example_messages = ['test', 'me']
+        example_messages = [Message(constants.INFO, 'test'), Message(constants.INFO, 'me')]
+        expected_messages = [Message(constants.INFO, 'test', restrictions = [AmountRestriction(0),]),\
+                                     Message(constants.INFO, 'me', restrictions = [AmountRestriction(0),])]
         set_cookie_data(storage, example_messages)
         # Test that the message actually contains what we expect.
-        self.assertEqual(list(storage), example_messages)
+        self.assertEqual(list(storage), expected_messages)
 
     def test_get_bad_cookie(self):
         request = self.get_request()
@@ -91,10 +96,13 @@
         instances is properly encoded/decoded by the custom JSON
         encoder/decoder classes.
         """
+        restrictions.time_provider = TestTimeProvider()
+        restrictions.time_provider.set_act_time(0)
         messages = [
             {
                 'message': Message(constants.INFO, 'Test message'),
-                'message_list': [Message(constants.INFO, 'message %s') \
+                'message_list': [Message(constants.INFO, 'message %s', \
+                                 restrictions = [TimeRestriction(5), AmountRestriction(3), TimeRestriction(2), AmountRestriction(10)]) \
                                  for x in xrange(5)] + [{'another-message': \
                                  Message(constants.ERROR, 'error')}],
             },
@@ -104,3 +112,7 @@
         value = encoder.encode(messages)
         decoded_messages = json.loads(value, cls=MessageDecoder)
         self.assertEqual(messages, decoded_messages)
+
+    def transport_storage(self, response, request):
+        request.COOKIES['messages'] = response.cookies['messages'].value
+
Index: contrib/messages/tests/base.py
===================================================================
--- contrib/messages/tests/base.py	(revision 14213)
+++ contrib/messages/tests/base.py	(working copy)
@@ -4,14 +4,18 @@
 from django.test import TestCase
 from django.conf import settings
 from django.utils.translation import ugettext_lazy
-from django.contrib.messages import constants, utils, get_level, set_level
+from django.contrib.messages import constants, utils, get_level, set_level, restrictions
 from django.contrib.messages.api import MessageFailure
+from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
 from django.contrib.messages.storage import default_storage, base
 from django.contrib.messages.storage.base import Message
+from django.contrib.messages.tests.time_provider import TestTimeProvider
 from django.core.urlresolvers import reverse
 from django.contrib.auth.models import User
 
+restrictions.time_provider= TestTimeProvider()
 
+# TODO: make def read(storage) method that would simulate reading messages from storage
 def add_level_messages(storage):
     """
     Adds 6 messages from different levels (including a custom one) to a storage
@@ -92,6 +96,13 @@
         storage._loaded_data = data or []
         return storage
 
+    def read_storage(self, storage):
+        """
+        Simulates reading all messages from given storage
+        Returns list of read messages
+        """
+        return list(storage)
+
     def test_add(self):
         storage = self.get_storage()
         self.assertFalse(storage.added_new)
@@ -167,7 +178,7 @@
             response = self.client.post(add_url, data, follow=True)
             self.assertRedirects(response, show_url)
             self.assertTrue('messages' in response.context)
-            messages = [Message(self.levels[level], msg) for msg in
+            messages = [Message(self.levels[level], msg, restrictions = [AmountRestriction(0),]) for msg in
                                                          data['messages']]
             self.assertEqual(list(response.context['messages']), messages)
             for msg in data['messages']:
@@ -180,7 +191,7 @@
         """
         settings.MESSAGE_LEVEL = constants.DEBUG
         data = {
-            'messages': ['Test message %d' % x for x in xrange(10)],
+            'messages': ['%d' % x for x in xrange(10)],
         }
         show_url = reverse('django.contrib.messages.tests.urls.show')
         messages = []
@@ -191,8 +202,12 @@
                               args=(level,))
             self.client.post(add_url, data)
         response = self.client.get(show_url)
+        new_messages = []
+        for level in ('debug', 'info', 'success', 'warning', 'error'):
+            new_messages.extend([Message(self.levels[level], msg, \
+                                         restrictions = [AmountRestriction(0),]) for msg in data['messages']])
         self.assertTrue('messages' in response.context)
-        self.assertEqual(list(response.context['messages']), messages)
+        self.assertEqual(list(response.context['messages']), new_messages)
         for msg in data['messages']:
             self.assertContains(response, msg)
 
@@ -406,3 +421,189 @@
             # Ensure the level tags constant is put back like we found it.
             self.restore_setting('MESSAGE_TAGS')
             base.LEVEL_TAGS = utils.get_level_tags()
+
+    def test_storing_restrictoins(self):
+        storage = self.get_storage()
+        response = self.get_response()
+
+        get_res = lambda ar1, ar2, tr1, tr2: [AmountRestriction(ar1), AmountRestriction(ar2), TimeRestriction(tr1), TimeRestriction(tr2)]
+        
+        restrictions.time_provider.set_act_time(0)
+        storage.add(constants.WARNING, 'First msg', restrictions = get_res(10, 20, 30, 40))
+        messages = self.__read_messages(storage, response)
+        self.assertEqual(len(messages), 1)
+        rest = messages[0].restrictions
+        for a in (9, 19):
+            self.assertTrue(a in [r.can_be_shown for r in rest if r.type == 'amount'])
+        for t in (30, 40):
+            self.assertTrue(t in [r.expires for r in rest if r.type == 'time'])
+
+        restrictions.time_provider.set_act_time(4)
+        storage = self.get_storage()
+        response = self.get_response()
+        storage.add(constants.WARNING, 'First msg', restrictions = get_res(10, 20, 30, 40))
+        messages = self.__read_messages(storage, response)
+        rest = messages[0].restrictions
+        for a in (9, 19):
+            self.assertTrue(a in [r.can_be_shown for r in rest if r.type == 'amount'])
+        for t in (34, 44):
+            self.assertTrue(t in [r.expires for r in rest if r.type == 'time'])
+
+    def test_expire_default(self):
+        """
+        Tests against message expiration in default behaviour (no explicity defined restrictions on expire -> show only once)
+        """
+        storage = self.get_existing_storage()
+        response = self.get_response()
+        
+        list (storage)
+        storage.update(response)
+        
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 0)
+
+    def test_expire_after_showing_given_amount_of_times(self):
+        """
+        Tests against expiring message after beeing shown given amount of times
+        """
+        storage = self.get_existing_storage()
+        response = self.get_response()
+
+        read_messages = lambda: self.__read_messages(storage, response)
+
+        storage.add(constants.WARNING, 'Third message, should be shown four times', restrictions = [AmountRestriction(4),])
+        storage.add(constants.ERROR, "Four'th message, should be shown five times", restrictions = [AmountRestriction(5),])
+
+
+        messages = read_messages ()
+        self.assertEqual(len(messages), 4)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 2)
+
+
+        for i in range(2):
+            messages = read_messages ()
+            self.assertEqual(len(messages), 2)
+            storing = self.stored_messages_count(storage, response)
+            self.assertEqual(storing, 2)
+            [self.assertTrue(level in [x.level for x in messages]) for level in (constants.ERROR, constants.WARNING)]
+
+
+        messages = read_messages ()
+        self.assertEqual(len(messages), 2)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 1)
+
+
+        messages = read_messages()
+        self.assertEqual(len(messages), 1)
+        self.assertEqual(messages[0].level, constants.ERROR)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 0)
+
+
+        messages = read_messages()
+        self.assertEqual(len(messages), 0)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 0)
+
+    def test_expire_after_time(self):
+        storage = self.get_storage()
+        response = self.get_response()
+        res = restrictions
+        res.time_provider.set_act_time(0)
+
+        storage.add(constants.WARNING, 'Third message, should be shown four times', restrictions = [TimeRestriction(4),])
+        storage.add(constants.ERROR, "Four'th message, should be shown five times", restrictions = [TimeRestriction(6),])
+
+        for i in range(10):
+            messages = self.__read_messages(storage, response)
+            self.assertEqual(len(messages), 2)
+            storing = self.stored_messages_count(storage, response)
+            self.assertEqual(storing, 2)
+
+        res.time_provider.set_act_time(5)
+        messages = self.__read_messages(storage, response)
+        self.assertEqual(len(messages), 1)
+        self.assertEqual(messages[0].level, constants.ERROR)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 1)
+
+        res.time_provider.set_act_time(7)
+        messages = self.__read_messages(storage, response)
+        self.assertEqual(len(messages), 0)
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 0)
+
+    def test_expire_fixed_restrictions(self):
+        """
+        Check message expirity when defining few sort of restrictions
+        of different kind
+        """
+        get_storage_and_response = lambda: (self.get_storage(), self.get_response())
+        storage, response = get_storage_and_response()
+        get_res = lambda ar1, ar2, tr1, tr2: [AmountRestriction(ar1), AmountRestriction(ar2), TimeRestriction(tr1), TimeRestriction(tr2)]
+
+
+        def check_storage(storage, iterations, expected_amount, fnct = None):
+            """
+            Checks whether given storage would contain expected_amount of messages before each of iterations iterations
+            Performs iterations amount of iterations
+            Calls fnct at the end of each iteration
+            """
+            for i in range(iterations): #read 2 times
+                messages = self.__read_messages(storage, response)
+                self.assertEqual(len(messages), expected_amount)
+                storing = self.stored_messages_count(storage, response)
+                self.assertEqual(storing, expected_amount)
+                if fnct: fnct()
+
+        # first - expires after amount of showing
+        restrictions.time_provider.set_act_time(0)
+        storage.add(constants.ERROR, "Some message", restrictions = get_res(7, 30, 5, 20))
+        storage.add(constants.INFO, "another message", restrictions = get_res(3, 10, 4, 11))
+        check_storage(storage, 2, 2)
+        messages = self.__read_messages(storage, response) # 3 times read
+        self.assertEqual(len(messages), 2) 
+        storing = self.stored_messages_count(storage, response)
+        self.assertEqual(storing, 1)
+        check_storage(storage, 3, 1)
+            
+
+        storage, response = get_storage_and_response()
+        restrictions.time_provider.set_act_time(0)
+        storage.add(constants.ERROR, "Some message", restrictions = get_res(7, 30, 5, 20))
+        storage.add(constants.INFO, "another message", restrictions = get_res(3, 10, 4, 11))
+        restrictions.time_provider.set_act_time(2)
+        check_storage(storage, 2, 2, lambda:restrictions.time_provider.set_act_time(3))
+ 
+        # second - expires after amount of time
+        storage, response = get_storage_and_response()
+        restrictions.time_provider.set_act_time(0)
+        storage.add(constants.ERROR, "Some message", restrictions = get_res(10, 30, 5, 20))
+        storage.add(constants.INFO, "another message", restrictions = get_res(10, 20, 3, 11))
+        check_storage(storage, 2, 2)
+        restrictions.time_provider.set_act_time(2)
+        check_storage(storage, 2, 2)
+        restrictions.time_provider.set_act_time(4)
+        check_storage(storage, 2, 1)
+
+    def transport_storage(self, response, request):
+        """
+        Transports storage from response to request
+        This method can be overriden by subclasses
+        """
+
+    def __read_messages(self, storage, response):#simulate next call to render messages
+        """
+        Simulates iterating storage in template
+        Returns messages from that iteration that would be returned
+        """
+        messages = self.read_storage(storage)
+        storage.update(response)
+        self.transport_storage (response, storage.request)
+        storage.used = False  
+        storage._queued_messages = []
+        del storage._loaded_data
+        return messages
+
Index: contrib/messages/tests/__init__.py
===================================================================
--- contrib/messages/tests/__init__.py	(revision 14213)
+++ contrib/messages/tests/__init__.py	(working copy)
@@ -1,6 +1,10 @@
+from django.contrib.messages.tests.session import SessionTest
 from django.contrib.messages.tests.cookie import CookieTest
 from django.contrib.messages.tests.fallback import FallbackTest
-from django.contrib.messages.tests.middleware import MiddlewareTest
-from django.contrib.messages.tests.session import SessionTest
+from django.contrib.messages.tests.restrictions import RestrictionsTest
+from django.contrib.messages.tests.message import MessageTest
 from django.contrib.messages.tests.user_messages import \
                                            UserMessagesTest, LegacyFallbackTest
+
+from django.contrib.messages.tests.middleware import MiddlewareTest
+
Index: contrib/messages/tests/fallback.py
===================================================================
--- contrib/messages/tests/fallback.py	(revision 14213)
+++ contrib/messages/tests/fallback.py	(working copy)
@@ -1,9 +1,11 @@
 from django.contrib.messages import constants
+from django.contrib.messages.storage.base import Message
 from django.contrib.messages.storage.fallback import FallbackStorage, \
     CookieStorage
 from django.contrib.messages.tests.base import BaseTest
 from django.contrib.messages.tests.cookie import set_cookie_data, \
     stored_cookie_messages_count
+from django.contrib.messages.restrictions import AmountRestriction
 from django.contrib.messages.tests.session import set_session_data, \
     stored_session_messages_count
 
@@ -44,7 +46,8 @@
         cookie_storage = self.get_cookie_storage(storage)
 
         # Set initial cookie data.
-        example_messages = [str(i) for i in range(5)]
+        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
+        expected_messages = [Message(constants.INFO, str(i), restrictions = [AmountRestriction(0),]) for i in range(5)]
         set_cookie_data(cookie_storage, example_messages)
 
         # Overwrite the _get method of the fallback storage to prove it is not
@@ -52,7 +55,7 @@
         self.get_session_storage(storage)._get = None
 
         # Test that the message actually contains what we expect.
-        self.assertEqual(list(storage), example_messages)
+        self.assertEqual(list(storage), expected_messages)
 
     def test_get_empty(self):
         request = self.get_request()
@@ -72,13 +75,14 @@
         session_storage = self.get_session_storage(storage)
 
         # Set initial cookie and session data.
-        example_messages = [str(i) for i in range(5)]
+        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
+        expected_messages = [Message(constants.INFO, str(i), restrictions = [AmountRestriction(0),]) for i in range(5)]
         set_cookie_data(cookie_storage, example_messages[:4] +
                         [CookieStorage.not_finished])
         set_session_data(session_storage, example_messages[4:])
 
         # Test that the message actually contains what we expect.
-        self.assertEqual(list(storage), example_messages)
+        self.assertEqual(list(storage), expected_messages)
 
     def test_get_fallback_only(self):
         request = self.get_request()
@@ -87,7 +91,7 @@
         session_storage = self.get_session_storage(storage)
 
         # Set initial cookie and session data.
-        example_messages = [str(i) for i in range(5)]
+        example_messages = [Message(constants.INFO, str(i)) for i in range(5)]
         set_cookie_data(cookie_storage, [CookieStorage.not_finished],
                         encode_empty=True)
         set_session_data(session_storage, example_messages)
@@ -102,8 +106,8 @@
         session_storage = self.get_session_storage(storage)
 
         # Set initial cookie and session data.
-        set_cookie_data(cookie_storage, ['cookie', CookieStorage.not_finished])
-        set_session_data(session_storage, ['session'])
+        set_cookie_data(cookie_storage, [Message(constants.INFO, 'cookie'), CookieStorage.not_finished])
+        set_session_data(session_storage, [Message(constants.INFO, 'session')])
 
         # When updating, previously used but no longer needed backends are
         # flushed.
@@ -173,3 +177,6 @@
         self.assertEqual(cookie_storing, 0)
         session_storing = self.stored_session_messages_count(storage, response)
         self.assertEqual(session_storing, 1)
+
+    def transport_storage(self, response, request):
+        request.COOKIES['messages'] = response.cookies['messages'].value
Index: contrib/messages/tests/session.py
===================================================================
--- contrib/messages/tests/session.py	(revision 14213)
+++ contrib/messages/tests/session.py	(working copy)
@@ -1,5 +1,6 @@
 from django.contrib.messages.tests.base import BaseTest
 from django.contrib.messages.storage.session import SessionStorage
+from django.contrib.messages.storage.base import Message
 
 
 def set_session_data(storage, messages):
@@ -32,7 +33,7 @@
     def test_get(self):
         storage = self.storage_class(self.get_request())
         # Set initial data.
-        example_messages = ['test', 'me']
+        example_messages = [Message(1, 'test') , Message(1, 'me')]
         set_session_data(storage, example_messages)
         # Test that the message actually contains what we expect.
         self.assertEqual(list(storage), example_messages)
Index: contrib/messages/tests/message.py
===================================================================
--- contrib/messages/tests/message.py	(revision 0)
+++ contrib/messages/tests/message.py	(revision 0)
@@ -0,0 +1,69 @@
+from django.test import TestCase
+from django.contrib.messages.storage.base import Message
+from django.contrib.messages import restrictions
+from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction, time_provider
+from django.contrib.messages.tests.time_provider import TestTimeProvider
+from django.contrib.messages import constants
+
+
+class MessageTest(TestCase):
+    def setUp(self):
+        self.tp = restrictions.time_provider = TestTimeProvider ()
+    def __check_active(self, msg, iterations):
+        """
+        Reads msg given amount of iterations, and after each read
+        checks whether before each read message is active
+        """
+        for i in range(iterations):
+            self.assertTrue(msg.active())
+            msg.on_iter()
+        self.assertFalse(msg.active())
+        msg.on_iter()
+        self.assertFalse(msg.active())
+
+    def test_active_default(self):
+        msg = Message(constants.INFO, "Test message")
+        self.__check_active(msg, 1)
+
+    def test_active_custom_one_amount_restriction(self):
+        msg = Message(constants.INFO, "Test message", restrictions = [AmountRestriction(3),])
+        self.__check_active(msg, 3)
+
+    def test_active_custom_few_amount_restriction(self):
+        msg = Message(constants.INFO, "Test message", restrictions = [AmountRestriction(x) for x in (2, 3, 5)])
+        self.__check_active(msg, 2)
+
+    def test_active_custom_one_time_restriction(self):
+        msg = Message(constants.INFO, "Test message", restrictions = [TimeRestriction(3),])
+        def check_iter():
+            for i in range(10): # iteration doesn't have direct impact for TimeRestriction
+                self.assertTrue(msg.active())
+                msg.on_iter()
+        check_iter()
+        self.tp.set_act_time(3)
+        check_iter()
+        self.tp.set_act_time(4)
+        self.assertFalse(msg.active())
+
+
+    def test_mixed_restrictions(self):
+        get_restrictions = lambda:[TimeRestriction(3), TimeRestriction(5), AmountRestriction(2), AmountRestriction(3)]
+        get_msg = lambda:Message(constants.INFO, "Test message", restrictions = get_restrictions())
+
+        msg = get_msg()
+        for i in range(2):
+            self.assertTrue(msg.active())
+            msg.on_iter()
+        self.assertFalse(msg.active())
+
+        msg = get_msg()
+        self.assertTrue(msg.active())
+        msg.on_iter()
+        self.assertTrue(msg.active())
+        self.tp.set_act_time(4)
+        self.assertFalse(msg.active())
+        for i in range(10):
+            self.assertFalse(msg.active())
+            msg.on_iter()
+        
+            
Index: contrib/messages/tests/user_messages.py
===================================================================
--- contrib/messages/tests/user_messages.py	(revision 14213)
+++ contrib/messages/tests/user_messages.py	(working copy)
@@ -1,5 +1,7 @@
 from django import http
 from django.contrib.auth.models import User
+from django.contrib.messages import constants
+from django.contrib.messages.storage.base import Message
 from django.contrib.messages.storage.user_messages import UserMessagesStorage,\
     LegacyFallbackStorage
 from django.contrib.messages.tests.cookie import set_cookie_data
@@ -57,9 +59,9 @@
         storage = self.storage_class(request)
         cookie_storage = self.get_cookie_storage(storage)
         self.user.message_set.create(message='user message')
-        set_cookie_data(cookie_storage, ['cookie'])
+        set_cookie_data(cookie_storage, [Message(constants.INFO,'cookie')])
 
         # Test that the message actually contains what we expect.
         self.assertEqual(len(storage), 2)
         self.assertEqual(list(storage)[0].message, 'user message')
-        self.assertEqual(list(storage)[1], 'cookie')
+        self.assertEqual(list(storage)[1].message, 'cookie')
Index: contrib/messages/storage/cookie.py
===================================================================
--- contrib/messages/storage/cookie.py	(revision 14213)
+++ contrib/messages/storage/cookie.py	(working copy)
@@ -7,6 +7,7 @@
 from django.utils import simplejson as json
 from django.utils.hashcompat import sha_hmac
 
+from django.contrib.messages.restrictions import AmountRestriction, TimeRestriction
 
 class MessageEncoder(json.JSONEncoder):
     """
@@ -14,11 +15,18 @@
     """
     message_key = '__json_message'
 
+    def __encode_restriction (self, r):
+        if r.type == 'amount': ret = '%s@%s' % ('a', r.can_be_shown)
+        elif r.type == 'time': ret = '%s@%s' % ('t', r.expires)
+        return ret
+
     def default(self, obj):
         if isinstance(obj, Message):
             message = [self.message_key, obj.level, obj.message]
             if obj.extra_tags:
                 message.append(obj.extra_tags)
+            restrictions = [self.__encode_restriction(r) for r in obj.restrictions]
+            message.append (restrictions)
             return message
         return super(MessageEncoder, self).default(obj)
 
@@ -27,11 +35,33 @@
     """
     Decodes JSON that includes serialized ``Message`` instances.
     """
+    def __get_restrictions (self, restrictions):
+        def _get_r(type, value):
+            if type == 'a':  return AmountRestriction (value)
+            elif type == 't':
+                ret = TimeRestriction(0)
+                ret.set_expirity_time(value)
+                return ret
+        ret = []
+        for r in restrictions:
+            rtype, value = r.split('@')
+            added = _get_r (rtype, value)
+            ret.append (added)
+        return ret
+        #return [_get_r(rtype, value) for rtype, value in [r.split('@') for r in restrictions] ]
 
+
+    def create_message(self, vars):
+        ''' creates message on the basis of encoded data '''
+        args = vars[:-1]
+        restrictions = vars[-1]
+        restrictions = self.__get_restrictions (restrictions)
+        return Message(*args, **{'restrictions': restrictions})
+
     def process_messages(self, obj):
         if isinstance(obj, list) and obj:
             if obj[0] == MessageEncoder.message_key:
-                return Message(*obj[1:])
+                return self.create_message (obj[1:])
             return [self.process_messages(item) for item in obj]
         if isinstance(obj, dict):
             return dict([(key, self.process_messages(value))
@@ -136,7 +166,10 @@
         """
         if not data:
             return None
-        bits = data.split('$', 1)
+        try:
+            bits = data.split('$', 1)
+        except: 
+            set_trace ()
         if len(bits) == 2:
             hash, value = bits
             if hash == self._hash(value):
@@ -150,3 +183,6 @@
         # with the data.
         self.used = True
         return None
+ 
+    def transport_storage(self, response, request):
+        storage.request.COOKIES['messages'] = response.cookies['messages'].value
Index: contrib/messages/storage/base.py
===================================================================
--- contrib/messages/storage/base.py	(revision 14213)
+++ contrib/messages/storage/base.py	(working copy)
@@ -1,10 +1,11 @@
 from django.conf import settings
 from django.utils.encoding import force_unicode, StrAndUnicode
-from django.contrib.messages import constants, utils
+from django.contrib.messages import constants, utils, restrictions as res
 
 
 LEVEL_TAGS = utils.get_level_tags()
 
+	
 
 class Message(StrAndUnicode):
     """
@@ -13,12 +14,15 @@
     or template.
     """
 
-    def __init__(self, level, message, extra_tags=None):
+    def __init__(self, level, message, extra_tags=None, restrictions = []):
         self.level = int(level)
         self.message = message
         self.extra_tags = extra_tags
 
-    def _prepare(self):
+        self.restrictions = restrictions or list([res.AmountRestriction(1)])
+        # if not given any restriction - one show by default
+
+    def _prepare(self): # todo: slef.restrictions = 
         """
         Prepares the message for serialization by forcing the ``message``
         and ``extra_tags`` to unicode in case they are lazy translations.
@@ -31,11 +35,13 @@
 
     def __eq__(self, other):
         return isinstance(other, Message) and self.level == other.level and \
-                                              self.message == other.message
+                                              self.message == other.message and \
+                                              self.restrictions == other.restrictions
 
     def __unicode__(self):
-        return force_unicode(self.message)
+        return force_unicode(self.message)  + ':' + unicode('CFVB')
 
+
     def _get_tags(self):
         label_tag = force_unicode(LEVEL_TAGS.get(self.level, ''),
                                   strings_only=True)
@@ -50,6 +56,17 @@
     tags = property(_get_tags)
 
 
+    def active (self):
+        for r in self.restrictions:
+            if r.to_remove(): return False
+        return True
+
+
+    def on_iter (self):
+        for r in self.restrictions:
+            r.on_iter ()
+
+
 class BaseStorage(object):
     """
     This is the base backend for temporary message storage.
@@ -60,8 +77,8 @@
 
     def __init__(self, request, *args, **kwargs):
         self.request = request
+        self.used = False
         self._queued_messages = []
-        self.used = False
         self.added_new = False
         super(BaseStorage, self).__init__(*args, **kwargs)
 
@@ -69,12 +86,19 @@
         return len(self._loaded_messages) + len(self._queued_messages)
 
     def __iter__(self):
-        self.used = True
-        if self._queued_messages:
-            self._loaded_messages.extend(self._queued_messages)
-            self._queued_messages = []
-        return iter(self._loaded_messages)
+        if not self.used:
+            self.used = True
+            if self._queued_messages:
+                self._loaded_messages.extend(self._queued_messages)
+                self._queued_messages = []
 
+            active_messages = [x for x in self._loaded_messages if x.active ()]
+            for x in active_messages:
+                x.on_iter ()
+            self._queued_messages.extend (active_messages)
+        return iter(self._queued_messages)
+
+
     def __contains__(self, item):
         return item in self._loaded_messages or item in self._queued_messages
 
@@ -104,6 +128,12 @@
         """
         raise NotImplementedError()
 
+    def filter_store(self, messages, response, *args, **kwargs):
+        ''' stores only active messages from given messages in storage '''
+        filtered_messages = [x for x in messages if x.active ()]
+        return self._store(filtered_messages, response, *args, **kwargs)
+
+
     def _store(self, messages, response, *args, **kwargs):
         """
         Stores a list of messages, returning a list of any messages which could
@@ -130,14 +160,16 @@
         be stored again. Otherwise, only messages added after the last
         iteration will be stored.
         """
+        # if used or used and added, then _queued_messages contains all messages that should be saved
+        # if added, then save: all messages currently stored and added ones
         self._prepare_messages(self._queued_messages)
         if self.used:
-            return self._store(self._queued_messages, response)
+            return self.filter_store(self._queued_messages, response)
         elif self.added_new:
             messages = self._loaded_messages + self._queued_messages
-            return self._store(messages, response)
+            return self.filter_store(messages, response)
 
-    def add(self, level, message, extra_tags=''):
+    def add(self, level, message, extra_tags='', restrictions = []):
         """
         Queues a message to be stored.
 
@@ -152,7 +184,7 @@
             return
         # Add the message.
         self.added_new = True
-        message = Message(level, message, extra_tags=extra_tags)
+        message = Message(level, message, extra_tags=extra_tags, restrictions = restrictions)
         self._queued_messages.append(message)
 
     def _get_level(self):
Index: contrib/messages/storage/session.py
===================================================================
--- contrib/messages/storage/session.py	(revision 14213)
+++ contrib/messages/storage/session.py	(working copy)
@@ -22,9 +22,9 @@
         """
         return self.request.session.get(self.session_key), True
 
-    def _store(self, messages, response, *args, **kwargs):
+    def _store(self, messages, response, *args, **kwargs): # overrides all messages existing in storage so far
         """
-        Stores a list of messages to the request's session.
+        Stores a list of messages to the request's session. 
         """
         if messages:
             self.request.session[self.session_key] = messages
