def get_profile_actions(self, req, user): """ Return list of actions """ actions = [] homeperm = self._get_home_perm(req) uresource = Resource('user', user.id) # Project settings for own account if user.username == req.authname: actions.append((-40, tag.a(_('View your projects'), href=homeperm.env.href('myprojects')))) actions.append((0, tag.a(_('Edit your settings'), href=homeperm.env.href('prefs')))) # View other user profile else: actions.append((-50, tag.a(_('View service profile'), href=homeperm.env.href('user', user.username)))) # If user can fully manage account if homeperm.has_permission('USER_EDIT', uresource): label = 'Manage your account' if user.username == req.authname else 'Manage account' actions.append((-2, tag.a(_(label), href=homeperm.env.href('admin/users/manage', username=user.username)))) # Tickets assigned to or created by user (if component is enabled and user has permissions to view them) # Note: href.kwargs cannot be used because of reserved word 'or' and un-ordered nature of dict if (self.env.is_component_enabled('trac.ticket.query.QueryModule') or self.env.is_component_enabled('multiproject.project.tickets.viewtickets.QueryModuleInterceptor')) \ and 'TICKET_VIEW' in req.perm: qstring = 'owner={0}&or&reporter={0}&group=status'.format(user.username) query = Query.from_string(self.env, qstring) actions.append((5, (tag.a(_('View tickets'), href=query.get_href(req.href))))) return actions
def get_profile_actions(self, req, account): """ Returns """ actions = [] # Check if user has MESSAGE_CREATE in home env home_env = HomeProject().get_env() home_perm = PermissionCache(home_env, req.authname) if 'MESSAGE_CREATE' not in home_perm: return [] # Own account if req.authname == account.username: actions.append((200, tag.a( _('Send message to...'), **{'class': 'messages-dialog', 'href': '#'} ))) else: actions.append((200, tag.a( _('Send message to %s' % account.username), **{'class': 'messages-dialog', 'href': '#user_id=%d' % account.id} ))) return actions
def get_navigation_items(self, req): """ Introduce some new items into metanav for log(in/out) purposes """ store = get_userstore() user = store.getUser(req.authname) if user and req.authname != 'anonymous': yield ('metanav', 'login', 'logged in as %s' % user.getDisplayName()) yield ('metanav', 'logout', tag.a('Logout', href = req.href('user', action = 'logout'))) else: current_uri = req.base_url + req.path_info yield ('metanav', 'login', tag.a('Login', href = self.env.home_href.user(action = 'login', goto = current_uri)))
def get_content(self, req, entry, stream, data): if req.perm.has_permission('REPOSITORY_MODIFY'): return tag.a('Delete %s' % (entry.name), href='#', class_='bsop_delete') else: return None
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. Supported arguments: - project: Name of the project to show status / provide follow buttons. Defaults to current project """ req = formatter.req # Parse optional arguments if args is None: args = parse_args(content) if len(args) > 1: args = args[1] # Read optional project name - fallback to current project_name = self.env.project_identifier if args and 'project' in args: project_name = args.get('project', '').strip() # Load project id from db project_id = Project.get(env_name=project_name).id watchers, is_watching = self._get_status(req, project_id) # If user is already watching, do not show the block if is_watching: return tag.div('') # Show macro only when user has permission to view timeline if name not in self.macros or 'TIMELINE_VIEW' not in req.perm or not project_id: # Return default content / instructions return tag.div( tag.h2(_('Follow project'), **{'class': 'title'}), tag.p(_('Project cannot be found or no permission to follow it')), **{'class': 'watch'} ) # For anonymous users, advice login/registering if req.authname == 'anonymous': return tag.div( tag.h2(_('Follow project'), **{'class': 'title'}), tag.p(_('Only registered users can follow the project activity. ')), tag.p(tag.a('Please login or register to service first', href=req.href('../home/user'))), **{'class': 'watch'} ) # Return rendered HTML with JS attached to it data = { 'project_id': project_id, 'env_name': self.env.project_identifier, 'project_name': project_name } chrome = Chrome(self.env) stream = chrome.render_template(req, 'multiproject_watch.html', data, fragment=True) if req.form_token: stream |= chrome._add_form_token(req.form_token) return stream
def get_content(self, req, entry, stream, data): rename_only = self.config.getbool('browserops', 'rename_only') verb = ['Move', 'Rename'][rename_only] if req.perm.has_permission('REPOSITORY_MODIFY'): return tag.a('%s %s' % (verb, entry.name), href='#', class_='bsop_move') else: return None
def get_profile_actions(self, req, user): """ Return list of actions """ actions = [] homeperm = self._get_home_perm(req) uresource = Resource('user', user.id) # Project settings for own account if user.username == req.authname: actions.append((-40, tag.a(_('View your projects'), href=homeperm.env.href('myprojects')))) actions.append((0, tag.a(_('Edit your settings'), href=homeperm.env.href('prefs')))) # View other user profile else: actions.append( (-50, tag.a(_('View service profile'), href=homeperm.env.href('user', user.username)))) # If user can fully manage account if homeperm.has_permission('USER_EDIT', uresource): label = 'Manage your account' if user.username == req.authname else 'Manage account' actions.append( (-2, tag.a(_(label), href=homeperm.env.href('admin/users/manage', username=user.username)))) # Tickets assigned to or created by user (if component is enabled and user has permissions to view them) # Note: href.kwargs cannot be used because of reserved word 'or' and un-ordered nature of dict if (self.env.is_component_enabled('trac.ticket.query.QueryModule') or self.env.is_component_enabled('multiproject.project.tickets.viewtickets.QueryModuleInterceptor')) \ and 'TICKET_VIEW' in req.perm: qstring = 'owner={0}&or&reporter={0}&group=status'.format( user.username) query = Query.from_string(self.env, qstring) actions.append((5, (tag.a(_('View tickets'), href=query.get_href(req.href))))) return actions
def _apply_changes(self, req, project): """ Saves changes into database and project configuration file """ try: # Save information into database project.project_name = req.args.get('name') project.description = req.args.get('descr') # Update author if needed author_id = req.args.get('author_id') if author_id and project.author_id != int(author_id): userstore = get_userstore() author = userstore.getUserWhereId(author_id) project.author = author # Check if author has admin permission to project: put in project if not authorperm = PermissionCache(self.env, author.username) if 'TRAC_ADMIN' not in authorperm: admin_rights = False groupstore = CQDEUserGroupStore( project.trac_environment_key) # Iterate existing project groups and put user into group with TRAC_ADMIN rights for gname, pname in groupstore.get_all_group_permissions(): if pname == 'TRAC_ADMIN': groupstore.add_user_to_group( author.username, gname) admin_rights = True add_notice( req, _('Added TRAC_ADMIN permissions to user: {0}'. format(author.username))) if not admin_rights: permlink = tag.a( 'You way want to modify permissions', href=req.href('admin/general/permissions')) add_warning( req, tag( _('User {0} does not have administrative rights to project. ' .format(author.username)), permlink)) # Save changes to database project.save() # Save information into config for option in ('name', 'descr'): self.config.set('project', option, req.args.get(option)) self.config.save() except Exception, e: self.log.exception('Failed to save project changes') add_warning(req, _('Failed to save changes: {0}'.format(e))) return req.redirect(req.href('admin/general/basics'))
def get_content(self, req, entry, stream, data): max_edit_size = self.config.getint('browserops', 'max_edit_size') if req.perm.has_permission('REPOSITORY_MODIFY') \ and entry.kind == 'file' \ and (max_edit_size <= 0 or entry.content_length <= max_edit_size): reponame = data['reponame'] or '' filename = posixpath.join(reponame, entry.path) return tag.a('Edit %s' % (entry.name), href=req.href.browser(filename, action='edit'), class_='bsop_edit') else: return None
def _make_private(self, req, project): cmd = MakeProjectPublic(project) if cmd.undo(): # Notify listeners for listener in self.project_change_listeners: listener.project_set_private(project) # Notify user add_notice(req, tag( _("Unpublished project: "), tag.a(_('public groups removed'), href=req.href('admin/general/permissions')) )) else: add_warning(req, "Failed to unpublish project")
def filter_stream(self, req, method, filename, stream, data): """ Add Site Admin link to user header area. """ home_perm = PermissionCache(HomeProject().get_env(), username=req.authname) if 'USER_AUTHOR' not in home_perm and 'USER_CREATE' not in home_perm: return stream # Add following link into user header trans = Transformer('//div[@id="login_link"]/a[@class="author"]')\ .after(tag.a('Site admin', href="#", class_="site_admin"))\ .after(tag.span('|', class_="sep")) return stream | trans
def _do_login(self, req): """ Logs user in Works so that first it is checked that user have inserted correct password. If yes, then create cookie Cookie contains random hex entropy that is also stored into database When user next time requests page data in cookie and db should match """ if req.method != 'POST': return self._show_login(req) remote_user = req.args.get('username') password = req.args.get('password') # If not params if not remote_user or not password: return self._show_login(req) # Try authentication auth = Authentication() auth_username = auth.authenticate(remote_user, password) # User is already logged in if req.authname == auth_username: add_notice(req, _('User is already logged in as %s.' % req.authname)) return req.redirect(self.env.home_href()) # Authentication failed if not auth_username: # Load user for salt userstore = get_userstore() user = userstore.getUser(remote_user) if not user: add_notice(req, _('Incorrect username or password - please try again')) return self._show_login(req) # Provide password reset link for local users that have an author if userstore.is_local(user) and userstore.get_user_author(user): token = get_token(self.env, user) reset_link = tag.a('request new password', href=req.href('/user/pwreset', username=remote_user, token=token)) add_notice(req, Markup("Incorrect username or password - please try again or %s" % reset_link)) else: add_notice(req, _("Incorrect username or password - please try again")) return self._show_login(req) return self._select_login_action(req, auth_username)
def remove_user(self, req): """ Show removal form and handle POST as remove action """ username = req.args.get('username') # Check method and permissions if not req.method.upper() == 'POST' or not username: raise PermissionError() # Load user userstore = get_userstore() user = userstore.getUser(req.authname) account = userstore.getUser(username) if not account: add_warning(req, "Could not find user '{0}' from service".format(account.username)) return req.redirect(req.href('admin/users/manage')) # Check permissions req.perm.require('USER_AUTHOR', Resource('user', id=account.id)) # If removable user is project author, change the ownership to person who deletes the user papi = projects.Projects() for project in papi.get_authored_projects(account): project.author = user project.save() # Check if user has TRAC_ADMIN rights for the new project, if not, try setting if not req.perm.has_permission('TRAC_ADMIN', Resource('project', id=project.id)): groupstore = CQDEUserGroupStore(project.trac_environment_key) # Iterate existing project groups and put user into group with TRAC_ADMIN rights for gname, pname in groupstore.get_all_group_permissions(): if pname == 'TRAC_ADMIN': groupstore.add_user_to_group(project.author.username, gname) self.log.info('Added TRAC_ADMIN permissions to {0} at {0}'.format(project.author, project)) self.log.info('Changed ownership of project {0} from {0} to {0}'.format(project, project.author, user)) add_notice(req, tag(_("Changed ownership of the project to you: "), tag.a(project.project_name, href=req.href('..', project.env_name)))) if userstore.deleteUser(account): add_notice(req, "Removed user '{0}' successfully from local store".format(account.username)) else: add_warning(req, "Failed to remove user '{0}' from local store".format(account.username)) # Redirect to user listing return req.redirect(req.href('admin/users/manage'))
def _apply_changes(self, req, project): """ Saves changes into database and project configuration file """ try: # Save information into database project.project_name = req.args.get('name') project.description = req.args.get('descr') # Update author if needed author_id = req.args.get('author_id') if author_id and project.author_id != int(author_id): userstore = get_userstore() author = userstore.getUserWhereId(author_id) project.author = author # Check if author has admin permission to project: put in project if not authorperm = PermissionCache(self.env, author.username) if 'TRAC_ADMIN' not in authorperm: admin_rights = False groupstore = CQDEUserGroupStore(project.trac_environment_key) # Iterate existing project groups and put user into group with TRAC_ADMIN rights for gname, pname in groupstore.get_all_group_permissions(): if pname == 'TRAC_ADMIN': groupstore.add_user_to_group(author.username, gname) admin_rights = True add_notice(req, _('Added TRAC_ADMIN permissions to user: {0}'.format(author.username))) if not admin_rights: permlink = tag.a('You way want to modify permissions', href=req.href('admin/general/permissions')) add_warning(req, tag(_('User {0} does not have administrative rights to project. '.format(author.username)), permlink)) # Save changes to database project.save() # Save information into config for option in ('name', 'descr'): self.config.set('project', option, req.args.get(option)) self.config.save() except Exception, e: self.log.exception('Failed to save project changes') add_warning(req, _('Failed to save changes: {0}'.format(e))) return req.redirect(req.href('admin/general/basics'))
def filter_stream(self, req, method, filename, stream, data): """ Adds announcement box before the mainnav element """ # Add element only if value is set if not self.announce_text: return stream add_script(req, 'multiproject/js/jquery.cookie.js') add_script(req, 'multiproject/js/announce.js') announcement = tag.div( tag.div(wiki_to_html(self.announce_text, self.env, req=req), class_='content'), tag.a(class_='close'), tag.div(class_='clear'), class_='announce' ) # Add Syntax Highlight tab from user preferences trans = Transformer('.//div[@id="mainnav"]').before(announcement) return stream | trans
def filter_stream(self, req, method, filename, stream, data): """ Adds announcement box before the mainnav element """ # Add element only if value is set if not self.announce_text: return stream add_script(req, 'multiproject/js/jquery.cookie.js') add_script(req, 'multiproject/js/announce.js') announcement = tag.div(tag.div(wiki_to_html(self.announce_text, self.env, req=req), class_='content'), tag.a(class_='close'), tag.div(class_='clear'), class_='announce') # Add Syntax Highlight tab from user preferences trans = Transformer('.//div[@id="mainnav"]').before(announcement) return stream | trans
def filter_stream(self, req, method, filename, stream, data): """ Adds project follower information in project summary block:: Followers: You and 1 other Followers: You and 10 others Followers: 10 """ # Filter only the summary table wiki macro if filename != 'multiproject_summary.html': return stream # Load project and followers info project = Project.get(self.env) watchers, is_watching = self._get_status(req, project.id) # By default show only the number status = tag.span(watchers) # Show link to user preferences if is_watching: status = tag.a('You', href=req.href('../home/prefs/following')) # And others? watchers -= 1 if watchers: status += ngettext(" and %(count)s other", " and %(count)s others", watchers, count=watchers) # Add following information into project summary block trans = Transformer('//div[@class="summary"]/table').append( tag.tr( tag.th('Followers:'), tag.td(status) ) ) return stream | trans
def render_admin_panel(self, req, cat, page, path_info): """ Renders admin panel and handles new user creation request """ req.perm.require('USER_CREATE') now = datetime.utcnow() expires = now + timedelta(days=90) data = { 'dateformats': DATEFORMATS, 'now': now, 'expires': expires, } # Helper class add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_create.js') # Get and set option goto address if 'goto' in req.args: req.session['goto'] = conf.safe_address(req.args.get('goto', '')) req.session.save() # Create new user to local database if req.method.upper() == 'GET': return 'admin_user_create.html', data elif req.method.upper() == 'POST': userstore = get_userstore() user = self._get_user(req) author = userstore.getUser(req.authname) # Update data for pre-filled form data['username'] = user.username data['first'] = user.givenName data['last'] = user.lastName data['mail'] = user.mail data['mobile'] = user.mobile # Validate and set author if not req.perm.has_permission('USER_AUTHOR') or not author: chrome.add_warning( req, _("User needs to have author with USER_AUTHOR permission")) return 'admin_user_create.html', data user.author_id = author.id user.expires = expires org_store = CQDEOrganizationStore.instance() auth_store = CQDEAuthenticationStore.instance() user.authentication_key = auth_store.get_authentication_id( LocalAuthentication.LOCAL) user.organization_keys = org_store.get_organization_keys( user, LocalAuthentication.LOCAL) or None # Validate user object error_msg = self.validate_user(req, user) if error_msg: chrome.add_warning(req, error_msg) return 'admin_user_create.html', data # Try to store user if userstore.storeUser(user): userlink = tag.a(user.username, href=req.href('admin/users/manage', username=user.username)) chrome.add_notice(req, tag(_('Created new local user: '******'Created new local user "%s" by "%s"' % (user.username, req.authname)) # Try to send email notification also try: self._send_notification(user, req.server_name) except TracError: # Notification sending failed self.log.exception("Notification sending failed") chrome.add_warning(req, _('Failed to send email notification')) # Handle optional goto argument if 'goto' in req.session: goto = req.session['goto'] del req.session['goto'] # NOTE: Show redirect address as a system message instead of direct redirection # This is because after moving to another project, the system messages are not shown due the separate # sessions per project chrome.add_notice( req, Markup('Go back to: <a href="%s">%s</a>' % (goto, goto))) # Redirect to the page so that we're not showing the created user form with prefilled return req.redirect(req.href('admin/users/create_local')) return 'admin_user_create.html', data
def get_navigation_items(self, req): if 'PROJECT_VIEW' in req.perm: yield ('mainnav', 'summary', tag.a('Summary', href=req.href()))
def get_navigation_items_check_login(self, req): if req.authname != 'anonymous': yield ('metanav', 'prefs', tag.a(_('Preferences'), href = req.href.prefs()))
def render_admin_panel(self, req, cat, page, path_info): """ Renders admin panel and handles new user creation request """ req.perm.require('USER_CREATE') now = datetime.utcnow() expires = now + timedelta(days=90) data = { 'dateformats':DATEFORMATS, 'now':now, 'expires':expires, } # Helper class add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_user_create.js') # Get and set option goto address if 'goto' in req.args: req.session['goto'] = conf.safe_address(req.args.get('goto', '')) req.session.save() # Create new user to local database if req.method.upper() == 'GET': return 'admin_user_create.html', data elif req.method.upper() == 'POST': userstore = get_userstore() user = self._get_user(req) author = userstore.getUser(req.authname) # Update data for pre-filled form data['username'] = user.username data['first'] = user.givenName data['last'] = user.lastName data['mail'] = user.mail data['mobile'] = user.mobile # Validate and set author if not req.perm.has_permission('USER_AUTHOR') or not author: chrome.add_warning(req, _("User needs to have author with USER_AUTHOR permission")) return 'admin_user_create.html', data user.author_id = author.id user.expires = expires org_store = CQDEOrganizationStore.instance() auth_store = CQDEAuthenticationStore.instance() user.authentication_key = auth_store.get_authentication_id(LocalAuthentication.LOCAL) user.organization_keys = org_store.get_organization_keys(user, LocalAuthentication.LOCAL) or None # Validate user object error_msg = self.validate_user(req, user) if error_msg: chrome.add_warning(req, error_msg) return 'admin_user_create.html', data # Try to store user if userstore.storeUser(user): userlink = tag.a(user.username, href=req.href('admin/users/manage', username=user.username)) chrome.add_notice(req, tag(_('Created new local user: '******'Created new local user "%s" by "%s"' % (user.username, req.authname)) # Try to send email notification also try: self._send_notification(user) except TracError: # Notification sending failed self.log.exception("Notification sending failed") chrome.add_warning(req, _('Failed to send email notification')) # Handle optional goto argument if 'goto' in req.session: goto = req.session['goto'] del req.session['goto'] # NOTE: Show redirect address as a system message instead of direct redirection # This is because after moving to another project, the system messages are not shown due the separate # sessions per project chrome.add_notice(req, Markup('Go back to: <a href="%s">%s</a>' % (goto, goto))) # Redirect to the page so that we're not showing the created user form with prefilled return req.redirect(req.href('admin/users/create_local')) return 'admin_user_create.html', data
def get_navigation_items(self, req): yield ('metanav', 'site_admin', tag.a(_('Site admin'), **{'class': 'site_admin', 'href': '#'}))
def get_navigation_items(self, req): if req.authname and req.authname != 'anonymous': yield ('metanav', 'notifications', tag.a(_('Notifications'), **{'class': 'notifications', 'href': '#'}))
def get_navigation_items(self, req): if 'MESSAGE_VIEW' in req.perm: yield ('metanav', 'messages', tag.a(_('Messages'), **{'class': 'messages', 'href': '#'}))
def get_navigation_items(self, req): if 'PROJECT_VIEW' in req.perm: yield ('mainnav', 'summary', tag.a('Summary', href = req.href()))