def _has_edit_perm(self,pagename,username,req): perm_obj=PermissionSystem(self.env) user_permissions=perm_obj.get_user_permissions(username) perm_list=[] for i in user_permissions: if user_permissions[i]==True: perm_list.append(i) if "WIKI_MODIFY" in perm_list: if self._is_the_creator(pagename,username,req)==True: return True if "TRAC_ADMIN" in perm_list: return True else: cnx=self.env.get_db_cnx() cur=cnx.cursor() cur.execute("select count() from wiki_permission where pagename=\"%s\";"%(pagename,)) exist=cur.fetchone() if exist[0] == 0: cur.close() cnx.commit() cnx.close() return True cur.execute("select perm_w from wiki_permission where pagename=\"%s\" and username=\"%s\";"%(pagename,username)) perm=cur.fetchone() cur.close() cnx.commit() cnx.close() if perm and perm[0]==1: return True return False return False
def handle_edit_locale_admins(self, req, locale_id): if not locale_id: req.redirect(req.href.admin('translations', 'locales')) Session = session(self.env) locale = Session.query(Locale).get(int(locale_id)) known_users = self.env.get_known_users() errors = [] perm = PermissionSystem(self.env) sids_without_necessary_perms = [] for admin in locale.admins: if not 'L10N_MODERATE' in perm.get_user_permissions(admin.sid): sids_without_necessary_perms.append(admin.sid) if sids_without_necessary_perms: msg = ngettext( "%s does not have the required permissions to administrate." % \ ', '.join(["'%s'" % s for s in sids_without_necessary_perms]), "%s don't have the required permissions to administrate." % \ ', '.join(["'%s'" % s for s in sids_without_necessary_perms]), len(sids_without_necessary_perms)) errors.append( tag(msg, _(" Don't forget to "), tag.a(_('update permissions'), href=req.href.admin('general', 'perm')), '.')) if req.method == 'POST' and len(req.args.getlist('admins')) >= 1: current_admins = req.args.getlist('current_admins') selected = req.args.getlist('admins') self.log.debug('Current Admins: %s', current_admins) self.log.debug('Selected Admins: %s', selected) allow_delete_admins = len(selected) >= 1 if not allow_delete_admins: errors.append( tag(_("There must be at least on admin for each locale."))) for admin in current_admins: if not allow_delete_admins: break if admin not in selected: locale_admin = Session.query(LocaleAdmin). \ filter(locale_admin_table.c.sid==admin).first() Session.delete(locale_admin) for admin in selected: if admin not in locale.admins: locale.admins.append(LocaleAdmin(locale, admin)) Session.commit() req.redirect(req.href.admin('translations', 'locales')) elif req.method == 'POST' and len(req.args.getlist('admins')) < 1: errors.append( tag(_("There must be at least on admin for each locale."))) data = {'locale': locale, 'known_users': known_users} if errors: data['error'] = tag.ul(*[tag.li(e) for e in errors if e]) return 'l10n_admin_locale_admins.html', data
def permissions(self): """Deprecated (but still used by the HDF compatibility layer) """ self.perm.env.log.warning("perm.permissions() is deprecated and " "is only present for HDF compatibility") permsys = PermissionSystem(self.perm.env) actions = permsys.get_user_permissions(self.perm.username) return [action for action in actions if action in self]
def remove_permissions(self, permissions): perm = PermissionSystem(self.env) for agent, p in permissions.items(): if '*' in p: p = [ i for i, j in perm.get_user_permissions(agent).items() if j] for permission in p: try: perm.revoke_permission(agent, permission) except: continue
def _has_permission(self, user, report_id): report_permissions = self._get_report_permissions(report_id) if not report_permissions: return True perms = PermissionSystem(self.env) report_permissions = set(report_permissions) user_perm = set(perms.get_user_permissions(user)) groups = set(self._get_user_groups(user)) user_perm.update(groups) if report_permissions.intersection(user_perm) != set([]): return True return False
def do_purge(self, req, path, users): """Purge obsolete data - i.e. environment data (sessions, preferences, permissions) from users no longer existing @param req @param path path to the trac env to purge @param users users to keep @return boolean success @return msg info """ self.env.log.debug('+ Purging obsolete data') dryrun = self.env.config.getbool('user_sync','dryrun',True) sql = [] envpath, tracenv = os.path.split(path) try: env = Environment(path) except IOError: self.env.log.debug('Could not initialize environment at %s' % (path,)) return False, 'Could not initialize environment at %s' % (path,) perm = PermissionSystem(env) if not 'TRAC_ADMIN' in perm.get_user_permissions(req.perm.username): raise PermissionError excludes = self.get_perm_groups(path)+users protect = "'"+"','".join(excludes)+"'" self.env.log.debug("Excluding from purge: %s" % (protect,)) db = env.get_db_cnx() cursor = db.cursor() if not dryrun: self.env.log.debug('Updating database for %s' % (tracenv,)) cursor.execute('DELETE FROM auth_cookie WHERE name NOT IN (%s)' % (protect,)) cursor.execute('DELETE FROM session WHERE sid NOT IN (%s)' % (protect,)) cursor.execute('DELETE FROM session_attribute WHERE sid NOT IN (%s)' % (protect,)) cursor.execute('DELETE FROM permission WHERE username NOT IN (%s)' % (protect,)) db.commit() sql_file_path = self.env.config.get('user_sync','sql_file_path') or os.path.join(self.env.path,'log') if sql_file_path.lower() == 'none': self.env.log.debug('SQLFile disabled (sql_file_path is "none")') else: sqlfile = '%s.sql' % (tracenv,) sqlfile = os.path.join(sql_file_path,sqlfile) self.env.log.debug('Writing SQL to %s' % (sqlfile,)) try: f = open(sqlfile,'a') f.write('\n--- SQL for purging Trac environment %s\n' % (tracenv,)); f.write('DELETE FROM auth_cookie WHERE name NOT IN (%s);\n' % (protect,)) f.write('DELETE FROM session WHERE sid NOT IN (%s);\n' % (protect,)) f.write('DELETE FROM session_attribute WHERE sid NOT IN (%s);\n' % (protect,)) f.write('DELETE FROM permission WHERE username NOT IN (%s);\n' % (protect,)) except IOError: self.env.log.debug('Could not write SQL file %s!' % (sqlfile,)) return False, 'Could not write SQL file %s!' % (sqlfile,) return True, 'Successfully purged environment %s' % (tracenv,)
def test_new_team_members_get_teammember_permissions(self): req = Mock(authname='admin', perm=MockPerm()) new_member_name = 'fnord' admin_panel = TeamAdminPanel(self.env) team_member = ValueObject(dict(name=new_member_name)) admin_panel.create_user_and_grant_permissions(req, team_member) permission_system = PermissionSystem(self.env) permissions = permission_system.get_user_permissions(new_member_name) self.assert_true(Role.TEAM_MEMBER in permissions) self.assert_true(permissions[Role.TEAM_MEMBER])
def _has_permission(self, user, report_id): report_permissions = self._get_report_permissions(report_id) if report_permissions == None or report_permissions == []: return True perms = PermissionSystem(self.env) report_permissions = set(report_permissions) user_perm = set(perms.get_user_permissions(user)) groups = set(self._get_user_groups(user)) user_perm.update(groups) if report_permissions.intersection(user_perm) != set([]): return True return False
def remove_permissions(self, permissions): perm = PermissionSystem(self.env) for agent, p in permissions.items(): if '*' in p: p = [ i for i, j in perm.get_user_permissions(agent).items() if j ] for permission in p: try: perm.revoke_permission(agent, permission) except: continue
def process_request(self, req): add_script(req, 'cc_selector/cc_selector.js') # fetch list of available developers: cc_developers = [] db = self.env.get_db_cnx() perm = PermissionSystem(self.env) for username, name, email in self.env.get_known_users(db): if perm.get_user_permissions(username).get('TICKET_VIEW'): cc_developers.append(username) req.hdf['cc_developers'] = cc_developers return 'cc_selector.cs', None
def projects(self, user): base_path, _project = os.path.split(self.env.path) _projects = [p for p in os.listdir(base_path) if p != _project] projects = {} for project in _projects: path = os.path.join(base_path, project) try: env = open_environment(path, use_cache=True) except: continue perm = PermissionSystem(env) if self.permission in perm.get_user_permissions(user): projects[project] = env return projects
def test_grant_undefined_permission_with_permission_grant(self): """Undefined permission is granted without checking granter.""" ps = PermissionSystem(self.env) ps.grant_permission('user1', 'PERMISSION_GRANT') self.env.db_transaction(""" INSERT INTO permission VALUES ('group1', 'TEST_PERM') """) req = MockRequest(self.env, method='POST', authname='user1', args={ 'add': True, 'subject': 'user2', 'group': 'group1'}) with self.assertRaises(RequestDone): self.panel.render_admin_panel(req, 'general', 'perm', None) self.assertIn('TEST_PERM', ps.get_user_permissions('group1', undefined=True)) self.assertIn('user2', ps.get_groups_dict()['group1'])
def get_tracenv_userdata(self, req, path, userlist=''): """Retrieve account data from the environment at the specified path @param path path to the environment @param userlist comma separated list of users to restrict the result to (e.g. the users from the password file), each user enclosed in single quotes (for SQL) @return array (empty array if the environment uses a different password file than the master env calling us) """ self.env.log.debug('Get user data from %s' % (path,)) data = {} env = Environment(path) # if this environment uses a different password file, we return an empty dataset if self.env.config.get('account-manager','password_file') != env.config.get('account-manager','password_file'): self.env.log.info('Password files do not match, skipping environment %s' % (path,)) return data perm = PermissionSystem(env) if not 'TRAC_ADMIN' in perm.get_user_permissions(req.perm.username): raise PermissionError db = env.get_db_cnx() cursor = db.cursor() sync_fields = self.env.config.getlist('user_sync','sync_fields') attr = "'"+"','".join(sync_fields)+"','email_verification_sent_to','email_verification_token'" self.env.log.debug('* Checking attributes: %s' % (attr,)) if userlist: cursor.execute("SELECT sid,name,value FROM session_attribute WHERE sid IN (%s) AND name IN (%s)" % (userlist,attr,)) else: cursor.execute("SELECT sid,name,value FROM session_attribute WHERE name IN (%s)" % (attr,)) for row in cursor: if not row[0] in data: data[row[0]] = {} data[row[0]][row[1]] = row[2] for sid in data.iterkeys(): no_data = True for att in sync_fields: if att in data[sid]: no_data = False break if no_data: self.env.log.debug('No data for %s in %s' % (sid,path,)) data[sid] = Null continue data[sid]['path'] = path cursor.execute("SELECT authenticated FROM session_attribute WHERE sid='%s'" % (sid,)) for row in cursor: data[sid]['authenticated'] = row[0] cursor.execute("SELECT datetime(last_visit,'unixepoch') AS last_visit FROM session WHERE sid='%s'" % (sid,)) for row in cursor: data[sid]['last_visit'] = row[0] return data
def install(self): """Installer""" # Utility function to interpreted boolean option value getBool = lambda s: s.strip().lower() in ['true', 'yes'] # Utility function to parse a multi-line/multi-value parameter def cleanMultiParams(v): params = [ s.split('|') for s in [l.strip() for l in v.split('\n')] if len(s) > 0 ] cleaned_params = [] for line in params: cleaned_params.append([row.strip() for row in line]) return cleaned_params # Utility function to transform any string to an ID getId = lambda s: ''.join([c for c in s if c.isalnum()]).lower() options = self.options # Add command line scripts trac-admin and tracd into bin entry_points = [('trac-admin', 'trac.admin.console', 'run'), ('tracd', 'trac.web.standalone', 'main')] zc.buildout.easy_install.scripts(entry_points, pkg_resources.working_set, options['executable'], options['bin-directory']) #################### # Init Trac instance #################### # Generate the trac instance, if required location = options['location'] project_name = options.get('project-name', 'My project') project_url = options.get('project-url', 'http://example.com') db = 'sqlite:%s' % os.path.join('db', 'trac.db') if not os.path.exists(location): os.mkdir(location) trac = TracAdmin(location) if not trac.env_check(): trac.do_initenv('"%s" %s' % (project_name, db)) env = trac.env # Remove Trac default example data clean_up = getBool(options.get('remove-examples', 'True')) if clean_up: # Remove default milestones for mil in Milestone.select(env): if mil.name in [ 'milestone1', 'milestone2', 'milestone3', 'milestone4' ]: mil.delete() # Remove default components for comp in Component.select(env): if comp.name in ['component1', 'component2']: comp.delete() # Add custom milestones for mil_data in cleanMultiParams(options.get('milestones', '')): mil_name = mil_data[0] try: mil = Milestone(env, name=mil_name) except ResourceNotFound: mil = Milestone(env) mil.name = mil_name mil.insert() # Add custom components for comp_data in cleanMultiParams(options.get('components', '')): comp_name = comp_data[0] try: comp = Component(env, name=comp_name) except ResourceNotFound: comp = Component(env) comp.name = comp_name if len(comp_data) == 2 and comp_data[1] not in [None, '']: comp.owner = comp_data[1] comp.insert() ####################### # Generate the trac.ini ####################### # Read the trac.ini config file trac_ini = os.path.join(location, 'conf', 'trac.ini') parser = ConfigParser.ConfigParser() parser.read([trac_ini]) # Clean-up trac.ini: add missing stuff if 'components' not in parser.sections(): parser.add_section('components') # Force upgrade of informations used during initialization parser.set('project', 'name', project_name) # Set all repositories repos = cleanMultiParams(options.get('repos', None)) repo_names = [getId(r[0]) for r in repos] repo_types = {}.fromkeys([r[1].lower() for r in repos]).keys() if 'repositories' not in parser.sections(): parser.add_section('repositories') for repo in repos: repo_name = getId(repo[0]) repo_type = repo[1] repo_dir = repo[2] repo_url = repo[3] parser.set('repositories', '%s.type' % repo_name, repo_type) parser.set('repositories', '%s.dir' % repo_name, repo_dir) if repo_url not in ['', None]: parser.set('repositories', '%s.url' % repo_name, repo_url) # Set default repository default_repo = getId(options.get('default-repo', None)) if default_repo and default_repo in repo_names: parser.set('repositories', '.alias', default_repo) parser.set('repositories', '.hidden', 'true') # Set repository sync method sync_method = options.get('repos-sync', 'request').strip().lower() svn_repos = [getId(r[0]) for r in repos if r[1] == 'svn'] if sync_method == 'request': parser.set('trac', 'repository_sync_per_request', ', '.join(svn_repos)) # TODO # elif sync_method == 'hook': # do stuff... # Set project description project_descr = options.get('project-description', None) if project_descr: parser.set('project', 'descr', project_descr) parser.set('header_logo', 'alt', project_descr) # Setup logo header_logo = options.get('header-logo', '') header_logo = os.path.realpath(header_logo) if os.path.exists(header_logo): shutil.copyfile(header_logo, os.path.join(location, 'htdocs', 'logo')) parser.set('header_logo', 'src', 'site/logo') parser.set('header_logo', 'link', project_url) # Set footer message parser.set( 'project', 'footer', options.get( 'footer-message', 'This Trac instance was generated by <a href="http://pypi.python.org/pypi/pbp.recipe.trac">pbp.recipe.trac</a>.' )) # SMTP parameters for name in ('always-bcc', 'always-cc', 'default-domain', 'enabled', 'from', 'from-name', 'password', 'port', 'replyto', 'server', 'subject-prefix', 'user'): param_name = "smtp-%s" % name default_value = None if param_name == "smtp-from-name": default_value = project_name value = options.get(param_name, default_value) if value is not None: parser.set('notification', param_name.replace('-', '_'), value) ############### # Plugins setup ############### # If one repository use Mercurial, hook its plugin if 'hg' in repo_types: parser.set('components', 'tracext.hg.*', 'enabled') # Configure the NavAdd plugin menu_items = cleanMultiParams(options.get('additional-menu-items', '')) item_list = [] for item in menu_items: item_title = item[0] item_url = item[1] item_id = getId(item_title) item_list.append((item_id, item_title, item_url)) if item_list > 0: parser.set('components', 'navadd.*', 'enabled') if 'navadd' not in parser.sections(): parser.add_section('navadd') parser.set('navadd', 'add_items', ','.join([i[0] for i in item_list])) for (uid, title, url) in item_list: parser.set('navadd', '%s.target' % uid, 'mainnav') parser.set('navadd', '%s.title' % uid, title) parser.set('navadd', '%s.url' % uid, url) # Enable and setup time tracking time_tracking = options.get('time-tracking-plugin', 'disabled').strip().lower() == 'enabled' if time_tracking: parser.set('components', 'timingandestimationplugin.*', 'enabled') # Enable and setup the stat plugin stats = options.get('stats-plugin', 'disabled').strip().lower() == 'enabled' if stats: parser.set('components', 'tracstats.*', 'enabled') ####################### # Final upgrades & sync ####################### # Apply custom parameters defined by the user custom_params = cleanMultiParams(options.get('trac-ini-additional', '')) for param in custom_params: if len(param) == 3: section = param[0] if section not in parser.sections(): parser.add_section(section) parser.set(section, param[1], param[2]) # Write the final trac.ini parser.write(open(trac_ini, 'w')) # Reload the environment env.shutdown() trac = TracAdmin(location) env = trac.env # Set custom permissions perm_sys = PermissionSystem(env) for cperm in cleanMultiParams(options.get('permissions', '')): if len(cperm) == 2: user = cperm[0] current_user_perms = perm_sys.get_user_permissions(user) perm_list = [p.upper() for p in cperm[1].split(' ') if len(p)] for perm in perm_list: if perm not in current_user_perms: perm_sys.grant_permission(user, perm) # Upgrade Trac instance to keep it fresh needs_upgrade = env.needs_upgrade() force_upgrade = getBool(options.get('force-instance-upgrade', 'False')) if needs_upgrade or force_upgrade: env.upgrade(backup=True) # Force repository resync repo_resync = getBool(options.get('force-repos-resync', 'False')) if repo_resync: rm = RepositoryManager(env) repositories = rm.get_real_repositories() for repos in sorted(repositories, key=lambda r: r.reponame): repos.sync(clean=True) # Upgrade default wiki pages embedded in Trac instance wiki_upgrade = getBool(options.get('wiki-doc-upgrade', 'False')) if wiki_upgrade: # Got the command below from trac/admin/console.py pages_dir = pkg_resources.resource_filename( 'trac.wiki', 'default-pages') WikiAdmin(env).load_pages(pages_dir, ignore=['WikiStart', 'checkwiki.py'], create_only=['InterMapTxt']) # Return files that were created by the recipe. The buildout # will remove all returned files upon reinstall. return tuple()
def render_admin_panel(self, req, cat, page, path_info): perm = PermissionSystem(self.env) all_permissions = perm.get_all_permissions() all_actions = perm.get_actions() if req.method == 'POST': subject = req.args.get('subject', '').strip() target = req.args.get('target', '').strip() action = req.args.get('action') group = req.args.get('group', '').strip() if subject and subject.isupper() or \ group and group.isupper() or \ target and target.isupper(): raise TracError(_("All upper-cased tokens are reserved for " "permission names.")) # Grant permission to subject if req.args.get('add') and subject and action: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') if action not in all_actions: raise TracError(_("Unknown action")) req.perm.require(action) if (subject, action) not in all_permissions: perm.grant_permission(subject, action) add_notice(req, _("The subject %(subject)s has been " "granted the permission %(action)s.", subject=subject, action=action)) req.redirect(req.href.admin(cat, page)) else: add_warning(req, _("The permission %(action)s was already " "granted to %(subject)s.", action=action, subject=subject)) # Add subject to group elif req.args.get('add') and subject and group: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') for action in perm.get_user_permissions(group): if not action in all_actions: # plugin disabled? self.env.log.warn("Adding %s to group %s: " "Permission %s unavailable, skipping perm check.", subject, group, action) else: req.perm.require(action, message=_("The subject %(subject)s was not added " "to the group %(group)s because the " "group has %(perm)s permission and " "users cannot grant permissions they " "don't possess.", subject=subject, group=group, perm=action)) if (subject, group) not in all_permissions: perm.grant_permission(subject, group) add_notice(req, _("The subject %(subject)s has been added " "to the group %(group)s.", subject=subject, group=group)) req.redirect(req.href.admin(cat, page)) else: add_warning(req, _("The subject %(subject)s was already " "added to the group %(group)s.", subject=subject, group=group)) # Copy permissions to subject elif req.args.get('copy') and subject and target: req.perm.require('PERMISSION_GRANT') subject_permissions = [i[1] for i in all_permissions if i[0] == subject and i[1].isupper()] if not subject_permissions: add_warning(req,_("The subject %(subject)s does not " "have any permissions.", subject=subject)) for action in subject_permissions: if (target, action) in all_permissions: continue if not action in all_actions: # plugin disabled? self.env.log.warn("Skipped granting %s to %s: " "permission unavailable.", action, target) else: if action not in req.perm: add_warning(req, _("The permission %(action)s was " "not granted to %(subject)s " "because users cannot grant " "permissions they don't possess.", action=action, subject=subject)) continue perm.grant_permission(target, action) add_notice(req, _("The subject %(subject)s has " "been granted the permission " "%(action)s.", subject=target, action=action)) req.redirect(req.href.admin(cat, page)) # Remove permissions action elif req.args.get('remove') and req.args.get('sel'): req.perm('admin', 'general/perm').require('PERMISSION_REVOKE') sel = req.args.get('sel') sel = sel if isinstance(sel, list) else [sel] for key in sel: subject, action = key.split(':', 1) subject = unicode_from_base64(subject) action = unicode_from_base64(action) if (subject, action) in perm.get_all_permissions(): perm.revoke_permission(subject, action) add_notice(req, _("The selected permissions have been " "revoked.")) req.redirect(req.href.admin(cat, page)) return 'admin_perms.html', { 'actions': all_actions, 'perms': perm.get_users_dict(), 'groups': perm.get_groups_dict(), 'unicode_to_base64': unicode_to_base64 }
def filter_stream(self, req, method, filename, stream, data): if filename not in ('ticket.html', 'ticket.rss'): return stream ticket_id = req.args.get('id') if not ticket_id: return stream # determine the username of the current user user = req.authname # determine if the user has the permission to see private comments perms = PermissionSystem(self.env) has_private_permission = \ self.private_comment_permission in perms.get_user_permissions(user) # Remove private comments from Ticket Page if filename == 'ticket.html': buf = StreamBuffer() def check_comments(): delimiter = '<div xmlns="http://www.w3.org/1999/xhtml" ' + \ 'class="change" id="trac-change-' comment_stream = str(buf) # split the comment_stream to get single comments comments_raw = comment_stream.split(delimiter) comment_stream = '' for comment in comments_raw: if comment is None or len(comment) < 1: continue # determine comment id find = comment.find('">') if find == -1: continue comment_id = comment[:find] # concat the delimiter and the comment again comment_code = delimiter + comment # if the user has the permission to see the comment # the comment_code will be appended to the comment_stream comment_private = self._is_comment_private( ticket_id, comment_id) if comment_private: comment_code = comment_code.replace( '<span class="threading">', '<span class="threading"> <span class="%s">' 'this comment is private</span>' % str(self.css_class_private_comment_marker)) if has_private_permission or not comment_private: comment_stream += comment_code return HTML(comment_stream) # filter all comments stream |= Transformer('//div[@class="change" and @id]') \ .copy(buf).replace(check_comments) # if the user has the private comment permission the checkboxes # to change the private value will be added if has_private_permission: comment_box = tag.label( _("Private Comment:"), tag.input(type='checkbox', name='private_comment')) stream |= Transformer('//h2[@id="trac-add-comment"]') \ .after(comment_box) # Trac 1.0 and later: # stream |= Transformer( # '//div[@id="trac-add-comment"]//fieldset').prepend(input) # Remove private comments from ticket RSS feed if filename == 'ticket.rss': comments = self._get_all_private_comments(ticket_id) self.log.debug("Private Comments for Ticket %d: %s" % (ticket_id, comments)) for comment_id in comments: stream |= Transformer('//item[%d]' % comment_id).remove() return stream
class TestAgiloPermissions(AgiloTestCase): def setUp(self): self.super() self.env.config.set('trac', 'permission_policies', 'AgiloPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy') self.perm = PermissionSystem(self.env) def test_roles(self): self.teh.grant_permission('master', 'SCRUM_MASTER') self.teh.grant_permission('owner', 'PRODUCT_OWNER') # test if contains main and sub permission # for scrum master permissions = self.perm.get_user_permissions('master') self.assert_true('SCRUM_MASTER' in permissions) self.assert_true(Action.SAVE_REMAINING_TIME in permissions) # for product owner permissions = self.perm.get_user_permissions('owner') self.assert_true('PRODUCT_OWNER' in permissions) self.assert_true(Action.CREATE_STORY in permissions) self.assert_true(Action.CREATE_REQUIREMENT in permissions) def test_ticket_permissions(self): # create user with the necessary permission self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(Type.TASK) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) # a team member should be able to save the remaining time for unassigned # tickets perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.SAVE_REMAINING_TIME) # Don't change the remaining time for tickets which belong to other # team members ticket[Key.OWNER] = "Just another team member" ticket.save_changes("foo", "bar") new_perm_cache = PermissionCache(self.env, name_team_member) self.assert_raises(PermissionError, new_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.SAVE_REMAINING_TIME) def test_edit_description_action_is_scoped_as_well(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) requirement = self.teh.create_ticket(Type.REQUIREMENT) requirement_resource = requirement.resource self.assert_false(perm.has_permission(Action.TICKET_EDIT_DESCRIPTION, requirement_resource)) def test_reporters_can_only_edit_unassigned_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) ticket = self.teh.create_ticket(Type.TASK) ticket[Key.REPORTER] = name_team_member ticket[Key.OWNER] = None some_minutes_ago = now() - timedelta(minutes=2) ticket.save_changes("foo", "bar", when=some_minutes_ago) self.assert_equals(name_team_member, ticket[Key.REPORTER]) self.assertNotEqual(name_team_member, ticket[Key.OWNER]) perm_cache = PermissionCache(self.env, name_team_member) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT_PAGE_ACCESS) def test_ticket_owner_or_resource_can_save_time(self): another_team_member = 'another team member' self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) self.teh.grant_permission(another_team_member, Role.TEAM_MEMBER) ticket = self.teh.create_ticket(Type.TASK, props={Key.OWNER: name_team_member, Key.REMAINING_TIME: '12'}) # Check that name_team_member can change remaining time new_perm_cache = PermissionCache(self.env, name_team_member) new_perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.SAVE_REMAINING_TIME) # Check that another_team_member can't change remaining time self.assert_true(another_team_member not in ticket.get_resource_list(include_owner=True)) new_perm_cache = PermissionCache(self.env, another_team_member) self.assert_raises(PermissionError, new_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.SAVE_REMAINING_TIME) def test_all_users_in_resources_can_edit_ticket(self): ticket = self.teh.create_ticket(Type.TASK) ticket[Key.OWNER] = "Just another team member" ticket[Key.RESOURCES] = " foo, %s, bar " % name_team_member ticket.save_changes("foo", "bar") self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) self.assertNotEqual(ticket[Key.OWNER], name_team_member) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) def test_attachement_permissions(self): # create user with the necessary permission self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) self.teh.grant_permission(name_product_owner, Role.PRODUCT_OWNER) ticket = self.teh.create_ticket(Type.REQUIREMENT) # None of the two users we use for testing must be the ticket's owner, # else they could always edit the ticket. ticket[Key.OWNER] = 'someone' ticket.save_changes('foo', 'we have to change the owner') # a product owner should be able to attach files to the ticket, but a # team member must not po_perm_cache = PermissionCache(self.env, name_product_owner) self.assert_true(Action.ATTACHMENT_CREATE in po_perm_cache(Realm.TICKET, ticket.id)) tm_perm_cache = PermissionCache(self.env, name_team_member) self.assert_raises(PermissionError, tm_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.ATTACHMENT_CREATE) def test_can_edit_ticket_if_he_can_create_referenced_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) story = self.teh.create_ticket(Type.USER_STORY) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, story.resource)) self.assert_false(perm_cache.has_permission(Action.TICKET_EDIT, story.resource)) # TODO: self.perm() as replacement for perm_cache... def _create_custom_ticket_type(self, type_name, field_names): custom_type = TicketType(self.env) custom_type.name = type_name custom_type.insert() config = AgiloConfig(self.env) config.change_option(type_name, field_names, section=AgiloConfig.AGILO_TYPES) config.reload() self.assert_true(type_name in config.get_available_types()) def test_can_edit_ticket_for_custom_types(self): custom_type_name = 'MaintenanceTask' self._create_custom_ticket_type(custom_type_name, [Key.COMPONENT]) permission_name = 'CREATE_' + custom_type_name.upper() self.teh.grant_permission(name_team_member, permission_name) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(custom_type_name) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT, ticket.resource)) self.assert_true(perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, ticket.resource)) def test_team_members_can_edit_bugs(self): bug = self.teh.create_ticket(Type.BUG, {Key.SUMMARY: 'A bug'}) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) self.assert_true(perm.has_permission(Action.TICKET_EDIT, bug.resource))
class TestAgiloPermissions(AgiloTestCase): def setUp(self): self.super() self.env.config.set( 'trac', 'permission_policies', 'AgiloPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy') self.perm = PermissionSystem(self.env) def test_roles(self): self.teh.grant_permission('master', 'SCRUM_MASTER') self.teh.grant_permission('owner', 'PRODUCT_OWNER') # test if contains main and sub permission # for scrum master permissions = self.perm.get_user_permissions('master') self.assert_true('SCRUM_MASTER' in permissions) self.assert_true(Action.SAVE_REMAINING_TIME in permissions) # for product owner permissions = self.perm.get_user_permissions('owner') self.assert_true('PRODUCT_OWNER' in permissions) self.assert_true(Action.CREATE_STORY in permissions) self.assert_true(Action.CREATE_REQUIREMENT in permissions) def test_ticket_permissions(self): # create user with the necessary permission self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(Type.TASK) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) # a team member should be able to save the remaining time for unassigned # tickets perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.SAVE_REMAINING_TIME) # Don't change the remaining time for tickets which belong to other # team members ticket[Key.OWNER] = "Just another team member" ticket.save_changes("foo", "bar") new_perm_cache = PermissionCache(self.env, name_team_member) self.assert_raises( PermissionError, new_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.SAVE_REMAINING_TIME) def test_edit_description_action_is_scoped_as_well(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) requirement = self.teh.create_ticket(Type.REQUIREMENT) requirement_resource = requirement.resource self.assert_false( perm.has_permission(Action.TICKET_EDIT_DESCRIPTION, requirement_resource)) def test_reporters_can_only_edit_unassigned_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) ticket = self.teh.create_ticket(Type.TASK) ticket[Key.REPORTER] = name_team_member ticket[Key.OWNER] = None some_minutes_ago = now() - timedelta(minutes=2) ticket.save_changes("foo", "bar", when=some_minutes_ago) self.assert_equals(name_team_member, ticket[Key.REPORTER]) self.assertNotEqual(name_team_member, ticket[Key.OWNER]) perm_cache = PermissionCache(self.env, name_team_member) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT_PAGE_ACCESS) def test_ticket_owner_or_resource_can_save_time(self): another_team_member = 'another team member' self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) self.teh.grant_permission(another_team_member, Role.TEAM_MEMBER) ticket = self.teh.create_ticket(Type.TASK, props={ Key.OWNER: name_team_member, Key.REMAINING_TIME: '12' }) # Check that name_team_member can change remaining time new_perm_cache = PermissionCache(self.env, name_team_member) new_perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.SAVE_REMAINING_TIME) # Check that another_team_member can't change remaining time self.assert_true(another_team_member not in ticket.get_resource_list( include_owner=True)) new_perm_cache = PermissionCache(self.env, another_team_member) self.assert_raises( PermissionError, new_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.SAVE_REMAINING_TIME) def test_all_users_in_resources_can_edit_ticket(self): ticket = self.teh.create_ticket(Type.TASK) ticket[Key.OWNER] = "Just another team member" ticket[Key.RESOURCES] = " foo, %s, bar " % name_team_member ticket.save_changes("foo", "bar") self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) self.assertNotEqual(ticket[Key.OWNER], name_team_member) perm_cache(Realm.TICKET, ticket.id).assert_permission(Action.TICKET_EDIT) def test_attachement_permissions(self): # create user with the necessary permission self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) self.teh.grant_permission(name_product_owner, Role.PRODUCT_OWNER) ticket = self.teh.create_ticket(Type.REQUIREMENT) # None of the two users we use for testing must be the ticket's owner, # else they could always edit the ticket. ticket[Key.OWNER] = 'someone' ticket.save_changes('foo', 'we have to change the owner') # a product owner should be able to attach files to the ticket, but a # team member must not po_perm_cache = PermissionCache(self.env, name_product_owner) self.assert_true( Action.ATTACHMENT_CREATE in po_perm_cache(Realm.TICKET, ticket.id)) tm_perm_cache = PermissionCache(self.env, name_team_member) self.assert_raises( PermissionError, tm_perm_cache(Realm.TICKET, ticket.id).assert_permission, Action.ATTACHMENT_CREATE) def test_can_edit_ticket_if_he_can_create_referenced_tickets(self): self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) story = self.teh.create_ticket(Type.USER_STORY) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, story.resource)) self.assert_false( perm_cache.has_permission(Action.TICKET_EDIT, story.resource)) # TODO: self.perm() as replacement for perm_cache... def _create_custom_ticket_type(self, type_name, field_names): custom_type = TicketType(self.env) custom_type.name = type_name custom_type.insert() config = AgiloConfig(self.env) config.change_option(type_name, field_names, section=AgiloConfig.AGILO_TYPES) config.reload() self.assert_true(type_name in config.get_available_types()) def test_can_edit_ticket_for_custom_types(self): custom_type_name = 'MaintenanceTask' self._create_custom_ticket_type(custom_type_name, [Key.COMPONENT]) permission_name = 'CREATE_' + custom_type_name.upper() self.teh.grant_permission(name_team_member, permission_name) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm_cache = PermissionCache(self.env, name_team_member) ticket = self.teh.create_ticket(custom_type_name) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT, ticket.resource)) self.assert_true( perm_cache.has_permission(Action.TICKET_EDIT_PAGE_ACCESS, ticket.resource)) def test_team_members_can_edit_bugs(self): bug = self.teh.create_ticket(Type.BUG, {Key.SUMMARY: 'A bug'}) self.teh.grant_permission(name_team_member, Role.TEAM_MEMBER) perm = PermissionCache(self.env, name_team_member) self.assert_true(perm.has_permission(Action.TICKET_EDIT, bug.resource))
def update_tracenv_userdata(self, req, path, userdata): """Update the userdata in the specified environment using the records passed by userdata. @param path : path to the trac environment to update @param userdata : collection of userdata as returned from merge() @return success : boolean @return msg : details """ sql = [] exists = '' msg = '' envpath, tracenv = os.path.split(path) self.env.log.debug('Updating userdata in environment %s' % (path,)) dryrun = self.env.config.getbool('user_sync','dryrun',True) if not dryrun: self.env.log.debug('HOT!!! We are NOT in dryrun mode!') else: self.env.log.debug('TESTING - we ARE in dryrun mode.') try: env = Environment(path) except IOError: self.env.log.debug('Could not initialize environment at %s' % (path,)) return False, 'Could not initialize environment at %s' % (path,) perm = PermissionSystem(env) if not 'TRAC_ADMIN' in perm.get_user_permissions(req.perm.username): raise PermissionError db = env.get_db_cnx() cursor = db.cursor() if not dryrun: self.env.log.debug('Updating database for %s' % (tracenv,)) for user in userdata: authenticated = userdata[user]['authenticated'] or 0 for att in userdata[user]: if att in ['path','authenticated','last_visit']: continue cursor.execute("SELECT value FROM session_attribute WHERE sid='%s' AND name='%s' AND authenticated=%s" % (user,att,authenticated,)) for row in cursor: exists = row[0] if exists: if exists == userdata[user][att]: continue if not dryrun: cursor.execute("UPDATE session_attribute SET value='%s' WHERE sid='%s' AND name='%s' AND authenticated=%s;\n" % (userdata[user][att],user,att,authenticated,)) sql.append("UPDATE session_attribute SET value='%s' WHERE sid='%s' AND name='%s' AND authenticated=%s;\n" % (userdata[user][att],user,att,authenticated,)) else: if not dryrun: cursor.execute("INSERT INTO session_attribute (sid,authenticated,name,value) VALUES('%s',%s,'%s','%s');\n" % (user,authenticated,att,userdata[user][att])) sql.append("INSERT INTO session_attribute (sid,authenticated,name,att) VALUES('%s',%s,'%s','%s');\n" % (user,authenticated,att,userdata[user][att])) if len(sql): if not dryrun: db.commit() sql_file_path = self.env.config.get('user_sync','sql_file_path') or os.path.join(self.env.path,'log') if sql_file_path.lower() == 'none': self.env.log.debug('SQLFile disabled (sql_file_path is "none")') else: sqlfile = '%s.sql' % (tracenv,) sqlfile = os.path.join(sql_file_path,sqlfile) try: if os.path.exists(sqlfile): os.unlink(sqlfile) self.env.log.debug('Writing SQL to %s' % (sqlfile,)) f = open(sqlfile,'a') f.write('--- SQL for Trac environment %s\n' % (tracenv,)); f.writelines(sql) f.close() except IOError: self.env.log.debug('Could not write SQL file %s!' % (sqlfile,)) return False, 'Could not write SQL file %s!' % (sqlfile,) except ValueError: self.env.log.debug('No value for sqlfile?') if dryrun: msg = 'Wrote SQL for Trac environment %s to %s' % (tracenv,sqlfile,) else: msg = 'Updated userdata in environment %s. SQL was additionally written to %s' % (tracenv,sqlfile,) else: msg = 'No updates for Trac environment %s' % (tracenv) self.env.log.debug('Done updating userdata in environment %s' % (path,)) return True, msg
def add_locale(self, req): data = {} errors = [] catalog_template_id = req.args.get('catalog_template', None) locale = req.args.get('locale') locale_admins = req.args.getlist('admins') def add_error(error): errors.append(error) data['error'] = tag.ul(*[tag.li(e) for e in errors if e]) data['locale'] = locale return data if not catalog_template_id: errors.append(_("You must first create a catalog template")) if not locale: return add_error(_("You must define the new catalog's locale")) if not locale_admins: return add_error(_("You must define at least one locale admin")) Session = session(self.env) catalog = Session.query(Catalog).get(catalog_template_id) num_plurals = get_plural(locale=locale).num_plurals _locale = Session.query(Locale).filter_by(locale=locale, catalog_id=catalog.id).first() if _locale: data['locale'] = _locale return add_error(_("Locale Exists Already")) locale = Locale(catalog, locale, num_plurals) for sid in locale_admins: locale.admins.append(LocaleAdmin(locale, sid)) catalog.locales.append(locale) Session.commit() perm = PermissionSystem(self.env) sids_without_necessary_perms = [] for admin in locale.admins: if not 'L10N_MODERATE' in perm.get_user_permissions(admin.sid): sids_without_necessary_perms.append(admin.sid) if sids_without_necessary_perms: msg = ngettext( "%s does not have the required permissions to administrate." % \ ', '.join(["'%s'" % s for s in sids_without_necessary_perms]), "%s don't have the required permissions to administrate." % \ ', '.join(["'%s'" % s for s in sids_without_necessary_perms]), len(sids_without_necessary_perms)) add_error(tag(msg, _(" Don't forget to "), tag.a(_('update permissions'), href=req.href.admin('general', 'perm')), '.')) add_notice(req, _("Locale added.")) # Are we importing existing data locale_catalog_path = req.args.get('catalog') include_fuzzy = req.args.get('include_fuzzy') == '1' if not locale_catalog_path or locale_catalog_path == '/': return data repos = self.env.get_repository(req.authname) revision = repos.youngest_rev try: node = get_existing_node(req, repos, locale_catalog_path, revision) except NoSuchChangeset, e: raise ResourceNotFound(e.message, _('Invalid Changeset Number'))
def render_admin_panel(self, req, cat, page, path_info): perm = PermissionSystem(self.env) all_permissions = perm.get_all_permissions() all_actions = perm.get_actions() if req.method == 'POST': subject = req.args.get('subject', '').strip() action = req.args.get('action') group = req.args.get('group', '').strip() if subject and subject.isupper() or \ group and group.isupper(): raise TracError(_('All upper-cased tokens are reserved for ' 'permission names')) # Grant permission to subject if req.args.get('add') and subject and action: req.perm.require('PERMISSION_GRANT') if action not in all_actions: raise TracError(_('Unknown action')) req.perm.require(action) if (subject, action) not in all_permissions: perm.grant_permission(subject, action) add_notice(req, _('The subject %(subject)s has been ' 'granted the permission %(action)s.', subject=subject, action=action)) req.redirect(req.href.admin(cat, page)) else: add_warning(req, _('The permission %(action)s was already ' 'granted to %(subject)s.', action=action, subject=subject)) # Add subject to group elif req.args.get('add') and subject and group: req.perm.require('PERMISSION_GRANT') for action in perm.get_user_permissions(group): if not action in all_actions: # plugin disabled? self.env.log.warn("Adding %s to group %s: " \ "Permission %s unavailable, skipping perm check." \ % (subject, group, action)) else: req.perm.require(action) if (subject, group) not in all_permissions: perm.grant_permission(subject, group) add_notice(req, _('The subject %(subject)s has been added ' 'to the group %(group)s.', subject=subject, group=group)) req.redirect(req.href.admin(cat, page)) else: add_warning(req, _('The subject %(subject)s was already ' 'added to the group %(group)s.', subject=subject, group=group)) # Remove permissions action elif req.args.get('remove') and req.args.get('sel'): req.perm.require('PERMISSION_REVOKE') sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] for key in sel: subject, action = key.split(':', 1) if (subject, action) in perm.get_all_permissions(): perm.revoke_permission(subject, action) add_notice(req, _('The selected permissions have been ' 'revoked.')) req.redirect(req.href.admin(cat, page)) return 'admin_perms.html', { 'actions': all_actions, 'perms': all_permissions }
def update_tracenv_userdata(self, req, path, userdata): """Update the userdata in the specified environment using the records passed by userdata. @param path : path to the trac environment to update @param userdata : collection of userdata as returned from merge() @return success : boolean @return msg : details """ sql = [] exists = '' msg = '' envpath, tracenv = os.path.split(path) self.env.log.debug('Updating userdata in environment %s' % (path, )) dryrun = self.env.config.getbool('user_sync', 'dryrun', True) if not dryrun: self.env.log.debug('HOT!!! We are NOT in dryrun mode!') else: self.env.log.debug('TESTING - we ARE in dryrun mode.') try: env = Environment(path) except IOError: self.env.log.debug('Could not initialize environment at %s' % (path, )) return False, 'Could not initialize environment at %s' % (path, ) perm = PermissionSystem(env) if not 'TRAC_ADMIN' in perm.get_user_permissions(req.perm.username): raise PermissionError db = env.get_db_cnx() cursor = db.cursor() if not dryrun: self.env.log.debug('Updating database for %s' % (tracenv, )) for user in userdata: authenticated = userdata[user]['authenticated'] or 0 for att in userdata[user]: if att in ['path', 'authenticated', 'last_visit']: continue cursor.execute( "SELECT value FROM session_attribute WHERE sid='%s' AND name='%s' AND authenticated=%s" % ( user, att, authenticated, )) for row in cursor: exists = row[0] if exists: if exists == userdata[user][att]: continue if not dryrun: cursor.execute( "UPDATE session_attribute SET value='%s' WHERE sid='%s' AND name='%s' AND authenticated=%s;\n" % ( userdata[user][att], user, att, authenticated, )) sql.append( "UPDATE session_attribute SET value='%s' WHERE sid='%s' AND name='%s' AND authenticated=%s;\n" % ( userdata[user][att], user, att, authenticated, )) else: if not dryrun: cursor.execute( "INSERT INTO session_attribute (sid,authenticated,name,value) VALUES('%s',%s,'%s','%s');\n" % (user, authenticated, att, userdata[user][att])) sql.append( "INSERT INTO session_attribute (sid,authenticated,name,att) VALUES('%s',%s,'%s','%s');\n" % (user, authenticated, att, userdata[user][att])) if len(sql): if not dryrun: db.commit() sql_file_path = self.env.config.get( 'user_sync', 'sql_file_path') or os.path.join( self.env.path, 'log') if sql_file_path.lower() == 'none': self.env.log.debug( 'SQLFile disabled (sql_file_path is "none")') else: sqlfile = '%s.sql' % (tracenv, ) sqlfile = os.path.join(sql_file_path, sqlfile) try: if os.path.exists(sqlfile): os.unlink(sqlfile) self.env.log.debug('Writing SQL to %s' % (sqlfile, )) f = open(sqlfile, 'a') f.write('--- SQL for Trac environment %s\n' % (tracenv, )) f.writelines(sql) f.close() except IOError: self.env.log.debug('Could not write SQL file %s!' % (sqlfile, )) return False, 'Could not write SQL file %s!' % (sqlfile, ) except ValueError: self.env.log.debug('No value for sqlfile?') if dryrun: msg = 'Wrote SQL for Trac environment %s to %s' % ( tracenv, sqlfile, ) else: msg = 'Updated userdata in environment %s. SQL was additionally written to %s' % ( tracenv, sqlfile, ) else: msg = 'No updates for Trac environment %s' % (tracenv) self.env.log.debug('Done updating userdata in environment %s' % (path, )) return True, msg
def render_admin_panel(self, req, cat, page, path_info): perm = PermissionSystem(self.env) all_actions = perm.get_actions() if req.method == 'POST': subject = req.args.get('subject', '').strip() target = req.args.get('target', '').strip() action = req.args.get('action') group = req.args.get('group', '').strip() if subject and subject.isupper() or \ group and group.isupper() or \ target and target.isupper(): raise TracError( _("All upper-cased tokens are reserved for " "permission names.")) # Grant permission to subject if 'add' in req.args and subject and action: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') if action not in all_actions: raise TracError(_("Unknown action")) req.perm.require(action) try: perm.grant_permission(subject, action) except TracError as e: add_warning(req, e) else: add_notice( req, _( "The subject %(subject)s has been " "granted the permission %(action)s.", subject=subject, action=action)) # Add subject to group elif 'add' in req.args and subject and group: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') for action in perm.get_user_permissions(group): req.perm.require( action, message=_( "The subject %(subject)s was not added to " "the group %(group)s because the group has " "%(perm)s permission and users cannot grant " "permissions they don't possess.", subject=subject, group=group, perm=action)) try: perm.grant_permission(subject, group) except TracError as e: add_warning(req, e) else: add_notice( req, _( "The subject %(subject)s has been " "added to the group %(group)s.", subject=subject, group=group)) # Copy permissions to subject elif 'copy' in req.args and subject and target: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') subject_permissions = perm.get_users_dict().get(subject, []) if not subject_permissions: add_warning( req, _( "The subject %(subject)s does not " "have any permissions.", subject=subject)) for action in subject_permissions: if action not in all_actions: # plugin disabled? self.log.warning( "Skipped granting %s to %s: " "permission unavailable.", action, target) else: if action not in req.perm: add_warning( req, _( "The permission %(action)s was " "not granted to %(subject)s " "because users cannot grant " "permissions they don't possess.", action=action, subject=subject)) continue try: perm.grant_permission(target, action) except PermissionExistsError: pass else: add_notice( req, _( "The subject %(subject)s has " "been granted the permission " "%(action)s.", subject=target, action=action)) req.redirect(req.href.admin(cat, page)) # Remove permissions action elif 'remove' in req.args and 'sel' in req.args: req.perm('admin', 'general/perm').require('PERMISSION_REVOKE') for key in req.args.getlist('sel'): subject, action = key.split(':', 1) subject = unicode_from_base64(subject) action = unicode_from_base64(action) if (subject, action) in perm.get_all_permissions(): perm.revoke_permission(subject, action) add_notice(req, _("The selected permissions have been " "revoked.")) req.redirect(req.href.admin(cat, page)) return 'admin_perms.html', { 'actions': all_actions, 'allowed_actions': [a for a in all_actions if a in req.perm], 'perms': perm.get_users_dict(), 'groups': perm.get_groups_dict(), 'unicode_to_base64': unicode_to_base64 }
def install(self): """Installer""" # Utility function to interpreted boolean option value getBool = lambda s: s.strip().lower() in ['true', 'yes'] # Utility function to parse a multi-line/multi-value parameter def cleanMultiParams(v): params = [s.split('|') for s in [l.strip() for l in v.split('\n')] if len(s) > 0] cleaned_params = [] for line in params: cleaned_params.append([row.strip() for row in line]) return cleaned_params # Utility function to transform any string to an ID getId = lambda s: ''.join([c for c in s if c.isalnum()]).lower() options = self.options ################# # eggs from the config options ################ requirements, ws = self.egg.working_set() for dist in pkg_resources.working_set: ws.add(dist) # Add command line scripts trac-admin and tracd into bin entry_points = [('trac-admin', 'trac.admin.console', 'run'), ('tracd', 'trac.web.standalone', 'main')] zc.buildout.easy_install.scripts( entry_points, ws, options['executable'], options['bin-directory'] ) #################### # Init Trac instance #################### # Generate the trac instance, if required location = options['location'] project_name = options.get('project-name', 'My project') project_url = options.get('project-url', 'http://example.com') db = 'sqlite:%s' % os.path.join('db', 'trac.db') if not os.path.exists(location): os.mkdir(location) trac = TracAdmin(location) if not trac.env_check(): trac.do_initenv('"%s" %s' % (project_name, db)) env = trac.env # Remove Trac default example data clean_up = getBool(options.get('remove-examples', 'True')) if clean_up: # Remove default milestones for mil in Milestone.select(env): if mil.name in ['milestone1', 'milestone2', 'milestone3', 'milestone4']: mil.delete() # Remove default components for comp in Component.select(env): if comp.name in ['component1', 'component2']: comp.delete() # Add custom milestones for mil_data in cleanMultiParams(options.get('milestones', '')): mil_name = mil_data[0] try: mil = Milestone(env, name=mil_name) except ResourceNotFound: mil = Milestone(env) mil.name = mil_name mil.insert() # Add custom components for comp_data in cleanMultiParams(options.get('components', '')): comp_name = comp_data[0] try: comp = Component(env, name=comp_name) except ResourceNotFound: comp = Component(env) comp.name = comp_name if len(comp_data) == 2 and comp_data[1] not in [None, '']: comp.owner = comp_data[1] comp.insert() ####################### # Generate the trac.ini ####################### # Read the trac.ini config file trac_ini = os.path.join(location, 'conf', 'trac.ini') parser = ConfigParser.ConfigParser() parser.read([trac_ini]) # Clean-up trac.ini: add missing stuff if 'components' not in parser.sections(): parser.add_section('components') # Force upgrade of informations used during initialization parser.set('project', 'name', project_name) # Set all repositories repos = cleanMultiParams(options.get('repos', None)) repo_names = [getId(r[0]) for r in repos] repo_types = {}.fromkeys([r[1].lower() for r in repos]).keys() if 'repositories' not in parser.sections(): parser.add_section('repositories') for repo in repos: repo_name = getId(repo[0]) repo_type = repo[1] repo_dir = repo[2] repo_url = repo[3] parser.set('repositories', '%s.type' % repo_name, repo_type) parser.set('repositories', '%s.dir' % repo_name, repo_dir) if repo_url not in ['', None]: parser.set('repositories', '%s.url' % repo_name, repo_url) # Set default repository default_repo = getId(options.get('default-repo', None)) if default_repo and default_repo in repo_names: parser.set('repositories', '.alias', default_repo) parser.set('repositories', '.hidden', 'true') # Set repository sync method sync_method = options.get('repos-sync', 'request').strip().lower() svn_repos = [getId(r[0]) for r in repos if r[1] == 'svn'] if sync_method == 'request': parser.set('trac', 'repository_sync_per_request', ', '.join(svn_repos)) # TODO # elif sync_method == 'hook': # do stuff... # Set project description project_descr = options.get('project-description', None) if project_descr: parser.set('project', 'descr', project_descr) parser.set('header_logo', 'alt', project_descr) # Setup logo header_logo = options.get('header-logo', '') header_logo = os.path.realpath(header_logo) if os.path.exists(header_logo): shutil.copyfile(header_logo, os.path.join(location, 'htdocs', 'logo')) parser.set('header_logo', 'src', 'site/logo') parser.set('header_logo', 'link', project_url) # Set footer message parser.set('project', 'footer', options.get('footer-message', 'This Trac instance was generated by <a href="http://pypi.python.org/pypi/pbp.recipe.trac">pbp.recipe.trac</a>.')) # SMTP parameters for name in ('always-bcc', 'always-cc', 'default-domain', 'enabled', 'from', 'from-name', 'password', 'port', 'replyto', 'server', 'subject-prefix', 'user'): param_name = "smtp-%s" % name default_value = None if param_name == "smtp-from-name": default_value = project_name value = options.get(param_name, default_value) if value is not None: parser.set('notification', param_name.replace('-', '_'), value) ############### # Plugins setup ############### # If one repository use Mercurial, hook its plugin if 'hg' in repo_types: parser.set('components', 'tracext.hg.*', 'enabled') # Configure the NavAdd plugin menu_items = cleanMultiParams(options.get('additional-menu-items', '')) item_list = [] for item in menu_items: item_title = item[0] item_url = item[1] item_id = getId(item_title) item_list.append((item_id, item_title, item_url)) if item_list > 0: parser.set('components', 'navadd.*', 'enabled') if 'navadd' not in parser.sections(): parser.add_section('navadd') parser.set('navadd', 'add_items', ','.join([i[0] for i in item_list])) for (uid, title, url) in item_list: parser.set('navadd', '%s.target' % uid, 'mainnav') parser.set('navadd', '%s.title' % uid, title) parser.set('navadd', '%s.url' % uid, url) # Enable and setup time tracking time_tracking = options.get('time-tracking-plugin', 'disabled').strip().lower() == 'enabled' if time_tracking: parser.set('components', 'timingandestimationplugin.*', 'enabled') # Enable and setup the stat plugin stats = options.get('stats-plugin', 'disabled').strip().lower() == 'enabled' if stats: parser.set('components', 'tracstats.*', 'enabled') ####################### # Final upgrades & sync ####################### # Apply custom parameters defined by the user custom_params = cleanMultiParams(options.get('trac-ini-additional', '')) for param in custom_params: if len(param) == 3: section = param[0] if section not in parser.sections(): parser.add_section(section) parser.set(section, param[1], param[2]) # Write the final trac.ini parser.write(open(trac_ini, 'w')) # Reload the environment env.shutdown() trac = TracAdmin(location) env = trac.env # Set custom permissions perm_sys = PermissionSystem(env) for cperm in cleanMultiParams(options.get('permissions', '')): if len(cperm) == 2: user = cperm[0] current_user_perms = perm_sys.get_user_permissions(user) perm_list = [p.upper() for p in cperm[1].split(' ') if len(p)] for perm in perm_list: if perm not in current_user_perms: perm_sys.grant_permission(user, perm) # Upgrade Trac instance to keep it fresh needs_upgrade = env.needs_upgrade() force_upgrade = getBool(options.get('force-instance-upgrade', 'False')) if needs_upgrade or force_upgrade: env.upgrade(backup=True) # Force repository resync repo_resync = getBool(options.get('force-repos-resync', 'False')) if repo_resync: rm = RepositoryManager(env) repositories = rm.get_real_repositories() for repos in sorted(repositories, key=lambda r: r.reponame): repos.sync(clean=True) # Upgrade default wiki pages embedded in Trac instance wiki_upgrade = getBool(options.get('wiki-doc-upgrade', 'False')) if wiki_upgrade: # Got the command below from trac/admin/console.py pages_dir = pkg_resources.resource_filename('trac.wiki', 'default-pages') WikiAdmin(env).load_pages( pages_dir , ignore=['WikiStart', 'checkwiki.py'] , create_only=['InterMapTxt'] ) # Return files that were created by the recipe. The buildout # will remove all returned files upon reinstall. return tuple()
def get_tracenv_userdata(self, req, path, userlist=''): """Retrieve account data from the environment at the specified path @param path path to the environment @param userlist comma separated list of users to restrict the result to (e.g. the users from the password file), each user enclosed in single quotes (for SQL) @return array (empty array if the environment uses a different password file than the master env calling us) """ self.env.log.debug('Get user data from %s' % (path, )) data = {} env = Environment(path) # if this environment uses a different password file, we return an empty dataset if self.env.config.get('account-manager', 'password_file') != env.config.get( 'account-manager', 'password_file'): self.env.log.info( 'Password files do not match, skipping environment %s' % (path, )) return data perm = PermissionSystem(env) if not 'TRAC_ADMIN' in perm.get_user_permissions(req.perm.username): raise PermissionError db = env.get_db_cnx() cursor = db.cursor() sync_fields = self.env.config.getlist('user_sync', 'sync_fields') attr = "'" + "','".join( sync_fields ) + "','email_verification_sent_to','email_verification_token'" self.env.log.debug('* Checking attributes: %s' % (attr, )) if userlist: cursor.execute( "SELECT sid,name,value FROM session_attribute WHERE sid IN (%s) AND name IN (%s)" % ( userlist, attr, )) else: cursor.execute( "SELECT sid,name,value FROM session_attribute WHERE name IN (%s)" % (attr, )) for row in cursor: if not row[0] in data: data[row[0]] = {} data[row[0]][row[1]] = row[2] for sid in data.iterkeys(): no_data = True for att in sync_fields: if att in data[sid]: no_data = False break if no_data: self.env.log.debug('No data for %s in %s' % ( sid, path, )) data[sid] = Null continue data[sid]['path'] = path cursor.execute( "SELECT authenticated FROM session_attribute WHERE sid='%s'" % (sid, )) for row in cursor: data[sid]['authenticated'] = row[0] cursor.execute( "SELECT datetime(last_visit,'unixepoch') AS last_visit FROM session WHERE sid='%s'" % (sid, )) for row in cursor: data[sid]['last_visit'] = row[0] return data
def filter_stream(self, req, method, filename, stream, data): if filename not in ('ticket.html', 'ticket.rss'): return stream ticket_id = req.args.get('id') if not ticket_id: return stream # determine the username of the current user user = req.authname # determine if the user has the permission to see private comments perms = PermissionSystem(self.env) has_private_permission = \ self.private_comment_permission in perms.get_user_permissions(user) # Remove private comments from Ticket Page if filename == 'ticket.html': buf = StreamBuffer() def check_comments(): delimiter = '<div xmlns="http://www.w3.org/1999/xhtml" ' + \ 'class="change" id="trac-change-' comment_stream = str(buf) # split the comment_stream to get single comments comments_raw = comment_stream.split(delimiter) comment_stream = '' for comment in comments_raw: if comment is None or len(comment) < 1: continue # determine comment id find = comment.find('">') if find == -1: continue comment_id = comment[:find] # concat the delimiter and the comment again comment_code = delimiter + comment # if the user has the permission to see the comment # the comment_code will be appended to the comment_stream comment_private = self._is_comment_private(ticket_id, comment_id) if comment_private: comment_code = comment_code.replace( '<span class="threading">', '<span class="threading"> <span class="%s">' 'this comment is private</span>' % str(self.css_class_private_comment_marker) ) if has_private_permission or not comment_private: comment_stream += comment_code return HTML(comment_stream) # filter all comments stream |= Transformer('//div[@class="change" and @id]') \ .copy(buf).replace(check_comments) # if the user has the private comment permission the checkboxes # to change the private value will be added if has_private_permission: comment_box = tag.label(_("Private Comment:"), tag.input(type='checkbox', name='private_comment')) stream |= Transformer('//h2[@id="trac-add-comment"]') \ .after(comment_box) # Trac 1.0 and later: # stream |= Transformer( # '//div[@id="trac-add-comment"]//fieldset').prepend(input) # Remove private comments from ticket RSS feed if filename == 'ticket.rss': comments = self._get_all_private_comments(ticket_id) self.log.debug("Private Comments for Ticket %d: %s" % (ticket_id, comments)) for comment_id in comments: stream |= Transformer('//item[%d]' % comment_id).remove() return stream
def render_admin_panel(self, req, cat, page, path_info): perm = PermissionSystem(self.env) all_permissions = perm.get_all_permissions() all_actions = perm.get_actions() if req.method == 'POST': subject = req.args.get('subject', '').strip() action = req.args.get('action') group = req.args.get('group', '').strip() if subject and subject.isupper() or \ group and group.isupper(): raise TracError( _('All upper-cased tokens are reserved for ' 'permission names')) # Grant permission to subject if req.args.get('add') and subject and action: req.perm.require('PERMISSION_GRANT') if action not in all_actions: raise TracError(_('Unknown action')) req.perm.require(action) if (subject, action) not in all_permissions: perm.grant_permission(subject, action) add_notice( req, _( 'The subject %(subject)s has been ' 'granted the permission %(action)s.', subject=subject, action=action)) req.redirect(req.href.admin(cat, page)) else: add_warning( req, _( 'The permission %(action)s was already ' 'granted to %(subject)s.', action=action, subject=subject)) # Add subject to group elif req.args.get('add') and subject and group: req.perm.require('PERMISSION_GRANT') for action in perm.get_user_permissions(group): if not action in all_actions: # plugin disabled? self.env.log.warn("Adding %s to group %s: " \ "Permission %s unavailable, skipping perm check." \ % (subject, group, action)) else: req.perm.require(action) if (subject, group) not in all_permissions: perm.grant_permission(subject, group) add_notice( req, _( 'The subject %(subject)s has been added ' 'to the group %(group)s.', subject=subject, group=group)) req.redirect(req.href.admin(cat, page)) else: add_warning( req, _( 'The subject %(subject)s was already ' 'added to the group %(group)s.', subject=subject, group=group)) # Remove permissions action elif req.args.get('remove') and req.args.get('sel'): req.perm.require('PERMISSION_REVOKE') sel = req.args.get('sel') sel = sel if isinstance(sel, list) else [sel] for key in sel: subject, action = key.split(':', 1) subject = unicode_from_base64(subject) action = unicode_from_base64(action) if (subject, action) in perm.get_all_permissions(): perm.revoke_permission(subject, action) add_notice(req, _('The selected permissions have been ' 'revoked.')) req.redirect(req.href.admin(cat, page)) perms = [perm for perm in all_permissions if perm[1].isupper()] groups = [perm for perm in all_permissions if not perm[1].isupper()] return 'admin_perms.html', { 'actions': all_actions, 'perms': perms, 'groups': groups, 'unicode_to_base64': unicode_to_base64 }
def render_admin_panel(self, req, cat, page, path_info): perm = PermissionSystem(self.env) all_permissions = perm.get_all_permissions() all_actions = perm.get_actions() if req.method == 'POST': subject = req.args.get('subject', '').strip() target = req.args.get('target', '').strip() action = req.args.get('action') group = req.args.get('group', '').strip() if subject and subject.isupper() or \ group and group.isupper() or \ target and target.isupper(): raise TracError( _("All upper-cased tokens are reserved for " "permission names.")) # Grant permission to subject if req.args.get('add') and subject and action: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') if action not in all_actions: raise TracError(_("Unknown action")) req.perm.require(action) if (subject, action) not in all_permissions: perm.grant_permission(subject, action) add_notice( req, _( "The subject %(subject)s has been " "granted the permission %(action)s.", subject=subject, action=action)) req.redirect(req.href.admin(cat, page)) else: add_warning( req, _( "The permission %(action)s was already " "granted to %(subject)s.", action=action, subject=subject)) # Add subject to group elif req.args.get('add') and subject and group: req.perm('admin', 'general/perm').require('PERMISSION_GRANT') for action in perm.get_user_permissions(group): if not action in all_actions: # plugin disabled? self.env.log.warn( "Adding %s to group %s: " "Permission %s unavailable, skipping perm check.", subject, group, action) else: req.perm.require( action, message=_( "The subject %(subject)s was not added " "to the group %(group)s because the " "group has %(perm)s permission and " "users cannot grant permissions they " "don't possess.", subject=subject, group=group, perm=action)) if (subject, group) not in all_permissions: perm.grant_permission(subject, group) add_notice( req, _( "The subject %(subject)s has been added " "to the group %(group)s.", subject=subject, group=group)) req.redirect(req.href.admin(cat, page)) else: add_warning( req, _( "The subject %(subject)s was already " "added to the group %(group)s.", subject=subject, group=group)) # Copy permissions to subject elif req.args.get('copy') and subject and target: req.perm.require('PERMISSION_GRANT') subject_permissions = [ i[1] for i in all_permissions if i[0] == subject and i[1].isupper() ] if not subject_permissions: add_warning( req, _( "The subject %(subject)s does not " "have any permissions.", subject=subject)) for action in subject_permissions: if (target, action) in all_permissions: continue if not action in all_actions: # plugin disabled? self.env.log.warn( "Skipped granting %s to %s: " "permission unavailable.", action, target) else: if action not in req.perm: add_warning( req, _( "The permission %(action)s was " "not granted to %(subject)s " "because users cannot grant " "permissions they don't possess.", action=action, subject=subject)) continue perm.grant_permission(target, action) add_notice( req, _( "The subject %(subject)s has " "been granted the permission " "%(action)s.", subject=target, action=action)) req.redirect(req.href.admin(cat, page)) # Remove permissions action elif req.args.get('remove') and req.args.get('sel'): req.perm('admin', 'general/perm').require('PERMISSION_REVOKE') sel = req.args.get('sel') sel = sel if isinstance(sel, list) else [sel] for key in sel: subject, action = key.split(':', 1) subject = unicode_from_base64(subject) action = unicode_from_base64(action) if (subject, action) in perm.get_all_permissions(): perm.revoke_permission(subject, action) add_notice(req, _("The selected permissions have been " "revoked.")) req.redirect(req.href.admin(cat, page)) perms = [perm for perm in all_permissions if perm[1].isupper()] groups = [perm for perm in all_permissions if not perm[1].isupper()] return 'admin_perms.html', { 'actions': all_actions, 'perms': perms, 'groups': groups, 'unicode_to_base64': unicode_to_base64 }
def get_ticket_fields(self): """Returns the list of fields available for tickets.""" from trac.ticket import model db = self.env.get_db_cnx() fields = [] # Basic text fields for name in ('summary', 'reporter'): field = {'name': name, 'type': 'text', 'label': name.title()} fields.append(field) # Owner field, can be text or drop-down depending on configuration field = {'name': 'owner', 'label': 'Owner'} if self.restrict_owner: field['type'] = 'select' users = [] perm = PermissionSystem(self.env) for username, name, email in self.env.get_known_users(db): if perm.get_user_permissions(username).get('TICKET_MODIFY'): users.append(username) field['options'] = users field['optional'] = True else: field['type'] = 'text' fields.append(field) # Description fields.append({'name': 'description', 'type': 'textarea', 'label': 'Description'}) # Default select and radio fields selects = [('type', model.Type), ('status', model.Status), ('priority', model.Priority), ('milestone', model.Milestone), ('component', model.Component), ('version', model.Version), ('severity', model.Severity), ('resolution', model.Resolution)] for name, cls in selects: options = [val.name for val in cls.select(self.env, db=db)] if not options: # Fields without possible values are treated as if they didn't # exist continue field = {'name': name, 'type': 'select', 'label': name.title(), 'value': self.config.get('ticket', 'default_' + name), 'options': options} if name in ('status', 'resolution'): field['type'] = 'radio' field['optional'] = True elif name in ('milestone', 'version'): field['optional'] = True fields.append(field) # Advanced text fields for name in ('keywords', 'cc', ): field = {'name': name, 'type': 'text', 'label': name.title()} fields.append(field) for field in self.get_custom_fields(): if field['name'] in [f['name'] for f in fields]: self.log.warning('Duplicate field name "%s" (ignoring)', field['name']) continue if not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', field['name']): self.log.warning('Invalid name for custom field: "%s" ' '(ignoring)', field['name']) continue field['custom'] = True fields.append(field) return fields
def invoke(self, message, warnings): """reply to a ticket""" ticket = self.ticket reporter = self._reporter(message) # get the mailBody and attachments mailBody, attachments = get_body_and_attachments(message) if not mailBody: warnings.append("Seems to be a reply to %s but I couldn't find a comment") return message #go throught work ts = TicketSystem(self.env) tm = TicketModule(self.env) perm = PermissionSystem(self.env) # TODO: Deprecate update without time_changed timestamp mockReq = self._MockReq(perm.get_user_permissions(reporter), reporter) avail_actions = ts.get_available_actions(mockReq, ticket) mailBody, inBodyFields, actions = self._get_in_body_fields(mailBody, avail_actions, reporter) if inBodyFields or actions : # check permissions perm = PermissionSystem(self.env) #we have properties movement, cheking user permission to do so if not perm.check_permission('MAIL2TICKET_PROPERTIES', reporter) : # None -> 'anoymous' raise ("%s does not have MAIL2TICKET_PROPERTIES permissions" % (user or 'anonymous')) action = None if actions : action = actions.keys()[0] controllers = list(tm._get_action_controllers(mockReq, ticket, action)) all_fields = [field['name'] for field in ts.get_ticket_fields()] #impact changes find in inBodyFields for field in inBodyFields : ticket._old[field] = ticket[field] ticket.values[field] = inBodyFields[field] mockReq.args[field] = inBodyFields[field] if action : mockReq.args['action_%s_reassign_owner' % action] = ticket['owner'] mockReq.args['comment'] = mailBody mockReq.args['ts'] = datetime.now()#to_datetime(None, utc) mockReq.args['ts'] = str(ticket.time_changed) changes, problems = tm.get_ticket_changes(mockReq, ticket, action) valid = problems and False or tm._validate_ticket(mockReq, ticket) tm._apply_ticket_changes(ticket, changes) # add attachments to the ticket add_attachments(self.env, ticket, attachments) ticket.save_changes(reporter, mailBody) for controller in controllers: controller.apply_action_side_effects(mockReq, ticket, action) # Call ticket change listeners for listener in ts.change_listeners: listener.ticket_changed(ticket, mailBody, reporter, ticket._old) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=ticket.time_changed)
def get_ticket_fields(self): """Returns the list of fields available for tickets.""" from trac.ticket import model db = self.env.get_db_cnx() fields = [] # Basic text fields for name in ('summary', 'reporter'): field = {'name': name, 'type': 'text', 'label': name.title()} fields.append(field) # Owner field, can be text or drop-down depending on configuration field = {'name': 'owner', 'label': 'Owner'} if self.restrict_owner: field['type'] = 'select' users = [] perm = PermissionSystem(self.env) for username, name, email in self.env.get_known_users(db): if perm.get_user_permissions(username).get('TICKET_MODIFY'): users.append(username) field['options'] = users field['optional'] = True else: field['type'] = 'text' fields.append(field) # Description fields.append({ 'name': 'description', 'type': 'textarea', 'label': 'Description' }) # Default select and radio fields selects = [('type', model.Type), ('status', model.Status), ('priority', model.Priority), ('milestone', model.Milestone), ('component', model.Component), ('version', model.Version), ('severity', model.Severity), ('resolution', model.Resolution)] for name, cls in selects: options = [val.name for val in cls.select(self.env, db=db)] if not options: # Fields without possible values are treated as if they didn't # exist continue field = { 'name': name, 'type': 'select', 'label': name.title(), 'value': self.config.get('ticket', 'default_' + name), 'options': options } if name in ('status', 'resolution'): field['type'] = 'radio' field['optional'] = True elif name in ('milestone', 'version'): field['optional'] = True fields.append(field) # Advanced text fields for name in ( 'keywords', 'cc', ): field = {'name': name, 'type': 'text', 'label': name.title()} fields.append(field) for field in self.get_custom_fields(): if field['name'] in [f['name'] for f in fields]: self.log.warning('Duplicate field name "%s" (ignoring)', field['name']) continue if not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', field['name']): self.log.warning( 'Invalid name for custom field: "%s" ' '(ignoring)', field['name']) continue field['custom'] = True fields.append(field) return fields
def get_ticket_fields(self): """Returns the list of fields available for tickets.""" from trac.ticket import model db = self.env.get_db_cnx() fields = [] # Basic text fields for name in ("summary", "reporter"): field = {"name": name, "type": "text", "label": name.title()} fields.append(field) # Owner field, can be text or drop-down depending on configuration field = {"name": "owner", "label": "Owner"} if self.restrict_owner: field["type"] = "select" users = [] perm = PermissionSystem(self.env) for username, name, email in self.env.get_known_users(db): if perm.get_user_permissions(username).get("TICKET_MODIFY"): users.append(username) field["options"] = users field["optional"] = True else: field["type"] = "text" fields.append(field) # Description fields.append({"name": "description", "type": "textarea", "label": "Description"}) # Default select and radio fields selects = [ ("type", model.Type), ("status", model.Status), ("priority", model.Priority), ("milestone", model.Milestone), ("component", model.Component), ("version", model.Version), ("severity", model.Severity), ("resolution", model.Resolution), ] for name, cls in selects: options = [val.name for val in cls.select(self.env, db=db)] if not options: # Fields without possible values are treated as if they didn't # exist continue field = { "name": name, "type": "select", "label": translate(self.env, name, True), "value": self.config.get("ticket", "default_" + name), "options": options, } if name in ("status", "resolution"): field["type"] = "radio" field["optional"] = True elif name in ("milestone", "version"): field["optional"] = True fields.append(field) # Advanced text fields for name in ("keywords", "cc"): field = {"name": name, "type": "text", "label": translate(self.env, name, True)} fields.append(field) for field in self.get_custom_fields(): if field["name"] in [f["name"] for f in fields]: self.log.warning('Duplicate field name "%s" (ignoring)', field["name"]) continue if not re.match("^[a-zA-Z][a-zA-Z0-9_]+$", field["name"]): self.log.warning('Invalid name for custom field: "%s" ' "(ignoring)", field["name"]) continue field["custom"] = True fields.append(field) return fields