diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.py b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.py
index d786e6456623110b38d9126b6e7e46aa08388d31..948d68fe29b8aedfa85ab687da8c7b56d6bf55fe 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.py
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.py
@@ -24,6 +24,8 @@ return context.ERP5Document_getHateoas(
   sort_on=sort_on,
   local_roles=local_roles,
   selection_domain=selection_domain,
+  restricted=1,
   extra_param_json=extra_param_json,
-  restricted=1
+  portal_status_message=portal_status_message,
+  portal_status_level=portal_status_level
 )
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.xml b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.xml
index 8419ac85974e8dbfa49cefeac594749ca4c41f8b..82ce578fca12dc20d6481e30d0487adefd5b5456 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.xml
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_restricted_style/ERP5Document_getHateoas.xml
@@ -50,7 +50,7 @@
         </item>
         <item>
             <key> <string>_params</string> </key>
-            <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, relative_url=None, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None</string> </value>
+            <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, relative_url=None, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value>
         </item>
         <item>
             <key> <string>id</string> </key>
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.py b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.py
index f8c82a00b4d90e35e605f80a1991da00e335d89a..0ed9e08378cec97b4f688e2b09a6ed63b22fc4c5 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.py
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.py
@@ -1,2 +1,24 @@
+"""Render form while keeping its values back to user.
+
+This script differs from Base_redirect that it keeps the form values in place.
+
+:param message: {str} message to be displayed at the user
+:param level: {str|int} severity of the message using ERP5Type.Log levels or their names like 'info', 'warn', 'error'
+:param keep_items: {dict} items to be available in the next call. They will be either added as hidden fields to the
+                   rendered form or in case of "portal_status_message" just displayed to the user
+:param REQUEST: request
+:param **kwargs: should contain parameters to ERP5Document_getHateoas such as 'query' to replace Selections
+"""
+
+keep_items = keep_items or {}
+
 form = getattr(context, form_id)
-return context.ERP5Document_getHateoas(form=form, mode='form')
+
+if not message and "portal_status_message" in keep_items:
+  message = keep_items.pop("portal_status_message")
+
+if not level and "portal_status_level" in keep_items:
+  level = keep_items.pop("portal_status_level")
+
+return context.ERP5Document_getHateoas(form=form, mode='form', REQUEST=REQUEST, extra_param_json=keep_items,
+                                       portal_status_message=message, portal_status_level=level, **kwargs)
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.xml b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.xml
index e1e14e9bd773c3bb32819968f3519efd1e8a1135..a1ff8d1688fb0aee531c8ca728f8a384018a4e4a 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.xml
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/Base_renderForm.xml
@@ -50,7 +50,7 @@
         </item>
         <item>
             <key> <string>_params</string> </key>
-            <value> <string>form_id</string> </value>
+            <value> <string>form_id, message=\'\', level=None, keep_items=None, REQUEST=None, **kwargs</string> </value>
         </item>
         <item>
             <key> <string>id</string> </key>
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
index ff8fd787c9844c82858c2e7f9b366812628ffd5a..4d676e1d26829850bf4bbc8562b60696ddb1fa32 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
@@ -41,10 +41,10 @@ import time
 from email.Utils import formatdate
 import re
 from zExceptions import Unauthorized
-from Products.ERP5Type.Utils import UpperCase
+from Products.ERP5Type.Log import log, DEBUG, INFO, WARNING, ERROR
 from Products.ERP5Type.Message import Message
+from Products.ERP5Type.Utils import UpperCase
 from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
-from Products.ERP5Type.Log import log
 from collections import OrderedDict
 from urlparse import urlparse
 
@@ -1190,33 +1190,22 @@ def renderFormDefinition(form, response_dict):
   response_dict["action"] = form.action
   response_dict["update_action"] = form.update_action
 
-mime_type = 'application/hal+json'
-portal = context.getPortalObject()
-sql_catalog = portal.portal_catalog.getSQLCatalog()
-
-# Calculate the site root to prevent unexpected browsing
-is_web_mode = (context.REQUEST.get('current_web_section', None) is not None) or (hasattr(context, 'isWebMode') and context.isWebMode())
-# is_web_mode =  traversed_document.isWebMode()
-if is_web_mode:
-  site_root = context.getWebSectionValue()
-  view_action_type = site_root.getLayoutProperty("configuration_view_action_category", default='object_view')
-else:
-  site_root = portal
-  view_action_type = "object_view"
-
-context.Base_prepareCorsResponse(RESPONSE=response)
-
-# Check if traversed_document is the site_root
-if relative_url:
-  temp_traversed_document = site_root.restrictedTraverse(relative_url, None)
-  if (temp_traversed_document is None):
-    response.setStatus(404)
-    return ""
-else:
-  temp_traversed_document = context
+def statusLevelToString(level):
+  """Transform any level format to lowercase string representation"""
+  if isinstance(level, (str, unicode)):
+    if level.lower() == "error":
+      return "error"
+    elif level.lower().startswith("warn"):
+      return "error"  # we might want to add another level for warning
+    else:
+      return "success"
+  if level == ERROR:
+    return "error"
+  elif level == WARNING:
+    return "error"
+  else:
+    return "success"
 
-temp_is_site_root = (temp_traversed_document.getPath() == site_root.getPath())
-temp_is_portal = (temp_traversed_document.getPath() == portal.getPath())
 
 def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, REQUEST=None,
                      response=None, view=None, mode=None, query=None,
@@ -1260,9 +1249,18 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
         "name": portal.getTitle(),
       }
     }
+    # possible other attributes
+    # _notification {dict} form of {'message': "", 'status': ""}
+    # _embedded {dict} form of {"_view": <erp5_document_properties>}
   }
-  
-  
+
+  # Inject notification into response no matter the kind of request
+  if portal_status_message:
+    result_dict['_notification'] = {
+      'message': str(portal_status_message),
+      'status': statusLevelToString(portal_status_level)
+    }
+
   if (restricted == 1) and (portal.portal_membership.isAnonymousUser()):
     login_relative_url = site_root.getLayoutProperty("configuration_login", default="")
     if (login_relative_url):
@@ -2101,6 +2099,35 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
   
   return result_dict
 
+
+mime_type = 'application/hal+json'
+portal = context.getPortalObject()
+sql_catalog = portal.portal_catalog.getSQLCatalog()
+
+# Calculate the site root to prevent unexpected browsing
+is_web_mode = (context.REQUEST.get('current_web_section', None) is not None) or (hasattr(context, 'isWebMode') and context.isWebMode())
+# is_web_mode =  traversed_document.isWebMode()
+if is_web_mode:
+  site_root = context.getWebSectionValue()
+  view_action_type = site_root.getLayoutProperty("configuration_view_action_category", default='object_view')
+else:
+  site_root = portal
+  view_action_type = "object_view"
+
+context.Base_prepareCorsResponse(RESPONSE=response)
+
+# Check if traversed_document is the site_root
+if relative_url:
+  temp_traversed_document = site_root.restrictedTraverse(relative_url, None)
+  if (temp_traversed_document is None):
+    response.setStatus(404)
+    return ""
+else:
+  temp_traversed_document = context
+
+temp_is_site_root = (temp_traversed_document.getPath() == site_root.getPath())
+temp_is_portal = (temp_traversed_document.getPath() == portal.getPath())
+
 response.setHeader('Content-Type', mime_type)
 hateoas = calculateHateoas(is_portal=temp_is_portal, is_site_root=temp_is_site_root,
                            traversed_document=temp_traversed_document,
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
index cd553162857da696e20964c0e0d78adfa362ed82..c0b0e3cff4ff6c4393c7aaeef60ad20b8471e905 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
@@ -56,7 +56,7 @@
         </item>
         <item>
             <key> <string>_params</string> </key>
-            <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, relative_url=None, restricted=0, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None</string> </value>
+            <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, relative_url=None, restricted=0, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value>
         </item>
         <item>
             <key> <string>id</string> </key>
diff --git a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
index ace84ff175e52136d4bf1711610155cd5bf72fca..1057d4fda5868100d31672853f1bcac11ccf71e4 100644
--- a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
+++ b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
@@ -1615,6 +1615,28 @@ return portal.portal_simulation.getInventoryList(section_uid=context.getUid())
     self.assertEqual(result_dict['_embedded'].get('count', None), None)
 
 
+class TestERP5Document_getHateoas_mode_form(ERP5HALJSONStyleSkinsMixin):
+
+  @simulate('Base_getRequestHeader', '*args, **kwargs',
+            'return "application/hal+json"')
+  @createIndexedDocument()
+  @changeSkin('Hal')
+  def test_getHateoasForm_message(self, document):
+    fake_request = do_fake_request("POST")
+
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
+      REQUEST=fake_request, mode="form", relative_url=document.getRelativeUrl(),
+      form=getattr(document, 'Foo_view'), portal_status_message="Couscous", portal_status_level='error')
+
+    self.assertEquals(fake_request.RESPONSE.status, 200)
+    self.assertEquals(fake_request.RESPONSE.getHeader('Content-Type'),
+      "application/hal+json"
+    )
+    result_dict = json.loads(result)
+    self.assertEqual(result_dict["_notification"]["status"], "error")
+    self.assertEqual(result_dict["_notification"]["message"], "Couscous")
+
+
 class TestERP5Document_getHateoas_mode_bulk(ERP5HALJSONStyleSkinsMixin):
 
   @simulate('Base_getRequestHeader', '*args, **kwargs',
diff --git a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.py b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.py
index 0c2a28ee71c244e1429d82149e4a71e43a7725cd..285831f432424d058c6b8008faff422d35852b69 100644
--- a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.py
+++ b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.py
@@ -1 +1,26 @@
+"""Render form while keeping its values back to user.
+
+This script differs from Base_redirect that it keeps the form values in place.
+
+:param message: {str} message to be displayed at the user
+:param level: {str|int} is ignored in XHTML style - no support for message level distinction
+:param keep_items: {dict} items to be available in the next call. They will be either added as hidden fields to the
+                   rendered form or in case of "portal_status_message" just displayed to the user
+:param REQUEST: request
+:param **kwargs: is used to pass necessary parameters to overcome backend-held state (aka Selections)
+"""
+
+keep_items = keep_items or {}
+
+if message and "portal_status_message" not in keep_items:
+  keep_items["portal_status_message"] = message
+
+keep_items.pop("portal_status_level", None)
+
+if REQUEST is None:
+  REQUEST = context.REQUEST
+
+for key, value in keep_items.items():
+  REQUEST.set(key, value)
+
 return getattr(context, form_id)()
diff --git a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.xml b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.xml
index e1e14e9bd773c3bb32819968f3519efd1e8a1135..a1ff8d1688fb0aee531c8ca728f8a384018a4e4a 100644
--- a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.xml
+++ b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/Base_renderForm.xml
@@ -50,7 +50,7 @@
         </item>
         <item>
             <key> <string>_params</string> </key>
-            <value> <string>form_id</string> </value>
+            <value> <string>form_id, message=\'\', level=None, keep_items=None, REQUEST=None, **kwargs</string> </value>
         </item>
         <item>
             <key> <string>id</string> </key>