#!/usr/bin/env python """ site2template This script will convert an iWeb site into an iWeb template and install it. The usage is fairly simple. Just create a site in iWeb with the name that you want the template to have. The site should should have each of the standard pages (i.e. About Me, Welcome, Photos, etc.). Once you have a site that looks the way you want, open the Terminal application and run: site2template site-name where site-name is the name of the site you want to convert to a template. site2template will generate all of the webtemplate files and install them in the iWeb application. It will also resize the thumnail images and install them in the proper location. The location of the thumbnail files can be specified using the --thumbs=DIR option where DIR is the name of the directory where the images are located. The images should be in TIFF format and have a name that is the name of the type of page (i.e. About Me, Welcome, etc.) with the file extension '.tiff'. So the image names would be 'About Me.tiff', 'Welcome.tiff', etc. You can obtain these images by using the Preview application and taking screenshots of your site pages. Each thumbnail will be resized automatically. This script comes with no warranty or guarantees. Use at your own risk. """ TEMPLATE_NAMES = ['About Me', 'Welcome', 'Photos', 'Movie', 'Blog', 'Podcast'] INPUT_FILE = '~/Library/Application Support/iWeb/Domain.sites/index.xml.gz' IWEB_TEMPLATES = '/Applications/iWeb.app/Contents/Resources/English.lproj/Templates' import os, sys, gzip, re, shutil import CoreGraphics as CG INPUT_FILE = os.path.expanduser(INPUT_FILE) IWEB_TEMPLATES = os.path.expanduser(IWEB_TEMPLATES) class DomainSites(object): def __init__(self, sitename, thumbs=None): self.name = sitename self.thumbs = None if thumbs: self.thumbs = os.path.expanduser(thumbs) # Read XML of domain self.sitesxml = gzip.open(INPUT_FILE, 'r').read() # Get metadata for domain self.metadata = re.compile(r'(<\w+:metadata>.+?)', re.S).search(self.sitesxml).group(1) # Store dictionary of all unfiltered images self.unfiltered = {} for name, data in re.compile(r'<\w+:unfiltered\s+sfa:ID="([^"]+)"[^>]*>(.+?)', re.S).findall(self.sitesxml): self.unfiltered[name] = data def getSites(self): """ Get XML for all sites """ return re.compile(r'(<\w+:site [^>]*?>.+?)', re.S).findall(self.sitesxml) def getSite(self, name): """ Get requested site XML Required Arguments: name -- name of the site to retrieve Returns: XML of the requested site """ for site in self.getSites(): n = re.search(r'\w+:name="([^"]+)"', site).group(1).strip() if n == name: return site raise ValueError, 'There is no site with the name "%s"' % name def getSiteNodes(self, name): """ Get XML nodes for requested site Required Arguments: name -- name of the site to retrieve the nodes of Returns: dictionary containing site nodes """ xml = self.getSite(name) # Get XML for all site nodes nodesxml = re.compile(r'<\w+:site-nodes[^>]*?>(.+?)', re.S).search(xml).group(1).strip() # Put site nodes into an array sitenodes = [x[0] for x in re.compile(r'(<\w+:site-(page|blog)[^>]*?>.+?)\s*', re.S).findall(nodesxml)] # Put site pages into a dictionary templates = {} for node in sitenodes: name = re.search(r'\w+:name="([^"]+)"', node).group(1).strip() templates[name] = node return templates def generateWebTemplates(self, name): """ Generate '.webtemplate' files for site `name` Required Arguments: name -- name of site to convert to a set of templates """ # Get document wrapper root = re.compile(r'(<\w+:document\b[^>]*?>)', re.S).search(self.sitesxml).group(1) root = root.replace(':document',':template') # Print out the templates to iWeb resources for type, template in self.getSiteNodes(name).items(): if TEMPLATE_NAMES and type not in TEMPLATE_NAMES: continue os.chdir(IWEB_TEMPLATES) try: os.mkdir(type) except: pass os.chdir(type) filename = '%s %s.webtemplate' % (name, type) try: os.mkdir(filename) except: pass os.chdir(filename) self.unfilterednum = 0 def dounfiltered(x): self.unfilterednum += 1 return '<%s:unfiltered %s:ID="%s-%s">%s' % \ (x.group(1), x.group(2), x.group(3), self.unfilterednum, self.unfiltered[x.group(3)], x.group(1)) # Replace image references with actual image data template = re.sub(r'<(\w+):unfiltered-ref\s+(\w+):IDREF="([^"]+)"\s*/>', dounfiltered, template) fp = gzip.open('index.xml.gz', 'w') fp.write(root) fp.write('') fp.write(self.metadata) fp.write('') fp.write(template) fp.write('') fp.close() # Copy image and other files into web template directory for src in re.findall(r'\w+:path="([^"]+)"', template): dst = src src = os.path.join(os.path.dirname(INPUT_FILE), src) if os.path.isfile(src): shutil.copyfile(src, dst) # If there is a thumbnail image, copy it into place if self.thumbs: if not os.path.isdir('thumbs'): os.mkdir('thumbs') thumbnail = os.path.join(self.thumbs, type + '.tiff') if os.path.isfile(thumbnail): self.createThumbnail(thumbnail, os.path.join('thumbs','page_thumb_1-1.tiff')) os.chdir('..') def createThumbnail(self, inputfile, outputfile, width=98, height=128): """ Resize the image """ # Load image img = CG.CGImageImport(CG.CGDataProviderCreateWithFilename(inputfile)) # Get new dimensions aspect = img.getWidth() / float(img.getHeight()) h = height w = h * aspect if w > width: w = width h = w / aspect w = int(w) h = int(h) # Create new imoge c = CG.CGBitmapContextCreateWithColor(w, h, CG.CGColorSpaceCreateDeviceRGB(), (0, 0, 0, 0)) c.setRGBFillColor(1.0, 1.0, 1.0, 1.0) pageRect = CG.CGRectMake(0, 0, w, h) c.drawImage(pageRect.inset(0, 0), img) # Write image c.writeToFile(outputfile, CG.kCGImageFormatTIFF) def uninstallWebTemplates(self, name): """ Remove .webtemplate references from plist file for given site Required Arguments: name -- name of site to uninstall """ tfile = os.path.join(IWEB_TEMPLATES, 'TemplatesInfo.plist') # Read template info plist templatesinfo = open(tfile, 'r').read() # Rip out previous templates with this name templatesinfo = re.compile(r'%s\s*.+?\s*' % name, re.S).sub(r'', templatesinfo) # Pull site name out of sorted templates templatesinfo = re.sub(r'\s*displayName\s*%s\s*keyName\s*%s\s*\s*' % (name, name), r'', templatesinfo) # Write out the plist file open(tfile, 'w').write(templatesinfo) def installWebTemplates(self, name): """ Add .webtemplates to the plist file for the given site Required Arguments: name -- name of site to install """ # Uninstall previous template self.uninstallWebTemplates(name) tfile = os.path.join(IWEB_TEMPLATES, 'TemplatesInfo.plist') # Read template info plist templatesinfo = open(tfile, 'r').read() templateentry = '%(name)s\n\n\tdisplayName\n\t%(type)s\n\tfileName\n\t%(filename)s\n' # Add each template to the plist file for type, template in self.getSiteNodes(name).items(): if TEMPLATE_NAMES and type not in TEMPLATE_NAMES: continue filename = '%s %s.webtemplate' % (name, type) # Update plist info templatesinfo = re.sub(r'(%s\s*BLEntries\s*\s*)' % type, r'\1%s' % (templateentry % {'name':name, 'type':type, 'filename':filename}), templatesinfo) # Add theme to sorted themes list sortedtheme = '\n\tdisplayName\n\t%s\n\tkeyName\n\t%s\n' % (name, name) templatesinfo = re.sub(r'(sortedThemes\s*)', r'\1%s' % sortedtheme, templatesinfo) # Write out the plist file open(tfile, 'w').write(templatesinfo) def main(): from optparse import OptionParser parser = OptionParser('usage: site2template [ options ] site-name') parser.add_option("-t", "--thumbs", dest="thumbs", metavar="DIR", default='', help="directory where thumbnail images reside") parser.add_option("-u", "--uninstall", dest="uninstall", action="store_true", help="uninstall template") (options, args) = parser.parse_args() if not args: parser.print_help() sys.exit(1) ds = DomainSites(sys.argv[1], os.path.abspath(options.thumbs)) if options.uninstall: ds.uninstallWebTemplates(args[0]) else: ds.generateWebTemplates(args[0]) ds.installWebTemplates(args[0]) if __name__ == '__main__': main()