from roundup.cgi.actions import Action from roundup.cgi import templating from roundup import hyperdb import csv import re import codecs import logging LOG = logging.getLogger(__name__) class ExportCSVNamesAction(Action): name = 'export' permissionType = 'View' list_sep = ';' def handle(self): ''' Export the specified search query as CSV. ''' # figure the request global LOG LOG = self.db.get_logger() request = templating.HTMLRequest(self.client) filterspec = request.filterspec sort = request.sort group = request.group columns = request.columns klass = self.db.getclass(request.classname) # full-text search if request.search_text: matches = self.db.indexer.search( re.findall(r'\b\w{2,25}\b', request.search_text), klass) else: matches = None header = self.client.additional_headers header['Content-Type'] = 'text/csv; charset=%s' % self.client.charset # some browsers will honor the filename here... header['Content-Disposition'] = 'inline; filename=query.csv' self.client.header() if self.client.env['REQUEST_METHOD'] == 'HEAD': # all done, return a dummy string return 'dummy' wfile = self.client.request.wfile if self.client.charset != self.client.STORAGE_CHARSET: wfile = codecs.EncodedFile(wfile, self.client.STORAGE_CHARSET, self.client.charset, 'replace') writer = csv.writer(wfile) # handle different types of columns. def repr_no_right(cls, col): """User doesn't have the right to see the value of col.""" def fct(arg): return "[hidden]" return fct def repr_link(cls, col): """Generate a function which returns the string representation of a link depending on `cls` and `col`.""" def fct(arg): if arg == None: return "" else: return str(cls.get(arg, col)) return fct def repr_list(cls, col): def fct(arg): if arg == None: return "" elif type(arg) is list: seq = [str(cls.get(val, col)) for val in arg] return self.list_sep.join(seq) return fct def repr_date(): def fct(arg): if arg == None: return "" else: if (arg.local(self.db.getUserTimezone()).pretty('%H:%M') == '00:00'): fmt = '%Y-%m-%d' else: fmt = '%Y-%m-%d %H:%M' return arg.local(self.db.getUserTimezone()).pretty(fmt) return fct def repr_val(): def fct(arg): if arg == None: return "" else: return str(arg) return fct props = klass.getprops() LOG.debug("Determine translation map.") ncols = [] represent = {} for col in columns: LOG.debug(" %r" % col) ncols.append(col) represent[col] = repr_val() if isinstance(props[col], hyperdb.Multilink): cname = props[col].classname cclass = self.db.getclass(cname) represent[col] = repr_list(cclass, 'name') if not self.hasPermission(self.permissionType, classname=cname): represent[col] = repr_no_right(cclass, 'name') else: if cclass.getprops().has_key('name'): represent[col] = repr_list(cclass, 'name') elif cname == 'user': represent[col] = repr_list(cclass, 'realname') if isinstance(props[col], hyperdb.Link): cname = props[col].classname cclass = self.db.getclass(cname) if not self.hasPermission(self.permissionType, classname=cname): represent[col] = repr_no_right(cclass, 'name') else: if cclass.getprops().has_key('name'): represent[col] = repr_link(cclass, 'name') elif cname == 'user': represent[col] = repr_link(cclass, 'realname') if isinstance(props[col], hyperdb.Date): represent[col] = repr_date() columns = ncols # generate the CSV output self.client._socket_op(writer.writerow, columns) # and search for itemid in klass.filter(matches, filterspec, sort, group): row = [] for col in columns: # check permission to view this property on this item # TODO: Permission filter doesn't work for the 'user' class if not self.hasPermission(self.permissionType, itemid=itemid, classname=request.classname, property=col): represent[col] = repr_no_right(request.classname, col) row.append(represent[col](klass.get(itemid, col))) self.client._socket_op(writer.writerow, row) return '\n' def init(instance): instance.registerAction('export_csv_names', ExportCSVNamesAction) # vim: set filetype=python sts=4 sw=4 et si