def _jumpToStateFor(self, ob, state_id, wf_id=None, *_, **__): """Inspired from doActionFor. This is public method to allow passing meta transition (Jump form any state to another in same workflow) """ from Products.ERP5.InteractionWorkflow import InteractionWorkflowDefinition from Products.ERP5Type.Core.InteractionWorkflow import InteractionWorkflow workflow_list = self.getWorkflowValueListFor(ob.getPortalType()) if wf_id is None: if not workflow_list: raise WorkflowException('No workflows found.') found = False for workflow in workflow_list: if not isinstance(workflow, (InteractionWorkflowDefinition, InteractionWorkflow,)) and \ state_id in workflow.getStateReferenceList(): found = True break if not found: raise WorkflowException('No workflow provides the destination state %r'\ % state_id) else: workflow = self.getWorkflowById(wf_id) if workflow is None: raise WorkflowException( 'Requested workflow definition not found.') workflow._executeMetaTransition(ob, state_id)
def _jumpToStateFor(self, ob, state_id, wf_id=None, *args, **kw): """Inspired from doActionFor. This is public method to allow passing meta transition (Jump form any state to another in same workflow) """ from Products.ERP5.InteractionWorkflow import InteractionWorkflowDefinition workflow_list = self.getWorkflowsFor(ob) if wf_id is None: if not workflow_list: raise WorkflowException('No workflows found.') found = False for workflow in workflow_list: if not isinstance(workflow, InteractionWorkflowDefinition) and\ state_id in workflow.states._mapping: found = True break if not found: raise WorkflowException('No workflow provides the destination state %r'\ % state_id) else: workflow = self.getWorkflowById(wf_id) if workflow is None: raise WorkflowException('Requested workflow definition not found.') workflow._executeMetaTransition(ob, state_id)
def doActionFor(self, ob, action, wf_id=None, *args, **kw): workflow_id = wf_id workflow_list = self.getWorkflowValueListFor(ob.getPortalType()) if workflow_id is None: if not workflow_list: raise WorkflowException(Message(u'No workflows found.')) for workflow in workflow_list: is_action_supported = workflow.isActionSupported( ob, action, **kw) if is_action_supported: kw['is_action_supported'] = is_action_supported break else: raise WorkflowException( Message(u"No workflow provides the '${action_id}' action.", mapping={'action_id': action})) else: workflow = self.getWorkflowById(workflow_id) if workflow is None: raise WorkflowException( Message(u'Requested workflow not found.')) return self._invokeWithNotification(workflow_list, ob, action, workflow.doActionFor, (ob, action) + tuple(args), kw)
def doActionFor(self, ob, action, wf_id=None, *args, **kw): """ Perform the given workflow action on 'ob'. """ wfs = self.getWorkflowsFor(ob) if wfs is None: wfs = () if wf_id is None: if not wfs: raise WorkflowException(_(u'No workflows found.')) found = 0 for wf in wfs: if wf.isActionSupported(ob, action, **kw): found = 1 break if not found: msg = _(u"No workflow provides the '${action_id}' action.", mapping={'action_id': action}) raise WorkflowException(msg) else: wf = self.getWorkflowById(wf_id) if wf is None: raise WorkflowException( _(u'Requested workflow definition not found.')) return self._invokeWithNotification(wfs, ob, action, wf.doActionFor, (ob, action) + args, kw)
def getInfoFor(self, ob, name, default=_marker, wf_id=None, *args, **kw): """ Get the given bit of workflow information for the object. """ if wf_id is None: wfs = self.getWorkflowsFor(ob) if wfs is None: if default is _marker: raise WorkflowException(_(u'No workflows found.')) else: return default found = 0 for wf in wfs: if wf.isInfoSupported(ob, name): found = 1 break if not found: if default is _marker: msg = _(u"No workflow provides '${name}' information.", mapping={'name': name}) raise WorkflowException(msg) else: return default else: wf = self.getWorkflowById(wf_id) if wf is None: if default is _marker: raise WorkflowException( _(u'Requested workflow definition not found.')) else: return default res = wf.getInfoFor(ob, name, default, *args, **kw) if res is _marker: msg = _(u'Could not get info: ${name}', mapping={'name': name}) raise WorkflowException(msg) return res
def canDoActionFor(self, ob, action, wf_id=None, guard_kw={}): """ Check we can perform the given workflow action on 'ob'. """ if wf_id is None: workflow_list = self.getWorkflowsFor(ob) or () else: workflow = self.getWorkflowById(wf_id) if workflow: workflow_list = (workflow, ) else: workflow_list = () for workflow in workflow_list: state_definition = workflow._getWorkflowStateOf(ob) if state_definition is not None: if action in state_definition.transitions: transition_definition = workflow.transitions.get(action, None) if transition_definition is not None and \ transition_definition.trigger_type == TRIGGER_USER_ACTION: return workflow._checkTransitionGuard( transition_definition, ob, **guard_kw) raise WorkflowException( _(u"No workflow provides the '${action_id}' action.", mapping={'action_id': action}))
def on_trash_transitioned(obj, event): api.portal.show_message( 'You are not allowed to transition an item in the recycling bin.', request=getRequest(), type='error') raise WorkflowException( 'You are not allowed to transition an item in the recycling bin.')
def before_publish_check(state_change): obj = state_change.object if (obj.meta_type == 'Metadata'): xml = obj.getXml() standard = obj.getStandard() valid = standard.validateXML(xml)[0] if (not valid): raise WorkflowException( "XML is not valid. Use 'Edit Metadata' to fix it first.")
def doActionFor(self, ob, action, comment='', **kw): ''' Allows the user to request a workflow action. This method must perform its own security checks. ''' kw['comment'] = comment sdef = self._getWorkflowStateOf(ob) if sdef is None: raise WorkflowException(_(u'Object is in an undefined state.')) if action not in sdef.transitions: raise Unauthorized(action) tdef = self.transitions.get(action, None) if tdef is None or tdef.trigger_type != TRIGGER_USER_ACTION: msg = _(u"Transition '${action_id}' is not triggered by a user " u"action.", mapping={'action_id': action}) raise WorkflowException(msg) if not self._checkTransitionGuard(tdef, ob, **kw): raise Unauthorized(action) self._changeStateOf(ob, tdef, kw)
def send_mail(self, sender, receiver, subject="", body=""): """Send email from sender to receiver """ mime_msg = MIMEMultipart('related') mime_msg['Subject'] = subject mime_msg['From'] = sender mime_msg['To'] = receiver msg_txt = MIMEText(body, 'plain') mime_msg.attach(msg_txt) try: host = getToolByName(self, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg))
def emailInvoice(self, templateHTML, to=[]): """ Send the invoice via email. :param templateHTML: The invoice template in HTML, ready to be send. :param to: A list with the addresses to send the invoice. """ ar = self.aq_parent # SMTP errors are silently ignored if server is in debug mode debug_mode = App.config.getConfiguration().debug_mode # Useful variables lab = ar.bika_setup.laboratory # Compose and send email. subject = t(_('Invoice')) + ' ' + ar.getInvoice().getId() mime_msg = MIMEMultipart('related') mime_msg['Subject'] = subject mime_msg['From'] = formataddr( (encode_header(lab.getName()), lab.getEmailAddress())) mime_msg.preamble = 'This is a multi-part MIME message.' msg_txt_t = MIMEText(templateHTML.encode('utf-8'), _subtype='html') mime_msg.attach(msg_txt_t) # Build the responsible's addresses mngrs = ar.getResponsible() for mngrid in mngrs['ids']: name = mngrs['dict'][mngrid].get('name', '') email = mngrs['dict'][mngrid].get('email', '') if (email != ''): to.append(formataddr((encode_header(name), email))) # Build the client's address caddress = ar.aq_parent.getEmailAddress() cname = ar.aq_parent.getName() if (caddress != ''): to.append(formataddr((encode_header(cname), caddress))) if len(to) > 0: # Send the emails mime_msg['To'] = ','.join(to) try: host = getToolByName(ar, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPServerDisconnected as msg: pass if not debug_mode: raise SMTPServerDisconnected(msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg))
def DCWorkflowDefinition_executeTransition(self, ob, tdef=None, kwargs=None): ''' Private method. Puts object in a new state. ''' sci = None econtext = None moved_exc = None validation_exc = None # Figure out the old and new states. old_state = self._getWorkflowStateOf(ob, True) if tdef is None: new_state = self.initial_state if not new_state: # Do nothing if there is no initial state. We may want to create # workflows with no state at all, only for worklists. return former_status = {} else: new_state = tdef.new_state_id or old_state former_status = self._getStatusOf(ob) old_sdef = self.states[old_state] try: new_sdef = self.states[new_state] except KeyError: raise WorkflowException('Destination state undefined: ' + new_state) # Execute the "before" script. before_script_success = 1 if tdef is not None and tdef.script_name: script = self.scripts[tdef.script_name] # Pass lots of info to the script in a single parameter. sci = StateChangeInfo(ob, self, former_status, tdef, old_sdef, new_sdef, kwargs) try: #LOG('_executeTransition', 0, "script = %s, sci = %s" % (repr(script), repr(sci))) script(sci) # May throw an exception. except ValidationFailed, validation_exc: before_script_success = 0 before_script_error_message = deepcopy(validation_exc.msg) validation_exc_traceback = sys.exc_traceback except ObjectMoved, moved_exc: ob = moved_exc.getNewObject()
def _executeTransition(self, ob, tdef=None, kwargs=None): ''' Private method. Puts object in a new state. ''' sci = None econtext = None moved_exc = None # Figure out the old and new states. old_sdef = self._getWorkflowStateOf(ob) old_state = old_sdef.getId() if tdef is None: new_state = self.initial_state former_status = {} else: new_state = tdef.new_state_id if not new_state: # Stay in same state. new_state = old_state former_status = self._getStatusOf(ob) new_sdef = self.states.get(new_state, None) if new_sdef is None: msg = _(u'Destination state undefined: ${state_id}', mapping={'state_id': new_state}) raise WorkflowException(msg) # Fire "before" event notify( BeforeTransitionEvent(ob, self, old_sdef, new_sdef, tdef, former_status, kwargs)) # Execute the "before" script. if tdef is not None and tdef.script_name: script = self.scripts[tdef.script_name] # Pass lots of info to the script in a single parameter. sci = StateChangeInfo(ob, self, former_status, tdef, old_sdef, new_sdef, kwargs) try: script(sci) # May throw an exception. except ObjectMoved, moved_exc: ob = moved_exc.getNewObject()
def changeWorkflowState(content, wf_id, state_id, **kw): """Change the workflow state of an object @param content: Content obj which state will be changed @param state_id: name of the state to put on content @param kw: change the values of same name of the state mapping @return: True if succeed. Otherwise, False """ portal_workflow = api.get_tool("portal_workflow") workflow = portal_workflow.getWorkflowById(wf_id) if not workflow: logger.error("%s: Cannot find workflow id %s" % (content, wf_id)) return False wf_state = { 'action': kw.get("action", None), 'actor': kw.get("actor", api.get_current_user().id), 'comments': "Setting state to %s" % state_id, 'review_state': state_id, 'time': DateTime() } # Get old and new state info old_state = workflow._getWorkflowStateOf(content) new_state = workflow.states.get(state_id, None) if new_state is None: raise WorkflowException( "Destination state undefined: {}".format(state_id)) # Change status and update permissions portal_workflow.setStatusOf(wf_id, content, wf_state) workflow.updateRoleMappingsFor(content) # Notify the object has been transitioned notify( AfterTransitionEvent(content, workflow, old_state, new_state, None, wf_state, None)) # Map changes to catalog indexes = ["allowedRolesAndUsers", "review_state", "is_active"] content.reindexObject(idxs=indexes) return True
def sendEmail(self): added = [] to = '' for analysis in self.analyses: department = analysis.getService().getDepartment() if department is None: continue department_id = department.UID() if department_id in added: continue added.append(department_id) manager = department.getManager() if manager is None: continue manager_id = manager.UID() if manager_id not in added and manager.getEmailAddress(): added.append(manager_id) name = safe_unicode(manager.getFullname()).encode('utf-8') email = safe_unicode(manager.getEmailAddress()).encode('utf-8') to = '%s, %s' % (to, formataddr((encode_header(name), email))) html = safe_unicode(self.template()).encode('utf-8') lab = self.context.bika_setup.laboratory mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.title mime_msg['From'] = formataddr( (encode_header(lab.getName()), lab.getEmailAddress())) mime_msg['To'] = to mime_msg.preamble = 'This is a multi-part MIME message.' msg_txt = MIMEText(html, _subtype='html') mime_msg.attach(msg_txt) # Send the email try: host = getToolByName(self.context, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPServerDisconnected as msg: raise SMTPServerDisconnected(msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg))
def publishFromHTML(self, aruid, results_html): # The AR can be published only and only if allowed uc = getToolByName(self.context, 'uid_catalog') #ars = uc(UID=aruid) ars = [p.getObject() for p in uc(UID=aruid)] if not ars or len(ars) != 1: return [] ar = ars[0] wf = getToolByName(ar, 'portal_workflow') allowed_states = ['verified', 'published'] # Publish/Republish allowed? if wf.getInfoFor(ar, 'review_state') not in allowed_states: # Pre-publish allowed? if not ar.getAnalyses(review_state=allowed_states): return [] # 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" % (ar.Title(), tmp_fn)) open(tmp_fn, "wb").write(results_html) # Create the pdf report (will always be attached to the AR) # we must supply the file ourself so that createPdf leaves it alone. 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" % (ar.Title(), pdf_fn)) else: os.remove(pdf_fn) recipients = [] contact = ar.getContact() lab = ar.bika_setup.laboratory # BIKA Cannabis hack. Create the CSV they desire here now #csvdata = self.create_cannabis_csv(ars) csvdata = self.create_metrc_csv(ars) pdf_fn = to_utf8(ar.getRequestID()) if pdf_report: if contact: recipients = [{ 'UID': contact.UID(), 'Username': to_utf8(contact.getUsername()), 'Fullname': to_utf8(contact.getFullname()), 'EmailAddress': to_utf8(contact.getEmailAddress()), 'PublicationModes': contact.getPublicationPreference() }] reportid = ar.generateUniqueId('ARReport') report = _createObjectByType("ARReport", ar, reportid) report.edit(AnalysisRequest=ar.UID(), Pdf=pdf_report, CSV=csvdata, Html=results_html, Recipients=recipients) report.unmarkCreationFlag() renameAfterCreation(report) # Set blob properties for fields containing file data fld = report.getField('Pdf') fld.get(report).setFilename(pdf_fn + ".pdf") fld.get(report).setContentType('application/pdf') fld = report.getField('CSV') fld.get(report).setFilename(pdf_fn + ".csv") fld.get(report).setContentType('text/csv') # Set status to prepublished/published/republished status = wf.getInfoFor(ar, 'review_state') transitions = {'verified': 'publish', 'published': 'republish'} transition = transitions.get(status, 'prepublish') try: wf.doActionFor(ar, transition) except WorkflowException: pass # compose and send email. # The managers of the departments for which the current AR has # at least one AS must receive always the pdf report by email. # https://github.com/bikalabs/Bika-LIMS/issues/1028 mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject(ar)[0] 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) to = [] #mngrs = ar.getResponsible() #for mngrid in mngrs['ids']: # name = mngrs['dict'][mngrid].get('name', '') # email = mngrs['dict'][mngrid].get('email', '') # if (email != ''): # to.append(formataddr((encode_header(name), email))) #if len(to) > 0: # # Send the email to the managers # mime_msg['To'] = ','.join(to) # attachPdf(mime_msg, pdf_report, pdf_fn) # # BIKA Cannabis hack. Create the CSV they desire here now # fn = pdf_fn # attachCSV(mime_msg,csvdata,fn) # try: # host = getToolByName(ar, 'MailHost') # host.send(mime_msg.as_string(), immediate=True) # except SMTPServerDisconnected as msg: # logger.warn("SMTPServerDisconnected: %s." % msg) # except SMTPRecipientsRefused as msg: # raise WorkflowException(str(msg)) # Send report to recipients recips = self.get_recipients(ar) for recip in recips: if 'email' not in recip.get('pubpref', []) \ or not recip.get('email', ''): continue title = encode_header(recip.get('title', '')) email = recip.get('email') formatted = formataddr((title, email)) # Create the new mime_msg object, cause the previous one # has the pdf already attached mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject(ar)[0] 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 and 'pdf' in recip.get('pubpref'): attachPdf(mime_msg, pdf_report, pdf_fn) # BIKA Cannabis hack. Create the CSV they desire here now fn = pdf_fn attachCSV(mime_msg, csvdata, fn) # For now, I will simply ignore mail send under test. if hasattr(self.portal, 'robotframework'): continue 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" % (ar.Title(), tmp_fn)) open(tmp_fn, "wb").write(msg_string) try: host = getToolByName(ar, 'MailHost') host.send(msg_string, immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg)) # Save file on the filesystem folder = os.environ.get('COAs_FOLDER', '') if len(folder) != 0: client_path = '{}/{}/'.format(folder, ar.getClientID()) if not os.path.exists(client_path): os.makedirs(client_path) today = self.ulocalized_time(DateTime(), long_format=0) today_path = '{}{}/'.format(client_path, today) if not os.path.exists(today_path): os.makedirs(today_path) fname = '{}{}.pdf'.format(today_path, pdf_fn) f = open(fname, 'w') f.write(pdf_report) f.close() csvname = '{}{}.csv'.format(today_path, pdf_fn) fcsv = open(csvname, 'w') fcsv.write(csvdata) fcsv.close() return [ar]
# this script allows to create a new related event by causality for # the current event from DateTime import DateTime from Products.CMFCore.WorkflowCore import WorkflowException date = DateTime() portal = context.getPortalObject() if portal_type not in portal.event_module.getVisibleAllowedContentTypeList(): raise WorkflowException("You Don't Have Permission to Add New Event") # Create the draft Event portal.event_module.newContent( portal_type=portal_type, title=title, description=description, start_date=date, source=context.getDefaultDestination(), destination=context.getDefaultSource(), causality=context.getRelativeUrl(), follow_up=context.getFollowUp(), )
def publishFromHTML(self, ar_uids, results_html): """ar_uids can be a single UID or a list of AR uids. The resulting ARs will be published together (ie, sent as a single outbound email) and the entire report will be saved in each AR's published-results tab. """ debug_mode = App.config.getConfiguration().debug_mode wf = getToolByName(self.context, 'portal_workflow') # The AR can be published only and only if allowed uc = getToolByName(self.context, 'uid_catalog') ars = [p.getObject() for p in uc(UID=ar_uids)] if not ars: return [] results_html = self.localize_images(results_html) # Create the pdf report for the supplied HTML. open('/tmp/2499.html', 'w').write(results_html) pdf_report = createPdf(results_html, False) # PDF written to debug file? if debug_mode: pdf_fn = tempfile.mktemp(suffix=".pdf") logger.info("Writing PDF for {} to {}".format( ', '.join([ar.Title() for ar in ars]), pdf_fn)) open(pdf_fn, 'wb').write(pdf_report) for ar in ars: # Generate in each relevant AR, a new ARReport reportid = ar.generateUniqueId('ARReport') report = _createObjectByType("ARReport", ar, reportid) report.edit( AnalysisRequest=ar.UID(), Pdf=pdf_report, Html=results_html, ) report.unmarkCreationFlag() renameAfterCreation(report) # Modify the workflow state of each AR that's been published status = wf.getInfoFor(ar, 'review_state') transitions = {'verified': 'publish', 'published': 'republish'} transition = transitions.get(status, 'prepublish') try: wf.doActionFor(ar, transition) except WorkflowException: pass # compose and send email. # The managers of the departments for which the current AR has # at least one AS must receive always the pdf report by email. # https://github.com/bikalabs/Bika-LIMS/issues/1028 lab = ars[0].bika_setup.laboratory mime_msg = MIMEMultipart('related') mime_msg['Subject'] = "Published results for %s" % \ ",".join([ar.Title() for ar in ars]) 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) to = [] to_emails = [] mngrs = [] for ar in ars: resp = ar.getResponsible() if 'dict' in resp and resp['dict']: for mngrid, mngr in resp['dict'].items(): if mngr['email'] not in [m['email'] for m in mngrs]: mngrs.append(mngr) for mngr in mngrs: name = mngr['name'] email = mngr['email'] to.append(formataddr((encode_header(name), email))) # Send report to recipients for ar in ars: recips = self.get_recipients(ar) for recip in recips: if 'email' not in recip.get('pubpref', []) \ or not recip.get('email', ''): continue title = encode_header(recip.get('title', '')) email = recip.get('email') if email not in to_emails: to.append(formataddr((title, email))) to_emails.append(email) # Create the new mime_msg object, cause the previous one # has the pdf already attached mime_msg = MIMEMultipart('related') mime_msg['Subject'] = "Published results for %s" % \ ",".join([ar.Title() for ar in ars]) 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'] = ",".join(to) # Attach the pdf to the email fn = "_".join([ar.Title() for ar in ars]) attachPdf(mime_msg, pdf_report, fn) # ALS hack. Create the CSV they desire here now csvdata = self.create_als_csv(ars) # Attach to email part = MIMEBase('text', "csv") fn = self.current_certificate_number() part.add_header('Content-Disposition', 'attachment; filename="{}.csv"'.format(fn)) part.set_payload(csvdata) mime_msg.attach(part) msg_string = mime_msg.as_string() try: host = getToolByName(ars[0], 'MailHost') host.send(msg_string, immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg)) return ars
def publishFromHTML(self, aruid, results_html): # The AR can be published only and only if allowed uc = getToolByName(self.context, 'uid_catalog') ars = uc(UID=aruid) if not ars or len(ars) != 1: return [] ar = ars[0].getObject(); wf = getToolByName(ar, 'portal_workflow') allowed_states = ['verified', 'published'] # Publish/Republish allowed? if wf.getInfoFor(ar, 'review_state') not in allowed_states: # Pre-publish allowed? if not ar.getAnalyses(review_state=allowed_states): return [] # 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" % (ar.Title(), tmp_fn)) open(tmp_fn, "wb").write(results_html) # Create the pdf report (will always be attached to the AR) # we must supply the file ourself so that createPdf leaves it alone. 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" % (ar.Title(), pdf_fn)) else: os.remove(pdf_fn) recipients = [] contact = ar.getContact() lab = ar.bika_setup.laboratory if pdf_report: if contact: recipients = [{ 'UID': contact.UID(), 'Username': to_utf8(contact.getUsername()), 'Fullname': to_utf8(contact.getFullname()), 'EmailAddress': to_utf8(contact.getEmailAddress()), 'PublicationModes': contact.getPublicationPreference() }] reportid = ar.generateUniqueId('ARReport') report = _createObjectByType("ARReport", ar, reportid) report.edit( AnalysisRequest=ar.UID(), Pdf=pdf_report, Html=results_html, Recipients=recipients ) report.unmarkCreationFlag() renameAfterCreation(report) # Set status to prepublished/published/republished status = wf.getInfoFor(ar, 'review_state') transitions = {'verified': 'publish', 'published' : 'republish'} transition = transitions.get(status, 'prepublish') try: wf.doActionFor(ar, transition) except WorkflowException: pass # compose and send email. # The managers of the departments for which the current AR has # at least one AS must receive always the pdf report by email. # https://github.com/bikalabs/Bika-LIMS/issues/1028 mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject(ar)[0] 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) to = [] mngrs = ar.getResponsible() for mngrid in mngrs['ids']: name = mngrs['dict'][mngrid].get('name', '') email = mngrs['dict'][mngrid].get('email', '') if (email != ''): to.append(formataddr((encode_header(name), email))) if len(to) > 0: # Send the email to the managers mime_msg['To'] = ','.join(to) attachPdf(mime_msg, pdf_report, ar.id) try: host = getToolByName(ar, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg)) # Send report to recipients recips = self.get_recipients(ar) for recip in recips: if 'email' not in recip.get('pubpref', []) \ or not recip.get('email', ''): continue title = encode_header(recip.get('title', '')) email = recip.get('email') formatted = formataddr((title, email)) # Create the new mime_msg object, cause the previous one # has the pdf already attached mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject(ar)[0] 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 and 'pdf' in recip.get('pubpref'): attachPdf(mime_msg, pdf_report, ar.id) # For now, I will simply ignore mail send under test. if hasattr(self.portal, 'robotframework'): continue 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" % (ar.Title(), tmp_fn)) open(tmp_fn, "wb").write(msg_string) try: host = getToolByName(ar, 'MailHost') host.send(msg_string, immediate=True) except SMTPServerDisconnected as msg: logger.warn("SMTPServerDisconnected: %s." % msg) except SMTPRecipientsRefused as msg: raise WorkflowException(str(msg)) return [ar]
def __call__(self): rc = getToolByName(self.context, REFERENCE_CATALOG) workflow = getToolByName(self.context, 'portal_workflow') BatchEmail = self.context.bika_setup.getBatchEmail() username = self.context.portal_membership.getAuthenticatedMember().getUserName() self.reporter = self.user_fullname(username) self.reporter_email = self.user_email(username) # signature image self.reporter_signature = "" c = [x for x in self.bika_setup_catalog(portal_type='LabContact') if x.getObject().getUsername() == username] if c: sf = c[0].getObject().getSignature() if sf: self.reporter_signature = sf.absolute_url() + "/Signature" # lab address self.laboratory = laboratory = self.context.bika_setup.laboratory lab_address = laboratory.getPostalAddress() \ or laboratory.getBillingAddress() \ or laboratory.getPhysicalAddress() if lab_address: _keys = ['address', 'city', 'state', 'zip', 'country'] _list = [lab_address.get(v) for v in _keys] self.lab_address = "<br/>".join(_list).replace("\n", "<br/>") else: self.lab_address = None # group/publish analysis requests by contact ARs_by_contact = {} for ar in self.analysis_requests: contact_uid = ar.getContact().UID() if contact_uid not in ARs_by_contact: ARs_by_contact[contact_uid] = [] ARs_by_contact[contact_uid].append(ar) for contact_uid, ars in ARs_by_contact.items(): ars.sort() self.contact = ars[0].getContact() self.pub_pref = self.contact.getPublicationPreference() batch_size = 'email' in self.pub_pref and BatchEmail or 5 # client address self.client = ars[0].aq_parent client_address = self.client.getPostalAddress() \ or self.contact.getBillingAddress() \ or self.contact.getPhysicalAddress() if client_address: _keys = ['address', 'city', 'state', 'zip', 'country'] _list = [client_address.get(v) for v in _keys] self.client_address = "<br/>".join(_list).replace("\n", "<br/>") else: self.client_address = None self.Footer = self.context.bika_setup.getResultFooter() # send batches of ARs to each contact for b in range(0, len(ars), batch_size): self.batch = ars[b:b+batch_size] self.any_accredited = False self.any_drymatter = False # get all services from all requests in this batch into a # dictionary: # {'Point Of Capture': {'Category': [service,service,...]}} self.services = {} out_fn = "_".join([ar.Title() for ar in self.batch]) for ar in self.batch: if ar.getReportDryMatter(): self.any_drymatter = True states = ("verified", "published") for analysis in ar.getAnalyses(full_objects=True, review_state=states): service = analysis.getService() poc = POINTS_OF_CAPTURE.getValue(service.getPointOfCapture()) cat = service.getCategoryTitle() if poc not in self.services: self.services[poc] = {} if cat not in self.services[poc]: self.services[poc][cat] = [] if service not in self.services[poc][cat]: self.services[poc][cat].append(service) if (service.getAccredited()): self.any_accredited = True # compose and send email if 'email' in self.pub_pref: # render template ar_results = self.ar_results() debug_mode = App.config.getConfiguration().debug_mode if debug_mode: open(join(Globals.INSTANCE_HOME,'var', out_fn + ".html"), "w").write(ar_results) pisa.showLogging() ramdisk = StringIO() pdf = pisa.CreatePDF(ar_results, ramdisk) pdf_data = ramdisk.getvalue() ramdisk.close() if debug_mode: open(join(Globals.INSTANCE_HOME,'var', out_fn + ".pdf"), "wb").write(pdf_data) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject() mime_msg['From'] = formataddr( (encode_header(laboratory.getName()), laboratory.getEmailAddress())) mime_msg['To'] = formataddr( (encode_header(self.contact.getFullname()), self.contact.getEmailAddress())) mime_msg.preamble = 'This is a multi-part MIME message.' msg_txt = MIMEText(ar_results, _subtype='html') mime_msg.attach(msg_txt) if not pdf.err: part = MIMEBase('application', "application/pdf") part.add_header('Content-Disposition', 'attachment; filename="%s.pdf"' % out_fn) part.set_payload( pdf_data ) Encoders.encode_base64(part) mime_msg.attach(part) try: host = getToolByName(self.context, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPServerDisconnected, msg: if not debug_mode: raise SMTPServerDisconnected(msg) except SMTPRecipientsRefused, msg: raise WorkflowException(str(msg)) if self.action == 'publish': for ar in self.batch: try: workflow.doActionFor(ar, 'publish') except WorkflowException: pass ## if not pdf.err: ## setheader = self.request.RESPONSE.setHeader ## setheader('Content-Type', 'application/pdf') ## setheader("Content-Disposition", "attachment;filename=\"%s.pdf\"" % out_fn) ## self.request.RESPONSE.write(pdf_data) else: raise Exception, "XXX pub_pref %s" % self.pub_pref
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 mime_msg['From'] = formataddr( (encode_header(lab.getName()), lab.getEmailAddress())) """ mime_msg['From'] = formataddr(("BIKA IMM", "*****@*****.**")) 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 doActionFor(self, ob, action): assert ob == self if action == 'nonsuch': raise WorkflowException('Test exception') self.updated += ((self._last_path, action),)
def doActionFor(self, ob, action): assert isinstance(ob, self.__class__) if action == 'nonsuch': raise WorkflowException('Test exception') self.updated.append((self._last_path[0], action))
def _executeTransition(self, ob, tdef=None, kwargs=None): ''' Private method. Puts object in a new state. ''' sci = None econtext = None moved_exc = None # Figure out the old and new states. old_sdef = self._getWorkflowStateOf(ob) old_state = old_sdef.getId() if tdef is None: new_state = self.initial_state former_status = {} else: new_state = tdef.new_state_id if not new_state: # Stay in same state. new_state = old_state former_status = self._getStatusOf(ob) new_sdef = self.states.get(new_state, None) if new_sdef is None: msg = _(u'Destination state undefined: ${state_id}', mapping={'state_id': new_state}) raise WorkflowException(msg) # Fire "before" event notify(BeforeTransitionEvent(ob, self, old_sdef, new_sdef, tdef, former_status, kwargs)) # Execute the "before" script. if tdef is not None and tdef.script_name: script = self.scripts[tdef.script_name] # Pass lots of info to the script in a single parameter. sci = StateChangeInfo( ob, self, former_status, tdef, old_sdef, new_sdef, kwargs) try: script(sci) # May throw an exception. except ObjectMoved as moved_exc: ob = moved_exc.getNewObject() # Re-raise after transition # Update variables. state_values = new_sdef.var_values if state_values is None: state_values = {} tdef_exprs = None if tdef is not None: tdef_exprs = tdef.var_exprs if tdef_exprs is None: tdef_exprs = {} status = {} for id, vdef in self.variables.items(): if not vdef.for_status: continue expr = None if id in state_values: value = state_values[id] elif id in tdef_exprs: expr = tdef_exprs[id] elif not vdef.update_always and id in former_status: # Preserve former value value = former_status[id] else: if vdef.default_expr is not None: expr = vdef.default_expr else: value = vdef.default_value if expr is not None: # Evaluate an expression. if econtext is None: # Lazily create the expression context. if sci is None: sci = StateChangeInfo( ob, self, former_status, tdef, old_sdef, new_sdef, kwargs) econtext = createExprContext(sci) value = expr(econtext) status[id] = value # Update state. status[self.state_var] = new_state tool = aq_parent(aq_inner(self)) tool.setStatusOf(self.id, ob, status) # Update role to permission assignments. self.updateRoleMappingsFor(ob) # Execute the "after" script. if tdef is not None and tdef.after_script_name: script = self.scripts[tdef.after_script_name] # Pass lots of info to the script in a single parameter. sci = StateChangeInfo( ob, self, status, tdef, old_sdef, new_sdef, kwargs) script(sci) # May throw an exception. # Fire "after" event notify(AfterTransitionEvent(ob, self, old_sdef, new_sdef, tdef, status, kwargs)) # Return the new state object. if moved_exc is not None: # Propagate the notification that the object has moved. raise moved_exc else: return new_sdef
def __call__(self): rc = getToolByName(self.context, REFERENCE_CATALOG) workflow = getToolByName(self.context, 'portal_workflow') laboratory = self.context.bika_setup.laboratory BatchEmail = self.context.bika_setup.getBatchEmail() BatchFax = self.context.bika_setup.getBatchFax() # group analysis requests by contact ARs_by_contact = {} for ar in self.analysis_requests: contact_uid = ar.getContact().UID() if contact_uid not in ARs_by_contact: ARs_by_contact[contact_uid] = [] ARs_by_contact[contact_uid].append(ar) for contact_uid, ars in ARs_by_contact.items(): ars.sort() self.contact = ars[0].getContact() self.pub_pref = self.contact.getPublicationPreference() batch_size = 'email' in self.pub_pref and BatchEmail or \ 'fax' in self.pub_pref and BatchFax or 1 # send batches of ARs to each contact for b in range(0, len(ars), batch_size): self.batch = ars[b:b+batch_size] self.any_accredited = False self.any_drymatter = False # get all services from all requests in this batch into a # dictionary: # {'Point Of Capture': {'Category': [service,service,...]}} self.services = {} for ar in self.batch: if ar.getReportDryMatter(): self.any_drymatter = True states = ("verified", "published") for analysis in ar.getAnalyses(full_objects=True, review_state=states): service = analysis.getService() poc = POINTS_OF_CAPTURE.getValue(service.getPointOfCapture()) cat = service.getCategoryTitle() if poc not in self.services: self.services[poc] = {} if cat not in self.services[poc]: self.services[poc][cat] = [] self.services[poc][cat].append(service) if (service.getAccredited()): self.any_accredited = True # compose and send email if 'email' in self.pub_pref: # render template to utf-8 ar_results = self.ar_results().encode("utf-8") # XXX ar_debug_name = '%s_%s.html' % \ (self.analysis_requests[0].Title(), self.action) open(join(Globals.INSTANCE_HOME,'var', ar_debug_name), "w").write(ar_results) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = self.get_mail_subject() mime_msg['From'] = formataddr( (encode_header(laboratory.getName()), laboratory.getEmailAddress())) mime_msg['To'] = formataddr( (encode_header(self.contact.getFullname()), self.contact.getEmailAddress())) mime_msg.preamble = 'This is a multi-part MIME message.' msg_txt = MIMEText(ar_results, _subtype='html') mime_msg.attach(msg_txt) try: host = getToolByName(self.context, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except SMTPRecipientsRefused, msg: raise WorkflowException(str(msg)) if self.action == 'publish': for ar in self.batch: try: workflow.doActionFor(ar, 'publish') except WorkflowException: pass else: raise Exception, "XXX pub_pref %s" % self.pub_pref