web/views/wdoc.py
changeset 0 b97547f5f1fa
child 431 18b4dd650ef8
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """inline help system, using ReST file in products `wdoc` directory
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from itertools import chain
       
    10 from os.path import join
       
    11 from bisect import bisect_right
       
    12 
       
    13 from mx.DateTime import strptime, today
       
    14 
       
    15 from logilab.common.changelog import ChangeLog
       
    16 from logilab.mtconverter import CHARSET_DECL_RGX
       
    17 
       
    18 from cubicweb.common.selectors import req_form_params_selector
       
    19 from cubicweb.common.view import StartupView
       
    20 from cubicweb.common.uilib import rest_publish
       
    21 from cubicweb.web import NotFound
       
    22 
       
    23 _ = unicode
       
    24 
       
    25 # table of content management #################################################
       
    26 
       
    27 try:
       
    28     from xml.etree.ElementTree import parse
       
    29 except ImportError:
       
    30     from elementtree.ElementTree import parse
       
    31 
       
    32 def build_toc_index(node, index):
       
    33     try:
       
    34         nodeidx = node.attrib['resource']
       
    35         assert not nodeidx in index, nodeidx
       
    36         index[nodeidx] = node
       
    37     except KeyError:
       
    38         pass
       
    39     for child in node:
       
    40         build_toc_index(child, index)
       
    41         child.parent = node
       
    42 
       
    43 def get_insertion_point(section, index):
       
    44     if section.attrib.get('insertafter'):
       
    45         snode = index[section.attrib['insertafter']]
       
    46         node = snode.parent
       
    47         idx = node.getchildren().index(snode) + 1
       
    48     elif section.attrib.get('insertbefore'):
       
    49         snode = index[section.attrib['insertbefore']]
       
    50         node = snode.parent
       
    51         idx = node.getchildren().index(snode)
       
    52     else:
       
    53         node = index[section.attrib['appendto']]
       
    54         idx = None
       
    55     return node, idx
       
    56                      
       
    57 def build_toc(config):
       
    58     alltocfiles = reversed(tuple(config.locate_all_files('toc.xml')))
       
    59     maintoc = parse(alltocfiles.next()).getroot()
       
    60     maintoc.parent = None
       
    61     index = {}
       
    62     build_toc_index(maintoc, index)
       
    63     # insert component documentation into the tree according to their toc.xml
       
    64     # file 
       
    65     for fpath in alltocfiles:
       
    66         toc = parse(fpath).getroot()
       
    67         for section in toc:
       
    68             node, idx = get_insertion_point(section, index)
       
    69             if idx is None:
       
    70                 node.append(section)
       
    71             else:
       
    72                 node.insert(idx, section)
       
    73             section.parent = node
       
    74             build_toc_index(section, index)
       
    75     return index
       
    76     
       
    77 def title(node, lang):
       
    78     for title in node.findall('title'):
       
    79         if title.attrib['{http://www.w3.org/XML/1998/namespace}lang'] == lang:
       
    80             return unicode(title.text)
       
    81 
       
    82 def subsections(node):
       
    83     return [child for child in node if child.tag == 'section']
       
    84 
       
    85 # help views ##################################################################
       
    86 
       
    87 class InlineHelpView(StartupView):
       
    88     __selectors__ = (req_form_params_selector,)
       
    89     form_params = ('fid',)
       
    90     id = 'wdoc'
       
    91     title = _('site documentation')
       
    92     
       
    93     def call(self):
       
    94         fid = self.req.form['fid']
       
    95         for lang in chain((self.req.lang, self.vreg.property_value('ui.language')),
       
    96                           self.config.available_languages()):
       
    97             rid = '%s_%s.rst' % (fid, lang)
       
    98             resourcedir = self.config.locate_doc_file(rid)
       
    99             if resourcedir:
       
   100                 break
       
   101         else:
       
   102             raise NotFound
       
   103         self.tocindex = build_toc(self.config)
       
   104         try:
       
   105             node = self.tocindex[fid]
       
   106         except KeyError:
       
   107             node = None
       
   108         else:
       
   109             self.navigation_links(node)
       
   110             self.w(u'<div class="hr"></div>')
       
   111             self.w(u'<h1>%s</h1>' % (title(node, self.req.lang)))            
       
   112         data = open(join(resourcedir, rid)).read()
       
   113         self.w(rest_publish(self, data))
       
   114         if node is not None:
       
   115             self.subsections_links(node)
       
   116             self.w(u'<div class="hr"></div>')
       
   117             self.navigation_links(node)
       
   118 
       
   119     def navigation_links(self, node):
       
   120         req = self.req
       
   121         parent = node.parent
       
   122         if parent is None:
       
   123             return
       
   124         brothers = subsections(parent)
       
   125         self.w(u'<div class="docnav">\n')
       
   126         previousidx = brothers.index(node) - 1
       
   127         if previousidx >= 0:
       
   128             self.navsection(brothers[previousidx], 'prev')            
       
   129         self.navsection(parent, 'up')            
       
   130         nextidx = brothers.index(node) + 1
       
   131         if nextidx < len(brothers):
       
   132             self.navsection(brothers[nextidx], 'next')            
       
   133         self.w(u'</div>\n')
       
   134 
       
   135     navinfo = {'prev': ('', 'data/previous.png', _('i18nprevnext_previous')),
       
   136                'next': ('', 'data/next.png', _('i18nprevnext_next')),
       
   137                'up': ('', 'data/up.png', _('i18nprevnext_up'))}
       
   138                
       
   139     def navsection(self, node, navtype):
       
   140         htmlclass, imgpath, msgid = self.navinfo[navtype]
       
   141         self.w(u'<span class="%s">' % htmlclass)
       
   142         self.w(u'%s : ' % self.req._(msgid))
       
   143         self.w(u'<a href="%s">%s</a>' % (
       
   144             self.req.build_url('doc/'+node.attrib['resource']),
       
   145             title(node, self.req.lang)))
       
   146         self.w(u'</span>\n')
       
   147         
       
   148     def subsections_links(self, node, first=True):
       
   149         sub = subsections(node)
       
   150         if not sub:
       
   151             return
       
   152         if first:
       
   153             self.w(u'<div class="hr"></div>')
       
   154         self.w(u'<ul class="docsum">')
       
   155         for child in sub:
       
   156             self.w(u'<li><a href="%s">%s</a>' % (
       
   157                 self.req.build_url('doc/'+child.attrib['resource']),
       
   158                 title(child, self.req.lang)))
       
   159             self.subsections_links(child, False)
       
   160             self.w(u'</li>')
       
   161         self.w(u'</ul>\n')
       
   162         
       
   163 
       
   164 
       
   165 class InlineHelpImageView(StartupView):
       
   166     __selectors__ = (req_form_params_selector,)
       
   167     form_params = ('fid',)
       
   168     id = 'wdocimages'
       
   169     binary = True
       
   170     templatable = False
       
   171     content_type = 'image/png'
       
   172     
       
   173     def call(self):
       
   174         fid = self.req.form['fid']
       
   175         for lang in chain((self.req.lang, self.vreg.property_value('ui.language')),
       
   176                           self.config.available_languages()):
       
   177             rid = join('images', '%s_%s.png' % (fid, lang))
       
   178             resourcedir = self.config.locate_doc_file(rid)
       
   179             if resourcedir:
       
   180                 break
       
   181         else:
       
   182             raise NotFound
       
   183         self.w(open(join(resourcedir, rid)).read())
       
   184 
       
   185 
       
   186 class ChangeLogView(StartupView):
       
   187     id = 'changelog'
       
   188     title = _('What\'s new?')
       
   189     maxentries = 25
       
   190     
       
   191     def call(self):
       
   192         rid = 'ChangeLog_%s' % (self.req.lang)
       
   193         allentries = []
       
   194         title = self.req._(self.title)
       
   195         restdata = ['.. -*- coding: utf-8 -*-', '', title, '='*len(title), '']
       
   196         w = restdata.append
       
   197         for fpath in self.config.locate_all_files(rid):
       
   198             cl = ChangeLog(fpath)
       
   199             encoding = 'utf-8'
       
   200             # additional content may be found in title
       
   201             for line in (cl.title + cl.additional_content).splitlines():
       
   202                 m = CHARSET_DECL_RGX.search(line)
       
   203                 if m is not None:
       
   204                     encoding = m.group(1)
       
   205                     continue
       
   206                 elif line.startswith('.. '):
       
   207                     w(unicode(line, encoding))
       
   208             for entry in cl.entries:
       
   209                 if entry.date:
       
   210                     date = strptime(entry.date, '%Y-%m-%d')
       
   211                 else:
       
   212                     date = today()
       
   213                 messages = []
       
   214                 for msglines, submsgs in entry.messages:
       
   215                     msgstr = unicode(' '.join(l.strip() for l in msglines), encoding)
       
   216                     msgstr += u'\n\n'
       
   217                     for submsglines in submsgs:
       
   218                         msgstr += '     - ' + unicode(' '.join(l.strip() for l in submsglines), encoding)
       
   219                         msgstr += u'\n'
       
   220                     messages.append(msgstr)
       
   221                 entry = (date, messages)
       
   222                 allentries.insert(bisect_right(allentries, entry), entry)
       
   223         latestdate = None
       
   224         i = 0
       
   225         for date, messages in reversed(allentries):
       
   226             if latestdate != date:
       
   227                 fdate = self.format_date(date)
       
   228                 w(u'\n%s' % fdate)
       
   229                 w('~'*len(fdate))
       
   230                 latestdate = date
       
   231             for msg in messages:
       
   232                 w(u'* %s' % msg)
       
   233                 i += 1
       
   234                 if i > self.maxentries:
       
   235                     break
       
   236         w('') # blank line
       
   237         self.w(rest_publish(self, '\n'.join(restdata)))
       
   238