def resolve_value_for_column(self, column_id, item, default=None): value = item.get(column_id, None) if value: return value # Maybe a custom attr attr = self.columns[column_id].get("attr", None) if attr: return getFromString(item["obj"], attr, default) # Maybe the column id represents a stringified call return getFromString(item["obj"], column_id, default)
def resolve_replace_url_for_column(self, column_id, item): column = self.columns[column_id] replace_url = column.get("replace_url", None) if not replace_url: return None value = getFromString(item["obj"], replace_url) if not value: return None url = self.url_or_path_to_url(value) value = self.resolve_value_for_column(column_id, item) or value return get_link(url, value=value)
def _folderitems(self, full_objects=False): """WARNING: :full_objects: could create a big performance hit. """ # Setting up some attributes plone_layout = getMultiAdapter((self.context.aq_inner, self.request), name=u'plone_layout') plone_utils = getToolByName(self.context.aq_inner, 'plone_utils') portal_types = getToolByName(self.context.aq_inner, 'portal_types') if self.request.form.get('show_all', '').lower() == 'true' \ or self.show_all is True \ or self.pagesize == 0: show_all = True else: show_all = False # idx increases one unit each time an object is added to the 'items' # dictionary to be returned. Note that if the item is not rendered, # the idx will not increase. idx = 0 results = [] self.show_more = False brains = self._fetch_brains(self.limit_from) for obj in brains: # avoid creating unnecessary info for items outside the current # batch; only the path is needed for the "select all" case... # we only take allowed items into account if not show_all and idx >= self.pagesize: # Maximum number of items to be shown reached! self.show_more = True break # we don't know yet if it's a brain or an object path = hasattr(obj, 'getPath') and obj.getPath() or \ "/".join(obj.getPhysicalPath()) # This item must be rendered, we need the object instead of a brain obj = obj.getObject() if hasattr(obj, 'getObject') else obj # check if the item must be rendered or not (prevents from # doing it later in folderitems) and dealing with paging if not obj or not self.isItemAllowed(obj): continue uid = obj.UID() title = obj.Title() description = obj.Description() icon = plone_layout.getIcon(obj) url = obj.absolute_url() relative_url = obj.absolute_url(relative=True) fti = portal_types.get(obj.portal_type) if fti is not None: type_title_msgid = fti.Title() else: type_title_msgid = obj.portal_type url_href_title = '%s at %s: %s' % (t(type_title_msgid), path, to_utf8(description)) modified = self.ulocalized_time(obj.modified()), # element css classes type_class = 'contenttype-' + \ plone_utils.normalizeString(obj.portal_type) state_class = '' states = {} for w in self.workflow.getWorkflowsFor(obj): state = w._getWorkflowStateOf(obj).id states[w.state_var] = state state_class += "state-%s " % state results_dict = dict( obj=obj, id=obj.getId(), title=title, uid=uid, path=path, url=url, fti=fti, item_data=json.dumps([]), url_href_title=url_href_title, obj_type=obj.Type, size=obj.getObjSize, modified=modified, icon=icon.html_tag(), type_class=type_class, # a list of lookups for single-value-select fields choices={}, state_class=state_class, relative_url=relative_url, view_url=url, table_row_class="", category='None', # a list of names of fields that may be edited on this item allow_edit=[], # a list of names of fields that are compulsory (if editable) required=[], # a dict where the column name works as a key and the value is # the name of the field related with the column. It is used # when the name given to the column and the content field it # represents diverges. bika_listing_table_items.pt defines an # attribute for each item, this attribute is named 'field' and # the system fills it taking advantage of this dictionary or # the name of the column if it isn't defined in the dict. field={}, # "before", "after" and replace: dictionary (key is column ID) # A snippet of HTML which will be rendered # before/after/instead of the table cell content. before={}, # { before : "<a href=..>" } after={}, replace={}, ) rs = None wf_state_var = None workflows = self.workflow.getWorkflowsFor(obj) for wf in workflows: if wf.state_var: wf_state_var = wf.state_var break if wf_state_var is not None: rs = self.workflow.getInfoFor(obj, wf_state_var) st_title = self.workflow.getTitleForStateOnType( rs, obj.portal_type) st_title = t(_(st_title)) if rs: results_dict['review_state'] = rs for state_var, state in states.items(): if not st_title: st_title = self.workflow.getTitleForStateOnType( state, obj.portal_type) results_dict[state_var] = state results_dict['state_title'] = st_title results_dict['class'] = {} # As far as I am concerned, adapters for IFieldIcons are only used # for Analysis content types. Since AnalysesView is not using this # "classic" folderitems from bikalisting anymore, this logic has # been added in AnalysesView. Even though, this logic hasn't been # removed from here, cause this _folderitems function is marked as # deprecated, so it will be eventually removed alltogether. for name, adapter in getAdapters((obj, ), IFieldIcons): auid = obj.UID() if hasattr(obj, 'UID') and callable( obj.UID) else None if not auid: continue alerts = adapter() # logger.info(str(alerts)) if alerts and auid in alerts: if auid in self.field_icons: self.field_icons[auid].extend(alerts[auid]) else: self.field_icons[auid] = alerts[auid] # Search for values for all columns in obj for key in self.columns.keys(): # if the key is already in the results dict # then we don't replace it's value value = results_dict.get(key, '') if key not in results_dict: attrobj = getFromString(obj, key) value = attrobj if attrobj else value # Custom attribute? Inspect to set the value # for the current column dinamically vattr = self.columns[key].get('attr', None) if vattr: attrobj = getFromString(obj, vattr) value = attrobj if attrobj else value results_dict[key] = value # Replace with an url? replace_url = self.columns[key].get('replace_url', None) if replace_url: attrobj = getFromString(obj, replace_url) if attrobj: url = self.url_or_path_to_url(attrobj) results_dict['replace'][key] = \ '<a href="%s">%s</a>' % (url, value) # The item basics filled. Delegate additional actions to folderitem # service. folderitem service is frequently overriden by child # objects item = self.folderitem(obj, results_dict, idx) # Call folder_item from subscriber adapters for subscriber in self.get_listing_view_adapters(): subscriber.folder_item(obj, item, idx) if item: results.append(item) idx += 1 return results
def folderitems(self, full_objects=False, classic=True): """This function returns an array of dictionaries where each dictionary contains the columns data to render the list. No object is needed by default. We should be able to get all the listing columns taking advantage of the catalog's metadata, so that the listing will be much more faster. If a very specific info has to be retrieve from the objects, we can define full_objects as True but performance can be lowered. :full_objects: a boolean, if True, each dictionary will contain an item with the object itself. item.get('obj') will return a object. Only works with the 'classic' way. WARNING: :full_objects: could create a big performance hit! :classic: if True, the old way folderitems works will be executed. This function is mainly used to maintain the integrity with the old version. """ # Getting a security manager instance for the current request self.security_manager = getSecurityManager() self.workflow = getToolByName(self.context, 'portal_workflow') if classic: return self._folderitems(full_objects) # idx increases one unit each time an object is added to the 'items' # dictionary to be returned. Note that if the item is not rendered, # the idx will not increase. idx = 0 results = [] self.show_more = False brains = self._fetch_brains(self.limit_from) for obj in brains: # avoid creating unnecessary info for items outside the current # batch; only the path is needed for the "select all" case... # we only take allowed items into account if idx >= self.pagesize: # Maximum number of items to be shown reached! self.show_more = True break # check if the item must be rendered or not (prevents from # doing it later in folderitems) and dealing with paging if not obj or not self.isItemAllowed(obj): continue # Get the css for this row in accordance with the obj's state states = obj.getObjectWorkflowStates if not states: states = {} state_class = ['state-{0}'.format(v) for v in states.values()] state_class = ' '.join(state_class) # Building the dictionary with basic items results_dict = dict( # To colour the list items by state state_class=state_class, # a list of names of fields that may be edited on this item allow_edit=[], # a dict where the column name works as a key and the value is # the name of the field related with the column. It is used # when the name given to the column and the content field it # represents diverges. bika_listing_table_items.pt defines an # attribute for each item, this attribute is named 'field' and # the system fills it taking advantage of this dictionary or # the name of the column if it isn't defined in the dict. field={}, # "before", "after" and replace: dictionary (key is column ID) # A snippet of HTML which will be rendered # before/after/instead of the table cell content. before={}, # { before : "<a href=..>" } after={}, replace={}, choices={}, ) # update with the base item info results_dict.update(self.get_item_info(obj)) # Set states and state titles ptype = obj.portal_type workflow = api.get_tool('portal_workflow') for state_var, state in states.items(): results_dict[state_var] = state state_title = self.state_titles.get(state, None) if not state_title: state_title = workflow.getTitleForStateOnType(state, ptype) if state_title: self.state_titles[state] = state_title if state_title and state == obj.review_state: results_dict['state_title'] = _(state_title) # extra classes for individual fields on this item # { field_id : "css classes" } results_dict['class'] = {} # Search for values for all columns in obj for key in self.columns.keys(): # if the key is already in the results dict # then we don't replace it's value value = results_dict.get(key, '') if not value: attrobj = getFromString(obj, key) value = attrobj if attrobj else value # Custom attribute? Inspect to set the value # for the current column dynamically vattr = self.columns[key].get('attr', None) if vattr: attrobj = getFromString(obj, vattr) value = attrobj if attrobj else value results_dict[key] = value # Replace with an url? replace_url = self.columns[key].get('replace_url', None) if replace_url: attrobj = getFromString(obj, replace_url) if attrobj: url = self.url_or_path_to_url(attrobj) results_dict['replace'][key] = \ '<a href="%s">%s</a>' % (url, value) # The item basics filled. Delegate additional actions to folderitem # service. folderitem service is frequently overriden by child # objects item = self.folderitem(obj, results_dict, idx) # Call folder_item from subscriber adapters for subscriber in self.get_listing_view_adapters(): subscriber.folder_item(obj, item, idx) if item: results.append(item) idx += 1 return results
def folderitems(self, full_objects=False): """ >>> portal = layer['portal'] >>> portal_url = portal.absolute_url() >>> from plone.app.testing import SITE_OWNER_NAME >>> from plone.app.testing import SITE_OWNER_PASSWORD Test page batching https://github.com/bikalabs/Bika-LIMS/issues/1276 When visiting the second page, the Water sampletype should be displayed: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/bika_setup/bika_sampletypes/folder_view?", ... "list_pagesize=10&list_review_state=default") >>> browser.contents '...Water...' """ #self.contentsMethod = self.context.getFolderContents if not hasattr(self, 'contentsMethod'): self.contentsMethod = getToolByName(self.context, self.catalog) context = aq_inner(self.context) plone_layout = getMultiAdapter((context, self.request), name=u'plone_layout') plone_utils = getToolByName(context, 'plone_utils') plone_view = getMultiAdapter((context, self.request), name=u'plone') portal_properties = getToolByName(context, 'portal_properties') portal_types = getToolByName(context, 'portal_types') workflow = getToolByName(context, 'portal_workflow') site_properties = portal_properties.site_properties norm = getUtility(IIDNormalizer).normalize if self.request.get('show_all', '').lower() == 'true' \ or self.show_all == True \ or self.pagesize == 0: show_all = True else: show_all = False if (hasattr(self, 'And') and self.And) \ or (hasattr(self, 'Or') and self.Or): # if contentsMethod is capable, we do an AdvancedQuery. if hasattr(self.contentsMethod, 'makeAdvancedQuery'): aq = self.contentsMethod.makeAdvancedQuery(self.contentFilter) if hasattr(self, 'And') and self.And: tmpAnd = And() for q in self.And: tmpAnd.addSubquery(q) aq &= tmpAnd if hasattr(self, 'Or') and self.Or: tmpOr = Or() for q in self.Or: tmpOr.addSubquery(q) aq &= tmpOr brains = self.contentsMethod.evalAdvancedQuery(aq) else: # otherwise, self.contentsMethod must handle contentFilter brains = self.contentsMethod(self.contentFilter) else: brains = self.contentsMethod(self.contentFilter) # idx increases one unit each time an object is added to the 'items' # dictionary to be returned. Note that if the item is not rendered, # the idx will not increase. idx = 0 results = [] self.show_more = False brains = brains[self.limit_from:] for i, obj in enumerate(brains): # avoid creating unnecessary info for items outside the current # batch; only the path is needed for the "select all" case... # we only take allowed items into account if not show_all and idx >= self.pagesize: # Maximum number of items to be shown reached! self.show_more = True break # we don't know yet if it's a brain or an object path = hasattr(obj, 'getPath') and obj.getPath() or \ "/".join(obj.getPhysicalPath()) # This item must be rendered, we need the object instead of a brain obj = obj.getObject() if hasattr(obj, 'getObject') else obj # check if the item must be rendered or not (prevents from # doing it later in folderitems) and dealing with paging if not obj or not self.isItemAllowed(obj): continue uid = obj.UID() title = obj.Title() description = obj.Description() icon = plone_layout.getIcon(obj) url = obj.absolute_url() relative_url = obj.absolute_url(relative=True) fti = portal_types.get(obj.portal_type) if fti is not None: type_title_msgid = fti.Title() else: type_title_msgid = obj.portal_type url_href_title = '%s at %s: %s' % (t(type_title_msgid), path, to_utf8(description)) modified = self.ulocalized_time(obj.modified()), # element css classes type_class = 'contenttype-' + \ plone_utils.normalizeString(obj.portal_type) state_class = '' states = {} for w in workflow.getWorkflowsFor(obj): state = w._getWorkflowStateOf(obj).id states[w.state_var] = state state_class += "state-%s " % state results_dict = dict( obj=obj, id=obj.getId(), title=title, uid=uid, path=path, url=url, fti=fti, item_data=json.dumps([]), url_href_title=url_href_title, obj_type=obj.Type, size=obj.getObjSize, modified=modified, icon=icon.html_tag(), type_class=type_class, # a list of lookups for single-value-select fields choices={}, state_class=state_class, relative_url=relative_url, view_url=url, table_row_class="", category='None', # a list of names of fields that may be edited on this item allow_edit=[], # a list of names of fields that are compulsory (if editable) required=[], # a dict where the column name works as a key and the value is # the name of the field related with the column. It is used # when the name given to the column and the content field it # represents diverges. bika_listing_table_items.pt defines an # attribute for each item, this attribute is named 'field' and # the system fills it taking advantage of this dictionary or # the name of the column if it isn't defined in the dict. field={}, # "before", "after" and replace: dictionary (key is column ID) # A snippet of HTML which will be rendered # before/after/instead of the table cell content. before={}, # { before : "<a href=..>" } after={}, replace={}, ) try: rs = workflow.getInfoFor(obj, 'review_state') st_title = workflow.getTitleForStateOnType(rs, obj.portal_type) st_title = t(PMF(st_title)) except: rs = 'active' st_title = None if rs: results_dict['review_state'] = rs for state_var, state in states.items(): if not st_title: st_title = workflow.getTitleForStateOnType( state, obj.portal_type) results_dict[state_var] = state results_dict['state_title'] = st_title # extra classes for individual fields on this item { field_id : "css classes" } results_dict['class'] = {} for name, adapter in getAdapters((obj, ), IFieldIcons): auid = obj.UID() if hasattr(obj, 'UID') and callable( obj.UID) else None if not auid: continue alerts = adapter() # logger.info(str(alerts)) if alerts and auid in alerts: if auid in self.field_icons: self.field_icons[auid].extend(alerts[auid]) else: self.field_icons[auid] = alerts[auid] # Search for values for all columns in obj for key in self.columns.keys(): # if the key is already in the results dict # then we don't replace it's value value = results_dict.get(key, '') if key not in results_dict: attrobj = getFromString(obj, key) value = attrobj if attrobj else value # Custom attribute? Inspect to set the value # for the current column dinamically vattr = self.columns[key].get('attr', None) if vattr: attrobj = getFromString(obj, vattr) value = attrobj if attrobj else value results_dict[key] = value # Replace with an url? replace_url = self.columns[key].get('replace_url', None) if replace_url: attrobj = getFromString(obj, replace_url) if attrobj: results_dict['replace'][key] = \ '<a href="%s">%s</a>' % (attrobj, value) # The item basics filled. Delegate additional actions to folderitem # service. folderitem service is frequently overriden by child objects item = self.folderitem(obj, results_dict, idx) if item: results.append(item) idx += 1 # Need manual_sort? # Note that the order has already been set in contentFilter, so # there is no need to reverse if self.manual_sort_on: results.sort(lambda x, y: cmp(x.get(self.manual_sort_on, ''), y.get(self.manual_sort_on, ''))) return results