Just a brief overview of how this thing works, or should work...


All types are subclasses of BaseContent. How to do this and use
CMFTypes as a framework will have to come later. For now this is the
important part.

Here is a visual overview of one of the sample types in cmf_types:

class DDocument
   |
   |-BaseContent--|
     |            |-type -> FieldList(Field,...)
     |-ExtensibleMetadata--|
     |	           |-metadatatype -> FieldList(Field, ...)
     |-data -> {'fieldname' : BaseUnit }
     |->metadata -> {'fieldname' : value }		  

OK, the demo type is called DDocument and it inherits from BaseContent.

DDocument is really defined by setting up the type descriptor,
DDocument.type=FieldList() with a list of Field objects (see Field.py)
that define the type. If all you do is define Fields then you get a
very simple type with just those fields listed in forms (as
strings). Each Field definition can optionally take a FormInfo object
of some type and tell the generator about how it would like to be
displayed on the form. FormInfo objects let you set labels,
descriptions (tooltips) and choose a widget that is used for display. 

DDocument, while simple, shows off some of these features. If you look
at the Field('body') you can see that we use a RichFormInfo object
which drops a widget into the edit form that handles rich content
types like MSWord documents. This happens because data is stored in a
BaseUnit which is a field storage object.




DEVELOPMENT HOOKS:

Accessors/Mutators

Depending on the mode of each Field in the Field list the runtime system
will look for an accessor or mutator. If for example the mode of a field is
"rw" (as is the default) then the generator will ensure that accessors and
mutators exist for that field. This can happen one of two ways, either as a
developer you define the methods directly on your class, or you let the
generator provide them for you. If you don't require specialized logic then
letting the generator create these methods on your new type is a good idea.

The format for accessors and mutators is as follows:

field -> title

accessor -> Title()          here/Title
mutator  -> setTitle(value)
editable -> editableTitle() [This is the raw, unaltered object, should rename to raw]


Validators:
if you want a custom validator for a field name define it as follows

field -> title

validate_title(proposed_value) -> "Error for client" || None

There are also hooks for pre and post validation that can be used to assert
things about the entire object, these are as follows:

pre_validate(self, REQUEST, errors)
post_validate(self, REQUEST, errrors)

you must then extract values from request and write values into error
with the field name as the key. If pre_validate throws errors then
other custom validators (including post) will not be called.


ACTIONS

Define an actions member on your BaseContent subtype and the external method
will apply this to the types tool for you. This means the if you want custom
views or something you only need to say something like

class Foo(BaseContent):
    actions = ({
        'id': 'view',
        'name': 'View',
        'action': 'custom_view',
        'permissions': (CMFCorePermissions.View,) 
        },)



USING FROM OTHER PROJECTS


projects __init__ should have something like the following


from Products.CMFTypes import process_types
from Products.CMFTypes.Generator import generateViews
from Products.CMFTypes.utils import pathFor
from ProjectCustomMetadata import ProjectCustomMetadata
from Globals import package_home
import os, os.path

ADD_CONTENT_PERMISSION = 'Add portal content'
PROJECTNAME = "project name"

_types = {}

def registerType(type):
    _types[type.meta_type] = type
    
def listTypes():
    return _types.values()

def initialize(context):
    ##Import Types here to register them
    import ProjectCustomTypes
    
    homedir = package_home(globals())
    edit_dir = view_dir = os.path.join(homedir, 'skins', 'project_views')
    script_dir = os.path.join(homedir, 'skins', 'project_scripts')
    
    content_types, constructors, ftis = process_types( listTypes(),
                                                       PROJECTNAME,
                                                       edit_dir=edit_dir,
                                                       view_dir=view_dir,
                                                       script_dir=script_dir,
                                                       metadatatype=ProjectCustomMetadata)

    
    utils.ContentInit(
        'PROJECTNAME Content',
        content_types      = content_types,
        permission         = ADD_CONTENT_PERMISSION,
        extra_constructors = constructors,
        fti                = ftis,
        ).initialize(context)



Then you might want an external method in Extensions that installs the types in a site:

from Products.CMFTypes.Extensions.utils import installTypes
from Products.PROJECTNAME import listTypes
from Products.PROJECTNAME.ProjectCustomMetadata import ProjectCustomMetadata
from StringIO import StringIO

def install(self):
    out = StringIO()
    installTypes(self, out, listTypes(), 'PROJECTNAME', ProjectCustomMetadata)
    return out.getvalue()
