def resource_created(self, resource, context): import trac.db_default from multiproduct.env import EnvironmentStub # Don't populate product database when running from within test # environment stub as test cases really don't expect that ... if isinstance(self.env, EnvironmentStub): return product = resource self.log.debug("Adding product info (%s) to tables:" % product.prefix) with self.env.db_direct_transaction as db: # create the default entries for this Product from defaults for table in trac.db_default.get_data(db): if not table[0] in self.PRODUCT_POPULATE_TABLES: continue self.log.debug(" -> %s" % table[0]) cols = table[1] + ('product', ) rows = [p + (product.prefix, ) for p in table[2]] db.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table[0], ','.join(cols), ','.join(['%s' for c in cols])), rows) # Import default pages in product wiki wikiadmin = WikiAdmin(ProductEnvironment(self.env, product.prefix)) pages = ('TitleIndex', 'RecentChanges', 'InterTrac', 'InterWiki') for page in pages: filename = resource_filename('trac.wiki', 'default-pages/' + page) wikiadmin.import_page(filename, page)
def _do_wiki_upgrade(self): """Move all wiki pages starting with Trac prefix to unbranded user guide pages. """ wiki_admin = WikiAdmin(self.env) pages = wiki_admin.get_wiki_list() for old_name in pages: if old_name.startswith('Trac'): new_name = wiki.new_name(old_name) if not new_name: continue if new_name in pages: printout( _( 'Ignoring %(page)s : ' 'The page %(new_page)s already exists', page=old_name, new_page=new_name)) continue try: wiki_admin._do_rename(old_name, new_name) except AdminCommandError, exc: printout( _('Error moving %(page)s : %(message)s', page=old_name, message=unicode(exc))) else: # On success, rename links in other pages self._do_wiki_rename_links(old_name, new_name) # On success, insert redirection page redirection = WikiPage(self.env, old_name) redirection.text = _('See [wiki:"%(name)s"].', name=new_name) comment = 'Bloodhound guide update' redirection.save('bloodhound', comment, '0.0.0.0')
def _do_wiki_upgrade(self): """Move all wiki pages starting with Trac prefix to unbranded user guide pages. """ wiki_admin = WikiAdmin(self.env) pages = wiki_admin.get_wiki_list() for old_name in pages: if old_name.startswith('Trac'): new_name = wiki.new_name(old_name) if not new_name: continue if new_name in pages: printout(_('Ignoring %(page)s : ' 'The page %(new_page)s already exists', page=old_name, new_page=new_name)) continue try: wiki_admin._do_rename(old_name, new_name) except AdminCommandError, exc: printout(_('Error moving %(page)s : %(message)s', page=old_name, message=unicode(exc))) else: # On success, rename links in other pages self._do_wiki_rename_links(old_name, new_name) # On success, insert redirection page redirection = WikiPage(self.env, old_name) redirection.text = _('See [wiki:"%(name)s"].', name=new_name) comment = 'Bloodhound guide update' redirection.save('bloodhound', comment, '0.0.0.0')
def new_name(name): new_name = wiki.new_name(name) if new_name != name: if not self._wiki_pages: wiki_admin = WikiAdmin(self.env) self._wiki_pages = wiki_admin.get_wiki_list() if new_name in self._wiki_pages: return new_name return name
def setUp(self): self.env = EnvironmentStub() self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-') self.tmpdir = os.path.join(self.env.path, 'tmp') os.mkdir(self.tmpdir) self.filename = os.path.join(self.tmpdir, 'file.txt') create_file(self.filename, self.page_text) self.admin = WikiAdmin(self.env) with self.env.db_transaction: for name, readonly in (('WritablePage', [0, 1, 0]), ('ReadOnlyPage', [1, 0, 1, 0, 1])): for r in readonly: page = WikiPage(self.env, name) page.text = '[wiki:%s@%d]' % (name, page.version + 1) page.readonly = r page.save('trac', '')
def setUp(self): self.env = EnvironmentStub() self.search_module = SearchModule(self.env) pages_dir = pkg_resources.resource_filename('trac.wiki', 'default-pages') for page_name in ('WikiStart', 'TracModWSGI'): page = os.path.join(pages_dir, page_name) WikiAdmin(self.env).import_page(page, page_name)
def create(self, project): """Create a Trac environment for the project This will NOT call trac-admin but interfaces with Trac's API directly. It performs the same steps as trac-admin""" if self.has_env(project): return True # When creating environments, you can supply a list # of (section, option, value) tuples to trac which serve as a default options = self.get_default_options(project) # If an (inherit, file, xy) option is present, trac will omit the default values if 'inherit_config' in self.component_config: options.append( ('inherit', 'file', self.component_config['inherit_config'])) try: # create environment env = Environment(self.get_env_path(project), create=True, options=options) # preload wiki pages wiki_pages = None if 'preload_wiki' in self.component_config: wiki_pages = self.component_config['preload_wiki'] else: try: wiki_pages = pkg_resources.resource_filename( 'trac.wiki', 'default-pages') except Exception as e: logger.exception( "Exception while trying to find wiki pages") wiki_pages = None if wiki_pages: try: WikiAdmin(env).load_pages(wiki_pages) except Exception as e: logger.exception("Unable to load wiki pages from %s", wiki_pages) else: logger.warning("Not wiki pages found for preloading") # all done return True except Exception as e: logger.exception( "Caught exception while creating Trac environment in " + self.get_env_path(project)) return False
def _create_env(self, path, dburi): env = Environment(path, True, [('trac', 'database', dburi), ('trac', 'base_url', 'http://localhost/'), ('project', 'name', u'Pŕójéćŧ Ńáḿé')]) dbm = DatabaseManager(env) dbm.set_database_version(21, 'initial_database_version') pages_dir = resource_filename('trac.wiki', 'default-pages') WikiAdmin(env).load_pages(pages_dir) att = Attachment(env, 'wiki', 'WikiStart') att.insert('filename.txt', io.BytesIO('test'), 4) env.shutdown()
def __init__(self, env): TracAdmin.__init__(self, env) self.env = open_environment(env) # new style, as of trac:changeset:7677 # see trac:#7770 # for now, support both use-cases if not hasattr(self, 'get_component_list'): # TODO: create these functions from trac.ticket.admin import ComponentAdminPanel from trac.ticket.admin import MilestoneAdminPanel from trac.wiki.admin import WikiAdmin self.ComponentAdminPanel = ComponentAdminPanel(self.env) self.get_component_list = self.ComponentAdminPanel.get_component_list self._do_component_remove = self.ComponentAdminPanel._do_remove self.MilestoneAdminPanel = MilestoneAdminPanel(self.env) self.get_milestone_list = self.MilestoneAdminPanel.get_milestone_list self._do_milestone_remove = self.MilestoneAdminPanel._do_remove self.WikiAdmin = WikiAdmin(self.env) self._do_wiki_load = self.WikiAdmin.load_pages
class WikiAdminTestCase(unittest.TestCase): page_text = 'Link to WikiStart' def setUp(self): self.env = EnvironmentStub() self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-') self.tmpdir = os.path.join(self.env.path, 'tmp') os.mkdir(self.tmpdir) self.filename = os.path.join(self.tmpdir, 'file.txt') create_file(self.filename, self.page_text) self.admin = WikiAdmin(self.env) with self.env.db_transaction: for name, readonly in (('WritablePage', [0, 1, 0]), ('ReadOnlyPage', [1, 0, 1, 0, 1])): for r in readonly: page = WikiPage(self.env, name) page.text = '[wiki:%s@%d]' % (name, page.version + 1) page.readonly = r page.save('trac', '') def tearDown(self): self.env.reset_db_and_disk() def _import_page(self, *args, **kwargs): with open(os.devnull, 'wb') as devnull: stdout = sys.stdout try: sys.stdout = devnull self.admin.import_page(*args, **kwargs) finally: sys.stdout = stdout def test_import_page_new(self): self._import_page(self.filename, 'NewPage') page = WikiPage(self.env, 'NewPage') self.assertEqual('NewPage', page.name) self.assertEqual(1, page.version) self.assertEqual(self.page_text, page.text) self.assertEqual(0, page.readonly) def test_import_page_readonly(self): page = WikiPage(self.env, 'ReadOnlyPage') self.assertEqual(5, page.version) self.assertEqual(1, page.readonly) self.assertNotEqual(self.page_text, page.text) self._import_page(self.filename, 'ReadOnlyPage') page = WikiPage(self.env, 'ReadOnlyPage') self.assertEqual(6, page.version) self.assertEqual(1, page.readonly) self.assertEqual(self.page_text, page.text) def test_import_page_not_readonly(self): page = WikiPage(self.env, 'WritablePage') self.assertEqual(3, page.version) self.assertEqual(0, page.readonly) self.assertNotEqual(self.page_text, page.text) self._import_page(self.filename, 'WritablePage') page = WikiPage(self.env, 'WritablePage') self.assertEqual(4, page.version) self.assertEqual(0, page.readonly) self.assertEqual(self.page_text, page.text) def test_import_page_uptodate(self): page = WikiPage(self.env, 'WritablePage') self.assertEqual(3, page.version) self.assertEqual(0, page.readonly) create_file(self.filename, page.text) page_text = page.text self._import_page(self.filename, 'WritablePage') page = WikiPage(self.env, 'WritablePage') self.assertEqual(3, page.version) self.assertEqual(0, page.readonly) self.assertEqual(page_text, page.text)
def do_initenv(self, line): def initenv_error(msg): printerr(_("Initenv for '%(env)s' failed.", env=self.envname), "\n" + msg) if self.env_check(): initenv_error(_("Does an environment already exist?")) return 2 if os.path.exists(self.envname) and os.listdir(self.envname): initenv_error(_("Directory exists and is not empty.")) return 2 if not os.path.exists(os.path.dirname(self.envname)): initenv_error(_("Base directory '%(env)s' does not exist. Please " "create it manually and retry.", env=os.path.dirname(self.envname))) return 2 arg = self.arg_tokenize(line) inherit_paths = [] i = 0 while i < len(arg): item = arg[i] if item.startswith('--inherit='): inherit_paths.append(arg.pop(i)[10:]) else: i += 1 arg = arg or [''] # Reset to usual empty in case we popped the only one project_name = None db_str = None repository_type = None repository_dir = None if len(arg) == 1 and not arg[0]: project_name, db_str = self.get_initenv_args() elif len(arg) == 2: project_name, db_str = arg elif len(arg) == 4: project_name, db_str, repository_type, repository_dir = arg else: initenv_error('Wrong number of arguments: %d' % len(arg)) return 2 try: printout(_("Creating and Initializing Project")) options = [ ('project', 'name', project_name), ('trac', 'database', db_str), ] def add_nav_order_options(section, default): for i, name in enumerate(default, 1): options.append((section, name + '.order', float(i))) add_nav_order_options('mainnav', default_mainnav_order) add_nav_order_options('metanav', default_metanav_order) if repository_dir: options.extend([ ('repositories', '.type', repository_type), ('repositories', '.dir', repository_dir), ]) if inherit_paths: options.append(('inherit', 'file', ",\n ".join(inherit_paths))) try: self.__env = Environment(self.envname, create=True, options=options) except Exception as e: initenv_error(_('Failed to create environment.')) printerr(e) traceback.print_exc() sys.exit(1) # Add a few default wiki pages printout(_(" Installing default wiki pages")) pages_dir = pkg_resources.resource_filename('trac.wiki', 'default-pages') WikiAdmin(self.__env).load_pages(pages_dir) if repository_dir: try: repos = RepositoryManager(self.__env).get_repository('') if repos: printout(_(" Indexing default repository")) repos.sync(self._resync_feedback) except TracError as e: printerr(_(""" --------------------------------------------------------------------- Warning: couldn't index the default repository. This can happen for a variety of reasons: wrong repository type, no appropriate third party library for this repository type, no actual repository at the specified repository path... You can nevertheless start using your Trac environment, but you'll need to check again your trac.ini file and the [trac] repository_type and repository_path settings. """)) except Exception as e: initenv_error(to_unicode(e)) traceback.print_exc() return 2 printout(_(""" --------------------------------------------------------------------- Project environment for '%(project_name)s' created. You may now configure the environment by editing the file: %(config_path)s If you'd like to take this new project environment for a test drive, try running the Trac standalone web server `tracd`: tracd --port 8000 %(project_path)s Then point your browser to http://localhost:8000/%(project_dir)s. There you can also browse the documentation for your installed version of Trac, including information on further setup (such as deploying Trac to a real web server). The latest documentation can also always be found on the project website: http://trac.edgewall.org/ Congratulations! """, project_name=project_name, project_path=self.envname, project_dir=os.path.basename(self.envname), config_path=os.path.join(self.envname, 'conf', 'trac.ini')))
def install(self): """Installer""" # Utility function to interpreted boolean option value getBool = lambda s: s.strip().lower() in ['true', 'yes'] # Utility function to parse a multi-line/multi-value parameter def cleanMultiParams(v): params = [ s.split('|') for s in [l.strip() for l in v.split('\n')] if len(s) > 0 ] cleaned_params = [] for line in params: cleaned_params.append([row.strip() for row in line]) return cleaned_params # Utility function to transform any string to an ID getId = lambda s: ''.join([c for c in s if c.isalnum()]).lower() options = self.options # Add command line scripts trac-admin and tracd into bin entry_points = [('trac-admin', 'trac.admin.console', 'run'), ('tracd', 'trac.web.standalone', 'main')] zc.buildout.easy_install.scripts(entry_points, pkg_resources.working_set, options['executable'], options['bin-directory']) #################### # Init Trac instance #################### # Generate the trac instance, if required location = options['location'] project_name = options.get('project-name', 'My project') project_url = options.get('project-url', 'http://example.com') db = 'sqlite:%s' % os.path.join('db', 'trac.db') if not os.path.exists(location): os.mkdir(location) trac = TracAdmin(location) if not trac.env_check(): trac.do_initenv('"%s" %s' % (project_name, db)) env = trac.env # Remove Trac default example data clean_up = getBool(options.get('remove-examples', 'True')) if clean_up: # Remove default milestones for mil in Milestone.select(env): if mil.name in [ 'milestone1', 'milestone2', 'milestone3', 'milestone4' ]: mil.delete() # Remove default components for comp in Component.select(env): if comp.name in ['component1', 'component2']: comp.delete() # Add custom milestones for mil_data in cleanMultiParams(options.get('milestones', '')): mil_name = mil_data[0] try: mil = Milestone(env, name=mil_name) except ResourceNotFound: mil = Milestone(env) mil.name = mil_name mil.insert() # Add custom components for comp_data in cleanMultiParams(options.get('components', '')): comp_name = comp_data[0] try: comp = Component(env, name=comp_name) except ResourceNotFound: comp = Component(env) comp.name = comp_name if len(comp_data) == 2 and comp_data[1] not in [None, '']: comp.owner = comp_data[1] comp.insert() ####################### # Generate the trac.ini ####################### # Read the trac.ini config file trac_ini = os.path.join(location, 'conf', 'trac.ini') parser = ConfigParser.ConfigParser() parser.read([trac_ini]) # Clean-up trac.ini: add missing stuff if 'components' not in parser.sections(): parser.add_section('components') # Force upgrade of informations used during initialization parser.set('project', 'name', project_name) # Set all repositories repos = cleanMultiParams(options.get('repos', None)) repo_names = [getId(r[0]) for r in repos] repo_types = {}.fromkeys([r[1].lower() for r in repos]).keys() if 'repositories' not in parser.sections(): parser.add_section('repositories') for repo in repos: repo_name = getId(repo[0]) repo_type = repo[1] repo_dir = repo[2] repo_url = repo[3] parser.set('repositories', '%s.type' % repo_name, repo_type) parser.set('repositories', '%s.dir' % repo_name, repo_dir) if repo_url not in ['', None]: parser.set('repositories', '%s.url' % repo_name, repo_url) # Set default repository default_repo = getId(options.get('default-repo', None)) if default_repo and default_repo in repo_names: parser.set('repositories', '.alias', default_repo) parser.set('repositories', '.hidden', 'true') # Set repository sync method sync_method = options.get('repos-sync', 'request').strip().lower() svn_repos = [getId(r[0]) for r in repos if r[1] == 'svn'] if sync_method == 'request': parser.set('trac', 'repository_sync_per_request', ', '.join(svn_repos)) # TODO # elif sync_method == 'hook': # do stuff... # Set project description project_descr = options.get('project-description', None) if project_descr: parser.set('project', 'descr', project_descr) parser.set('header_logo', 'alt', project_descr) # Setup logo header_logo = options.get('header-logo', '') header_logo = os.path.realpath(header_logo) if os.path.exists(header_logo): shutil.copyfile(header_logo, os.path.join(location, 'htdocs', 'logo')) parser.set('header_logo', 'src', 'site/logo') parser.set('header_logo', 'link', project_url) # Set footer message parser.set( 'project', 'footer', options.get( 'footer-message', 'This Trac instance was generated by <a href="http://pypi.python.org/pypi/pbp.recipe.trac">pbp.recipe.trac</a>.' )) # SMTP parameters for name in ('always-bcc', 'always-cc', 'default-domain', 'enabled', 'from', 'from-name', 'password', 'port', 'replyto', 'server', 'subject-prefix', 'user'): param_name = "smtp-%s" % name default_value = None if param_name == "smtp-from-name": default_value = project_name value = options.get(param_name, default_value) if value is not None: parser.set('notification', param_name.replace('-', '_'), value) ############### # Plugins setup ############### # If one repository use Mercurial, hook its plugin if 'hg' in repo_types: parser.set('components', 'tracext.hg.*', 'enabled') # Configure the NavAdd plugin menu_items = cleanMultiParams(options.get('additional-menu-items', '')) item_list = [] for item in menu_items: item_title = item[0] item_url = item[1] item_id = getId(item_title) item_list.append((item_id, item_title, item_url)) if item_list > 0: parser.set('components', 'navadd.*', 'enabled') if 'navadd' not in parser.sections(): parser.add_section('navadd') parser.set('navadd', 'add_items', ','.join([i[0] for i in item_list])) for (uid, title, url) in item_list: parser.set('navadd', '%s.target' % uid, 'mainnav') parser.set('navadd', '%s.title' % uid, title) parser.set('navadd', '%s.url' % uid, url) # Enable and setup time tracking time_tracking = options.get('time-tracking-plugin', 'disabled').strip().lower() == 'enabled' if time_tracking: parser.set('components', 'timingandestimationplugin.*', 'enabled') # Enable and setup the stat plugin stats = options.get('stats-plugin', 'disabled').strip().lower() == 'enabled' if stats: parser.set('components', 'tracstats.*', 'enabled') ####################### # Final upgrades & sync ####################### # Apply custom parameters defined by the user custom_params = cleanMultiParams(options.get('trac-ini-additional', '')) for param in custom_params: if len(param) == 3: section = param[0] if section not in parser.sections(): parser.add_section(section) parser.set(section, param[1], param[2]) # Write the final trac.ini parser.write(open(trac_ini, 'w')) # Reload the environment env.shutdown() trac = TracAdmin(location) env = trac.env # Set custom permissions perm_sys = PermissionSystem(env) for cperm in cleanMultiParams(options.get('permissions', '')): if len(cperm) == 2: user = cperm[0] current_user_perms = perm_sys.get_user_permissions(user) perm_list = [p.upper() for p in cperm[1].split(' ') if len(p)] for perm in perm_list: if perm not in current_user_perms: perm_sys.grant_permission(user, perm) # Upgrade Trac instance to keep it fresh needs_upgrade = env.needs_upgrade() force_upgrade = getBool(options.get('force-instance-upgrade', 'False')) if needs_upgrade or force_upgrade: env.upgrade(backup=True) # Force repository resync repo_resync = getBool(options.get('force-repos-resync', 'False')) if repo_resync: rm = RepositoryManager(env) repositories = rm.get_real_repositories() for repos in sorted(repositories, key=lambda r: r.reponame): repos.sync(clean=True) # Upgrade default wiki pages embedded in Trac instance wiki_upgrade = getBool(options.get('wiki-doc-upgrade', 'False')) if wiki_upgrade: # Got the command below from trac/admin/console.py pages_dir = pkg_resources.resource_filename( 'trac.wiki', 'default-pages') WikiAdmin(env).load_pages(pages_dir, ignore=['WikiStart', 'checkwiki.py'], create_only=['InterMapTxt']) # Return files that were created by the recipe. The buildout # will remove all returned files upon reinstall. return tuple()