Back to index

plone3  3.1.7
cmfns.py
Go to the documentation of this file.
00001 ##################################################################
00002 # Marshall: A framework for pluggable marshalling policies
00003 # Copyright (C) 2004 ObjectRealms, LLC
00004 #
00005 # This program is free software; you can redistribute it and/or modify
00006 # it under the terms of the GNU General Public License as published by
00007 # the Free Software Foundation; either version 2 of the License, or
00008 # (at your option) any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 # GNU General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU General Public License
00016 # along with this program; if not, write to the Free Software
00017 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 ##################################################################
00019 
00020 """
00021 Serialize CMF Framework Attributes - Type, Workflow,
00022 Local Roles.
00023 
00024 Created: 10/11/2004
00025 Author: Kapil Thangavelu <k_vertigo@objectrealms.net>
00026 $Id: $
00027 """
00028 
00029 from DateTime import DateTime
00030 from Products.CMFCore.utils import getToolByName
00031 from Products.Marshall import config
00032 from Products.Marshall.handlers.atxml import XmlNamespace
00033 from Products.Marshall.handlers.atxml import SchemaAttribute
00034 from Products.Marshall import utils
00035 
00036 TypeRNGSchemaFragment = '''
00037   <define name="TypeInfo"
00038           xmlns:cmf="http://cmf.zope.org/namespaces/default"
00039           datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
00040           xmlns="http://relaxng.org/ns/structure/1.0">
00041            <element name="cmf:type">
00042              <text />
00043            </element>
00044           </zeroOrMore>
00045   </define>
00046 '''
00047 
00048 class TypeAttribute(SchemaAttribute):
00049 
00050     def getAttributeNames(self):
00051         return (self.name,)
00052 
00053     def get(self, instance):
00054         return instance.getPortalTypeName()
00055 
00056     def deserialize(self, instance, ns_data):
00057         value = ns_data.get(self.name)
00058         instance._setPortalTypeName( value )
00059 
00060     def serialize(self, dom, parent_node, instance):
00061         value = self.get(instance)
00062         elname = "%s:%s"%(self.namespace.prefix, self.name)
00063         node = dom.createElementNS( CMF.xmlns, elname )
00064         value_node = dom.createTextNode( str( value ) )
00065         node.appendChild( value_node )
00066         node.normalize()
00067         parent_node.appendChild( node )
00068 
00069 
00070 SecurityRNGSchemaFragment = '''
00071   <define name="SecurityInfo"
00072           xmlns:zs="http://xml.zope.org/ns/local_roles"
00073           datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
00074           xmlns="http://relaxng.org/ns/structure/1.0">
00075           <zeroOrMore>
00076             <element name="zs:security">
00077               <zeroOrMore>
00078                <element name="zs:local_role">
00079                  <attribute name="user_id" />
00080                  <attribute name="role" />
00081                </element>
00082               </zeroOrMore>
00083             </element>
00084           </zeroOrMore>
00085   </define>
00086 '''
00087 
00088 class LocalRolesAttribute(SchemaAttribute):
00089     
00090     """
00091     serializes local roles into the form of
00092 
00093     <security>
00094 
00095       <local_role user_id="matz" role"bar" />
00096       <local_role user_id="john" role"manager" />
00097       <local_role user_id="john" role"owner" />
00098       <local_role user_id="mike" role"manager" />
00099 
00100     </security>
00101 
00102     """
00103 
00104     def getAttributeNames(self):
00105         return (self.name,)
00106     
00107     def get(self, instance):
00108         return getattr( instance, '__ac_local_roles__', {})
00109 
00110     def deserialize(self, instance, ns_data):
00111         values = ns_data.get(self.name)
00112         if not values:
00113             return
00114         for user_id, role in values:
00115             instance.manage_addLocalRoles( user_id, [role])
00116 
00117     def serialize(self, dom, parent_node, instance):
00118         values = self.get(instance)
00119 
00120         if not values:
00121             return
00122 
00123         elname = "%s:%s"%(self.namespace.prefix, "security")
00124         node = dom.createElementNS( CMF.xmlns,  elname )
00125 
00126         for user_id, roles in values.items():
00127             for role in roles:
00128                 elname = "%s:%s"%(self.namespace.prefix, self.name )
00129                 lr_node = dom.createElementNS( CMF.xmlns, elname )
00130                 user_attr = dom.createAttribute("user_id")
00131                 user_attr.value = user_id
00132                 lr_node.setAttributeNode( user_attr )
00133                 
00134                 role_attr = dom.createAttribute( "role")
00135                 role_attr.value = role
00136                 lr_node.setAttributeNode( role_attr )
00137                 node.appendChild( lr_node )
00138         
00139         parent_node.appendChild( node )
00140 
00141     def processXml(self, context, ctx_node):
00142         value = context.reader.Value()
00143         if value is None:
00144             return
00145         value = value.strip()
00146         if not value:
00147             return
00148 
00149         data = context.getDataFor( self.namespace.xmlns )
00150         values = data.setdefault(self.name, [])
00151         user_id = role = None
00152         
00153         while context.reader.MoveToNextAttribute():            
00154             if context.reader.LocalName() == 'user_id':
00155                 user_id = reader.Value()
00156             elif context.reader.LocalName() == 'role':
00157                 role = context.reader.Value()
00158 
00159         assert user_id, role
00160         values.append( (user_id, role ) )
00161             
00162 
00163 WorkflowRNGSchemaFragment = '''
00164   <define name="WorkflowHistory"
00165           xmlns:cmf="http://cmf.zope.org/namespaces/default/"
00166           datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
00167           xmlns="http://relaxng.org/ns/structure/1.0">
00168 
00169       <element name="workflow_history">
00170        <oneOrMore>
00171         <element name="workflow">
00172           <attribute name="id" />
00173            <element name="history">
00174             <oneOrMore>
00175              <element name="wf_var">
00176               <attribute name="id" />
00177               <attribute name="type" />
00178               <attribute name="value" />
00179              </element>
00180             </oneOrMore>
00181            </element>
00182         </element>
00183        </oneOrMore>
00184       </element>
00185 
00186   </define>
00187 '''
00188 
00189 class WorkflowAttribute(SchemaAttribute):
00190     """
00191     serializes workflow state into the form below
00192 
00193     it will try to deal as best as possible with arbitrary
00194     python values, if it can't deal it will error out.
00195 
00196     <workflow_history>
00197       <workflow id="">
00198        <!-- in chronological order -->
00199        <history>
00200          <wf_var id="" type="" value="" />
00201          <wf_var id="" type="" value="" />
00202        </history>
00203       </workflow>
00204     </workflow_history>
00205     """
00206 
00207     def getAttributeNames(self):
00208         return ("workflow", "var", "history", 'workflow_history')
00209         
00210     def get(self, instance):
00211         return getattr(instance, 'workflow_history', None)
00212 
00213     def deserialize(self, instance, ns_data):
00214         wf_records = ns_data.get(self.name, {})
00215         wf_tool = getToolByName(instance, 'portal_workflow')
00216 
00217         for wf_id in wf_records:
00218             #history = list( instance.workflow_history.setdefault( wf_id, () ) )
00219             history=[]
00220             for record in wf_records[ wf_id ]:
00221                 history.append( record )
00222             instance.workflow_history[ wf_id ] = tuple( history )
00223 
00224     def serialize(self, dom, parent_node, instance):
00225         history = self.get( instance )
00226 
00227         if history is None:
00228             return
00229         
00230         elname = "%s:workflow_history"%(self.namespace.prefix)
00231         node = dom.createElementNS( self.namespace.xmlns, elname )
00232         keys = history.keys()
00233         for wf_id in keys:
00234             wf_node = self.serializeWorkflow( dom, wf_id, history )
00235             node.appendChild( wf_node )
00236         parent_node.appendChild( node )
00237         
00238 
00239     def serializeWorkflow(self, dom, wf_id, wf_hist):
00240 
00241         prefix = self.namespace.prefix
00242         xmlns  = self.namespace.xmlns
00243         
00244         elname = "%s:workflow"%prefix
00245         node = dom.createElementNS(xmlns, elname)
00246 
00247         wfid_attr = dom.createAttribute( "id" )
00248         wfid_attr.value = wf_id
00249         
00250         node.setAttributeNode( wfid_attr )
00251         
00252         for history in wf_hist[wf_id]:
00253             elname = "%s:%s"%(prefix, "history")
00254             history_node = dom.createElementNS(xmlns, elname)
00255             items = history.items()
00256             items.sort(lambda a,b: cmp(a[0],b[0]))
00257             
00258             for k,v in items:
00259                 elname = "%s:%s"%(prefix, "var" )
00260                 var_node = dom.createElementNS(xmlns, elname)
00261                 # Attributes normally do not have a namespace
00262                 value_attr = dom.createAttribute("value")
00263                 type_attr = dom.createAttribute("type")
00264                 id_attr = dom.createAttribute("id")
00265 
00266                 value, vtype = marshall_value( v )
00267 
00268                 id_attr.value = str(k)
00269                 type_attr.value = vtype
00270                 value_attr.value = value
00271 
00272                 var_node.setAttributeNode(id_attr)
00273                 var_node.setAttributeNode(type_attr)
00274                 var_node.setAttributeNode(value_attr)
00275                 
00276                 history_node.appendChild( var_node )
00277                 
00278             node.appendChild( history_node )
00279 
00280         return node
00281                 
00282     def processXml(self, context, node):
00283 
00284         tag,namespace=utils.fixtag(node.tag,context.ns_map)
00285         data = context.getDataFor(self.namespace.xmlns)
00286         nsprefix=node.tag[:node.tag.find('}')+1]
00287 
00288         #iworkflow
00289         wf_node=node.find(nsprefix+'workflow')
00290         wf_id = wf_node.attrib.get(nsprefix+'id') or wf_node.attrib.get('id') #be tolerant with namespace sloppyness;)
00291         assert wf_id
00292             
00293             
00294         wf_data = data.setdefault(self.name, {})
00295         wf_data.setdefault( wf_id, [] )
00296         wf_pstate = data.setdefault('_wf_pstate', wf_id)
00297             
00298         #history
00299         hist_nodes=wf_node.findall(nsprefix+'history')
00300         wf_pstate = data['_wf_pstate']
00301         for hist_node in hist_nodes:
00302             record = {}
00303             data[self.name][wf_pstate].append( record )
00304     
00305             #var
00306             var_nodes=hist_node.findall(nsprefix+'var')
00307             vid = vtype = value = None
00308             
00309             for var_node in var_nodes:
00310                 vid=var_node.attrib.get(nsprefix+'id') or var_node.attrib.get('id')
00311                 vtype=var_node.attrib.get(nsprefix+'type',None) or var_node.attrib.get('type')
00312                 value=var_node.attrib.get(nsprefix+'value',None) or var_node.attrib.get('value') or ''
00313                 
00314                 assert vid and vtype and not value is None
00315     
00316                 value = demarshall_value( value, vtype )
00317                 wf_pstate = data['_wf_pstate']            
00318                 data[self.name][wf_pstate][-1][vid]=value
00319 
00320         return True
00321     
00322 
00323 def marshall_value( value ):
00324 
00325     if isinstance(value, str):
00326         return value, 'str'
00327     elif isinstance(value, int):
00328         return str(value), 'int'
00329     elif isinstance(value, float):
00330         return str(value), 'float'
00331     elif isinstance(value, DateTime):
00332         return value.ISO(), 'date'
00333     elif isinstance(value, type(None)):
00334         return 'None', 'None'
00335     else:
00336         raise SyntaxError("Unknown value type %r"%value)
00337     
00338 def demarshall_value( value, type ):
00339 
00340     if type == 'str':
00341         return value
00342     elif type == 'int':
00343         return int(value)
00344     elif type == 'float':
00345         return float(value)
00346     elif type == 'date':
00347         if value.strip()=='':
00348             return None
00349         else:
00350             return DateTime(value)
00351     elif type == 'None':
00352         return None
00353     else:
00354         raise SyntaxError("Unknown Type %r"%type)
00355     
00356 class CMF(XmlNamespace):
00357     
00358     xmlns = config.CMF_NS
00359     prefix = 'cmf'
00360     attributes = (
00361         TypeAttribute('type'),
00362         WorkflowAttribute('workflow_history '),
00363         LocalRolesAttribute('local_role')
00364         )
00365 
00366     def getAttributeByName(self, name):
00367 
00368         for attr in self.attributes:
00369             if name in attr.getAttributeNames():
00370                 return attr
00371             
00372     def processXml(self, context, node):
00373         return XmlNamespace.processXml(self,context,node)
00374 
00375     def getSchemaInfo( self ):
00376 
00377         return [
00378             ( "TypeInfo", "optional", TypeRNGSchemaFragment ),
00379             ( "SecurityInfo", "optional", SecurityRNGSchemaFragment ),
00380             ( "WorkflowInfo", "optional", WorkflowRNGSchemaFragment )
00381               ]