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 get_project_watchers(self, projects): """ Returns a map of project watchers. project_id => number of watchers """ w = CQDEWatchlistStore() project_watchers = {} for project in projects: watchlist = w.get_watchers_by_project(project.id) project_watchers[project.id] = len(watchlist) return project_watchers
def follow_project(self, user_name, group_name): """ Sets user to follow project if group is correct. :param list user_name: username for get user object :param list group_name: groupname to check correct group """ if self.env is not None: if group_name == 'Members' or group_name == 'Owners': from multiproject.common.projects.project import Project project = Project.get(None, None, _get_trac_project_name(self.env)) user = conf.getUserStore().getUser(user_name) from multiproject.core.watchlist import CQDEWatchlistStore watch_store = CQDEWatchlistStore() watch_store.watch_project(user.id, project.id)
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 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) 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_get_by_notification(self): w = CQDEWatchlistStore() w.watch_project(32, 23, "immediate") w.watch_project(34, 23, "daily") watchlist = w.get_by_notification("daily") self.assertTrue(len(watchlist) == 1) self.assertTrue(watchlist[0].user_id == 34) watchlist = w.get_by_notification("weekly") self.assertTrue(len(watchlist) == 0)
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 test_watch(self): w = CQDEWatchlistStore() self.assertTrue(w) w.watch_project(32, 23) w.watch_project(34, 23, "daily") watchlist = w.get_projects_by_user(32) self.assertTrue(len(watchlist) == 1) self.assertTrue(watchlist[0].user_id == 32) self.assertTrue(watchlist[0].project_id == 23) self.assertTrue(watchlist[0].notification == "immediate") watchlist = w.get_watchers_by_project(23) self.assertTrue(len(watchlist) == 2) self.assertTrue(w.is_watching(32, 23)) self.assertTrue(w.is_watching(34, 23))
def _get_status(self, req, project_id): """ Returns the watching status as a tuple: - Number of followers - True/False whether current user is already following or not :param req: Trac Request :return: Status :rtype: tuple """ # Load watching info userstore = get_userstore() user = userstore.getUser(req.authname) watchstore = CQDEWatchlistStore() is_watching = False # NOTE: Some environments may not have special anonymous/authenticated users, thus user can be None if user: is_watching = watchstore.is_watching(user.id, project_id) watchers = len(watchstore.get_watchers_by_project(project_id)) return watchers, is_watching
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 _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 process_request(self, req): """ Handles the request :param req: Request :return: json """ if req.method != 'POST': raise TracError('Invalid request') req.perm.require('TIMELINE_VIEW') userstore = get_userstore() watchstore = CQDEWatchlistStore() action = req.args.get('action') project_id = 0 # Determine project id from URL match = REQ_REGEXP.match(req.path_info) if match: project_id = int(match.group('pid')) # Load userinfo user = userstore.getUser(req.authname) uid = user.id if user else None #load project project = Project.get(id=project_id) # Start following if action == 'watch': watchstore.watch_project(uid, project_id) # Notify listeners. for listener in self.project_change_listeners: try: listener.project_watchers(project) except: pass # Stop following elif action == 'unwatch': watchstore.unwatch_project(uid, project_id) goto = req.args.get('goto', req.href('')) return req.redirect(goto)
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_unwatch(self): w = CQDEWatchlistStore() w.watch_project(32, 23, "immediate") w.watch_project(34, 23, "daily") w.unwatch_project(32, 23) watchlist = w.get_projects_by_user(32) self.assertTrue(len(watchlist) == 0) watchlist = w.get_projects_by_user(34) self.assertTrue(len(watchlist) == 1) watchlist = w.get_watchers_by_project(23) self.assertTrue(len(watchlist) == 1) self.assertFalse(w.is_watching(32, 23))
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 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)