def target_node(self): if self._target_node is not None: return self._target_node env_name = self.environment_identifier() if not env_name: conf.log.warning("Failed reading identifier") raise NodeError('Target node was invalid: env_name not found') project = Project.get(env_name=env_name) # env_name might be different from project.env_name! base_url = project.get_dav_url(relative=True) path_info = normalized_base_url(self.req.uri) project_id = project.id download_config = FilesDownloadConfig(project.env_name, base_url=base_url) if not path_info.startswith(download_config.base_url): raise NodeError('Target node was invalid: uri %s does not start with %s', path_info, base_url) self._download_config = download_config try: node_factory = MappedFileNode(project_id, download_config.base_path, download_config.base_url, download_config.downloads_dir) self._target_node = MappedFileNode.from_request_path_info(path_info, node_factory) except Exception as e: conf.log.warning("Not node %s", str(e)) raise NodeError('Target node was invalid: %s', str(e)) return self._target_node
def post_process_request(self, req, template, data, content_type): #add_script(req, 'multiproject/js/browser.js') repository_name = None data_repositories = None latest_revisions = [] if len(req.path_info.split("/")) > 2: #Get repository name from path_info repository_name = req.path_info.split("/")[2] if template == 'browser.html': username = urllib.quote(req.authname) project = Project.get(self.env) schemes = None if repository_name: scm_type = repository_name + ".type" scm_dir = repository_name + ".dir" scm = self.env.config.get('repositories', scm_type) repository_name = self.env.config.get('repositories', scm_dir).split("/")[-1] schemes = self.protocols(project.id, scm) rm = RepositoryManager(self.env) list_repos = rm.get_real_repositories() for r in list_repos: if r.get_base().split("/")[-1] == repository_name: l_rev = r.get_youngest_rev() if l_rev: export_url = '../export/archive/'+repository_name+"?rev="+str(l_rev)+"&format=zip" latest_revisions.append(export_url) else: scm = self.env.config.get('trac', 'repository_type') schemes = self.protocols(project.id, scm) data_repo_names = self.get_repositories() if len(data_repo_names) > 0: data_repositories = [] for repo in data_repo_names: type_scheme = [] for data_scheme in self.protocols(project.id, repo[1]): type_scheme.append(self.create_co_command(repo[1], username, data_scheme, repo[0])) data_repositories.append(type_scheme) names = {'git':'GIT', 'svn':'Subversion', 'hg':'Mercurial'} cmd_kinds = {'git':'Clone', 'hg':'Clone', 'svn':'Check out'} type = names[scm] data['kinds'] = cmd_kinds data['schemes'] = schemes data['name'] = names[scm] data['type'] = scm data['data_repositories'] = data_repositories data['export_urls'] = latest_revisions co_commands = {} for scheme in schemes: co_commands[scheme] = self.create_co_command(scm, username, scheme, repository_name) data['co_commands'] = co_commands return template, data, content_type
def render_admin_panel(self, req, category, page, path_info): """ Process a request for an admin panel. :Returns: A tuple of the form `(template, data)`, """ # Ensure the user has project admin permissions req.perm.assert_permission('TRAC_ADMIN') backups = [] backup_href = Href('%s/admin/general/backup' % req.base_path) # Load the user based on authname user = get_userstore().getUser(req.authname) # Get the current environment name env_name = conf.resolveProjectName(self.env) # Initiate ProjectBackup, containing the backup/restore implementation prj = Project.get(env_name=env_name) pb = ProjectBackup(prj) backups = pb.get_backups() # Do the backup if req.path_info.endswith('backup/backup') and req.method == 'POST': try: pb.backup(user_id=user.id, description=req.args.get('description')) add_notice(req, _('Backup created')) except TracError, e: add_warning(req, _('Backup failed: %s' % e)) # Return back to default backup page return req.redirect(backup_href())
def filter_stream(self, req, method, filename, stream, data): """ Adds project total count information in project summary block:: Downloads: 288 """ # TODO: Make interface for the project summary box and implement it here # Filter only the summary table wiki macro if filename != 'multiproject_summary.html': return stream # Load project and followers info project = Project.get(self.env) count = ProjectDownloadEntry.total_download_count(project.id) if count == 0: return stream # Add following information into project summary block trans = Transformer('//div[@class="summary"]/table').append( tag.tr( tag.th('Downloads:'), tag.td(count) ) ) return stream | trans
def process_request(self, req): """ Process request for listing, creating and removing projects """ add_script(req, 'multiproject/js/summary.js') data = {} if req.authname == 'anonymous': req.perm.require('PROJECT_VIEW') if not ('PROJECT_VIEW' in req.perm or 'PROJECT_PRIVATE_VIEW' in req.perm): return 'no_access.html', data, None # Load project from db project = Project.get(self.env) if req.args.get('action') == 'timeline_events': return self.get_summary_timeline_events(req, project.created) # TODO: Move project timeline implementation into macro # Get recent timeline events # TODO: Move project downloads implementation into macro # Get list of featured downloads downloads = [] if self.env.is_component_enabled('tracdownloads.api.DownloadsApi'): api = None # In case of import error we don't have it, so don't return any downloads try: from tracdownloads.api import DownloadsApi api = DownloadsApi(self.env) except ImportError: self.log.warning( "Unable to import DownloadsApi, but it's enabled.") if api: downloads = api.get_summary_items() # Load wiki:SummaryPage and show it as a main page summary_content = None summary_wiki = WikiPage(self.env, 'SummaryPage') if summary_wiki: context = Context.from_request(req, summary_wiki.resource) wiki_renderer = WikiTextRenderer(self.env) summary_content = wiki_renderer.render(context, 'text/x-trac-wiki', summary_wiki.text) data = { '_project_': project, # project object of a project we are in 'downloads': downloads, # list of featured downloads 'wiki_summary': summary_content, # Rendered content of the SummaryPage 'is_summary': True, 'env': self.env, } return 'summary.html', data, None
def notify_now(self, notification_frequency): """ Send notification email to project watchers. :param str notification_frequency: The notification frequency, used to fetch users which have something on that watchlist. """ store = CQDEWatchlistStore() watches = store.get_by_notification(notification_frequency) notifylist = {} for w in watches: if notifylist.get(w.project_id) is None: notifylist[w.project_id] = [] notifylist[w.project_id].append(w.user_id) userstore = get_userstore() counter = 0 for project_id in notifylist.keys(): project = Project.get(id=project_id) # Get all events for this project events = self._get_project_events(project, notification_frequency) # Send one email per user, because everyone may have a different # set of permissions (i.e. which events he/she has permission to see) for user_id in notifylist[project_id]: user = userstore.getUserWhereId(user_id) if user: # filter eventlist by user's permissions filtered_events = WatchlistEvents(self.env).filter_events( events, user, project) if filtered_events: addresses = [user.mail] message = self._format_message(user, project, filtered_events) title = "Project updated: %s" % project.env_name mail = EmailNotifier(self.env, subject=title, data={'body': message}) mail.notify_emails(addresses) self.env.log.debug('Sent email notification to: %s' % user) counter += 1 else: if notification_frequency != 'immediate': self.env.log.debug( 'No events to sent to %s about %s' % (user, project)) else: self.env.log.warning( 'User %d in notification list was not found in userstore' % user_id) # Log the results self.env.log.info('Sent %s watchlist notifications (%s)' % (counter, notification_frequency))
def render_admin_panel(self, req, cat, page, path_info): """ Returns the admin view and handles the form actions """ req.perm.require('TRAC_ADMIN') project = Project.get(self.env) if req.method == 'POST': myprojects_url = conf.url_home_path + "/myprojects" conf.log.exception("Redirect URL: %s" % myprojects_url) thisurl = Href(req.base_path)(req.path_info) context = Context.from_request(req) # Handle project remove if 'remove' in req.args: # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return req.redirect(thisurl) # Archive the project before removal archive = ProjectArchive() archived = archive.archive(project) if not archived: add_warning(req, 'Could not archive project "%s". Will not remove the project' % project.project_name) return req.redirect(thisurl) # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_archived(project) # Do the actual project removal projects = Projects() if projects.remove_project(project): # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_deleted(project) # NOTE: We no longer have project tables/session etc available req.redirect(myprojects_url) else: add_warning(req, 'Could not remove project "%s". Try again later' % project.project_name) return req.redirect(thisurl) add_script(req, 'multiproject/js/jquery-ui.js') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_system.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') # NOTE: Trac automatically puts 'project' dict in chrome thus it cannot be used data = {'multiproject': { 'project':project, 'home_url':conf.url_home_path }} return 'admin_system.html', data
def remove_project(self, req): """ Handler for removing project :param Request req: Trac request Handler expects to have following arguments in it: - project: Name of the project env - goto: Optional URL where to go after project removal. Defaults to /home/myprojects """ backurl = req.args.get('goto', ProjectListModule.home('myprojects')) short_name = req.args.get('project') if not short_name: return self.remove_failure(req) if req.args.get('cancel'): add_notice(req, 'Project deletion cancelled') return self.remove_failure(req) author = req.authname project = Project.get(env_name=short_name) if project is None: add_warning(req, 'Specified project does not exist!') return self.remove_failure(req) # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return self.remove_failure(req) if req.method != 'POST': return self.remove_failure(req, project=project) try: project.validate() except ProjectValidationException: req.redirect(backurl) # Check the permissions and method prjs = Projects() if ('TRAC_ADMIN' in req.perm or prjs.is_project_owner(project.env_name, author)) and req.method == 'POST': archive = ProjectArchive() # Archive the project before removal if not archive.archive(project): add_warning(req, 'Could not archive project "%s". Will not remove the project' % project.project_name) elif prjs.remove_project(project): # Notify listeners for listener in self.project_change_listeners: listener.project_deleted(project) # Notify end user add_notice(req, 'Project "%s" removed' % project.project_name) else: add_warning(req, 'Could not remove project "%s". Try again later' % project.project_name) else: add_warning(req, 'You are not allowed to remove project "%s" (check permissions and method)' % project.project_name) req.redirect(backurl)
def process_request(self, req): """ Process request for listing, creating and removing projects """ add_script(req, 'multiproject/js/summary.js') data = {} if req.authname == 'anonymous': req.perm.require('PROJECT_VIEW') if not ('PROJECT_VIEW' in req.perm or 'PROJECT_PRIVATE_VIEW' in req.perm): return 'no_access.html', data, None # Load project from db project = Project.get(self.env) if req.args.get('action') == 'timeline_events': return self.get_summary_timeline_events(req, project.created) # TODO: Move project timeline implementation into macro # Get recent timeline events # TODO: Move project downloads implementation into macro # Get list of featured downloads downloads = [] if self.env.is_component_enabled('tracdownloads.api.DownloadsApi'): api = None # In case of import error we don't have it, so don't return any downloads try: from tracdownloads.api import DownloadsApi api = DownloadsApi(self.env) except ImportError: self.log.warning("Unable to import DownloadsApi, but it's enabled.") if api: downloads = api.get_summary_items() # Load wiki:SummaryPage and show it as a main page summary_content = None summary_wiki = WikiPage(self.env, 'SummaryPage') if summary_wiki: context = Context.from_request(req, summary_wiki.resource) wiki_renderer = WikiTextRenderer(self.env) summary_content = wiki_renderer.render(context, 'text/x-trac-wiki', summary_wiki.text) data = { '_project_': project, # project object of a project we are in 'downloads': downloads, # list of featured downloads 'wiki_summary': summary_content, # Rendered content of the SummaryPage 'is_summary': True, 'env': self.env, } return 'summary.html', data, None
def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') protos = ProtocolManager(Project.get(self.env).id) scm_type = self.env.config.get('trac', 'repository_type') if req.method == 'POST': self._process_changes(req, protos, scm_type) return self._show_panel(req, protos, scm_type)
def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') protos = ProtocolManager(Project.get(self.env).id) scm_type = self.env.config.get('trac', 'repository_type') if req.method == 'POST': self._process_changes(req, protos, scm_type) return self._show_panel(req, protos, scm_type)
def _notify_admins(self, permission, email_path, debug='false'): is_debug = debug.lower() in ('true', 'yes') if permission != 'TRAC_ADMIN': raise AdminCommandError('Only TRAC_ADMIN permission is supported') # A standard thing to do in IAdminCommandProviders (otherwise, # accessing project_identifier would give errors) if not hasattr(self.env, 'project_identifier'): MultiProjectEnvironmentInit(self.env).environment_needs_upgrade(None) env_name = self.env.project_identifier if env_name == self.env.config.get('multiproject', 'sys_home_project_name'): raise AdminCommandError('Command does not support home project') if not os.path.exists(email_path): raise AdminCommandError(_("Email template was not found!")) project = Project.get(self.env) email_template = '' try: with open(email_path) as fd: email_template = fd.read() except OSError as e: raise AdminCommandError(_("Error with opening file %(path)s: %(error_msg)s", path=email_path, error_msg=e)) except Exception as e: raise AdminCommandError(_("Unknown error when parsing file %(path)s: %(error_msg)s", path=email_path, error_msg=e)) email_template = [i.strip() for i in email_template.split('\n', 1) if i] if not len(email_template) > 1 or not all(email_template): raise AdminCommandError(_("Email template %(path)s was invalid.", path=email_path)) subject, body = email_template text_template = NewTextTemplate(body) admins = project.get_admin_email_addresses() data = {'env_name': env_name} if is_debug: printout('## DEBUG MODE - NOT SENDING EMAILS ##') printout("project: {0}".format(env_name)) printout("to: {0}".format(','.join(admins))) printout("subject: {0}".format(subject)) printout("----------------------------") printout(text_template.generate(**data)) printout("----------------------------") if not is_debug: notifier = EmailNotifier(self.env, subject=subject, data=data) notifier.template = text_template notifier.notify(admins) printout('Emails sent')
def _notify_admins(self, permission, email_path, debug="false"): is_debug = debug.lower() in ("true", "yes") if permission != "TRAC_ADMIN": raise AdminCommandError("Only TRAC_ADMIN permission is supported") # A standard thing to do in IAdminCommandProviders (otherwise, # accessing project_identifier would give errors) if not hasattr(self.env, "project_identifier"): MultiProjectEnvironmentInit(self.env).environment_needs_upgrade(None) env_name = self.env.project_identifier if env_name == self.env.config.get("multiproject", "sys_home_project_name"): raise AdminCommandError("Command does not support home project") if not os.path.exists(email_path): raise AdminCommandError(_("Email template was not found!")) project = Project.get(self.env) email_template = "" try: with open(email_path) as fd: email_template = fd.read() except OSError as e: raise AdminCommandError(_("Error with opening file %(path)s: %(error_msg)s", path=email_path, error_msg=e)) except Exception as e: raise AdminCommandError( _("Unknown error when parsing file %(path)s: %(error_msg)s", path=email_path, error_msg=e) ) email_template = [i.strip() for i in email_template.split("\n", 1) if i] if not len(email_template) > 1 or not all(email_template): raise AdminCommandError(_("Email template %(path)s was invalid.", path=email_path)) subject, body = email_template text_template = NewTextTemplate(body) admins = project.get_admin_email_addresses() data = {"env_name": env_name} if is_debug: printout("## DEBUG MODE - NOT SENDING EMAILS ##") printout("project: {0}".format(env_name)) printout("to: {0}".format(",".join(admins))) printout("subject: {0}".format(subject)) printout("----------------------------") printout(text_template.generate(**data)) printout("----------------------------") if not is_debug: notifier = EmailNotifier(self.env, subject=subject, data=data) notifier.template = text_template notifier.notify(admins) printout("Emails sent")
def make_membership_request(self, req): project = Project.get(self.env) # Make a request members = MembershipApi(self.env, project) members.request_membership(req.authname, req.args.get('message')) type = 'membership' if req.args.has_key('type'): type = req.args.get('type') return 'membership_requested.html', {'_project_':project, 'type':type}, None
def post_process_request(self, req, template, data, content_type): """ Puts global ``mproject`` variable into data """ # Ensure the mproject is set data = data or {'mproject': None} if self.config.get('multiproject', 'sys_home_project_name', 'home') != self.env.path.split('/')[-1] and data: project = Project.get(self.env) data.update({'mproject': project}) return template, data, content_type
def render_admin_panel(self, req, cat, page, path_info): """ Overrides BasicsAdminPanel rendering function. Handle project info update (update global db) and then delegate handling back to BasicsAdminPanel """ req.perm.require('TRAC_ADMIN') userstore = get_userstore() user = userstore.getUser(req.authname) project = Project.get(self.env) # Update database if form posted if req.method == 'POST': # Remove icon if requested if 'reset' in req.args: # NOTE: Icon is removed from filesystem already at this point self._unset_icon(req, project) project.icon_name = None # Update icon if set if not isinstance(req.args.get('icon', ''), basestring): icon_name = self._set_icon(req, project) if icon_name: # If image is changed if icon_name != project.icon_name: self._unset_icon(req, project) project.icon_name = icon_name else: add_warning(req, 'Failed to set the project icon') # Save changes in database if 'apply' in req.args: self._apply_changes(req, project) # Reload page return req.redirect(req.href(req.path_info)) data = { 'user': user, 'icon_size': self.icon_size, 'mproject': project, 'allow_public_projects': conf.allow_public_projects } # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_basics.js') Chrome(self.env).add_textarea_grips(req) return 'admin_basics_replacement.html', data
def render_admin_panel(self, req, cat, page, path_info): """ Overrides BasicsAdminPanel rendering function. Handle project info update (update global db) and then delegate handling back to BasicsAdminPanel """ req.perm.require('TRAC_ADMIN') userstore = get_userstore() user = userstore.getUser(req.authname) project = Project.get(self.env) # Update database if form posted if req.method == 'POST': # Remove icon if requested if 'reset' in req.args: # NOTE: Icon is removed from filesystem already at this point self._unset_icon(req, project) project.icon_name = None # Update icon if set if not isinstance(req.args.get('icon', ''), basestring): icon_name = self._set_icon(req, project) if icon_name: # If image is changed if icon_name != project.icon_name: self._unset_icon(req, project) project.icon_name = icon_name else: add_warning(req, 'Failed to set the project icon') # Save changes in database if 'apply' in req.args: self._apply_changes(req, project) # Reload page return req.redirect(req.href(req.path_info)) data = { 'user': user, 'icon_size': self.icon_size, 'mproject': project, 'allow_public_projects': conf.allow_public_projects } # Add javascript libraries for datepicker and autocomplete add_script(req, 'multiproject/js/jquery-ui.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_basics.js') Chrome(self.env).add_textarea_grips(req) return 'admin_basics_replacement.html', data
def post_process_request(self, req, template, data, content_type): """ Puts global ``mproject`` variable into data """ # Ensure the mproject is set data = data or {'mproject': None} if self.config.get('multiproject', 'sys_home_project_name', 'home') != self.env.path.split('/')[-1] and data: project = Project.get(self.env) data.update({'mproject': project}) return template, data, content_type
def openProject(self, req, projectname, revision, extensionlist): """ Returns project's connection string and repository file list matched to search criteria. """ # Return values if str(projectname).strip() == '': e = exceptions.Exception raise e("Incorrect project name") if revision.strip() == '': revision = None if extensionlist.strip() == '': extensionlist = None # Find node for the requested path/rev repomgr = RepositoryManager(self.env) repository = repomgr.get_repository(None) projectname = conf.cleanupProjectName(projectname) parts = [] project = Project.get(env_name=projectname) if project: parts.append(self.get_scm_repository_url(project.env_name)) else: return [] try: if revision: revision = repository.normalize_rev(revision) rev_or_latest = revision or repository.youngest_rev getfiles = [] self._getfiles(req, getfiles, repository, '', rev_or_latest) if extensionlist: extensions = extensionlist.split(',') searchresult = [] for file in getfiles: extension = os.path.splitext(str(file))[1].strip('.') if extension in extensions: searchresult.append(file) addfiles = ",".join(searchresult) else: addfiles = ",".join(getfiles) if addfiles: # Append version control files parts.append('versioncontrolfiles|' + addfiles) except Exception: self.log.exception("ProjectsRPC.openProject failed") return parts
def get_latest_timeline_events(self, req, count, project_created=None): """ Returns latest (within 10 days) timeline events. If count is given, returns only given number of elements. :param Request req: Trac request :param int count: Number of elements to returns. Defaults to all :returns: List of events """ events = [] available_filters = [] for event_provider in self.event_providers: available_filters += event_provider.get_timeline_filters(req) or [] # TODO: make this incredibly obscure piece of code readable # check the request or session for enabled filters, or use default filters = [] filters_list = [] for afilter in available_filters: filters_list.append(afilter[0]) filter_set = set(filters_list) for item in filter_set: filters.append(item) # start time of timeline is last update of if not known, last two monts if not project_created: project = Project.get(self.env) project_start_date = project.created else: project_start_date = project_created project_start_date = project_start_date.replace(tzinfo=datefmt.localtz) # do the actual event querying for provider in self.event_providers: todate = datetime.now(datefmt.localtz) fromdate = todate - timedelta(days=10) eventcount = 0 while eventcount < count and todate > project_start_date: for event in provider.get_timeline_events( req, fromdate, todate, filters): eventcount += 1 events.append(self._event_data(provider, event)) todate = fromdate fromdate = todate - timedelta(days=10) events.sort(lambda x, y: cmp(y['date'], x['date'])) # Note, when count = None, all the events are returned return events[:count] if events else []
def openProject(self, req, projectname, revision, extensionlist): """ Returns project's connection string and repository file list matched to search criteria. """ # Return values if str(projectname).strip() == '': e = exceptions.Exception raise e("Incorrect project name") if revision.strip() == '': revision = None if extensionlist.strip() == '': extensionlist = None # Find node for the requested path/rev repomgr = RepositoryManager(self.env) repository = repomgr.get_repository(None) projectname = conf.cleanupProjectName(projectname) parts = [] project = Project.get(env_name=projectname) if project: parts.append(self.get_scm_repository_url(project.env_name)) else: return [] try: if revision: revision = repository.normalize_rev(revision) rev_or_latest = revision or repository.youngest_rev getfiles = [] self._getfiles(req, getfiles, repository, '', rev_or_latest) if extensionlist: extensions = extensionlist.split(',') searchresult = [] for file in getfiles: extension = os.path.splitext(str(file))[1].strip('.') if extension in extensions: searchresult.append(file) addfiles = ",".join(searchresult) else: addfiles = ",".join(getfiles) if addfiles: # Append version control files parts.append('versioncontrolfiles|' + addfiles) except Exception: self.log.exception("ProjectsRPC.openProject failed") return parts
def get_latest_timeline_events(self, req, count, project_created=None): """ Returns latest (within 10 days) timeline events. If count is given, returns only given number of elements. :param Request req: Trac request :param int count: Number of elements to returns. Defaults to all :returns: List of events """ events = [] available_filters = [] for event_provider in self.event_providers: available_filters += event_provider.get_timeline_filters(req) or [] # TODO: make this incredibly obscure piece of code readable # check the request or session for enabled filters, or use default filters = [] filters_list = [] for afilter in available_filters: filters_list.append(afilter[0]) filter_set = set(filters_list) for item in filter_set: filters.append(item) # start time of timeline is last update of if not known, last two monts if not project_created: project = Project.get(self.env) project_start_date = project.created else: project_start_date = project_created project_start_date = project_start_date.replace (tzinfo = datefmt.localtz) # do the actual event querying for provider in self.event_providers: todate = datetime.now(datefmt.localtz) fromdate = todate - timedelta(days = 10) eventcount = 0 while eventcount < count and todate > project_start_date: for event in provider.get_timeline_events(req, fromdate, todate, filters): eventcount += 1 events.append(self._event_data(provider, event)) todate = fromdate fromdate = todate - timedelta(days = 10) events.sort(lambda x, y: cmp(y['date'], x['date'])) # Note, when count = None, all the events are returned return events[:count] if events else []
def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TRAC_ADMIN') project = Project.get(self.env) child_projects = project.get_child_projects() data = { '_project_': project, 'parent_project': project.parent_project, 'child_projects': child_projects, 'home': conf.url_home_path } add_script(req, 'multiproject/js/admin_relations.js') return 'multiproject_admin_relations.html', data
def notify_now(self, notification_frequency): """ Send notification email to project watchers. :param str notification_frequency: The notification frequency, used to fetch users which have something on that watchlist. """ store = CQDEWatchlistStore() watches = store.get_by_notification(notification_frequency) notifylist = {} for w in watches: if notifylist.get(w.project_id) is None: notifylist[w.project_id] = [] notifylist[w.project_id].append(w.user_id) userstore = get_userstore() counter = 0 for project_id in notifylist.keys(): project = Project.get(id=project_id) # Get all events for this project events = self._get_project_events(project, notification_frequency) # Send one email per user, because everyone may have a different # set of permissions (i.e. which events he/she has permission to see) for user_id in notifylist[project_id]: user = userstore.getUserWhereId(user_id) if user: # filter eventlist by user's permissions filtered_events = WatchlistEvents(self.env).filter_events(events, user, project) if filtered_events: addresses = [user.mail] message = self._format_message(user, project, filtered_events) title = "Project updated: %s" % project.env_name mail = EmailNotifier(self.env, subject=title, data={'body':message}) mail.notify_emails(addresses) self.env.log.debug('Sent email notification to: %s' % user) counter += 1 else: if notification_frequency != 'immediate': self.env.log.debug('No events to sent to %s about %s' % (user, project)) else: self.env.log.warning('User %d in notification list was not found in userstore' % user_id) # Log the results self.env.log.info('Sent %s watchlist notifications (%s)' % (counter, notification_frequency))
def render_admin_panel(self, req, cat, page, path_info): """ Renders announcements admin panel """ req.perm.require('TRAC_ADMIN') showforum = self.config.getbool('discussion', 'show_news_forum', True) project = Project.get(self.env) groupstore = CQDEUserGroupStore(project.trac_environment_key) (public_exists, allowed) = self.get_announce_allowed(groupstore) if req.method == 'POST': if 'hideforum' in req.args: self.config.set('discussion', 'show_news_forum', 'false') self.config.save() showforum = False # Notify listeners for listener in self.announcement_admin_listeners: listener.announcements_hidden() elif 'showforum' in req.args: self.config.set('discussion', 'show_news_forum', 'true') self.config.save() showforum = True # Notify listeners for listener in self.announcement_admin_listeners: listener.announcements_visible() elif 'allow' in req.args: #Allow should add the DISCUSSION_ANNOUNCE_APPEND to the "public contributors" group. groupstore.grant_permission_to_group( "Public contributors", "DISCUSSION_ANNOUNCEAPPEND") (public_exists, allowed) = self.get_announce_allowed(groupstore) elif 'disallow' in req.args: #Disallow remove it. groupstore.revoke_permission_from_group( "Public contributors", "DISCUSSION_ANNOUNCEAPPEND") (public_exists, allowed) = self.get_announce_allowed(groupstore) elif 'rename' in req.args: news = ProjectNews(project.env_name) news.rename_news_forum(req.args.get('forumname')) data = {} data['showforum'] = showforum data['allowed'] = allowed data['public_exists'] = public_exists return 'admin_news_forum.html', data
def refresh_project(self, project_identifier, from_date, to_date=datetime.now(datefmt.localtz), afilters=None, update=False): """ Will refresh a project events in cache in given date range. .. NOTE:: Dates needs to be given in datefmt.localtz form """ # Initialize objects project = Project.get(env_name=project_identifier) if not project: conf.log.warning('Project {0} is already removed from system or it cannot be found'.format(project_identifier)) return e = open_environment(conf.getEnvironmentSysPath(project.env_name), use_cache=True) pte = ProjectTimelineEvents(e) providers = pte.event_providers project_href = Href(conf.url_projects_path + '/' + project.env_name) context = Context(resource=Resource(), href=project_href) req = self._create_dummy_req(project_identifier) context.req = req context.perm = req.perm # Read events from timeline events = [] for provider in providers: try: # Use filters in parameter or check filters from providers if afilters: filters = afilters else: available_filters = provider.get_timeline_filters(req) or [] filters = [f[0] for f in available_filters] for event in provider.get_timeline_events(req, from_date, to_date, filters): event_data = self._event_data(provider, event) if event_data['author'] != 'trac': # Skip system events events.append(CachedEvent.from_event_data(project, event_data, context, filters[0])) except: conf.log.error("Could not read timeline events for %s from %s" % (project_identifier, str(provider))) # Write events into sql table self._write_events_into_cache(events, update)
def expand_macro(self, formatter, name, content): """ Execute / Render the macro. Return the content to caller. """ if name not in self.macros: return None identifier = self.env.project_identifier project = Project.get(self.env) if project is None: return None if name == 'ProjectName': return project.project_name elif name == 'ProjectIdentifier': return identifier elif name == 'ProjectOwner': return project.author.username elif name == 'ProjectCreateDate': return project.created elif name == 'ProjectUrl': url = project.get_url() txt = content or identifier return html.a(txt, href=url) elif name == 'ProjectVersioncontrolUrl': url = project.get_repository_url() txt = content or url return html.a(txt, href=url) elif name == 'ProjectWebDavUrl': url = project.get_dav_url() txt = content or url return html.a(txt, href=url) elif name == 'WelcomeText': return html.h1("Welcome to " + project.project_name, id="WelcomeToProject")
def _get_watchlist_events(self, user): watchlist = CQDEWatchlistStore().get_projects_by_user(user.id) events = [] event_helper = WatchlistEvents(self.env) # TODO: inefficient querying for watch in watchlist: project = Project.get(id=watch.project_id) project_events = event_helper.get_project_events(project, days = 7, minutes = 0) # filter eventlist by user's permissions project_events = event_helper.filter_events(project_events, user, project) if project_events: # show only one event per project events.append(project_events[0]) events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date'])) return events
def expand_macro(self, formatter, name, content): """ Execute / Render the macro. Return the content to caller. """ if name not in self.macros: return None identifier = self.env.project_identifier project = Project.get(self.env) if project is None: return None if name == 'ProjectName': return project.project_name elif name == 'ProjectIdentifier': return identifier elif name == 'ProjectOwner': return project.author.username elif name == 'ProjectCreateDate': return project.created elif name == 'ProjectUrl': url = project.get_url() txt = content or identifier return html.a(txt, href = url) elif name == 'ProjectVersioncontrolUrl': url = project.get_repository_url() txt = content or url return html.a(txt, href = url) elif name == 'ProjectWebDavUrl': url = project.get_dav_url() txt = content or url return html.a(txt, href = url) elif name == 'WelcomeText': return html.h1("Welcome to " + project.project_name, id = "WelcomeToProject")
def notify_project(self, data, project_id=None, store=True): # Add timestamp if not already set if 'created' not in data: data.update({'created': datetime.utcnow().isoformat()}) if not project_id: project_id = Project.get(self.env).id # Load notification system and used it for sending - unless disabled ns = self.env[NotificationSystem] if not ns: return try: count = ns.send_notification([ns.generate_channel_name(project_id=project_id)], data, store) self.log.info('Sent project wide notification to project (%s) about: %s and %s received it' % (project_id, data, count)) except TracError, err: self.log.warning('Failed to send notification to project (%s): %s' % (project_id, err))
def _get_watchlist_events(self, req): events = [] user = get_userstore().getUser(req.authname) if not user: return None watchlist = CQDEWatchlistStore().get_projects_by_user(user.id) event_helper = WatchlistEvents(self.env) # TODO: inefficient querying for watch in watchlist: project = Project.get(id=watch.project_id) project_events = event_helper.get_project_events(project, days=7, minutes=0) # filter eventlist by user's permissions project_events = event_helper.filter_events(project_events, user, project) if project_events: events.extend(project_events) events.sort(lambda x, y: cmp(y[1]["date"], x[1]["date"])) return events
def render_admin_panel(self, req, cat, page, path_info): """ Renders announcements admin panel """ req.perm.require('TRAC_ADMIN') showforum = self.config.getbool('discussion', 'show_news_forum', True) project = Project.get(self.env) groupstore = CQDEUserGroupStore(project.trac_environment_key) (public_exists, allowed) = self.get_announce_allowed(groupstore) if req.method == 'POST': if 'hideforum' in req.args: self.config.set('discussion', 'show_news_forum', 'false') self.config.save() showforum = False # Notify listeners for listener in self.announcement_admin_listeners: listener.announcements_hidden() elif 'showforum' in req.args: self.config.set('discussion', 'show_news_forum', 'true') self.config.save() showforum = True # Notify listeners for listener in self.announcement_admin_listeners: listener.announcements_visible() elif 'allow' in req.args: #Allow should add the DISCUSSION_ANNOUNCE_APPEND to the "public contributors" group. groupstore.grant_permission_to_group("Public contributors", "DISCUSSION_ANNOUNCEAPPEND") (public_exists, allowed) = self.get_announce_allowed(groupstore) elif 'disallow' in req.args: #Disallow remove it. groupstore.revoke_permission_from_group("Public contributors", "DISCUSSION_ANNOUNCEAPPEND") (public_exists, allowed) = self.get_announce_allowed(groupstore) elif 'rename' in req.args: news = ProjectNews(project.env_name) news.rename_news_forum(req.args.get('forumname')) data = {} data['showforum'] = showforum data['allowed'] = allowed data['public_exists'] = public_exists return 'admin_news_forum.html', data
def render_preference_panel(self, req, panel): if req.authname == 'anonymous': raise TracError("Can't change preferences!", "No access!") if req.method == 'POST': self.save_watchlist(req) watchlist = [] projects = {} user = get_userstore().getUser(req.authname) w = CQDEWatchlistStore() if user: watchlist = w.get_projects_by_user(user.id) # TODO: inefficient querying for watch in watchlist: projects[watch.project_id] = Project.get(id=watch.project_id) return 'multiproject_user_prefs_watchlist.html', { 'watchlist': watchlist, 'projects' : projects, 'notification_values': w.notifications }
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. Supported arguments: - project: Name of the project to show status / provide follow buttons. Defaults to current project """ req = formatter.req # Load project from db project = Project.get(self.env) # Get project metadata (categories) (combined_categories, separated_categories_per_context, context_by_id, context_order, languages) = self._get_project_categories(project) # Get project visibility: public or private visibility = _('Public') if project.public else _('Private') # Return rendered HTML with JS attached to it data = { '_project_': project, 'combined_categories': combined_categories, 'separated_categories_per_context': separated_categories_per_context, 'context_order': context_order, 'languages': languages, 'context_by_id': context_by_id, 'visibility_label': visibility, # Private / Public 'to_web_time': to_web_time # TODO: Is this really required? } chrome = Chrome(self.env) stream = chrome.render_template(req, 'multiproject_summary.html', data, fragment=True) if req.form_token: stream |= chrome._add_form_token(req.form_token) return stream
def has_permission(self): identifier = self.environment_identifier() if not identifier: conf.log.warning("Failed reading identifier") return False # We need environment for permission check, get it .. albeit ugly # TODO: this probably has performance hit as environment is not loaded with caching! if identifier == conf.sys_home_project_name: env = HomeProject().get_env() else: proj = Project.get(env_name=identifier) env = proj.get_env() env.log.debug('basic_access checking has_permission %s for %s' % (self.required_action, self.username)) perms = env[PermissionSystem].get_user_permissions(self.username) return perms.get(self.required_action, False)
def has_permission(self): identifier = self.environment_identifier() if not identifier: conf.log.warning("Failed reading identifier") return False # We need environment for permission check, get it .. albeit ugly # TODO: this probably has performance hit as environment is not loaded with caching! if identifier == conf.sys_home_project_name: env = HomeProject().get_env() else: proj = Project.get(env_name=identifier) env = proj.get_env() env.log.debug('basic_access checking has_permission %s for %s' % (self.required_action, self.username)) perms = env[PermissionSystem].get_user_permissions(self.username) return perms.get(self.required_action, False)
def render_admin_panel(self, req, cat, page, path_info): """ Renders storage admin panel page """ req.perm.require("TRAC_ADMIN") project = Project.get(self.env) vcs_type = conf.getVersionControlType(self.env.project_identifier) # TODO: deprecate / heavy call! vcs_url = project.get_repository_url() data = { "vcs_name": conf.getVersionControlName(vcs_type), "vcs_type": vcs_type, "vcs_url": vcs_url, "dav_url": project.get_dav_url(), "vcs_cmd": "clone", } if vcs_type == "svn": data["vcs_cmd"] = "checkout" return "storages.html", data
def save_watchlist(self, req): user = get_userstore().getUser(req.authname) if not user: return w = CQDEWatchlistStore() if req.args.get('notification'): notifylist = self.__to_list(req.args.get('notification')) for item in notifylist: project_id, notification = item.split('_') w.watch_project(user.id, project_id, notification) # Notify listeners. project = Project.get(id=project_id) for listener in self.project_change_listeners: listener.project_watchers(project) if req.args.get('remove'): removelist = self.__to_list(req.args.get('remove')) for project_id in removelist: w.unwatch_project(user.id, project_id)
def _get_watchlist_events(self, req): events = [] user = get_userstore().getUser(req.authname) if not user: return None watchlist = CQDEWatchlistStore().get_projects_by_user(user.id) event_helper = WatchlistEvents(self.env) # TODO: inefficient querying for watch in watchlist: project = Project.get(id=watch.project_id) project_events = event_helper.get_project_events(project, days=7, minutes=0) # filter eventlist by user's permissions project_events = event_helper.filter_events( project_events, user, project) if project_events: events.extend(project_events) events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date'])) return events
def files_node_factory_and_config(self, req=None, context_name='files'): """ :param req: Request object :param str context_name: either 'files' or 'webdav' :return: """ ctx = None ctx_key = 'files.node_factory.' + context_name if req: ctx = get_context(req) try: return ctx[ctx_key], self.files_download_config(context_name, req=req) except KeyError: pass download_config = self.files_download_config(context_name=context_name, req=req) project = Project.get(self.env) project_id = project.id node_factory = MappedFileNode(project_id, download_config.base_path, download_config.base_url, download_config.downloads_dir) if ctx: ctx[ctx_key] = node_factory return node_factory, download_config
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. Supported arguments: - project: Name of the project to show status / provide follow buttons. Defaults to current project """ req = formatter.req # Load project from db project = Project.get(self.env) # Get project metadata (categories) (combined_categories, separated_categories_per_context, context_by_id, context_order, languages) = self._get_project_categories(project) # Get project visibility: public or private ug = CQDEUserGroupStore(project.trac_environment_key) visibility = _('Public') if ug.is_public_project() else _('Private') # Return rendered HTML with JS attached to it data = { '_project_': project, 'combined_categories': combined_categories, 'separated_categories_per_context': separated_categories_per_context, 'context_order': context_order, 'languages': languages, 'context_by_id': context_by_id, 'visibility_label': visibility, # Private / Public 'to_web_time': to_web_time # TODO: Is this really required? } chrome = Chrome(self.env) stream = chrome.render_template(req, 'multiproject_summary.html', data, fragment=True) if req.form_token: stream |= chrome._add_form_token(req.form_token) return stream
def filter_stream(self, req, method, filename, stream, data): """ Adds project total count information in project summary block:: Downloads: 288 """ # TODO: Make interface for the project summary box and implement it here # Filter only the summary table wiki macro if filename != "multiproject_summary.html": return stream # Load project and followers info project = Project.get(self.env) count = ProjectDownloadEntry.total_download_count(project.id) if count == 0: return stream # Add following information into project summary block trans = Transformer('//div[@class="summary"]/table').append(tag.tr(tag.th("Downloads:"), tag.td(count))) return stream | trans
def post_process_request(self, req, template, data, content_type): if template == 'browser.html': username = urllib.quote(req.authname) scm = self.env.config.get('trac', 'repository_type') project = Project.get(self.env) names = {'git':'GIT', 'svn':'Subversion', 'hg':'Mercurial'} cmd_kinds = {'git':'Clone', 'hg':'Clone', 'svn':'Check out'} type = names[scm] schemes = self.protocols(project, scm) data['kinds'] = cmd_kinds data['schemes'] = schemes data['name'] = names[scm] data['type'] = scm co_commands = {} for scheme in schemes: co_commands[scheme] = self.create_co_command(scm, username, scheme) data['co_commands'] = co_commands return template, data, content_type
def process_request(self, req): req.perm.require('MEMBERSHIP_REQUEST_CREATE') all_members = {} project = Project.get(self.env) ug = CQDEUserGroupStore(project.trac_environment_key) for member, group in ug.get_all_user_groups(): all_members[member] = 1 if req.authname in all_members.keys(): type = "privilege" else: type = "membership" # Check the message length only if user has clicked 'request' msg_ok = 'request' not in req.args or ('message' in req.args and len(req.args.get('message')) >= 40) # If request, then make request, ow. show form if req.args.has_key('request') and msg_ok: return self.make_membership_request(req) else: return 'membership_request_form.html', {'type':type, 'msg_ok':msg_ok, 'message':req.args.get('message')}, None
def post_process_request(self, req, template, data, content_type): #add_script(req, 'multiproject/js/browser.js') repository_name = None data_repositories = None latest_revisions = [] if len(req.path_info.split("/")) > 2: #Get repository name from path_info repository_name = req.path_info.split("/")[2] if template == 'browser.html': username = urllib.quote(req.authname) project = Project.get(self.env) schemes = None if repository_name: scm_type = repository_name + ".type" scm_dir = repository_name + ".dir" scm = self.env.config.get('repositories', scm_type) repository_name = self.env.config.get('repositories', scm_dir).split("/")[-1] schemes = self.protocols(project.id, scm) rm = RepositoryManager(self.env) list_repos = rm.get_real_repositories() for r in list_repos: if r.get_base().split("/")[-1] == repository_name: l_rev = r.get_youngest_rev() if l_rev: export_url = '../export/archive/' + repository_name + "?rev=" + str( l_rev) + "&format=zip" latest_revisions.append(export_url) else: scm = self.env.config.get('trac', 'repository_type') schemes = self.protocols(project.id, scm) data_repo_names = self.get_repositories() if len(data_repo_names) > 0: data_repositories = [] for repo in data_repo_names: type_scheme = [] for data_scheme in self.protocols(project.id, repo[1]): type_scheme.append( self.create_co_command(repo[1], username, data_scheme, repo[0])) data_repositories.append(type_scheme) names = {'git': 'GIT', 'svn': 'Subversion', 'hg': 'Mercurial'} cmd_kinds = {'git': 'Clone', 'hg': 'Clone', 'svn': 'Check out'} type = names[scm] data['kinds'] = cmd_kinds data['schemes'] = schemes data['name'] = names[scm] data['type'] = scm data['data_repositories'] = data_repositories data['export_urls'] = latest_revisions co_commands = {} for scheme in schemes: co_commands[scheme] = self.create_co_command( scm, username, scheme, repository_name) data['co_commands'] = co_commands return template, data, content_type
def _get_context_data(self, context_id=None): """ Returns dict with keys and values (type means here context.admin_type):: combined_context_ids: ids of contexts of type combined but not autocomplete not_separate_context_ids: set of ids of contexts of type combined, main, or autocomplete autocomplete_context_ids: ids of contexts of type autocomplete main_context_id: id of the context with type main, defaults to -1 context_by_id: context_id => context mapping all_contexts: all contexts involved_context_ids: ids of contexts for which the categories are needed to be fetched context_id: safe context id of the request :returns: dict with data """ main_context_id = -1 combined_context_ids = [] autocomplete_context_ids = [] not_separate_context_ids = set([]) context_by_id = {} all_contexts = self.categ.get_contexts() context_id = safe_int(context_id) project_key = Project.get(self.env).id for context in all_contexts: context_by_id[context.context_id] = context if context.admin_type == context.ADMIN_TYPE_MAIN: if main_context_id != -1: self.log.warning( "Invalid category data: Duplicate main contexts") context.admin_type = context.ADMIN_TYPE_COMBINED else: main_context_id = context.context_id not_separate_context_ids.add(context.context_id) if context.admin_type == context.ADMIN_TYPE_COMBINED: combined_context_ids.append(context.context_id) not_separate_context_ids.add(context.context_id) elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE: autocomplete_context_ids.append(context.context_id) not_separate_context_ids.add(context.context_id) if main_context_id == -1: self.log.warning('Invalid category data: Main context not set!') involved_context_ids = [] if context_id: if context_id not in context_by_id: raise TracError('Invalid context id provided %s' % context_id.__class__) if context_id == main_context_id: involved_context_ids = [ main_context_id ] + combined_context_ids + autocomplete_context_ids else: involved_context_ids = [context_id] else: involved_context_ids = [ context.context_id for context in all_contexts ] data = { 'project_key': project_key, 'autocomplete_context_ids': autocomplete_context_ids, 'combined_context_ids': combined_context_ids, 'not_separate_context_ids': not_separate_context_ids, 'main_context_id': main_context_id, 'context_by_id': context_by_id, 'all_contexts': all_contexts, 'involved_context_ids': involved_context_ids, 'context_id': context_id } return data
def process_request(self, req): """ Handle the export requests :raises: TracError in case of failure """ req.perm.require('BROWSER_VIEW') req.perm.require('FILE_VIEW') repository_name = req.path_info.split("/")[-1] # Get default repository and its type rm = RepositoryManager(self.env) list_repos = rm.get_real_repositories() repo = None repo_type = None for r in list_repos: if r.get_base().split("/")[-1].lower() == repository_name: repo = r break repo_type = repo.get_base().split(":")[0] svn_path = 'trunk' format = plaintext(req.args.get('format', 'zip')) conf.log.exception("Repotype at beginning: %s" % repo_type) # Get revision info. For svn it's in format: <revnum>/<path> revision = plaintext(str(req.args.get('rev', repo.get_youngest_rev()))) # Validate if given revision really exists try: revision = repo.normalize_rev(revision) except NoSuchChangeset: raise HTTPNotFound('No such changeset') # Validate format if format not in self.formats: raise TracError('Format is not supported') # Load project object based on current environment env_name = conf.resolveProjectName(self.env) #repo_type = self.env.config.get('trac', 'repository_type') repo_dir = conf.getEnvironmentVcsPath(env_name, repo_type, repository_name) project = Project.get(env_name=env_name) if repo_type not in conf.supported_scm_systems: raise TracError('Non-supported VCS type') # Create temporary directory with appropriate subdirectory where to export repository tempfd = tempfile.NamedTemporaryFile(delete=False) # Dump the repository per type, into defined location conf.log.exception("Repotype: %s, repo_dir: %s" % (repo_type, repo_dir)) try: if repo_type == 'git': # Use short revision format revision = revision[:6] prefix = '%s-%s' % (env_name, revision[:6]) self._archive_git(repo_dir, revision, format, tempfd.name, prefix) elif repo_type == 'hg': # In case of both local:global revision format, use only global if ':' in revision: revision = revision.split(':', 1)[1] prefix = '%s-%s' % (env_name, revision[:6]) self._archive_hg(repo_dir, revision, format, tempfd.name, prefix) elif repo_type == 'svn': assert format == 'zip', 'Only zip format is supported for subversion' # Redirect to Trac's internal changeset functionality # Example: https://localhost/svnproject/changeset/4/trunk?old_path=%2F&format=zip changeset_href = Href('/%s/changeset' % env_name) return req.redirect( changeset_href(revision, repository_name + "/", format='zip')) # Redirect raises RequestDone: re-raise it except RequestDone: raise except Exception, err: self.env.log.exception('Repository dump failed: %s' % err) raise TracError( 'Repository archive failed - please try again later')
def remove_project(self, req): """ Handler for removing project :param Request req: Trac request Handler expects to have following arguments in it: - project: Name of the project env - goto: Optional URL where to go after project removal. Defaults to /home/myprojects """ backurl = req.args.get('goto', ProjectListModule.home('myprojects')) short_name = req.args.get('project') if not short_name: return self.remove_failure(req) if req.args.get('cancel'): add_notice(req, 'Project deletion cancelled') return self.remove_failure(req) author = req.authname project = Project.get(env_name=short_name) if project is None: add_warning(req, 'Specified project does not exist!') return self.remove_failure(req) # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return self.remove_failure(req) if req.method != 'POST': return self.remove_failure(req, project=project) try: project.validate() except ProjectValidationException: req.redirect(backurl) # Check the permissions and method prjs = Projects() if ('TRAC_ADMIN' in req.perm or prjs.is_project_owner( project.env_name, author)) and req.method == 'POST': archive = ProjectArchive() # Archive the project before removal if not archive.archive(project): add_warning( req, 'Could not archive project "%s". Will not remove the project' % project.project_name) elif prjs.remove_project(project): # Notify listeners for listener in self.project_change_listeners: listener.project_deleted(project) # Notify end user add_notice(req, 'Project "%s" removed' % project.project_name) else: add_warning( req, 'Could not remove project "%s". Try again later' % project.project_name) else: add_warning( req, 'You are not allowed to remove project "%s" (check permissions and method)' % project.project_name) req.redirect(backurl)
def create_project(self, req): """ Handler for creating project request """ req.perm.require("PROJECT_CREATE") if req.method != 'POST': return self.create_failure( req, 'POST request needed when creating a new project') author = get_context(req)['author'] # If agreement needed but not getting it, show failure if conf.project_requires_agreed_terms and not self._is_active_user( req): return self.create_failure( req, 'You need to approve legal text to create a project!') # Read and transform some variables vcs_type = req.args.get('vcstype') vcs_name = req.args.get('vcs_name') if not self.validate_repository_name(vcs_name): return self.create_failure(req, 'Check repository name.') parent_project = None if "_project_" in req.args: parent_project = Project.get(env_name=req.args.get('_project_')) self.__require_permissions_for_cloning(req.authname, parent_project) vcs_type = conf.getVersionControlType( parent_project.env_name ) # TODO: expensive call, probably needed # Read settings settings = {} if vcs_type: settings['vcs_type'] = vcs_type if vcs_name: settings['vcs_name'] = vcs_name identifier = req.args.get('prj_short_name') name = req.args.get('prj_long_name') project_visibility = 'prj_is_public' in req.args public = False published = None if project_visibility: public = True published = datetime.now() # Create project object project = Project( id=None, env_name=identifier, project_name=name, description=req.args.get('prj_description'), author_id=author.id, created=None, # Use default which is now() public=public, published=published) # Create project environment projects = Projects() try: projects.create_project(project, settings) except ProjectValidationException as exc: self.log.warning( 'Project creation failed due the validation: {0}'.format( exc.value)) return self.create_failure(req, exc.value) except: self.log.exception('Project creation failed') return self.create_failure( req, _("Creating project failed. Try again later.")) if public: projects.add_public_project_visibility(project.id) #Add author to follow project watch_store = CQDEWatchlistStore() watch_store.watch_project(author.id, project.id) #Change project trac.ini to support multiple repositories project_env_path = conf.getEnvironmentSysPath(project.env_name) repo_env_path = conf.getEnvironmentVcsPath(project.env_name, vcs_type, vcs_name) os.rename(project_env_path + '/conf/trac.ini', project_env_path + '/conf/trac.ini.bak') oldfile = open(project_env_path + '/conf/trac.ini.bak', 'r') newfile = open(project_env_path + '/conf/trac.ini', 'w') lines = oldfile.readlines() for line in lines: newfile.write(line) if line.startswith('database ='): break newfile.write( 'repository_dir =\nrepository_type = svn\n\n[repositories]\n') newfile.write('%s.dir = %s\n' % (vcs_name, repo_env_path)) newfile.write('%s.type = %s\n' % (vcs_name, vcs_type)) newfile.close() oldfile.close() os.remove(project_env_path + '/conf/trac.ini.bak') # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: try: listener.project_created(project) listener.project_watchers(project) if public: listener.project_set_public(project) except: pass return self.create_success(req, project)
def render_admin_panel(self, req, cat, page, path_info): """ Returns the admin view and handles the form actions """ req.perm.require('TRAC_ADMIN') project = Project.get(self.env) if req.method == 'POST': myprojects_url = conf.url_home_path + "/myprojects" conf.log.exception("Redirect URL: %s" % myprojects_url) thisurl = Href(req.base_path)(req.path_info) context = Context.from_request(req) # Handle project remove if 'remove' in req.args: # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return req.redirect(thisurl) # Archive the project before removal archive = ProjectArchive() archived = archive.archive(project) if not archived: add_warning( req, 'Could not archive project "%s". Will not remove the project' % project.project_name) return req.redirect(thisurl) # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_archived(project) # Do the actual project removal projects = Projects() if projects.remove_project(project): # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_deleted(project) # NOTE: We no longer have project tables/session etc available req.redirect(myprojects_url) else: add_warning( req, 'Could not remove project "%s". Try again later' % project.project_name) return req.redirect(thisurl) add_script(req, 'multiproject/js/jquery-ui.js') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_system.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') # NOTE: Trac automatically puts 'project' dict in chrome thus it cannot be used data = { 'multiproject': { 'project': project, 'home_url': conf.url_home_path } } return 'admin_system.html', data