Index: /quintagroup.transmogrify.pfm2pfg/trunk/README.txt
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/README.txt	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/README.txt	(revision 1497)
@@ -0,0 +1,52 @@
+quintagroup.transmogrifier.pfm2pfg Package Readme
+=================================================
+
+Overview
+--------
+
+Configuration of collective.transmogrifier pipeline for migrating 
+PloneFormMailer content to PloneFormGen content
+
+Installation
+------------
+
+See docs/INSTALL.txt for installation instructions.
+
+Supported Plone versions
+------------------------
+
+This package was designed to work and tested on next Plone versions:
+
+ - Plone 2.1.5
+
+    To run it you need to comment registration in ``configure.zcml`` of
+    PloneFormGen dependent components. That is because Plone 2.1.5 runs on
+    Zope 2.8, which includes Zope X3.0 and that version of zope doesn't support
+    'zcml:condition' attribute in ZCML configuration files.
+ 
+ - Plone 3.1.5
+
+Running tests
+-------------
+
+ - Plone 2.1.5
+
+    Remove or rename ``test_import.py`` in package's tests directory because
+    this test module depends on Plone 3.1. And run next:
+
+    bin/instance test --libdir path/to/quintagroup.transmogrifier.pfm2pfg
+
+ - Plone 3.1.5
+
+    Run next command:
+
+    bin/instance test -s quintagroup.transmogrifier.pfm2pfg -m test_import
+
+Credits
+-------
+
+Design and development
+   `Bohdan Koval`_ at Quintagroup_
+
+.. _Bohdan Koval: mailto:koval@quintagroup.com
+.. _Quintagroup: http://www.quintagroup.com/
Index: /quintagroup.transmogrify.pfm2pfg/trunk/TODO.txt
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/TODO.txt	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/TODO.txt	(revision 1497)
@@ -0,0 +1,8 @@
+TODO
+====
+
+- Importing enhancements:
+
+  - add handling of TALES expressions on updating PloneFormGen fields
+
+  - PloneFormMailer's PatternField is imported as StringField (it wouldn't work correctly)
Index: /quintagroup.transmogrify.pfm2pfg/trunk/docs/HISTORY.txt
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/docs/HISTORY.txt	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/docs/HISTORY.txt	(revision 1497)
@@ -0,0 +1,9 @@
+Changelog for quintagroup.transmogrifier.pfm2pfg
+
+    (name of developer listed in brackets)
+
+quintagroup.transmogrifier.pfm2pfg - 0.1 Unreleased
+
+    - Initial package structure.
+      [zopeskel]
+
Index: /quintagroup.transmogrify.pfm2pfg/trunk/docs/INSTALL.txt
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/docs/INSTALL.txt	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/docs/INSTALL.txt	(revision 1497)
@@ -0,0 +1,43 @@
+quintagroup.transmogrifier.pfm2pfg Installation
+==========================
+
+To install quintagroup.transmogrifier.pfm2pfg into the global Python environment (or a workingenv),
+using a traditional Zope 2 instance, you can do this:
+
+ * When you're reading this you have probably already run 
+   ``easy_install quintagroup.transmogrifier.pfm2pfg``. Find out how to install setuptools
+   (and EasyInstall) here:
+   http://peak.telecommunity.com/DevCenter/EasyInstall
+
+ * Create a file called ``quintagroup.transmogrifier.pfm2pfg-configure.zcml`` in the
+   ``/path/to/instance/etc/package-includes`` directory.  The file
+   should only contain this::
+
+       <include package="quintagroup.transmogrifier.pfm2pfg" />
+
+
+Alternatively, if you are using zc.buildout and the plone.recipe.zope2instance
+recipe to manage your project, you can do this:
+
+ * Add ``quintagroup.transmogrifier.pfm2pfg`` to the list of eggs to install, e.g.:
+ 
+    [buildout]
+    ...
+    eggs =
+        ...
+        quintagroup.transmogrifier.pfm2pfg
+        
+  * Tell the plone.recipe.zope2instance recipe to install a ZCML slug:
+  
+    [instance]
+    recipe = plone.recipe.zope2instance
+    ...
+    zcml =
+        quintagroup.transmogrifier.pfm2pfg
+        
+  * Re-run buildout, e.g. with:
+  
+    $ ./bin/buildout
+        
+You can skip the ZCML slug if you are going to explicitly include the package
+from another package's configure.zcml file.
Index: /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.GPL
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.GPL	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.GPL	(revision 1497)
@@ -0,0 +1,225 @@
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
Index: /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.txt
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.txt	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/docs/LICENSE.txt	(revision 1497)
@@ -0,0 +1,16 @@
+  quintagroup.transmogrifier.pfm2pfg is copyright Quintagroup
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
+  MA 02111-1307 USA.
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/__init__.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/__init__.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/__init__.py	(revision 1497)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/__init__.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/__init__.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/__init__.py	(revision 1497)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/__init__.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/__init__.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/__init__.py	(revision 1497)
@@ -0,0 +1,1 @@
+# i'm a package
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/configure.zcml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/configure.zcml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/configure.zcml	(revision 1497)
@@ -0,0 +1,55 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
+    xmlns:zcml="http://namespaces.zope.org/zcml"
+    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier"
+    i18n_domain="quintagroup.transmogrifier.pfm2pfg">
+
+    <include package="quintagroup.transmogrifier" file="meta.zcml" />
+    <include package="quintagroup.transmogrifier" />
+
+    <configure zcml:condition="installed Products.PloneFormMailer">
+
+        <five:implements 
+            class="Products.PloneFormMailer.PloneFormMailer.PloneFormMailer"
+            interface=".exporting.IPloneFormMailer"
+            />
+
+        <adapter
+            for=".exporting.IPloneFormMailer"
+            provides="quintagroup.transmogrifier.interfaces.IExportDataCorrector"
+            factory=".exporting.PloneFormMailerExporter"
+            name="marshall"
+            />
+
+    </configure>
+
+    <!-- comment next if you are running Plone 2.1.5 -->
+    <configure zcml:condition="installed Products.PloneFormGen">
+
+        <transmogrifier:stylesheet
+            source="marshall"
+            from="PloneFormMailer"
+            to="FormFolder"
+            file="formmailer-formfolder.xsl"
+            />
+
+        <transmogrifier:stylesheet
+            source="marshall"
+            from="PloneFormMailer"
+            to="FormMailerAdapter"
+            file="formmaileradapter.xsl"
+            />
+
+        <transmogrifier:stylesheet
+            source="marshall"
+            from="PloneFormMailer"
+            to="FormThanksPage"
+            file="formthankspage.xsl"
+            />
+
+        <include file="formgen.zcml" />
+
+    </configure>
+
+</configure>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/exporting.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/exporting.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/exporting.py	(revision 1497)
@@ -0,0 +1,82 @@
+from xml.dom import minidom
+
+from zope.interface import Interface, implements
+
+from Products.Archetypes import atapi
+from Products.Marshall.config import AT_NS
+from Products.PageTemplates.TALES import CompilerError
+
+from quintagroup.transmogrifier.interfaces import IExportDataCorrector
+from quintagroup.transmogrifier.adapters.exporting import ReferenceExporter
+
+class IPloneFormMailer(Interface):
+    """ Marker interface for PloneFormMailer content type
+    """
+
+class PloneFormMailerExporter(ReferenceExporter):
+    """ Marshalls PloneFormMailer to XML.
+    """
+    implements(IExportDataCorrector)
+
+    def __init__(self, context):
+        self.context = context
+        self.tales_fnames = ['recipient_name', 'bcc_recipients', 'cc_recipients']
+        self.mail_subject_field = 'subject'
+
+    def __call__(self, data):
+        xml = data['data']
+        xml = self.exportReferences(xml)
+        data['data'] = self.exportFormFields(xml)
+        return data
+
+    def exportFormFields(self, data):
+        """ Reformat xml file and add Formulator fields.
+        """
+        # reformat xml tree
+        doc = minidom.parseString(data)
+        root = doc.documentElement
+
+        # some TALES fields must be exported as value of executed expression
+        schema = self.context.Schema()
+        for fname in self.tales_fnames:
+            nodes = [i for i in root.getElementsByTagName('field') if i.getAttribute('name') == fname]
+            for node in nodes:
+                root.removeChild(node)
+            # trying to get value of TALES field
+            # if expression can't be evaluated skip this field
+            try:
+                values = schema[fname].getAccessor(self.context)()
+            except CompilerError:
+                continue
+            if not isinstance(values, (list, tuple)):
+                values = [values]
+            values = filter(None, values)
+            for value in values:
+                node = doc.createElementNS(AT_NS, "field")
+                name_attr = doc.createAttribute("name")
+                name_attr.value = fname
+                node.setAttributeNode(name_attr)
+                value_node = doc.createTextNode(str(value))
+                node.appendChild(value_node)
+                node.normalize()
+                root.appendChild(node)
+
+        # PloneFormMailer overrides standard 'subject' schema field with it's own,
+        # which is a subject of a message. That's why it isn't exported as '<dc:subject />'
+        # element (it is concerned with current implementation of Marshall's CMF namespaces
+        value = atapi.BaseObject.__getitem__(self.context, self.mail_subject_field)
+        if value:
+            node = doc.createElementNS(AT_NS, "field")
+            name_attr = doc.createAttribute("name")
+            name_attr.value = self.mail_subject_field
+            node.setAttributeNode(name_attr)
+            value_node = doc.createTextNode(str(value))
+            node.appendChild(value_node)
+            node.normalize()
+            root.appendChild(node)
+
+        # append form fields
+        fm_doc = minidom.parseString(self.context.form.get_xml())
+        root.appendChild(fm_doc.documentElement)
+
+        return doc.toxml('utf-8')
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formgen.zcml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formgen.zcml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formgen.zcml	(revision 1497)
@@ -0,0 +1,75 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope">
+
+    <!-- export data correctors -->
+    <!-- FormFolder adapter -->
+    <adapter
+        for="Products.PloneFormGen.interfaces.IPloneFormGenForm
+            collective.transmogrifier.interfaces.ITransmogrifier"
+        factory=".importing.FormFolderImporter"
+        name="marshall"
+        />
+
+    <!-- default adapter -->
+    <adapter
+        for="Products.PloneFormGen.interfaces.IPloneFormGenField"
+        factory=".importing.BaseFieldDemarshaller"
+        />
+
+    <!-- custom adapters -->
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGStringField"
+        factory=".importing.StringFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGTextField"
+        factory=".importing.TextFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGRichLabelField"
+        factory=".importing.LabelFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGPasswordField"
+        factory=".importing.StringFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGIntegerField"
+        factory=".importing.IntegerFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGFixedPointField"
+        factory=".importing.StringFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGDateField"
+        factory=".importing.DateTimeFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGLinesField"
+        factory=".importing.LinesFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGBooleanField"
+        factory=".importing.BooleanFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGSelectionField"
+        factory=".importing.SelectionFieldDemarshaller"
+        />
+
+    <adapter
+        for="Products.PloneFormGen.content.fields.FGMultiSelectField"
+        factory=".importing.MultiSelectFieldDemarshaller"
+        />
+
+</configure>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmailer-formfolder.xsl
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmailer-formfolder.xsl	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmailer-formfolder.xsl	(revision 1497)
@@ -0,0 +1,78 @@
+<xsl:stylesheet version="1.0" 
+     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+     xmlns:at="http://plone.org/ns/archetypes/"
+     xmlns:cmf="http://cmf.zope.org/namespaces/default/"
+     xmlns:dc="http://purl.org/dc/elements/1.1/">
+
+<xsl:template match="/">
+    <xsl:for-each select="*">
+        <!-- matched 'metadata' element and we copy it -->
+        <xsl:copy>
+            <xsl:for-each select="*|text()">
+                <xsl:choose>
+                    <!-- do some special with 'field' elements -->
+                    <xsl:when test="name()='field'">
+                        <xsl:apply-templates select="." />
+                    </xsl:when>
+                    <!-- change value of 'cmf:type' attribute -->
+                    <xsl:when test="name()='cmf:type'">
+                        <xsl:apply-templates select="." />
+                    </xsl:when>
+                    <!-- skip 'cmf:workflow_history' element (PFG has another workflow) -->
+                    <xsl:when test="name()='cmf:workflow_history'" />
+                    <!-- copy all other elements -->
+                    <xsl:otherwise>
+                        <xsl:copy-of select="."/>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:for-each>
+        </xsl:copy>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="at:field">
+    <xsl:choose>
+        <!-- next fields are omitted -->
+        <xsl:when test="@name='before_script'" />
+        <xsl:when test="@name='form_buttons'" />
+
+        <!-- change field's name attribute from 'body' to 'text' -->
+        <xsl:when test="@name='form_pre'">
+            <xsl:copy>
+                <xsl:attribute name="name">formPrologue</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <!-- change field's name attribute from 'form_post' to 'text' -->
+        <xsl:when test="@name='form_post'">
+            <xsl:copy>
+                <xsl:attribute name="name">formEpilogue</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <xsl:when test="@name='sent_redirect'">
+            <xsl:copy>
+                <xsl:attribute name="name">thanksPageOverride</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <xsl:when test="@name='cpyaction'">
+            <xsl:copy>
+                <xsl:attribute name="name">afterValidationOverride</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <!-- copy all other elements -->
+        <xsl:otherwise>
+            <xsl:copy-of select="."/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<xsl:template match="cmf:type">
+    <xsl:copy>
+        <xsl:text>FormFolder</xsl:text>
+    </xsl:copy>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmaileradapter.xsl
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmaileradapter.xsl	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formmaileradapter.xsl	(revision 1497)
@@ -0,0 +1,94 @@
+<xsl:stylesheet version="1.0" 
+     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+     xmlns:at="http://plone.org/ns/archetypes/"
+     xmlns:cmf="http://cmf.zope.org/namespaces/default/"
+     xmlns:dc="http://purl.org/dc/elements/1.1/"
+     xmlns:xmp="adobe:ns:meta">
+
+<xsl:template match="/">
+    <xsl:for-each select="*">
+        <!-- matched 'metadata' element and we copy it -->
+        <xsl:copy>
+            <xsl:for-each select="*|text()">
+                <xsl:choose>
+                    <!-- do some special with 'field' elements -->
+                    <xsl:when test="name()='field'">
+                        <xsl:apply-templates select="." />
+                    </xsl:when>
+                    <!-- remove unnecessary elements -->
+                    <xsl:when test="name()='form'" />
+                    <xsl:when test="name()='uid'" />
+                    <xsl:when test="name()='dc:title'" />
+                    <xsl:when test="name()='dc:description'" />
+                    <xsl:when test="name()='dc:contributor'" />
+                    <xsl:when test="name()='dc:creator'" />
+                    <xsl:when test="name()='dc:rights'" />
+                    <xsl:when test="name()='dc:language'" />
+                    <!--xsl:when test="name()='xmp:CreateDate'" /-->
+                    <!-- xsl:when test="name()='xmp:ModifyDate'" /-->
+                    <xsl:when test="name()='cmf:type'" />
+                    <xsl:when test="name()='cmf:workflow_history'" />
+                    <xsl:when test="name()='cmf:security'" />
+                    <!-- copy all other elements -->
+                    <xsl:otherwise>
+                        <xsl:copy-of select="."/>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:for-each>
+        </xsl:copy>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="at:field">
+    <xsl:choose>
+        <!-- next at fields are omitted -->
+        <xsl:when test="@name='id'" />
+        <xsl:when test="@name='relatedItems'" />
+        <xsl:when test="@name='location'" />
+        <xsl:when test="@name='effectiveDate'" />
+        <xsl:when test="@name='expirationDate'" />
+        <xsl:when test="@name='allowDiscussion'" />
+        <xsl:when test="@name='excludeFromNav'" />
+        <!-- skip zpt of message body, because it can't be used in PFG -->
+        <xsl:when test="@name='body_pt'" />
+
+        <!-- change field's name attribute from 'recipient_email' to 'recipientOverride' (TALESString) -->
+        <xsl:when test="@name='recipient_email'">
+            <xsl:copy>
+                <xsl:attribute name="name">recipientOverride</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <!-- change field's name attribute from 'subject' to 'subjectOverride' (TALESString) -->
+        <xsl:when test="@name='subject'">
+            <xsl:copy>
+                <xsl:attribute name="name">subjectOverride</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+
+        <xsl:when test="@name='body_type'">
+            <xsl:copy>
+                <xsl:attribute name="name">body_type</xsl:attribute>
+                <xsl:choose>
+                    <xsl:when test="contains(string(), 'text/plain')">
+                        <xsl:text>plain</xsl:text>
+                    </xsl:when>
+                    <xsl:when test="contains(string(), 'text/html')">
+                        <xsl:text>html</xsl:text>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:text>plain</xsl:text>
+                    </xsl:otherwise>
+                </xsl:choose>
+           </xsl:copy>
+        </xsl:when>
+
+        <!-- copy all other elements -->
+        <xsl:otherwise>
+            <xsl:copy-of select="."/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formthankspage.xsl
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formthankspage.xsl	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/formthankspage.xsl	(revision 1497)
@@ -0,0 +1,45 @@
+<xsl:stylesheet version="1.0" 
+     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+     xmlns:at="http://plone.org/ns/archetypes/"
+     xmlns:cmf="http://cmf.zope.org/namespaces/default/"
+     xmlns:dc="http://purl.org/dc/elements/1.1/">
+
+<xsl:template match="/">
+    <xsl:for-each select="*">
+        <!-- matched 'metadata' element and we copy it -->
+        <xsl:copy>
+            <xsl:for-each select="*|text()">
+                <xsl:choose>
+                    <!-- do some special with 'field' elements -->
+                    <xsl:when test="name()='field'">
+                        <xsl:apply-templates select="." />
+                    </xsl:when>
+                    <!-- copy all other elements -->
+                    <xsl:otherwise>
+                        <xsl:copy-of select="."/>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:for-each>
+        </xsl:copy>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="at:field">
+    <xsl:choose>
+        <xsl:when test="@name='sent_title'">
+            <dc:title>
+                <xsl:value-of select="."/>
+            </dc:title>
+        </xsl:when>
+        <xsl:when test="@name='sent_message'">
+            <xsl:copy>
+                <xsl:attribute name="name">thanksPrologue</xsl:attribute>
+                <xsl:value-of select="." />
+            </xsl:copy>
+        </xsl:when>
+        <!-- skip all other elements -->
+        <xsl:otherwise />
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/import.cfg
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/import.cfg	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/import.cfg	(revision 1497)
@@ -0,0 +1,25 @@
+[transmogrifier]
+pipeline =
+    reader
+    manifestimporter
+    substitution
+    constructor
+    xslt
+    datacorrector
+    fileimporter
+    demarshaller
+    referencesimporter
+    propertiesimporter
+    commentsimporter
+    IMPORTING
+include = quintagroup.transmogrifier:import.cfg
+
+[substitution]
+blueprint = quintagroup.transmogrifier.substitution
+key = _type
+PloneFormMailer = FormFolder
+
+[xslt]
+blueprint = quintagroup.transmogrifier.xslt
+from-key = _old_type
+to-key = _type
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/importing.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/importing.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/importing.py	(revision 1497)
@@ -0,0 +1,522 @@
+import logging
+import os.path
+from xml.dom import minidom
+from DateTime import DateTime
+
+from zope.interface import implements, Interface
+from zope.component import getUtility
+
+from ZODB.POSException import ConflictError
+from Acquisition import aq_inner, aq_parent
+
+from Products.Marshall.registry import getComponent
+from Products.Marshall.config import AT_NS
+from Products.CMFPlone.utils import _createObjectByType
+
+from quintagroup.transmogrifier.interfaces import IImportDataCorrector
+from quintagroup.transmogrifier.adapters.importing import ReferenceImporter
+from quintagroup.transmogrifier.xslt import XSLTSection
+
+BOOL_FIELD_PROPS = ['enabled', 'required', 'hidden']
+
+FIELD_MAP = {
+            'StringField'   : 'FormStringField',
+            'EmailField'    : ('FormStringField', {'fgStringValidator': 'isEmail'}),
+            'LinkField'     : ('FormStringField', {'fgStringValidator': 'isURL'}),
+            'PatternField'  : 'FormStringField',
+
+            'TextAreaField' : 'FormTextField',
+            'RawTextAreaField': 'FormTextField',
+
+            'PasswordField' : 'FormPasswordField',
+            'LabelField'    : 'FormRichLabelField', #FormLabelField
+
+            'IntegerField'  : 'FormIntegerField',
+            'FloatField'    : 'FormFixedPointField',
+
+            'DateTimeField' : 'FormDateField',
+            'FileField'     : 'FormFileField',
+
+            'LinesField'    : 'FormLinesField',
+
+            'CheckBoxField' : 'FormBooleanField',
+            'ListField'     : ('FormSelectionField', {'fgFormat': 'select'}),
+            'RadioField'    : ('FormSelectionField', {'fgFormat': 'radio'}),
+            'MultiListField': ('FormMultiSelectionField', {'fgFormat': 'select'}),
+            'MultiCheckBoxField':('FormMultiSelectionField', {'fgFormat': 'checkbox'}),
+            }
+
+class IXMLDemarshaller(Interface):
+    """
+    """
+    def demarshall(obj, data):
+        """
+        """
+
+class FormFolderImporter(ReferenceImporter):
+    """ Demarshaller of PloneFormGen's FormFolder content type.
+    """
+    implements(IImportDataCorrector)
+
+    def __init__(self, context, transmogrifier):
+        self.context = context
+        self.demarshaller = getComponent("atxml")
+        self.auto_added_fgfields = ['replyto', 'topic', 'comments']
+        self.date_fields = {}
+
+    def __call__(self, data):
+        data = super(FormFolderImporter, self).__call__(data)
+        xml = data['data']
+        data['data'] = self.transformWithMinidom(xml)
+
+        # update FormMailerAdapter and FormThanksPage objects
+        cleaned_xml = self.updateMailer(xml)
+
+        self.updateResponsePage(cleaned_xml)
+
+        self.updateFormFields(xml)
+
+        return data
+
+    def transformWithMinidom(self, xml):
+        """ Do some extra transformations on xml document (that can be done by XSLT).
+        """
+        doc = minidom.parseString(xml)
+        root = doc.documentElement
+
+        # get 'modification_date' and 'creation_date' for next usage when creating fields
+        for i in root.getElementsByTagName('xmp:CreateDate'):
+            self.date_fields['creation_date'] = i.firstChild.nodeValue.strip()
+        for i in root.getElementsByTagName('xmp:ModifyDate'):
+            self.date_fields['modification_date'] = i.firstChild.nodeValue.strip()
+
+        # xxx: update button labels (those elements are now skiped by xslt)
+        # PFM has only one field 'form_buttons', but PFG has two: 'submitLabel', 'resetLabel' and
+        # field 'useCancelButton'
+
+        # add element for setting of 'thanksPage' field to 'thank-you' FormThanksPage
+        elem = doc.createElementNS(AT_NS, "field")
+        name_attr = doc.createAttribute("name")
+        name_attr.value = 'thanksPage'
+        elem.setAttributeNode(name_attr)
+        value = doc.createTextNode('thank-you')
+        elem.appendChild(value)
+        root.appendChild(elem)
+
+        # update 'thanksPageOverride' field
+        elem = [i for i in doc.getElementsByTagName('field') if i.getAttribute('name') == 'thanksPageOverride']
+        if elem:
+            elem = elem[0]
+            old_text = elem.firstChild
+            new_text = doc.createTextNode('redirect_to:' + old_text.nodeValue.strip())
+            elem.removeChild(old_text)
+            elem.appendChild(new_text)
+
+        # xxx: update 'afterValidationOverride' field
+        # 'afterValidationOverride' is TALES expression, but PFM's 'cpyaction' is name 
+        # of controller python script
+        elem = [i for i in doc.getElementsByTagName('field') 
+                if i.getAttribute('name') == 'afterValidationOverride']
+        if elem:
+            elem = elem[0]
+            old_text = elem.firstChild
+            new_text = doc.createTextNode('string:' + old_text.nodeValue.strip())
+            elem.removeChild(old_text)
+            elem.appendChild(new_text)
+
+        # xxx: update 'onDisplayOverride' field (this element is now skiped by xslt)
+        # 'onDisplayOverride' is TALES expression, but PFM's 'before_script' is python script
+        # in 'scripts' subfolder
+
+        return doc.toxml('utf-8')
+
+    def updateMailer(self, xml):
+        mailer = self.context['mailer']
+        transformed = self.transformWithXSLT(xml, 'FormMailerAdapter')
+        self.demarshaller.demarshall(mailer, transformed)
+        mailer.indexObject()
+        return transformed
+
+    def updateResponsePage(self, xml):
+        page = self.context['thank-you']
+        transformed = self.transformWithXSLT(xml, 'FormThanksPage')
+        self.demarshaller.demarshall(page, transformed)
+        # override default value of description field
+        page.getField('description').getMutator(page)('')
+        page.indexObject()
+        return transformed
+
+    def transformWithXSLT(self, xml, to):
+        """ Apply XSLT transformations using XSLT transmogrifier section.
+        """
+        item = dict(
+            _from='PloneFormMailer',
+            _to=to,
+            _files=dict(
+                marshall=dict(
+                    name='.marshall.xml',
+                    data=xml
+                )
+            )
+        )
+        section = XSLTSection(object(), 'xslt', {'blueprint': ''}, iter((item,)))
+        for i in section: pass
+        return item['_files']['marshall']['data']
+
+    def updateFormFields(self, data):
+        """ Walk trough xml tree and create fields in FormFolder.
+        """
+        # delete fields that were added on FormFolder creation
+        for oid in [i for i in self.auto_added_fgfields if i in self.context]:
+            self.context._delObject(oid)
+
+        doc = minidom.parseString(data)
+        root = doc.documentElement
+        form_element = root.getElementsByTagName('form')
+        if not form_element:
+            return
+        groups = form_element[0].getElementsByTagName('group')
+        for group in groups:
+            group_title = group.getElementsByTagName('title')[0]
+            group_title = str(group_title.firstChild.nodeValue).strip()
+            if group_title == 'Default':
+                for field in group.getElementsByTagName('field'):
+                    field_id = str(field.getElementsByTagName('id')[0].firstChild.nodeValue).strip()
+                    field_type = str(field.getElementsByTagName('type')[0].firstChild.nodeValue).strip()
+                    self.createField(field_type, field_id, field, self.context)
+            else:
+                fieldset = self.createFieldset(group_title)
+                if not fieldset:
+                    return
+                for field in group.getElementsByTagName('field'):
+                    field_id = str(field.getElementsByTagName('id')[0].firstChild.nodeValue).strip()
+                    field_type = str(field.getElementsByTagName('type')[0].firstChild.nodeValue).strip()
+                    self.createField(field_type, field_id, field, fieldset)
+
+    def createField(self, type_name, field_id, field_node, context=None):
+        """ Created formfield and update it from field_node's xml tree.
+
+            Use demarshalling adapter.
+        """
+        if FIELD_MAP.get(type_name) is None:
+            return
+
+        if isinstance(FIELD_MAP[type_name], tuple):
+            type_name, options = FIELD_MAP[type_name]
+        else:
+            type_name, options = FIELD_MAP[type_name], {}
+        # update options with 'creation_date' and 'modification_date', that are
+        # the same as in FormFolder (dates on fields must be the same)
+        options.update(self.date_fields)
+
+        if field_id not in context.contentIds():
+            try:
+                field = _createObjectByType(type_name, context, field_id)
+            except ConflictError:
+                raise
+            except:
+                return
+        else:
+            field = context._getOb(field_id)
+
+        try:
+            IXMLDemarshaller(field).demarshall(field_node, **options)
+        except ConflictError:
+            raise
+
+    def createFieldset(self, title):
+        """ Create FieldsetFolder with id=title
+        """
+        if title not in self.context.contentIds():
+            try:
+                fieldset = _createObjectByType('FieldsetFolder', self.context, title)
+            except ConflictError:
+                raise
+            except:
+                return
+        else:
+            fieldset = self.context._getOb(title)
+        fieldset.Schema()['title'].getMutator(fieldset)(title)
+
+        return fieldset
+
+class BaseFieldDemarshaller(object):
+    """ Base class for demarshallers of PloneFormGen's fields from PloneFormMailer fields.
+    """
+    implements(IXMLDemarshaller)
+
+    def __init__(self, context):
+        self.context = context
+
+    def demarshall(self, node, **kw):
+        self.extractData(node)
+        # update data dictionary form keyword arguments
+        for k, v in kw.items():
+            self.data[k] = v
+        # call special hook for changing data
+        self.modifyData()
+
+        # update instance
+        schema = self.context.Schema()
+        for fname, value in self.data.items():
+            if not schema.has_key(fname):
+                continue
+            mutator = schema[fname].getMutator(self.context)
+            if not mutator:
+                continue
+            mutator(value)
+
+        self.context.indexObject()
+
+    def extractData(self, node):
+        tree = XMLObject()
+        elementToObject(tree, node)
+        simplify_single_entries(tree)
+
+        data = {}
+        values = tree.first.field.first.values
+        for name in values.getElementNames():
+            value = getattr(values.first, name)
+            if value.attributes.get('type') == 'float':
+                data[name] = float(value.text)
+            elif value.attributes.get('type') == 'int':
+                data[name] = int(value.text)
+                # boolean property is exported as int, fix that
+                if name in BOOL_FIELD_PROPS:
+                    data[name] = bool(data[name])
+            elif value.attributes.get('type') == 'list':
+                # XXX bare eval here (this may be a security leak ?)
+                data[name] = eval(str(value.text))
+            elif value.attributes.get('type') == 'datetime':
+                data[name] = DateTime(value.text)
+            else:
+                data[name] = str(value.text)
+
+        # get tales expressions
+        tales = tree.first.field.first.tales
+        for name in tales.getElementNames():
+            value = getattr(tales.first, name)
+            data["tales:"+name] = str(value.text)
+
+        self.data = data
+
+    def modifyData(self):
+        pass
+
+    def renameEntry(self, old, new):
+        if self.data.has_key(old):
+            self.data[new] = self.data.pop(old)
+
+    def entryIsDigit(self, key):
+        """ Formulator exports empty fields in xml as empty elements without
+            'type' attribute. That's is why we get in data dictionary for 
+            integer fields values that are empty strings. This method is 
+            used to check whether integer field has integer value.
+        """
+        if key in self.data and isinstance(self.data[key], int):
+            return True
+        else:
+            return False
+
+class StringFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of StringField and other fields of this kind.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+        if self.entryIsDigit('display_maxwidth'):
+            self.renameEntry('display_maxwidth', 'fgmaxlength')
+        if self.entryIsDigit('display_width'):
+            self.renameEntry('display_width', 'fgsize')
+
+class TextFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of TextField.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+        if self.entryIsDigit('max_length'):
+            self.renameEntry('max_length', 'fgmaxlength')
+        if self.entryIsDigit('height'):
+            self.renameEntry('height', 'fgRows')
+
+class LabelFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of  LabelField.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+
+class IntegerFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of IntegerField.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+        if self.entryIsDigit('display_maxwidth'):
+            self.renameEntry('display_maxwidth', 'fgmaxlength')
+        if self.entryIsDigit('display_width'):
+            self.renameEntry('display_width', 'fgsize')
+        if self.entryIsDigit('start'):
+            self.renameEntry('start', 'minval')
+        if self.entryIsDigit('end'):
+            self.renameEntry('end', 'maxval')
+
+class DateTimeFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of DateTimeField.
+    """
+
+    def modifyData(self):
+        if 'default' in self.data:
+            # convert from DateTime object to string
+            self.data['default'] = str(self.data['default'])
+            self.renameEntry('default', 'fgDefault')
+        # date_only is boolean flag
+        self.data['fgShowHM'] = not bool(self.data['date_only'])
+        if 'start_datetime' in self.data:
+            self.data['start_datetime'] = self.data['start_datetime'].year()
+            self.renameEntry('start_datetime', 'fgStartingYear')
+        if 'end_datetime' in self.data:
+            self.data['end_datetime'] = self.data['end_datetime'].year()
+            self.renameEntry('end_datetime', 'fgEndingYear')
+
+class LinesFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of LinesField.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+        if self.entryIsDigit('height'):
+            self.renameEntry('height', 'fgRows')
+
+class BooleanFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of BooleanField.
+    """
+
+    def modifyData(self):
+        self.data['default'] = bool(self.data['default'])
+        self.renameEntry('default', 'fgDefault')
+
+
+class SelectionFieldDemarshaller(BaseFieldDemarshaller):
+    """ Demarshaller of SelectionField.
+    """
+
+    def modifyData(self):
+        self.renameEntry('default', 'fgDefault')
+        l = []
+        for i in self.data['items']:
+            i = list(i)
+            i.reverse()
+            l.append(i)
+        self.data['items'] = ['|'.join(i) for i in l]
+        self.renameEntry('items', 'fgVocabulary')
+
+class MultiSelectFieldDemarshaller(SelectionFieldDemarshaller):
+    """ Demarshaller of MultiSelectField.
+    """
+
+    def modifyData(self):
+        super(MultiSelectFieldDemarshaller, self).modifyData()
+        if self.entryIsDigit('size'):
+            self.renameEntry('size', 'fgRows')
+
+
+# next code was stolen from Formulator product
+from xml.dom.minidom import parse, parseString, Node
+
+# an extremely simple system for loading in XML into objects
+
+class Object:
+    pass
+
+class XMLObject:
+    def __init__(self):
+        self.elements = Object()
+        self.first = Object()
+        self.attributes = {}
+        self.text = ''
+
+    def getElementNames(self):
+        return [element for element in dir(self.elements)
+                if not element.startswith('__')]
+
+    def getAttributes(self):
+        return self.attributes
+
+def elementToObject(parent, node):
+    # create an object to represent element node
+    object = XMLObject()
+    # make object attributes off node attributes
+    for key, value in node.attributes.items():
+        object.attributes[key] = value
+    # make lists of child elements (or ignore them)
+    for child in node.childNodes:
+        nodeToObject(object, child)
+    # add ourselves to parent node
+    name = str(node.nodeName)
+    l = getattr(parent.elements, name, [])
+    l.append(object)
+    setattr(parent.elements, name, l)
+
+def attributeToObject(parent, node):
+    # should never be called
+    pass
+
+def textToObject(parent, node):
+    # add this text to parents text content
+    parent.text += node.data
+
+def processingInstructionToObject(parent, node):
+    # don't do anything with these
+    pass
+
+def commentToObject(parent, node):
+    # don't do anything with these
+    pass
+
+def documentToObject(parent, node):
+    elementToObject(parent, node.documentElement)
+
+def documentTypeToObject(parent, node):
+    # don't do anything with these
+    pass
+
+_map = {
+    Node.ELEMENT_NODE: elementToObject,
+    Node.ATTRIBUTE_NODE: attributeToObject,
+    Node.TEXT_NODE: textToObject,
+ #   Node.CDATA_SECTION_NODE:
+ #   Node.ENTITY_NODE:
+    Node.PROCESSING_INSTRUCTION_NODE: processingInstructionToObject,
+    Node.COMMENT_NODE: commentToObject,
+    Node.DOCUMENT_NODE: documentToObject,
+    Node.DOCUMENT_TYPE_NODE: documentTypeToObject,
+#    Node.NOTATION_NODE:
+    }
+
+def nodeToObject(parent, node):
+    _map[node.nodeType](parent, node)
+
+def simplify_single_entries(object):
+    for name in object.getElementNames():
+        l = getattr(object.elements, name)
+        # set the first subelement (in case it's just one, this is easy)
+        setattr(object.first, name, l[0])
+        # now do the same for rest
+        for element in l:
+            simplify_single_entries(element)
+
+def XMLToObjectsFromFile(path):
+    return XMLToObjects(parse(path))
+
+def XMLToObjectsFromString(s):
+    return XMLToObjects(parseString(s))
+
+def XMLToObjects(document):
+    object = XMLObject()
+    documentToObject(object, document)
+    document.unlink()
+    simplify_single_entries(object)
+    return object
+
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/overrides.zcml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/overrides.zcml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/overrides.zcml	(revision 1497)
@@ -0,0 +1,12 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier">
+
+    <transmogrifier:registerConfig
+        name="import"
+        title="Import pipeline configuration"
+        description="Customized for PloneFormMailer/PloneFormGen content types content import pipeline."
+        configuration="import.cfg"
+        />
+
+</configure>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/__init__.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/__init__.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/__init__.py	(revision 1497)
@@ -0,0 +1,1 @@
+# i'm a package
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/complex_form.xml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/complex_form.xml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/complex_form.xml	(revision 1497)
@@ -0,0 +1,646 @@
+<?xml version="1.0" encoding="utf-8"?>
+<metadata xmlns="http://plone.org/ns/archetypes/" xmlns:cmf="http://cmf.zope.org/namespaces/default/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="adobe:ns:meta">
+    <dc:title>
+        Test form
+    </dc:title>
+    <dc:creator>
+        admin
+    </dc:creator>
+    <xmp:CreateDate>
+        2007-05-15T08:41:51Z
+    </xmp:CreateDate>
+    <xmp:ModifyDate>
+        2007-12-04T09:25:47Z
+    </xmp:ModifyDate>
+    <field name="id">
+        complex-form
+    </field>
+    <field name="allowDiscussion">
+        None
+    </field>
+    <field name="effectiveDate">
+        2007/12/04 03:25:47.294 US/Central
+    </field>
+    <field name="subject">
+        string:Test form submit
+    </field>
+    <field name="recipient_name">
+        Test
+    </field>
+    <field name="recipient_email">
+        string:recipient@mail.org
+    </field>
+    <field name="cc_recipients">
+        cc1@mail.org
+    </field>
+    <field name="cc_recipients">
+        cc2@mail.org
+    </field>
+    <field name="bcc_recipients">
+        bcc1@mail.org
+    </field>
+    <field name="bcc_recipients">
+        bcc2@mail.org
+    </field>
+    <field name="additional_headers">
+        Header1: value1
+    </field>
+    <field name="additional_headers">
+        Header2: value2
+    </field>
+    <field name="body_pre">
+        Next are input fields
+    </field>
+    <field name="body_post">
+        You have filled all necessary fields
+    </field>
+    <field name="body_footer">
+        It's built on Plone
+    </field>
+    <field name="body_pt">
+        &lt;tal:block i18n:domain=&quot;pfm&quot;
+           tal:define=&quot;form here/form;
+                       groups form/get_groups;&quot;&gt;
+&lt;html&gt;
+&lt;body style=&quot;font-family:verdana,sans-serif;&quot;&gt;
+&lt;p tal:condition=&quot;here/getBodyPre&quot; tal:content=&quot;here/getBodyPre&quot; /&gt;
+&lt;p tal:condition=&quot;options/prepend&quot; tal:content=&quot;options/prepend&quot; /&gt;
+
+&lt;tal:block tal:repeat=&quot;group groups&quot;&gt;
+  &lt;h1 tal:condition=&quot;python:group!='Default'&quot; tal:content=&quot;group&quot; /&gt;
+  &lt;table style=&quot;font-family:verdana,sans-serif;&quot;&gt;
+    &lt;tbody valign=&quot;top&quot;&gt;
+      &lt;tal:block tal:repeat=&quot;field python:form.get_fields_in_group(group)&quot;&gt;
+        &lt;tr&gt;
+          &lt;th i18n:translate=&quot;&quot; style=&quot;text-align:right; white-space: nowrap; border-bottom: 1px solid #cccccc;&quot; tal:content=&quot;field/title&quot; /&gt;
+          &lt;td&gt;
+            &lt;div tal:repeat=&quot;line field/mailsplitter&quot;&gt;
+              &lt;span tal:content=&quot;line&quot;/&gt;
+            &lt;/div&gt;
+          &lt;/td&gt;
+        &lt;/tr&gt;
+      &lt;/tal:block&gt;
+    &lt;/tbody&gt;
+  &lt;/table&gt;
+&lt;/tal:block&gt;
+
+&lt;p tal:condition=&quot;options/append&quot; tal:content=&quot;options/append&quot; /&gt;
+&lt;p tal:condition=&quot;here/getBodyPost&quot; tal:content=&quot;here/getBodyPost&quot; /&gt;
+&lt;pre tal:content=&quot;here/getFooter&quot; /&gt;
+&lt;/body&gt;
+&lt;/html&gt;
+&lt;/tal:block&gt;
+    </field>
+    <field name="body_type">
+        text/html
+    </field>
+    <field name="form_pre">
+        
+&lt;p&gt;form prologue&lt;/p&gt;
+
+    </field>
+    <field name="form_post">
+        
+&lt;p&gt;form epilogue&lt;/p&gt;
+
+    </field>
+    <field name="form_buttons">
+        Submit|submit|submit|context
+    </field>
+    <field name="form_buttons">
+        Reset|reset|reset|standalone
+    </field>
+    <field name="sent_title">
+        The form was sent.
+    </field>
+    <field name="sent_message">
+
+        &lt;p&gt;Thank you for submiting it.&lt;br /&gt;&lt;/p&gt;
+
+    </field>
+    <field name="sent_redirect">
+        string:test
+    </field>
+    <field name="cpyaction">
+        script
+    </field>
+    <uid>
+        738f9995ffd82394ff8a3fb75450c354
+    </uid>
+    <cmf:type>
+        PloneFormMailer
+    </cmf:type>
+    <cmf:workflow_history>
+        <cmf:workflow id="PloneFormMailer_workflow">
+            <cmf:history>
+                <cmf:var id="action" type="None" value="None"/>
+                <cmf:var id="actor" type="str" value="admin"/>
+                <cmf:var id="comments" type="str" value=""/>
+                <cmf:var id="review_state" type="str" value="members only send"/>
+                <cmf:var id="time" type="date" value="2007-05-15 03:41:51"/>
+            </cmf:history>
+            <cmf:history>
+                <cmf:var id="action" type="str" value="enable anonymous send"/>
+                <cmf:var id="actor" type="str" value="admin"/>
+                <cmf:var id="comments" type="str" value=""/>
+                <cmf:var id="review_state" type="str" value="anoymous send"/>
+                <cmf:var id="time" type="date" value="2007-12-04 03:25:47"/>
+            </cmf:history>
+        </cmf:workflow>
+    </cmf:workflow_history>
+    <cmf:security>
+        <cmf:local_role role="Owner" user_id="admin"/>
+    </cmf:security>
+<form>
+  <title>PFM Formulator</title>
+  <row_length>4</row_length>
+  <name>form</name>
+  <action/>
+  <method>POST</method>
+  <enctype/>
+  <encoding>UTF-8</encoding>
+  <stored_encoding>ISO-8859-1</stored_encoding>
+  <unicode_mode>false</unicode_mode>
+  <i18n_domain/>
+  <groups>
+    <group>
+      <title>Default</title>
+      <fields>
+          <field><id>field1</id> <type>StringField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default></default>
+                  <description>field description</description>
+                  <display_maxwidth></display_maxwidth>
+                  <display_width type="int">20</display_width>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <hidden type="int">0</hidden>
+                  <max_length></max_length>
+                  <required type="int">1</required>
+                  <title>String field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+              </messages>
+          </field>
+          <field><id>field2</id> <type>EmailField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default>test@mail.com</default>
+                  <description></description>
+                  <display_maxwidth></display_maxwidth>
+                  <display_width type="int">20</display_width>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <hidden type="int">0</hidden>
+                  <max_length></max_length>
+                  <required type="int">1</required>
+                  <title>Email field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+                  <message name="not_email">You did not enter an email address.</message>
+              </messages>
+          </field>
+          <field><id>field3</id> <type>LinkField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <check_link type="int">0</check_link>
+                  <check_timeout type="float">7.0</check_timeout>
+                  <check_timeout>7.0</check_timeout>
+                  <css_class></css_class>
+                  <default></default>
+                  <description></description>
+                  <display_maxwidth type="int">15</display_maxwidth>
+                  <display_width type="int">20</display_width>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <hidden type="int">0</hidden>
+                  <link_type>external</link_type>
+                  <max_length></max_length>
+                  <required type="int">0</required>
+                  <title>Link field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+                  <message name="not_link">The specified link is broken.</message>
+              </messages>
+          </field>
+
+          <field><id>field4</id> <type>PatternField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default></default>
+                  <description></description>
+                  <display_maxwidth></display_maxwidth>
+                  <display_width type="int">20</display_width>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <hidden type="int">1</hidden>
+                  <max_length></max_length>
+                  <pattern>*</pattern>
+                  <required type="int">1</required>
+                  <title>Pattern field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+                  <message name="pattern_not_matched">The entered value did not match the pattern.</message>
+              </messages>
+          </field>
+          <field><id>field5</id> <type>TextAreaField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default></default>
+                  <description></description>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <height type="int">5</height>
+                  <hidden type="int">0</hidden>
+                  <max_length type="int">1000</max_length>
+                  <max_linelength></max_linelength>
+                  <max_lines></max_lines>
+                  <required type="int">1</required>
+                  <title>Text area field</title>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+                  <width type="int">40</width>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_many_lines">You entered too many lines.</message>
+                  <message name="line_too_long">A line was too long.</message>
+                  <message name="too_long">You entered too many characters.</message>
+              </messages>
+          </field>
+          <field><id>field6</id> <type>RawTextAreaField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default></default>
+                  <description></description>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <height type="int">5</height>
+                  <hidden type="int">0</hidden>
+                  <max_length></max_length>
+                  <required type="int">1</required>
+                  <title>Raw text area field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+                  <width type="int">40</width>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+              </messages>
+          </field>
+          <field><id>field7</id> <type>PasswordField</type>
+              <values>
+                  <alternate_name></alternate_name>
+                  <css_class></css_class>
+                  <default></default>
+                  <description></description>
+                  <display_maxwidth></display_maxwidth>
+                  <display_width type="int">20</display_width>
+                  <enabled type="int">1</enabled>
+                  <external_validator></external_validator>
+                  <extra></extra>
+                  <hidden type="int">0</hidden>
+                  <max_length></max_length>
+                  <required type="int">1</required>
+                  <title>Password field</title>
+                  <truncate type="int">0</truncate>
+                  <unicode type="int">0</unicode>
+                  <whitespace_preserve type="int">0</whitespace_preserve>
+              </values>
+              <tales>
+              </tales>
+              <messages>
+                  <message name="external_validator_failed">The input failed the external validator.</message>
+                  <message name="required_not_found">Input is required but no input given.</message>
+                  <message name="too_long">Too much input was given.</message>
+              </messages>
+           </field>
+           <field><id>field8</id> <type>LabelField</type>
+               <values>
+                   <css_class></css_class>
+                   <default>label text</default>
+                   <description></description>
+                   <enabled type="int">1</enabled>
+                   <external_validator></external_validator>
+                   <extra></extra>
+                   <hidden type="int">0</hidden>
+                   <title>Label field</title>
+               </values>
+               <tales>
+               </tales>
+               <messages>
+               </messages>
+            </field>
+            <field><id>field9</id> <type>IntegerField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default>55</default>
+                    <description></description>
+                    <display_maxwidth type="int">40</display_maxwidth>
+                    <display_width type="int">20</display_width>
+                    <enabled type="int">1</enabled>
+                    <end type="int">1000</end>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <hidden type="int">0</hidden>
+                    <required type="int">1</required>
+                    <start type="int">10</start>
+                    <title>Integer field</title>
+                    <whitespace_preserve type="int">0</whitespace_preserve>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="not_integer">You did not enter an integer.</message>
+                    <message name="integer_out_of_range">The integer you entered was out of range.</message>
+                </messages>
+            </field>
+            <field><id>field10</id> <type>FloatField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default></default>
+                    <description></description>
+                    <display_maxwidth type="int">10</display_maxwidth>
+                    <display_width type="int">20</display_width>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <hidden type="int">0</hidden>
+                    <required type="int">1</required>
+                    <title>Float field</title>
+                    <whitespace_preserve type="int">0</whitespace_preserve>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="not_float">You did not enter a floating point number.</message>
+                </messages>
+            </field>
+            <field><id>field11</id> <type>DateTimeField</type>
+                <values>
+                    <allow_empty_time type="int">0</allow_empty_time>
+                    <alternate_name></alternate_name>
+                    <ampm_time_style type="int">0</ampm_time_style>
+                    <css_class></css_class>
+                    <date_only type="int">0</date_only>
+                    <date_separator>/</date_separator>
+                    <default type="datetime">2008/10/10 10:10:00 GMT+3</default>
+                    <default_now type="int">0</default_now>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <end_datetime type="datetime">2010/12/31 23:59:00 GMT+2</end_datetime>
+                    <external_validator></external_validator>
+                    <hidden type="int">0</hidden>
+                    <hide_day type="int">0</hide_day>
+                    <input_order>ymd</input_order>
+                    <input_style>text</input_style>
+                    <required type="int">1</required>
+                    <start_datetime type="datetime">2006/01/01</start_datetime>
+                    <time_separator>:</time_separator>
+                    <title>Date time field</title>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="not_datetime">You did not enter a valid date and time.</message>
+                    <message name="datetime_out_of_range">The date and time you entered were out of range.</message>
+                </messages>
+            </field>
+            <field><id>field12</id> <type>FileField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default></default>
+                    <description></description>
+                    <display_maxwidth></display_maxwidth>
+                    <display_width type="int">20</display_width>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <hidden type="int">0</hidden>
+                    <title>File field</title>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                </messages>
+            </field>
+            <field><id>field13</id> <type>LinesField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default type="list">['first', 'second', 'third']</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <height type="int">5</height>
+                    <hidden type="int">0</hidden>
+                    <max_length></max_length>
+                    <max_linelength></max_linelength>
+                    <max_lines></max_lines>
+                    <required type="int">1</required>
+                    <title>Lines field</title>
+                    <unicode type="int">0</unicode>
+                    <view_separator>&lt;br /&gt;</view_separator>
+                    <whitespace_preserve type="int">0</whitespace_preserve>
+                    <width type="int">40</width>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="too_many_lines">You entered too many lines.</message>
+                    <message name="line_too_long">A line was too long.</message>
+                    <message name="too_long">You entered too many characters.</message>
+                </messages>
+            </field>
+            <field><id>field14</id> <type>CheckBoxField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default type="int">1</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <hidden type="int">0</hidden>
+                    <title>Checkbox field</title>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                </messages>
+            </field>
+            <field><id>field15</id> <type>ListField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default>first</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <extra_item></extra_item>
+                    <first_item type="int">0</first_item>
+                    <hidden type="int">0</hidden>
+                    <items type="list">[('First', 'first'), ('Second', 'second'), ('Third', 'third')]</items>
+                    <required type="int">1</required>
+                    <size type="int">5</size>
+                    <title>List field</title>
+                    <unicode type="int">0</unicode>
+                    <whitespace_preserve type="int">0</whitespace_preserve>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="unknown_selection">You selected an item that was not in the list.</message>
+                </messages>
+            </field>
+            <field><id>field16</id> <type>RadioField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default>first</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra_item></extra_item>
+                    <first_item type="int">0</first_item>
+                    <hidden type="int">0</hidden>
+                    <items type="list">[('First', 'first'), ('Second', 'second'), ('Third', 'third')]</items>
+                    <orientation>vertical</orientation>
+                    <required type="int">1</required>
+                    <title>Radio field</title>
+                    <unicode type="int">0</unicode>
+                    <whitespace_preserve type="int">0</whitespace_preserve>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="unknown_selection">You selected an item that was not in the list.</message>
+                </messages>
+            </field>
+            <field><id>field17</id> <type>MultiListField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default type="list">['first', 'third']</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra></extra>
+                    <extra_item></extra_item>
+                    <hidden type="int">0</hidden>
+                    <items type="list">[('First', 'first'), ('Second', 'second'), ('Third', 'third')]</items>
+                    <required type="int">1</required>
+                    <size type="int">5</size>
+                    <title>Multi list field</title>
+                    <unicode type="int">0</unicode>
+                    <view_separator>&lt;br /&gt;</view_separator>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="unknown_selection">You selected an item that was not in the list.</message>
+                </messages>
+            </field>
+            <field><id>field18</id> <type>MultiCheckBoxField</type>
+                <values>
+                    <alternate_name></alternate_name>
+                    <css_class></css_class>
+                    <default type="list">['first', 'third']</default>
+                    <description></description>
+                    <enabled type="int">1</enabled>
+                    <external_validator></external_validator>
+                    <extra_item></extra_item>
+                    <hidden type="int">0</hidden>
+                    <items type="list">[('First', 'first'), ('Second', 'second'), ('Third', 'third')]</items>
+                    <orientation>vertical</orientation>
+                    <required type="int">1</required>
+                    <title>Multi checkbox field</title>
+                    <unicode type="int">0</unicode>
+                    <view_separator>&lt;br /&gt;</view_separator>
+                </values>
+                <tales>
+                </tales>
+                <messages>
+                    <message name="external_validator_failed">The input failed the external validator.</message>
+                    <message name="required_not_found">Input is required but no input given.</message>
+                    <message name="unknown_selection">You selected an item that was not in the list.</message>
+                </messages>
+            </field>
+      </fields>
+    </group>
+  </groups>
+</form>
+</metadata>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/fieldset_form.xml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/fieldset_form.xml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/fieldset_form.xml	(revision 1497)
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<metadata xmlns="http://plone.org/ns/archetypes/" xmlns:cmf="http://cmf.zope.org/namespaces/default/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="adobe:ns:meta">
+<form>
+  <title>PFM Formulator</title>
+  <row_length>4</row_length>
+  <name>form</name>
+  <action></action>
+  <method>POST</method>
+  <enctype></enctype>
+  <encoding>UTF-8</encoding>
+  <stored_encoding>ISO-8859-1</stored_encoding>
+  <unicode_mode>false</unicode_mode>
+  <i18n_domain></i18n_domain>
+  <groups>
+    <group>
+      <title>Default</title>
+      <fields>
+
+      <field><id>checkbox</id> <type>CheckBoxField</type>
+        <values>
+          <alternate_name></alternate_name>
+          <css_class></css_class>
+          <default type="int">1</default>
+          <description>simple checkbox field</description>
+          <enabled type="int">1</enabled>
+          <external_validator></external_validator>
+          <extra></extra>
+          <hidden type="int">0</hidden>
+          <title>checkbox</title>
+        </values>
+        <tales>
+        </tales>
+        <messages>
+          <message name="external_validator_failed">The input failed the external validator.</message>
+        </messages>
+      </field>
+      <field><id>datetime</id> <type>DateTimeField</type>
+        <values>
+          <allow_empty_time type="int">0</allow_empty_time>
+          <alternate_name></alternate_name>
+          <ampm_time_style type="int">1</ampm_time_style>
+          <css_class></css_class>
+          <date_only type="int">0</date_only>
+          <date_separator>/</date_separator>
+          <default type="datetime">2005/12/01 13:25:00 GMT+2</default>
+          <default_now type="int">0</default_now>
+          <description>datetime field</description>
+          <enabled type="int">1</enabled>
+          <end_datetime type="datetime">2010/12/10 12:10:00 GMT+2</end_datetime>
+          <external_validator></external_validator>
+          <hidden type="int">0</hidden>
+          <hide_day type="int">0</hide_day>
+          <input_order>ymd</input_order>
+          <input_style>list</input_style>
+          <required type="int">0</required>
+          <start_datetime type="datetime">2005/10/10 12:10:00 GMT+3</start_datetime>
+          <time_separator>:</time_separator>
+          <title>datetime</title>
+        </values>
+        <tales>
+        </tales>
+        <messages>
+          <message name="external_validator_failed">The input failed the external validator.</message>
+          <message name="required_not_found">Input is required but no input given.</message>
+          <message name="not_datetime">You did not enter a valid date and time.</message>
+          <message name="datetime_out_of_range">The date and time you entered were out of range.</message>
+        </messages>
+      </field>
+      </fields>
+    </group>
+    <group>
+      <title>Other</title>
+      <fields>
+
+      <field><id>email</id> <type>EmailField</type>
+        <values>
+          <alternate_name></alternate_name>
+          <css_class></css_class>
+          <default></default>
+          <description>email field</description>
+          <display_maxwidth></display_maxwidth>
+          <display_width type="int">20</display_width>
+          <enabled type="int">1</enabled>
+          <external_validator></external_validator>
+          <extra></extra>
+          <hidden type="int">0</hidden>
+          <max_length></max_length>
+          <required type="int">0</required>
+          <title>email</title>
+          <truncate type="int">0</truncate>
+          <unicode type="int">0</unicode>
+          <whitespace_preserve type="int">0</whitespace_preserve>
+        </values>
+        <tales>
+        </tales>
+        <messages>
+          <message name="external_validator_failed">The input failed the external validator.</message>
+          <message name="required_not_found">Input is required but no input given.</message>
+          <message name="too_long">Too much input was given.</message>
+          <message name="not_email">You did not enter an email address.</message>
+        </messages>
+      </field>
+      <field><id>float</id> <type>FloatField</type>
+        <values>
+          <alternate_name></alternate_name>
+          <css_class></css_class>
+          <default>3.14</default>
+          <description>float field</description>
+          <display_maxwidth></display_maxwidth>
+          <display_width type="int">20</display_width>
+          <enabled type="int">1</enabled>
+          <external_validator></external_validator>
+          <extra></extra>
+          <hidden type="int">0</hidden>
+          <required type="int">0</required>
+          <title>float</title>
+          <whitespace_preserve type="int">0</whitespace_preserve>
+        </values>
+        <tales>
+        </tales>
+        <messages>
+          <message name="external_validator_failed">The input failed the external validator.</message>
+          <message name="required_not_found">Input is required but no input given.</message>
+          <message name="not_float">You did not enter a floating point number.</message>
+        </messages>
+      </field>
+      <field><id>string</id> <type>StringField</type>
+        <values>
+          <alternate_name></alternate_name>
+          <css_class></css_class>
+          <default>value</default>
+          <description>description</description>
+          <display_maxwidth></display_maxwidth>
+          <display_width type="int">20</display_width>
+          <enabled type="int">1</enabled>
+          <external_validator></external_validator>
+          <extra></extra>
+          <hidden type="int">0</hidden>
+          <max_length></max_length>
+          <required type="int">0</required>
+          <title>String field</title>
+          <truncate type="int">0</truncate>
+          <unicode type="int">1</unicode>
+          <whitespace_preserve type="int">0</whitespace_preserve>
+        </values>
+        <tales>
+          <default>string:Default</default>
+        </tales>
+        <messages>
+          <message name="external_validator_failed">The input failed the external validator.</message>
+          <message name="required_not_found">Input is required but no input given.</message>
+          <message name="too_long">Too much input was given.</message>
+        </messages>
+      </field>
+      </fields>
+    </group>
+  </groups>
+</form>
+</metadata>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_export.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_export.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_export.py	(revision 1497)
@@ -0,0 +1,107 @@
+import time
+import unittest
+from xml.dom import minidom
+
+from Testing import ZopeTestCase
+from Products.CMFPlone.tests.PloneTestCase import PloneTestCase
+from Products.Marshall.registry import getComponent
+
+try:
+    import Products.PloneFormMailer
+    HAS_PFM = True
+except ImportError:
+    HAS_PFM = False
+
+# next condition is required, because a test preparation
+# raises errors on Plone 3+
+if HAS_PFM:
+    from quintagroup.transmogrifier.pfm2pfg.exporting import PloneFormMailerExporter
+
+    ZopeTestCase.installProduct('Formulator')
+    ZopeTestCase.installProduct('MimetypesRegistry')
+    ZopeTestCase.installProduct('PortalTransforms')
+    ZopeTestCase.installProduct('Archetypes')
+    ZopeTestCase.installProduct('PloneFormMailer')
+    ZopeTestCase.installProduct('TALESField')
+
+    def setupPloneFormMailerTestCase(app, quiet=0):
+        get_transaction().begin()
+        _start = time.time()
+        if not quiet:
+            ZopeTestCase._print('Adding PloneFormMailer ... ')
+        app.portal.portal_quickinstaller.installProduct('PloneFormMailer')
+        get_transaction().commit()
+        if not quiet:
+            ZopeTestCase._print('done (%.3fs)\n' % (time.time()-_start,))
+
+    app = ZopeTestCase.app()
+    setupPloneFormMailerTestCase(app, quiet=True)
+    ZopeTestCase.close(app)
+
+class TestExport(PloneTestCase):
+    """Test case for proping up a test portal."""
+    def afterSetUp(self):
+        self.loginPortalOwner()
+        self.portal.invokeFactory('PloneFormMailer', id='form')
+        form = self.portal.form
+        ['recipient_name', 'bcc_recipients', 'cc_recipients']
+        form.setRecipientName("string:Recipient")
+        form.setCCRecipients([
+            'string:cc1@mail.org',
+            'string:cc2@mail.org'
+        ])
+        form.setBCCRecipients([
+            'string:bcc1@mail.org',
+            'string:bcc2@mail.org'
+        ])
+        form.setSubject('string:Email subject')
+
+    def _testField(self, xml, name, values):
+        """ Check whether marshalled in xml format field has some values.
+        """
+        doc = minidom.parseString(xml)
+        elems = [i for i in doc.getElementsByTagName('field') if i.getAttribute('name') == name]
+        if type(values) not in (list, tuple):
+            values = [values]
+        fields = []
+        for i in elems:
+            fields.append(i.firstChild.nodeValue.strip())
+        return values == fields
+
+    def test_data_corrector(self):
+        _, _, xml = getComponent('atxml').marshall(self.portal.form)
+        data = {'name': '', 'data': xml}
+        #import pdb; pdb.set_trace()
+        adapter = PloneFormMailerExporter(self.portal.form)
+        adapter(data)
+        xml = data['data']
+        self.assertEqual(self._testField(xml, 'recipient_name', 'Recipient'), True)
+        self.assertEqual(
+            self._testField(xml,
+                'cc_recipients',
+                ['cc1@mail.org', 'cc2@mail.org']),
+            True
+        )
+        self.assertEqual(
+            self._testField(xml,
+                'bcc_recipients',
+                ['bcc1@mail.org', 'bcc2@mail.org']),
+            True
+        )
+        self.assertEqual(self._testField(xml, 'subject', 'string:Email subject'), True)
+
+    def test_bad_tales_field(self):
+        self.portal.form.setRecipientName("bad tales expression")
+        _, _, xml = getComponent('atxml').marshall(self.portal.form)
+        data = {'name': '', 'data': xml}
+        #import pdb; pdb.set_trace()
+        adapter = PloneFormMailerExporter(self.portal.form)
+        adapter(data)
+        xml = data['data']
+        #import pdb; pdb.set_trace()
+        self.assertEqual(self._testField(xml, 'recipient_name', 'bad tales expression'), False)
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestExport))
+    return suite
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.cfg
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.cfg	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.cfg	(revision 1497)
@@ -0,0 +1,23 @@
+[transmogrifier]
+pipeline =
+    source
+    substitution
+    constructor
+    xslt
+    datacorrector
+    demarshaller
+    referencesimporter
+include = quintagroup.transmogrifier:import.cfg
+
+[source]
+blueprint = quintagroup.transmogrifier.pfm2pfg.tests.source
+
+[substitution]
+blueprint = quintagroup.transmogrifier.substitution
+key = _type
+PloneFormMailer = FormFolder
+
+[xslt]
+blueprint = quintagroup.transmogrifier.xslt
+from-key = _old_type
+to-key = _type
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.py	(revision 1497)
@@ -0,0 +1,455 @@
+import unittest
+import os.path
+import email
+
+from zope.interface import classProvides, implements
+import transaction
+
+from Products.Five import zcml
+from Products.Five import fiveconfigure
+from Testing import ZopeTestCase as ztc
+
+from Products.PloneTestCase import PloneTestCase as ptc
+from Products.PloneTestCase.layer import onsetup
+
+from collective.transmogrifier.interfaces import ISectionBlueprint, ISection, ITransmogrifier
+
+class Source(object):
+    classProvides(ISectionBlueprint)
+    implements(ISection)
+
+    def __init__(self, transmogrifier, name, options, previous):
+        self.previous = previous
+
+    def __iter__(self):
+        for item in self.previous:
+            yield item
+
+        fname = os.path.join(os.path.dirname(__file__), 'complex_form.xml')
+        xml = file(fname).read()
+        item = dict(
+            _type='PloneFormMailer',
+            _path='complex-form',
+            _files=dict(
+                marshall=dict(
+                    name='.marshall.xml',
+                    data=xml
+                )
+            )
+        )
+        yield item
+
+        fname = os.path.join(os.path.dirname(__file__), 'fieldset_form.xml')
+        xml = file(fname).read()
+        item = dict(
+            _type='PloneFormMailer',
+            _path='fieldset-form',
+            _files=dict(
+                marshall=dict(
+                    name='.marshall.xml',
+                    data=xml
+                )
+            )
+        )
+        yield item
+
+ztc.installProduct('PloneFormGen')
+
+@onsetup
+def setup_product():
+    fiveconfigure.debug_mode = True
+    import quintagroup.transmogrifier.pfm2pfg
+    zcml.load_config('configure.zcml', quintagroup.transmogrifier.pfm2pfg)
+    import quintagroup.transmogrifier.pfm2pfg.tests
+    zcml.load_config('test_import.zcml', quintagroup.transmogrifier.pfm2pfg.tests)
+    fiveconfigure.debug_mode = False
+
+setup_product()
+ptc.setupPloneSite(products=['PloneFormGen'])
+
+class TestImport(ptc.PloneTestCase):
+    """ Test importing of PloneFormGen content.
+    """
+
+    def afterSetUp(self):
+        if 'complex-form' not in self.portal:
+            # run transmogrifier pipeline
+            transmogrifier = ITransmogrifier(self.portal)
+            transmogrifier('test_import')
+
+    def beforeTearDown(self):
+        transaction.commit()
+
+    def test_form_folder(self):
+        self.failUnless('complex-form' in self.portal)
+        form = self.portal['complex-form']
+
+        self.assertEqual(form['title'], 'Test form')
+        self.assertEqual(form.getFormPrologue(), '<p>form prologue</p>')
+        self.assertEqual(form.getFormEpilogue(), '<p>form epilogue</p>')
+        self.assertEqual(form['thanksPageOverride'], 'redirect_to:string:test')
+        self.assertEqual(form['afterValidationOverride'], 'string:script')
+
+    def test_mailer(self):
+        form = self.portal['complex-form']
+        self.failUnless('mailer' in form)
+        mailer = form['mailer']
+
+        self.assertEqual(mailer['subjectOverride'], 'string:Test form submit')
+        self.assertEqual(mailer['recipient_name'], 'Test')
+        self.assertEqual(mailer['recipientOverride'], 'string:recipient@mail.org')
+        self.assertEqual(mailer['additional_headers'], ('Header1: value1', 'Header2: value2'))
+        self.assertEqual(mailer['body_type'], 'html')
+        self.assertEqual(mailer.getBody_pre(), 'Next are input fields')
+        self.assertEqual(mailer.getBody_post(), 'You have filled all necessary fields')
+        self.assertEqual(mailer.getBody_footer(), 'It\'s built on Plone')
+        self.assertEqual(mailer['cc_recipients'], ('cc1@mail.org', 'cc2@mail.org'))
+        self.assertEqual(mailer['bcc_recipients'], ('bcc1@mail.org', 'bcc2@mail.org'))
+
+        self.assertEqual(mailer.created(), form.created())
+        self.assertEqual(mailer.modified(), form.modified())
+
+    def test_thanks_page(self):
+        form = self.portal['complex-form']
+        self.failUnless('thank-you' in form)
+        page = form['thank-you']
+
+        self.assertEqual(page.Title(), 'The form was sent.')
+        self.assertEqual(page.getThanksPrologue(), 
+            '<p>Thank you for submiting it.<br /></p>')
+
+        self.assertEqual(page.created(), form.created())
+        self.assertEqual(page.modified(), form.modified())
+
+    def test_string_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field1' not in form)
+        field = form['field1']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormStringField')
+        self.assertEqual(field['title'], 'String field')
+        self.assertEqual(field.Description(), 'field description')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['hidden'], False)
+        self.assertEqual(field['fgDefault'], '')
+        self.assertEqual(field['fgmaxlength'], 255)
+        self.assertEqual(field['fgsize'], 20)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_email_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field2' not in form)
+        field = form['field2']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormStringField')
+        self.assertEqual(field['title'], 'Email field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['hidden'], False)
+        self.assertEqual(field['fgDefault'], 'test@mail.com')
+        self.assertEqual(field['fgmaxlength'], 255)
+        self.assertEqual(field['fgsize'], 20)
+        self.assertEqual(field['fgStringValidator'], 'isEmail')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_link_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field3' not in form)
+        field = form['field3']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormStringField')
+        self.assertEqual(field['title'], 'Link field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], False)
+        self.assertEqual(field['hidden'], False)
+        self.assertEqual(field['fgDefault'], '')
+        self.assertEqual(field['fgmaxlength'], 15)
+        self.assertEqual(field['fgsize'], 20)
+        self.assertEqual(field['fgStringValidator'], 'isURL')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_pattern_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field4' not in form)
+        field = form['field4']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormStringField')
+        self.assertEqual(field['title'], 'Pattern field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['hidden'], True)
+        self.assertEqual(field['fgDefault'], '')
+        self.assertEqual(field['fgmaxlength'], 255)
+        self.assertEqual(field['fgsize'], 20)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_textarea_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field5' not in form)
+        field = form['field5']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormTextField')
+        self.assertEqual(field['title'], 'Text area field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['hidden'], False)
+        self.assertEqual(field.getFgDefault(), '')
+        self.assertEqual(field['fgmaxlength'], 1000)
+        self.assertEqual(field['fgRows'], 5)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_rawtextarea_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field6' not in form)
+        field = form['field6']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormTextField')
+        self.assertEqual(field['title'], 'Raw text area field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['hidden'], False)
+        self.assertEqual(field.getFgDefault(), '')
+        self.assertEqual(field['fgmaxlength'], '0')
+        self.assertEqual(field['fgRows'], 5)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_password_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field7' not in form)
+        field = form['field7']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormPasswordField')
+        self.assertEqual(field['title'], 'Password field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], '')
+        self.assertEqual(field['fgmaxlength'], 255)
+        self.assertEqual(field['fgsize'], 20)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_label_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field8' not in form)
+        field = form['field8']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormRichLabelField')
+        self.assertEqual(field['title'], 'Label field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['fgDefault'], 'label text')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_integer_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field9' not in form)
+        field = form['field9']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormIntegerField')
+        self.assertEqual(field['title'], 'Integer field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], '55')
+        self.assertEqual(field['fgmaxlength'], 40)
+        self.assertEqual(field['fgsize'], 20)
+        self.assertEqual(field['minval'], 10)
+        self.assertEqual(field['maxval'], 1000)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_float_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field10' not in form)
+        field = form['field10']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormFixedPointField')
+        self.assertEqual(field['title'], 'Float field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], '')
+        self.assertEqual(field['fgmaxlength'], 10)
+        self.assertEqual(field['fgsize'], 20)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_datetime_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field11' not in form)
+        field = form['field11']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormDateField')
+        self.assertEqual(field['title'], 'Date time field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], '2008/10/10 10:10:00 GMT+3')
+        self.assertEqual(field['fgStartingYear'], 2006)
+        self.assertEqual(field['fgEndingYear'], 2010)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_file_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field12' not in form)
+        field = form['field12']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormFileField')
+        self.assertEqual(field['title'], 'File field')
+        self.assertEqual(field.Description(), '')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_lines_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field13' not in form)
+        field = form['field13']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormLinesField')
+        self.assertEqual(field['title'], 'Lines field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], ('first', 'second', 'third'))
+        self.assertEqual(field['fgRows'], 5)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_checkbox_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field14' not in form)
+        field = form['field14']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormBooleanField')
+        self.assertEqual(field['title'], 'Checkbox field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['fgDefault'], True)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_list_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field15' not in form)
+        field = form['field15']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormSelectionField')
+        self.assertEqual(field['title'], 'List field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['fgDefault'], 'first')
+        self.assertEqual(field['fgVocabulary'], ('first|First', 'second|Second', 'third|Third'))
+        self.assertEqual(field['fgFormat'], 'select')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_radio_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field16' not in form)
+        field = form['field16']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormSelectionField')
+        self.assertEqual(field['title'], 'Radio field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['fgDefault'], 'first')
+        self.assertEqual(field['fgVocabulary'], ('first|First', 'second|Second', 'third|Third'))
+        self.assertEqual(field['fgFormat'], 'radio')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_multilist_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field17' not in form)
+        field = form['field17']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormMultiSelectionField')
+        self.assertEqual(field['title'], 'Multi list field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], ('first', 'third'))
+        self.assertEqual(field['fgVocabulary'], ('first|First', 'second|Second', 'third|Third'))
+        self.assertEqual(field['fgFormat'], 'select')
+        self.assertEqual(field['fgRows'], 5)
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_multicheckbox_field(self):
+        form = self.portal['complex-form']
+        self.failIf('field18' not in form)
+        field = form['field18']
+
+        self.assertEqual(field.getPortalTypeName(), 'FormMultiSelectionField')
+        self.assertEqual(field['title'], 'Multi checkbox field')
+        self.assertEqual(field.Description(), '')
+        self.assertEqual(field['required'], True)
+        self.assertEqual(field['fgDefault'], ('first', 'third'))
+        self.assertEqual(field['fgVocabulary'], ('first|First', 'second|Second', 'third|Third'))
+        self.assertEqual(field['fgFormat'], 'checkbox')
+
+        self.assertEqual(field.created(), form.created())
+        self.assertEqual(field.modified(), form.modified())
+
+    def test_fieldset(self):
+        self.failIf('fieldset-form' not in self.portal)
+        form = self.portal['fieldset-form']
+        self.assertEqual(form.objectIds(), ['mailer', 'thank-you', 'checkbox', 'datetime', 'Other'])
+
+        fieldset = form['Other']
+        self.assertEqual(fieldset['title'], 'Other')
+        self.assertEqual(fieldset.objectIds(), ['email', 'float', 'string'])
+
+class FakeRequest(dict):
+
+    def __init__(self, **kwargs):
+        self.form = kwargs
+
+class TestSubmit(ptc.PloneTestCase):
+    """ test ya_gpg.py """
+
+    def dummy_send(self, mfrom, mto, messageText):
+        self.mfrom = mfrom
+        self.mto = mto
+        self.messageText = messageText
+
+    def afterSetUp(self):
+        transmogrifier = ITransmogrifier(self.portal)
+        transmogrifier('test_import')
+        self.form = getattr(self.folder, 'complex-form')
+        for i in self.form.contentValues():
+            field = i.getField('required')
+            if field is not None:
+                field.getMutator(i)(False)
+        self.mailhost = self.folder.MailHost
+        self.mailhost._send = self.dummy_send
+
+    def test_submitting(self):
+        """ Test submitting of form """
+        request = FakeRequest(field2='field2@mail.org')
+        errors = self.form.fgvalidate(REQUEST=request)
+        msg = email.message_from_string(self.messageText)
+        self.assertEqual(msg.get('To'), 'Test <recipient@mail.org>')
+        self.assertEqual(msg.get('Subject'), '=?utf-8?q?Test_form_submit?=')
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestImport))
+    suite.addTest(unittest.makeSuite(TestSubmit))
+    return suite
Index: /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.zcml
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.zcml	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/quintagroup/transmogrifier/pfm2pfg/tests/test_import.zcml	(revision 1497)
@@ -0,0 +1,18 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:transmogrifier="http://namespaces.plone.org/transmogrifier"
+    i18n_domain="quintagroup.transmogrifier.pfm2pfg">
+
+    <utility
+        component=".test_import.Source"
+        name="quintagroup.transmogrifier.pfm2pfg.tests.source"
+        />
+
+    <transmogrifier:registerConfig
+        name="test_import"
+        title="Import pipeline configuration"
+        description="Customized for PloneFormMailer/PloneFormGen content types content import pipeline."
+        configuration="test_import.cfg"
+        />
+
+</configure>
Index: /quintagroup.transmogrify.pfm2pfg/trunk/setup.cfg
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/setup.cfg	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/setup.cfg	(revision 1497)
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
Index: /quintagroup.transmogrify.pfm2pfg/trunk/setup.py
===================================================================
--- /quintagroup.transmogrify.pfm2pfg/trunk/setup.py	(revision 1497)
+++ /quintagroup.transmogrify.pfm2pfg/trunk/setup.py	(revision 1497)
@@ -0,0 +1,34 @@
+from setuptools import setup, find_packages
+import os
+
+version = '0.1'
+
+setup(name='quintagroup.transmogrifier.pfm2pfg',
+      version=version,
+      description="Configuration of collective.transmogrifier pipeline for migrating PloneFormMailer content to PloneFormGen content",
+      long_description=open("README.txt").read() + "\n" +
+                       open(os.path.join("docs", "HISTORY.txt")).read(),
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=[
+        "Framework :: Plone",
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='',
+      author='Quintagroup',
+      author_email='info@quintagroup.com',
+      url='',
+      license='GPL',
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['quintagroup', 'quintagroup.transmogrifier'],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          'setuptools',
+          'quintagroup.transmogrifier'
+          # -*- Extra requirements: -*-
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )
