def send_mail(self, username, profile): site = find_site(self.context) title = get_setting(self.context, 'title') subject = 'Thank you for joining %s' % title body_template = get_renderer( 'templates/email_accept_site_invitation.pt').implementation() system_email_domain = get_setting(self.context, 'system_email_domain') from_name = '%s invitation' % title from_email = 'invitation@%s' % system_email_domain mailer = getUtility(IMailDelivery) msg = MIMEMultipart('alternative') msg['From'] = '%s <%s>' % (from_name, from_email) msg['To'] = profile.email msg['Subject'] = subject bodyhtml = body_template( username=username, site_href=resource_url(site, self.request), system_name=title ) bodyplain = html2text.html2text(bodyhtml) htmlpart = MIMEText(bodyhtml.encode('UTF-8'), 'html', 'UTF-8') plainpart = MIMEText(bodyplain.encode('UTF-8'), 'plain', 'UTF-8') msg.attach(plainpart) msg.attach(htmlpart) mailer.send([profile.email], msg)
def error_monitor_status_view(context, request): """ Simple text only view that shows only error state, for use with external monitoring. """ error_monitor_dir = get_setting(context, 'error_monitor_dir') subsystems = get_setting(context, 'error_monitor_subsystems') subsystems = subsystems + ['postoffice quarantine'] req_subsystems = request.params.getall('subsystem') if req_subsystems: subsystems = filter(lambda x: x in subsystems, req_subsystems) buf = StringIO() for subsystem in subsystems: # Special case postoffice quarantine if subsystem == 'postoffice quarantine': use_postoffice = not not get_setting( context, 'postoffice.zodb_uri', False) if use_postoffice: queue, closer = _get_postoffice_queue(request.context) if queue.count_quarantined_messages() == 0: print >>buf, 'postoffice quarantine: OK' else: print >>buf, 'postoffice quarantine: ERROR' # Normal case elif _get_error_monitor_state(error_monitor_dir, subsystem): print >>buf, '%s: ERROR' % subsystem else: print >>buf, '%s: OK' % subsystem return Response(buf.getvalue(), content_type='text/plain')
def admin_menu(context, request): admin_settings = {} site = find_site(context) settings = request.registry.settings syslog_view = get_setting(context, 'syslog_view', None) admin_settings['syslog_view_enabled'] = syslog_view != None admin_settings['has_logs'] = not not get_setting(context, 'logs_view', None) admin_settings['redislog'] = asbool(settings.get('redislog', 'False')) admin_settings['can_administer'] = has_permission('administer', site, request) admin_settings['can_email'] = has_permission('email', site, request) statistics_folder = get_setting(context, 'statistics_folder', None) if statistics_folder is not None and os.path.exists(statistics_folder): csv_files = [fn for fn in os.listdir(statistics_folder) if fn.endswith('.csv')] admin_settings['statistics_view_enabled'] = not not csv_files else: admin_settings['statistics_view_enabled'] = False admin_settings['quarantine_url'] = ('%s/po_quarantine.html' % request.application_url) site = find_site(context) if 'offices' in site: admin_settings['offices_url'] = resource_url(site['offices'], request) else: admin_settings['offices_url'] = None admin_settings['has_mailin'] = ( get_setting(context, 'zodbconn.uri.postoffice') and get_setting(context, 'postoffice.queue')) return admin_settings
def send_digests(self, context): mailer = getUtility(IMailDelivery) system_name = get_setting(context, "system_name", "KARL") system_email_domain = get_setting(context, "system_email_domain") sent_from = "%s@%s" % (system_name, system_email_domain) from_addr = "%s <%s>" % (system_name, sent_from) subject = "[%s] Your alerts digest" % system_name template = get_template("email_digest.pt") for profile in find_profiles(context).values(): if not profile._pending_alerts: continue # Perform each in its own transaction, so a problem with one # user's email doesn't block all others transaction.manager.begin() try: attachments = [] for alert in profile._pending_alerts: attachments += alert['attachments'] msg = MIMEMultipart() if attachments else Message() msg["From"] = from_addr msg["To"] = "%s <%s>" % (profile.title, profile.email) msg["Subject"] = subject body_text = template( system_name=system_name, alerts=profile._pending_alerts, ) if isinstance(body_text, unicode): body_text = body_text.encode("UTF-8") if attachments: body = MIMEText(body_text, 'html', 'utf-8') msg.attach(body) else: msg.set_payload(body_text, "UTF-8") msg.set_type("text/html") for attachment in attachments: msg.attach(attachment) mailer.send(sent_from, [profile.email,], msg) del profile._pending_alerts[:] transaction.manager.commit() except Exception, e: # Log error and continue log.error("Error sending digest to %s <%s>" % (profile.title, profile.email)) b = StringIO() traceback.print_exc(file=b) log.error(b.getvalue()) b.close() transaction.manager.abort()
def _get_postoffice_queue(context): zodb_uri = get_setting(context, 'zodbconn.uri.postoffice') queue_name = get_setting(context, 'postoffice.queue') if zodb_uri and queue_name: db = context._p_jar.db().databases['postoffice'] return open_queue(db, queue_name) return None, None
def __init__(self, context, request, page_title=None): super(AdminTemplateAPI, self).__init__(context, request, page_title) settings = request.registry.settings syslog_view = get_setting(context, 'syslog_view', None) self.syslog_view_enabled = syslog_view != None self.has_logs = not not get_setting(context, 'logs_view', None) self.redislog = asbool(settings.get('redislog', 'False')) statistics_folder = get_setting(context, 'statistics_folder', None) if statistics_folder is not None and os.path.exists(statistics_folder): csv_files = [fn for fn in os.listdir(statistics_folder) if fn.endswith('.csv')] self.statistics_view_enabled = not not csv_files else: self.statistics_view_enabled = False self.quarantine_url = ('%s/po_quarantine.html' % request.application_url) site = find_site(context) if 'offices' in site: self.offices_url = resource_url(site['offices'], request) else: self.offices_url = None self.has_mailin = ( get_setting(context, 'zodbconn.uri.postoffice') and get_setting(context, 'postoffice.queue'))
def run(): root, closer = get_root(app) set_subsystem('mailin') zodb_uri = get_setting(root, 'postoffice.zodb_uri') zodb_path = get_setting(root, 'postoffice.zodb_path', '/postoffice') queue = get_setting(root, 'postoffice.queue') if zodb_uri is None: parser.error("postoffice.zodb_uri must be set in config file") if queue is None: parser.error("postoffice.queue must be set in config file") runner = MailinRunner2(root, zodb_uri, zodb_path, queue) try: runner() if options.dry_run: transaction.abort() else: transaction.commit() p_jar = getattr(root, '_p_jar', None) if p_jar is not None: # Attempt to fix memory leak p_jar.db().cacheMinimize() except: transaction.abort() raise finally: closer() runner.close()
def __init__(self, context, request, page_title=None): super(AdminTemplateAPI, self).__init__(context, request, page_title) syslog_view = get_setting(context, 'syslog_view', None) self.syslog_view_enabled = syslog_view != None self.has_logs = not not get_setting(context, 'logs_view', None) self.error_monitoring = not not get_setting( context, 'error_monitor_subsystems', None ) statistics_folder = get_setting(context, 'statistics_folder', None) if statistics_folder is not None and os.path.exists(statistics_folder): csv_files = [fn for fn in os.listdir(statistics_folder) if fn.endswith('.csv')] self.statistics_view_enabled = not not csv_files else: self.statistics_view_enabled = False use_postoffice = not not get_setting( context, 'postoffice.zodb_uri', False) if use_postoffice: self.quarantine_url = ('%s/po_quarantine.html' % request.application_url) else: self.quarantine_url = ('%s/mailin/quarantine' % request.application_url) site = find_site(context) if 'offices' in site: self.offices_url = resource_url(site['offices'], request) else: self.offices_url = None
def syslog_view(context, request): syslog_path = get_setting(context, "syslog_view") instances = get_setting(context, "syslog_view_instances", ["karl"]) filter_instance = request.params.get("instance", "_any") if filter_instance == "_any": filter_instances = instances else: filter_instances = [filter_instance] def line_filter(line): try: month, day, time, host, instance, message = line.split(None, 5) except ValueError: # Ignore lines that don't fit the format return None if instance not in filter_instances: return None return line if syslog_path: syslog = codecs.open(syslog_path, encoding="utf-8", errors="replace") else: syslog = StringIO() batch_info = get_fileline_batch(syslog, context, request, line_filter=line_filter, backwards=True) return dict( api=AdminTemplateAPI(context, request), menu=_menu_macro(), instances=instances, instance=filter_instance, batch_info=batch_info, )
def error_monitor_view(context, request): error_monitor_dir = get_setting(context, "error_monitor_dir", "") subsystems = get_setting(context, "error_monitor_subsystems") states = {} urls = {} for subsystem in subsystems: urls[subsystem] = model_url(context, request, "error_monitor_subsystem.html", query={"subsystem": subsystem}) states[subsystem] = _get_error_monitor_state(error_monitor_dir, subsystem) # Tack on mailin quarantine status while we're at it use_postoffice = not not get_setting(context, "postoffice.zodb_uri", False) if use_postoffice: name = "postoffice quarantine" urls[name] = "%s/po_quarantine.html" % request.application_url queue, closer = _get_postoffice_queue(request.context) if queue.count_quarantined_messages() > 0: states[name] = ["Messages in quarantine."] else: states[name] = [] subsystems = list(subsystems) subsystems.append(name) return dict( api=AdminTemplateAPI(context, request), menu=_menu_macro(), subsystems=subsystems, urls=urls, states=states )
def request_password_reset(user, profile, request): profile.password_reset_key = sha1( str(random.random())).hexdigest() profile.password_reset_time = datetime.datetime.now() context = find_site(profile) reset_url = resource_url( context, request, "reset_confirm.html", query=dict(key=profile.password_reset_key)) # send email mail = MIMEMultipart('alternative') system_name = get_setting(context, 'title', 'KARL') admin_email = get_setting(context, 'admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Reset Request" % system_name bodyhtml = render( "templates/email_reset_password.pt", dict(login=user['login'], reset_url=reset_url, system_name=system_name), request=request, ) bodyplain = html2text.html2text(bodyhtml) htmlpart = MIMEText(bodyhtml.encode('UTF-8'), 'html', 'UTF-8') plainpart = MIMEText(bodyplain.encode('UTF-8'), 'plain', 'UTF-8') mail.attach(plainpart) mail.attach(htmlpart) recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail)
def _get_aspell_settings(context): D = {} D['executable'] = get_setting(context, 'aspell_executable', 'aspell') D['max_words'] = int(get_setting(context, 'aspell_max_words', 5000)) langs_csv = get_setting(context, 'aspell_languages', 'en') D['languages'] = [x.strip() for x in langs_csv.split(',')] return D
def request_password_reset(user, profile, request): profile.password_reset_key = sha1( str(random.random())).hexdigest() profile.password_reset_time = datetime.datetime.now() context = find_site(profile) reset_url = resource_url( context, request, "reset_confirm.html", query=dict(key=profile.password_reset_key)) # send email mail = Message() system_name = get_setting(context, 'system_name', 'KARL') admin_email = get_setting(context, 'admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Reset Request" % system_name body = render( "templates/email_reset_password.pt", dict(login=user['login'], reset_url=reset_url, system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail)
def error_monitor_subsystem_view(context, request): error_monitor_dir = get_setting(context, 'error_monitor_dir', '') subsystems = get_setting(context, 'error_monitor_subsystems') subsystem = request.params.get('subsystem', None) if subsystem is None or subsystem not in subsystems: raise NotFound() back_url = resource_url(context, request, 'error_monitor.html') if 'clear' in request.params: path = os.path.join(error_monitor_dir, subsystem) os.remove(path) return HTTPFound(location=back_url) clear_url = resource_url(context, request, request.view_name, query={'clear': '1', 'subsystem': subsystem}) entries = _get_error_monitor_state(error_monitor_dir, subsystem) return dict( api=AdminTemplateAPI(context, request), menu=_menu_macro(), subsystem=subsystem, entries=entries, back_url=back_url, clear_url=clear_url, )
def syslog_view(context, request): syslog_path = get_setting(context, 'syslog_view') instances = get_setting(context, 'syslog_view_instances', ['karl']) filter_instance = request.params.get('instance', '_any') if filter_instance == '_any': filter_instances = instances else: filter_instances = [filter_instance] entries = [] with codecs.open(syslog_path, encoding='utf-8', errors='replace') as syslog: for line in syslog: line = _decode(line) try: month, day, time, host, instance, message = line.split(None, 5) except ValueError: # Ignore lines that don't fit the format continue if instance not in filter_instances: continue entries.append(line) entries.reverse() # Show more recent entries first return render_template_to_response( 'templates/admin/syslog.pt', api=AdminTemplateAPI(context, request), menu=_menu_macro(), instances=instances, instance=filter_instance, entries=entries, )
def error_monitor_view(context, request): error_monitor_dir = get_setting(context, 'error_monitor_dir', '') subsystems = get_setting(context, 'error_monitor_subsystems') states = {} urls = {} for subsystem in subsystems: urls[subsystem] = resource_url(context, request, 'error_monitor_subsystem.html', query={'subsystem': subsystem}) states[subsystem] = _get_error_monitor_state( error_monitor_dir, subsystem ) # Tack on mailin quarantine status while we're at it use_postoffice = not not get_setting( context, 'postoffice.zodb_uri', False) if use_postoffice: name = 'postoffice quarantine' urls[name] = '%s/po_quarantine.html' % request.application_url queue, closer = _get_postoffice_queue(request.context) if queue.count_quarantined_messages() > 0: states[name] = ['Messages in quarantine.'] else: states[name] = [] subsystems = list(subsystems) subsystems.append(name) return dict( api=AdminTemplateAPI(context, request), menu=_menu_macro(), subsystems=subsystems, urls=urls, states=states, )
def mailin(args, instance): log.info("Processing mailin for %s" % instance) root, closer = args.get_root(instance) set_current_instance(instance) set_subsystem('mailin') zodb_uri = get_setting(root, 'postoffice.zodb_uri') zodb_path = get_setting(root, 'postoffice.zodb_path', '/postoffice') queue = get_setting(root, 'postoffice.queue') if zodb_uri is None: args.parser.error("postoffice.zodb_uri must be set in config file") if queue is None: args.parser.error("postoffice.queue must be set in config file") runner = None try: runner = MailinRunner2(root, zodb_uri, zodb_path, queue) runner() transaction.commit() p_jar = getattr(root, '_p_jar', None) if p_jar is not None: # Attempt to fix memory leak p_jar.db().cacheMinimize() except: transaction.abort() raise finally: closer() if runner is not None: runner.close()
def _get_mailto(context, peopledir): mailinglist = context.get('mailinglist') if mailinglist is not None: system_email_domain = get_setting(context, "system_email_domain") system_list_subdomain = get_setting(context, "system_list_subdomain", system_email_domain) return 'mailto:%s@%s' % (mailinglist.short_address, system_list_subdomain)
def __init__(self, context, request, page_title=None): super(AdminTemplateAPI, self).__init__(context, request, page_title) syslog_view = get_setting(context, 'syslog_view', None) self.syslog_view_enabled = syslog_view != None self.has_logs = not not get_setting(context, 'logs_view', None) self.error_monitoring = not not get_setting( context, 'error_monitor_subsystems', None )
def _get_mailto(context, peopledir): mailinglist = context.get("mailinglist") if mailinglist is not None: pd_path = resource_path_tuple(peopledir) report_path = resource_path_tuple(context) mail_name = "+".join(report_path[len(pd_path) :]) system_email_domain = get_setting(context, "system_email_domain") system_list_subdomain = get_setting(context, "system_list_subdomain", system_email_domain) return "mailto:%s@%s" % (mailinglist.short_address, system_list_subdomain)
def quarantine_message(self, message): mailer = getUtility(IMailDelivery) from_email = get_setting(self.root, 'postoffice.bounce_from_email') if from_email is None: from_email = get_setting(self.root, 'admin_email') error = traceback.format_exc() self.queue.quarantine(message, error, wrap_send(mailer.send), from_email) return error
def __call__(self): context, request = self.context, self.request api = AdminTemplateAPI(context, request, "Admin UI: Send Email") admin_email = get_setting(context, "admin_email") system_name = get_setting(context, "system_name") profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ("self", "%s <%s>" % (admin.title, admin.email)), ("admin", "%s Administrator <%s>" % (system_name, admin_email)), ] if "send_email" in request.params: mailer = getUtility(IMailDelivery) group = request.params["to_group"] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) if getattr(profile, "security_state", None) == "inactive": continue userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params["from_email"] == "self": message["From"] = from_emails[0][1] message_from = admin.email else: message["From"] = from_emails[1][1] message_from = admin_email message["To"] = "%s <%s>" % (profile.title, profile.email) message["Subject"] = request.params["subject"] body = u"<html><body>%s</body></html>" % (request.params["text"]) message.set_payload(body.encode("UTF-8"), "UTF-8") message.set_type("text/html") mailer.send([profile.email], message) n += 1 status_message = "Sent message to %d users." % n if has_permission(ADMINISTER, context, request): redirect_to = model_url(context, request, "admin.html", query=dict(status_message=status_message)) else: redirect_to = model_url( find_communities(context), request, "all_communities.html", query=dict(status_message=status_message), ) return HTTPFound(location=redirect_to) return dict(api=api, menu=_menu_macro(), to_groups=self.to_groups, from_emails=from_emails)
def change_password_view(context, request): min_pw_length = get_setting(context, 'min_pw_length') form = ChangePasswordForm(min_pw_length=min_pw_length) if 'form.cancel' in request.POST: return HTTPFound(location=model_url(context, request)) if 'form.submitted' in request.POST: try: converted = form.validate(request.POST) users = find_users(context) userid = context.__name__ user = users.get_by_id(userid) # check the old password # XXX: repoze.who.plugins.zodb.interfaces.IUsers # really should have a check_password(id, password) # method. We shouldn't have to use get_sha_password # directly. enc = get_sha_password(converted['old_password']) if enc != user['password']: raise CustomInvalid({'old_password': '******'}) users.change_password(userid, converted['password']) # send email system_name = get_setting(context, 'system_name', 'KARL') mail = karl.mail.Message() admin_email = get_setting(context, 'admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (context.title, context.email) mail["Subject"] = "%s Password Change Notification" % system_name system_name = get_setting(context, 'system_name', 'KARL') body = render_template( "templates/email_change_password.pt", login=user['login'], password=converted['password'], system_name=system_name, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [context.email] mailer = getUtility(IMailDelivery) mailer.send(admin_email, recipients, mail) path = model_url(context, request) msg = '?status_message=Password%20changed' return HTTPFound(location=path+msg) except Invalid, e: fielderrors = e.error_dict fill_values = form.convert(request.POST)
def __call__(self): context, request = self.context, self.request api = AdminTemplateAPI(context, request, 'Admin UI: Send Email') admin_email = get_setting(context, 'admin_email') system_name = get_setting(context, 'system_name') profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ('self', '%s <%s>' % (admin.title, admin.email)), ('admin', '%s Administrator <%s>' % (system_name, admin_email)), ] if 'send_email' in request.params: mailer = getUtility(IMailDelivery) group = request.params['to_group'] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params['from_email'] == 'self': message['From'] = from_emails[0][1] message_from = admin.email else: message['From'] = from_emails[1][1] message_from = admin_email message['To'] = '%s <%s>' % (profile.title, profile.email) message['Subject'] = request.params['subject'] body = u'<html><body>%s</body></html>' % ( request.params['text'] ) message.set_payload(body.encode('UTF-8'), 'UTF-8') message.set_type('text/html') mailer.send(message_from, [profile.email], message) n += 1 status_message = "Sent message to %d users." % n redirect_to = model_url(context, request, 'admin.html', query=dict(status_message=status_message)) return HTTPFound(location=redirect_to) return render_template_to_response( 'templates/admin/email_users.pt', api=api, menu=_menu_macro(), to_groups = self.to_groups, from_emails=from_emails, )
def _get_mailto(context, peopledir): mailinglist = context.get('mailinglist') if mailinglist is not None: pd_path = model_path_tuple(peopledir) report_path = model_path_tuple(context) mail_name = '+'.join(report_path[len(pd_path):]) system_email_domain = get_setting(context, "system_email_domain") system_list_subdomain = get_setting(context, "system_list_subdomain", system_email_domain) return 'mailto:%s@%s' % (mailinglist.short_address, system_list_subdomain)
def show_mailin_trace_blog(context, request): path = get_setting(context, 'mailin_trace_file') formatted_timestamp = None if os.path.exists(path): timestamp = os.path.getmtime(path) timestamp = datetime.datetime.fromtimestamp(timestamp) formatted_timestamp = timestamp.ctime() return dict( api=TemplateAPI(context, request), system_email_domain=get_setting(context, 'system_email_domain'), timestamp=formatted_timestamp, )
def _get_common_email_info(community, community_href): info = {} info['system_name'] = get_setting(community, 'system_name', 'KARL') info['system_email_domain'] = get_setting(community, 'system_email_domain') info['from_name'] = '%s invitation' % info['system_name'] info['from_email'] = 'invitation@%s' % info['system_email_domain'] info['c_title'] = community.title info['c_description'] = community.description info['c_href'] = community_href info['mfrom'] = '%s <%s>' % (info['from_name'], info['from_email']) return info
def message(self): if self._message is not None: return self._message community = self._community request = self.request profile = self.profile model = self._model community_href = model_url(community, request) model_href = model_url(model, request) manage_preferences_href = model_url(profile, request) system_name = get_setting(self.context, "system_name", "KARL") system_email_domain = get_setting(self.context, "system_email_domain") body_template = get_template(self._template) from_name = "%s | %s" % (self.creator.title, system_name) msg = Message() msg["From"] = "%s <%s>" % (from_name, self.mfrom) msg["To"] = "%s <%s>" % (community.title, profile.email) msg["Subject"] = self._subject body = body_template( context=self.context, community=community, community_href=community_href, model=model, model_href=model_href, manage_preferences_href=manage_preferences_href, profile=profile, creator=self.creator, content_type=self._content_type_name, digest=self.digest, alert=self, ) if self.digest: # Only interested in body for digest html = document_fromstring(body) body_element = html.cssselect('body')[0] span = etree.Element("span", nsmap=body_element.nsmap) span[:] = body_element[:] # Copy all body elements to an empty span body = etree.tostring(span, pretty_print=True) if isinstance(body, unicode): body = body.encode('utf-8') msg.set_payload(body, 'utf-8') msg.set_type("text/html") self._message = msg return msg
def handle_submit(self, converted): context = self.context request = self.request system_name = get_setting(context, 'system_name', 'KARL') address = converted['email'] if address: address = address.lower() search = getAdapter(context, ICatalogSearch) count, docids, resolver = search( interfaces=[IProfile], email=[address]) users = find_users(context) for docid in docids: profile = resolver(docid) if profile is None: continue userid = profile.__name__ user = users.get_by_id(userid) if user is None: continue # found the profile and user break else: raise ValidationError(**{"email": "%s has no account with the email address: %s" % (system_name, address)}) request_password_reset(user, profile, request) url = resource_url(context, request, 'reset_sent.html') + ( '?email=%s' % urllib.quote_plus(address)) return HTTPFound(location=url)
def show_profiles_view(context, request): system_name = get_setting(context, 'system_name', 'KARL') page_title = '%s Profiles' % system_name api = TemplateAPI(context, request, page_title) # Grab the data for the two listings, main communities and portlet search = ICatalogSearch(context) query = dict(sort_index='title', interfaces=[IProfile], limit=5) titlestartswith = request.params.get('titlestartswith') if titlestartswith: query['titlestartswith'] = (titlestartswith, titlestartswith) num, docids, resolver = search(**query) profiles = [] for docid in docids: model = resolver(docid) if model is None: continue profiles.append(model) mgr = ILetterManager(context) letter_info = mgr.get_info(request) return render_to_response( 'templates/profiles.pt', dict(api=api, profiles=profiles, letters=letter_info), request=request, )
def showtag_view(context, request, community=None, user=None, crumb_title=None): """Show a page for a particular tag, optionally refined by context.""" page_title = 'Show Tag' api = TemplateAPI(context, request, page_title) # The tag screens (cloud, listing, and this view) each have a # "jump box" that allows you to quickly jump to another tag. All # three will point here at /showtag?tag=tag1. We detect this mode # and do a redirect. jump_tag = request.params.get('jumptag', False) if jump_tag: location = resource_url(context, request, request.view_name, jump_tag) return HTTPFound(location=location) # Our strategy is to support tag URLs that are like this: # /showtag/tag1 # ...instead of: # /tagpage.html?tag=tag1 # However, our tag data isn't traversable (it is site.tags and not # site['tags']. So we have a view at /showtag that picks apart # the next hop in the URL. tag = request.subpath if not tag: # The user didn't provide anything beyond /showtag in the URL tag = None entries = related = [] else: # Ahh, the good part. Let's find some tag results and unpack # data into what the ZPT needs. tag = tag[0] catalog = find_catalog(context) dm = catalog.document_map tags = find_tags(context) if community is None and user is None: # Only show related tags portlet in global view related = tags.getRelatedTags(tag, user=user, community=community) else: related = [] entries = [] if user: users = (user,) else: users = None for docid in tags.getItems(tags=(tag,), users=users, community=community, ): # XXX Need to wire in batching address = dm.address_for_docid(int(docid)) if address is None: raise KeyError(docid) resource = find_resource(context, address) # Skip documents which aren't viewable by authenticated user if not has_permission('view', resource, request): continue # Do a secondary query for each result to find the # per-user info users = tags.getUsers(tags=(tag,), items=(docid,), community=community) if len(users) == 1: tuh = '1 person' else: tuh = '%s people' % len(users) tuhref = resource_url(context, request, 'tagusers.html', query={'tag': tag, 'docid': docid}) entry = { 'title': resource.title, 'description': getattr(resource, 'description', ''), 'href': resource_url(resource, request), 'type': get_content_type_name(resource), 'tagusers_href': tuhref, 'tagusers_count': tuh, } entries.append(entry) args = dict( api=api, tag=tag, entries=entries, related=related, ) if crumb_title: # XXX Would context.title be a bit nicer for displaying to user? system_name = get_setting(context, 'title', 'KARL') args['crumbs'] = '%s / %s / %s' % ( system_name, crumb_title, context.__name__) return dict(**args)
def ts_config(self): ts_config = self._v_ts_config if ts_config is None: ts_config = get_setting(self, 'pgtextindex.ts_config', 'english') self._v_ts_config = ts_config return ts_config
def enabled(self): return get_setting(self.context, 'two_factor_enabled', False)
def send_digests(self, context, period='daily'): PERIODS = { 'daily': [IProfile.ALERT_DAILY_DIGEST], 'weekly': [IProfile.ALERT_DAILY_DIGEST, IProfile.ALERT_WEEKLY_DIGEST], 'biweekly': [ IProfile.ALERT_DAILY_DIGEST, IProfile.ALERT_WEEKLY_DIGEST, IProfile.ALERT_BIWEEKLY_DIGEST ], } periods = PERIODS[period] mailer = getUtility(IMailDelivery) system_name = get_setting(context, "title", "KARL") sent_from = get_setting(context, "admin_email") from_addr = "%s <%s>" % (system_name, sent_from) subject = "[%s] Your alerts digest" % system_name template = get_renderer("email_digest.pt").implementation() for profile in find_profiles(context).values(): if not list(profile._pending_alerts): continue # Perform each in its own transaction, so a problem with one # user's email doesn't block all others transaction.manager.begin() alerts = profile._pending_alerts.consume() try: pending = [] skipped = [] for alert in alerts: community = alert.get('community') if community is not None: pref = profile.get_alerts_preference(community) if pref in periods: pending.append(alert) else: skipped.append(alert) else: # XXX belt-and-suspenders: send it now pending.append(alert) if len(pending) > 0: attachments = [] for alert in pending: attachments += alert['attachments'] msg = MIMEMultipart() if attachments else Message() msg["From"] = from_addr msg["To"] = "%s <%s>" % (profile.title, profile.email) msg["Subject"] = subject body_text = template.render( system_name=system_name, alerts=pending, ) if isinstance(body_text, unicode): body_text = body_text.encode("UTF-8") if attachments: body = MIMEText(body_text, 'html', 'utf-8') msg.attach(body) else: msg.set_payload(body_text, "UTF-8") msg.set_type("text/html") for attachment in attachments: msg.attach(attachment) mailer.send([profile.email], msg) for alert in skipped: profile._pending_alerts.append(alert) transaction.manager.commit() except Exception: # Log error and continue log.error("Error sending digest to %s <%s>" % (profile.title, profile.email)) b = StringIO() traceback.print_exc(file=b) log.error(b.getvalue()) b.close() transaction.manager.abort()
def min_pw_length(): global _MIN_PW_LENGTH if _MIN_PW_LENGTH is None: _MIN_PW_LENGTH = get_setting(None, 'min_pw_length', 8) return _MIN_PW_LENGTH
from karl.views import peopledirectory from karl.views.api import TemplateAPI from karl.views.forms import validators as karlvalidators from karl.views.forms import widgets as karlwidgets from karl.views.forms.widgets import VerticalRadioChoice from karl.views.people import AdminEditProfileFormController as AdminFCBase from karl.views.people import EditProfileFormController from karl.views.people import groups_field from karl.views.people import login_field from karl.views.people import show_profile_view from karl.views.peopledirectory import get_actions from karl.views.peopledirectory import get_admin_actions from osi.utilities.former_staff import make_non_staff min_pw_length = get_setting(None, 'min_pw_length', 8) class AdminEditProfileFormController(AdminFCBase): def __init__(self, context, request): super(AdminEditProfileFormController, self).__init__(context, request) users = self.users self.is_staff = users.member_of_group(self.userid, 'group.KarlStaff') self.is_own_profile = context.__name__ == authenticated_userid(request) def __call__(self): kw = super(AdminEditProfileFormController, self).__call__() kw['is_own_profile'] = self.is_own_profile kw['is_staff'] = self.is_staff return kw
def __call__(self): context, request = self.context, self.request api = AdminTemplateAPI(context, request, 'Admin UI: Send Email') admin_email = get_setting(context, 'admin_email') system_name = get_setting(context, 'system_name') profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ('self', '%s <%s>' % (admin.title, admin.email)), ('admin', '%s Administrator <%s>' % (system_name, admin_email)), ] if 'send_email' in request.params: mailer = getUtility(IMailDelivery) group = request.params['to_group'] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) if getattr(profile, 'security_state', None) == 'inactive': continue userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params['from_email'] == 'self': message['From'] = from_emails[0][1] message_from = admin.email else: message['From'] = from_emails[1][1] message_from = admin_email message['To'] = '%s <%s>' % (profile.title, profile.email) message['Subject'] = request.params['subject'] body = u'<html><body>%s</body></html>' % ( request.params['text']) message.set_payload(body.encode('UTF-8'), 'UTF-8') message.set_type('text/html') mailer.send([profile.email], message) n += 1 status_message = "Sent message to %d users." % n if has_permission(ADMINISTER, context, request): redirect_to = resource_url( context, request, 'admin.html', query=dict(status_message=status_message)) else: redirect_to = resource_url( find_communities(context), request, 'all_communities.html', query=dict(status_message=status_message)) return HTTPFound(location=redirect_to) return dict( api=api, menu=_menu_macro(), to_groups=self.to_groups, from_emails=from_emails, )
classes = [] for name, title, description, urlname in _VIEWS: classes.append({ 'name': name, 'title': title, 'description': description, 'href': urlname, 'url': request.resource_url(context, urlname), 'selected': name == view_cookie, }) actions = [] if has_permission('create', context, request): actions.append(('Add Community', 'add_community.html')) system_name = get_setting(context, 'title', 'KARL') page_title = '%s%s Communities' % (prefix, system_name) my_communities = get_my_communities(context, request) preferred_communities = get_preferred_communities(context, request) return { 'communities': communities, 'batch_info': batch_info, 'letters': letter_info, 'community_tabs': classes, 'actions': actions, 'my_communities': my_communities, 'preferred_communities': preferred_communities, 'api': TemplateAPI(context, request, page_title), 'profile': None,
def send_text_code(self, profile): msg = "%s authorization code: %s" % (get_setting( self.context, 'title'), profile.current_auth_code) self.send_text_to_number(profile.two_factor_phone, msg)
def message(self): if self._message is not None: return self._message community = self._community request = self.request profile = self.profile model = self._model community_href = resource_url(community, request) model_href = resource_url(model, request) manage_preferences_href = resource_url( profile, request) + '/manage_communities.html' # noqa system_name = get_setting(self.context, "title", "KARL") attachments, attachment_links, attachment_hrefs = self.attachments from_name = "%s | %s" % (self.creator.title, system_name) msg = MIMEMultipart() if attachments else Message() msg["From"] = '"%s" <%s>' % (from_name, self.mfrom) msg["To"] = '"%s" <%s>' % (community.title, profile.email) msg["Subject"] = self._subject msg["Precedence"] = 'bulk' body_text = self.template( context=self.context, community=community, community_href=community_href, model=model, model_href=model_href, manage_preferences_href=manage_preferences_href, attachments=attachment_links, attachment_hrefs=attachment_hrefs, profile=profile, profiles=self.profiles, creator=self.creator, content_type=self._content_type_name, digest=self.digest, alert=self, resource_url=resource_url, request=request, reply_enabled=self.reply_enabled) if self.digest: # Only interested in body for digest html = document_fromstring(body_text) body_element = html.cssselect('body')[0] span = etree.Element("span", nsmap=body_element.nsmap) span[:] = body_element[:] # Copy all body elements to an empty span body_text = etree.tostring(span, pretty_print=True) if isinstance(body_text, unicode): body_text = body_text.encode('utf-8') if attachments: body = MIMEText(body_text, 'html', 'utf-8') msg.attach(body) for attachment in attachments: msg.attach(attachment) else: msg.set_payload(body_text, 'utf-8') msg.set_type("text/html") self._message = msg return msg
def show_blog_view(context, request): if 'year' in request.GET and 'month' in request.GET: year = int(request.GET['year']) month = int(request.GET['month']) dt = datetime.date(year, month, 1).strftime('%B %Y') page_title = 'Blog: %s' % dt where_month = ("and state->>'created' like '%.4d-%.2d%%%%'" % (year, month)) else: page_title = 'Blog' where_month = '' from newt.db import search community = find_community(context) community_cond = qbe.sql(context._p_jar, dict(community=community)) results = search.search( context._p_jar, """ select * from newt natural join karlex where class_name = 'karl.content.models.blog.BlogEntry' and """ + community_cond + """ and newt_can_view(state, %s) """ + where_month + """ order by state->>'created' desc """, effective_principals(request), ) api = TemplateAPI(context, request, page_title) actions = [] if has_permission('create', context, request): actions.append(('Add Blog Entry', request.resource_url(context, 'add_blogentry.html')), ) batch = get_simple_batch(results, context, request) # Unpack into data for the template entries = [] profiles = find_profiles(context) karldates = getUtility(IKarlDates) fmt0 = '<a href="%s#addcomment">Add a Comment</a>' fmt1 = '<a href="%s#comments">1 Comment</a>' fmt2 = '<a href="%s#comments">%i Comments</a>' for entry in batch['entries']: profile = profiles[entry.creator] byline_info = getMultiAdapter((entry, request), IBylineInfo) entry_url = resource_url(entry, request) # Get information about comments on this entry to display in # the last line of the entry comment_count = len(entry['comments']) if comment_count == 0: comments_blurb = fmt0 % entry_url elif comment_count == 1: comments_blurb = fmt1 % entry_url else: comments_blurb = fmt2 % (entry_url, comment_count) info = { 'title': entry.title, 'href': resource_url(entry, request), 'description': entry.description, 'creator_title': profile.title, 'creator_href': entry_url, 'long_date': karldates(entry.created, 'longform'), 'byline_info': byline_info, 'comments_blurb': comments_blurb, } entries.append(info) feed_url = "%satom.xml" % resource_url(context, request) workflow = get_workflow(IBlogEntry, 'security', context) if workflow is None: security_states = [] else: security_states = get_security_states(workflow, None, request) system_email_domain = get_setting(context, "system_email_domain") return dict( api=api, actions=actions, entries=entries, system_email_domain=system_email_domain, feed_url=feed_url, batch_info=batch, security_states=security_states, )
def table(self): table = self._v_table if table is None: table = get_setting(self, 'pgtextindex.table', 'pgtextindex') self._v_table = table return table
def check_upload_size(context, obj, field_name): max_size = int(get_setting(context, 'upload_limit', 0)) if max_size and obj.size > max_size: msg = 'File size exceeds upload limit of %d.' % max_size transaction.get().doom() raise Invalid({field_name: msg})
def make_non_staff(profile, inform_moderators=True): """ When a user is removed from the KarlStaff role, their community memberships are removed. Moderators of their communities are optionally informed via email. """ id = profile.__name__ moderators = {} users = find_users(profile) profile.categories = {} for group in list(users.get_by_id(id)['groups']): if group.startswith('group.community'): # Remove user from group users.remove_user_from_group(id, group) if not inform_moderators: continue # Keep track of moderators we need to email making sure # each moderator is emailed only once and each community is # only mentioned once in any given email. community_name = group.split(':')[1] moderators_group = ('group.community:%s:moderators' % community_name) for moderator in users.users_in_group(moderators_group): if moderator == id: continue # Really should only come up in unittests if moderator not in moderators: moderators[moderator] = set() moderators[moderator].add(community_name) if not inform_moderators: return communities = find_communities(profile) profiles = profile.__parent__ mailer = getUtility(IMailDelivery) for moderator_id in moderators: moderator = profiles[moderator_id] msg = Message() msg['From'] = get_setting(profile, 'admin_email') msg['To'] = '%s <%s>' % (moderator.title, moderator.email) msg['Subject'] = 'Notice that %s is now former staff' % profile.title former_communities = sorted( [communities[c] for c in moderators[moderator_id]], key=lambda x: x.title) app_url = get_setting(profile, 'offline_app_url') communities_info = [ dict(title=c.title, unremove_url='%s%s?user_id=%s' % (app_url, model_path(c, 'members', 'add_existing.html'), id)) for c in former_communities ] body = render( 'templates/email_notify_former_staff.pt', dict(name=profile.title, communities=communities_info), ) if isinstance(body, unicode): body = body.encode('UTF-8') msg.set_payload(body, 'UTF-8') msg.set_type('text/html') mailer.send([msg['To']], msg)
def phone_factor_enabled(self): return bool( get_setting(self.context, 'two_factor_plivo_auth_id', False) and get_setting(self.context, 'two_factor_plivo_auth_token', False) and get_setting(self.context, 'two_factor_src_phone_number', False))
def max_ranked(self): max_ranked = self._v_max_ranked if max_ranked is None: max_ranked = int(get_setting(self, 'pgtextindex.max_ranked', 6000)) self._v_max_ranked = max_ranked return max_ranked
def show_blog_view(context, request): # add portlets to template layout = request.layout_manager.layout layout.add_portlet('blog_archive') if 'year' in request.GET and 'month' in request.GET: year = int(request.GET['year']) month = int(request.GET['month']) def filter_func(name, item): created = item.created return created.year == year and created.month == month dt = datetime.date(year, month, 1).strftime('%B %Y') page_title = 'Blog: %s' % dt else: filter_func = None page_title = 'Blog' api = TemplateAPI(context, request, page_title) actions = [] if has_permission('create', context, request): actions.append(('Add Blog Entry', request.resource_url(context, 'add_blogentry.html')), ) batch = get_container_batch(context, request, filter_func=filter_func, interfaces=[IBlogEntry], sort_index='creation_date', reverse=True) # Unpack into data for the template entries = [] profiles = find_profiles(context) karldates = getUtility(IKarlDates) fmt0 = '<a href="%s#addcomment">Add a Comment</a>' fmt1 = '<a href="%s#comments">1 Comment</a>' fmt2 = '<a href="%s#comments">%i Comments</a>' for entry in batch['entries']: profile = profiles[entry.creator] byline_info = getMultiAdapter((entry, request), IBylineInfo) entry_url = resource_url(entry, request) # Get information about comments on this entry to display in # the last line of the entry comment_count = len(entry['comments']) if comment_count == 0: comments_blurb = fmt0 % entry_url elif comment_count == 1: comments_blurb = fmt1 % entry_url else: comments_blurb = fmt2 % (entry_url, comment_count) info = { 'title': entry.title, 'href': resource_url(entry, request), 'description': entry.description, 'creator_title': profile.title, 'creator_href': entry_url, 'long_date': karldates(entry.created, 'longform'), 'byline_info': byline_info, 'comments_blurb': comments_blurb, } entries.append(info) feed_url = "%satom.xml" % resource_url(context, request) workflow = get_workflow(IBlogEntry, 'security', context) if workflow is None: security_states = [] else: security_states = get_security_states(workflow, None, request) system_email_domain = get_setting(context, "system_email_domain") community = find_community(context) mailin_addr = '%s@%s' % (community.__name__, system_email_domain) return dict( api=api, actions=actions, entries=entries, system_email_domain=system_email_domain, # Deprecated UX1 feed_url=feed_url, batch_info=batch, security_states=security_states, mailin_addr=mailin_addr, # UX2 )
import logging import user_agents from pyramid.security import authenticated_userid from pyramid.security import forget from karl.utils import find_profiles from karl.utils import get_setting logger = logging.getLogger('request_logger') var = get_setting(None, 'var') if var is not None: filehandler = logging.FileHandler('%s/log/tracker.log' % var) filehandler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(message)s') filehandler.setFormatter(formatter) logger.addHandler(filehandler) def request_logger(event): request = event.request if not (request.path.startswith('/static') or request.path.startswith('/newest_feed_items.json')): userid = authenticated_userid(request), if userid is not None: userid = userid[0] email = '' profile = None try: profiles = find_profiles(request.context)
def login_view(context, request): settings = request.registry.settings request.layout_manager.use_layout('anonymous') came_from = _fixup_came_from(request, request.POST.get('came_from')) if request.params.get('form.submitted', None) is not None: challenge_qs = {'came_from': came_from} # identify login = request.POST.get('login') password = request.POST.get('password') if login is None or password is None: return HTTPFound(location='%s/login.html' % request.application_url) max_age = request.POST.get('max_age') if max_age is not None: max_age = int(max_age) # authenticate userid = None reason = 'Bad username or password' users = find_users(context) for authenticate in (password_authenticator, impersonate_authenticator): userid = authenticate(users, login, password) if userid: break # if not successful, try again if not userid: challenge_qs['reason'] = reason return HTTPFound( location='%s/login.html?%s' % (request.application_url, urlencode(challenge_qs, doseq=True))) # else, remember return remember_login(context, request, userid, max_age, came_from) # Log in user seamlessly with kerberos if enabled try_kerberos = request.GET.get('try_kerberos', None) if try_kerberos: try_kerberos = asbool(try_kerberos) else: try_kerberos = asbool(get_setting(context, 'kerberos', 'False')) if try_kerberos: from karl.security.kerberos_auth import get_kerberos_userid userid = get_kerberos_userid(request) if userid: return remember_login(context, request, userid, None, came_from) # Break infinite loop if kerberos authorization fails if request.authorization and request.authorization[0] == 'Negotiate': try_kerberos = False page_title = 'Login to %s' % settings.get( 'system_name', 'KARL') # Per #366377, don't say what screen layout = request.layout_manager.layout layout.page_title = page_title api = TemplateAPI(context, request, page_title) came_from = _fixup_came_from(request, request.params.get('came_from', request.url)) request.session['came_from'] = came_from sso_providers = [] sso = settings.get('sso') if sso: # importing here rather than in global scope allows to only require # velruse be installed for systems using it. from velruse import login_url for name in sso.split(): provider = settings.get('sso.%s.provider' % name) title = settings.get('sso.%s.title' % name) sso_providers.append({ 'title': title, 'name': name, 'url': login_url(request, provider) }) api.status_message = request.params.get('reason', None) response = render_to_response('templates/login.pt', dict(api=api, came_from=came_from, nothing='', try_kerberos=try_kerberos, sso_providers=sso_providers, app_url=request.application_url), request=request) forget_headers = forget(request) response.headers.extend(forget_headers) return response
def message(self): if self._message is not None: return self._message community = self._community request = self.request profile = self.profile blogentry = self._blogentry community_href = resource_url(community, request) blogentry_href = resource_url(blogentry, request) manage_preferences_href = resource_url(profile, request) system_name = get_setting(self.context, "system_name", "KARL") system_email_domain = get_setting(self.context, "system_email_domain") reply_to = '"%s" <%s+blog-%s@%s>' % ( community.title, community.__name__, docid_to_hex( blogentry.docid), system_email_domain) attachments, attachment_links, attachment_hrefs = self.attachments body_template = get_renderer(self._template).implementation() from_name = "%s | %s" % (self.creator.title, system_name) msg = MIMEMultipart() if attachments else Message() msg["From"] = '"%s" <%s>' % (from_name, self.mfrom) msg["To"] = '"%s" <%s>' % (profile.title, profile.email) msg["Reply-to"] = reply_to msg["Subject"] = self._subject msg["Precedence"] = 'bulk' body_text = body_template( context=self.context, community=community, community_href=community_href, blogentry=blogentry, blogentry_href=blogentry_href, attachments=attachment_links, attachment_hrefs=attachment_hrefs, manage_preferences_href=manage_preferences_href, profile=profile, profiles=self.profiles, creator=self.creator, digest=self.digest, alert=self, history=self._history, ) if self.digest: # Only interested in body for digest html = document_fromstring(body_text) body_element = html.cssselect('body')[0] span = etree.Element("span", nsmap=body_element.nsmap) span[:] = body_element[:] # Copy all body elements to an empty span body_text = etree.tostring(span, pretty_print=True) if isinstance(body_text, unicode): body_text = body_text.encode('utf-8') if attachments: body = MIMEText(body_text, 'html', 'utf-8') msg.attach(body) for attachment in attachments: msg.attach(attachment) else: msg.set_payload(body_text, 'utf-8') msg.set_type("text/html") self._message = msg return self._message
def src_number(self): src_number = get_setting(self.context, 'two_factor_src_phone_number') return ''.join(n for n in src_number if n in string.digits)
def bounce_message(self, message, error): mailer = getUtility(IMailDelivery) from_email = get_config_setting('postoffice.bounce_from_email') if from_email is None: from_email = get_setting(self.root, 'admin_email') self.queue.bounce(message, wrap_send(mailer.bounce), from_email, error)