on Plone, tiles are nothing but Zope browser views; we were wondering if it is possible to declare/use different templates for a tile on an simple way.
according to plone.tiles documentation you can override a template for a different context.
<plone:tile
name="sample.persistenttile"
template="override.pt"
permission="zope.Public"
for="*"
layer=".interfaces.IMyLayer"
/>
should we need to implement a way to mark the tiles with some interface for this? is there a different/easier solution?
Related
Razor view engine supports tilde-slash paths out of the box like this:
<script src="~/js/myscript.js" /> <!-- cool trick by the way!! -->
Is there any way I can override the processing of tilde?
Reason behind this
The reason I want to "override" this is because I use a CDN so the "root" should point to a different domain.
I already wrote my own replacement for Url.Content("~/blahblah") extension so it points where I need it to (my own Url.ContentCDN("~/blah") actually points to xxx.cloudfront.net/blah) can I do something like this for Razor's built-in "hack"? Not sure where do I start...
Or the only way would be to find all the tilde-paths in my solution and plug my Url-extension? (the solution is quite big, hundreds of views, so a "cleaner" way would be to overload/override this)
Thanks!
I've created a Dexterity product that includes container and non-container Dexterity content types. Having discovered collective.documentviewer (yay! thanks! huzzah!), I'd like to use its dvpdf-group-view, but that is registered in ZCML as being for Folders, and my Dexterity containers don't qualify. I've looked through the web interfaces available on my container type, added SiteRoot, and that enabled the view to be applied, but is also completely wrong.
I'm confident there's a right way to do this, and I'm pretty sure it's central to the whole adapter/interface mechanism, but I just can't find it in any of the books.
Anyone care to try an explanation? First, the line or two that would enable a Dexterity container to pretend it's also a Folder; second, how to change the default view of a single instance of a Dexterity type so that it presents a foreign component's view?
Thanks.
1. Register the view for dexterity containers too
The view is registered for the Archetypes folder interface (Products.CMFCore.interfaces._content.IFolderish), but your dexterity container does not provide this interface (but plone.dexterity.interfaces.IDexterityContainer).
The reason may be that the product and/or the view is not compatible with dexterity.
Anway, you can try it out yourself by registering the view also for the the IDexterityContainer interface by putting a little ZCML in the configure.zcml in your package (see also the Creating a package section of the Dexterity Developer Manual):
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<configure package="collective.documentviewer">
<browser:page
name="dvpdf-group-view"
for="plone.dexterity.interfaces.IDexterityContainer"
class=".views.GroupView"
template="templates/group-view.pt"
permission="zope2.View"
layer=".interfaces.ILayer" />
</configure>
</configure>
The <browser:page> is copied from the collective.documentviewer configure.zcml but I've changed the interface for= to the dexterity container interface, so that the view also works for dexterity containers.
The inner <configure package="collective.documentviewer"> tells the ZCML parser that the configuration should be applied as if the configure.zcml would be directly in collective.documentviewer - this allows you for example to use the original template (otherwise you would have to copy it or do some nasty things).
I did not test it myself: it may still be that the view needs an archetypes container and does not work with a dexterity container. It may also be that you have to register more components from the documentviewer for dexterity containers (maybe the menus? take a look at what is registered in the original configure.zcml.
If everything works well you should consider doing the changes in collective.documentviewer on github directly and make a pull-request to the author (be aware that dexterity is not plone-core yet). But first ask if and how you should do it :-)
2. Changing the default view
With plone it is possible to define multiple views for a specific type. The view can then be selected in the display menu per instance of this type. If you open up http://localhost:8080/Plone/portal_types/manage_main and click on your type, there is a field Available view methods, where you can add the view-name (dvpdf-group-view) on a seperate line.
After you create a new object of your type or visit an existing one, you have a "Display"-menu which should list the view. Select it and this object now has this view as default.
(If you want to make the view not selectable on other objects of this type you could just remove it from the type configuration so that it is not selectable anymore - the existing configuration of your object will stay).
While working on a dexterity based project I needed one of my content types to support collective.quickupload by marking it with the IQuickUploadCapable interface.
What I'm currently doing is adding an 'implements' to my configure.zcml file:
`<class class="plone.dexterity.content.Container">
<implements interface="collective.quickupload.browser.interfaces.IQuickUploadCapable" />
</class>`
Since my content type is a Container this works however my first inclination was to use a grok style approach instead of declaring it in ZCML. What's the grok/dexterity way to tell my dexterity content type that it implements an additional interface, or should I stick to the current approach?
Also I tried adding the interface as a behaviour in my profiles/default/types/my.dexterity.content.xml file but this didn't work (I didn't really expect it to as behaviours serve a different purpose).
Sean's answer is good. The other way is to create a behaviour and apply that. You need to register the behaviour with:
<plone:behavior
title="Quickupload"
provides="collective.quickupload.browser.interfaces.IQuickUploadCapable"
/>
You can then add 'collective.quickupload.browser.interfaces.IQuickUploadCapable' to your list of behaviours in the FTI.
Your approach using is not good because it means all Container-based Dexterity types get the marker interface, not just your type.
Why not just subclass IQuickUploadCapable as a mixin after form.Schema in your type interface?
You can not use it as a behaviour because it doesn't claim to be used in that way.
As I read from pypi, is intended to be used in a portlet or in a viewlet.
To add it in a grok style you should:
from collective.quickupload.browser.interfaces import IQuickUploadCapable
from plone.directives import form
class IMyContent(form.schema):
grok.implements(IQuickUploadCapable)
And that's it!
Be sure that your content type allows files to be added inside it, so is both folderish and it allows files to be added (or it just doesn't restrict to any specific content type).
To get a one-off view on a Plone folder I do something like this (not all code shown):
In configure.zcml:
<!-- Marker interface. Set this on the folder through the ZMI
interfaces tab.
-->
<interface interface=".interfaces.IMySpecialFolder" />
In browser/configure.zcml:
<!-- Special case view. Set as the folder's view through the ZMI
properties tab (layout property).
-->
<browser:page
for="..interfaces.IMySpecialFolder"
name="special"
template="special.pt"
permission="zope2.View"
/>
This works great, but I would like to control the folder's display menu to list only my special case view. I can add it, and it shows up only on my marked folder, but I have to change the site-wide ATFolder FTI.
In browser/configure.zcml:
<include package="plone.app.contentmenu" />
<browser:menuItem
for="..interfaces.IMySpecialFolder"
menu="plone_displayviews"
title="Special view"
action="##special"
description="Special case folder view"
/>
In profiles/default/types/Folder.xml:
<?xml version="1.0"?>
<object name="Folder">
<property name="view_methods" purge="False">
<element value="special"/>
</property>
</object>
Of course I cannot remove the existing available view methods without affecting every folder on the site.
Is there a way to do this one-off display menu tweaking without changing a content type's FTI?
Actually, it seems like this problem has been tackled before. p4a.z2utils patches CMFDynamicViewFTI to get the list of available views from an IDynamicallyViewable adapter lookup. (dateable.chronos uses this mechanism for its folder calendar views). So my question becomes:
Is there a way to do this one-off display menu tweaking without changing a content type's FTI and without patching Plone?
The plone display menu builder uses ISelectableBrowserDefault to get available options in the Display menu (see
http://dev.plone.org/plone/browser/plone.app.contentmenu/trunk/plone/app/contentmenu/menu.py#L220)
So I think (but I haven't tried this) that if you define an adapter for a more specific interface (in your case IMySpecialFolder) that provides the Products.CMFDynamicViewFTI.interface.ISelectableBrowserDefault it should work.
The adapter should have the methods required by plone.app.contentmenu.menu.DisplayMenu above.
Answering my own question, I've realised that the most straightforward way to achieve one-off folder views is to follow the pattern Plone itself applies in the Members folder: a PythonScript index_html that calls the custom view, e.g.
member_search=context.restrictedTraverse('member_search_form')
return member_search()
Products.CMFPlone illustrates how to setup such a PythonScript with a GenericSetup import handler.
In retrospect I realise I didn't need the marker interface in my question scenario. It's not necessary here in the answer either.
Note that this solution doesn't result in "the folder's display menu listing only my special case view" as I asked, but does away with the display menu altogether. I'm fine with that.
One way you could have solved it is using traversalhook to register menu items, or in this case, unregister menu items, or register menu items with conditions that make them not appear. With the traversal hook you can use a marker interface to make it just happen in a certain folder, subsection or page.
You can see where we implemented similar code here
https://github.com/collective/collective.listingviews/blob/master/src/collective/listingviews/browser/views/controlpanel.py#L105
In this case we just wanted to register new display menu items dynamically based on control panel configuration.
I am creating a site which will have a "desktop" and a "mobile" theme. I've two theme packages for this site: mysite.theme and mysite.mobile_theme. The mobile_theme is a stripped down version of the desktop theme, with new views and a reduced set of viewlets. I want to switch between these two themes based on the URL the site is visited from (i.e., mobile.mysite.com vs. www.mysite.com).
As the mobile and desktop themes will share a lot of code, mysite.mobile_theme descends from mysite.theme in the following ways:
1) mobile_theme GS skins.xml has a skin path based on the old theme, so the desktop theme's CSS etc. is used:
<skin-path name="mysite.mobile_theme" based-on="mysite.theme">
2) IThemeSpecific marker subclasses the original one, so views which I'm not overriding for the mobile site fallback to the ones in mysite.theme:
from mysite.theme.browser.interfaces import IThemeSpecific as IBaseTheme
class IThemeSpecific(IBaseTheme):
"""Marker interface that defines a Zope 3 browser layer.
"""
3) I have registered various views in mysite.mobile_theme to override the certain ones in mysite.theme.
4) I've used generic setup to have different viewlet registrations for each theme.
At this stage, if I select mysite.mobile_theme in the "Default skin" option portal skins->properties, everything works correctly: my views are used and the viewlets settings from the mobile_theme's GS profile are picked up correctly. So it appears the theme is set up correctly overall.
As mentioned above, however, I would like to swap between these two themes based on URL.
First, I swapped the "Default skin" back to "mysite.theme". I then created an access_rule in the root on my Plone site, roughly following these instructions to select a skin based on URL. It's at plonesite/access_rule and is set up as the access_rule for the plone site:
url = context.REQUEST.get('ACTUAL_URL', '')
if 'mobile' in url:
context.changeSkin('mysite.mobile_theme', context.REQUEST)
else:
context.changeSkin('mysite.theme', context.REQUEST)
I've also tried using context.REQUEST.set('plone_skin', 'mysite.theme') rather than calling context.changeSkin(...).
Using this setup, the viewlets displayed change correctly based on the URL I've used--so it looks like the skin is being changed at some point--but the mysite.mobile_theme's view classes/templates are not being used in preference to mysite.theme's. In summary:
If I call from a URL containing "mobile" I get mysite.theme's views, but mysite.mobile_theme's viewlet registrations.
Otherwise, I get mysite.theme's views and mysite.theme's viewlet registrations.
It looks like I might have to hook into the traversal mechanism to change it so if "mobile" is in the URL, the mysite.mobile_theme's views registered against its IThemeSpecific are chosen rather than the mysite.theme ones, but I'm not sure this is correct nor how I'd go about this.
Can anyone give me some pointers?
UPDATE 3hrs after originally asking
To answer my own question (which I can't do for another 5 hours due to SO's rules):
"""
It would appear that you must patch much lower down in the stack to make this work. I looked at how it was done in plone.gomobile, and they monkeypatch the skin choosing code itself. See:
http://code.google.com/p/plonegomobile/source/browse/gomobile.mobile/trunk/gomobile/mobile/monkeypatch.py
"""
You could use collective.editskinswitcher. Its main use case is to use the Plone Default theme on say edit.example.com and My Custom Theme on www.example.com. You can probably tweak its property sheet to fit your use case though.
Since the 'mobile theme' use case is fairly common I would accept patches to make that easier; or I may work on that myself some time.
(BTW, note that there is a fix-browser-layers branch that may help when you miss some items that are registered for a specific browser layer; seems ready to merge except that I would like to add some tests first.)
I have done this in some prototypes of mobile themes. Please consider thoses two addons not ready for production:
https://github.com/toutpt/plonetheme.jquerymobile
https://github.com/toutpt/plonetheme.senchatouch
The related code is:
The patch on browserlayer to mark the request with my theme layer: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/layer.py
The patch on plonetool to add ##mobile on every content page: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/PloneTool.py
The patch on skintool to tell skin layer is this one if browser layer: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/SkinsTool.py
If you are using plone.app.theming, you also can switch your diazo theme: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/transform.py
Do I understand correctly that at the mobile URL your skins are correct, but your Zope3 Views are not? Which makes sense to me, since the view classes are based on Interfaces. In the same code above, where you use context.changeSkin, add:
from zope.interface import alsoProvides
alsoProvides(context, IMobileView)
and have your view zcml specify for ... IMobileView
[edit: on second thoughts, this really should be what happens when you change the skin - where the additional inteface will be your "IThemeSpecific" - so I'm not sure what's at play here, but it wouldn't hurt to try alsoProvides(context, IThemeSpecific)]