def test_get_my_tasks(self): dbStub.addResult(project_tasks_result) p = Projects() project_list = [ Project(1, "project1", "project1", "Project 1", 1, "2010-01-01"), Project(2, "project2", "project2", "Project 2", 2, "2010-01-02") ] tasks = p.get_all_user_tasks("username", project_list) self.assertEquals(len(tasks), 6) self.assertTrue(dbStub.closed)
def query_archived_projects(self, query): """ Returns a list of archived projects given with query """ projects = [] with admin_query() as cursor: try: cursor.execute(query) for project in cursor.fetchall(): # Create regular project prj = Project(id=project[1], env_name=project[3], project_name=project[2], description=None, author_id=project[4], created=project[5], parent_id=project[6]) # Add some archiving parameters prj.archive_folder_name = project[7] prj.archive_path = conf.archive_path + "/" + project[7] prj.archive_date = project[8] prj.remove_due = project[9] prj.removed_at = project[10] prj.project_archive_id = project[0] projects.append(prj) except: conf.log.exception( "Querying archived projects failed with query '''%s'''" % query) return projects
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 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 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): """ 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 query_archived_projects(self, query): """ Returns a list of archived projects given with query """ projects = [] with admin_query() as cursor: try: cursor.execute(query) for project in cursor.fetchall(): # Create regular project prj = Project( id=project[1], env_name=project[3], project_name=project[2], description=None, author_id=project[4], created=project[5], parent_id=project[6], ) # Add some archiving parameters prj.archive_folder_name = project[7] prj.archive_path = conf.archive_path + "/" + project[7] prj.archive_date = project[8] prj.remove_due = project[9] prj.removed_at = project[10] prj.project_archive_id = project[0] projects.append(prj) except: conf.log.exception("Querying archived projects failed with query '''%s'''" % query) return projects
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 _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 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 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 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 createProject(self, req, projectid, projectname, description, project_visibility, serviceslist): """ Request to create a new project """ services = {} if str(projectid).strip() == '': e = exceptions.Exception raise e("Incorrect project identification name") if str(projectname).strip() == '': e = exceptions.Exception raise e("Incorrect project name") users = get_userstore() author = users.getUser(req.authname) if not author.can_create_project(): raise Exception("You are not allowed to create projects") public = False published = None if project_visibility == "on" or project_visibility == "true": public = True published = datetime.now() # Create project class project = Project(id=None, env_name=unicode(projectid), project_name=projectname, description=description, author_id=author.id, created=None, public=public, published=published) if project_visibility == "on" or project_visibility == "true": services['project_visibility'] = 'on' else: services['project_visibility'] = 'off' projects = Projects() projects.getServices(services, serviceslist) # Create project try: projects.create_project(project, services) return self.get_scm_repository_url(project.env_name) except ProjectValidationException as exc: raise Exception(exc.value) except: raise Exception("Creating project failed. Try again later.")
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 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 _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 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 setUp(self): userObj = User() userObj.id = 30 userObj.username = '******' self.projectObj = Project(24, 'storageauthtest', 'Storage auth testing', 'Desc', userObj.id, None, author=userObj) conf.use_test_db(True) self.load_fixtures() self.store = CQDEUserGroupStore(self.projectObj.id) self.store.remove_group('Owners') self.store.remove_group('Public contributors') self.store.remove_group('Public viewers')
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_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 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 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 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 test_validate(self): invalid_identifier = Project(1, u'testi_', u'testi_', u'Long name', 1, None) toolong_identifier = Project(1, u'asdfghjklqwertyuiopzxcvbnmasdfahg', u'asdfghjklqwertyuiopzxcvbnmasdfahg', u'Long name', 1, None) # Test invalid identifier msg = "" try: invalid_identifier.validate() except ProjectValidationException as exc: msg = exc.value self.assertTrue( msg.startswith("Identifier can not start or end with underscore")) # Test too long identifier msg = "" try: toolong_identifier.validate() except ProjectValidationException as exc: msg = exc.value self.assertTrue(msg.startswith("Too long project indentifier. L"))
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 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): 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