def _do_product_add(self, prefix, owner, name): product = Product(self.env) product._data.update({'prefix': prefix, 'name': name, 'owner': owner}) try: product.insert() except TracError, exc: raise AdminCommandError(to_unicode(exc))
def test_delete(self): """test that we are able to delete Products""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product.delete() post = list(Product.select(self.env, where={'prefix':'tp'})) self.assertEqual(0, len(post))
def _do_product_add(self, prefix, owner, name): product = Product(self.env) product._data.update({'prefix':prefix, 'name':name, 'owner':owner}) try: product.insert() except TracError, exc: raise AdminCommandError(to_unicode(exc))
def test_get_tickets(self): for pdata in ( {'prefix': 'p2', 'name':'product, too', 'description': ''}, {'prefix': 'p3', 'name':'strike three', 'description': ''}, ): num_tickets = 5 product = Product(self.global_env) product._data.update(pdata) product.insert() self.env = ProductEnvironment(self.global_env, product) for i in range(num_tickets): ticket = Ticket(self.env) ticket['summary'] = 'hello ticket #%s-%d' % (product.prefix, i) ticket['reporter'] = 'admin' tid = ticket.insert() # retrieve tickets using both global and product scope tickets_from_global = [(t['product'], t['id']) for t in Product.get_tickets(self.global_env, product.prefix)] self.assertEqual(len(tickets_from_global), num_tickets) tickets_from_product = [(t['product'], t['id']) for t in Product.get_tickets(self.env)] self.assertEqual(len(tickets_from_product), num_tickets) # both lists should contain same elements intersection = set(tickets_from_global) & set(tickets_from_product) self.assertEqual(len(intersection), num_tickets)
def _do_save(self, req, product): """common processing for product save events""" req.perm.require('PRODUCT_VIEW') name = req.args.get('name') prefix = req.args.get('prefix') description = req.args.get('description', '') owner = req.args.get('owner') or req.authname keys = {'prefix': prefix} field_data = {'name': name, 'description': description, 'owner': owner, } warnings = [] def warn(msg): add_warning(req, msg) warnings.append(msg) if product._exists: if name != product.name and Product.select(self.env, where={'name': name}): warn(_('A product with name "%(name)s" already exists, please ' 'choose a different name.', name=name)) elif not name: warn(_('You must provide a name for the product.')) else: req.perm.require('PRODUCT_MODIFY') product.update_field_dict(field_data) product.update(req.authname) add_notice(req, _('Your changes have been saved.')) else: req.perm.require('PRODUCT_CREATE') if not prefix: warn(_('You must provide a prefix for the product.')) elif Product.select(self.env, where={'prefix': prefix}): warn(_('Product "%(id)s" already exists, please choose another ' 'prefix.', id=prefix)) if not name: warn(_('You must provide a name for the product.')) elif Product.select(self.env, where={'name': name}): warn(_('A product with name "%(name)s" already exists, please ' 'choose a different name.', name=name)) if not warnings: prod = Product(self.env) prod.update_field_dict(keys) prod.update_field_dict(field_data) prod.insert() add_notice(req, _('The product "%(id)s" has been added.', id=prefix)) if warnings: product.update_field_dict(keys) product.update_field_dict(field_data) return self._render_editor(req, product) req.redirect(req.href.products(prefix))
def _load_product_from_data(cls, env, prefix): r"""Ensure test product with prefix is loaded """ # TODO: Use fixtures implemented in #314 product_data = cls.PRODUCT_DATA[prefix] prefix = to_unicode(prefix) product = Product(env) product._data.update(product_data) product.insert()
def test_creating_new_product_calls_environment_created(self): self._enable_component(DummyPlugin) self._enable_multiproduct() self.env.upgrade() prod = Product(self.env) prod.update_field_dict(dict(prefix='p1')) ProductEnvironment(self.env, prod, create=True) with self.env.db_direct_transaction as db: db('SELECT * FROM "p1_dummy_table"')
def test_select(self): """tests that select can search Products by fields""" p2_data = {'prefix':'tp2', 'name':'test project 2', 'description':'a different test project'} p3_data = {'prefix':'tp3', 'name':'test project 3', 'description':'test project'} product2 = Product(self.env) product2._data.update(p2_data) product3 = Product(self.env) product3._data.update(p3_data) product2.insert() product3.insert() products = list(Product.select(self.env, where={'prefix':'tp'})) self.assertEqual(1, len(products)) products = list(Product.select(self.env, where={'name':'test project'})) self.assertEqual(1, len(products)) products = list(Product.select(self.env, where={'prefix':'tp3', 'name':'test project 3'})) self.assertEqual(1, len(products))
def test_insert(self): """test saving new Product""" data = {'prefix':'new', 'name':'new', 'description':'new'} product = Product(self.env) product._data.update(data) product.insert() check_products = list(Product.select(self.env, where={'prefix':'new'})) self.assertEqual(product._data['prefix'], check_products[0]._data['prefix']) self.assertEqual(1, len(check_products))
def test_update(self): """tests that we can use update to push data to the database""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] self.assertEqual('test project', product._data['name']) new_data = {'prefix':'tp', 'name':'updated', 'description':'nothing'} product._data.update(new_data) product.update() comp_product = list(Product.select(self.env, where={'prefix':'tp'}))[0] self.assertEqual('updated', comp_product._data['name'])
def process_request(self, req): """process request handler""" req.perm.require('PRODUCT_VIEW') pid = req.args.get('productid', None) if pid: req.perm('product', pid).require('PRODUCT_VIEW') try: product = Product(self.env, {'prefix': pid}) except ResourceNotFound: product = Product(self.env) path_info = req.args.get('pathinfo') if path_info and path_info != '/': if not product._exists: # bh:ticket:561 - Display product list and warning message if pid: add_warning(req, _("Product %(pid)s not found", pid=pid)) return self._render_list(req) else: raise HTTPNotFound( _('Unable to render product page. Wrong setup?')) if pid: add_link(req, 'up', req.href.products(), _('Products')) action = req.args.get('action', 'view') if req.method == 'POST': if 'cancel' in req.args: req.redirect(req.href.products(product.prefix)) elif action == 'edit': return self._do_save(req, product) elif action == 'delete': raise TracError(_('Product removal is not allowed!')) elif action in ('new', 'edit'): return self._render_editor(req, product) elif action == 'delete': raise TracError(_('Product removal is not allowed!')) if not product._exists: if pid: # bh:ticket:561 - Display product list and warning message add_warning(req, _("Product %(pid)s not found", pid=pid)) return self._render_list(req) data = { 'product': product, 'context': web_context(req, product.resource) } return 'product_view.html', data, None
def lookup_env(cls, env, prefix=None, name=None): """Instantiate environment according to product prefix or name @throws LookupError if no product matches neither prefix nor name """ if isinstance(env, ProductEnvironment): global_env = env.parent else: global_env = env # FIXME: Update if multiproduct.dbcursor.GLOBAL_PRODUCT != '' if not prefix and not name: return global_env elif isinstance(env, ProductEnvironment) and \ env.product.prefix == prefix: return env if prefix: try: return ProductEnvironment(global_env, prefix) except LookupError: if not name: raise if name: # Lookup product by name products = Product.select(global_env, where={'name' : name}) if products: return ProductEnvironment(global_env, products[0]) else: raise LookupError("Missing product '%s'" % (name,)) else: raise LookupError("Mising product '%s'" % (prefix or name,))
def test_product_edit(self): spy = self.global_env[TestRequestSpy] self.assertIsNot(None, spy) # HTTP GET req = self._get_request_obj(self.global_env) req.authname = 'testuser' req.environ['PATH_INFO'] = '/products/%s' % (self.default_product, ) req.environ['QUERY_STRING'] = 'action=edit' real_prefix = self.default_product def assert_product_edit(req, template, data, content_type): self.assertEquals('product_edit.html', template) self.assertIs(None, content_type) self.assertFalse('products' in data) self.assertTrue('context' in data) ctx = data['context'] self.assertEquals('product', ctx.resource.realm) self.assertEquals(real_prefix, ctx.resource.id) self.assertTrue('product' in data) self.assertEquals(real_prefix, data['product'].prefix) spy.testProcessing = assert_product_edit self.expectedPrefix = self.default_product self.expectedPathInfo = '' with self.assertRaises(RequestDone): self._dispatch(req, self.global_env) # HTTP POST req = self._get_request_obj(self.global_env) req.authname = 'testuser' req.environ['REQUEST_METHOD'] = 'POST' req.environ['PATH_INFO'] = '/products/%s' % (self.default_product, ) req.args = dict(action='edit', description='New description', prefix=self.default_product, name=self.env.product.name) spy.testProcessing = assert_product_edit self.expectedPrefix = self.default_product self.expectedPathInfo = '' self.record_response = True with self.assertRaises(RequestDone): self._dispatch(req, self.global_env) try: product = Product(self.global_env, {'prefix': self.env.product.prefix}) except ResourceNotFound: self.fail('Default test product deleted ?') else: self.assertEquals('New description', product.description) product_url = Href(req.base_path).products(self.default_product) self.assertRedirect(req, product_url)
def find_ticket(self, ticket_spec): ticket = None m = re.match(r'#?(?P<tid>\d+)', ticket_spec) if m: tid = m.group('tid') try: ticket = Ticket(self.env, tid) except ResourceNotFound: # ticket not found in current product, try all other products for p in Product.select(self.env): if p.prefix != self.env.product.prefix: # TODO: check for PRODUCT_VIEW permissions penv = ProductEnvironment(self.env.parent, p.prefix) try: ticket = Ticket(penv, tid) except ResourceNotFound: pass else: break # ticket still not found, use fallback for <prefix>:ticket:<id> syntax if ticket is None: try: resource = ResourceIdSerializer.get_resource_by_id(ticket_spec) ticket = self._create_ticket_by_full_id(resource) except: raise NoSuchTicketError return ticket
def lookup_env(cls, env, prefix=None, name=None): """Instantiate environment according to product prefix or name @throws LookupError if no product matches neither prefix nor name """ if isinstance(env, ProductEnvironment): global_env = env.parent else: global_env = env # FIXME: Update if multiproduct.dbcursor.GLOBAL_PRODUCT != '' if not prefix and not name: return global_env elif isinstance(env, ProductEnvironment) and \ env.product.prefix == prefix: return env if prefix: try: return ProductEnvironment(global_env, prefix) except LookupError: if not name: raise if name: # Lookup product by name products = Product.select(global_env, where={'name': name}) if products: return ProductEnvironment(global_env, products[0]) else: raise LookupError("Missing product '%s'" % (name, )) else: raise LookupError("Mising product '%s'" % (prefix or name, ))
def test_set_table_field(self): """tests that table.field style update works""" test = {'prefix': 'td', 'name': 'test field access', 'description': 'product to test field setting'} product = Product(self.env) # attempt to set the fields from the data product.prefix = test['prefix'] product.name = test['name'] product.description = test['description'] self.assertEqual(product._data['prefix'], test['prefix']) self.assertEqual(product._data['name'], test['name']) self.assertEqual(product._data['description'], test['description'])
def test_insert_duplicate_key(self): """test attempted saving of Product with existing key fails""" dupe_key_data = {'prefix':'tp', 'name':'dupe', 'description':'dupe primary key'} product2 = Product(self.env) product2._data.update(dupe_key_data) self.assertRaises(TracError, product2.insert)
def test_field_set(self): """tests that we can use table.field = something to set field data""" prefix = self.default_data['prefix'] product = list(Product.select(self.env, where={'prefix':prefix}))[0] new_description = 'test change of description' product.description = new_description self.assertEqual(new_description, product.description)
def test_update_key_change(self): """tests that we raise an error for attempting to update key fields""" bad_data = {'prefix':'tp0', 'name':'update', 'description':'nothing'} product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product._data.update(bad_data) self.assertRaises(TracError, product.update)
def _render_list(self, req): """products list""" products = [p for p in Product.select(self.env) if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix))] data = {'products': products, 'context': web_context(req, Resource('product', None))} return 'product_list.html', data, None
def test_insert_into_multiple_products(self): # UIDs are global, autoincremented # IDs are product-scoped, incremented in the SQL translator self.env = ProductEnvironment(self.global_env, self.default_product) tid = self._insert_ticket('hello kitty', reporter='admin') ticket = Ticket(self.env, tid) self.assertEqual(tid, 1) self.assertEqual(self._get_ticket_uid(tid), 1) self.assertEqual(ticket.id, tid) tid = self._insert_ticket('hello kitteh', reporter='admin') ticket = Ticket(self.env, tid) self.assertEqual(tid, 2) self.assertEqual(self._get_ticket_uid(tid), 2) self.assertEqual(ticket.id, tid) p2 = Product(self.global_env) p2.prefix = 'p2' p2.name = 'product, too' p2.owner = 'admin' p2.insert() self.env = ProductEnvironment(self.global_env, p2) tid = self._insert_ticket('hello catty', reporter='admin') ticket = Ticket(self.env, tid) self.assertEqual(tid, 1) self.assertEqual(self._get_ticket_uid(tid), 3) self.assertEqual(ticket.id, tid) tid = self._insert_ticket('hello ocelot', reporter='admin') ticket = Ticket(self.env, tid) self.assertEqual(tid, 2) self.assertEqual(self._get_ticket_uid(tid), 4) self.assertEqual(ticket.id, tid)
def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*']) self.env.path = tempfile.mkdtemp('bh-product-tempenv') self.envprovider = MultiProductEnvironmentProvider(self.env) try: self.envprovider.upgrade_environment(self.env.db_transaction) except OperationalError: # table remains but database version is deleted pass self.default_data = {'prefix':'tp', 'name':'test project', 'description':'a test project'} product = Product(self.env) product._data.update(self.default_data) product.insert()
def test_set_table_field(self): """tests that table.field style update works""" test = { 'prefix': 'td', 'name': 'test field access', 'description': 'product to test field setting' } product = Product(self.env) # attempt to set the fields from the data product.prefix = test['prefix'] product.name = test['name'] product.description = test['description'] self.assertEqual(product._data['prefix'], test['prefix']) self.assertEqual(product._data['name'], test['name']) self.assertEqual(product._data['description'], test['description'])
def test_field_data_get(self): """tests that we can use table.field syntax to get to the field data""" prefix = self.default_data['prefix'] name = self.default_data['name'] description = self.default_data['description'] product = list(Product.select(self.env, where={'prefix':prefix}))[0] self.assertEqual(prefix, product.prefix) self.assertEqual(name, product.name) self.assertEqual(description, product.description)
def test_migrating_to_multiproduct_with_custom_default_prefix(self): ticket = self.insert_ticket('ticket') self.env.config.set('multiproduct', 'default_product_prefix', 'xxx') self._enable_multiproduct() self.env.upgrade() products = Product.select(self.env) self.assertEqual(len(products), 1) self.assertEqual(products[0].prefix, 'xxx')
def _render_list(self, req): """products list""" products = [p for p in Product.select(self.env) if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix))] map(lambda p: setattr(p, 'href', resolve_product_href( lookup_product_env(self.env, p.prefix), self.env)), products) data = {'products': products, 'context': web_context(req, Resource('product', None))} return 'product_list.html', data, None
def _render_list(self, req): """products list""" products = [ p for p in Product.select(self.env) if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix)) ] data = { 'products': products, 'context': web_context(req, Resource('product', None)) } return 'product_list.html', data, None
def get_product_list(cls, env, req, href_fcn=None): """Returns a list of products as (prefix, name, url) tuples """ if href_fcn is None: href_fcn = req.href.products product_list = [] for product in Product.select(env): if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', product.prefix). child(product.resource)): product_list.append((product.prefix, product.name, href_fcn(product.prefix))) return product_list
def manager_exists(self, neighborhood): """Check whether the target environment exists physically. """ if neighborhood._realm == 'global': # Global environment return isinstance(self.env, (Environment, ProductEnvironment)) elif neighborhood._realm == 'product': prefix = neighborhood._id if not prefix: # Global environment return True return Product(lookup_product_env(self.env, GLOBAL_PRODUCT), {'prefix' : prefix})._exists
def get_product_list(cls, env, req, href_fcn=None): """Returns a list of products as (prefix, name, url) tuples """ if href_fcn is None: href_fcn = req.href.products product_list = [] for product in Product.select(env): if 'PRODUCT_VIEW' in req.perm( Neighborhood('product', product.prefix).child(product.resource)): product_list.append( (product.prefix, product.name, href_fcn(product.prefix))) return product_list
def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*']) self.env.path = tempfile.mkdtemp(prefix='bh-product-tempenv-') self.mpsystem = MultiProductSystem(self.env) try: self.mpsystem.upgrade_environment(self.env.db_transaction) except self.env.db_exc.OperationalError: # table remains but database version is deleted pass self.listener = self._enable_resource_change_listener() self.default_data = { 'prefix': self.INITIAL_PREFIX, 'name': self.INITIAL_NAME, 'description': self.INITIAL_DESCRIPTION } self.global_env = self.env self.product = Product(self.env) self.product._data.update(self.default_data) self.product.insert()
def _render_list(self, req): """products list""" products = [ p for p in Product.select(self.env) if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix)) ] map( lambda p: setattr( p, 'href', resolve_product_href(lookup_product_env(self.env, p.prefix), self.env)), products) data = { 'products': products, 'context': web_context(req, Resource('product', None)) } return 'product_list.html', data, None
def pre_process_request(self, req, handler): """pre process request filter""" pid = None match = PRODUCT_RE.match(req.path_info) if match: dispatcher = self.env[RequestDispatcher] if dispatcher is None: raise TracError('Unable to load RequestDispatcher.') pid = match.group('pid') if pid: products = Product.select(self.env, where={'prefix': pid}) if pid and len(products) == 1: req.args['productid'] = pid req.args['product'] = products[0].name if handler is self and match.group('pathinfo') not in ('', '/'): # select a new handler environ = req.environ.copy() pathinfo = environ['PATH_INFO'].split('/') pathinfo = '/'.join(pathinfo[:1] + pathinfo[3:]) environ['PATH_INFO'] = pathinfo newreq = Request(environ, lambda *args, **kwds: None) new_handler = None for hndlr in dispatcher.handlers: if hndlr is not self and hndlr.match_request(newreq): new_handler = hndlr req.args.update(newreq.args) break if new_handler is None: if req.path_info.endswith('/'): target = req.path_info.rstrip('/').encode('utf-8') if req.query_string: target += '?' + req.query_string req.redirect(req.href + target, permanent=True) raise HTTPNotFound('No handler matched request to %s', req.path_info) handler = new_handler else: raise ResourceNotFound( _("Product %(id)s does not exist.", id=pid), _("Invalid product id")) return handler
def pre_process_request(self, req, handler): """pre process request filter""" pid = None match = PRODUCT_RE.match(req.path_info) if match: dispatcher = self.env[RequestDispatcher] if dispatcher is None: raise TracError('Unable to load RequestDispatcher.') pid = match.group('pid') if pid: products = Product.select(self.env, where={'prefix': pid}) if pid and len(products) == 1: req.args['productid'] = pid req.args['product'] = products[0].name if handler is self and match.group('pathinfo') not in ('', '/'): # select a new handler environ = req.environ.copy() pathinfo = environ['PATH_INFO'].split('/') pathinfo = '/'.join(pathinfo[:1] + pathinfo[3:]) environ['PATH_INFO'] = pathinfo newreq = Request(environ, lambda *args, **kwds: None) new_handler = None for hndlr in dispatcher.handlers: if hndlr is not self and hndlr.match_request(newreq): new_handler = hndlr req.args.update(newreq.args) break if new_handler is None: if req.path_info.endswith('/'): target = req.path_info.rstrip('/').encode('utf-8') if req.query_string: target += '?' + req.query_string req.redirect(req.href + target, permanent=True) raise HTTPNotFound('No handler matched request to %s', req.path_info) handler = new_handler else: raise ResourceNotFound(_("Product %(id)s does not exist.", id=pid), _("Invalid product id")) return handler
def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*']) self.env.path = tempfile.mkdtemp('bh-product-tempenv') self.mpsystem = MultiProductSystem(self.env) try: self.mpsystem.upgrade_environment(self.env.db_transaction) except OperationalError: # table remains but database version is deleted pass self.listener = self._enable_resource_change_listener() self.default_data = {'prefix':self.INITIAL_PREFIX, 'name':self.INITIAL_NAME, 'description':self.INITIAL_DESCRIPTION} self.product = Product(self.env) self.product._data.update(self.default_data) self.product.insert()
def process_request(self, req): """Override for TicketModule process_request""" ticketid = req.args.get('id') productid = req.args.get('productid', '') if not ticketid: # if /newticket is executed in global scope (from QCT), redirect # the request to /products/<first_product_in_DB>/newticket if not productid and \ not isinstance(self.env, ProductEnvironment): default_product = self.env.config.get('ticket', 'default_product') products = Product.select(self.env, {'fields': ['prefix']}) prefixes = [prod.prefix for prod in products] if not default_product or default_product not in prefixes: default_product = products[0].prefix req.redirect(req.href.products(default_product, 'newticket')) return self._process_newticket_request(req) if req.path_info in ('/newticket', '/products'): raise TracError(_("id can't be set for a new ticket request.")) if isinstance(self.env, ProductEnvironment): ticket = Ticket(self.env, ticketid) if productid and ticket['product'] != productid: msg = "Ticket %(id)s in product '%(prod)s' does not exist." raise ResourceNotFound(_(msg, id=ticketid, prod=productid), _("Invalid ticket number")) return self._process_ticket_request(req) # executed in global scope -> assume ticketid=UID, redirect to product with self.env.db_direct_query as db: rows = db("""SELECT id,product FROM ticket WHERE uid=%s""", (ticketid, )) if not rows: msg = "Ticket with uid %(uid)s does not exist." raise ResourceNotFound(_(msg, uid=ticketid), _("Invalid ticket number")) tid, prefix = rows[0] req.redirect(req.href.products(prefix, 'ticket', tid))
def process_request(self, req): """Override for TicketModule process_request""" ticketid = req.args.get('id') productid = req.args.get('productid', '') if not ticketid: # if /newticket is executed in global scope (from QCT), redirect # the request to /products/<first_product_in_DB>/newticket if not productid and \ not isinstance(self.env, ProductEnvironment): default_product = self.env.config.get('ticket', 'default_product') products = Product.select(self.env, {'fields': ['prefix']}) prefixes = [prod.prefix for prod in products] if not default_product or default_product not in prefixes: default_product = products[0].prefix req.redirect(req.href.products(default_product, 'newticket')) return self._process_newticket_request(req) if req.path_info in ('/newticket', '/products'): raise TracError(_("id can't be set for a new ticket request.")) if isinstance(self.env, ProductEnvironment): ticket = Ticket(self.env, ticketid) if productid and ticket['product'] != productid: msg = "Ticket %(id)s in product '%(prod)s' does not exist." raise ResourceNotFound(_(msg, id=ticketid, prod=productid), _("Invalid ticket number")) return self._process_ticket_request(req) # executed in global scope -> assume ticketid=UID, redirect to product with self.env.db_direct_query as db: rows = db("""SELECT id,product FROM ticket WHERE uid=%s""", (ticketid,)) if not rows: msg = "Ticket with uid %(uid)s does not exist." raise ResourceNotFound(_(msg, uid=ticketid), _("Invalid ticket number")) tid, prefix = rows[0] req.redirect(req.href.products(prefix, 'ticket', tid))
def _render_link(self, context, name, label, extra='', prefix=None): """Render link to product page. """ product_env = product = None env = self.env if isinstance(env, ProductEnvironment): if (prefix is not None and env.product.prefix == prefix) \ or (prefix is None and env.name == name): product_env = env env = env.parent try: if product_env is None: if prefix is not None: product_env = ProductEnvironment(env, to_unicode(prefix)) else: product = Product.select(env, where={'name' : to_unicode(name)}) if not product: raise LookupError("Missing product") product_env = ProductEnvironment(env, to_unicode(product[0])) except LookupError: pass if product_env is not None: product = product_env.product href = resolve_product_href(to_env=product_env, at_env=self.env) if 'PRODUCT_VIEW' in context.perm(product.resource): return tag.a(label, class_='product', href=href() + extra, title=product.name) if 'PRODUCT_CREATE' in context.perm('product', name): params = [('action', 'new')] if prefix: params.append( ('prefix', prefix) ) if name: params.append( ('name', name) ) return tag.a(label, class_='missing product', href=env.href('products', params), rel='nofollow') return tag.a(label, class_='missing product')
def __init__(self, env, product, create=False): """Initialize the product environment. :param env: the global Trac environment :param product: product prefix or an instance of multiproduct.model.Product """ if not isinstance(env, trac.env.Environment): cls = self.__class__ raise TypeError("Initializer must be called with " \ "trac.env.Environment instance as first argument " \ "(got %s instance instead)" % (cls.__module__ + '.' + cls.__name__, )) ComponentManager.__init__(self) if isinstance(product, Product): if product._env is not env: raise ValueError("Product's environment mismatch") elif isinstance(product, basestring): products = Product.select(env, where={'prefix': product}) if len(products) == 1 : product = products[0] else: env.log.debug("Products for '%s' : %s", product, products) raise LookupError("Missing product %s" % (product,)) self.parent = env self.product = product self.systeminfo = [] self.setup_config() # when creating product environment, invoke `IEnvironmentSetupParticipant.environment_created` # for all setup participants that don't support multi product environments if create: for participant in self.product_setup_participants: with ComponentEnvironmentContext(self, participant): participant.environment_created()
def __init__(self, env, product, create=False): """Initialize the product environment. :param env: the global Trac environment :param product: product prefix or an instance of multiproduct.model.Product """ if not isinstance(env, trac.env.Environment): cls = self.__class__ raise TypeError("Initializer must be called with " \ "trac.env.Environment instance as first argument " \ "(got %s instance instead)" % (cls.__module__ + '.' + cls.__name__, )) ComponentManager.__init__(self) if isinstance(product, Product): if product._env is not env: raise ValueError("Product's environment mismatch") elif isinstance(product, basestring): products = Product.select(env, where={'prefix': product}) if len(products) == 1: product = products[0] else: env.log.debug("Products for '%s' : %s", product, products) raise LookupError("Missing product %s" % (product, )) self.parent = env self.product = product self.systeminfo = [] self.setup_config() # when creating product environment, invoke `IEnvironmentSetupParticipant.environment_created` # for all setup participants that don't support multi product environments if create: for participant in self.product_setup_participants: with ComponentEnvironmentContext(self, participant): participant.environment_created()
def _upgrade_system_tables(self, db, create_temp_table): # migrate system table (except wiki which is handled separately) # to a new schema # - create tables with the new schema # - populate system tables with global configuration for each product # - exception is permission table where permissions # are also populated in global scope # # permission table specifics: 'anonymous' and 'authenticated' users # should by default have a PRODUCT_VIEW permission for all products self.log.info("Migrating system tables to a new schema") for table in self.MIGRATE_TABLES: if table == 'wiki': continue temp_table_name, cols = create_temp_table(table) for product in Product.select(self.env): self.log.info("Populating table '%s' for product '%s' ('%s')", table, product.name, product.prefix) db("""INSERT INTO %s (%s, product) SELECT %s,'%s' FROM %s""" % (table, cols, cols, product.prefix, temp_table_name)) if table == 'permission': db.executemany( """INSERT INTO permission (username, action, product) VALUES (%s, %s, %s)""", [('anonymous', 'PRODUCT_VIEW', product.prefix), ('authenticated', 'PRODUCT_VIEW', product.prefix)]) if table == 'permission': self.log.info("Populating table '%s' for global scope", table) db("""INSERT INTO %s (%s, product) SELECT %s,'%s' FROM %s""" % (table, cols, cols, '', temp_table_name)) self._drop_temp_table(db, temp_table_name) db.executemany( """INSERT INTO permission (username, action, product) VALUES (%s, %s, %s)""", [('anonymous', 'PRODUCT_VIEW', ''), ('authenticated', 'PRODUCT_VIEW', '')])
def test_new_product_perm(self): """Only product owner and TRAC_ADMIN will access new product """ newproduct = Product(self.global_env) newproduct.prefix = 'NEW' newproduct.name = 'New product' newproduct.owner = 'owneruser' newproduct.insert() env = ProductEnvironment(self.global_env, newproduct) self.global_perm_admin._do_add('adminuser', 'TRAC_ADMIN') admin_perm = perm.PermissionCache(env, 'adminuser') owner_perm = perm.PermissionCache(env, 'owneruser') user_perm = perm.PermissionCache(env, 'testuser') global_permsys = perm.PermissionSystem(self.global_env) permsys = perm.PermissionSystem(env) self.assertEquals({'EMAIL_VIEW': True, 'TEST_ADMIN': True, 'TEST_CREATE': True, 'TEST_DELETE': True, 'TEST_MODIFY': True, 'TRAC_ADMIN' : True}, global_permsys.get_user_permissions('adminuser')) self.assertEquals({}, global_permsys.get_user_permissions('owneruser')) self.assertEquals({}, global_permsys.get_user_permissions('testuser')) self.assertEquals({}, permsys.get_user_permissions('adminuser')) self.assertEquals({}, permsys.get_user_permissions('owneruser')) self.assertEquals({}, permsys.get_user_permissions('testuser')) all_actions = self.permsys.get_actions() all_actions.remove('TRAC_ADMIN') for action in all_actions: self.assertTrue(admin_perm.has_permission(action)) self.assertTrue(owner_perm.has_permission(action)) self.assertFalse(user_perm.has_permission(action)) self.assertTrue(admin_perm.has_permission('TRAC_ADMIN')) self.assertFalse(owner_perm.has_permission('TRAC_ADMIN')) self.assertFalse(user_perm.has_permission('TRAC_ADMIN'))
def test_get_tickets(self): for pdata in ( { 'prefix': 'p2', 'name': 'product, too', 'description': '' }, { 'prefix': 'p3', 'name': 'strike three', 'description': '' }, ): num_tickets = 5 product = Product(self.global_env) product._data.update(pdata) product.insert() self.env = ProductEnvironment(self.global_env, product) for i in range(num_tickets): ticket = Ticket(self.env) ticket['summary'] = 'hello ticket #%s-%d' % (product.prefix, i) ticket['reporter'] = 'admin' tid = ticket.insert() # retrieve tickets using both global and product scope tickets_from_global = [ (t['product'], t['id']) for t in Product.get_tickets(self.global_env, product.prefix) ] self.assertEqual(len(tickets_from_global), num_tickets) tickets_from_product = [(t['product'], t['id']) for t in Product.get_tickets(self.env)] self.assertEqual(len(tickets_from_product), num_tickets) # both lists should contain same elements intersection = set(tickets_from_global) & set(tickets_from_product) self.assertEqual(len(intersection), num_tickets)
class ProductTestCase(unittest.TestCase): """Unit tests covering the Product model""" INITIAL_PREFIX = 'tp' INITIAL_NAME = 'test project' INITIAL_DESCRIPTION = 'a test project' def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*']) self.env.path = tempfile.mkdtemp(prefix='bh-product-tempenv-') self.mpsystem = MultiProductSystem(self.env) try: self.mpsystem.upgrade_environment(self.env.db_transaction) except self.env.db_exc.OperationalError: # table remains but database version is deleted pass self.listener = self._enable_resource_change_listener() self.default_data = {'prefix':self.INITIAL_PREFIX, 'name':self.INITIAL_NAME, 'description':self.INITIAL_DESCRIPTION} self.global_env = self.env self.product = Product(self.env) self.product._data.update(self.default_data) self.product.insert() def tearDown(self): shutil.rmtree(self.env.path) self.env.reset_db() def _enable_resource_change_listener(self): listener = TestResourceChangeListener(self.env) listener.resource_type = Product listener.callback = self.listener_callback return listener def listener_callback(self, action, resource, context, old_values = None): # pylint: disable=unused-argument # pylint: disable=attribute-defined-outside-init self.prefix = resource.prefix self.name = resource.name self.description = resource.description def test_set_table_field(self): """tests that table.field style update works""" test = {'prefix': 'td', 'name': 'test field access', 'description': 'product to test field setting'} product = Product(self.env) # attempt to set the fields from the data product.prefix = test['prefix'] product.name = test['name'] product.description = test['description'] self.assertEqual(product._data['prefix'], test['prefix']) self.assertEqual(product._data['name'], test['name']) self.assertEqual(product._data['description'], test['description']) def test_select(self): """tests that select can search Products by fields""" p2_data = {'prefix':'tp2', 'name':'test project 2', 'description':'a different test project'} p3_data = {'prefix':'tp3', 'name':'test project 3', 'description':'test project'} product2 = Product(self.env) product2._data.update(p2_data) product3 = Product(self.env) product3._data.update(p3_data) product2.insert() product3.insert() products = list(Product.select(self.env, where={'prefix':'tp'})) self.assertEqual(1, len(products)) products = list(Product.select(self.env, where={'name':'test project'})) self.assertEqual(1, len(products)) products = list(Product.select(self.env, where={'prefix':'tp3', 'name':'test project 3'})) self.assertEqual(1, len(products)) def test_update(self): """tests that we can use update to push data to the database""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] self.assertEqual('test project', product._data['name']) new_data = {'prefix':'tp', 'name':'updated', 'description':'nothing'} product._data.update(new_data) product.update() comp_product = list(Product.select(self.env, where={'prefix':'tp'}))[0] self.assertEqual('updated', comp_product._data['name']) def test_update_key_change(self): """tests that we raise an error for attempting to update key fields""" bad_data = {'prefix':'tp0', 'name':'update', 'description':'nothing'} product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product._data.update(bad_data) self.assertRaises(TracError, product.update) def test_insert(self): """test saving new Product""" data = {'prefix':'new', 'name':'new', 'description':'new'} product = Product(self.env) product._data.update(data) product.insert() check_products = list(Product.select(self.env, where={'prefix':'new'})) self.assertEqual(product._data['prefix'], check_products[0]._data['prefix']) self.assertEqual(1, len(check_products)) def test_insert_duplicate_key(self): """test attempted saving of Product with existing key fails""" dupe_key_data = {'prefix':'tp', 'name':'dupe', 'description':'dupe primary key'} product2 = Product(self.env) product2._data.update(dupe_key_data) self.assertRaises(TracError, product2.insert) def test_delete(self): """test that we are able to delete Products""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product.delete() post = list(Product.select(self.env, where={'prefix':'tp'})) self.assertEqual(0, len(post)) def test_delete_twice(self): """test that we error when deleting twice on the same key""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product.delete() self.assertRaises(TracError, product.delete) def test_field_data_get(self): """tests that we can use table.field syntax to get to the field data""" prefix = self.default_data['prefix'] name = self.default_data['name'] description = self.default_data['description'] product = list(Product.select(self.env, where={'prefix':prefix}))[0] self.assertEqual(prefix, product.prefix) self.assertEqual(name, product.name) self.assertEqual(description, product.description) def test_field_set(self): """tests that we can use table.field = something to set field data""" prefix = self.default_data['prefix'] product = list(Product.select(self.env, where={'prefix':prefix}))[0] new_description = 'test change of description' product.description = new_description self.assertEqual(new_description, product.description) def test_missing_unique_fields(self): """ensure that that insert method works when _meta does not specify unique fields when inserting more than one ProductResourceMap instances """ class TestModel(ModelBase): """A test model with no unique_fields""" _meta = {'table_name': 'bloodhound_testmodel', 'object_name': 'TestModelObject', 'key_fields': ['id',], 'non_key_fields': ['value'], 'unique_fields': [],} from trac.db import DatabaseManager schema = [TestModel._get_schema(), ] with self.env.db_transaction as db: db_connector, dummy = DatabaseManager(self.env)._get_connector() for table in schema: for statement in db_connector.to_sql(table): db(statement) structure = dict([(table.name, [col.name for col in table.columns]) for table in schema]) tm1 = TestModel(self.env) tm1._data.update({'id':1, 'value':'value1'}) tm1.insert() tm2 = TestModel(self.env) tm2._data.update({'id':2, 'value':'value2'}) tm2.insert() def test_change_listener_created(self): self.assertEqual('created', self.listener.action) self.assertIsInstance(self.listener.resource, Product) self.assertEqual(self.INITIAL_PREFIX, self.prefix) self.assertEqual(self.INITIAL_NAME, self.name) self.assertEqual(self.INITIAL_DESCRIPTION, self.description) def test_change_listener_changed(self): CHANGED_NAME = "changed name" self.product.name = CHANGED_NAME self.product.update() self.assertEqual('changed', self.listener.action) self.assertIsInstance(self.listener.resource, Product) self.assertEqual(CHANGED_NAME, self.name) self.assertEqual({"name":self.INITIAL_NAME}, self.listener.old_values) def test_change_listener_deleted(self): self.product.delete() self.assertEqual('deleted', self.listener.action) self.assertIsInstance(self.listener.resource, Product) self.assertEqual(self.INITIAL_PREFIX, self.prefix) def test_get_tickets(self): for pdata in ( {'prefix': 'p2', 'name':'product, too', 'description': ''}, {'prefix': 'p3', 'name':'strike three', 'description': ''}, ): num_tickets = 5 product = Product(self.global_env) product._data.update(pdata) product.insert() self.env = ProductEnvironment(self.global_env, product) for i in range(num_tickets): ticket = Ticket(self.env) ticket['summary'] = 'hello ticket #%s-%d' % (product.prefix, i) ticket['reporter'] = 'admin' tid = ticket.insert() # retrieve tickets using both global and product scope tickets_from_global = [(t['product'], t['id']) for t in Product.get_tickets(self.global_env, product.prefix)] self.assertEqual(len(tickets_from_global), num_tickets) tickets_from_product = [(t['product'], t['id']) for t in Product.get_tickets(self.env)] self.assertEqual(len(tickets_from_product), num_tickets) # both lists should contain same elements intersection = set(tickets_from_global) & set(tickets_from_product) self.assertEqual(len(intersection), num_tickets)
def resource_exists(self, resource): """Check whether product exists physically. """ products = Product.select(self.env, where={'name' : resource.id}) return bool(products)
def test_delete_twice(self): """test that we error when deleting twice on the same key""" product = list(Product.select(self.env, where={'prefix':'tp'}))[0] product.delete() self.assertRaises(TracError, product.delete)