def projects_from_directory(directory): """returns list of projects from a directory""" projects = [] for entry in os.listdir(directory): try: open_environment(os.path.join(directory, entry)) except: continue projects.append(entry) return projects
def __get_intertrac_ticket(self, ticket, dep_en): # 指定されたInterTrac形式のチケット名から情報を取得する # 問題があった場合はエラーを返す. if not ticket: return {'error' : None} project_name, id, dep = self.__split_itertrac_ticket_string(ticket) intertrac = self.__get_project_info(project_name) if intertrac is None: return {'error' : ERROE_MSG1 % project_name} if id == "": return {'error' : ERROE_MSG5} # 依存関係を指定しているか確認する 例:(FF) idx = id.rfind('(') if dep: if dep_en == False: #依存関係を使用しない場合でカッコがあった場合は return {'error' : ERROE_MSG7} if dep.startswith('FF')==False \ and dep.startswith('FS')==False \ and dep.startswith('SF')==False \ and dep.startswith('SS')==False: return {'error' : ERROE_MSG2} try: if self.__get_current_project_name() == project_name: tkt = Ticket(self.env, id) else: path = intertrac.get('path', '') project = open_environment(path, use_cache=True) tkt = Ticket(project, id) url = intertrac.get('url', '') + '/ticket/' + id dep_url = intertrac.get('url', '') + '/dependency/ticket/' + id except Exception, e: return {'error' : ERROE_MSG4 % (ticket, project_name, id)}
def load_intertrac_setting(self): # interTracの設定を取得します. self.intertracs0 = {} self.aliases = {} for key, value in self.config.options('intertrac'): # オプションの数のループを回り,左辺値の.を探します. idx = key.rfind('.') if idx > 0: prefix, attribute = key[:idx], key[idx+1:] intertrac = self.intertracs0.setdefault(prefix, {}) intertrac[attribute] = value intertrac['name'] = prefix else: self.aliases[key] = value.lower() intertrac = self.intertracs0.setdefault(value.lower(), {}) intertrac.setdefault('alias', []).append(key) keys = self.intertracs0.keys() for key in keys: intertrac = self.intertracs0[key] path = intertrac.get('path', '') label = intertrac.get('label', '') url = intertrac.get('url', '') if path == '' or url == '': del self.intertracs0[key] else: if label == '': label = os.path.basename(self.env.path) self.log.debug(IT_ERROR_MSG4, key, label) self.config.set('intertrac', key + '.label', label) try: if self.__get_current_project_name() != label: project = open_environment(path, use_cache=True) except Exception, e: self.log.error(IT_ERROR_MSG2, key) del self.intertracs0[key]
def main(args=sys.argv[1:]): parser = OptionParser('%prog [options] project <project2> <project3> ...') parser.add_option('-d', '--dict', dest='dict', default=None, help="python file mapping of old user, new user") options, args = parser.parse_args(args) # if no projects, print usage if not args: parser.print_help() sys.exit(0) # get the environments envs = [] for arg in args: env = open_environment(arg) envs.append(env) # get the users assert options.dict assert os.path.exists(options.dict) users = eval(file(options.dict).read()) assert isinstance(users, dict) if not users: sys.exit(0) # nothing to do # change the permissions for env in envs: renamer = RenameTracUsers(env) renamer.rename_users(users)
def main(): sys.stderr = codecs.getwriter('shift_jis')(sys.stderr) sys.stderr.write("Running "+sys.argv[0]+"..\n") env = open_environment(sys.argv[1]) env.projkey=sys.argv[2] owner = sys.argv[3] ref = DummyRef() print '<?xml version="1.0"?>' print '<JiraJelly xmlns:jira="jelly:com.atlassian.jira.jelly.enterprise.JiraTagLib">' print '<jira:CreateProject key="'+env.projkey+'" name="' + env.config.get('project', 'descr') + '" lead="'+owner+'">' print ''' <jira:CreatePermissionScheme name="'''+env.projkey+'''-scheme"> <jira:AddPermission permissions="Assignable,Browse,Create,Assign,Resolve,Close,ModifyReporter,Attach,Comment" group="jira-users" type="group"/> <jira:SelectProjectScheme/> </jira:CreatePermissionScheme> ''' for c in Component(env).select(env): createComponent(c.name,c.description,c.owner) for v in Version(env).select(env): createMilestone(v.name) tickets=[] for t in Query(env).execute(ref): tickets.append(int(t["id"])) tickets.sort() i = 0 for t in tickets: processTicket(env, t, owner) print '</jira:CreateProject>' print '</JiraJelly>'
def on_published(self, review_request=None, **kwargs): # Information about the review review_id = review_request.display_id ticket_ids = review_request.get_bug_list() # Connect to trac try: tracenv = env.open_environment(self.settings['tracsite']) except core.TracError: logging.error('Unable to open Trac site') return # Add the review to each trac ticket for ticket_id in ticket_ids: try: tracticket = ticket.Ticket(tracenv,tkt_id=ticket_id) addTracLink(tracticket, review_request.display_id, review_request.submitter) except resource.ResourceNotFound: # Ticket doesn't exist pass # Cleanup tracenv.shutdown()
def globally_get_command_help(self, *args): sys_home_project_name = self.config.get('multiproject', 'sys_home_project_name') for env_name, in self.projects_iterator(['env_name'], batch_size=1): if env_name == sys_home_project_name: continue env = None try: env_path = safe_path(self.config.get('multiproject', 'sys_projects_root'), env_name) env = open_environment(env_path, True) except TracError as e: printout(_('ERROR: Opening environment %(env_name)s failed', env_name=env_name)) continue try: command_manager = AdminCommandManager(env) helps = command_manager.get_command_help(list(args)) if not args: TracAdmin.print_doc(helps, short=True) elif len(helps) == 1: TracAdmin.print_doc(helps, long=True) else: TracAdmin.print_doc(helps) except AdminCommandError as e: printout(_('ERROR: Getting command help in environment %(env_name)s failed: ', env_name=env_name) + e) break
def expand_macro(self, formatter, name, text, args): projects_dir = args.get('path', os.environ.get('TRAC_ENV_PARENT_DIR', '/env/trac/projects')) match = args.get('match', '.*') rawhtml = args.get('rawhtml', 'false') if not os.path.isdir(projects_dir): return sys.stderr from StringIO import StringIO out = StringIO() for f in os.listdir(projects_dir): project_dir = projects_dir + '/'+ f if os.path.isdir(project_dir) and f != '.egg-cache' and re.match(match,f): from trac.env import open_environment selfenv = open_environment(project_dir) import copy context = copy.copy(formatter.context) href = '/projects/' + f + '/' context.href = Href(href) context.req.href = context.href wikitext = text wikitext = wikitext.replace('$dir',project_dir) wikitext = wikitext.replace('$basedir',f) wikitext = wikitext.replace('$name',selfenv.project_name) wikitext = wikitext.replace('$href', href) if rawhtml == 'false': Formatter(selfenv, context).format(wikitext, out) else: out.write(wikitext) return out.getvalue()
def __init__(self, hook_name, options, config_path=None, debug=False): ''' ''' if not config_path: config_path = os.path.dirname(__file__) + '/hooks.config' self.config = TracGerritHookConfig(config_path) self.options = options self.options_dict = options.__dict__ self.repo_name = self.options.project_name self.section = self.config.get_section_for_repo(self.repo_name) if self.config.has_option(self.section, 'comment_always'): self.comment_always = self.config.getboolean(self.section, 'comment_always') self.trac_env = self.config.get_env_for_repo(self.repo_name) if not self.trac_env: sys.exit(0) if self.trac_env.startswith("http"): self.trac_over_rpc = True else: self.trac_over_rpc = False self.env = open_environment(self.trac_env) self.hook_name = hook_name self.debug = debug self.commit_msg = "" ## make sure PYTHON_EGG_CACHE is set if not 'PYTHON_EGG_CACHE' in os.environ: os.environ['PYTHON_EGG_CACHE'] = self.config.\ get('hook-settings', 'python_egg_cache')
def get_project_events(self, project, days, minutes): """ List all events in project that happened in a given time span. """ events = [] project_href = Href(conf.url_projects_path + "/" + project.env_name) req = DummyReq('user', 'password', 'method', 'uri', 'args') req.permissions = ( 'TICKET_VIEW', 'CHANGESET_VIEW', 'WIKI_VIEW', 'ATTACHMENT_VIEW', 'DISCUSSION_VIEW', 'MILESTONE_VIEW') req.authname = 'authname' req.abs_href = project_href project_env = open_environment(conf.getEnvironmentSysPath(project.env_name), use_cache=True) event_provider = ProjectTimelineEvents(project_env) last_events = event_provider.get_timeline_events(req, time_in_days=days, time_in_minutes=minutes) for event in last_events: context = Context(resource=Resource(), href=project_href) context.req = req context.perm = req.perm events.append([project, event, context]) events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date'])) return events
def get_sorted_dicts (env, table, other_env=None): if other_env: from trac.env import open_environment env_path = os.path.join(os.path.dirname(env.path), 'ncs') env = open_environment(env_path, use_cache=True) return CustomDBTableSystem(env).sorted_dicts(table)
def _expire_cookie(self, req): """Instruct the user agent to drop the auth_session cookie by setting the "expires" property to a date in the past. Basically, whenever "trac_auth" cookie gets expired, expire "trac_auth_session" too. """ # First of all expire trac_auth_session cookie, if it exists. if 'trac_auth_session' in req.incookie: self._expire_session_cookie(req) # Capture current cookie value. cookie = req.incookie.get('trac_auth') if cookie: trac_auth = cookie.value else: trac_auth = None # Then let auth.LoginModule expire all other cookies. auth.LoginModule._expire_cookie(self, req) # And finally revoke distributed authentication data too. if trac_auth: for path in self.auth_share_participants: env = open_environment(path, use_cache=True) db = env.get_db_cnx() cursor = db.cursor() cursor.execute(""" DELETE FROM auth_cookie WHERE cookie=%s """, (trac_auth,)) db.commit() env.log.debug('Auth data revoked from: ' + \ req.environ.get('SCRIPT_NAME', 'unknown'))
def project_information(self): # interTracの設定を取得します. intertracs0 = {} for key, value in self.config.options("intertrac"): # オプションの数のループを回り,左辺値の.を探します. idx = key.rfind(".") if idx > 0: # .が無い場合はショートカットでので無視します prefix, attribute = key[:idx], key[idx + 1 :] # 左辺値を分割します intertrac = intertracs0.setdefault(prefix, {}) intertrac[attribute] = value # 左辺値のピリオド以降をキーで右辺値を登録 intertrac["name"] = prefix # プロジェクト名を設定します. intertracs = [] # 取得したinterTrac設定の名前が小文字になっているので元に戻します. # ついでに,プロジェクトの一覧表示用のデータを作成しておきます. # 結局はintertrac['label'] 設定することにしたので意味はないのですが,つくっちゃったのでこのままにします. for prefix in intertracs0: intertrac = intertracs0[prefix] # Trac.iniのパスを取得します path = intertrac.get("path", "") # trac.iniをオープンする project = open_environment(path, use_cache=True) # 名前をtrac.iniのプロジェクト名で置き換えます. intertrac["name"] = intertrac["label"] # プロジェクトの一覧表示用のデータを作成します. url = intertrac.get("url", "") title = intertrac.get("title", url) name = project.project_name intertracs.append({"name": name, "title": title, "url": url, "path": path}) return intertracs
def __init__(self, env=None): if not env: self.env = open_environment() else: self.env = env self._init_db()
def __init__(self, options): """ Initialization and check of the options :param options: an object that holds all the info needed for the hook to proceed :type options: class options needs to have all these properties :param bugzilla: The bugzilla server URL (without the rest uri) :type bugzilla: string :param rest_uri: The REST part of the url :type rest_uri: string :param chglog: The changelog message (extracted from debian/changelog) :type chglog: string :param commentonly: Do only comment :type commentonly: bool :param msg: The commit message :type msg: string :param netrc: The netrc file location :type netrc: string :param proxy: Overrides the proxy in case you don't like the ENV one :type proxy: string :param rev: The revision number / commit hash :type rev: string :param tm: Whether to add the target milestone to the bug (as the current week) :type tm: bool :param user: The user that did the commit (coming from the VCS) :type user: string :param vcstype: What VCS are we working with (HG/GIT/SVN) :type vcstype: string :param vcsurl: the base url of the VCS (this is not trivial to guess, look at the code) :type vcsurl: string """ supported_vcstypes = ['hg', 'git', 'trac', 'svn'] if options.vcstype not in supported_vcstypes: print >> sys.stderr, "Unsupported vcs type. Supported types are %s " % supported_vcstypes self.options = options self.config = self.parse_config() if self.options.vcstype == 'svn': self.finalurl = os.path.join(self.options.vcsurl, self.config['svn_commiturl'], self.options.rev) elif self.options.vcstype == 'git': self.finalurl = self.options.vcsurl + self.config['git_commit_url'] % self.options.rev elif self.options.vcstype == 'hg': self.finalurl = os.path.join(self.options.vcsurl, self.config['hg_commit_url'], '%s' % self.options.rev) elif self.options.vcstype == 'trac': from trac.env import open_environment env = open_environment(self.options.vcsurl) self.finalurl = os.path.join(env.config.get('project', 'url'), self.config['trac_commit_url'], self.options.rev) else: print >> sys.stderr, 'Configuration is not complete: please check the options passed' sys.exit(1) if self.options.tm: year, week = datetime.now().isocalendar()[0:2] self.target_milestone = '%d-%02d' % (year, week) else: self.target_milestone = None self.open_statuses = [ 'NEW', 'ASSIGNED', 'REOPENED', 'WAITING', 'NEED_INFO' ]
def _get_env(self): if not self._env: assert self.exists, "Can't use a non-existant project" try: self._env = open_environment(self.env_path, use_cache=True) self._valid = True except Exception, e: self._env = BadEnv(self.env_path, e)
def get_projects(self, db=None): """Return an iterable of (shotname, env) for all known projects.""" db = db or self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT name, env_path FROM tracforge_projects') for name, path in cursor: env = open_environment(path, use_cache=True) yield name, env
def import_project(self, sourcepath, destinationpath, name=None, **kwargs): if name is None: raise KeyError("This importer requires a Trac project to already exist. Use --name to specify it's dir name.") boxqueue = Queue() env_path = os.path.join(destinationpath, name) self.env = open_environment(env_path) import logging self.env.log.setLevel(logging.WARNING) self.log = logging.getLogger(self.env.path + '.' + self.__class__.__name__) self.log.setLevel(logging.DEBUG) if os.path.isdir(sourcepath): self.log.info('Importing directory %s', sourcepath) entries = os.listdir(sourcepath) for entry in entries: fullpath = os.path.join(sourcepath, entry) if not os.path.isfile(fullpath): continue boxqueue.put((fullpath, None)) else: self.log.info('Importing from %s', sourcepath) if sourcepath.endswith('.xml'): mlists = self.get_listdata_from_xml(sourcepath) elif sourcepath.endswith('.json'): mlists = self.get_listdata_from_json(sourcepath) importdir = os.path.dirname(sourcepath) for mlist in mlists: path = os.path.join(importdir, mlist.pop('mailbox')) if not os.path.exists(path): self.log.error("Can't find mailbox %s from %s", path, sourcepath) continue if mlist.get('mailboxmd5'): self.log.debug('Checking MD5 sum of %s...', path) md5digest = md5(open(path, 'r').read()).hexdigest() if md5digest != mlist['mailboxmd5']: self.log.error("%s's md5 (%s) doesn't match %s from %s. Skipping it", path, md5digest, mlist['mailboxmd5'], sourcepath) continue self.log.debug('MD5 of %s ok', path) else: self.log.warning("No md5 found for %s in %s", path, sourcepath) boxqueue.put((path, mlist)) else: boxqueue.put((sourcepath, None)) def worker(queue): while True: mbox, metadata = queue.get() try: self.read_file(mbox, metadata) except: self.log.exception("Error in %s", mbox) queue.task_done() for _ in range(min(boxqueue.qsize(), 5)): t = Thread(target=worker, args=(boxqueue,)) t.daemon = True t.start() boxqueue.join()
def trac_projects(self): """returns existing Trac projects""" proj = {} for i in os.listdir(self.directory): try: env = open_environment(os.path.join(self.directory, i)) except: continue proj[i] = env return proj
def _get_home_perm(self, req): """ Returns permission cache from home environment """ home_env = open_environment(os.path.join( self.env.config.get('multiproject', 'sys_projects_root'), self.env.config.get('multiproject', 'sys_home_project_name')), use_cache=True ) return PermissionCache(home_env, req.authname)
def _open_environment(env_path, run_once=False): if run_once: return open_environment(env_path) global env_cache, env_cache_lock env = None env_cache_lock.acquire() try: if not env_path in env_cache: env_cache[env_path] = open_environment(env_path) env = env_cache[env_path] finally: env_cache_lock.release() # Re-parse the configuration file if it changed since the last the time it # was parsed env.config.parse_if_needed() return env
def getVersionControlType(self, env_name): """ .. WARNING:: Do not call this unless absolutely needed This will initialize new trac environment which is heavy operation to do """ # TODO: whole method should be removed env = open_environment(conf.getEnvironmentSysPath(env_name), use_cache=True) return env.config.get('trac', 'repository_type')
def __init__(self, project=options.project, author=AUTHOR, maxage=options.maxage, url=options.url): self.env = open_environment(project) db = self.env.get_db_cnx() cursor = db.cursor() if url is None: url = self.env.config.get('trac', 'base_url') self.env.href = Href(url) self.env.abs_href = Href(url) self.msg = MESSAGE % (maxage) self.now = int(time.time()) maxtime = int(time.time()) - (60 * 60 * 24 * maxage) cursor.execute("SELECT id FROM ticket t, ticket_custom c " \ "WHERE t.status <> %s " \ "AND t.changetime < %s " \ "AND t.id = c.ticket " \ "AND c.name = %s " \ "AND c.value = %s ", ('closed', maxtime, 'pending', '1')) rows = cursor.fetchall() for row in rows: id = row[0] try: ticket = Ticket(self.env, id, db); ticket['status'] = 'closed' ticket['pending'] = '0'; # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, self.msg, self.now, db, cnum + 1) db.commit() print 'Closing Ticket %s (%s)\n' % (id, ticket['summary']) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: import traceback traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (id, e)
def get_project_list(self, req): # get search path and base_url search_path, this_project = os.path.split(self.env.path) base_url, _ = posixpath.split(req.abs_href()) #Closes #4158, thanks to jholg if 'tracforge' in self.config: if self.config.get('tracforge', 'master_path') == self.env.path: base_url = '/'.join((base_url, this_project, 'projects')) projects = [] for project in os.listdir(search_path): # skip our own project if project == this_project: continue #Include only if project is in include_projects, or include_projects is empty if self.include_projects and (project not in self.include_projects): continue #Exclude if project is in exclude_projcets if project in self.exclude_projects: continue # make up URL for project project_url = '/'.join( (base_url, project) ) project_path = os.path.join(search_path, project) if not os.path.isdir( project_path ): continue try: env = open_environment(project_path, use_cache = True) except: try: env = open_environment(project_path) except: continue projects.append((project, project_path, project_url, env)) return projects
def move(self, ticket_id, author, env, delete=False): """ move a ticket to another environment env: environment to move to """ tables = {"attachment": "id", "ticket_change": "ticket"} # open the environment if it is a string if isinstance(env, basestring): base_path, _project = os.path.split(self.env.path) env = open_environment(os.path.join(base_path, env), use_cache=True) # get the old ticket old_ticket = Ticket(self.env, ticket_id) # make a new ticket from the old ticket values new_ticket = Ticket(env) new_ticket.values = old_ticket.values.copy() new_ticket.insert(when=old_ticket.time_created) # copy the changelog and attachment DBs for table, _id in tables.items(): for row in get_all_dict(self.env, "SELECT * FROM %s WHERE %s=%%s" % (table, _id), str(ticket_id)): row[_id] = new_ticket.id insert_row_from_dict(env, table, row) # copy the attachments src_attachment_dir = os.path.join(self.env.path, "attachments", "ticket", str(ticket_id)) if os.path.exists(src_attachment_dir): dest_attachment_dir = os.path.join(env.path, "attachments", "ticket") if not os.path.exists(dest_attachment_dir): os.makedirs(dest_attachment_dir) dest_attachment_dir = os.path.join(dest_attachment_dir, str(new_ticket.id)) shutil.copytree(src_attachment_dir, dest_attachment_dir) # note the previous location on the new ticket new_ticket.save_changes(author, "moved from %s" % self.env.abs_href("ticket", ticket_id)) # location of new ticket new_location = env.abs_href.ticket(new_ticket.id) if delete: old_ticket.delete() else: # close old ticket and point to new one old_ticket["status"] = u"closed" old_ticket["resolution"] = u"moved" old_ticket.save_changes(author, u"moved to %s" % new_location) if env.config["trac"].get("base_url"): return new_location else: return None
def send_project_index(environ, start_response, parent_dir=None, env_paths=None): req = Request(environ, start_response) loadpaths = [pkg_resources.resource_filename('trac', 'templates')] if req.environ.get('trac.env_index_template'): env_index_template = req.environ['trac.env_index_template'] tmpl_path, template = os.path.split(env_index_template) loadpaths.insert(0, tmpl_path) else: template = 'index.html' data = {'trac': {'version': TRAC_VERSION, 'time': user_time(req, format_datetime)}, 'req': req} if req.environ.get('trac.template_vars'): for pair in req.environ['trac.template_vars'].split(','): key, val = pair.split('=') data[key] = val try: href = Href(req.base_path) projects = [] for env_name, env_path in get_environments(environ).items(): try: env = open_environment(env_path, use_cache=not environ['wsgi.run_once']) proj = { 'env': env, 'name': env.project_name, 'description': env.project_description, 'href': href(env_name) } except Exception as e: proj = {'name': env_name, 'description': to_unicode(e)} projects.append(proj) projects.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) data['projects'] = projects loader = TemplateLoader(loadpaths, variable_lookup='lenient', default_encoding='utf-8') tmpl = loader.load(template) stream = tmpl.generate(**data) if template.endswith('.xml'): output = stream.render('xml') req.send(output, 'text/xml') else: output = stream.render('xhtml', doctype=DocType.XHTML_STRICT, encoding='utf-8') req.send(output, 'text/html') except RequestDone: pass
def __init__(self, trac=options.trac, project=options.project): self.env = open_environment(trac) self.trac = trac self.project = project self.now = datetime.now(utc) #Convert the timestamp from a float to an int to drop the .0 self.stamp = int(math.floor(time.time())) self.github = 'https://github.com/api/v2/json' try: self.db = self.env.get_db_cnx() except TracError, e: print_error(e.message)
def search_userProfile(self, userProfileTemplate=None): if self.masterUserProfilesStore=='': return [] _masterEnv = open_environment(self.masterUserProfilesStore) def _changeStore(userProfile): userProfile.project_name = userProfile.store.env.project_name userProfile.store=self return userProfile return map( _changeStore, UserProfilesSystem(_masterEnv).search_userProfile(userProfileTemplate) )
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url): self.env = open_environment(project) repos = self.env.get_repository() repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev self.msg = "(In [%s]) %s" % (rev, chgset.message) self.now = datetime.now(utc) cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = CommitHook._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def __init__(self, project): """ Initiates the class. :param project: Instance of the :class:`Project` to backup/restore """ self.project = project self.env = open_environment(self.project.trac_fs_path, use_cache=True) backup_dir = self.env.config.get('trac', 'backup_dir', '/tmp') self.backup_path_tmpl = backup_dir + '/project-%s.snapshot-%d.sql' self.mysql_path = self.env.config.get('trac', 'mysql_path', 'mysql') self.dm = DatabaseManager(self.env)
parser.add_option('-c', '--component', dest='component', type='string') parser.add_option('-p', '--project', dest='project', type='string', help='Path to the project') options, args = parser.parse_args() if None in (options.summary, options.description, options.reporter, options.owner, options.component, options.project): sys.stderr.write("Please make sure that summary, description, reporter," " owner, componnet and project are defined") sys.stderr.flush() sys.exit(1) env = open_environment(options.project) t = model.Ticket(env) t['status'] = options.status t['summary'] = options.summary t['description'] = options.description.replace("\\n", "\n") t['reporter'] = options.reporter t['owner'] = options.owner t['type'] = options.type t['component'] = options.component t.insert() try: tn = TicketNotifyEmail(env) tn.notify(t, newticket=True) except Exception, e:
def expand_macro(self, formatter, name, content, args=None): """ Expand the macro. We will accept a parameter to point us to another project if necessary. """ if name != 'Announcements': return None data = {} req = formatter.req env_name = None count = 0 title = None env = None # Parse settings for the macro env_name, count, title = self._parse_args(args, content) if not count: self.log.debug("Defaulting count to 5") count = 5 data['news_title'] = title current_env_name = self.env.project_identifier if not env_name: env_name = current_env_name data['env_name'] = env_name # Check if the project exists. mpp_env = None try: mpp_env = TracEnvironment.read(env_name) except TracError: data['news_error'] = "Couldn't find project '{0}'".format(env_name) self.log.error(data['news_error']) # Then check the permissions if mpp_env: # Check if project's announcements have been configured as hidden if env_name != current_env_name: env = open_environment(os.path.join( self.config.get('multiproject', 'sys_projects_root'), env_name), use_cache=True) else: env = self.env if not env.config.getbool('discussion', 'show_news_forum', True): self.log.debug( "Project announcements hidden, not showing with macro") return None # Check permission in specified environment permcache = PermissionCache(env, username=req.authname) if 'DISCUSSION_VIEW' in permcache: try: news = ProjectNews(env_name) data['newsitems'] = news.get_project_news( limit=count, f_name="Announcements") data['news_forum_id'] = news.get_news_forum_id() except Exception, e: self.log.exception( "Failed to read project {0} news.".format(env_name)) raise TracError( "Couldn't read news for project '{0}': {1}".format( env_name, e)) else: data[ 'news_error'] = "No permission to read news from project '{0}'".format( env_name)
def _resolve_env(self, name): from trac.env import open_environment import os.path dir_ = self._intertracs[name] path = os.path.join(os.path.dirname(self.env.path), dir_) return open_environment(path, True)
def open_environment(self, environ, start_response): env_path = environ.get('trac.env_path') if not env_path: env_parent_dir = environ.get('trac.env_parent_dir') env_paths = environ.get('trac.env_paths') if env_parent_dir or env_paths: # The first component of the path is the base name of the # environment path_info = environ.get('PATH_INFO', '').lstrip('/').split('/') env_name = path_info.pop(0) if not env_name: # No specific environment requested, so render an environment # index page send_project_index(environ, start_response, env_parent_dir, env_paths) raise RequestDone errmsg = None # To make the matching patterns of request handlers work, we append # the environment name to the `SCRIPT_NAME` variable, and keep only # the remaining path in the `PATH_INFO` variable. script_name = environ.get('SCRIPT_NAME', '') try: script_name = unicode(script_name, 'utf-8') # (as Href expects unicode parameters) environ['SCRIPT_NAME'] = Href(script_name)(env_name) environ['PATH_INFO'] = '/' + '/'.join(path_info) if env_parent_dir: env_path = os.path.join(env_parent_dir, env_name) else: env_path = get_environments(environ).get(env_name) if not env_path or not os.path.isdir(env_path): errmsg = 'Environment not found' except UnicodeDecodeError: errmsg = 'Invalid URL encoding (was %r)' % script_name if errmsg: write = start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(errmsg)))]) write(errmsg) raise RequestDone if not env_path: raise EnvironmentError('The environment options "TRAC_ENV" or ' '"TRAC_ENV_PARENT_DIR" or the mod_python ' 'options "TracEnv" or "TracEnvParentDir" are ' 'missing. Trac requires one of these options ' 'to locate the Trac environment(s).') run_once = environ['wsgi.run_once'] env = None self.global_env = global_env = None try: self.global_env = global_env = open_environment(env_path, use_cache=not run_once) factory = environment_factory(global_env) factory_env = factory().open_environment(environ, env_path, global_env, use_cache=not run_once) if factory \ else None env = factory_env if factory_env else global_env except Exception: raise return env
def env(env_path): return open_environment(env_path, use_cache=True)
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url, reponame=options.repos): self.env = open_environment(project) self.reponame = reponame if reponame: repos = self.env.get_repository(reponame) revstring = rev + '/' + reponame else: repos = self.env.get_repository() revstring = rev repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev self.msg = "(In [%s]) %s" % (revstring, chgset.message) self.now = datetime.now(utc) cmd_groups = command_re.findall(self.msg) log("cmd_groups:%s", cmd_groups) tickets = {} # \todo Explain what xxx1 and xxx2 do; I can't see more params # in command_re. for cmd, tkts, xxx1, xxx2 in cmd_groups: log("cmd:%s, tkts%s ", cmd, tkts) funcname = _supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id, spent in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append([func, spent]) for tkt_id, vals in tickets.iteritems(): log("tkt_id:%s, vals%s ", tkt_id, vals) spent_total = 0.0 try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for (cmd, spent) in vals: cmd(ticket) if spent: spent_total += float(spent) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 if spent_total: self._setTimeTrackerFields(ticket, spent_total) ticket.save_changes(self.author, self.msg, self.now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) log('Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def get_trac_environment(self): """Returns a Trac environment object""" return open_environment(self.tracdir, use_cache=True)
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url): self.env = open_environment(project) repos = self.env.get_repository() repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev lines = [] #print chgset.message for line in chgset.message.splitlines(): if line.startswith("*"): lines.append(" " + line) else: lines.append(line) msg = "\n".join(lines) #print msg self.msg = "(In [%s]) %s" % (rev, msg) self.now = datetime.now(utc) cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = CommitHook._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
print 'Unexpected error while processing commit %s :' % commit print 'ticket ID %s: %s' % (tkt_id, e) sys.exit(3) def handle_ref(old, new, ref, env): # If something else than the master branch (or whatever is contained by the # constant BRANCHES) was pushed, skip this ref. if not ref.startswith('refs/heads/') or ref[11:] not in BRANCHES: return # Get the list of hashs for commits in the changeset. args = (old == '0' * 40) and [new] or [new, '^' + old] pending_commits = call_git('rev-list', args).splitlines() for commit in pending_commits: try: handle_commit(commit, env) except Exception, e: print 'Unexpected error while processing commit %s: %s' % ( commit[:7], e) sys.exit(4) if __name__ == '__main__': from trac.env import open_environment env = open_environment(TRAC_ENV) for line in sys.stdin: handle_ref(env=env, *line.split())
def send_project_index(environ, start_response, parent_dir=None, env_paths=None): req = Request(environ, start_response) loadpaths = [pkg_resources.resource_filename('trac', 'templates')] if req.environ.get('trac.env_index_template'): env_index_template = req.environ['trac.env_index_template'] tmpl_path, template = os.path.split(env_index_template) loadpaths.insert(0, tmpl_path) else: template = 'index.html' data = { 'trac': { 'version': TRAC_VERSION, 'time': user_time(req, format_datetime) }, 'req': req } if req.environ.get('trac.template_vars'): for pair in req.environ['trac.template_vars'].split(','): key, val = pair.split('=') data[key] = val try: href = Href(req.base_path) projects = [] for env_name, env_path in get_environments(environ).items(): try: env = open_environment(env_path, use_cache=not environ['wsgi.run_once']) proj = { 'env': env, 'name': env.project_name, 'description': env.project_description, 'href': href(env_name) } except Exception as e: proj = {'name': env_name, 'description': to_unicode(e)} projects.append(proj) projects.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) data['projects'] = projects loader = TemplateLoader(loadpaths, variable_lookup='lenient', default_encoding='utf-8') tmpl = loader.load(template) stream = tmpl.generate(**data) if template.endswith('.xml'): output = stream.render('xml') req.send(output, 'text/xml') else: output = stream.render('xhtml', doctype=DocType.XHTML_STRICT, encoding='utf-8') req.send(output, 'text/html') except RequestDone: pass
def dispatch_request(environ, start_response): """Main entry point for the Trac web interface. @param environ: the WSGI environment dict @param start_response: the WSGI callback for starting the response """ # SCRIPT_URL is an Apache var containing the URL before URL rewriting # has been applied, so we can use it to reconstruct logical SCRIPT_NAME script_url = environ.get('SCRIPT_URL') if script_url is not None: path_info = environ.get('PATH_INFO') if not path_info: environ['SCRIPT_NAME'] = script_url elif script_url.endswith(path_info): environ['SCRIPT_NAME'] = script_url[:-len(path_info)] # If the expected configuration keys aren't found in the WSGI environment, # try looking them up in the process environment variables environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR')) environ.setdefault('trac.env_index_template', os.getenv('TRAC_ENV_INDEX_TEMPLATE')) environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS')) environ.setdefault('trac.locale', '') environ.setdefault('trac.base_url', os.getenv('TRAC_BASE_URL')) locale.setlocale(locale.LC_ALL, environ['trac.locale']) # Determine the environment env_path = environ.get('trac.env_path') if not env_path: env_parent_dir = environ.get('trac.env_parent_dir') env_paths = environ.get('trac.env_paths') if env_parent_dir or env_paths: # The first component of the path is the base name of the # environment path_info = environ.get('PATH_INFO', '').lstrip('/').split('/') env_name = path_info.pop(0) if not env_name: # No specific environment requested, so render an environment # index page send_project_index(environ, start_response, env_parent_dir, env_paths) return [] errmsg = None # To make the matching patterns of request handlers work, we append # the environment name to the `SCRIPT_NAME` variable, and keep only # the remaining path in the `PATH_INFO` variable. script_name = environ.get('SCRIPT_NAME', '') try: script_name = unicode(script_name, 'utf-8') # (as Href expects unicode parameters) environ['SCRIPT_NAME'] = Href(script_name)(env_name) environ['PATH_INFO'] = '/' + '/'.join(path_info) if env_parent_dir: env_path = os.path.join(env_parent_dir, env_name) else: env_path = get_environments(environ).get(env_name) if not env_path or not os.path.isdir(env_path): errmsg = 'Environment not found' except UnicodeDecodeError: errmsg = 'Invalid URL encoding (was %r)' % script_name if errmsg: start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(errmsg)))]) return [errmsg] if not env_path: raise EnvironmentError('The environment options "TRAC_ENV" or ' '"TRAC_ENV_PARENT_DIR" or the mod_python ' 'options "TracEnv" or "TracEnvParentDir" are ' 'missing. Trac requires one of these options ' 'to locate the Trac environment(s).') run_once = environ['wsgi.run_once'] env = env_error = None try: env = open_environment(env_path, use_cache=not run_once) if env.base_url_for_redirect: environ['trac.base_url'] = env.base_url # Web front-end type and version information if not hasattr(env, 'webfrontend'): mod_wsgi_version = environ.get('mod_wsgi.version') if mod_wsgi_version: mod_wsgi_version = ( "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" % ('.'.join([str(x) for x in mod_wsgi_version ]), environ.get('mod_wsgi.process_group'), environ.get('mod_wsgi.application_group') or '%{GLOBAL}')) environ.update({ 'trac.web.frontend': 'mod_wsgi', 'trac.web.version': mod_wsgi_version }) env.webfrontend = environ.get('trac.web.frontend') if env.webfrontend: env.systeminfo.append( (env.webfrontend, environ['trac.web.version'])) except Exception, e: env_error = e
def process_request(self, req): req.perm.assert_permission('ROADMAP_VIEW') #If the page was posted, a filter was applied. Build and redirect using query strnig if req.args.has_key('update'): req.redirect(self._get_href(req)) req.hdf['title'] = 'Project List' #Process filtering arguments self.status = req.args.has_key('status') and req.args['status'] or None #Process sorting arguments self.desc = req.args.has_key( 'desc') and req.args['desc'] == '1' or None self.order = req.args.has_key( 'order') and req.args['order'] or DEFAULT_SORT_FIELD #Get search path and base_url search_path, this_project = os.path.split(self.env.path) base_url, _ = posixpath.split(req.abs_href()) href = Href(base_url) #Start with an empty project list projects = [] for project in os.listdir(search_path): #Open the project environment project_path = os.path.join(search_path, project) env = open_environment(project_path) #Check if DB needs upgrading check_upgrade(env) #Trim project description if too long if len(env.project_description) <= 60: description = env.project_description else: description = "%s..." % env.project_description[:60] #Get last_login timestamp, and convert to human readable last_login = int(get_property(env, 'last_login', 0)) if last_login == 0: last_login = '' else: last_login = datetime.fromtimestamp(last_login).strftime( TIME_FORMAT) #Filter by status project_status = get_property(env, 'status', 'unknown') if self.status and project_status != self.status: continue projects.append({ 'name': env.project_name, 'description': description, 'company': get_property(env, 'company'), 'created': get_property(env, 'date_created'), 'started': get_property(env, 'date_started'), 'scheduled': get_property(env, 'date_scheduled'), 'finished': get_property(env, 'date_finished'), 'percent_finished': get_property(env, 'percent', '0'), 'percent_remaining': 100 - int(get_property(env, 'percent', '0')), 'status': STATUS[project_status], 'client': get_property(env, 'client'), 'manager': get_property(env, 'manager'), 'last_login': last_login, 'href': href(project) }) #Status selection sorted_keys = STATUS.keys() sorted_keys.sort() statuses = [dict(name='', label='*', selected=self.status == None)] statuses += [ dict(name=x, label=STATUS[x], selected=self.status == x) for x in sorted_keys ] #################################### ## Functions for project sorting, depending on the field def cmp_datetime(x, y): try: return datetime.strptime(x, TIME_FORMAT) < datetime.strptime( y, TIME_FORMAT) and -1 or 1 except: return x.lower() < y.lower() and -1 or 1 def cmp_date(x, y): try: return datetime.strptime(x, DATE_FORMAT) < datetime.strptime( y, DATE_FORMAT) and -1 or 1 except: return x.lower() < y.lower() and -1 or 1 def cmp_int(x, y): try: return int(x) < int(y) and -1 or 1 except: return x < y and -1 or 1 def cmp_str_nocase(x, y): try: return x.lower() < y.lower() and -1 or 1 except: return x < y and -1 or 1 ################################# #For some fields, use a special comparison function if self.order in ('created', 'last_login'): cmp = lambda x, y: cmp_datetime(x[self.order], y[self.order]) if self.order in ('started', 'scheduled', 'finished'): cmp = lambda x, y: cmp_date(x[self.order], y[self.order]) if self.order in ('percent_finished'): cmp = lambda x, y: cmp_int(x[self.order], y[self.order]) else: cmp = lambda x, y: cmp_str_nocase(x[self.order], y[self.order]) projects.sort(cmp=cmp, reverse=self.desc and True or False) #Set template HDF req.hdf['projects'] = projects req.hdf['statuses'] = statuses req.hdf['status'] = self.status req.hdf['order'] = self.order req.hdf['desc'] = self.desc add_stylesheet(req, 'tracprojectmanager/css/projectlist.css') return 'projectlist.cs', None
def accessor(self, *args, **kwords): val = super(EnvironmentOption,self).accessor(*args, **kwords) assert val, 'You must configure a valid Trac environment path for [%s] %s'%(self.section, self.name) return open_environment(val, use_cache=True)
def dispatch_request(environ, start_response): """Main entry point for the Trac web interface. :param environ: the WSGI environment dict :param start_response: the WSGI callback for starting the response """ global _warn_setuptools if _warn_setuptools is False: _warn_setuptools = True warn_setuptools_issue(out=environ.get('wsgi.errors')) # SCRIPT_URL is an Apache var containing the URL before URL rewriting # has been applied, so we can use it to reconstruct logical SCRIPT_NAME script_url = environ.get('SCRIPT_URL') if script_url is not None: path_info = environ.get('PATH_INFO') if not path_info: environ['SCRIPT_NAME'] = script_url else: # mod_wsgi squashes slashes in PATH_INFO (!) script_url = _slashes_re.sub('/', script_url) path_info = _slashes_re.sub('/', path_info) if script_url.endswith(path_info): environ['SCRIPT_NAME'] = script_url[:-len(path_info)] # If the expected configuration keys aren't found in the WSGI environment, # try looking them up in the process environment variables environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR')) environ.setdefault('trac.env_index_template', os.getenv('TRAC_ENV_INDEX_TEMPLATE')) environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS')) environ.setdefault('trac.locale', '') environ.setdefault('trac.base_url', os.getenv('TRAC_BASE_URL')) locale.setlocale(locale.LC_ALL, environ['trac.locale']) # Determine the environment env_path = environ.get('trac.env_path') if not env_path: env_parent_dir = environ.get('trac.env_parent_dir') env_paths = environ.get('trac.env_paths') if env_parent_dir or env_paths: # The first component of the path is the base name of the # environment path_info = environ.get('PATH_INFO', '').lstrip('/').split('/') env_name = path_info.pop(0) if not env_name: # No specific environment requested, so render an environment # index page send_project_index(environ, start_response, env_parent_dir, env_paths) return [] errmsg = None # To make the matching patterns of request handlers work, we append # the environment name to the `SCRIPT_NAME` variable, and keep only # the remaining path in the `PATH_INFO` variable. script_name = environ.get('SCRIPT_NAME', '') try: script_name = unicode(script_name, 'utf-8') # (as Href expects unicode parameters) environ['SCRIPT_NAME'] = Href(script_name)(env_name) environ['PATH_INFO'] = '/' + '/'.join(path_info) if env_parent_dir: env_path = os.path.join(env_parent_dir, env_name) else: env_path = get_environments(environ).get(env_name) if not env_path or not os.path.isdir(env_path): errmsg = 'Environment not found' except UnicodeDecodeError: errmsg = 'Invalid URL encoding (was %r)' % script_name if errmsg: start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(errmsg)))]) return [errmsg] if not env_path: raise EnvironmentError('The environment options "TRAC_ENV" or ' '"TRAC_ENV_PARENT_DIR" or the mod_python ' 'options "TracEnv" or "TracEnvParentDir" are ' 'missing. Trac requires one of these options ' 'to locate the Trac environment(s).') run_once = environ['wsgi.run_once'] env = env_error = None try: env = open_environment(env_path, use_cache=not run_once) if env.base_url_for_redirect: environ['trac.base_url'] = env.base_url # Web front-end type and version information if not hasattr(env, 'webfrontend'): mod_wsgi_version = environ.get('mod_wsgi.version') if mod_wsgi_version: mod_wsgi_version = ( "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" % ('.'.join([str(x) for x in mod_wsgi_version ]), environ.get('mod_wsgi.process_group'), environ.get('mod_wsgi.application_group') or '%{GLOBAL}')) environ.update({ 'trac.web.frontend': 'mod_wsgi', 'trac.web.version': mod_wsgi_version }) env.webfrontend = environ.get('trac.web.frontend') if env.webfrontend: env.webfrontend_version = environ['trac.web.version'] except Exception as e: env_error = e req = RequestWithSession(environ, start_response) translation.make_activable(lambda: req.locale, env.path if env else None) try: return _dispatch_request(req, env, env_error) finally: translation.deactivate() if env and not run_once: env.shutdown(threading._get_ident()) # Now it's a good time to do some clean-ups # # Note: enable the '##' lines as soon as there's a suspicion # of memory leak due to uncollectable objects (typically # objects with a __del__ method caught in a cycle) # ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE) unreachable = gc.collect()
def move(self, ticket_id, author, env, delete=False): """ move a ticket to another environment env: environment to move to """ self.log.info( "Starting move of ticket %d to environment %r. delete: %r", ticket_id, env, delete) tables = {'attachment': 'id', 'ticket_change': 'ticket'} # open the environment if it is a string if isinstance(env, basestring): base_path, _project = os.path.split(self.env.path) env = open_environment(os.path.join(base_path, env), use_cache=True) PermissionCache(env, author).require('TICKET_CREATE') # get the old ticket old_ticket = Ticket(self.env, ticket_id) # make a new ticket from the old ticket values new_ticket = Ticket(env) new_ticket.values = old_ticket.values.copy() new_ticket.insert(when=old_ticket.values['time']) self.log.debug("Ticket inserted into target environment as id %s", new_ticket.id) # copy the changelog and attachment DBs for table, _id in tables.items(): for row in get_all_dict( self.env, "SELECT * FROM %s WHERE %s = %%s" % (table, _id), str(ticket_id)): row[_id] = new_ticket.id insert_row_from_dict(env, table, row) self.log.debug("Finished copying data from %r table", table) # copy the attachments src_attachment_dir = os.path.join(self.env.path, 'attachments', 'ticket', str(ticket_id)) if os.path.exists(src_attachment_dir): self.log.debug("Copying attachements from %r", src_attachment_dir) dest_attachment_dir = os.path.join(env.path, 'attachments', 'ticket') if not os.path.exists(dest_attachment_dir): os.makedirs(dest_attachment_dir) dest_attachment_dir = os.path.join(dest_attachment_dir, str(new_ticket.id)) shutil.copytree(src_attachment_dir, dest_attachment_dir) # note the previous location on the new ticket if delete: new_ticket.save_changes( author, 'moved from %s (ticket deleted)' % self.env.abs_href()) else: new_ticket.save_changes( author, 'moved from %s' % self.env.abs_href('ticket', ticket_id)) self.log.info("Finished making new ticket @ %r", env.abs_href('ticket', ticket_id)) if delete: self.log.debug("Deleting old ticket") old_ticket.delete() if env.base_url: return env.abs_href('ticket', new_ticket.id) else: self.log.debug("Marking old ticket as duplicate.") # location of new ticket if env.base_url: target_name = env.abs_href('ticket', new_ticket.id) else: target_name = "{0}:#{1}".format(env.project_name, new_ticket.id) # close old ticket and point to new one old_ticket['status'] = u'closed' old_ticket['resolution'] = u'duplicate' old_ticket.save_changes(author, u'moved to %s' % target_name)