12 import pygraphviz as pgv
14 from amara import bindery
15 from amara.xslt import transform
16 from Cheetah.Template import Template
18 parser = argparse.ArgumentParser(description='Process docbook article tree.')
19 parser.add_argument('--style', nargs='?',
20 default=os.path.dirname(os.getcwd())+'/style/default/')
21 parser.add_argument('--output', nargs='?',
22 default=os.path.dirname(os.getcwd())+'/htdocs/')
23 args = parser.parse_args()
25 style_xslt = args.style+"docbook.xsl"
26 style_tmpl = args.style+"index.en.html.tmpl"
27 outputdir = args.output
29 tmptarget = tempfile.mkdtemp()+'/'
31 valid_scripts = ['.py','.pl']
37 except OSError as exc: # Python >2.5
38 if exc.errno == errno.EEXIST:
42 def publish(src,target):
43 cmd = ["rsync","-a","--delete",src,target]
44 retcode = subprocess.call(cmd)
46 print 'Error: '+' '.join(cmd)+' Returncode ['+str(retcode)+']'
49 PREFIXES={u'db': u'http://docbook.org/ns/docbook',
50 u'xi': u'http://www.w3.org/2001/XInclude',
51 u'xl': u'http://www.w3.org/1999/xlink'}
54 """Class containing the state of the directory with articles"""
60 for dirname, dirnames, filenames in os.walk(self._cwd):
61 for filename in filenames:
62 if fnmatch.fnmatch(filename, '*.xml'):
63 file_ = os.path.join(dirname,filename)
64 doc = bindery.parse(file_, prefixes=PREFIXES)
65 title = doc.xml_select(u'/db:article/db:info/db:title')
66 menu = doc.xml_select(u'/db:article/db:info/db:titleabbrev')
68 base = file_.split('.')[1]
69 link = base.replace('index','')
70 self._tree.append(link)
73 return set(self._tree)
76 """Class representing a version of a webpage"""
77 def __init__(self,page):
84 self._rendered_article = None
92 def set_article(self,art):
93 self._rendered_article = art
96 self._doc = bindery.parse(self._file, prefixes=PREFIXES)
97 if self._doc.xml_select(u'/db:article/db:info/db:title'):
98 self._title = unicode(self._doc.article.info.title)
99 if self._doc.xml_select(u'/db:article/db:info/db:titleabbrev'):
100 self._menu = unicode(self._doc.article.info.titleabbrev)
102 dirname = os.path.dirname(self._file)
103 code = self._doc.xml_select(u"//xi:include[@parse='text']")
106 (p, ext) = os.path.splitext(c.href)
107 if ext in valid_scripts:
108 exe = os.path.join(os.path.abspath(dirname+c.href))
109 xml = subprocess.Popen([exe],stdout=subprocess.PIPE)
110 xstr = bindery.parse(str(xml.stdout.read()))
111 idp = c.xml_index_on_parent
112 for x in xstr.xml_children:
113 c.xml_parent.xml_insert(idp,x)
114 c.xml_parent.xml_remove(c)
116 for r in self._doc.xml_select(u"//db:link[@xl:href]"):
117 rf = os.path.join(dirname,r.href)
118 if os.path.isfile(rf):
119 self._resources.append(rf)
120 for i in self._doc.xml_select(u"//db:imagedata[@fileref]"):
121 im = os.path.join(dirname,i.fileref)
122 if os.path.isfile(im):
123 self._resources.append(im)
126 # amara can not handle the docbook stylesheets
127 # xmlarticle = transform(doc,style_xslt)
129 dirname = os.path.dirname(self._file)
131 infile = os.path.basename(tempfile.mktemp())
132 outfile = tempfile.mktemp()
133 tfi = open(infile,'w')
134 tfi.write(self._doc.xml_encode())
136 # cmd = ["saxon-xslt-xinclude","-o",outfile,infile,style_xslt]
137 cmd = ["xsltproc","--xinclude","--output",outfile,style_xslt,infile]
138 retcode = subprocess.call(cmd)
140 print 'Error: '+' '.join(cmd)+' Returncode ['+str(retcode)+']'
141 tfo = open(outfile,'r')
142 self._rendered_article = tfo.read()
148 def template(self,sitemap):
149 htmlmenu = sitemap.gen_menu(self._lang,None,None)
150 levelmenu = sitemap.gen_menu(self._lang,self,"tree")
151 template = Template(file=style_tmpl,
152 searchList=[{'title':self._title},
154 {'article':self._rendered_article},
155 {'levelmenu':levelmenu},
156 {'levelname':'Menu'}])
157 outfile = tmptarget+'html'.join(self._file.rsplit('xml',1))
158 mkdir_p(os.path.dirname(outfile))
159 out = open(outfile, 'w')
160 out.write(str(template))
165 """Class representing a webpage on the site"""
166 def __init__(self,link):
168 # find the representations of the link.
171 if self._link[-1] == '/':
173 lang = self._scan_languages(path)
175 self._pages.append(Page(l))
177 def _scan_languages(self,path):
179 for l in glob.glob('.'+path+'*'):
181 if len(ls) > 3 and ls[3] == 'xml':
182 lang.append((ls[2],l))
189 for page in self._pages:
194 for page in self._pages:
195 p.append(page.language())
199 for page in self._pages:
202 def template(self,sitemap):
203 for page in self._pages:
204 page.template(sitemap)
207 for page in self._pages:
208 if page.language()==lang:
212 def __init__(self,token,value):
224 return self._children
231 return self.inorder(self._root)
236 for x in self.inorder(l.children()):
239 def _add(self,trie, key, content):
243 node = Node(k,content)
248 self._add(ch.children(), key, content)
250 def add(self,key, content):
251 self._add(self._root, key, content)
253 def _graph(self, trie, G):
255 G.add_node(l.token())
256 for ch in l.children():
257 G.add_edge(l.token(),ch.token())
258 self._graph(l.children(), G)
261 G = pgv.AGraph(directed=True)
262 G.add_node("sitemap")
263 for ch in self._root:
264 G.add_edge("sitemap",ch.token())
265 self._graph(self._root, G)
270 def _menu(self, trie, lang, page, css):
271 html = "<ul%s>\n" % css
274 if l.value().page(lang) == page:
275 sel = ' class="selected"'
276 html += '<li%s><a href="%s">%s</a>\n' \
277 % (sel,l.value().link(),l.value().page(lang).menu())
278 html += self._menu(l.children(), lang, page, "")
282 def menu(self,lang,page,cssclass):
285 css = ' class="'+cssclass+'"'
286 return self._menu(self._root, lang, page, css)
289 """Class keeping the internal site structure"""
291 self._file = 'sitemap.txt'
294 def add_link(self, link):
295 tokens = filter(None,re.split(r'(^/\w*/|\w*/)',link))
296 self._tree.add(tokens,Link(link))
299 f = open(self._file,'w')
300 f.write('\n'.join(link.link() for link in self._tree))
306 sml = f.read().split()
310 except IOError, what_error:
311 print 'INFO: Could not read sitemap.txt - one will be created'
314 return set(link.link() for link in self._tree)
318 for link in self._tree:
321 print "Prepare [%5.2f s]" % (round(t2-t1,2))
323 for link in self._tree:
324 sitelang = sitelang.union(set(link.languages()))
326 print "Language [%5.2f s]" % (round(t3-t2,2))
327 for link in self._tree:
330 print "Render [%5.2f s]" % (round(t4-t3,2))
331 for link in self._tree:
334 print "Template [%5.2f s]" % (round(t5-t4,2))
337 sm[l] = Page((l,'/sitemap'))
338 sm[l].set_article(self.gen_menu(l,None,"tree sitemap"))
341 print "Sitemap [%5.2f s]" % (round(t6-t5,2))
346 def gen_menu(self,lang,page,cssclass):
347 return self._tree.menu(lang,page,cssclass)
350 publish(tmptarget, args.output)
351 publish(args.style+"css", args.output)
352 publish(args.style+"images",args.output)
354 def generateSitemap():
357 sfile = open('sitemap.txt')
358 flist = sfile.read().split()
361 sitemap.append(dict(link=f))
362 except IOError, what_error:
363 print 'Sitemap missing - generating one.'
365 for dirname, dirnames, filenames in os.walk('.'):
366 for filename in filenames:
367 if fnmatch.fnmatch(filename, '*.xml'):
368 xfile = os.path.join(dirname,filename)
369 doc = bindery.parse(xfile,
370 prefixes={u'db': u'http://docbook.org/ns/docbook',
371 u'xi': u'http://www.w3.org/2001/XInclude',
372 u'xl': u'http://www.w3.org/1999/xlink'})
373 title = doc.xml_select(u'/db:article/db:info/db:title')
374 menu = doc.xml_select(u'/db:article/db:info/db:titleabbrev')
375 code = doc.xml_select(u"//xi:include[@parse='text']")
376 resource = doc.xml_select(u"//db:link[@xl:href]")
377 image = doc.xml_select(u"//db:imagedata[@fileref]")
380 (p, ext) = os.path.splitext(c.href)
381 if ext in valid_scripts:
386 base = xfile.split('.')[1]
387 link = base.replace('index','')
388 level = len(filter(None,re.split(r'(^/\w*/|\w*/)',link)))
391 rf = os.path.join(dirname,r.href)
392 if os.path.isfile(rf):
395 im = os.path.join(dirname,i.fileref)
396 if os.path.isfile(im):
398 page = dict(title=unicode(doc.article.info.title),
399 menu=unicode(doc.article.info.titleabbrev),
400 output=os.path.join(dirname,
401 filename.replace('xml','html')),
407 if l['link'] == link:
411 print "adding "+link+" to sitemap"
415 sfile = open('sitemap.txt','w')
417 sfile.write(l['link']+'\n')
421 def expandXincludeTxt(page):
422 doc = bindery.parse(page['file'],
423 prefixes={u'db': u'http://docbook.org/ns/docbook',
424 u'xi': u'http://www.w3.org/2001/XInclude'})
426 code = doc.xml_select(u"//xi:include[@parse='text']")
428 (p, ext) = os.path.splitext(c.href)
429 if ext in valid_scripts:
430 exe = os.path.join(os.path.abspath(c.href))
431 xml = subprocess.Popen([exe],stdout=subprocess.PIPE)
432 xstr = bindery.parse(str(xml.stdout.read()))
433 id = c.xml_index_on_parent
434 for x in xstr.xml_children:
435 c.xml_parent.xml_insert(id,x)
436 c.xml_parent.xml_remove(c)
439 def xsltConvert(doc):
440 # amara can not handle the docbook stylesheets
441 # xmlarticle = transform(doc,style_xslt)
443 rundir = os.path.dirname(page['file'])
445 infile = os.path.basename(tempfile.mktemp())
446 outfile = tempfile.mktemp()
447 tfi = open(infile,'w')
448 tfi.write(doc.xml_encode())
450 # cmd = ["saxon-xslt-xinclude","-o",outfile,infile,style_xslt]
451 cmd = ["xsltproc","--xinclude","--output",outfile,style_xslt,infile]
452 retcode = subprocess.call(cmd)
454 print 'Error: '+' '.join(cmd)+' Returncode ['+str(retcode)+']'
455 tfo = open(outfile,'r')
463 def genMenu(page,sitemap,slevel,elevel):
466 if elevel == MAXLEVEL or elevel == 1 or page == None:
470 html = '<ul class="tree">\n'
471 idx = sitemap.index(page)
472 while (sitemap[idx]['level'] == page['level']):
474 title = sitemap[idx]['menu']
476 while (idx < len(sitemap) and sitemap[idx]['level'] == page['level']):
477 sm.append(sitemap[idx])
482 if slevel > p['level'] or elevel < p['level']:
484 if not title and p['link'] == '/':
487 if oldlevel < p['level']:
489 elif oldlevel > p['level']:
490 if p['link'][-1] == '/':
492 html+='</ul>\n</li>\n'
493 if page != None and page == p:
494 html+='<li class="selected"><a href="%s">%s</a>' % (p['link'],p['menu'])
496 html+='<li><a href="%s">%s</a>' % (p['link'],p['menu'])
497 if p['link'][-1] != '/' or p['link'] == '/':
499 oldlevel = p['level']
503 def writeToTemplate(page,doc,sitemap):
504 (menu,menuname) = genMenu(page,sitemap,1,MAXLEVEL)
505 (levelmenu,levelname) = genMenu(page,sitemap,page['level'],page['level'])
506 template = Template(file=style_tmpl,
507 searchList=[{'title':page['title']},
510 {'levelmenu':levelmenu},
511 {'levelname':levelname}])
512 outfile = tmptarget+page['output']
513 mkdir_p(os.path.dirname(outfile))
514 out = open(outfile, 'w')
515 out.write(str(template))
517 for r in page['res']:
518 mkdir_p(os.path.dirname(tmptarget+r))
519 shutil.copyfile(r, tmptarget+r)
521 def createSitemap(sitemap):
522 (menu,menuname) = genMenu(None,sitemap,1,MAXLEVEL)
523 template = Template(file=style_tmpl,
530 outfile = tmptarget+'sitemap.en.html'
531 mkdir_p(os.path.dirname(outfile))
532 out = open(outfile, 'w')
533 out.write(str(template))
542 missing = dir_.set() - sitemap.set()
543 removed = sitemap.set() - dir_.set()
545 print removed+' pages missing!!'
547 print 'adding missing page '+page
548 sitemap.add_link(page)
549 if len(missing & removed) != 0:
550 print 'writing new sitemap - please adjust if needed'
559 print "Publish [%5.2f s]" % (round(t2-t1,2))
561 sitemap = generateSitemap()
562 tmptarget = tempfile.mkdtemp()+'/'
566 print "Page : %-30s %30s" % (page['link'],
567 time.ctime(os.stat(page['file']).st_mtime)),
568 doc = expandXincludeTxt(page)
569 pubdoc = xsltConvert(doc)
570 writeToTemplate(page,pubdoc,sitemap)
572 print "[%5.2f s]" % (round(t2-t1,2))
575 print "Total time\t\t\t\t\t\t\t [%5.2f s]" % (round(tot,2))
576 createSitemap(sitemap)
577 publish(tmptarget, args.output)
578 publish(args.style+"css", args.output)
579 publish(args.style+"images",args.output)