Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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(),
        }
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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")
Ejemplo n.º 5
0
 def setFromFilename(self, filename, _prefix=None):
     self._template = ViewPageTemplateFile(filename, _prefix)
Ejemplo n.º 6
0
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 ""
Ejemplo n.º 7
0
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()
        }
Ejemplo n.º 9
0
class Renderer(BasePortletRenderer, BaseSearchView):
    render = ViewPageTemplateFile("collectionsearch.pt")
Ejemplo n.º 10
0
class BaseForm(EditForm):
    """
    """

    template = ViewPageTemplateFile('settings_edit.pt')
Ejemplo n.º 11
0
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()
            }
Ejemplo n.º 12
0
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()
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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')
Ejemplo n.º 16
0
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()
Ejemplo n.º 18
0
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 ""
Ejemplo n.º 19
0
class PersonalBar(PersonalBarViewlet):
    index = ViewPageTemplateFile('templates/personalbar_viewlet.pt')
Ejemplo n.º 20
0
class Viewlet(ContentRelatedItems):
    """ Viewlet listing the related items of the article
    """
    index = ViewPageTemplateFile('related.pt')
Ejemplo n.º 21
0
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')
Ejemplo n.º 25
0
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)
Ejemplo n.º 29
0
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
Ejemplo n.º 30
0
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)