def test_differing_profile_proj_shortname_rest_api(self): User.upsert('foo_bar') # default auth provider's user_project_shortname() converts _ to - (for subdomain name validation reasons) # but can access user URL with "_" still self.app.get('/rest/u/foo_bar/') # and with "-" too, no redirect here to avoid api clients having to deal with unexpected redirects self.app.get('/rest/u/foo-bar/')
def test_link_to_send_message_form(self): User.by_username('test-admin').set_pref('email_address', '*****@*****.**') User.by_username('test-user').set_pref('email_address', '*****@*****.**') r = self.app.get('/u/test-user/profile', status=200) assert r.html.find('a', dict(href='send_message'))
def test_differing_profile_proj_shortname(self): User.upsert('foo_bar') # default auth provider's user_project_shortname() converts _ to - for the project name response = self.app.get('/u/foo_bar/', status=302) assert_equal(response.location, 'http://localhost/u/foo-bar/') response = self.app.get('/u/foo_bar/profile/xyz?a=b', status=302) assert_equal(response.location, 'http://localhost/u/foo-bar/profile/xyz?a=b')
def test_disable_user_messages(self): User.by_username('test-admin').set_pref('email_address', '*****@*****.**') test_user = User.by_username('test-user') test_user.set_pref('email_address', '*****@*****.**') test_user.set_pref('disable_user_messages', True) r = self.app.get('/u/test-user/profile') assert '<a href="send_message">Send me a message</a>' not in r r = self.app.get('/u/test-user/profile/send_message', status=302) assert 'This user has disabled direct email messages' in self.webflash(r)
def test_user_messages_sender_disabled(self): admin_user = User.by_username('test-admin') admin_user.set_pref('email_address', '*****@*****.**') admin_user.set_pref('disable_user_messages', True) test_user = User.by_username('test-user') test_user.set_pref('email_address', '*****@*****.**') r = self.app.get('/u/test-user/profile/send_message', status=200) assert 'you currently have user messages disabled' in r
def test_disable_user_messages(self): User.by_username('test-admin').set_pref('email_address', '*****@*****.**') test_user = User.by_username('test-user') test_user.set_pref('email_address', '*****@*****.**') test_user.set_pref('disable_user_messages', True) r = self.app.get('/u/test-user/profile') assert '<a href="send_message">Send me a message</a>' not in r r = self.app.get('/u/test-user/profile/send_message', status=302) assert 'This user has disabled direct email messages' in self.webflash( r)
def test_differing_profile_proj_shortname(self): User.upsert('foo_bar') # default auth provider's user_project_shortname() converts _ to - (for subdomain name validation reasons) # but can access user URL with "_" still self.app.get('/u/foo_bar/profile/') # and accessing it by "-" which was the previous way, will redirect response = self.app.get('/u/foo-bar/', status=302) assert_equal(response.location, 'http://localhost/u/foo_bar/') response = self.app.get('/u/foo-bar/profile/xyz?a=b', status=302) assert_equal(response.location, 'http://localhost/u/foo_bar/profile/xyz?a=b')
def test_link_to_send_message_form(self): User.by_username('test-admin').set_pref('email_address', '*****@*****.**') User.by_username('test-user').set_pref('email_address', '*****@*****.**') r = self.app.get('/u/test-user/profile', status=200) assert '<a href="send_message">Send me a message</a>' in r r = self.app.get('/u/test-user/profile', extra_environ={'username': '******'}, status=200) assert '<a href="send_message">Send me a message</a>' not in r
def test_ticket_move_with_users_not_in_project(self): app1 = c.project.app_instance('bugs') app2 = c.project.app_instance('bugs2') app1.globals.custom_fields.extend([ {'name': '_user_field', 'type': 'user', 'label': 'User field'}, {'name': '_user_field_2', 'type': 'user', 'label': 'User field 2'}]) app2.globals.custom_fields.extend([ {'name': '_user_field', 'type': 'user', 'label': 'User field'}, {'name': '_user_field_2', 'type': 'user', 'label': 'User field 2'}]) ThreadLocalORMSession.flush_all() ThreadLocalORMSession.close_all() from allura.websetup import bootstrap bootstrap.create_user('test-user-0') with h.push_context(c.project._id, app_config_id=app1.config._id): ticket = Ticket.new() ticket.summary = 'test ticket' ticket.description = 'test description' ticket.custom_fields['_user_field'] = 'test-user' # in project # not in project ticket.custom_fields['_user_field_2'] = 'test-user-0' # not in project ticket.assigned_to_id = User.by_username('test-user-0')._id t = ticket.move(app2.config) assert_equal(t.assigned_to_id, None) assert_equal(t.custom_fields['_user_field'], 'test-user') assert_equal(t.custom_fields['_user_field_2'], '') post = Post.query.find( dict(thread_id=ticket.discussion_thread._id)).first() assert post is not None, 'No comment about ticket moving' message = 'Ticket moved from /p/test/bugs/1/' message += '\n\nCan\'t be converted:\n' message += '\n- **_user_field_2**: test-user-0 (user not in project)' message += '\n- **assigned_to**: test-user-0 (user not in project)' assert_equal(post.text, message)
def setUp(self): from allura.model import User setup_basic_test() setup_global_objects() self.user = User.by_username("test-user-2") c.user = self.user
def test_ticket_move(self): app1 = c.project.app_instance('bugs') app2 = c.project.app_instance('bugs2') with h.push_context(c.project._id, app_config_id=app1.config._id): ticket = Ticket.new() ticket.summary = 'test ticket' ticket.description = 'test description' ticket.assigned_to_id = User.by_username('test-user')._id ticket.discussion_thread.add_post(text='test comment') assert_equal(Ticket.query.find({'app_config_id': app1.config._id}).count(), 1) assert_equal(Ticket.query.find({'app_config_id': app2.config._id}).count(), 0) assert_equal(Post.query.find(dict(thread_id=ticket.discussion_thread._id)).count(), 1) t = ticket.move(app2.config) assert_equal(Ticket.query.find({'app_config_id': app1.config._id}).count(), 0) assert_equal(Ticket.query.find({'app_config_id': app2.config._id}).count(), 1) assert_equal(t.summary, 'test ticket') assert_equal(t.description, 'test description') assert_equal(t.assigned_to.username, 'test-user') assert_equal(t.url(), '/p/test/bugs2/1/') post = Post.query.find(dict(thread_id=ticket.discussion_thread._id, text={'$ne': 'test comment'})).first() assert post is not None, 'No comment about ticket moving' message = 'Ticket moved from /p/test/bugs/1/' assert_equal(post.text, message) post = Post.query.find(dict(text='test comment')).first() assert_equal(post.thread.discussion_id, app2.config.discussion_id) assert_equal(post.thread.app_config_id, app2.config._id) assert_equal(post.app_config_id, app2.config._id)
def test_login(self): user = User.by_username("test-user") init_logins = user.stats.tot_logins_count r = self.app.post("/auth/do_login", params=dict(username=user.username, password="******")) assert user.stats.tot_logins_count == 1 + init_logins assert user.stats.getLastMonthLogins() == 1 + init_logins
def _to_python(self, value, state): from allura.model import User if value: if c.user == User.anonymous(): raise fe.Invalid('Log in to Mark as Private', value, state) else: return value
def test_ticket_move_with_users_not_in_project(self): app1 = c.project.app_instance('bugs') app2 = c.project.app_instance('bugs2') app1.globals.custom_fields.extend([ {'name': '_user_field', 'type': 'user', 'label': 'User field'}, {'name': '_user_field_2', 'type': 'user', 'label': 'User field 2'}]) app2.globals.custom_fields.extend([ {'name': '_user_field', 'type': 'user', 'label': 'User field'}, {'name': '_user_field_2', 'type': 'user', 'label': 'User field 2'}]) ThreadLocalORMSession.flush_all() ThreadLocalORMSession.close_all() from allura.websetup import bootstrap bootstrap.create_user('test-user-0') with h.push_context(c.project._id, app_config_id=app1.config._id): ticket = Ticket.new() ticket.summary = 'test ticket' ticket.description = 'test description' ticket.custom_fields['_user_field'] = 'test-user' # in project ticket.custom_fields['_user_field_2'] = 'test-user-0' # not in project ticket.assigned_to_id = User.by_username('test-user-0')._id # not in project t = ticket.move(app2.config) assert_equal(t.assigned_to_id, None) assert_equal(t.custom_fields['_user_field'], 'test-user') assert_equal(t.custom_fields['_user_field_2'], '') post = Post.query.find(dict(thread_id=ticket.discussion_thread._id)).first() assert post is not None, 'No comment about ticket moving' message = 'Ticket moved from /p/test/bugs/1/' message += '\n\nCan\'t be converted:\n' message += '\n- **_user_field_2**: test-user-0 (user not in project)' message += '\n- **assigned_to**: test-user-0 (user not in project)' assert_equal(post.text, message)
def setUp(self): from allura.model import User setup_basic_test() setup_global_objects() self.user = User.by_username('test-user-2') c.user = self.user
def move(self, app_config, notify=True): """Move ticket from current tickets app to tickets app with given app_config""" app = app_config.project.app_instance(app_config) prior_url = self.url() prior_app = self.app prior_ticket_num = self.ticket_num attachments = self.attachments attach_metadata = BaseAttachment.metadata_for(self) prior_cfs = [(cf["name"], cf["type"], cf["label"]) for cf in prior_app.globals.custom_fields or []] new_cfs = [(cf["name"], cf["type"], cf["label"]) for cf in app.globals.custom_fields or []] skipped_fields = [] user_fields = [] for cf in prior_cfs: if cf not in new_cfs: # can't convert skipped_fields.append(cf) elif cf[1] == "user": # can convert and field type == user user_fields.append(cf) messages = [] for cf in skipped_fields: name = cf[0] messages.append("- **%s**: %s" % (name, self.custom_fields.get(name, ""))) for cf in user_fields: name = cf[0] username = self.custom_fields.get(name, None) user = app_config.project.user_in_project(username) if not user or user == User.anonymous(): messages.append("- **%s**: %s (user not in project)" % (name, username)) self.custom_fields[name] = "" # special case: not custom user field (assigned_to_id) user = self.assigned_to if user and not app_config.project.user_in_project(user.username): messages.append("- **assigned_to**: %s (user not in project)" % user.username) self.assigned_to_id = None custom_fields = {} for cf in new_cfs: fn, ft, fl = cf old_val = self.custom_fields.get(fn, None) if old_val is None: custom_fields[fn] = None if ft == "user" else "" custom_fields[fn] = old_val self.custom_fields = custom_fields # move ticket. ensure unique ticket_num while True: with h.push_context(app_config.project_id, app_config_id=app_config._id): ticket_num = app.globals.next_ticket_num() self.ticket_num = ticket_num self.app_config_id = app_config._id new_url = app_config.url() + str(self.ticket_num) + "/" try: session(self).flush(self) h.log_action(log, "moved").info("Ticket %s moved to %s" % (prior_url, new_url)) break except OperationFailure, err: if "duplicate" in err.args[0]: log.warning("Try to create duplicate ticket %s when moving from %s" % (new_url, prior_url)) session(self).expunge(self) continue
def test_login(self): user = User.by_username('test-user') init_logins = c.user.stats.tot_logins_count r = self.app.post('/auth/do_login', params=dict(username=user.username, password='******')) assert user.stats.tot_logins_count == 1 + init_logins assert user.stats.getLastMonthLogins() == 1 + init_logins
def test_login(self): user = User.by_username('test-user') init_logins = user.stats.tot_logins_count self.app.post('/auth/do_login', params=dict( username=user.username, password='******')) assert user.stats.tot_logins_count == 1 + init_logins assert user.stats.getLastMonthLogins() == 1 + init_logins
def update(self, ticket_form): self.globals.invalidate_bin_counts() # update is not allowed to change the ticket_num ticket_form.pop('ticket_num', None) self.labels = ticket_form.pop('labels', []) custom_sums = set() custom_users = set() other_custom_fields = set() for cf in self.globals.custom_fields or []: (custom_sums if cf['type'] == 'sum' else custom_users if cf['type'] == 'user' else other_custom_fields).add(cf['name']) if cf['type'] == 'boolean' and 'custom_fields.' + cf[ 'name'] not in ticket_form: self.custom_fields[cf['name']] = 'False' # this has to happen because the milestone custom field has special layout treatment if '_milestone' in ticket_form: other_custom_fields.add('_milestone') milestone = ticket_form.pop('_milestone', None) if 'custom_fields' not in ticket_form: ticket_form['custom_fields'] = dict() ticket_form['custom_fields']['_milestone'] = milestone attachment = None if 'attachment' in ticket_form: attachment = ticket_form.pop('attachment') for k, v in ticket_form.iteritems(): if k == 'assigned_to': if v: user = c.project.user_in_project(v) if user: self.assigned_to_id = user._id elif k != 'super_id': setattr(self, k, v) if 'custom_fields' in ticket_form: for k, v in ticket_form['custom_fields'].iteritems(): if k in custom_sums: # sums must be coerced to numeric type try: self.custom_fields[k] = float(v) except (TypeError, ValueError): self.custom_fields[k] = 0 elif k in custom_users: # restrict custom user field values to project members user = self.app_config.project.user_in_project(v) self.custom_fields[k] = user.username \ if user and user != User.anonymous() else '' elif k in other_custom_fields: # strings are good enough for any other custom fields self.custom_fields[k] = v self.commit() if attachment is not None: self.attach(attachment.filename, attachment.file, content_type=attachment.type) # flush so we can participate in a subticket search (if any) session(self).flush() super_id = ticket_form.get('super_id') if super_id: self.set_as_subticket_of(bson.ObjectId(super_id))
def test_private_ticket(self): from pylons import c from allura.model import ProjectRole, User from allura.model import ACE, ALL_PERMISSIONS, DENY_ALL from allura.lib.security import Credentials, has_access from allura.websetup import bootstrap admin = c.user creator = bootstrap.create_user('Not a Project Admin') developer = bootstrap.create_user('Project Developer') observer = bootstrap.create_user('Random Non-Project User') anon = User(_id=None, username='******', display_name='Anonymous') t = Ticket(summary='my ticket', ticket_num=3, reported_by_id=creator._id) assert creator == t.reported_by role_admin = ProjectRole.by_name('Admin')._id role_developer = ProjectRole.by_name('Developer')._id role_creator = t.reported_by.project_role()._id developer.project_role().roles.append(role_developer) cred = Credentials.get().clear() t.private = True assert t.acl == [ ACE.allow(role_developer, ALL_PERMISSIONS), ACE.allow(role_creator, ALL_PERMISSIONS), DENY_ALL ] assert has_access(t, 'read', user=admin)() assert has_access(t, 'create', user=admin)() assert has_access(t, 'update', user=admin)() assert has_access(t, 'read', user=creator)() assert has_access(t, 'create', user=creator)() assert has_access(t, 'update', user=creator)() assert has_access(t, 'read', user=developer)() assert has_access(t, 'create', user=developer)() assert has_access(t, 'update', user=developer)() assert not has_access(t, 'read', user=observer)() assert not has_access(t, 'create', user=observer)() assert not has_access(t, 'update', user=observer)() assert not has_access(t, 'read', user=anon)() assert not has_access(t, 'create', user=anon)() assert not has_access(t, 'update', user=anon)() t.private = False assert t.acl == [] assert has_access(t, 'read', user=admin)() assert has_access(t, 'create', user=admin)() assert has_access(t, 'update', user=admin)() assert has_access(t, 'read', user=developer)() assert has_access(t, 'create', user=developer)() assert has_access(t, 'update', user=developer)() assert has_access(t, 'read', user=creator)() assert has_access(t, 'unmoderated_post', user=creator)() assert not has_access(t, 'create', user=creator)() assert not has_access(t, 'update', user=creator)() assert has_access(t, 'read', user=observer)() assert has_access(t, 'read', user=anon)()
def setUp(self): super(TestGitCommit, self).setUp() setup_basic_test() user = User.by_username('test-admin') user.set_password('testpassword') addr = M.EmailAddress.upsert('*****@*****.**') user.claim_address('*****@*****.**') self.setup_with_tools()
def test_login(self): user = User.by_username('test-user') init_logins = user.stats.tot_logins_count self.app.get('/').follow() # establish session self.app.post('/auth/do_login', antispam=True, params=dict( username=user.username, password='******', _session_id=self.app.cookies['_session_id'], )) assert user.stats.tot_logins_count == 1 + init_logins assert user.stats.getLastMonthLogins() == 1 + init_logins
def test_login(self): user = User.by_username('test-user') init_logins = user.stats.tot_logins_count self.app.get('/') # establish session self.app.post('/auth/do_login', params=dict( username=user.username, password='******', _session_id=self.app.cookies['_session_id'] )) assert user.stats.tot_logins_count == 1 + init_logins assert user.stats.getLastMonthLogins() == 1 + init_logins
def test_send_message(self, check, gen_message_id, sendsimplemail): check.return_value = True gen_message_id.return_value = 'id' test_user = User.by_username('test-user') test_user.set_pref('email_address', '*****@*****.**') response = self.app.get('/u/test-user/profile/send_message', status=200) assert 'you currently have user messages disabled' not in response response.mustcontain( '<b>From:</b> "Test Admin" <[email protected]>' ) self.app.post('/u/test-user/profile/send_user_message', params={ 'subject': 'test subject', 'message': 'test message', 'cc': 'on' }) sendsimplemail.post.assert_called_once_with( cc=User.by_username('test-admin').get_pref('email_address'), text= 'test message\n\n---\n\nThis message was sent to you via the Allura web mail form. You may reply to this message directly, or send a message to Test Admin at http://localhost/u/test-admin/profile/send_message\n', toaddr=User.by_username('test-user').get_pref('email_address'), fromaddr=User.by_username('test-admin').get_pref('email_address'), reply_to=User.by_username('test-admin').get_pref('email_address'), message_id='id', subject='test subject') sendsimplemail.reset_mock() self.app.post('/u/test-user/profile/send_user_message', params={ 'subject': 'test subject', 'message': 'test message' }) sendsimplemail.post.assert_called_once_with( cc=None, text= 'test message\n\n---\n\nThis message was sent to you via the Allura web mail form. You may reply to this message directly, or send a message to Test Admin at http://localhost/u/test-admin/profile/send_message\n', toaddr=User.by_username('test-user').get_pref('email_address'), fromaddr=User.by_username('test-admin').get_pref('email_address'), reply_to=User.by_username('test-admin').get_pref('email_address'), message_id='id', subject='test subject') check.return_value = False response = self.app.get('/u/test-user/profile/send_message', status=200) assert 'Sorry, messaging is rate-limited' in response
def test_profile_user_card(self): user = User.by_username('test-admin') locals = { 'city': 'test-city', 'country': 'US' } webpages = ['http://allura.apache.org/'] user.set_pref('localization', locals) user.set_pref('webpages', webpages) r = self.app.get('/u/test-admin/profile/user_card') assert user.icon_url() in r.html.find('img').attrs['src'] assert user.display_name == r.html.find('div', attrs={'class': 'name'}).getText() assert user.get_pref('localization')['city'] in r.html.find('span', attrs={'class': 'subitem-loc'}).getText() assert user.get_pref('localization')['country'] in r.html.find('span', attrs={'class': 'subitem-loc'}).getText() assert user.get_pref('webpages')[0] in str(r.html.find('span', attrs={'class': 'subitem-web'}))
def test_ticket_move(self): app1 = c.project.app_instance('bugs') app2 = c.project.app_instance('bugs2') with h.push_context(c.project._id, app_config_id=app1.config._id): ticket = Ticket.new() ticket.summary = 'test ticket' ticket.description = 'test description' ticket.assigned_to_id = User.by_username('test-user')._id ticket.discussion_thread.add_post(text='test comment') assert_equal( Ticket.query.find({ 'app_config_id': app1.config._id }).count(), 1) assert_equal( Ticket.query.find({ 'app_config_id': app2.config._id }).count(), 0) assert_equal( Post.query.find( dict(thread_id=ticket.discussion_thread._id)).count(), 1) t = ticket.move(app2.config) assert_equal( Ticket.query.find({ 'app_config_id': app1.config._id }).count(), 0) assert_equal( Ticket.query.find({ 'app_config_id': app2.config._id }).count(), 1) assert_equal(t.summary, 'test ticket') assert_equal(t.description, 'test description') assert_equal(t.assigned_to.username, 'test-user') assert_equal(t.url(), '/p/test/bugs2/1/') post = Post.query.find( dict(thread_id=ticket.discussion_thread._id, text={'$ne': 'test comment'})).first() assert post is not None, 'No comment about ticket moving' message = 'Ticket moved from /p/test/bugs/1/' assert_equal(post.text, message) post = Post.query.find(dict(text='test comment')).first() assert_equal(post.thread.discussion_id, app2.config.discussion_id) assert_equal(post.thread.app_config_id, app2.config._id) assert_equal(post.app_config_id, app2.config._id)
def update(self, ticket_form): self.globals.invalidate_bin_counts() # update is not allowed to change the ticket_num ticket_form.pop('ticket_num', None) self.labels = ticket_form.pop('labels', []) custom_users = set() other_custom_fields = set() for cf in self.globals.custom_fields or []: (custom_users if cf['type'] == 'user' else other_custom_fields).add(cf['name']) if cf['type'] == 'boolean' and 'custom_fields.' + cf[ 'name'] not in ticket_form: self.custom_fields[cf['name']] = 'False' # this has to happen because the milestone custom field has special layout treatment if '_milestone' in ticket_form: other_custom_fields.add('_milestone') milestone = ticket_form.pop('_milestone', None) if 'custom_fields' not in ticket_form: ticket_form['custom_fields'] = dict() ticket_form['custom_fields']['_milestone'] = milestone attachment = None if 'attachment' in ticket_form: attachment = ticket_form.pop('attachment') for k, v in ticket_form.iteritems(): if k == 'assigned_to': if v: user = c.project.user_in_project(v) if user: self.assigned_to_id = user._id else: setattr(self, k, v) if 'custom_fields' in ticket_form: for k, v in ticket_form['custom_fields'].iteritems(): if k in custom_users: # restrict custom user field values to project members user = self.app_config.project.user_in_project(v) self.custom_fields[k] = user.username \ if user and user != User.anonymous() else '' elif k in other_custom_fields: # strings are good enough for any other custom fields self.custom_fields[k] = v self.commit() if attachment is not None: self.attach(attachment.filename, attachment.file, content_type=attachment.type)
def update(self, ticket_form): self.globals.invalidate_bin_counts() # update is not allowed to change the ticket_num ticket_form.pop('ticket_num', None) self.labels = ticket_form.pop('labels', []) custom_users = set() other_custom_fields = set() for cf in self.globals.custom_fields or []: (custom_users if cf['type'] == 'user' else other_custom_fields).add(cf['name']) if cf['type'] == 'boolean' and 'custom_fields.' + cf['name'] not in ticket_form: self.custom_fields[cf['name']] = 'False' # this has to happen because the milestone custom field has special layout treatment if '_milestone' in ticket_form: other_custom_fields.add('_milestone') milestone = ticket_form.pop('_milestone', None) if 'custom_fields' not in ticket_form: ticket_form['custom_fields'] = dict() ticket_form['custom_fields']['_milestone'] = milestone attachment = None if 'attachment' in ticket_form: attachment = ticket_form.pop('attachment') for k, v in ticket_form.iteritems(): if k == 'assigned_to': if v: user = c.project.user_in_project(v) if user: self.assigned_to_id = user._id else: setattr(self, k, v) if 'custom_fields' in ticket_form: for k,v in ticket_form['custom_fields'].iteritems(): if k in custom_users: # restrict custom user field values to project members user = self.app_config.project.user_in_project(v) self.custom_fields[k] = user.username \ if user and user != User.anonymous() else '' elif k in other_custom_fields: # strings are good enough for any other custom fields self.custom_fields[k] = v self.commit() if attachment is not None: self.attach( attachment.filename, attachment.file, content_type=attachment.type)
def feed(self, since=None, until=None, page=None, limit=None): username = c.project.shortname.split('/')[1] user = User.by_username(username) if request.environ['PATH_INFO'].endswith('.atom'): feed_type = 'atom' else: feed_type = 'rss' title = 'Recent posts by %s' % user.display_name feed = Notification.feed( {'author_id':user._id}, feed_type, title, c.project.url(), title, since, until, page, limit) response.headers['Content-Type'] = '' response.content_type = 'application/xml' return feed.writeString('utf-8')
def _check_can_message(self, from_user, to_user): if from_user is User.anonymous(): flash('You must be logged in to send user messages.', 'info') redirect(request.referer or '/') if not (from_user and from_user.get_pref('email_address')): flash('In order to send messages, you must have an email address ' 'associated with your account.', 'info') redirect(request.referer or '/') if not (to_user and to_user.get_pref('email_address')): flash('This user can not receive messages because they do not have ' 'an email address associated with their account.', 'info') redirect(request.referer or '/') if to_user.get_pref('disable_user_messages'): flash('This user has disabled direct email messages', 'info') redirect(request.referer or '/')
def get_custom_user(self, custom_user_field_name): fld = None for f in c.app.globals.custom_fields: if f.name == custom_user_field_name: fld = f break if not fld: raise KeyError, 'Custom field "%s" does not exist.' % custom_user_field_name if fld.type != 'user': raise TypeError, 'Custom field "%s" is of type "%s"; expected ' \ 'type "user".' % (custom_user_field_name, fld.type) username = self.custom_fields.get(custom_user_field_name) if not username: return None user = self.app_config.project.user_in_project(username) if user == User.anonymous(): return None return user
def _check_can_message(self, from_user, to_user): if from_user is User.anonymous(): flash('You must be logged in to send user messages.', 'info') redirect(request.referer) if not (from_user and from_user.get_pref('email_address')): flash('In order to send messages, you must have an email address ' 'associated with your account.', 'info') redirect(request.referer) if not (to_user and to_user.get_pref('email_address')): flash('This user can not receive messages because they do not have ' 'an email address associated with their account.', 'info') redirect(request.referer) if to_user.get_pref('disable_user_messages'): flash('This user has disabled direct email messages', 'info') redirect(request.referer)
def update(self, ticket_form): # update is not allowed to change the ticket_num ticket_form.pop("ticket_num", None) self.labels = ticket_form.pop("labels", []) custom_users = set() other_custom_fields = set() for cf in self.globals.custom_fields or []: (custom_users if cf["type"] == "user" else other_custom_fields).add(cf["name"]) if cf["type"] == "boolean" and "custom_fields." + cf["name"] not in ticket_form: self.custom_fields[cf["name"]] = "False" # this has to happen because the milestone custom field has special # layout treatment if "_milestone" in ticket_form: other_custom_fields.add("_milestone") milestone = ticket_form.pop("_milestone", None) if "custom_fields" not in ticket_form: ticket_form["custom_fields"] = dict() ticket_form["custom_fields"]["_milestone"] = milestone attachment = None if "attachment" in ticket_form: attachment = ticket_form.pop("attachment") for k, v in ticket_form.iteritems(): if k == "assigned_to": if v: user = c.project.user_in_project(v) if user: self.assigned_to_id = user._id else: setattr(self, k, v) if "custom_fields" in ticket_form: for k, v in ticket_form["custom_fields"].iteritems(): if k in custom_users: # restrict custom user field values to project members user = self.app_config.project.user_in_project(v) self.custom_fields[k] = user.username if user and user != User.anonymous() else "" elif k in other_custom_fields: # strings are good enough for any other custom fields self.custom_fields[k] = v if attachment is not None: self.add_multiple_attachments(attachment) # flush the session to make attachments available in the # notification email ThreadLocalORMSession.flush_all() self.commit()
def test_send_message(self, check, gen_message_id, sendsimplemail): check.return_value = True gen_message_id.return_value = 'id' test_user = User.by_username('test-user') test_user.set_pref('email_address', '*****@*****.**') response = self.app.get( '/u/test-user/profile/send_message', status=200) assert 'you currently have user messages disabled' not in response assert '<b>From:</b> "Test Admin" <[email protected]>' in response self.app.post('/u/test-user/profile/send_user_message', params={'subject': 'test subject', 'message': 'test message', 'cc': 'on'}) sendsimplemail.post.assert_called_once_with( cc=User.by_username('test-admin').get_pref('email_address'), text=u'test message\n\n---\n\nThis message was sent to you via the Allura web mail form. You may reply to this message directly, or send a message to Test Admin at http://localhost/u/test-admin/profile/send_message\n', toaddr=User.by_username('test-user').get_pref('email_address'), fromaddr=User.by_username('test-admin').get_pref('email_address'), reply_to=User.by_username('test-admin').get_pref('email_address'), message_id=u'id', subject=u'test subject') sendsimplemail.reset_mock() self.app.post('/u/test-user/profile/send_user_message', params={'subject': 'test subject', 'message': 'test message'}) sendsimplemail.post.assert_called_once_with( cc=None, text=u'test message\n\n---\n\nThis message was sent to you via the Allura web mail form. You may reply to this message directly, or send a message to Test Admin at http://localhost/u/test-admin/profile/send_message\n', toaddr=User.by_username('test-user').get_pref('email_address'), fromaddr=User.by_username('test-admin').get_pref('email_address'), reply_to=User.by_username('test-admin').get_pref('email_address'), message_id=u'id', subject=u'test subject') check.return_value = False response = self.app.get( '/u/test-user/profile/send_message', status=200) assert 'Sorry, messaging is rate-limited' in response
def setUp(self): setup_basic_test() setup_global_objects() self.user = User.by_username('test-user-2') c.user = self.user
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find(dict( _id={'$in':[ObjectId(id) for id in aslist(post_data['__ticket_ids'])]}, app_config_id=self.app_config_id)).all() fields = set(['status', 'private']) values = {} labels = post_data.get('labels', []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get('assigned_to') if assigned_to == '-': values['assigned_to_id'] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values['assigned_to_id'] = user._id private = post_data.get('private') if private: values['private'] = asbool(private) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = '' if labels: values['labels'] = self.append_new_labels(ticket.labels, labels.split(',')) for k, v in sorted(values.iteritems()): if k == 'assigned_to_id': new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text( get_label(k), new_user.display_name, old_user.display_name) elif k == 'private': def private_text(val): if val: return 'Yes' else: return 'No' message += get_change_text( get_label(k), private_text(v), private_text(getattr(ticket, k))) else: message += get_change_text( get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) \ if cf.type == 'user' \ else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == 'boolean': v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text( cf.label, new_value, old_value) if message != '': changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({'_id': {'$in': filtered_changes.keys()}}).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender = c.project.app_instance(self.app_config).email_address, fromaddr = str(c.user._id), reply_to = tg_config['forgemail.return_path'], subject = '[%s:%s] Mass edit changes by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template('forgetracker:data/mass_report.html') head = [] for f, v in sorted(values.iteritems()): if f == 'assigned_to_id': user = User.query.get(_id=v) v = user.display_name if user else v head.append('- **%s**: %s' % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == 'user': user = User.by_username(v) v = user.display_name if user else v head.append('- **%s**: %s' % (cf.label, v)) tmpl_context = {'context': c, 'data': {'header': jinja2.Markup('\n'.join(['Mass edit changing:', ''] + head))}} for user in users: tmpl_context['data'].update({'changes': changes_iter(user)}) mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get('TicketMonitoringEmail') visible_changes = [] for t_id, t in changed_tickets.items(): if (not t.private or self.app_config.options.get('TicketMonitoringType') == 'AllTicketChanges'): visible_changes.append( (changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context['data'].update({'changes': visible_changes}) mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [monitoring_email])) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = 'Updated {} ticket{} in {}'.format(count, 's' if count != 1 else '', app) Notification.post_user(c.user, None, 'flash', text=text)
def assigned_to_name(self): who = self.assigned_to if who in (None, User.anonymous()): return 'nobody' return who.get_pref('display_name')
def setUp(self): super(WithUserAndBugsApp, self).setUp() c.user = User(username='******') h.set_context('test', 'bugs', neighborhood='Projects')
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find( dict(_id={ '$in': [ObjectId(id) for id in aslist(post_data['__ticket_ids'])] }, app_config_id=self.app_config_id)).all() fields = set(['status', 'private']) values = {} labels = post_data.get('labels', []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get('assigned_to') if assigned_to == '-': values['assigned_to_id'] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values['assigned_to_id'] = user._id private = post_data.get('private') if private: values['private'] = asbool(private) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = '' if labels: values['labels'] = self.append_new_labels( ticket.labels, labels.split(',')) for k, v in sorted(values.iteritems()): if k == 'assigned_to_id': new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text(get_label(k), new_user.display_name, old_user.display_name) elif k == 'private': def private_text(val): if val: return 'Yes' else: return 'No' message += get_change_text( get_label(k), private_text(v), private_text(getattr(ticket, k))) else: message += get_change_text(get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) \ if cf.type == 'user' \ else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == 'boolean': v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text(cf.label, new_value, old_value) if message != '': changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({ '_id': { '$in': filtered_changes.keys() } }).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender=c.project.app_instance(self.app_config).email_address, fromaddr=str(c.user._id), reply_to=str(c.user._id), subject='[%s:%s] Mass edit changes by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template('forgetracker:data/mass_report.html') head = [] for f, v in sorted(values.iteritems()): if f == 'assigned_to_id': user = User.query.get(_id=v) v = user.display_name if user else v head.append('- **%s**: %s' % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == 'user': user = User.by_username(v) v = user.display_name if user else v head.append('- **%s**: %s' % (cf.label, v)) tmpl_context = { 'context': c, 'data': { 'header': jinja2.Markup('\n'.join(['Mass edit changing:', ''] + head)) } } for user in users: tmpl_context['data'].update({'changes': changes_iter(user)}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get( 'TicketMonitoringEmail') visible_changes = [] for t_id, t in changed_tickets.items(): if (not t.private or self.app_config.options.get('TicketMonitoringType') == 'AllTicketChanges'): visible_changes.append( (changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context['data'].update({'changes': visible_changes}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email])) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = 'Updated {} ticket{} in {}'.format(count, 's' if count != 1 else '', app) Notification.post_user(c.user, None, 'flash', text=text)
def configuration(self): username = c.project.shortname.split('/')[1] user = User.by_username(username) return dict(user=user)
def index(self, **kw): username = c.project.shortname.split('/')[1] user = User.by_username(username) return dict(user=user)
def move(self, app_config, notify=True): '''Move ticket from current tickets app to tickets app with given app_config''' app = app_config.project.app_instance(app_config) prior_url = self.url() prior_app = self.app prior_ticket_num = self.ticket_num attachments = self.attachments attach_metadata = BaseAttachment.metadata_for(self) prior_cfs = [(cf['name'], cf['type'], cf['label']) for cf in prior_app.globals.custom_fields or []] new_cfs = [(cf['name'], cf['type'], cf['label']) for cf in app.globals.custom_fields or []] skipped_fields = [] user_fields = [] for cf in prior_cfs: if cf not in new_cfs: # can't convert skipped_fields.append(cf) elif cf[1] == 'user': # can convert and field type == user user_fields.append(cf) messages = [] for cf in skipped_fields: name = cf[0] messages.append('- **%s**: %s' % (name, self.custom_fields.get(name, ''))) for cf in user_fields: name = cf[0] username = self.custom_fields.get(name, None) user = app_config.project.user_in_project(username) if not user or user == User.anonymous(): messages.append('- **%s**: %s (user not in project)' % (name, username)) self.custom_fields[name] = '' # special case: not custom user field (assigned_to_id) user = self.assigned_to if user and not app_config.project.user_in_project(user.username): messages.append('- **assigned_to**: %s (user not in project)' % user.username) self.assigned_to_id = None custom_fields = {} for cf in new_cfs: fn, ft, fl = cf old_val = self.custom_fields.get(fn, None) if old_val is None: custom_fields[fn] = None if ft == 'user' else '' custom_fields[fn] = old_val self.custom_fields = custom_fields # move ticket. ensure unique ticket_num while True: with h.push_context(app_config.project_id, app_config_id=app_config._id): ticket_num = app.globals.next_ticket_num() self.ticket_num = ticket_num self.app_config_id = app_config._id new_url = app_config.url() + str(self.ticket_num) + '/' try: session(self).flush(self) h.log_action(log, 'moved').info('Ticket %s moved to %s' % (prior_url, new_url)) break except OperationFailure, err: if 'duplicate' in err.args[0]: log.warning( 'Try to create duplicate ticket %s when moving from %s' % (new_url, prior_url)) session(self).expunge(self) continue
def test_private_ticket(self): from allura.model import ProjectRole from allura.model import ACE, DENY_ALL from allura.lib.security import Credentials, has_access from allura.websetup import bootstrap admin = c.user creator = bootstrap.create_user('Not a Project Admin') developer = bootstrap.create_user('Project Developer') observer = bootstrap.create_user('Random Non-Project User') anon = User(_id=None, username='******', display_name='Anonymous') t = Ticket(summary='my ticket', ticket_num=3, reported_by_id=creator._id) assert creator == t.reported_by role_admin = ProjectRole.by_name('Admin')._id role_developer = ProjectRole.by_name('Developer')._id role_creator = ProjectRole.by_user(t.reported_by, upsert=True)._id ProjectRole.by_user( developer, upsert=True).roles.append(role_developer) ThreadLocalORMSession.flush_all() cred = Credentials.get().clear() t.private = True assert_equal(t.acl, [ ACE.allow(role_developer, 'save_searches'), ACE.allow(role_developer, 'read'), ACE.allow(role_developer, 'create'), ACE.allow(role_developer, 'update'), ACE.allow(role_developer, 'unmoderated_post'), ACE.allow(role_developer, 'post'), ACE.allow(role_developer, 'moderate'), ACE.allow(role_developer, 'delete'), ACE.allow(role_creator, 'read'), ACE.allow(role_creator, 'post'), ACE.allow(role_creator, 'create'), ACE.allow(role_creator, 'unmoderated_post'), DENY_ALL]) assert has_access(t, 'read', user=admin)() assert has_access(t, 'create', user=admin)() assert has_access(t, 'update', user=admin)() assert has_access(t, 'read', user=creator)() assert has_access(t, 'post', user=creator)() assert has_access(t, 'unmoderated_post', user=creator)() assert has_access(t, 'create', user=creator)() assert not has_access(t, 'update', user=creator)() assert has_access(t, 'read', user=developer)() assert has_access(t, 'create', user=developer)() assert has_access(t, 'update', user=developer)() assert not has_access(t, 'read', user=observer)() assert not has_access(t, 'create', user=observer)() assert not has_access(t, 'update', user=observer)() assert not has_access(t, 'read', user=anon)() assert not has_access(t, 'create', user=anon)() assert not has_access(t, 'update', user=anon)() t.private = False assert t.acl == [] assert has_access(t, 'read', user=admin)() assert has_access(t, 'create', user=admin)() assert has_access(t, 'update', user=admin)() assert has_access(t, 'read', user=developer)() assert has_access(t, 'create', user=developer)() assert has_access(t, 'update', user=developer)() assert has_access(t, 'read', user=creator)() assert has_access(t, 'unmoderated_post', user=creator)() assert has_access(t, 'create', user=creator)() assert not has_access(t, 'update', user=creator)() assert has_access(t, 'read', user=observer)() assert has_access(t, 'read', user=anon)()
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find( dict( _id={"$in": [ObjectId(id) for id in aslist(post_data["__ticket_ids"])]}, app_config_id=self.app_config_id, ) ).all() fields = set(["status", "private"]) values = {} labels = post_data.get("labels", []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get("assigned_to") if assigned_to == "-": values["assigned_to_id"] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values["assigned_to_id"] = user._id private = post_data.get("private") if private: values["private"] = asbool(private) discussion_disabled = post_data.get("discussion_disabled") if discussion_disabled: values["disabled_discussion"] = asbool(discussion_disabled) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = "" if labels: values["labels"] = self.append_new_labels(ticket.labels, labels.split(",")) for k, v in sorted(values.iteritems()): if k == "assigned_to_id": new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text(get_label(k), new_user.display_name, old_user.display_name) elif k == "private" or k == "discussion_disabled": def _text(val): if val: return "Yes" else: return "No" message += get_change_text(get_label(k), _text(v), _text(getattr(ticket, k))) else: message += get_change_text(get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) if cf.type == "user" else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == "boolean": v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text(cf.label, new_value, old_value) if message != "": changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({"_id": {"$in": filtered_changes.keys()}}).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be # properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender=c.project.app_instance(self.app_config).email_address, fromaddr=str(c.user._id), reply_to=tg_config["forgemail.return_path"], subject="[%s:%s] Mass edit changes by %s" % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template("forgetracker:data/mass_report.html") head = [] for f, v in sorted(values.iteritems()): if f == "assigned_to_id": user = User.query.get(_id=v) v = user.display_name if user else v head.append("- **%s**: %s" % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == "user": user = User.by_username(v) v = user.display_name if user else v head.append("- **%s**: %s" % (cf.label, v)) tmpl_context = {"context": c, "data": {"header": jinja2.Markup("\n".join(["Mass edit changing:", ""] + head))}} for user in users: tmpl_context["data"].update({"changes": changes_iter(user)}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)]) ) mail_tasks.sendmail.post(**mail) if self.app_config.options.get("TicketMonitoringType") in ("AllTicketChanges", "AllPublicTicketChanges"): monitoring_email = self.app_config.options.get("TicketMonitoringEmail") visible_changes = [] for t_id, t in changed_tickets.items(): if not t.private or self.app_config.options.get("TicketMonitoringType") == "AllTicketChanges": visible_changes.append((changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context["data"].update({"changes": visible_changes}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email]) ) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = "%s/%s" % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = "Updated {} ticket{} in {}".format(count, "s" if count != 1 else "", app) Notification.post_user(c.user, None, "flash", text=text)