class BikaListingView(BrowserView): """ """ template = ViewPageTemplateFile("templates/bika_listing.pt") title = "" description = "" catalog = "portal_catalog" contentFilter = {} allow_edit = True context_actions = {} show_select_column = False show_select_row = False show_select_all_checkbox = True show_sort_column = False show_workflow_action_buttons = True show_column_toggles = True categories = [] # setting pagesize to 1000 specifically disables the batch sizez dropdown pagesize = 25 pagenumber = 1 # select checkbox is normally called uids:list # if table_only is set then the context form tag might require # these to have a different name=FieldName:list. select_checkbox_name = "uids" # when rendering multiple bika_listing tables, form_id must be unique form_id = "list" review_state = 'default' """ ### column definitions The keys of the columns dictionary must all exist in all items returned by subclassing view's .folderitems. Blank entries are inserted in the default folderitems for all entries without values. ### possible column dictionary keys are: - allow_edit if self.allow_edit is also True, this field is made editable Interim fields are always editable - type "string" is the default, and actually, will require a NUMBER entry in the rendered text field. "choices" renders a dropdown. Selected automatically if a vocabulary exists. the vocabulary data must be placed in item['choices'][column_id]. it's a list of dictionaries: [{'ResultValue':x}, {'ResultText',x}]. TODO 'choices' should probably expect a DisplayList... "boolean" a checkbox is rendered "date" A text field is rendered, with a jquery DatePicker attached. - index the name of the catalog index for the column. adds 'indexed' class, to allow ajax table sorting for indexed columns - sortable: defaults True. if False, adds nosort class - toggle: enable/disable column toggle ability - input_class: CSS class applied to input widget in edit mode - input_width: size attribute applied to input widget in edit mode """ columns = { 'obj_type': { 'title': _('Type') }, 'id': { 'title': _('ID') }, 'title_or_id': { 'title': _('Title') }, 'modified': { 'title': _('Last modified') }, 'state_title': { 'title': _('State') }, } # Additional indexes to be searched # any index name not specified in self.columns[] can be added here. filter_indexes = ['Title', 'Description', 'SearchableText'] """ ### review_state filter with just one review_state, the selector won't show. if review_state[x]['transitions'] is defined, it is a list of dictionaries: [{'id':'x'}] Transitions will be ordered by and restricted to, these items. if review_state[x]['custom_actions'] is defined. it's a list of dict: [{'id':'x'}] These transitions will be forced into the list of workflow actions. They will need to be handled manually in the appropriate WorkflowAction subclass. """ review_states = [ { 'id': 'default', 'contentFilter': {}, 'title': _('All'), 'columns': ['obj_type', 'title_or_id', 'modified', 'state_title'] }, ] def __init__(self, context, request): super(BikaListingView, self).__init__(context, request) path = hasattr(context, 'getPath') and context.getPath() \ or "/".join(context.getPhysicalPath()) if hasattr(self, 'contentFilter'): if not 'path' in self.contentFilter: self.contentFilter['path'] = {"query": path, "level": 0} else: if not 'path' in self.contentFilter: self.contentFilter = {'path': {"query": path, "level": 0}} self.portal = getToolByName(context, 'portal_url').getPortalObject() self.portal_url = self.portal.absolute_url() self.base_url = context.absolute_url() self.view_url = self.base_url self.translate = self.context.translate def _process_request(self): # Use this function from a template that is using bika_listing_table # in such a way that the table_only request var will be used to # in-place-update the table. form_id = self.form_id form = self.request.form workflow = getToolByName(self.context, 'portal_workflow') catalog = getToolByName(self.context, self.catalog) # If table_only specifies another form_id, then we abort. # this way, a single table among many can request a redraw, # and only it's content will be rendered. if form_id not in self.request.get('table_only', form_id): return '' ## review_state_selector cookie = json.loads(self.request.get("review_state", '{}')) cookie_key = "%s%s" % (self.context.portal_type, form_id) # first check POST selected_state = self.request.get("%s_review_state" % form_id, '') if not selected_state: # then check cookie selected_state = cookie.get(cookie_key, 'default') # get review_state id=selected_state states = [r for r in self.review_states if r['id'] == selected_state] review_state = states and states[0] or self.review_states[0] # set request and cookie to currently selected state id if not selected_state: selected_state = self.review_states[0]['id'] self.review_state = cookie[cookie_key] = selected_state cookie = json.dumps(cookie) self.request['review_state'] = cookie self.request.response.setCookie('review_state', cookie, path="/") # contentFilter is expected in every review_state. for k, v in review_state['contentFilter'].items(): self.contentFilter[k] = v # sort on sort_on = self.request.get(form_id + '_sort_on', '') # manual_sort_on: only sort the current batch of items # this is a compromise for sorting without column indexes self.manual_sort_on = None if sort_on \ and sort_on in self.columns.keys() \ and self.columns[sort_on].get('index', None): idx = self.columns[sort_on].get('index', sort_on) self.contentFilter['sort_on'] = idx else: if sort_on: self.manual_sort_on = sort_on if 'sort_on' in self.contentFilter: del self.contentFilter['sort_on'] # sort order self.sort_order = self.request.get(form_id + '_sort_order', '') if self.sort_order: self.contentFilter['sort_order'] = self.sort_order else: if 'sort_order' not in self.contentFilter: self.sort_order = 'ascending' self.contentFilter['sort_order'] = 'ascending' self.request.set(form_id + '_sort_order', 'ascending') else: self.sort_order = self.contentFilter['sort_order'] if self.manual_sort_on: del self.contentFilter['sort_order'] # pagesize pagesize = self.request.get(form_id + '_pagesize', self.pagesize) if type(pagesize) in (list, tuple): pagesize = pagesize[0] try: pagesize = int(pagesize) except: pagesize = self.pagesize self.pagesize = pagesize # Plone's batching wants this variable: self.request.set('pagesize', self.pagesize) # pagenumber self.pagenumber = int( self.request.get(form_id + '_pagenumber', self.pagenumber)) # Plone's batching wants this variable: self.request.set('pagenumber', self.pagenumber) # index filters. self.And = [] self.Or = [] ##logger.info("contentFilter: %s"%self.contentFilter) for k, v in self.columns.items(): if not v.has_key('index') \ or v['index'] == 'review_state' \ or v['index'] in self.filter_indexes: continue self.filter_indexes.append(v['index']) ##logger.info("Filter indexes: %s"%self.filter_indexes) # any request variable named ${form_id}_{index_name} # will pass it's value to that index in self.contentFilter. # all conditions using ${form_id}_{index_name} are searched with AND for index in self.filter_indexes: idx = catalog.Indexes.get(index, None) if not idx: logger.debug("index named '%s' not found in %s. " "(Perhaps the index is still empty)." % (index, self.catalog)) continue request_key = "%s_%s" % (form_id, index) value = self.request.get(request_key, '') if len(value) > 1: ##logger.info("And: %s=%s"%(index, value)) if idx.meta_type in ('ZCTextIndex', 'FieldIndex'): self.And.append(MatchRegexp(index, value)) elif idx.meta_type == 'DateIndex': logger.error("Unhandled DateIndex search on '%s'" % index) continue else: self.Or.append(Generic(index, value)) # if there's a ${form_id}_filter in request, then all indexes # are are searched for it's value. # ${form_id}_filter is searched with OR agains all indexes request_key = "%s_filter" % form_id value = self.request.get(request_key, '') if len(value) > 1: for index in self.filter_indexes: idx = catalog.Indexes.get(index, None) if not idx: logger.debug("index named '%s' not found in %s. " "(Perhaps the index is still empty)." % (index, self.catalog)) continue ##logger.info("Or: %s=%s"%(index, value)) if idx.meta_type in ('ZCTextIndex', 'FieldIndex'): self.Or.append(MatchRegexp(index, value)) elif idx.meta_type == 'DateIndex': if value.find(":") > -1: try: lohi = [DateTime(x) for x in value.split(":")] except: logger.error( "Error (And, DateIndex='%s', term='%s')" % (index, value)) self.Or.append(Between(index, lohi[0], lohi[1])) else: try: self.Or.append(Eq(index, DateTime(value))) except: logger.error( "Error (Or, DateIndex='%s', term='%s')" % (index, value)) else: self.Or.append(Generic(index, value)) self.Or.append(MatchRegexp('review_state', value)) # get toggle_cols cookie value # and modify self.columns[]['toggle'] to match. toggle_cols = self.get_toggle_cols() for col in self.columns.keys(): if col in toggle_cols: self.columns[col]['toggle'] = True else: self.columns[col]['toggle'] = False def get_toggle_cols(self): states = [ r for r in self.review_states if r['id'] == self.review_state ] review_state = states and states[0] or self.review_states[0] try: toggles = {} # request OR cookie OR default toggles = json.loads( self.request.get(self.form_id + "_toggle_cols", self.request.get("toggle_cols", "{}"))) except: pass finally: if not toggles: toggles = {} cookie_key = "%s%s" % (self.context.portal_type, self.form_id) toggle_cols = toggles.get(cookie_key, [ col for col in self.columns.keys() if col in review_state['columns'] and ('toggle' not in self.columns[col] or self.columns[col]['toggle'] == True) ]) return toggle_cols def GET_url(self, **kwargs): url = self.context.absolute_url() query = {} for x in "pagenumber", "pagesize", "review_state": if str(getattr(self, x)) != 'None': query['%s_%s' % (self.form_id, x)] = getattr(self, x) for x in kwargs.keys(): query['%s_%s' % (self.form_id, x)] = kwargs.get(x) if query: url = url + "?" + "&".join( ["%s=%s" % (x, y) for x, y in query.items()]) return url def __call__(self): """ Handle request parameters and render the form.""" self._process_request() if self.request.get('table_only', '') == self.form_id: return self.contents_table( table_only=self.request.get('table_only')) else: return self.template() def selected_cats(self, items): """return a list of categories containing 'selected'=True items """ cats = [] for item in items: if 'selected' in item and \ 'category' in item and \ item['selected'] and \ item['category'] not in cats: cats.append(item['category']) return cats def folderitems(self, full_objects=False): """ """ #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 show_all = self.request.get('show_all', '').lower() == 'true' pagenumber = int(self.request.get('pagenumber', 1) or 1) pagesize = self.pagesize start = (pagenumber - 1) * pagesize end = start + pagesize 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) results = [] self.page_start_index = "" for i, obj in enumerate(brains): # we don't know yet if it's a brain or an object path = hasattr(obj, 'getPath') and obj.getPath() or \ "/".join(obj.getPhysicalPath()) # avoid creating unnecessary info for items outside the current # batch; only the path is needed for the "select all" case... if not show_all and not start <= i < end: if hasattr(obj, 'getObject'): uid = obj.UID else: uid = obj.UID() results.append(dict(path=path, uid=uid)) continue if self.page_start_index == "": self.page_start_index = i if hasattr(obj, 'getObject'): obj = obj.getObject() 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 = u'%s at %s: %s' % \ (self.context.translate(type_title_msgid, context = self.request), path, safe_unicode(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="", # 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=[], # "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: review_state = workflow.getInfoFor(obj, 'review_state') state_title = self.context.translate( PMF( workflow.getTitleForStateOnType( review_state, obj.portal_type))) except: review_state = 'active' state_title = None if review_state: results_dict['review_state'] = review_state for state_var, state in states.items(): if not state_title: state_title = workflow.getTitleForStateOnType( state, obj.portal_type) results_dict[state_var] = state results_dict['state_title'] = state_title # XXX add some kind of out-of-date indicator to bika listing ## if App.config.getConfiguration().debug_mode: ## from Products.CMFEditions.utilities import dereference ## pr = getToolByName(self.context, 'portal_repository') ## o = hasattr(obj, 'getObject') and obj.getObject() or obj ## if pr.isVersionable(o): ## pa = getToolByName(self.context, 'portal_archivist') ## history_id = str(dereference(o)[1]) ## version_id = hasattr(o, 'version_id') \ ## and str(o.version_id) or None ## if not 'version_id' in self.columns.keys(): ## self.columns['version_id'] = {'title':'version'} ## for x in range(len(self.review_states)): ## self.review_states[x]['columns'].append('version_id') ## results_dict['version_id'] = '%s/%s' % (version_id, history_id) # 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 hasattr(obj, key): # if the key is already in the results dict # then we don't replace it's value if results_dict.has_key(key): continue value = getattr(obj, key) if callable(value): value = value() results_dict[key] = value results.append(results_dict) return results def contents_table(self, table_only=False): """ If you set table_only to true, then nothing outside of the <table/> tag will be printed (form tags, authenticator, etc). Then you can insert your own form tags around it. """ table = BikaListingTable(bika_listing=self, table_only=table_only) return table.render(self)
class Report(BrowserView): implements(IViewView) template = ViewPageTemplateFile( "templates/qualitycontrol_resultspersamplepoint.pt") # if unsuccessful we return here: default_template = ViewPageTemplateFile("templates/qualitycontrol.pt") def __init__(self, context, request, report=None): super(Report, self).__init__(context, request) self.report = report self.selection_macros = SelectionMacrosView(self.context, self.request) def __call__(self): MinimumResults = self.context.bika_setup.getMinimumResults() warning_icon = "<img " + \ "src='" + self.portal_url + "/++resource++bika.lims.images/warning.png' " + \ "height='9' width='9'/>" error_icon = "<img " + \ "src='" + self.portal_url + "/++resource++bika.lims.images/exclamation.png' " + \ "height='9' width='9'/>" header = _("Results per sample point") subheader = _( "Analysis results for per sample point and analysis service") self.contentFilter = { 'portal_type': 'Analysis', 'review_state': ['verified', 'published'] } parms = [] titles = [] val = self.selection_macros.parse_client(self.request) if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) val = self.selection_macros.parse_samplepoint(self.request) sp_uid = val if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) val = self.selection_macros.parse_sampletype(self.request) st_uid = val if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) val = self.selection_macros.parse_analysisservice(self.request) if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) else: message = _("No analysis services were selected.") self.context.plone_utils.addPortalMessage(message, 'error') return self.default_template() val = self.selection_macros.parse_daterange(self.request, 'getDateSampled', 'DateSampled') if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) val = self.selection_macros.parse_state( self.request, 'bika_worksheetanalysis_workflow', 'worksheetanalysis_review_state', 'Worksheet state') if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) # Query the catalog and store analysis data in a dict analyses = {} out_of_range_count = 0 in_shoulder_range_count = 0 analysis_count = 0 proxies = self.bika_analysis_catalog(self.contentFilter) if not proxies: message = _("No analyses matched your query") self.context.plone_utils.addPortalMessage(message, 'error') return self.default_template() # # Compile a list of dictionaries, with all relevant analysis data for analysis in proxies: analysis = analysis.getObject() result = analysis.getResult() client = analysis.aq_parent.aq_parent uid = analysis.UID() service = analysis.getService() keyword = service.getKeyword() service_title = "%s (%s)" % (service.Title(), keyword) result_in_range = ResultOutOfRange(analysis) if service_title not in analyses.keys(): analyses[service_title] = [] try: result = float(analysis.getResult()) except: # XXX Unfloatable analysis results should be indicated continue analyses[service_title].append({ 'service': service, 'obj': analysis, 'Request ID': analysis.aq_parent.getId(), 'Analyst': analysis.getAnalyst(), 'Result': result, 'Sampled': analysis.getDateSampled(), 'Captured': analysis.getResultCaptureDate(), 'Uncertainty': analysis.getUncertainty(), 'result_in_range': result_in_range, 'Unit': service.getUnit(), 'Keyword': keyword, 'icons': '', }) analysis_count += 1 keys = analyses.keys() keys.sort() parms += [ { "title": _("Total analyses"), "value": analysis_count }, ] ## This variable is output to the TAL self.report_data = { 'header': header, 'subheader': subheader, 'parms': parms, 'tables': [], 'footnotes': [], } plotscript = """ set terminal png transparent truecolor enhanced size 700,350 font "Verdana, 8" set title "%(title)s" set xlabel "%(xlabel)s" set ylabel "%(ylabel)s" set key off #set logscale set timefmt "%(date_format_long)s" set xdata time set format x "%(date_format_short)s\\n%(time_format)s" set xrange ["%(x_start)s":"%(x_end)s"] set auto fix set offsets graph 0, 0, 1, 1 set xtics border nomirror rotate by 90 font "Verdana, 5" offset 0,-3 set ytics nomirror f(x) = mean_y fit f(x) 'gpw_DATAFILE_gpw' u 1:3 via mean_y stddev_y = sqrt(FIT_WSSR / (FIT_NDF + 1)) plot mean_y-stddev_y with filledcurves y1=mean_y lt 1 lc rgb "#efefef",\ mean_y+stddev_y with filledcurves y1=mean_y lt 1 lc rgb "#efefef",\ mean_y with lines lc rgb '#ffffff' lw 3,\ "gpw_DATAFILE_gpw" using 1:3 title 'data' with points pt 7 ps 1 lc rgb '#0000ee' lw 2,\ '' using 1:3 smooth unique lc rgb '#aaaaaa' lw 2,\ '' using 1:4 with lines lc rgb '#000000' lw 1,\ '' using 1:5 with lines lc rgb '#000000' lw 1""" ## Compile plots and format data for display for service_title in keys: # used to calculate XY axis ranges result_values = [int(o['Result']) for o in analyses[service_title]] result_dates = [o['Sampled'] for o in analyses[service_title]] parms = [] plotdata = str() range_min = '' range_max = '' for a in analyses[service_title]: a['Sampled'] = a['Sampled'].strftime( self.date_format_long) if a['Sampled'] else '' a['Captured'] = a['Captured'].strftime(self.date_format_long) if \ a['Captured'] else '' R = a['Result'] U = a['Uncertainty'] a['Result'] = a['obj'].getFormattedResult() in_range = a['result_in_range'] # result out of range if str(in_range[0]) == 'False': out_of_range_count += 1 a['Result'] = "%s %s" % (a['Result'], error_icon) # result almost out of range if str(in_range[0]) == '1': in_shoulder_range_count += 1 a['Result'] = "%s %s" % (a['Result'], warning_icon) spec = {} if hasattr(a["obj"], 'specification') and a["obj"].specification: spec = a["obj"].specification plotdata += "%s\t%s\t%s\t%s\t%s\n" % ( a['Sampled'], R, spec.get("min", ""), spec.get("max", ""), U and U or 0, ) plotdata.encode('utf-8') unit = analyses[service_title][0]['Unit'] if MinimumResults <= len(dict([(d, d) for d in result_dates])): _plotscript = str(plotscript) % { 'title': "", 'xlabel': t(_("Date Sampled")), 'ylabel': unit and unit or '', 'x_start': "%s" % min(result_dates).strftime(self.date_format_long), 'x_end': "%s" % max(result_dates).strftime(self.date_format_long), 'date_format_long': self.date_format_long, 'date_format_short': self.date_format_short, 'time_format': self.time_format, } plot_png = plot(str(plotdata), plotscript=str(_plotscript), usefifo=False) # Temporary PNG data file fh, data_fn = tempfile.mkstemp(suffix='.png') os.write(fh, plot_png) plot_url = data_fn self.request['to_remove'].append(data_fn) plot_url = data_fn else: plot_url = "" table = { 'title': "%s: %s" % (t(_("Analysis Service")), service_title), 'parms': parms, 'columns': ['Request ID', 'Analyst', 'Result', 'Sampled', 'Captured'], 'data': analyses[service_title], 'plot_url': plot_url, } self.report_data['tables'].append(table) translate = self.context.translate ## footnotes if out_of_range_count: msgid = _("Analyses out of range") self.report_data['footnotes'].append("%s %s" % (error_icon, t(msgid))) if in_shoulder_range_count: msgid = _("Analyses in error shoulder range") self.report_data['footnotes'].append("%s %s" % (warning_icon, t(msgid))) self.report_data['parms'].append({ "title": _("Analyses out of range"), "value": out_of_range_count }) self.report_data['parms'].append({ "title": _("Analyses in error shoulder range"), "value": in_shoulder_range_count }) title = t(header) if titles: title += " (%s)" % " ".join(titles) return { 'report_title': title, 'report_data': self.template(), }
class CommentsViewlet(ViewletBase): form = CommentForm index = ViewPageTemplateFile('comments.pt') def update(self): super(CommentsViewlet, self).update() discussion_allowed = self.is_discussion_allowed() anonymous_allowed_or_can_reply = ( self.is_anonymous() and self.anonymous_discussion_allowed() or self.can_reply()) if discussion_allowed and anonymous_allowed_or_can_reply: z2.switch_on(self, request_layer=IFormLayer) self.form = self.form(aq_inner(self.context), self.request) alsoProvides(self.form, IWrappedForm) self.form.update() # view methods def can_reply(self): """Returns true if current user has the 'Reply to item' permission. """ return getSecurityManager().checkPermission('Reply to item', aq_inner(self.context)) def can_manage(self): """We keep this method for <= 1.0b9 backward compatibility. Since we do not want any API changes in beta releases. """ return self.can_review() def can_review(self): """Returns true if current user has the 'Review comments' permission. """ return getSecurityManager().checkPermission('Review comments', aq_inner(self.context)) def is_discussion_allowed(self): context = aq_inner(self.context) return context.restrictedTraverse('@@conversation_view').enabled() def comment_transform_message(self): """Returns the description that shows up above the comment text, dependent on the text_transform setting and the comment moderation workflow in the discussion control panel. """ context = aq_inner(self.context) registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings, check=False) # text transform setting if settings.text_transform == "text/x-web-intelligent": message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT), context=self.request) elif settings.text_transform == "text/x-web-markdown": message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN), context=self.request) else: message = translate(Message(COMMENT_DESCRIPTION_PLAIN_TEXT), context=self.request) # comment workflow wftool = getToolByName(context, "portal_workflow", None) workflow_chain = wftool.getChainForPortalType('Discussion Item') if workflow_chain: comment_workflow = workflow_chain[0] comment_workflow = wftool[comment_workflow] # check if the current workflow implements a pending state. If this # is true comments are moderated if 'pending' in comment_workflow.states: message = message + " " + \ translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED), context=self.request) return message def has_replies(self, workflow_actions=False): """Returns true if there are replies. """ if self.get_replies(workflow_actions) is not None: try: self.get_replies(workflow_actions).next() return True except StopIteration: # pragma: no cover pass return False def get_replies(self, workflow_actions=False): """Returns all replies to a content object. If workflow_actions is false, only published comments are returned. If workflow actions is true, comments are returned with workflow actions. """ context = aq_inner(self.context) conversation = IConversation(context) wf = getToolByName(context, 'portal_workflow') # workflow_actions is only true when user # has 'Manage portal' permission def replies_with_workflow_actions(): # Generator that returns replies dict with workflow actions for r in conversation.getThreads(): comment_obj = r['comment'] # list all possible workflow actions actions = [ a for a in wf.listActionInfos(object=comment_obj) if a['category'] == 'workflow' and a['allowed'] ] r = r.copy() r['actions'] = actions yield r def published_replies(): # Generator that returns replies dict with workflow status. for r in conversation.getThreads(): comment_obj = r['comment'] workflow_status = wf.getInfoFor(comment_obj, 'review_state') if workflow_status == 'published': r = r.copy() r['workflow_status'] = workflow_status yield r # Return all direct replies if len(conversation.objectIds()): if workflow_actions: return replies_with_workflow_actions() else: return published_replies() def get_commenter_home_url(self, username=None): if username is None: return None else: return "%s/author/%s" % (self.context.portal_url(), username) def get_commenter_portrait(self, username=None): if username is None: # return the default user image if no username is given return 'defaultUser.gif' else: portal_membership = getToolByName(self.context, 'portal_membership', None) return portal_membership\ .getPersonalPortrait(username)\ .absolute_url() def anonymous_discussion_allowed(self): # Check if anonymous comments are allowed in the registry registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings, check=False) return settings.anonymous_comments def show_commenter_image(self): # Check if showing commenter image is enabled in the registry registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings, check=False) return settings.show_commenter_image def is_anonymous(self): portal_membership = getToolByName(self.context, 'portal_membership', None) return portal_membership.isAnonymousUser() def login_action(self): return '%s/login_form?came_from=%s' % \ (self.navigation_root_url, url_quote(self.request.get('URL', '')),) def format_time(self, time): # We have to transform Python datetime into Zope DateTime # before we can call toLocalizedTime. util = getToolByName(self.context, 'translation_service') zope_time = DateTime(time.isoformat()) return util.toLocalizedTime(zope_time, long_format=True)
class Folder(SettingsEditFormBase): """Enable, disable and customise syndication settings for a folder. """ base_template = SettingsEditFormBase.template template = ViewPageTemplateFile("syndication.pt") form_fields = form.FormFields(ISyndicationInfo).omit('enabled') label = _(u"Configure Folder Syndication") actions = form.Actions( form.Action( name="enable", label=_(u"Enable Syndication"), condition="disabled", success="handle_enable", ), form.Action( name="change", label=_(u"Change"), condition="enabled", success="handle_change", ), form.Action( name="revert", label=_(u"Revert to Site Default"), condition="enabled", success="handle_revert", ), form.Action( name="disable", label=_(u"Disable Syndication"), condition="enabled", success="handle_disable", )) @memoize def getContent(self): return getAdapter(self.context, ISyndicationInfo) @memoize def disabled(self, action=None): return not self.enabled() @memoize def enabled(self, action=None): return self.getContent().enabled @memoize def allowed(self, action=None): return self.getContent().allowed def handle_enable(self, action, data): self.getContent().enable() self._handle_success(action, data) self.status = _(u"Syndication enabled.") self._setRedirect("portal_actions", "object/syndication") def handle_disable(self, action, data): self.getContent().disable() self.status = _(u"Syndication disabled.") self._setRedirect("portal_actions", "object/syndication") def handle_change(self, action, data): self._handle_success(action, data) self.status = _(u"Syndication settings changed.") self._setRedirect("portal_actions", "object/syndication") def handle_revert(self, action, data): self.getContent().revert() self.status = _(u"Syndication reset to site default.") self._setRedirect("portal_actions", "object/syndication")
def setFromFilename(self, filename, _prefix=None): self._template = ViewPageTemplateFile(filename, _prefix)
class InstrumentQCFailuresViewlet(ViewletBase): """ Print a viewlet showing failed instruments """ index = ViewPageTemplateFile("templates/instrument_qc_failures_viewlet.pt") def __init__(self, context, request, view, manager=None): super(InstrumentQCFailuresViewlet, self).__init__(context, request, view, manager=manager) self.nr_failed = 0 self.failed = { 'out-of-date': [], 'qc-fail': [], 'next-test': [], 'validation': [], 'calibration': [] } def get_failed_instruments(self): """ Find all active instruments who have failed QC tests Find instruments whose certificate is out of date Find instruments which are disposed until next calibration test Return a dictionary with all info about expired/invalid instruments """ bsc = getToolByName(self, 'bika_setup_catalog') insts = bsc(portal_type='Instrument', inactive_state='active') for i in insts: i = i.getObject() instr = { 'uid': i.UID(), 'title': i.Title(), } if i.isValidationInProgress(): instr['link'] = '<a href="%s/validations">%s</a>' % ( i.absolute_url(), i.Title()) self.nr_failed += 1 self.failed['validation'].append(instr) elif i.isCalibrationInProgress(): instr['link'] = '<a href="%s/calibrations">%s</a>' % ( i.absolute_url(), i.Title()) self.nr_failed += 1 self.failed['calibration'].append(instr) elif i.isOutOfDate(): instr['link'] = '<a href="%s/certifications">%s</a>' % ( i.absolute_url(), i.Title()) self.nr_failed += 1 self.failed['out-of-date'].append(instr) elif not i.isQCValid(): instr['link'] = '<a href="%s/referenceanalyses">%s</a>' % ( i.absolute_url(), i.Title()) self.nr_failed += 1 self.failed['qc-fail'].append(instr) elif i.getDisposeUntilNextCalibrationTest(): instr['link'] = '<a href="%s/referenceanalyses">%s</a>' % ( i.absolute_url(), i.Title()) self.nr_failed += 1 self.failed['next-test'].append(instr) def render(self): mtool = getToolByName(self.context, 'portal_membership') member = mtool.getAuthenticatedMember() roles = member.getRoles() allowed = 'LabManager' in roles or 'Manager' in roles self.get_failed_instruments() if allowed and self.nr_failed: return self.index() else: return ""
class SiteTitle(ViewletBase, Utilities): index = ViewPageTemplateFile('templates/sitetitle_viewlet.pt')
class Report(BrowserView): implements(IViewView) template = ViewPageTemplateFile("templates/report_out.pt") def __init__(self, context, request, report=None): self.report = report BrowserView.__init__(self, context, request) def __call__(self): # get all the data into datalines pc = getToolByName(self.context, 'portal_catalog') sc = getToolByName(self.context, 'bika_setup_catalog') bc = getToolByName(self.context, 'bika_analysis_catalog') rc = getToolByName(self.context, 'reference_catalog') self.report_content = {} parm_lines = {} parms = [] headings = {} headings['header'] = _("Attachments") headings['subheader'] = _( "The attachments linked to analysis requests and analyses") count_all = 0 query = {'portal_type': 'Attachment'} if self.request.form.has_key('getClientUID'): client_uid = self.request.form['getClientUID'] query['getClientUID'] = client_uid client = rc.lookupObject(client_uid) client_title = client.Title() else: client = logged_in_client(self.context) if client: client_title = client.Title() query['getClientUID'] = client.UID() else: client_title = 'Undefined' parms.append({ 'title': _('Client'), 'value': client_title, 'type': 'text' }) date_query = formatDateQuery(self.context, 'DateLoaded') if date_query: query['getDateLoaded'] = date_query loaded = formatDateParms(self.context, 'DateLoaded') else: loaded = 'Undefined' parms.append({'title': _('Loaded'), 'value': loaded, 'type': 'text'}) # and now lets do the actual report lines formats = {'columns': 6, 'col_heads': [ _('Request'), \ _('File'), \ _('Attachment type'), \ _('Content type'), \ _('Size'), \ _('Loaded'), \ ], 'class': '', } datalines = [] attachments = pc(query) for a_proxy in attachments: attachment = a_proxy.getObject() attachment_file = attachment.getAttachmentFile() icon = attachment_file.getBestIcon() filename = attachment_file.filename filesize = attachment_file.get_size() filesize = filesize / 1024 sizeunit = "Kb" if filesize > 1024: filesize = filesize / 1024 sizeunit = "Mb" dateloaded = attachment.getDateLoaded() dataline = [] dataitem = {'value': attachment.getTextTitle()} dataline.append(dataitem) dataitem = {'value': filename, 'img_before': icon} dataline.append(dataitem) dataitem = {'value': attachment.getAttachmentType().Title()} dataline.append(dataitem) dataitem = { 'value': self.context.lookupMime(attachment_file.getContentType()) } dataline.append(dataitem) dataitem = {'value': '%s%s' % (filesize, sizeunit)} dataline.append(dataitem) dataitem = {'value': self.ulocalized_time(dateloaded)} dataline.append(dataitem) datalines.append(dataline) count_all += 1 # footer data footlines = [] footline = [] footitem = {'value': _('Total'), 'colspan': 5, 'class': 'total_label'} footline.append(footitem) footitem = {'value': count_all} footline.append(footitem) footlines.append(footline) self.report_content = { 'headings': headings, 'parms': parms, 'formats': formats, 'datalines': datalines, 'footings': footlines } return { 'report_title': self.context.translate(headings['header']), 'report_data': self.template() }
class Renderer(BasePortletRenderer, BaseSearchView): render = ViewPageTemplateFile("collectionsearch.pt")
class BaseForm(EditForm): """ """ template = ViewPageTemplateFile('settings_edit.pt')
class Report(BrowserView): implements(IViewView) default_template = ViewPageTemplateFile("templates/productivity.pt") template = ViewPageTemplateFile( "templates/productivity_analysesperdepartment.pt") def __init__(self, context, request, report=None): super(Report, self).__init__(context, request) self.report = report self.selection_macros = SelectionMacrosView(self.context, self.request) def __call__(self): parms = [] titles = [] # Apply filters self.contentFilter = {'portal_type': 'Analysis'} val = self.selection_macros.parse_daterange(self.request, 'getDateRequested', _('Date Requested')) if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) val = self.selection_macros.parse_state(self.request, 'bika_analysis_workflow', 'getAnalysisState', _('Analysis State')) if val: self.contentFilter[val['contentFilter'] [0]] = val['contentFilter'][1] parms.append(val['parms']) titles.append(val['titles']) # Query the catalog and store results in a dictionary analyses = self.bika_analysis_catalog(self.contentFilter) if not analyses: message = _("No analyses matched your query") self.context.plone_utils.addPortalMessage(message, "error") return self.default_template() groupby = self.request.form.get('GroupingPeriod', '') if (groupby != ''): parms.append({"title": _("Grouping period"), "value": _(groupby)}) datalines = {} footlines = {} totalcount = len(analyses) totalpublishedcount = 0 totalperformedcount = 0 for analysis in analyses: analysis = analysis.getObject() analysisservice = analysis.getService() department = analysisservice.getDepartment() department = department.Title() if department else '' daterequested = analysis.created() group = '' if groupby == 'Day': group = self.ulocalized_time(daterequested) elif groupby == 'Week': group = daterequested.strftime( "%Y") + ", " + daterequested.strftime("%U") elif groupby == 'Month': group = daterequested.strftime( "%B") + " " + daterequested.strftime("%Y") elif groupby == 'Year': group = daterequested.strftime("%Y") else: group = '' dataline = { 'Group': group, 'Requested': 0, 'Performed': 0, 'Published': 0, 'Departments': {} } deptline = { 'Department': department, 'Requested': 0, 'Performed': 0, 'Published': 0 } if (group in datalines): dataline = datalines[group] if (department in dataline['Departments']): deptline = dataline['Departments'][department] grouptotalcount = dataline['Requested'] + 1 groupperformedcount = dataline['Performed'] grouppublishedcount = dataline['Published'] depttotalcount = deptline['Requested'] + 1 deptperformedcount = deptline['Performed'] deptpubishedcount = deptline['Published'] workflow = getToolByName(self.context, 'portal_workflow') arstate = workflow.getInfoFor(analysis.aq_parent, 'review_state', '') if (arstate == 'published'): deptpubishedcount += 1 grouppublishedcount += 1 totalpublishedcount += 1 if (analysis.getResult()): deptperformedcount += 1 groupperformedcount += 1 totalperformedcount += 1 group_performedrequested_ratio = float( groupperformedcount) / float(grouptotalcount) group_publishedperformed_ratio = groupperformedcount > 0 and float( grouppublishedcount) / float(groupperformedcount) or 0 anl_performedrequested_ratio = float(deptperformedcount) / float( depttotalcount) anl_publishedperformed_ratio = deptperformedcount > 0 and float( deptpubishedcount) / float(deptperformedcount) or 0 dataline['Requested'] = grouptotalcount dataline['Performed'] = groupperformedcount dataline['Published'] = grouppublishedcount dataline[ 'PerformedRequestedRatio'] = group_performedrequested_ratio dataline['PerformedRequestedRatioPercentage'] = ('{0:.0f}'.format( group_performedrequested_ratio * 100)) + "%" dataline[ 'PublishedPerformedRatio'] = group_publishedperformed_ratio dataline['PublishedPerformedRatioPercentage'] = ('{0:.0f}'.format( group_publishedperformed_ratio * 100)) + "%" deptline['Requested'] = depttotalcount deptline['Performed'] = deptperformedcount deptline['Published'] = deptpubishedcount deptline['PerformedRequestedRatio'] = anl_performedrequested_ratio deptline['PerformedRequestedRatioPercentage'] = ('{0:.0f}'.format( anl_performedrequested_ratio * 100)) + "%" deptline['PublishedPerformedRatio'] = anl_publishedperformed_ratio deptline['PublishedPerformedRatioPercentage'] = ('{0:.0f}'.format( anl_publishedperformed_ratio * 100)) + "%" dataline['Departments'][department] = deptline datalines[group] = dataline # Footer total data total_performedrequested_ratio = float(totalperformedcount) / float( totalcount) total_publishedperformed_ratio = totalperformedcount > 0 and float( totalpublishedcount) / float(totalperformedcount) or 0 footline = { 'Requested': totalcount, 'Performed': totalperformedcount, 'Published': totalpublishedcount, 'PerformedRequestedRatio': total_performedrequested_ratio, 'PerformedRequestedRatioPercentage': ('{0:.0f}'.format(total_performedrequested_ratio * 100)) + "%", 'PublishedPerformedRatio': total_publishedperformed_ratio, 'PublishedPerformedRatioPercentage': ('{0:.0f}'.format(total_publishedperformed_ratio * 100)) + "%" } footlines['Total'] = footline self.report_data = { 'parameters': parms, 'datalines': datalines, 'footlines': footlines } if self.request.get('output_format', '') == 'CSV': import csv import StringIO import datetime fieldnames = [ 'Group', 'Department', 'Requested', 'Performed', 'Published', ] output = StringIO.StringIO() dw = csv.DictWriter(output, extrasaction='ignore', fieldnames=fieldnames) dw.writerow(dict((fn, fn) for fn in fieldnames)) for group_name, group in datalines.items(): for dept_name, dept in group['Departments'].items(): dw.writerow({ 'Group': group_name, 'Department': dept_name, 'Requested': dept['Requested'], 'Performed': dept['Performed'], 'Published': dept['Published'], }) report_data = output.getvalue() output.close() date = datetime.datetime.now().strftime("%Y%m%d%H%M") setheader = self.request.RESPONSE.setHeader setheader('Content-Type', 'text/csv') setheader( "Content-Disposition", "attachment;filename=\"analysesperdepartment_%s.csv\"" % date) self.request.RESPONSE.write(report_data) else: return { 'report_title': _('Analyses summary per department'), 'report_data': self.template() }
class TaskResponseView(BrowserView): """ Base class for Task Reports TODO Add error checking """ template = ViewPageTemplateFile('templates/taskresponse.pt') def __call__(self): self.request.set("disable_border", True) # TODO test this and swap it in # site = getSite() # wft = site.portal_workflow context = self.context wft = context.portal_workflow form = self.request.form comments = form.get("task_comments", "") if comments: context.setDescription(comments) start_date_year = form.get("startDate-year", "") start_date_month = form.get("startDate-month", "") start_date_day = form.get("startDate-day", "") if start_date_year and start_date_month and start_date_day: context.startDate = DateTime("%s/%s/%s" % \ (form["startDate-year"], form["startDate-month"], form["startDate-day"]) ) completion_date_year = form.get("completionDate-year", "") completion_date_month = form.get("completionDate-month", "") completion_date_day = form.get("completionDate-day", "") if completion_date_year and completion_date_month \ and completion_date_day: context.completionDate = DateTime("%s/%s/%s" % \ (form["completionDate-year"], form["completionDate-month"], form["completionDate-day"]) ) workflow_action = form.get("workflow_action", None) # FIXME only reindex when something has changed context.reindexObject() if workflow_action: wft.doActionFor(context, workflow_action) if form: # Reload the page, clearing the form self.request.response.redirect(context.absolute_url()) else: return self.template() def date_input_widget(self, name): z2.switch_on(self) widget = DateWidget(self.request) widget.name = name widget.id = name context = self.context date = getattr(context, name) if date: widget.value = (date.year(), date.month(), date.day()) widget.show_today_link = True widget.ignoreContext = False widget.update() return widget.render()
class MeetingView(BrowserView): noword_template = ViewPageTemplateFile('templates/meeting-noword.pt') word_template = ViewPageTemplateFile('templates/meeting-word.pt') def __init__(self, context, request): super(MeetingView, self).__init__(context, request) self.model = self.context.model def __call__(self): if is_word_meeting_implementation_enabled(): # Enable border to show the zip export action also for # committee members. Because the plone_view's `showEditableBorder` # checks for `ModifyPortalContent`, we have to enable the border # manually. self.request.set('enable_border', True) return self.word_template() else: return self.noword_template() def get_css_class(self, document): """Provide CSS classes for icons.""" return get_css_class(document) def transition_url(self, transition): return MeetingTransitionController.url_for(self.context, self.model, transition.name) def unscheduled_proposals(self): return self.context.get_unscheduled_proposals() def get_protocol_document_label(self): if self.model.is_pending(): return _(u'document_label_pre_protocol', u'Pre-protocol') else: return _(u'document_label_protocol', u'Protocol') def get_protocol_document(self): if self.model.protocol_document: return IContentListingObject( self.model.protocol_document.resolve_document()) def get_protocol_document_link(self): document = self.get_protocol_document() return document.render_link(title=self.get_protocol_document_label(), show_icon=False) def get_agendaitem_list_document(self): if self.model.agendaitem_list_document: return IContentListingObject( self.model.agendaitem_list_document.resolve_document()) def get_agendaitem_list_document_link(self): document = self.get_agendaitem_list_document() return document.render_link(title=_(u'document_label_agenda_item_list', default=u'Agenda item list'), show_icon=False) def url_protocol(self): return self.model.get_url(view='protocol') def url_generate_protocol(self): if is_word_meeting_implementation_enabled(): return self.url_merge_docx_protocol() if not self.model.has_protocol_document(): return GenerateProtocol.url_for(self.model) else: return UpdateProtocol.url_for(self.model) def url_merge_docx_protocol(self): return MergeDocxProtocol.url_for(self.model) def has_agendaitem_list_document(self): return self.model.has_agendaitem_list_document() def has_protocol_document(self): return self.model.has_protocol_document() def url_download_protocol(self): if self.has_protocol_document: return self.model.protocol_document.get_download_url() def will_closing_regenerate_protocol(self): """This method can tell whether we will regenerate the protocol when closing the meething. The protocol will be generated while it is locked or not yet existing. When the user reopens the meeting the document will be left unlocked because the user may have made changes at this point. The protocol is no longer regenerated automatically when it may have been changed. """ if not self.has_protocol_document(): return True return self.model.protocol_document.is_locked() def url_download_agendaitem_list(self): if self.has_agendaitem_list_document: return self.model.agendaitem_list_document.get_download_url() def url_generate_agendaitem_list(self): if not self.model.has_agendaitem_list_document(): return GenerateAgendaItemList.url_for(self.model) else: return UpdateAgendaItemList.url_for(self.model) def url_agendaitem_list(self): return self.model.get_url(view='agenda_item_list') def url_zipexport(self): return self.model.get_url(view='export-meeting-zip') def url_manually_generate_excerpt(self): return self.model.get_url(view='generate_excerpt') def transitions(self): return self.model.get_state().get_transitions() def agenda_items(self): return self.model.agenda_items def manually_generated_excerpts(self): docs = [ excerpt.resolve_document() for excerpt in self.model.excerpt_documents ] return IContentListing(docs) def render_handlebars_agendaitems_template(self): if is_word_meeting_implementation_enabled(): return self.render_handlebars_agendaitems_template_word() else: return self.render_handlebars_agendaitems_template_noword() def render_handlebars_navigation_template(self): return prepare_handlebars_template( TEMPLATES_DIR.joinpath('navigation-word.html')) def render_handlebars_agendaitems_template_noword(self): return prepare_handlebars_template( TEMPLATES_DIR.joinpath('agendaitems-noword.html'), translations=( _('label_edit_cancel', default='Cancel'), _('label_edit_save', default='Save'), _('label_decide_action', default='Decide this agenda item'), _('label_reopen_action', default='Reopen this agenda item'), _('label_revise_action', default='Revise this agenda item'), _('action_rename_agenda_item', default='Rename agenda item'), _('action_rename_agenda_paragraph', default='Rename paragraph'), _('action_remove_agenda_item', default='Remove agenda item'), _('action_remove_agenda_paragraph', default='Remove paragraph'), ), max_proposal_title_length=ISubmittedProposal['title'].max_length) def render_handlebars_agendaitems_template_word(self): return prepare_handlebars_template( TEMPLATES_DIR.joinpath('agendaitems-word.html'), translations=( _('label_edit_cancel', default='Cancel'), _('label_edit_save', default='Save'), _('label_attachments', default='Attachments'), _('label_excerpts', default='Excerpts'), _('label_toggle_attachments', default='Toggle attachments'), _('label_agenda_item_number', default='Agenda item number'), _('label_decision_number', default=u'Decision number'), _('label_agenda_item_decided', default='Decided'), _('action_edit_document', default='Edit with word'), _('action_decide', default='Decide'), _('action_generate_excerpt', default='Generate excerpt'), _('action_rename_agenda_item', default='Rename agenda item'), _('action_rename_agenda_paragraph', default='Rename paragraph'), _('action_remove_agenda_item', default='Remove agenda item'), _('action_remove_agenda_paragraph', default='Remove paragraph'), _('action_reopen', default='Reopen agenda item'), _('action_return_excerpt', default='Return to proposal'), _('help_return_excerpt', default= 'Return this excerpt the as official answer to the proposal.' ), _('label_returned_excerpt', default='Returned'), _('help_returned_excerpt', default='This excerpt was returned to the dossier'), _('action_create_task', default='Create task'), _('help_create_task', default='Create a new task the meeting dossier and attach' ' this excerpt.')), max_proposal_title_length=ISubmittedProposal['title'].max_length) def render_handlebars_proposals_template(self): return prepare_handlebars_template( TEMPLATES_DIR.joinpath('proposals.html'), translations=(_('label_schedule', default='Schedule'), _('label_no_proposals', default='No proposals submitted'))) def json_is_editable(self): return json.dumps(self.model.is_editable()) def json_is_agendalist_editable(self): return json.dumps(self.model.is_agendalist_editable()) @property def url_update_agenda_item_order(self): return '{}/agenda_items/update_order'.format( self.context.absolute_url()) @property def url_list_agenda_items(self): return '{}/agenda_items/list'.format(self.context.absolute_url()) @property def url_schedule_text(self): return '{}/agenda_items/schedule_text'.format( self.context.absolute_url()) @property def url_schedule_paragraph(self): return '{}/agenda_items/schedule_paragraph'.format( self.context.absolute_url()) @property def url_list_unscheduled_proposals(self): return '{}/unscheduled_proposals'.format(self.context.absolute_url()) @property def msg_unexpected_error(self): return translate(_('An unexpected error has occurred', default='An unexpected error has occurred'), context=self.request) @require_word_meeting_feature def get_participants(self): result = [] participants = self.model.participants presidency = self.model.presidency secretary = self.model.secretary for membership in Membership.query.for_meeting(self.model): item = { 'fullname': membership.member.fullname, 'email': membership.member.email, 'member_id': membership.member.member_id } if membership.member in participants: item['presence_cssclass'] = 'presence present' else: item['presence_cssclass'] = 'presence not-present' if membership.member == presidency: item['role'] = { 'name': 'presidency', 'label': _(u'meeting_role_presidency', default=u'Presidency') } elif membership.member == secretary: item['role'] = { 'name': 'secretary', 'label': _(u'meeting_role_secretary', default=u'Secretary') } else: item['role'] = {'name': '', 'label': ''} result.append(item) result.sort(key=itemgetter('fullname')) return result @require_word_meeting_feature def get_closing_infos(self): transition_controller = self.model.workflow.transition_controller infos = { 'is_closed': False, 'close_url': None, 'reopen_url': None, 'cancel_url': None } can_change = api.user.has_permission( 'Modify portal content', obj=self.model.committee.resolve_committee()) if self.model.is_closed(): infos['is_closed'] = True infos['reopen_url'] = can_change and transition_controller.url_for( self.context, self.model, 'closed-held') else: close_transition = self.get_close_transition() if close_transition: infos[ 'close_url'] = can_change and transition_controller.url_for( self.context, self.model, close_transition.name) cancel_transition = self.get_cancel_transition() if cancel_transition: infos[ 'cancel_url'] = can_change and transition_controller.url_for( self.context, self.model, cancel_transition.name) return infos @require_word_meeting_feature def get_close_transition(self): for transition in self.model.workflow.get_transitions( self.model.get_state()): if transition.state_to == 'closed' and transition.visible: return transition return None @require_word_meeting_feature def get_cancel_transition(self): for transition in self.model.workflow.get_transitions( self.model.get_state()): if transition.state_to == 'cancelled' and transition.visible: return transition return None
class BikaListingTable(tableview.Table): render = ViewPageTemplateFile("templates/bika_listing_table.pt") def __init__(self, bika_listing=None, table_only=False): self.table = self self.table_only = table_only self.bika_listing = bika_listing self.pagesize = bika_listing.pagesize folderitems = bika_listing.folderitems() if hasattr(self.bika_listing, 'manual_sort_on') \ and self.bika_listing.manual_sort_on: psi = self.bika_listing.page_start_index psi = psi and psi or 0 # We do a sort of the current page using self.manual_sort_on, here page = folderitems[psi:psi + self.bika_listing.pagesize] page.sort( lambda x, y: cmp(x.get(self.bika_listing.manual_sort_on, ''), y.get(self.bika_listing.manual_sort_on, ''))) if self.bika_listing.sort_order[0] in ['d', 'r']: page.reverse() folderitems = folderitems[:psi] \ + page \ + folderitems[psi+self.bika_listing.pagesize:] tableview.Table.__init__(self, bika_listing.request, bika_listing.base_url, bika_listing.view_url, folderitems, pagesize=bika_listing.pagesize) self.context = bika_listing.context self.request = bika_listing.request self.form_id = bika_listing.form_id self.items = folderitems def tabindex(self): i = 0 while True: i += 1 yield i def get_workflow_actions(self): """ Compile a list of possible workflow transitions for items in this Table. """ # return empty list if selecting checkboxes are disabled if not self.show_select_column: return [] workflow = getToolByName(self.context, 'portal_workflow') cookie = json.loads(self.request.get("review_state", '{}')) cookie_key = "%s%s" % (self.context.portal_type, self.form_id) # first check POST selected_state = self.request.get("%s_review_state" % self.form_id, '') if not selected_state: # then check cookie selected_state = cookie.get(cookie_key, '') # get review_state id=selected_state states = [ r for r in self.bika_listing.review_states if r['id'] == selected_state ] review_state = states and states[0] \ or self.bika_listing.review_states[0] # get all transitions for all items. transitions = {} actions = [] for obj in [i.get('obj', '') for i in self.items]: obj = hasattr(obj, 'getObject') and obj.getObject() or obj for t in workflow.getTransitionsFor(obj): transitions[t['id']] = t # the list is restricted to and ordered by these transitions. if 'transitions' in review_state: for transition_dict in review_state['transitions']: if transition_dict['id'] in transitions: actions.append(transitions[transition_dict['id']]) else: actions = transitions.values() # and these are removed if 'hide_transitions' in review_state: actions = [ a for a in actions if a['id'] not in review_state['hide_transitions'] ] # if there is a review_state['some_state']['custom_actions'] attribute # on the BikaListingView, append these actions to the list. if 'custom_actions' in review_state: for action in review_state['custom_actions']: actions.append(action) for a, action in enumerate(actions): actions[a]['title'] = \ self.bika_listing.translate(PMF(actions[a]['id'] + "_transition_title")) return actions
class BandeauManagerViewlet(ViewletBase): index = ViewPageTemplateFile('bandeauManager.pt')
class PollTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/poll.pt') is_configurable = False def results(self): uuid = self.data.get('uuid', None) if uuid is not None: obj = uuidToObject(uuid) return obj @property def utility(self): ''' Access to IPolls utility ''' utility = queryUtility(IPolls, name='collective.polls') return utility def poll(self): utility = self.utility uid = uuid = self.data.get('uuid', None) poll = None if uuid is not None: poll = utility.poll_by_uid(uid) if not poll: # if we have no open poll, try closed ones results = utility.recent_polls(show_all=True, limit=1, review_state='closed') poll = results and results[0].getObject() or None return poll def getVotingResults(self): poll = self.poll() if poll.show_results: return poll.getResults() else: return None @property def can_vote(self): utility = self.utility poll = self.poll() if poll: try: return utility.allowed_to_vote(poll, self.request) except Unauthorized: return False return False @property def available(self): utility = self.utility poll = self.poll() if poll: can_view = utility.allowed_to_view(poll) # Do not show this portlet in the poll context return can_view and not (poll == self.context) return False def populate_with_object(self, obj): super(PollTile, self).populate_with_object(obj) uuid = api.content.get_uuid(obj) data_mgr = ITileDataManager(self) data_mgr.set({'uuid': uuid}) def poll_uid(self): ''' Return uid for current poll ''' utility = self.utility return utility.uid_for_poll(self.poll()) def delete(self): data_mgr = ITileDataManager(self) data_mgr.delete() def accepted_ct(self): valid_ct = ['collective.polls.poll'] return valid_ct def has_data(self): uuid = self.data.get('uuid', None) return uuid is not None def is_closed(self): state = 'closed' if self.poll(): state = api.content.get_state(self.poll()) return state == 'closed'
class BandeauViewlet(EnvironmentViewlet): index = ViewPageTemplateFile('bandeau.pt') marker = 'Solgema.EnvironmentViewlets.interfaces.IBandeauMarker' def getSolgemaBandeaux(self): bandeaux = self.getItems() bandeauxList = [] base_ajax_content_load = self.request.get('ajax_content_load') base_ajax_load = self.request.get('ajax_load') setattr(self.request, 'ajax_content_load', 1) setattr(self.request, 'ajax_load', 1) for bandeau in bandeaux: bdict = {} if getattr(bandeau, 'image_sizes', None): height = str(bandeau.image_sizes['base'][1]) url = str(bandeau.getPath() + '/image') try: title = str(bandeau.Description) except: try: title = str(bandeau.Description.encode('utf-8')) except: try: title = str(bandeau.Description.decode('utf-8')) except: title = safe_unicode(bandeau.Description) if getattr(bandeau, 'bannerImageLink', ''): link = str(self.context.absolute_url() + '/resolveuid/' + bandeau.bannerImageLink) else: link = None repeat = getattr(bandeau, 'backgroundRepeat', None) if not repeat: repeat = 'no-repeat' repeat = str(repeat) align = getattr(bandeau, 'backgroundAlign', None) if not align: align = 'left' align = str(align) cssClass = 'bandeau_image' cssStyle = 'position:relative;' if getattr(bandeau, 'backgroundExtend', False): cssClass += ' backgroundExtend' if getattr(bandeau, 'backgroundFixed', False): cssClass += ' backgroundFixed' if len(bandeauxList) == 0: cssClass += ' ' + bandeau.id.replace( '.', '_') + ' carousel-banner-content selected' cssStyle += ' display:block;' else: cssClass += ' ' + bandeau.id.replace( '.', '_') + ' carousel-banner-content' cssStyle += ' display:none;' if link: backgrounddiv = '<a style="display:block; height:%spx; width:100%%; background:transparent url(%s) %s %s top;" title="%s" class="%s" href="%s"></a>' % ( height, url, repeat, align, title, cssClass, link) else: backgrounddiv = '<div style="height:%spx; width:100%%; background:transparent url(%s) %s %s top;" title="%s" class="%s"></div>' % ( height, url, repeat, align, title, cssClass) # bandeauxList.append({'id':bandeau.id, 'content':bandeau.tag(title=bandeau.Description())}) bdict = { 'id': bandeau.id, 'content': backgrounddiv, 'cssClass': cssClass, 'cssStyle': cssStyle, 'url': url, 'link': link, 'align': align, 'repeat': repeat } else: bandeau = bandeau.getObject() if (has_dx and IImage.providedBy(bandeau)) or hasattr( bandeau, 'tag'): if hasattr(bandeau, 'getHeight'): height = bandeau.getHeight() else: height = ImageScaling(bandeau, self.request).scale().height if has_dx and IImage.providedBy(bandeau): url = bandeau.absolute_url() else: url = str(bandeau.absolute_url() + '/image') title = bandeau.title bkg = IBackgroundContent(bandeau, None) repeat = getattr(bkg, 'backgroundRepeat', None) if not repeat: repeat = 'no-repeat' align = getattr(bkg, 'backgroundAlign', None) if not align: align = 'left' align = str(align) cssClass = 'bandeau_image' cssStyle = 'position:relative;' if getattr(bkg, 'backgroundExtend', False): cssClass += ' backgroundExtend' if getattr(bkg, 'backgroundFixed', False): cssClass += ' backgroundFixed' if len(bandeauxList) == 0: cssClass += ' ' + bandeau.id.replace( '.', '_') + ' carousel-banner-content selected' cssStyle += ' display:block;' else: cssClass += ' ' + bandeau.id.replace( '.', '_') + ' carousel-banner-content' cssStyle += ' display:none;' if getattr(bkg, 'bannerImageLink', ''): link = str(self.context.absolute_url() + '/resolveuid/' + bkg.bannerImageLink) else: link = None if link: backgrounddiv = '<a style="display:block; height:%spx; width:100%%; background:transparent url(%s) %s %s top;" title="%s" class="%s" href="%s"></a>' % ( height, url, repeat, align, title, cssClass, link) else: backgrounddiv = '<div style="height:%spx; width:100%%; background:transparent url(%s) %s %s top;" title="%s" class="%s"></div>' % ( height, url, repeat, align, title, cssClass) bdict = { 'id': bandeau.id, 'content': backgrounddiv, 'cssClass': cssClass, 'cssStyle': cssStyle, 'url': url, 'link': link, 'align': align, 'repeat': repeat } elif has_dx and IDocument.providedBy(bandeau): bdict['id'] = bandeau.id bdict['content'] = bandeau.text and bandeau.text.raw or '' elif hasattr(bandeau, 'getText') and not bandeau.portal_type in [ 'Topic', 'Collection' ]: try: bdict['id'] = bandeau.id bdict['content'] = bandeau.getText() except: raise ValueError('error with: ' + str(bandeau)) elif bandeau.portal_type == 'Collage': bdict['id'] = bandeau.id bdict['content'] = ObjectView(bandeau, self.request, 'collage_renderer.pt') elif bandeau.portal_type == 'FlashMovie': bdict['id'] = bandeau.id bdict['content'] = ObjectView( bandeau, self.request, 'flashmovie_macro_flashobject.pt') elif bandeau.portal_type == 'Folder': bdict['id'] = bandeau.id bdict['content'] = ObjectView(bandeau, self.request, 'folder_renderer.pt') else: bdict['id'] = bandeau.id bdict['content'] = bandeau() bandeauxList.append(bdict) if not base_ajax_content_load: delattr(self.request, 'ajax_content_load') if not base_ajax_load: delattr(self.request, 'ajax_load') return bandeauxList @view.memoize def bandeauxList(self): return self.getSolgemaBandeaux() def update(self): super(BandeauViewlet, self).update() self.portal_title = self.portal_state.portal_title()
class NewVersionsViewlet(ViewletBase): """ Handle notifications related to new version of Bika LIMS 1) Check pypi for new version 2) Check prefs to see if upgrade steps are required. """ index = ViewPageTemplateFile("templates/new_versions.pt") def get_versions(self): """Configure self.versions, a list of product versions from portal.quickinstaller """ self.versions = {} qi = self.context.portal_quickinstaller for key in qi.keys(): self.versions[key] = qi.getProductVersion(key) def check_new_version(self): """Look for new updates at pypi """ self.current_version = self.versions['bika.lims'] if not self.current_version: self.has_new_version = False return url = "https://pypi.python.org/pypi/bika.lims/json" try: jsonstr = urllib.urlopen(url).read() self.pypi = json.loads(jsonstr) v = self.new_version = self.pypi['info']['version'] self.new_date = \ self.pypi['releases'][v][0]['upload_time'].split('T')[0] except Exception as e: logger.info("Failed to retrieve new version info: %s" % e) v = self.current_version self.new_date = "" self.has_new_version = v > self.current_version def check_new_upgrade_step(self): """Warn about upgrade steps that have not been run. This will override the users choice in settings: un-executed upgrade steps are always alerted. """ qi = self.context.portal_quickinstaller self.info = qi.upgradeInfo('bika.lims') if self.info['installedVersion'] < self.info['newVersion']: self.has_upgrade_step = True else: self.has_upgrade_step = False def check_session(self): """Return False if the session hint claims that we already checked. Return True if the session has no record, or if more than one day has passed since we last checked. """ et = time.time() try: sdm = self.context.session_data_manager except AttributeError: # While testing, the session data manager is not yet instantiated. return False session = sdm.getSessionData(create=True) diff = et - session.get('bika.lims-version-check', et) if diff > 86400 or diff == 0: session['bika.lims-version-check'] = et return True else: return False def render(self): if not self.check_session(): return "" self.get_versions() self.check_new_version() self.check_new_upgrade_step() mtool = getToolByName(self.context, 'portal_membership') member = mtool.getAuthenticatedMember() roles = member.getRoles() allowed = 'LabManager' in roles or 'Manager' in roles if allowed \ and self.context.bika_setup.getShowNewReleasesInfo() \ and self.has_new_version: return self.index() elif allowed and self.has_upgrade_step: return self.index() else: return ""
class PersonalBar(PersonalBarViewlet): index = ViewPageTemplateFile('templates/personalbar_viewlet.pt')
class Viewlet(ContentRelatedItems): """ Viewlet listing the related items of the article """ index = ViewPageTemplateFile('related.pt')
class OrderPublishView(BrowserView): template = ViewPageTemplateFile("templates/order_publish.pt") _products = [] _current_product_index = 0 _publish = False def __init__(self, context, request, publish=False): super(OrderPublishView, self).__init__(context, request) self._publish = publish self._products = [self.context] @property def _DEFAULT_TEMPLATE(self): registry = getUtility(IRegistry) return registry.get('bika.lims.order.default_order_template', 'default.pt') def __call__(self): if self.context.portal_type == 'Order': self._products = [self.context] elif self.context.portal_type == 'OrderFolder' \ and self.request.get('items', ''): uids = self.request.get('items').split(',') uc = getToolByName(self.context, 'uid_catalog') self._products = [obj.getObject() for obj in uc(UID=uids)] else: #Do nothing self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) # Do publish? if self.request.form.get('publish', '0') == '1': self.publishFromPOST() else: return self.template() def showOptions(self): """ Returns true if the options top panel will be displayed in the template """ return self.request.get('pub', '1') == '1' def getOrderTemplate(self): templates_dir = 'templates/sheets' embedt = self.request.form.get('template', self._DEFAULT_TEMPLATE) if embedt.find(':') >= 0: prefix, template = embedt.split(':') templates_dir = queryResourceDirectory('sheets', prefix).directory embedt = template embed = ViewPageTemplateFile(os.path.join(templates_dir, embedt)) reptemplate = "" try: reptemplate = embed(self) except: tbex = traceback.format_exc() arid = self._products[self._current_product_index].id reptemplate = "<div class='error-report'>%s - %s '%s':<pre>%s</pre></div>" % ( arid, _("Unable to load the template"), embedt, tbex) self._nextProduct() return reptemplate def getOrderSheetStyle(self): """ Returns the css style to be used for the current template. If the selected template is 'default.pt', this method will return the content from 'default.css'. If no css file found for the current template, returns empty string """ template = self.request.form.get('template', self._DEFAULT_TEMPLATE) content = '' if template.find(':') >= 0: prefix, template = template.split(':') resource = queryResourceDirectory('sheets', prefix) css = '{0}.css'.format(template[:-3]) if css in resource.listDirectory(): content = resource.readFile(css) else: this_dir = os.path.dirname(os.path.abspath(__file__)) templates_dir = os.path.join(this_dir, 'templates/sheets/') path = '%s/%s.css' % (templates_dir, template[:-3]) with open(path, 'r') as content_file: content = content_file.read() return content def getProducts(self): """ Returns a dict with the order entries """ return self._products def getProductsCount(self): """ Returns the number of product orders to manage """ return len(self._products) def getOrderObj(self): """ Returns the order obj """ return self._products[self._current_product_index] def getOrder(self): """ Returns the dict for the current product """ return self._order_data(self._products[self._current_product_index]) def _nextProduct(self): """ Move to the next product """ if self._current_product_index < len(self._products): self._current_product_index += 1 def _order_data(self, order, excludearuids=[]): """ Creates an order dict, accessible from the view and from each specific template. """ data = { 'obj': order, 'id': order.getId(), 'order_number': order.getOrderNumber(), 'title': order.Title(), 'description': order.Description(), 'supplier_id': order.getSupplierUID(), 'date_dispatched': self.ulocalized_time(order.getDateDispatched(), long_format=1), 'remarks': order.getRemarks(), 'date_published': self.ulocalized_time(DateTime(), long_format=1), 'subtotal': order.getSubtotal(), 'vat_amount': order.getVATAmount(), 'url': order.absolute_url(), 'remarks': to_utf8(order.getRemarks()), 'footer': to_utf8(self.context.bika_setup.getResultFooter()), } data['supplier'] = self._supplier_data(order) # Get the Product List for the Order # print order.order_lineitems items = order.order_lineitems # products = order.aq_parent.objectValues('Product') products = self.context.get_supplier_products() item_list = [] grand_total = 0.00 for item in items: withvat_price = 0.00 prodid = item['Product'] product = [pro for pro in products if pro.getId() == prodid][0] price = float(item['Price']) vat = float(item['VAT']) qty = float(item['Quantity']) withvat_price = price * qty * ((vat / 100) + 1) item_list.append({ 'title': product.Title(), 'description': product.Description(), 'unit': product.getUnit(), 'price': price, 'vat': '%s%%' % vat, 'quantity': qty, 'subtotal': '%.2f' % (price * qty), 'withvat': '%.2f' % (withvat_price) }) grand_total += withvat_price item_list = sorted(item_list, key=itemgetter('title')) data['products'] = item_list data["grandTotal"] = '%.2f' % grand_total return data def _supplier_data(self, order): data = {} supplier = order.aq_parent if supplier: data['obj'] = supplier data['id'] = supplier.id data['title'] = supplier.Title() data['url'] = supplier.absolute_url() data['name'] = to_utf8(supplier.getName()) data['phone'] = to_utf8(supplier.getPhone()) data['fax'] = to_utf8(supplier.getFax()) supplier_address = supplier.getPostalAddress() if supplier_address: _keys = ['address', 'city', 'state', 'zip', 'country'] _list = [ "<div>%s</div>" % supplier_address.get(v) for v in _keys if supplier_address.get(v) ] supplier_address = "".join(_list) else: supplier_address = '' data['address'] = to_utf8(supplier_address) data['email'] = to_utf8(supplier.getEmailAddress()) return data def localise_images(self, htmlreport): """WeasyPrint will attempt to retrieve attachments directly from the URL referenced in the HTML report, which may refer back to a single-threaded (and currently occupied) zeoclient, hanging it. All "attachments" using urls ending with at_download/AttachmentFile must be converted to local files. Returns a list of files which were created, and a modified copy of htmlreport. """ cleanup = [] _htmltext = to_utf8(htmlreport) # first regular image tags for match in re.finditer("""http.*at_download\/AttachmentFile""", _htmltext, re.I): url = match.group() att_path = url.replace(self.portal_url + "/", "") attachment = self.portal.unrestrictedTraverse(att_path) af = attachment.getAttachmentFile() filename = af.filename extension = "." + filename.split(".")[-1] outfile, outfilename = tempfile.mkstemp(suffix=extension) outfile = open(outfilename, 'wb') outfile.write(str(af.data)) outfile.close() _htmltext.replace(url, outfilename) cleanup.append(outfilename) return cleanup, _htmltext def publishFromPOST(self): html = self.request.form.get('html') style = self.request.form.get('style') uid = self.request.form.get('uid') reporthtml = "<html><head>%s</head><body><div id='report'>%s</body></html>" % ( style, html) return self.publishFromHTML(uid, safe_unicode(reporthtml).encode('utf-8')) def publishFromHTML(self, prouid, results_html): uc = getToolByName(self.context, 'uid_catalog') pros = uc(UID=prouid) if not pros or len(pros) != 1: return [] pro = pros[0].getObject() # HTML written to debug file debug_mode = App.config.getConfiguration().debug_mode if debug_mode: tmp_fn = tempfile.mktemp(suffix=".html") logger.debug("Writing HTML for %s to %s" % (pro.Title(), tmp_fn)) open(tmp_fn, "wb").write(results_html) # Create the pdf report (will always be attached to the Order) # we must supply the file ourself so that createPdf leaves it alone. # This version replaces 'attachment' links; probably not required, # so it's repeated below, without these localise_images. # cleanup, results_html_for_pdf = self.localise_images(results_html) # pdf_fn = tempfile.mktemp(suffix=".pdf") # pdf_report = createPdf(htmlreport=results_html_for_pdf, outfile=pdf_fn) # for fn in cleanup: # os.remove(fn) pdf_fn = tempfile.mktemp(suffix=".pdf") pdf_report = createPdf(htmlreport=results_html, outfile=pdf_fn) # PDF written to debug file if debug_mode: logger.debug("Writing PDF for %s to %s" % (pro.Title(), pdf_fn)) else: os.remove(pdf_fn) recipients = [] # Send report to supplier supplier_data = self._supplier_data(pro) title = encode_header(supplier_data.get('title', '')) email = supplier_data.get('email') formatted = formataddr((title, email)) # Create the new mime_msg object mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject(pro) # Edit this to change the From address lab = pro.bika_setup.laboratory mime_msg['From'] = formataddr( (encode_header(lab.getName()), lab.getEmailAddress())) mime_msg.preamble = 'This is a multi-part MIME message.' msg_txt = MIMEText(results_html, _subtype='html') mime_msg.attach(msg_txt) mime_msg['To'] = formatted # Attach the pdf to the email if requested if pdf_report: attachPdf(mime_msg, pdf_report, pdf_fn) msg_string = mime_msg.as_string() # content of outgoing email written to debug file if debug_mode: tmp_fn = tempfile.mktemp(suffix=".email") logger.debug("Writing MIME message for %s to %s" % (pro.Title(), tmp_fn)) open(tmp_fn, "wb").write(msg_string) try: host = getToolByName(pro, 'MailHost') host.send(msg_string, immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg)) pro.setDateDispatched(DateTime()) return [pro] def publish(self): """ Publish the Order. Generates a results pdf file associated, sends an email with the report to the lab manager and sends a notification (usually an email with the PDF attached) to the Supplier's contact and CCs. """ if len(self._products) > 1: published_products = [] for pro in self._products: propub = OrderPublishView(pro, self.request, publish=True) pro = propub.publish() published_products.extend(pro) published_products = [ppro.id for ppro in published_products] return published_products results_html = safe_unicode(self.template()).encode('utf-8') return self.publishFromHTML(results_html) def get_mail_subject(self, ar): """ Returns the email subject """ supplier = ar.aq_parent subject = "Order Details: %s" % (ar.getDateDispatched()) return subject
class FooterManagerViewlet(ViewletBase): index = ViewPageTemplateFile('footerManager.pt')
class TilesView(BrowserView): index = ViewPageTemplateFile("templates/tiles_view.pt") def __call__(self, **kwargs): return self.index()
class PrintFooterManagerViewlet(ViewletBase): index = ViewPageTemplateFile('printfooterManager.pt')
class gwGlobalSectionsViewlet(GlobalSectionsViewlet, viewletBase): grok.name('genweb.globalsections') grok.viewletmanager(IPortalTop) grok.layer(IVilaixTheme) index = ViewPageTemplateFile('viewlets_templates/sections.pt') allowed_section_types = ['Folder', 'Collection', 'Document'] def show_menu(self): return not self.genweb_config( ).treu_menu_horitzontal and self.portal_tabs def menuPrincipal(self): """ returns folders (menu-principal)""" urltool = getToolByName(self.context, 'portal_url') portal_catalog = getToolByName(self.context, 'portal_catalog') # Obtain all folders in first level "published" o "visible" path = urltool.getPortalPath() + '/menu-principal' folders = portal_catalog.searchResults( portal_type=self.allowed_section_types, path=dict(query=path, depth=1), sort_on='getObjPositionInParent') results = [] for fold in folders: if (fold.portal_type in self.allowed_section_types): if fold.exclude_from_nav is not True: results.append( dict(name=fold.Title, url=fold.getURL(), id=fold.getId, description=fold.Description)) return results def menu(self): """ returns subfolders (submenus) for the dropdown in navbar""" urltool = getToolByName(self.context, 'portal_url') portal_catalog = getToolByName(self.context, 'portal_catalog') # Obtain all folders in first level "published" o "visible" path = urltool.getPortalPath() + '/menu-principal' folders = portal_catalog.searchResults( portal_type=self.allowed_section_types, path=dict(query=path, depth=1), sort_on='getObjPositionInParent') subfolders = {} for fold in folders: if (fold.portal_type in self.allowed_section_types): if fold.exclude_from_nav is not True: subfolders[fold.getId] = self.SubMenu(fold.getPath()) return subfolders def SubMenu(self, path): """ Get subfolders of current folder for create submenu""" path = path portal_catalog = getToolByName(self.context, 'portal_catalog') subfolders = portal_catalog.searchResults( portal_type=self.allowed_section_types, path=dict(query=path, depth=1), sort_on='getObjPositionInParent') results = [] for fold in subfolders: if (fold.portal_type in self.allowed_section_types): if fold.exclude_from_nav is not True: results.append( dict(name=fold.Title, url=fold.getURL(), id=fold.getId, description=fold.Description)) return results
class LogoViewlet(BandeauViewlet): index = ViewPageTemplateFile('logo.pt') marker = 'Solgema.EnvironmentViewlets.interfaces.ILogoMarker' def getItems(self): catalog = getToolByName(self.context, 'portal_catalog') context_state = getMultiAdapter((self.context, self.request), name=u'plone_context_state') folder = context_state.folder() folder_path = '/'.join(folder.getPhysicalPath()) itemsBrains = catalog.searchResults(object_provides=self.marker, sort_on='getObjPositionInParent') items = [ a for a in itemsBrains if '/'.join(a.getPath().split('/')[0:-1]) in folder_path ] items_paths = [a.getPath() for a in items] items.sort(lambda x, y: cmp(len(x.getPath().split('/')), len(y.getPath().split('/')))) items.reverse() if items: return items[0] return [] def logo(self): logo = self.getItems() if not logo: return None if getattr(logo, 'image_sizes', None): sizes = logo.image_sizes['base'] return { 'id': logo.getId, 'content': '<img width="%s" height="%s" title="%s" alt="%s" src="%s"/>' % (str(sizes[0]), str( sizes[1]), logo.Description, logo.Title, logo.getPath()) } else: logo = logo.getObject() if has_dx and IImage.providedBy(logo): return { 'id': logo.id, 'content': ImageScaling(logo, self.request).tag(title=logo.Description()) } elif hasattr(logo, 'tag'): return { 'id': logo.id, 'content': logo.tag(title=logo.Description()) } elif has_dx and IDocument.providedBy(logo): return { 'id': logo.id, 'content': logo.text and logo.text.raw or '' } elif hasattr(logo, 'getText'): return {'id': logo.id, 'content': logo.getText()} elif logo.portal_type == 'FlashMovie': return { 'id': logo.id, 'content': ObjectView(logo, self.request, 'flashmovie_macro_flashobject.pt') } return None @view.memoize def wholeLogoHTML(self): logo = self.logo() if not logo: return '' link = '/Members' in self.request.get( 'URL') and self.site_url or self.navigation_root_url return '<a id="portal-logo" href="%s" accesskey="1" class="visualNoPrint">%s</a>' % ( link, logo['content'])
class PersonalOverview(TabbedView): """The personal overview view show all documents and dossier where the actual user is the responsible. """ template = ViewPageTemplateFile("personal_overview.pt") default_tabs = [ { 'id': 'mydossiers', 'icon': None, 'url': '#', 'class': None }, { 'id': 'mydocuments-proxy', 'icon': None, 'url': '#', 'class': None }, { 'id': 'mytasks', 'icon': None, 'url': '#', 'class': None }, { 'id': 'myissuedtasks', 'icon': None, 'url': '#', 'class': None }, ] admin_tabs = [ { 'id': 'alltasks', 'icon': None, 'url': '#', 'class': None }, { 'id': 'allissuedtasks', 'icon': None, 'url': '#', 'class': None }, ] def __call__(self): """If user is not allowed to view PersonalOverview, redirect him to the repository root, otherwise behave like always. """ user = AccessControl.getSecurityManager().getUser() if user == AccessControl.SecurityManagement.SpecialUsers.nobody: raise Unauthorized if not self.user_is_allowed_to_view(): catalog = getToolByName(self.context, 'portal_catalog') repos = catalog(portal_type='opengever.repository.repositoryroot') repo_url = repos[0].getURL() return self.request.RESPONSE.redirect(repo_url) else: if is_open_task_report_allowed(): # Somehow if only the pdf-open-task-report action is available # plone's `showEditableBorder` assumes that the edit-bar should # be hidden. # So we have to force the edit bar if the user can generate an # open task report. api.portal.get().REQUEST.set('enable_border', True) return self.template(self) def personal_overview_title(self): current_user = ogds_service().fetch_current_user() if current_user: user_name = current_user.label(with_principal=False) else: user_name = api.user.get_current().getUserName() return _('personal_overview_title', default='Personal Overview: ${user_name}', mapping=dict(user_name=user_name)) def _is_user_admin(self): m_tool = getToolByName(self.context, 'portal_membership') member = m_tool.getAuthenticatedMember() if member: if member.has_role('Administrator') \ or member.has_role('Manager'): return True return False @property def notification_tabs(self): tabs = [] if is_activity_feature_enabled(): tabs.append({ 'id': 'mynotifications', 'title': _('label_my_notifications', default=u'My notifications'), 'icon': None, 'url': '#', 'class': None }) return tabs @property def meeting_tabs(self): tabs = [] if is_meeting_feature_enabled(): tabs.append({ 'id': 'myproposals', 'title': _('label_my_proposals', default=u'My proposals'), 'icon': None, 'url': '#', 'class': None }) return tabs def get_tabs(self): tabs = self.default_tabs + self.meeting_tabs + self.notification_tabs if self.is_user_allowed_to_view_additional_tabs(): tabs += self.admin_tabs return tabs def is_user_allowed_to_view_additional_tabs(self): """The additional tabs Alltasks and AllIssuedTasks are only shown to adminsitrators and users of the current inbox group.""" inbox = get_current_org_unit().inbox() current_user = ogds_service().fetch_current_user() return current_user in inbox.assigned_users() or self._is_user_admin() @memoize_contextless def user_is_allowed_to_view(self): """Returns True if the current client is one of the user's home clients or an administrator and he therefore is allowed to view the PersonalOverview, False otherwise. """ try: current_user = ogds_service().fetch_current_user() if get_current_admin_unit().is_user_assigned(current_user): return True elif self._is_user_admin(): return True except OperationalError as e: LOG.exception(e) return False
def __init__(self, context, request, templatename): self.context = context self.request = request pt = ViewPageTemplateFile(templatename) self.template = BoundPageTemplate(pt, self)
class CollectionTile(PersistentCoverTile): index = ViewPageTemplateFile("templates/collection.pt") is_configurable = True is_editable = False configured_fields = [] def get_title(self): return self.data['title'] def results(self): self.configured_fields = self.get_configured_fields() size_conf = [ i for i in self.configured_fields if i['id'] == 'number_to_show' ] if size_conf and 'size' in size_conf[0].keys(): size = int(size_conf[0]['size']) else: size = 4 uuid = self.data.get('uuid', None) if uuid is not None: obj = uuidToObject(uuid) return obj.results(batch=False)[:size] else: return [] def is_empty(self): return self.data.get('uuid', None) is None def populate_with_object(self, obj): super(CollectionTile, self).populate_with_object(obj) # check permission if obj.portal_type in self.accepted_ct(): title = obj.Title() description = obj.Description() uuid = IUUID(obj) data_mgr = ITileDataManager(self) data_mgr.set({ 'title': title, 'description': description, 'uuid': uuid, }) def accepted_ct(self): """ Return a list of content types accepted by the tile. """ return ['Collection'] # TODO: add deprecation warning def has_data(self): return not self.is_empty() def get_configured_fields(self): # Override this method, since we are not storing anything # in the fields, we just use them for configuration tileType = queryUtility(ITileType, name=self.__name__) conf = self.get_tile_configuration() fields = getFieldsInOrder(tileType.schema) results = [] for name, obj in fields: field = {'id': name, 'title': obj.title} if name in conf: field_conf = conf[name] if ('visibility' in field_conf and field_conf['visibility'] == u'off'): # If the field was configured to be invisible, then just # ignore it continue if 'htmltag' in field_conf: # If this field has the capability to change its html tag # render, save it here field['htmltag'] = field_conf['htmltag'] if 'imgsize' in field_conf: field['scale'] = field_conf['imgsize'] if 'size' in field_conf: field['size'] = field_conf['size'] results.append(field) return results
class ItemDetails(BrowserView): """Contains backend code for expanded item """ template = ViewPageTemplateFile('itemdetails.pt') def __call__(self): uid = None itemindex = 0 if self.request.form.has_key('uid'): uid = self.request.form['uid'] if self.request.form.has_key('itemindex'): itemindex = self.request.form['itemindex'] if uid is not None: query = {'UID': uid} pdt = getToolByName(self.context, 'portal_discussion') cat = getToolByName(self.context, 'uid_catalog') resbrains = cat.searchResults(query) if len(resbrains) == 1: item = resbrains[0] contobj = item.getObject() fullpath = item.getPath() splitpath = fullpath.split('/')[:-1] prettypath = '/' + '/'.join(splitpath) URLsuffix = getListingTemplateForContextParent(item) pathlink = self.context.portal_url( ) + prettypath + '/' + URLsuffix pathtitle = prettypath lasttimestamp = DateTime().timeTime() lastcommentid = '0' showxmorelink = True commentscount = 0 xmorecomments = 0 replydict = [] isDiscussable = contobj.isDiscussable() canReply = canreply(contobj) jsondata = getjsondata(self.context, replydict, self.context.portal_url(), contobj.absolute_url()) if isDiscussable: dobj = pdt.getDiscussionFor(contobj) alldiscussions = dobj.objectValues() alldiscussions.sort( lambda x, y: cmp(x.modified(), y.modified()), reverse=True) maxdispcomments = get_displaycountforlist() lastxdiscussions = alldiscussions[:maxdispcomments] commentscount = dobj.replyCount(contobj) if commentscount > maxdispcomments: showxmorelink = True xmorecomments = commentscount - maxdispcomments elif commentscount > 0 and commentscount <= maxdispcomments: showxmorelink = False xmorecomments = 0 else: showxmorelink = True commentscount = 0 xmorecomments = 0 if len(alldiscussions) > 0: lasttimestamp = alldiscussions[0].modified().timeTime() lastcommentid = alldiscussions[0].id lastxdiscussions.sort( lambda x, y: cmp(x.modified(), y.modified())) for eachdisc in lastxdiscussions: reply = dobj.getReply(eachdisc.id) if reply <> None: replydict.append({ 'depth': 0, 'object': reply, 'showoutput': True }) other_data = { 'view_type': 'listview', 'canreply': str(canReply) } jsondata = getjsondata(self.context, replydict, self.context.portal_url(), contobj.absolute_url(), other_data) return self.template(item_type=contobj.portal_type, item_type_title=contobj.Type(), item=item, pathlink=pathlink, pathtitle=pathtitle, contobj=contobj, showxmorelink=showxmorelink, xmorecomments=xmorecomments, allowdiscussion=isDiscussable, usercanreply=canReply, uid=uid, reply_dict=jsondata, title=contobj.Title(), commentcount=commentscount, lasttimestamp=lasttimestamp, lastcommentid=lastcommentid, itemindex=itemindex, view_type='listview') else: raise NotFound('Object not found for request', 'Not found', self.request) else: raise NotFound('uid is not passed', 'Not found', self.request)