def getVisibleTemplates(self, obj): '''Returns, among self.template, the template(s) that can be shown''' res = [] if not self.showTemplate: # Show them all in the formats specified in self.formats for template in self.template: res.append( Object(template=template, formats=self.formats, freezeFormats=self.getFreezeFormats(obj, template))) else: for template in self.template: formats = self.showTemplate(obj, template) if not formats: continue elif isinstance(formats, bool): formats = self.formats elif isinstance(formats, basestring): formats = (formats, ) res.append( Object(template=template, formats=formats, freezeFormats=self.getFreezeFormats(obj, template))) # Compute the already frozen documents, and update the available formats # accordingly when self.showFrozenOnly is True. for info in res: frozenFormats = [] for fmt in info.formats: if self.isFrozen(obj, info.template, fmt): frozenFormats.append(fmt) info.frozenFormats = frozenFormats # Replace formats with frozenFormats when relevant if frozenFormats and self.showFrozenOnly: info.formats = frozenFormats return res
def getRequestValue(self, obj, requestName=None): '''Concatenates the list from distinct form elements in the request''' request = obj.REQUEST name = requestName or self.name # A List may be into another List (?) prefix = name + '*-row-*' # Allows to detect a row of data for this List res = {} isDict = True # We manage both List and Dict for key in request.keys(): if not key.startswith(prefix): continue # I have found a row: get its index row = Object() rowId = key.split('*')[-1] if rowId == '-1': continue # Ignore the template row for subName, subField in self.fields: keyName = '%s*%s*%s' % (name, subName, rowId) if keyName + subField.getRequestSuffix() in request: v = subField.getRequestValue(obj, requestName=keyName) setattr(row, subName, v) if rowId.isdigit(): rowId = int(rowId) isDict = False res[rowId] = row # Produce a sorted list (List only) if not isDict: keys = res.keys() keys.sort() res = [res[key] for key in keys] # I store in the request this computed value. This way, when individual # subFields will need to get their value, they will take it from here, # instead of taking it from the specific request key. Indeed, specific # request keys contain row indexes that may be wrong after row deletions # by the user. if res: request.set(name, res) return res
def __init__(self, config, mode): # "config" is the main Appy config self.config = config # "mode" can be "fg" (foreground, debug mode) or "bg" (background) self.mode = mode # Initialise the loggers cfg = config.log self.loggers = Object(site=cfg.getLogger('site'), app=cfg.getLogger('app', mode == 'fg')) try: # Load the application model self.model = config.model.get() # Initialise the HTTP server cfg = config.server HTTPServer.__init__(self, (cfg.address, cfg.port), Handler) # Initialise the database self.db = config.db.getDatabase(self.loggers.app) except Model.Error as err: self.loggers.app.error(err) logging.shutdown() sys.exit(1) except Exception as e: self.logTraceback() logging.shutdown() sys.exit(1) # The current user login self.user = '******' # The server is ready self.loggers.app.info(self.READY % (cfg.address, cfg.port, os.getpid()))
def getCustomContext(self, obj, rq): '''Before calling pod to compute a result, if specific elements must be added to the context, compute it here. This request-dependent method is not called when computing a pod field for freezing it into the database.''' res = {} # Get potential custom params from the request. Custom params must be # coded as a string containing a valid Python dict. customParams = rq.get('customParams') if customParams: paramsDict = eval(customParams) res.update(paramsDict) # Compute the selected linked objects if self.getChecked is specified # and if the user can read this Ref field. if self.getChecked and \ obj.allows(obj.getField(self.getChecked).readPermission): # Get the UIDs specified in the request reqUids = rq['checkedUids'] and rq['checkedUids'].split(',') or [] unchecked = rq['checkedSem'] == 'unchecked' objects = [] tool = obj.tool for uid in getattr(obj.o.aq_base, self.getChecked, ()): if unchecked: condition = uid not in reqUids else: condition = uid in reqUids if condition: tied = tool.getObject(uid) if tied.allows('read'): objects.append(tied) res['_checked'] = Object() setattr(res['_checked'], self.getChecked, objects) return res
def getInfo(self, obj, layoutType): '''Gets information about this page, for p_obj, as an object.''' res = Object() for elem in Page.subElements: showable = self.isShowable(obj, layoutType, elem) setattr(res, 'show%s' % elem.capitalize(), showable) return res
def addPageLinks(self, field, obj): '''If p_field is a navigable Ref, we must add, within self.pagesInfo, objects linked to p_obj through this Ref as links.''' if field.page.name in self.hiddenPages: return infos = [] for ztied in field.getValue(obj, appy=False): infos.append(Object(title=ztied.title, url=ztied.absolute_url())) self.pagesInfo[field.page.name].links = infos
def convertDict(klass, d): '''Returns a appy.Object instance representing dict p_d''' res = Object() for name, value in d.items(): # Ensure "name" will be a valid attribute name for a Python object n = string.normalize(name, 'alphanum_') setattr(res, n, klass.convertValue(value)) return res
def encode(self): # Do nothing if we have a SOAP message already if isinstance(self.data, basestring): return self.data # self.data is here a Python object. Wrap it in a SOAP Body. soap = Object(Body=self.data) # Marshall it marshaller = XmlMarshaller(rootTag='Envelope', namespaces=self.ns, namespacedTags=self.namespacedTags) return marshaller.marshall(soap)
def initialiseLoop(self, context, elems): '''Initialises information about the loop, before entering into it. It is possible that this loop overrides an outer loop whose iterator has the same name. This method returns a tuple (loop, outerOverriddenLoop).''' # The "loop" object, made available in the POD context, contains info # about all currently walked loops. For every walked loop, a specific # object, le'ts name it curLoop, accessible at # getattr(loop, self.iters[0]), stores info about its status: # * curLoop.length gives the total number of walked elements within # the loop # * curLoop.nb gives the index (starting at 0) if the currently # walked element. # * curLoop.first is True if the currently walked element is the # first one. # * curLoop.last is True if the currently walked element is the # last one. # * curLoop.odd is True if the currently walked element is odd # * curLoop.even is True if the currently walked element is even # For example, if you have a "for" statement like this: # for elem in myListOfElements # Within the part of the ODT document impacted by this statement, you # may access to: # * loop.elem.length to know the total length of myListOfElements # * loop.elem.nb to know the index of the current elem within # myListOfElements. if 'loop' not in context: context['loop'] = Object() try: total = len(elems) except Exception: total = 0 curLoop = Object(length=total) # Does this loop override an outer loop with homonym iterator ? outerLoop = None iter = self.iters[0] if hasattr(context['loop'], iter): outerLoop = getattr(context['loop'], iter) # Put this loop in the global object "loop" setattr(context['loop'], iter, curLoop) return curLoop, outerLoop
def addPage(self, field, obj, layoutType): '''Adds page-related information in the phase.''' # If the page is already there, we have nothing more to do. if (field.page.name in self.pages) or \ (field.page.name in self.hiddenPages): return # Add the page only if it must be shown. showOnView = field.page.isShowable(obj, 'view') showOnEdit = field.page.isShowable(obj, 'edit') if showOnView or showOnEdit: # The page must be added self.pages.append(field.page.name) # Create the dict about page information and add it in self.pageInfo pageInfo = Object(page=field.page, showOnView=showOnView, showOnEdit=showOnEdit, links=None) pageInfo.update(field.page.getInfo(obj, layoutType)) self.pagesInfo[field.page.name] = pageInfo else: self.hiddenPages.append(field.page.name)
class Collapsible: '''Represents a chunk of HTML code that can be collapsed/expanded via clickable icons.''' # Various sets of icons can be used. Each one has a CSS class in appy.css iconSets = { 'expandCollapse': Object(expand='expand', collapse='collapse'), 'showHide': Object(expand='show', collapse='hide'), 'showHideInv': Object(expand='hide', collapse='show') } # Icon allowing to collapse/expand a chunk of HTML px = Px(''' <img var="coll=collapse; icons=coll.icons" id=":'%s_img' % coll.id" align=":coll.align" class=":coll.css" onclick=":'toggleCookie(%s,%s,%s,%s,%s)' % (q(coll.id), \ q(coll.display), q(coll.default), \ q(icons.expand), q(icons.collapse))" src=":coll.expanded and url(icons.collapse) or url(icons.expand)"/>''') def __init__(self, id, request, default='collapsed', display='block', icons='expandCollapse', align='left'): '''p_display is the value of style attribute "display" for the XHTML element when it must be displayed. By default it is "block"; for a table it must be "table", etc.''' self.id = id # The ID of the collapsible HTML element self.request = request # The request object self.default = default self.display = display self.align = align # Must the element be collapsed or expanded ? self.expanded = request.get(id, default) == 'expanded' self.style = 'display:%s' % (self.expanded and self.display or 'none') # The name of the CSS class depends on the set of applied icons self.css = icons self.icons = self.iconSets[icons]
def createTotals(self, isEdit): '''When rendering the List field, if total rows are defined, create a Total instance for every sub-field for which a total must be computed.''' if isEdit or not self.totalRows: return res = {} # Keyed by Totals.name for totals in self.totalRows: subTotals = Object() for name in totals.subFields: totalObj = Total(name, self.getField(name), totals.initValue) setattr(subTotals, name, totalObj) res[totals.id] = subTotals return res
def getStorableRowValue(self, obj, requestValue): '''Gets a ready-to-store Object instance representing a single row, from p_requestValue.''' res = Object() for name, field in self.fields: if not hasattr(requestValue, name): continue subValue = getattr(requestValue, name) try: setattr(res, name, field.getStorableValue(obj, subValue)) except ValueError: # The value for this field for this specific row is incorrect. # It can happen in the process of validating the whole List # field (a call to m_getStorableValue occurs at this time). We # don't care about it, because later on we will have sub-field # specific validation that will also detect the error and will # prevent storing the wrong value in the database. setattr(res, name, subValue) return res
def process(self, obj): '''Processes a response from Ogone.''' # Call the response method defined in this Ogone field. if not self.ogoneResponseOk(obj): obj.log('Ogone response SHA failed. REQUEST: %s' % \ str(obj.REQUEST.form)) raise Exception('Failure, possible fraud detection, an ' \ 'administrator has been contacted.') # Create a nice object from the form. response = Object() for k, v in obj.REQUEST.form.items(): setattr(response, k, v) # Call the field method that handles the response received from Ogone. url = self.responseMethod(obj.appy(), response) # Redirect the user to the correct page. If the field method returns # some URL, use it. Else, use the view page of p_obj. if not url: url = obj.absolute_url() obj.goto(url)
def getValue(self, obj, name=None, template=None, format=None, result=None, queryData=None, customContext=None, noSecurity=False, executeAction=True): '''For a pod field, getting its value means computing a pod document or returning a frozen one. A pod field differs from other field types because there can be several ways to produce the field value (ie: self.template can hold various templates; output file format can be odt, pdf,.... We get those precisions about the way to produce the file, either from params, or from default values. * p_template is the specific template, among self.template, that must be used as base for generating the document; * p_format is the output format of the resulting document; * p_result, if given, must be the absolute path of the document that will be computed by pod. If not given, pod will produce a doc in the OS temp folder; * if the pod document is related to a query, the query parameters needed to re-trigger the query are given in p_queryData; * dict p_customContext may be specified and will override any other value available in the context, including values from the field-specific context. ''' obj = obj.appy() template = template or self.template[0] format = format or 'odt' # Security check if not noSecurity and not queryData: if self.showTemplate and not self.showTemplate(obj, template): raise Exception(UNAUTHORIZED) # Return the possibly frozen document (not applicable for query-related # pods). if not queryData: frozen = self.isFrozen(obj, template, format) if frozen: fileName = self.getDownloadName(obj, template, format, False) return FileInfo(frozen, inDb=False, uploadName=fileName) # We must call pod to compute a pod document from "template" tool = obj.tool ztool = tool.o diskFolder = tool.getDiskFolder() # Get the path to the pod template templatePath = self.getTemplatePath(diskFolder, template) # Get or compute the specific POD context specificContext = None if callable(self.context): specificContext = self.callMethod(obj, self.context) else: specificContext = self.context # Compute the name of the result file if not result: result = '%s/%s_%f.%s' % (getOsTempFolder(), obj.id, time.time(), format) # Define parameters to give to the appy.pod renderer podContext = { 'tool': tool, 'user': obj.user, 'self': obj, 'field': self, 'now': ztool.getProductConfig().DateTime(), '_': obj.translate, 'projectFolder': diskFolder, 'template': template, 'request': tool.request } # If the pod document is related to a query, re-trigger it and put the # result in the pod context. if queryData: # Retrieve query params cmd = ', '.join(Pod.queryParams) cmd += " = queryData.split(';')" exec(cmd) # (re-)execute the query, but without any limit on the number of # results; return Appy objects. objs = ztool.executeQuery(obj.o.portal_type, searchName=search, sortBy=sortKey, sortOrder=sortOrder, filters=sutils.getDictFrom(filters), maxResults='NO_LIMIT') podContext['objects'] = [o.appy() for o in objs.objects] podContext['queryData'] = queryData.split(';') # Add the field-specific and custom contexts if present if specificContext: podContext.update(specificContext) if customContext: podContext.update(customContext) # Variable "_checked" can be expected by a template but absent (ie, # when generating frozen documents). if '_checked' not in podContext: podContext['_checked'] = Object() # Define a potential global styles mapping if callable(self.stylesMapping): stylesMapping = self.callMethod(obj, self.stylesMapping) else: stylesMapping = self.stylesMapping # Execute the "before" action when relevant if executeAction and self.beforeAction: self.beforeAction(obj, template, podContext, format) # Get the optional script to give to the renderer script = self.script if callable(script): script = script(obj, template, podContext) rendererParams = { 'template': templatePath, 'context': podContext, 'result': result, 'stylesMapping': stylesMapping, 'imageResolver': ztool.getApp(), 'overwriteExisting': True, 'forceOoCall': self.forceOoCall, 'raiseOnError': self.raiseOnError, 'script': script } cfg = ztool.getProductConfig(True) if cfg.unoEnabledPython: rendererParams['pythonWithUnoPath'] = cfg.unoEnabledPython if cfg.libreOfficePort: rendererParams['ooPort'] = cfg.libreOfficePort # Launch the renderer try: renderer = Renderer(**rendererParams) renderer.run() except PodError as pe: if not os.path.exists(result): # In some (most?) cases, when OO returns an error, the result is # nevertheless generated. obj.log(str(pe).strip(), type='error') return POD_ERROR # Give a friendly name for this file fileName = self.getDownloadName(obj, template, format, queryData) # Execute the tied action when relevant if executeAction and self.action: self.action(obj, template, podContext, format) # Get a FileInfo instance to manipulate the file on the filesystem return FileInfo(result, inDb=False, uploadName=fileName)