def _send_notification(self, user): """ Send account information to new user :param User user: :raises: TracError if email sending fails """ data = { 'system':conf.domain_name, 'username':user.username, 'password':user.password, 'expires':user.expires } enotify = EmailNotifier(self.env, "New user account created", data) enotify.template_name = "local_account_created.txt" enotify.notify(user.mail)
def _send_notification(self, user, server_name): """ Send account information to new user :param User user: :raises: TracError if email sending fails """ data = { 'system': server_name, 'username': user.username, 'password': user.password, 'expires': user.expires } enotify = EmailNotifier(self.env, "New user account created", data) enotify.template_name = "local_account_created.html" enotify.notify(user.mail)
def process_request(self, req): """ Takes the provided arguments and posts an email to author - username: User account whose password needs to be changed - token: Validation token generated by system """ back_url = req.href() username = req.args.get('username', '') token = req.args.get('token', '') # Check if required parameters are set if not username or not token: add_warning(req, _('Invalid request')) return req.redirect(back_url) # Check if user really exists userstore = get_userstore() user = userstore.getUser(username) if not user: add_warning(req, _('Invalid request')) return req.redirect(back_url) # Check token if token != get_token(self.env, user): add_warning(req, _('Invalid request')) return req.redirect(back_url) # Construct and send email manage_url = self.env.abs_home_href( 'admin/users/manage', username=user.username ) data = { 'user':user, 'domain_name':self.env.config.get('multiproject', 'domain_name'), 'manage_url':manage_url } # Get author email address author = userstore.get_user_author(user) if not author or not author.mail: add_warning(req, _('Author email not found - please contact service support instead')) return req.redirect(back_url) try: # Import module here so that multiproject.common.users can be imported easily/without circular dependencies from multiproject.common.notifications.email import EmailNotifier # Open home environment home_env = get_home(self.env) enotify = EmailNotifier(home_env, 'Password reset requested', data) enotify.template_name = 'multiproject_reset_password.txt' enotify.notify(author.mail) add_notice(req, _('Password reset requested - you will be informed about the new password once changed')) # Email sending failed except TracError: add_warning(req, _('Email sending failed - please try again later')) # Return back to homepage return req.redirect(back_url)
def edit_user(self, req): """ Handle user edit: view & save """ changes = {} username = req.args.get('username') if not username: add_warning(req, _('Invalid username')) return self.list_users(req) # Load user being edited userstore = get_userstore() user = userstore.getUser(username) if not user: add_warning(req, _('Invalid username (or connection error)')) return self.list_users(req) # Load user who's doing the edit changed_by = userstore.getUser(req.authname) papi = Projects() # Check permissions and redirect to user listing (handy after editing the user) req.perm.require('USER_AUTHOR', Resource('user', id=user.id)) data = req.args data['user'] = user data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None data['base_path'] = req.base_path data['dateformats'] = DATEFORMATS data['is_local'] = userstore.is_local(user) data['now'] = datetime.utcnow() data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0) data['states'] = userstore.USER_STATUS_LABELS data['projects'] = papi.get_authored_projects(user) # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_edit.js') # If get request show edit if req.method.upper() == 'GET': return 'admin_user_edit.html', data # Close pressed: get back to user listing if req.args.get('close'): return req.redirect(req.href('admin/users/manage')) # Handle save if 'limitexceeded' in req.args: add_warning(req, _('Picture you tried to upload was too big. Try a smaller one')) # Update author if changed author_id = req.args.get('author_id', None) # If id field is empty but name is not: manual input if not author_id and req.args.get('author_text'): add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check set reset the author if author_id: author = userstore.getUserWhereId(int(author_id)) if not author: add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check if author is valid: has permission to author? perm = PermissionCache(self.env, author.username) if 'USER_AUTHOR' not in perm: add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author)) return self.back(req) user.author_id = author.id changes['author'] = author else: user.author_id = None user.lastName = req.args.get('last') if not user.lastName: add_warning(req, _('Last name required')) return self.back(req) old_mail = user.mail user.mail = req.args.get('email') if not user.mail: add_warning(req, _('Email address required')) return self.back(req) if old_mail != user.mail: changes['email'] = user.mail org_store = CQDEOrganizationStore.instance() # TODO: is this correct? # When changing email, reset organizations to which the user belongs in user.organization_keys = org_store.get_organization_keys(user) or None # Update password if changed password = req.args.get('password') if password: if not userstore.is_local(user): add_warning(req, _("Can't change password for user that uses external authentication method")) return self.back(req) if len(password) < 7: add_warning(req, _("Password must be at least 7 characters long - please provide longer password")) return self.back(req) if password != req.args.get('confirmpw'): add_warning(req, _("Password do not match - please check")) return self.back(req) user.givenName = req.args.get('first') user.mobile = req.args.get('mobile') # Set or reset account expiration date expiration_str = req.args.get('expires', '') if expiration_str: try: # Parse date and set expiration time in the end of the day expires = datetime.strptime(expiration_str, DATEFORMATS['py']) expires += timedelta(hours=23, minutes=59, seconds=59) # If changed if expires != user.expires: user.expires = expires changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py']) except Exception: self.log.exception('Date formatting failed') add_warning(req, _('Non-recognized expiration format')) pass # Remove expiration date elif user.expires: changes['expiration_date'] = 'Never expires' user.expires = None # Update status if set status = int(req.args.get('status', 0)) if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status: changes['status'] = userstore.USER_STATUS_LABELS[status] user.status = status if req.args.get('removeicon'): user.icon = None else: icon = req.args.get('icon') if not isinstance(icon, unicode) and icon.filename: user.createIcon(req.args.get('icon')) self.log.info('Saving changes to user: %s' % user) ok = userstore.updateUser(user) if ok and password: changes['password'] = password ok = userstore.updatePassword(user, password) if not ok: add_warning(req, _("Could not save changes")) add_notice(req, _("User %s updated" % username)) # Notify user about changes via email? if req.args.get('notify'): data = { 'user':user, 'changed_by':changed_by, 'changes':changes } try: enotify = EmailNotifier(self.env, "Account updated", data) enotify.template_name = 'account_edited.txt' enotify.notify(user.mail) add_notice(req, _("Notified user about the changes")) except TracError: add_warning(req, _("Failed to send email notification - user changed anyway")) # Check if user has still (after modification) permission to modify user # NOTE: req.perm cannot be used here because it is not updated yet resource = Resource('user', id=user.id) perm = PermissionCache(self.env, username=req.authname) if perm.has_permission('USER_AUTHOR', resource): return self.back(req) add_notice(req, _('You have no longer permission to modify the account: %s' % user.username)) return req.redirect(req.href('admin/users/manage'))
def main(): if '-h' in sys.argv or '--help' in sys.argv: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " sys.exit() from_batch = 0 if '--batch' in sys.argv: from_batch_index = sys.argv.index('--batch')+1 if len(sys.argv) > from_batch_index: from_batch = int(sys.argv[int(from_batch_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--batch used incorrectly)" sys.exit() from_sleep = 5 if '--sleep' in sys.argv: from_sleep_index = sys.argv.index('--sleep')+1 if len(sys.argv) > from_sleep_index: from_sleep = int(sys.argv[int(from_sleep_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--sleep used incorrectly)" sys.exit() total_batch_count = -1 if '--limit' in sys.argv: from_limit_index = sys.argv.index('--limit')+1 if len(sys.argv) > from_limit_index: total_batch_count = int(sys.argv[int(from_limit_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--limit used incorrectly)" sys.exit() env_name = 'HelpAndSupport' if '--env' in sys.argv: env_index = sys.argv.index('--env')+1 if len(sys.argv) > env_index: env_name = sys.argv[int(env_index)] else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [-n] [--env ENV] " print " (--env used incorrectly)" sys.exit() debug_only = False if '-n' in sys.argv: debug_only = True header = '' message = '' with open('message.txt') as fd: message = fd.read() with open('subject.txt') as fd: lines = fd.read().splitlines() for line in lines: if line: header = line mailNotifier = EmailNotifier(Environment(conf.getEnvironmentSysPath(env_name)), header, message) print "header: '%s'" % header print "message:\n---\n'%s'\n---\n" % message print "debug?:%s"%debug_only with open('project_admins.txt') as fd: users = fd.read().splitlines() index = 0 counter = 0 sent_batch_count = 0 for chunk in chunks(users, 5): counter += 1 if from_batch <= counter and (total_batch_count == -1 or total_batch_count > sent_batch_count): print "sending batch %s, from %s to %s, emails: %s" % (counter, index+1, index+len(chunk), chunk) if not debug_only: mailNotifier.notify(chunk) time.sleep(from_sleep) sent_batch_count += 1 index += 5
def main(): if '-h' in sys.argv or '--help' in sys.argv: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " sys.exit() from_batch = 0 if '--batch' in sys.argv: from_batch_index = sys.argv.index('--batch') + 1 if len(sys.argv) > from_batch_index: from_batch = int(sys.argv[int(from_batch_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--batch used incorrectly)" sys.exit() from_sleep = 5 if '--sleep' in sys.argv: from_sleep_index = sys.argv.index('--sleep') + 1 if len(sys.argv) > from_sleep_index: from_sleep = int(sys.argv[int(from_sleep_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--sleep used incorrectly)" sys.exit() total_batch_count = -1 if '--limit' in sys.argv: from_limit_index = sys.argv.index('--limit') + 1 if len(sys.argv) > from_limit_index: total_batch_count = int(sys.argv[int(from_limit_index)]) else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [--sleep N] [-n] [--env ENV] " print " (--limit used incorrectly)" sys.exit() env_name = 'HelpAndSupport' if '--env' in sys.argv: env_index = sys.argv.index('--env') + 1 if len(sys.argv) > env_index: env_name = sys.argv[int(env_index)] else: print "Usage: python send_mail_to_project_admins.py [--batch N] [--limit N] [-n] [--env ENV] " print " (--env used incorrectly)" sys.exit() debug_only = False if '-n' in sys.argv: debug_only = True header = '' message = '' with open('message.txt') as fd: message = fd.read() with open('subject.txt') as fd: lines = fd.read().splitlines() for line in lines: if line: header = line mailNotifier = EmailNotifier( Environment(conf.getEnvironmentSysPath(env_name)), header, message) print "header: '%s'" % header print "message:\n---\n'%s'\n---\n" % message print "debug?:%s" % debug_only with open('project_admins.txt') as fd: users = fd.read().splitlines() index = 0 counter = 0 sent_batch_count = 0 for chunk in chunks(users, 5): counter += 1 if from_batch <= counter and ( total_batch_count == -1 or total_batch_count > sent_batch_count): print "sending batch %s, from %s to %s, emails: %s" % ( counter, index + 1, index + len(chunk), chunk) if not debug_only: mailNotifier.notify(chunk) time.sleep(from_sleep) sent_batch_count += 1 index += 5
def edit_user(self, req): """ Handle user edit: view & save """ changes = {} username = req.args.get('username') if not username: add_warning(req, _('Invalid username')) return self.list_users(req) # Load user being edited userstore = get_userstore() user = userstore.getUser(username) if not user: add_warning(req, _('Invalid username (or connection error)')) return self.list_users(req) # Load user who's doing the edit changed_by = userstore.getUser(req.authname) papi = Projects() # Check permissions and redirect to user listing (handy after editing the user) #req.perm.require('USER_AUTHOR', Resource('user', id=user.id)) if self.check_author_and_deputies(changed_by.id, user.author_id, userstore.get_deputies(user.id), req, user.id) == False: add_warning(req, _("You don't have rights to edit user")) req.redirect(req.href("admin")) data = req.args data['user'] = user data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None data['deputies'] = userstore.get_deputies(user.id) data['base_path'] = req.base_path data['dateformats'] = DATEFORMATS data['is_local'] = userstore.is_local(user) data['now'] = datetime.utcnow() data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0) data['states'] = userstore.USER_STATUS_LABELS data['projects'] = papi.get_authored_projects(user) # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_edit.js') # If get request show edit if req.method.upper() == 'GET' and req.args.get('remove_deputy'): deputy = userstore.getUser(req.args.get('remove_deputy').strip()) remove_res = userstore.remove_deputy(user.id, deputy.id) return req.send(remove_res, content_type='text/plain', status=200) elif req.method.upper() == 'GET': return 'admin_user_edit.html', data # Close pressed: get back to user listing if req.args.get('close'): return req.redirect(req.href('admin/users/manage')) if req.args.get('deputy_name'): deputy = userstore.getUser(req.args.get('deputy_name').strip()) resource = Resource('user', id=deputy.id) perm = PermissionCache(self.env, username=deputy.username) if perm.has_permission('USER_AUTHOR', resource): if(userstore.add_deputy(user.id, deputy.username)): add_notice(req, _("Deputy "+deputy.username+" added.")) return_url = 'home/admin/users/manage?username='******'home/admin/users/manage?username='******'t have enough rights")) return_url = 'home/admin/users/manage?username='******'limitexceeded' in req.args: add_warning(req, _('Picture you tried to upload was too big. Try a smaller one')) # Update author if changed author_id = req.args.get('author_id', None) # If id field is empty but name is not: manual input if not author_id and req.args.get('author_text'): add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check set reset the author if author_id: author = userstore.getUserWhereId(int(author_id)) if not author: add_warning(req, _('Author cannot be found')) return 'admin_user_edit.html', data # Check if author is valid: has permission to author? perm = PermissionCache(self.env, author.username) if 'USER_AUTHOR' not in perm: add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author)) return self.back(req) user.author_id = author.id changes['author'] = author else: user.author_id = None user.lastName = req.args.get('last') if not user.lastName: add_warning(req, _('Last name required')) return self.back(req) old_mail = user.mail user.mail = req.args.get('email') if not user.mail: add_warning(req, _('Email address required')) return self.back(req) if old_mail != user.mail: changes['email'] = user.mail org_store = CQDEOrganizationStore.instance() # TODO: is this correct? # When changing email, reset organizations to which the user belongs in user.organization_keys = org_store.get_organization_keys(user) or None # Update password if changed password = req.args.get('password') if password: if not userstore.is_local(user): add_warning(req, _("Can't change password for user that uses external authentication method")) return self.back(req) if len(password) < 7: add_warning(req, _("Password must be at least 7 characters long - please provide longer password")) return self.back(req) if password != req.args.get('confirmpw'): add_warning(req, _("Password do not match - please check")) return self.back(req) user.givenName = req.args.get('first') user.mobile = req.args.get('mobile') # Set or reset account expiration date expiration_str = req.args.get('expires', '') if expiration_str: try: # Parse date and set expiration time in the end of the day expires = datetime.strptime(expiration_str, DATEFORMATS['py']) expires += timedelta(hours=23, minutes=59, seconds=59) # If changed if expires != user.expires: user.expires = expires changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py']) except Exception: self.log.exception('Date formatting failed') add_warning(req, _('Non-recognized expiration format')) pass # Remove expiration date elif user.expires: changes['expiration_date'] = 'Never expires' user.expires = None # Update status if set status = int(req.args.get('status', 0)) if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status: changes['status'] = userstore.USER_STATUS_LABELS[status] user.status = status if req.args.get('removeicon'): user.icon = None else: icon = req.args.get('icon') if not isinstance(icon, unicode) and icon.filename: user.createIcon(req.args.get('icon')) self.log.info('Saving changes to user: %s' % user) ok = userstore.updateUser(user) if ok and password: changes['password'] = password ok = userstore.updatePassword(user, password) if not ok: add_warning(req, _("Could not save changes")) add_notice(req, _("User %s updated" % username)) # Notify user about changes via email? if req.args.get('notify'): data = { 'user':user, 'changed_by':changed_by, 'changes':changes } try: enotify = EmailNotifier(self.env, "Account updated", data) enotify.template_name = 'account_edited.txt' enotify.notify(user.mail) add_notice(req, _("Notified user about the changes")) except TracError: add_warning(req, _("Failed to send email notification - user changed anyway")) # Check if user has still (after modification) permission to modify user # NOTE: req.perm cannot be used here because it is not updated yet #resource = Resource('user', id=user.id) #perm = PermissionCache(self.env, username=req.authname) #if perm.has_permission('USER_AUTHOR', resource): # return self.back(req) if self.check_author_and_deputies(changed_by.id, user.author_id, userstore.get_deputies(user.id), req, user.id) == True: return_url = 'home/admin/users/manage?username='******'You have no longer permission to modify the account: %s' % user.username)) return req.redirect(req.href('admin/users/manage'))