/plone/app/portlets/tests/test_formextender.py

https://github.com/plone/plone.app.portlets
Python | 266 lines | 200 code | 47 blank | 19 comment | 8 complexity | 8bd5f8316bd80f2c902c994d7c83a225 MD5 | raw file
  1. from plone.app.portlets.browser.interfaces import IPortletAddForm
  2. from plone.app.portlets.browser.interfaces import IPortletEditForm
  3. from plone.app.portlets.portlets import news
  4. from plone.app.portlets.storage import PortletAssignmentMapping
  5. from plone.app.portlets.tests.base import PortletsTestCase
  6. from plone.portlets.interfaces import IPortletAssignment
  7. from plone.portlets.interfaces import IPortletAssignmentSettings
  8. from plone.portlets.interfaces import IPortletManager
  9. from plone.portlets.interfaces import IPortletRenderer
  10. from plone.portlets.interfaces import IPortletType
  11. from plone.z3cform.fieldsets.extensible import FormExtender
  12. from plone.z3cform.fieldsets.interfaces import IFormExtender
  13. from z3c.form import field
  14. from zope import schema
  15. from zope.component import adapter
  16. from zope.component import getGlobalSiteManager
  17. from zope.component import getMultiAdapter
  18. from zope.component import getUtility
  19. from zope.interface import implementer
  20. from zope.interface import Interface
  21. from zope.publisher.interfaces.browser import IDefaultBrowserLayer
  22. # A sample schemaextender:
  23. EXTENDER_PREFIX = "portletcssclass"
  24. class IPortletCssClass(Interface):
  25. """Schema for portlet css class"""
  26. # css_class is just an example.
  27. # In real life a css_class implementation would be a
  28. # Choice field with a vocabulary, editable in a controlpanel.
  29. css_class = schema.TextLine(title="Portlet appearance", required=False)
  30. class PortletCssClassFormExtender(FormExtender):
  31. def update(self):
  32. self.add(IPortletCssClass, prefix=EXTENDER_PREFIX)
  33. @adapter(IPortletAssignment)
  34. @implementer(IPortletCssClass)
  35. class PortletCssClassAdapter:
  36. def __init__(self, context):
  37. # avoid recursion
  38. self.__dict__["context"] = context
  39. def __setattr__(self, attr, value):
  40. settings = IPortletAssignmentSettings(self.context)
  41. settings[attr] = value
  42. def __getattr__(self, attr):
  43. settings = IPortletAssignmentSettings(self.context)
  44. return settings.get(attr, None)
  45. class TestSchemaExtender(PortletsTestCase):
  46. def test_addform_fields(self):
  47. schema_field_names = [k for k in field.Fields(news.INewsPortlet).keys()]
  48. # We use the news portlet as a random example of a portlet
  49. portlet = getUtility(IPortletType, name="portlets.News")
  50. mapping = self.portal.restrictedTraverse("++contextportlets++plone.leftcolumn")
  51. addview = mapping.restrictedTraverse("+/" + portlet.addview)
  52. addview.update()
  53. addview_field_names = [k for k in addview.fields.keys()]
  54. # Our addview schema before we register our extender:
  55. self.assertEqual(addview_field_names, schema_field_names)
  56. # Register our schemaextender
  57. gsm = getGlobalSiteManager()
  58. gsm.registerAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  59. gsm.registerAdapter(
  60. PortletCssClassFormExtender,
  61. (Interface, IDefaultBrowserLayer, IPortletAddForm),
  62. IFormExtender,
  63. "portletcssclass.extender",
  64. )
  65. mapping = self.portal.restrictedTraverse("++contextportlets++plone.leftcolumn")
  66. addview = mapping.restrictedTraverse("+/" + portlet.addview)
  67. addview.update()
  68. addview_field_names = [k for k in addview.fields.keys()]
  69. gsm.unregisterAdapter(
  70. PortletCssClassFormExtender,
  71. (Interface, IDefaultBrowserLayer, IPortletAddForm),
  72. IFormExtender,
  73. "portletcssclass.extender",
  74. )
  75. gsm.unregisterAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  76. # Our addview schema now includes our extended schema:
  77. self.assertEqual(
  78. addview_field_names, schema_field_names + [EXTENDER_PREFIX + ".css_class"]
  79. )
  80. def test_invoke_add_form(self):
  81. portlet = getUtility(IPortletType, name="portlets.News")
  82. mapping = self.portal.restrictedTraverse("++contextportlets++plone.leftcolumn")
  83. for m in mapping.keys():
  84. del mapping[m]
  85. addview = mapping.restrictedTraverse("+/" + portlet.addview)
  86. addview.update()
  87. addview.createAndAdd(
  88. data={"count": 5, EXTENDER_PREFIX + ".css_class": "my-class"}
  89. )
  90. portlet_assignment = mapping.values()[0]
  91. settings = IPortletAssignmentSettings(portlet_assignment)
  92. self.assertEqual(portlet_assignment.count, 5)
  93. # We have not extended our storage adapter, so nothing gets saved:
  94. self.assertIsNone(settings.get("css_class", None))
  95. # Register our schemaextender
  96. gsm = getGlobalSiteManager()
  97. gsm.registerAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  98. gsm.registerAdapter(
  99. PortletCssClassFormExtender,
  100. (Interface, IDefaultBrowserLayer, IPortletAddForm),
  101. IFormExtender,
  102. "portletcssclass.extender",
  103. )
  104. for m in mapping.keys():
  105. del mapping[m]
  106. addview = mapping.restrictedTraverse("+/" + portlet.addview)
  107. addview.update()
  108. addview.createAndAdd(
  109. data={"count": 5, EXTENDER_PREFIX + ".css_class": "my-class"}
  110. )
  111. portlet_assignment = mapping.values()[0]
  112. settings = IPortletAssignmentSettings(portlet_assignment)
  113. gsm.unregisterAdapter(
  114. PortletCssClassFormExtender,
  115. (Interface, IDefaultBrowserLayer, IPortletAddForm),
  116. IFormExtender,
  117. "portletcssclass.extender",
  118. )
  119. gsm.unregisterAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  120. self.assertEqual(portlet_assignment.count, 5)
  121. # The prefix is used for the form field, not for the stored data:
  122. self.assertEqual(settings.get("css_class"), "my-class")
  123. def test_editform_fields(self):
  124. schema_field_names = [k for k in field.Fields(news.INewsPortlet).keys()]
  125. mapping = PortletAssignmentMapping()
  126. request = self.folder.REQUEST
  127. mapping["foo"] = news.Assignment(count=5)
  128. editview = getMultiAdapter((mapping["foo"], request), name="edit")
  129. editview.update()
  130. editview_field_names = [k for k in editview.fields.keys()]
  131. # Our editview schema before we register our extender:
  132. self.assertEqual(editview_field_names, schema_field_names)
  133. # Register our schemaextender
  134. gsm = getGlobalSiteManager()
  135. gsm.registerAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  136. gsm.registerAdapter(
  137. PortletCssClassFormExtender,
  138. (Interface, IDefaultBrowserLayer, IPortletEditForm),
  139. IFormExtender,
  140. "portletcssclass.extender",
  141. )
  142. mapping = PortletAssignmentMapping()
  143. request = self.folder.REQUEST
  144. mapping["foo"] = news.Assignment(count=5)
  145. editview = getMultiAdapter((mapping["foo"], request), name="edit")
  146. editview.update()
  147. editview_field_names = [k for k in editview.fields.keys()]
  148. gsm.unregisterAdapter(
  149. PortletCssClassFormExtender,
  150. (Interface, IDefaultBrowserLayer, IPortletEditForm),
  151. IFormExtender,
  152. "portletcssclass.extender",
  153. )
  154. gsm.unregisterAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  155. # Our editview schema now includes our extended schema:
  156. self.assertEqual(
  157. editview_field_names, schema_field_names + [EXTENDER_PREFIX + ".css_class"]
  158. )
  159. def test_invoke_edit_form(self):
  160. mapping = PortletAssignmentMapping()
  161. request = self.folder.REQUEST
  162. mapping["foo"] = news.Assignment(count=5)
  163. editview = getMultiAdapter((mapping["foo"], request), name="edit")
  164. editview.update()
  165. editview.applyChanges(
  166. data={"count": 6, EXTENDER_PREFIX + ".css_class": "my-class"}
  167. )
  168. portlet_assignment = mapping.values()[0]
  169. settings = IPortletAssignmentSettings(portlet_assignment)
  170. self.assertEqual(portlet_assignment.count, 6)
  171. # We have not extended our storage adapter, so nothing gets saved:
  172. self.assertIsNone(settings.get("css_class", None))
  173. # Register our schemaextender
  174. gsm = getGlobalSiteManager()
  175. gsm.registerAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  176. gsm.registerAdapter(
  177. PortletCssClassFormExtender,
  178. (Interface, IDefaultBrowserLayer, IPortletEditForm),
  179. IFormExtender,
  180. "portletcssclass.extender",
  181. )
  182. mapping = PortletAssignmentMapping()
  183. request = self.folder.REQUEST
  184. mapping["foo"] = news.Assignment(count=5)
  185. editview = getMultiAdapter((mapping["foo"], request), name="edit")
  186. editview.update()
  187. editview.applyChanges(
  188. data={"count": 6, EXTENDER_PREFIX + ".css_class": "my-class"}
  189. )
  190. portlet_assignment = mapping.values()[0]
  191. settings = IPortletAssignmentSettings(portlet_assignment)
  192. gsm.unregisterAdapter(
  193. PortletCssClassFormExtender,
  194. (Interface, IDefaultBrowserLayer, IPortletEditForm),
  195. IFormExtender,
  196. "portletcssclass.extender",
  197. )
  198. gsm.unregisterAdapter(PortletCssClassAdapter, (IPortletAssignment,))
  199. self.assertEqual(portlet_assignment.count, 6)
  200. # The prefix is used for the form field, not for the stored data:
  201. self.assertEqual(settings.get("css_class"), "my-class")
  202. def test_renderer(self):
  203. context = self.folder
  204. request = self.folder.REQUEST
  205. view = self.folder.restrictedTraverse("@@plone")
  206. manager = getUtility(
  207. IPortletManager, name="plone.leftcolumn", context=self.portal
  208. )
  209. assignment = news.Assignment(count=5)
  210. renderer = getMultiAdapter(
  211. (context, request, view, manager, assignment), IPortletRenderer
  212. )
  213. self.assertTrue(isinstance(renderer, news.Renderer))
  214. def test_suite():
  215. from unittest import makeSuite
  216. from unittest import TestSuite
  217. suite = TestSuite()
  218. suite.addTest(makeSuite(TestSchemaExtender))
  219. return suite