2 # Copyright (C) 2007 Stefan Seefeld
4 # For license terms see the file COPYING.txt.
7 from roundup
import hyperdb
8 from roundup
.cgi
.exceptions
import *
9 from roundup
.exceptions
import UsageError
10 from roundup
.date
import Date
, Range
, Interval
11 from roundup
import actions
12 from SimpleXMLRPCServer
import *
13 from xmlrpclib
import Binary
16 """Translate value to becomes valid for XMLRPC transmission."""
18 if isinstance(value
, (Date
, Range
, Interval
)):
20 elif type(value
) is list:
21 return [translate(v
) for v
in value
]
22 elif type(value
) is tuple:
23 return tuple([translate(v
) for v
in value
])
24 elif type(value
) is dict:
25 return dict([[translate(k
), translate(value
[k
])] for k
in value
])
30 def props_from_args(db
, cl
, args
, itemid
=None):
31 """Construct a list of properties from the given arguments,
32 and return them after validation."""
36 if isinstance(arg
, Binary
):
39 key
, value
= arg
.split('=', 1)
41 raise UsageError
, 'argument "%s" not propname=value'%arg
42 if isinstance(key
, unicode):
44 key
= key
.encode ('ascii')
45 except UnicodeEncodeError:
46 raise UsageError
, 'argument %r is no valid ascii keyword'%key
47 if isinstance(value
, unicode):
48 value
= value
.encode('utf-8')
51 props
[key
] = hyperdb
.rawToHyperdb(db
, cl
, itemid
,
53 except hyperdb
.HyperdbValueError
, message
:
54 raise UsageError
, message
60 class RoundupInstance
:
61 """The RoundupInstance provides the interface accessible through
62 the Python XMLRPC mapping."""
64 def __init__(self
, db
, actions
, translator
):
67 self
.actions
= actions
68 self
.translator
= translator
72 for c
in self
.db
.classes
:
73 cls
= self
.db
.classes
[c
]
74 props
= [(n
,repr(v
)) for n
,v
in cls
.properties
.items()]
78 def list(self
, classname
, propname
=None):
79 cl
= self
.db
.getclass(classname
)
81 propname
= cl
.labelprop()
82 result
= [cl
.get(itemid
, propname
)
83 for itemid
in cl
.list()
84 if self
.db
.security
.hasPermission('View', self
.db
.getuid(),
85 classname
, propname
, itemid
)
89 def filter(self
, classname
, search_matches
, filterspec
,
91 cl
= self
.db
.getclass(classname
)
92 result
= cl
.filter(search_matches
, filterspec
, sort
=sort
, group
=group
)
95 def display(self
, designator
, *properties
):
96 classname
, itemid
= hyperdb
.splitDesignator(designator
)
97 cl
= self
.db
.getclass(classname
)
98 props
= properties
and list(properties
) or cl
.properties
.keys()
101 if not self
.db
.security
.hasPermission('View', self
.db
.getuid(),
102 classname
, p
, itemid
):
103 raise Unauthorised('Permission to view %s of %s denied'%
105 result
= [(prop
, cl
.get(itemid
, prop
)) for prop
in props
]
108 def create(self
, classname
, *args
):
110 if not self
.db
.security
.hasPermission('Create', self
.db
.getuid(), classname
):
111 raise Unauthorised('Permission to create %s denied'%classname
)
113 cl
= self
.db
.getclass(classname
)
116 props
= props_from_args(self
.db
, cl
, args
)
118 # check for the key property
120 if key
and not props
.has_key(key
):
121 raise UsageError
, 'you must provide the "%s" property.'%key
124 if not self
.db
.security
.hasPermission('Create', self
.db
.getuid(),
125 classname
, property=key
):
126 raise Unauthorised('Permission to create %s.%s denied'%(classname
, key
))
128 # do the actual create
130 result
= cl
.create(**props
)
132 except (TypeError, IndexError, ValueError), message
:
133 raise UsageError
, message
136 def set(self
, designator
, *args
):
138 classname
, itemid
= hyperdb
.splitDesignator(designator
)
139 cl
= self
.db
.getclass(classname
)
140 props
= props_from_args(self
.db
, cl
, args
, itemid
) # convert types
141 for p
in props
.iterkeys():
142 if not self
.db
.security
.hasPermission('Edit', self
.db
.getuid(),
143 classname
, p
, itemid
):
144 raise Unauthorised('Permission to edit %s of %s denied'%
147 result
= cl
.set(itemid
, **props
)
149 except (TypeError, IndexError, ValueError), message
:
150 raise UsageError
, message
154 builtin_actions
= {'retire': actions
.Retire
}
156 def action(self
, name
, *args
):
157 """Execute a named action."""
159 if name
in self
.actions
:
160 action_type
= self
.actions
[name
]
161 elif name
in self
.builtin_actions
:
162 action_type
= self
.builtin_actions
[name
]
164 raise Exception('action "%s" is not supported %s' % (name
, ','.join(self
.actions
.keys())))
165 action
= action_type(self
.db
, self
.translator
)
166 return action
.execute(*args
)
169 class RoundupDispatcher(SimpleXMLRPCDispatcher
):
170 """RoundupDispatcher bridges from cgi.client to RoundupInstance.
171 It expects user authentication to be done."""
173 def __init__(self
, db
, actions
, translator
,
174 allow_none
=False, encoding
=None):
177 # python2.5 and beyond
178 SimpleXMLRPCDispatcher
.__init__(self
, allow_none
, encoding
)
181 SimpleXMLRPCDispatcher
.__init__(self
)
182 self
.register_instance(RoundupInstance(db
, actions
, translator
))
185 def dispatch(self
, input):
186 return self
._marshaled_dispatch(input)
188 def _dispatch(self
, method
, params
):
190 retn
= SimpleXMLRPCDispatcher
._dispatch(self
, method
, params
)
191 retn
= translate(retn
)