# vim: expandtab
from StringIO import StringIO
from string import Template
from trac.util import TracError
from trac.wiki import wiki_to_html
from trac.wiki import WikiPage
import trac.perm
import re, string, svn, os, time
import fcntl
SVN_URL = 'http://trac-hacks.org/svn/'
# Production configuration
SVN_LOCAL_PATH = 'file:///srv/trac-hacks/svn/'
SVN_PERMISSIONS = '/srv/trac-hacks/permissions'
BASE_URL = "/"
# Test configuration
#SVN_LOCAL_PATH = 'file:///home/athomas/projects/trac/env/svn/'
#SVN_PERMISSIONS = '/home/athomas/projects/trac/env/permissions'
#BASE_URL = "/trachacks/"
tag_cache = {}
def fetch_page(cursor, page):
cursor.execute("SELECT text FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1", (page,))
text = cursor.fetchone()
if not text:
raise TracError("No such template page %s" % (page, BASE_URL, page))
return text[0]
def expand_vars(text, vars):
s = Template(text)
return s.substitute(vars)
def get_branch_values(hdf, branch):
out = []
o = hdf.getObj(branch)
if not o: return out
if o.value():
out.append(o.value())
else:
o = o.child()
while o:
out.append(o.value())
o = o.next()
return out
def generate_vars(hdf):
vars = {}
vars['OWNER'] = hdf.getValue('trac.authname', 'anonymous')
vars['WIKINAME'] = hdf.getValue('args.name', 'NoPage')
if not vars['WIKINAME'].lower().endswith(hdf.getValue('args.type', '')):
vars['WIKINAME'] += hdf.getValue('args.type', '').title()
vars['TITLE'] = hdf.getValue('args.title', ' '.join(re.findall('[A-Z][a-z]+', vars['WIKINAME'])))
vars['LCNAME'] = vars['WIKINAME'].lower()
vars['TYPE'] = hdf.getValue('args.type', '')
vars['SOURCEURL'] = SVN_URL + vars['LCNAME']
vars['DESCRIPTION'] = hdf.getValue('args.description', 'No description available')
vars['EXAMPLE'] = hdf.getValue('args.example', 'No example available')
return vars
def execute(hdf, template, env):
out = StringIO()
errors = []
authname = hdf.getValue("trac.authname", "anonymous")
if not template:
raise TracError("No template page supplied")
if authname == "anonymous":
errors.append('You need to register then login in order to create a new hack.' % (hdf.getValue("trac.href.registration", ""), hdf.getValue("trac.href.login", "")))
db = env.get_db_cnx()
cursor = db.cursor()
# Fetch meta-data from tags
META_TAGS = set()
from tractags.api import TagEngine
wikitags = TagEngine(env).tagspace.wiki
for tag in wikitags.get_tagged_names(['metatag']):
META_TAGS.update(wikitags.get_tagged_names([tag]))
TYPES = wikitags.get_tagged_names(['type'])
RELEASES = wikitags.get_tagged_names(['release'])
page_name = hdf.getValue('args.name', '')
if not page_name.lower().endswith(hdf.getValue('args.type', '')):
page_name += hdf.getValue('args.type', '').title()
page_title = hdf.getValue('args.title', '')
page_description = hdf.getValue('args.description', '')
page_example = hdf.getValue('args.example', '')
page_type = hdf.getValue('args.type', 'plugin')
page_tags = get_branch_values(hdf, 'args.tags')
page_releases = get_branch_values(hdf, 'args.releases')
page_preview = hdf.getValue('args.previewhack', '')
page_create = hdf.getValue('args.createhack', '')
def write_tags(out, tags, checked = (), name = "tags", type="checkbox"):
count = 0
for tag in sorted(tags):
if tag.startswith('tags/'):
continue
(linktext,title,desc) = getInfo(db,tag)
link = env.href.wiki(tag)
check = ""
if tag in checked:
check = " checked"
out.write(' %s \n' % (type, name, tag, check, link, title, tag))
count += 1
if count % 8 == 0:
out.write("
\n")
return count
# Validation
if page_preview or page_create:
try:
fetch_page(cursor, page_name)
except:
pass
else:
errors.append("Page name %s already exists" % page_name)
if not re.match('^([A-Z][a-z]+){2,}$', page_name): errors.append('Invalid WikiName, only alpha characters are accepted and must be CamelCase')
if not page_name: errors.append("No WikiName provided")
if not page_title: errors.append("No page title provided")
if not page_type: errors.append('No page type selected')
if not page_description: errors.append("No description provided")
if not page_example: errors.append("No example provided")
if not page_releases: errors.append("No releases selected")
if page_create and not errors:
import subprocess
repos_dir = env.config.get('trac', 'repository_dir')
cursor.execute("SELECT name FROM component WHERE name=%s", (page_name,))
row = cursor.fetchone()
if row:
errors.append("Component '%s' already exists" % page_name)
if subprocess.call(["svn", "ls", "%s/%s" % (SVN_LOCAL_PATH, page_name.lower())]) == 0:
errors.append("Repository path '%s' already exists" % page_name.lower())
if not os.access(SVN_PERMISSIONS, os.W_OK):
errors.append("Can't write to Subversion permissions file")
lockfile = open("/var/tmp/newhack.lock", "w")
try:
rv = fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
if rv:
errors.append('Failed to acquire lock, received error code %i' % rv)
except IOError:
errors.append('A hack is currently being created by another user. Try again later.')
if not errors:
try:
# Insert component
cursor.execute('INSERT INTO component (name, owner) VALUES (%s, %s)', (page_name, authname))
# Create page
page = WikiPage(env, page_name, db = db)
page.text = expand_vars(fetch_page(cursor, template), generate_vars(hdf))
out.write('Created wiki page.
\n')
# Creating SVN paths
paths = ['%s%s' % (SVN_LOCAL_PATH, page_name.lower())]
for release in page_releases:
paths.append("%s%s/%s" % (SVN_LOCAL_PATH, page_name.lower(), release))
output = os.popen('/usr/bin/op create-hack %s "New hack %s, created by %s" %s 2>&1' % (authname, page_name, authname, ' '.join(paths))).readlines()
if output:
raise Exception("Failed to create Subversion paths:\n%s" % ''.join(output))
out.write("Created SVN layout.
\n")
# Add SVN permissions
perms = open(SVN_PERMISSIONS, "a")
perms.write("\n[/%s]\n%s = rw\n" % (page_name.lower(), authname))
out.write('Added SVN write permission.
\n')
out.write('\nFinished.