How to embed a PloneFormGen form into a page in Plone

published Oct 25, 2007 12:16   by admin ( last modified Oct 25, 2007 12:16 )

This describes how to embed a Ploneformgen form into another page. It was was done on Plone 2.5 with PloneFormGen 1.1.3, so these tricks may not work for other versions.  If there is a simpler way of doing this, please enlighten me.

  • The first scenario is this: to embed a Ploneformgen form directly into a page template
  • The second scenario is: I have created a content type that is folderish, that simply collects all objects inside and renders them. One of these objects inside is a form folder. I wanted this to be rendered and shown inside my content type's page template.


The solution is the same to both problems.

 

 

The first problem was to make it render some HTML to be included in my page.

 

If you just want to embed the form directly into a template, this code is enough, you do it in one step:

<div tal:content="structure python: context.main_form_for_sending_mail.ww_pfgwrapper()"/>

The Ploneformgen object used above has the id "main_form_for_sending_mail".

Below is a simplified piece of code from the folderish content type. It simply collects all Form folders it can find inside of itself and renders them by calling a template on each of them

    def itemContents(self):
        items = self.contentValues(['FormFolder',])
        output = []
        for item in items:
            output.append(item.ww_pfgwrapper())
        return output

My folderish content type has a page template that calls itemContents:

<div tal:repeat="item python:here.itemContents()">
<span tal:content="structure item"/>
</div>

 

So what is that ww_pfgwrapper thing there in the python code? It is a page template that renders the form folder object for inclusion. There is already a page template coming with PloneFormgen that renders the form. It is called fg_base_view. It renders the whole web page with plone headers and footers, and that is not suitable inside a page. There is a macro in fg_base_view, but unfortunately for some reason this macro is defined as the whole template contents, and would still give the whole plone layout.

The solution turned out to be to make a copy of fg_base_view and call it something else, in this case ww_pfgwrapper.

ww_pfgwrapper is the same as fg_base_view with five major changes (the contents of the ww_pfgwrapper template is posted at the end of this article):

  • It does not call the main macro
  • It defines a portal variable (that got undefined by not calling main macro)
  • It has none of the fill-slot stuff
  • It has tal:omit-tag="" for the html and body tags
  • It redefines the name of the edit_macro that is going to be used


Let's go through the reasons for these changes:

It does not call the main macro
Obvious. If that was called we'd get the plone layout once again embedded in the middle of a document.

It has none of the fill-slot stuff
The fill-slot attribute is also deleted, and all the stuff outside of the body element (save the html tag and the defines).

It defines a portal variable

Once the main macro is deleted there is no definition left for the portal variable. One needs to be put in:

portal python:here.portal_url.getPortalObject();


It redefines the name of the edit_macro that is going to be used

The macro responsible for setting the action attribute of the form, will set this attribute to the url + the name of the template. This does not work, even if we make ww_pfgwrapper into a .cpt file with an accompanying .metadata file, just like fg_base_view.  Go figure.

Instead we want the form to call the url + "/fg_base_view", because then it works. The template object responsible for defining the action attribute is called fg_edit_macros. That is the culprit that tacks on the template name last to the action attribute of the form. Make a copy of fg_edit_macros, call it ww_fg_edit_macros, and make a change in the ww_pfg_wrapper template near the top to use that template instead:

base_macros here/fg_edit_macros/macros;

is changed to:

base_macros here/ww_fg_edit_macros/macros;

We then make one small change in the new ww_fg_edit_macros template

tal:attributes="action python:getattr(here,'formActionOverride','') or here.absolute_url()+'/'+template.id"


is changed to:

tal:attributes="action python:getattr(here,'formActionOverride','') or here.absolute_url()+'/'+'fg_base_view'


That is, hard code "fg_base_view" in there, at the end.

After these changes the template ww_pfg_wrapper looks like this:


<tal:block
define="errors options/state/getErrors | nothing;
Iterator python:modules['Products.Archetypes'].IndexIterator;
schematas nothing;
fieldsets python:['default'];
default_fieldset string:default;
fieldset default_fieldset;
fields python:here.fgFields(request);
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
type_name here/getPortalTypeName|here/archetype_name;
base_macros here/ww_fg_edit_macros/macros;
edit_template python:'%s_edit' % portal_type;
edit_macros python:path('here/%s/macros | nothing' % edit_template);
header_macro edit_macros/header | header_macro | base_macros/header;
typedescription_macro edit_macros/typedescription | typedescription_macro | base_macros/typedescription;
body_macro edit_macros/body | body_macro | base_macros/body;
footer_macro edit_macros/footer | footer_macro | base_macros/footer;
lockable python:hasattr(here, 'wl_isLocked');
isLocked python:lockable and here.wl_isLocked();
tabindex tabindex|python:Iterator(pos=7000);
css python:here.getUniqueWidgetAttr(fields, 'helper_css');
js python:here.getUniqueWidgetAttr(fields, 'helper_js');
portal python:here.portal_url.getPortalObject();
">

<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en"
lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="plone" tal:omit-tag="">





<body tal:omit-tag="">
<br/>
<br/>
<b tal:content="here/Title" class="documentFirstHeading">
Title or id
</b>:

<p tal:replace="structure here/getFormPrologue | nothing" />
<metal:use_body use-macro="body_macro" />
<metal:use_footer use-macro="footer_macro" />
<p tal:replace="structure here/getFormEpilogue | nothing" />



</body>

</html>

</tal:block>

And now it works. The form folder can reside inside your folderish object, but it can also be at e.g. the Plone root, and be called by acquisition:

output.append(self.main_form_for_sending_mail.ww_pfgwrapper())


In this way, one can have one central form to embed in all, or some pages. And with acquisition it is of course possible to have different form for different parts of the site by placing Form folders by the same name (id) in different places.