From: David G. <svn...@pl...> - 2010-05-29 17:53:05
|
Author: davisagli Date: Sat May 29 17:52:55 2010 New Revision: 36808 Added: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/failsafe_main_template.pt sandbox/plone.app.skineditor/trunk/plone/app/skineditor/tests/test_viewlets.py sandbox/plone.app.skineditor/trunk/plone/app/skineditor/viewlet.py Modified: sandbox/plone.app.skineditor/trunk/TODO.txt sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/configure.zcml sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.pt sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.py sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/resources/skineditor.js sandbox/plone.app.skineditor/trunk/plone/app/skineditor/configure.zcml Log: add support for viewlets; add failsafe main_template in case of breakage Modified: sandbox/plone.app.skineditor/trunk/TODO.txt ============================================================================== --- sandbox/plone.app.skineditor/trunk/TODO.txt (original) +++ sandbox/plone.app.skineditor/trunk/TODO.txt Sat May 29 17:52:55 2010 @@ -9,6 +9,7 @@ - (first check to confirm if this is an issue) - on popup close, update opened drawers rather than entire page - option to show only customized resources +- fix lookup of layers for 2 views with the same name but different contexts Research: - warning on customizing something that has already been customized Modified: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/configure.zcml ============================================================================== --- sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/configure.zcml (original) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/configure.zcml Sat May 29 17:52:55 2010 @@ -27,4 +27,11 @@ name="plone.app.skineditor.css" file="resources/style.css" /> + <browser:page + for="*" + name="failsafe_main_template" + template="failsafe_main_template.pt" + permission="zope.Public" + /> + </configure> Modified: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.pt ============================================================================== --- sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.pt (original) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.pt Sat May 29 17:52:55 2010 @@ -1,15 +1,15 @@ +<tal:block tal:define="main_template view/main_template"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" xmlns:i18n="http://xml.zope.org/namespaces/i18n" - metal:use-macro="context/main_template/macros/master" + metal:use-macro="context/?main_template/macros/master" + tal:define="dummy python:request.set('disable_border',1)" i18n:domain="plone"> <head> -<tal:block metal:fill-slot="top_slot" - tal:define="dummy python:request.set('disable_border',1)"> -</tal:block> + <tal:javascript metal:fill-slot="javascript_head_slot"> <script type="text/javascript" src="++resource++plone.app.skineditor.js"></script> </tal:javascript> @@ -57,3 +57,4 @@ </body> </html> +</tal:block> \ No newline at end of file Modified: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.py ============================================================================== --- sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.py (original) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/console.py Sat May 29 17:52:55 2010 @@ -6,8 +6,17 @@ class SkinsConsole(BrowserView): - index = ViewPageTemplateFile('console.pt') + console = ViewPageTemplateFile('console.pt') layers = ViewPageTemplateFile('layers.pt') + + main_template = 'main_template' + + def index(self): + try: + return self.console() + except: + self.main_template = '@@failsafe_main_template' + return self.console() @memoize def results(self, exact=False): Added: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/failsafe_main_template.pt ============================================================================== --- (empty file) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/failsafe_main_template.pt Sat May 29 17:52:55 2010 @@ -0,0 +1,30 @@ +<metal:page define-macro="master"> +<metal:block define-slot="top_slot" /> +<tal:doctype tal:replace="structure string:<!DOCTYPE html PUBLIC + "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">" /> + +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:tal="http://xml.zope.org/namespaces/tal" + xmlns:metal="http://xml.zope.org/namespaces/metal" + xmlns:i18n="http://xml.zope.org/namespaces/i18n" + xml:lang="en" + lang="en" + tal:define="portal_state context/@@plone_portal_state; + context_state context/@@plone_context_state; + plone_view context/@@plone; + lang portal_state/language; + view nocall:view | nocall: plone_view; + dummy python: plone_view.mark_view(view); + portal_url portal_state/portal_url; + checkPermission nocall: context/portal_membership/checkPermission; + site_properties context/portal_properties/site_properties; + ajax_load request/ajax_load | nothing" + tal:attributes="lang lang; + xml:lang lang"> + + <body> + <metal:slot metal:define-slot="main"/> + </body> +</html> +</metal:page> Modified: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/resources/skineditor.js ============================================================================== --- sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/resources/skineditor.js (original) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/browser/resources/skineditor.js Sat May 29 17:52:55 2010 @@ -21,7 +21,7 @@ } jq('form', content).each(function() { var action = jq(this).attr('action'); - if (action.charAt(0) == '/') { + if (action.charAt(0) != '/') { jq(this).attr('action', base.substr(0,base.lastIndexOf('/')) + '/' + action); } }); Modified: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/configure.zcml ============================================================================== --- sandbox/plone.app.skineditor/trunk/plone/app/skineditor/configure.zcml (original) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/configure.zcml Sat May 29 17:52:55 2010 @@ -8,5 +8,7 @@ name="cmfskins"/> <utility factory=".zopeview.ZopeViewResourceType" name="zopeview"/> + <utility factory=".viewlet.ViewletResourceType" + name="viewlet"/> </configure> Added: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/tests/test_viewlets.py ============================================================================== --- (empty file) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/tests/test_viewlets.py Sat May 29 17:52:55 2010 @@ -0,0 +1,114 @@ +import unittest +import zope.component.testing +from zope.component import provideAdapter +from zope.app.component.hooks import setSite, setHooks +from zope.component.registry import Components +from zope.interface import Interface, implements +from zope.publisher.interfaces import IView +from zope.publisher.interfaces.browser import IBrowserRequest + +from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile +from five.customerize.zpt import TTWViewTemplate +from plone.app.customerize.tool import ViewTemplateContainer +from plone.app.skineditor.viewlet import ViewletResourceType +from zope.viewlet.interfaces import IViewlet, IViewletManager +from zope.viewlet.viewlet import ViewletBase +#from plone.app.viewletmanager.manager import OrderedViewletManager + +class IDummyBrowserLayer(IBrowserRequest): + pass + +class DummyRequest(object): + implements(IDummyBrowserLayer) + +class DummySite(object): + REQUEST = DummyRequest() + def __init__(self): + self.registry = Components('components') + self.portal_view_customizations = ViewTemplateContainer() + def getSiteManager(self): + return self.registry + +class IDummyViewletManager(IViewletManager): + pass + +class SimpleViewlet(ViewletBase): + index = ViewPageTemplateFile('cmf_test_skins/test/test.pt') + +class ViewletResourceTestLayer: + + @classmethod + def setUp(self): + # global view registration + provideAdapter(SimpleViewlet, + (Interface, IBrowserRequest, IView, IDummyViewletManager), + provides=IViewlet, name='viewlet') + provideAdapter(SimpleViewlet, + (Interface, IDummyBrowserLayer, IView, IDummyViewletManager), + provides=IViewlet, name='viewlet') + # local view registration + site = DummySite() + setSite(site) + setHooks() + custom_zpt = TTWViewTemplate( + id = 'customized_viewlet', + text = 'test', + view = SimpleViewlet, + permission = 'View', + name = 'viewlet') + site.portal_view_customizations.addTemplate('customized_viewlet', custom_zpt) + sm = site.getSiteManager() + sm.registerAdapter(custom_zpt, + (Interface, IDummyBrowserLayer, IView, IDummyViewletManager), + provided=IViewlet, name='viewlet') + + @classmethod + def tearDown(self): + zope.component.testing.tearDown() + + +class TestViewletResourceType(unittest.TestCase): + layer = ViewletResourceTestLayer + + def setUp(self): + self.rt = ViewletResourceType() + + def test_resource_order_and_attributes(self): + resources = list(self.rt) + self.assertEqual(len(resources), 3) + + # first resource: local registration, specific browser layer + res = resources[0] + self.assertEqual(res.name, 'viewlet') + self.assertEqual(res.type, 'viewlet') + self.assertEqual(res.description, 'Viewlet for * in the plone.app.skineditor.tests.test_viewlets.IDummyViewletManager manager') + self.assertEqual(res.info, 'In the database: portal_view_customizations/customized_viewlet') + self.assertEqual(res.context, ('zope.interface.Interface', 'plone.app.skineditor.tests.test_viewlets.IDummyViewletManager')) + self.assertEqual(res.layer, 'plone.app.skineditor.tests.test_viewlets.IDummyBrowserLayer') + self.assertEqual(res.actions, [('Edit', 'customized_viewlet/manage_main'), + ('Remove', '/manage_delObjects?ids=customized_viewlet')]) + + # second resource: global registration, specific browser layer + res = resources[1] + self.assertEqual(res.name, 'viewlet') + self.assertEqual(res.type, 'viewlet') + self.assertEqual(res.description, 'Viewlet for * in the plone.app.skineditor.tests.test_viewlets.IDummyViewletManager manager') + self.failUnless(res.info.startswith('On the filesystem: ')) + self.failUnless(res.info.endswith('cmf_test_skins/test/test.pt')) + self.assertEqual(res.context, ('zope.interface.Interface', 'plone.app.skineditor.tests.test_viewlets.IDummyViewletManager')) + self.assertEqual(res.layer, 'plone.app.skineditor.tests.test_viewlets.IDummyBrowserLayer') + self.assertEqual(res.actions, [('View', '/@@customizezpt.html?required=zope.interface.Interface,plone.app.skineditor.tests.test_viewlets.IDummyBrowserLayer,zope.browser.interfaces.IView,plone.app.skineditor.tests.test_viewlets.IDummyViewletManager&view_name=viewlet')]) + + # third resource: global registration, general browser layer + res = resources[2] + self.assertEqual(res.name, 'viewlet') + self.assertEqual(res.type, 'viewlet') + self.assertEqual(res.description, 'Viewlet for * in the plone.app.skineditor.tests.test_viewlets.IDummyViewletManager manager') + self.failUnless(res.info.startswith('On the filesystem: ')) + self.failUnless(res.info.endswith('cmf_test_skins/test/test.pt')) + self.assertEqual(res.context, ('zope.interface.Interface', 'plone.app.skineditor.tests.test_viewlets.IDummyViewletManager')) + self.assertEqual(res.layer, 'zope.publisher.interfaces.browser.IBrowserRequest') + self.assertEqual(res.actions, [('View', '/@@customizezpt.html?required=zope.interface.Interface,zope.publisher.interfaces.browser.IBrowserRequest,zope.browser.interfaces.IView,plone.app.skineditor.tests.test_viewlets.IDummyViewletManager&view_name=viewlet')]) + +def test_suite(): + return unittest.defaultTestLoader.loadTestsFromName(__name__) Added: sandbox/plone.app.skineditor/trunk/plone/app/skineditor/viewlet.py ============================================================================== --- (empty file) +++ sandbox/plone.app.skineditor/trunk/plone/app/skineditor/viewlet.py Sat May 29 17:52:55 2010 @@ -0,0 +1,70 @@ +from Products.CMFCore.utils import getToolByName +import itertools +from zope.component import getGlobalSiteManager, getSiteManager +from zope.app.component.hooks import getSite +from plone.app.skineditor.interfaces import IResourceType +from plone.app.skineditor.interfaces import IResourceRegistration +from zope.interface import implements +from plone.app.customerize.registration import templateViewRegistrationInfos +from plone.memoize.instance import memoize +from five.customerize.interfaces import ITTWViewTemplate +from zope.viewlet.interfaces import IViewlet + +class ViewletResourceRegistration(object): + implements(IResourceRegistration) + type = 'viewlet' + +class ViewletResourceType(object): + implements(IResourceType) + name = 'viewlet' + + @memoize + def layer_precedence(self): + request = getSite().REQUEST + return request.__provides__.__iro__ + + def iter_viewlet_registrations(self): + gsm = getGlobalSiteManager() + sm = getSiteManager() + layer_precedence = self.layer_precedence() + for reg in itertools.chain(gsm.registeredAdapters(), sm.registeredAdapters()): + if len(reg.required) != 4: + continue + if reg.required[1] not in layer_precedence: + continue + if IViewlet.implementedBy(reg.factory) or ITTWViewTemplate.providedBy(reg.factory): + yield reg + + def __iter__(self): + """ Returns an iterator enumerating the resources of this type. """ + pvc = getToolByName(getSite(), 'portal_view_customizations') + layer_precedence = self.layer_precedence() + by_layer_precedence_and_ttwness = lambda x: (layer_precedence.index(x.required[1]), int(not ITTWViewTemplate.providedBy(x.factory))) + regs = sorted(self.iter_viewlet_registrations(), key=by_layer_precedence_and_ttwness) + for info in templateViewRegistrationInfos(regs, mangle=False): + required = info['required'].split(',') + res = ViewletResourceRegistration() + res.name = info['viewname'] + res.context = required[0], required[3] + if required[0] == 'zope.interface.Interface': + res.description = 'Viewlet for *' + else: + res.description = u'Viewlet for %s' % required[0] + res.description += ' in the %s manager' % required[3] + res.layer = required[1] + res.actions = [] + if info['customized']: + obj = getattr(pvc, info['customized']) + path = '/'.join(obj.getPhysicalPath()) + res.info = 'In the database: %s' % path + res.actions.append(('Edit', obj.absolute_url() + '/manage_main')) + remove_url = pvc.absolute_url() + '/manage_delObjects?ids=' + info['customized'] + res.actions.append(('Remove', remove_url)) + else: + res.info = 'On the filesystem: %s' % info['zptfile'] + view_url = pvc.absolute_url() + '/@@customizezpt.html?required=%s&view_name=%s' % (info['required'], info['viewname']) + res.actions.append(('View', view_url)) + yield res + + def export(self, context): + raise NotImplemented |