Blame Scripts/CentOS-Web/Apps/page.py

cc10e6
#!/usr/bin/python
cc10e6
#
8c93bb
# Apps.page -- This module encapsulates the page layout of web
8c93bb
# applications.
cc10e6
#
8f6ee0
# Copyright (C) 2011 The CentOS Project
cc10e6
#
cc10e6
# This program is free software; you can redistribute it and/or modify
cc10e6
# it under the terms of the GNU General Public License as published by
cc10e6
# the Free Software Foundation; either version 2 of the License, or (at
cc10e6
# your option) any later version.
cc10e6
#
cc10e6
# This program is distributed in the hope that it will be useful, but
cc10e6
# WITHOUT ANY WARRANTY; without even the implied warranty of
cc10e6
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
cc10e6
# General Public License for more details.
cc10e6
#
cc10e6
# You should have received a copy of the GNU General Public License
cc10e6
# along with this program; if not, write to the Free Software
cc10e6
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
cc10e6
#
cc10e6
# ----------------------------------------------------------------------
cc10e6
# $Id$
cc10e6
# ----------------------------------------------------------------------
cc10e6
cc10e6
import cgi
cc10e6
import cgitb; cgitb.enable()
8f6ee0
8c93bb
from Apps import xhtml
8c93bb
8c93bb
class Layout(xhtml.Strict):
cffc47
    """Xhtml page modeling."""
8f6ee0
8f6ee0
8f6ee0
    def __init__(self):
8f6ee0
        """Initialize page data."""
f19343
        self.qs = cgi.parse()
8f6ee0
        self.name = 'Home'
8f6ee0
        self.title = 'The CentOS Project'
8f6ee0
        self.description = 'Community Enterprise Operating System'
8f6ee0
        self.keywords = 'centos, project, community, enterprise, operating system'
8f6ee0
        self.copyright = '2009-2011 The CentOS Project. All rights reserved.'
8f6ee0
        self.language = 'en'
8f6ee0
f19343
        # Define page header. This is the information displayed
f19343
        # between the page top and the page content.
f19343
        self.header = self.logo()
e1d4e1
        self.header += self.google()
e1d4e1
        self.header += self.navibar()
e1d4e1
        self.header += self.releases()
e1d4e1
        self.header += self.page_links()
e1d4e1
        self.header += self.page_navibar()
f19343
f19343
        # Define page body. This is the information displayed between
f19343
        # the page header and page footer.
e1d4e1
        self.body = 'None'
f19343
f19343
        # Define page footer. This is the information displayed
f19343
        # between the page bottom and the page content, the last
f19343
        # information displayed in the page.
f19343
        self.footer = self.credits()
8f6ee0
d59f66
8c93bb
    def logo(self):
e1d4e1
        """Returns The CentOS Logo.
cc10e6
8f6ee0
        The page logo is displayed on the top-left corner of the page.
8f6ee0
        We use this area to show The CentOS Logo, the main visual
8f6ee0
        representation of The CentOS Project. In order to print the
8f6ee0
        page logo correctly, the image related must be 78 pixels of
8f6ee0
        height.
7e8dd3
8f6ee0
        """
8f6ee0
        attrs = []
8f6ee0
        attrs.append({'id': 'logo'})
8f6ee0
        attrs.append({'title': 'Community Enterprise Operating System', 'href': '/centos-web/'})
8f6ee0
        attrs.append({'src': '/centos-web-pub/Images/centos-logo.png', 'alt': 'CentOS'})
cc10e6
8c93bb
        return self.tag_div(attrs[0], [8,1], self.tag_a(attrs[1], [12,1], self.tag_img(attrs[2], [0,0]), 0), 1)
cc10e6
cc10e6
e1d4e1
    def google(self):
e1d4e1
        """Returns Google advertisements (468x60 pixels)."""
8f6ee0
        output = """
cc10e6
        
cc10e6
            Google Advertisement
cc10e6
            <script type="text/javascript">
cc10e6
                google_ad_client = "pub-6973128787810819";
cc10e6
                google_ad_width = 468;
cc10e6
                google_ad_height = 60;
cc10e6
                google_ad_format = "468x60_as";
cc10e6
                google_ad_type = "text_image";
cc10e6
                google_ad_channel = "";
cc10e6
                google_color_border = "204c8d";
cc10e6
                google_color_bg = "345c97";
cc10e6
                google_color_link = "0000FF";
cc10e6
                google_color_text = "FFFFFF";
cc10e6
                google_color_url = "008000";
cc10e6
                //-->
cc10e6
            </script>
cc10e6
            
cc10e6
                src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
cc10e6
            </script>
cc10e6
        
cc10e6
cc10e6
        

8f6ee0
        """
8f6ee0
        return output
cc10e6
cc10e6
e1d4e1
    def navibar(self):
e1d4e1
        """Returns top-level navigation bar. 
cc10e6
    
e1d4e1
        The top-level navigation bar organizes links to main web
e1d4e1
        applications The CentOS Project makes use of. Links to these
e1d4e1
        web applications stay always visible, no matter what web
e1d4e1
        application you be visiting (e.g., Wiki, Lists, Forums,
f19343
        Projects, Bugs, Docs, Downloads and Sponsors.).
cc10e6
8f6ee0
        """
8f6ee0
        names = ['Home', 'Wiki', 'Lists', 'Forums', 'Projects', 'Bugs', 'Docs', 'Downloads', 'Sponsors']
8f6ee0
        attrs = []
8f6ee0
        focus = self.name
cc10e6
8f6ee0
        for i in range(len(names)):
8c93bb
            if names[i].lower() == 'home':
8c93bb
                attrs.append({'href': '/centos-web/'})
8c93bb
            else:
8c93bb
                attrs.append({'href': '/centos-web/?app=' + names[i].lower()})
8f6ee0
8c93bb
        tabs = self.navibar_tabs(names, attrs, focus)
8c93bb
        tabs += self.separator()
8f6ee0
8f6ee0
        return tabs
8f6ee0
8f6ee0
8c93bb
    def navibar_tabs(self, names, attrs, focus=''):
8f6ee0
        """Returns navigation tabs.
8f6ee0
e1d4e1
        The navigation tabs are the smaller components a navigation
e1d4e1
        bar like "top-level navigation bar" and "application
e1d4e1
        navigation bar" are made of.
8f6ee0
e1d4e1
        names: List containing link names of tabs.
8f6ee0
e1d4e1
        attrs: List containing a dictionary for each tab link name
e1d4e1
            inside the `names' list. Dictionaries inside attrs
e1d4e1
            argument contain the link attributes (e.g., accesskey,
e1d4e1
            title, and href) used by link names so they can be
e1d4e1
            linkable once rendered.
cc10e6
    
e1d4e1
        focus: Name of the link marked as current.
cc10e6
    
8f6ee0
        """
8f6ee0
        navibar_tabs = ''
cc10e6
8f6ee0
        for i in range(len(names)):
8c93bb
            content = self.tag_span('', [0,0], str(names[i]))
8c93bb
            content = self.tag_a(attrs[i], [16,1], content)
8f6ee0
            if str(names[i]).lower() == focus.lower():
8c93bb
                content = self.tag_span({'class': 'current'}, [12,1], content, 1)
8f6ee0
            else:
8c93bb
                content = self.tag_span('', [12,1], content, 1)
8f6ee0
            navibar_tabs += content
cc10e6
8c93bb
        return self.tag_div({'class': 'tabs'}, [8,1], navibar_tabs, 1)
cc10e6
cc10e6
3694e5
    def releases(self, names=['6.0'], attrs=[{'href': '/centos-web/p=releases&id=6.0'}]):
e1d4e1
        """Returns The CentOS Distribution last releases.
e1d4e1
e1d4e1
        This method introduces the `releases' method by providing
e1d4e1
        links to it.
e1d4e1
e1d4e1
        names: List containing release numbers in the form M.N, where M
e1d4e1
            means major release and N minor release.
e1d4e1
e1d4e1
        attrs: List containing a dictionary for each release number
e1d4e1
            provided in `names' argument. These dictionaries provide
e1d4e1
            the link attributes required by release numbers in order
e1d4e1
            for them to be transformed into valid links once the page
e1d4e1
            be rendered.
e1d4e1
        
e1d4e1
        """
8f6ee0
        releases = ''
7e8dd3
8c93bb
        title = self.tag_a({'href': '/centos-web/?p=releases'}, [0,0], 'Last Releases') + ':'
8c93bb
        title = self.tag_span({'class': 'title'}, [16,1], title)
cc10e6
8f6ee0
        for i in range(len(names)):
8c93bb
            link = self.tag_a(attrs[i], [20,1], names[i])
8f6ee0
            if i == len(names) - 1:
8c93bb
                span = self.tag_span({'class': 'last release'}, [16,1], link, 1) 
8f6ee0
            else:
8c93bb
                span = self.tag_span({'class': 'release'}, [16,1], link, 1) 
8f6ee0
            releases += span
8c93bb
        releases = self.tag_div({'class': 'left'}, [12,1], title + releases, 1)
cc10e6
8c93bb
        rsslink = self.tag_span('', [0,0], 'RSS')
3694e5
        rsslink = self.tag_a({'href': '/centos-web/' + self.qs_args({'rss':'releases'}), 'title': 'RSS'}, [20,1], rsslink)
8c93bb
        rsslink = self.tag_span({'class': 'rss'}, [16,1], rsslink, 1)
8c93bb
        rsslink = self.tag_div({'class': 'right'}, [12, 1], rsslink, 1)
7e8dd3
8c93bb
        return self.tag_div({'id': 'last-releases'}, [8,1], releases + rsslink, 1)
7e8dd3
cc10e6
e1d4e1
    def user_links_logs(self):
e1d4e1
        """Return links related to user's logs.
e1d4e1
        
e1d4e1
        This function introduces the `logs' module. The `logs' module
e1d4e1
        registers all user's activity, from login to logout. This link
e1d4e1
        must be display/accessible only after a user has successfully
e1d4e1
        login.
cc10e6
e1d4e1
        """
e1d4e1
        last_visit = self.tag_a({'href': '/centos-web/' + self.qs_args({'app':'', 'p':'logs'})}, [0,0], 'Logs')
e1d4e1
        return self.tag_div({'class': 'logs'}, [12, 1], last_visit, 1)
cc10e6
cc10e6
e1d4e1
    def user_links_session(self):
e1d4e1
        """Returns links related to user's session.
e1d4e1
        
e1d4e1
        This function introduces the `session' module. The `session'
e1d4e1
        module provides state to user interactions so their action can
e1d4e1
        be registered individually.
cc10e6
e1d4e1
        """
8f6ee0
        names = []
8f6ee0
        attrs = []
8f6ee0
        session = ''
8f6ee0
8f6ee0
        names.append('Lost your password?')
3694e5
        attrs.append({'href': '/centos-web/' + self.qs_args({'app':'', 'p':'lostpwd'})})
8f6ee0
        names.append('Register')
3694e5
        attrs.append({'href': '/centos-web/' + self.qs_args({'app':'', 'p':'register'})})
8f6ee0
        names.append('Login')
3694e5
        attrs.append({'href': '/centos-web/' + self.qs_args({'app':'', 'p':'login'})})
8f6ee0
8f6ee0
        for i in range(len(names)):
8c93bb
            output = self.tag_a(attrs[i], [20,1], str(names[i]), 0)
8f6ee0
            if i == len(names) - 1:
8c93bb
                output = self.tag_span({'class': 'last'}, [16,1], output, 1)
8f6ee0
            else:
8c93bb
                output = self.tag_span('', [16,1], output, 1)
8f6ee0
            session += output
8f6ee0
8c93bb
        return self.tag_div({'class': 'session'}, [12,1], session, 1)
cc10e6
cc10e6
e1d4e1
    def user_links_trails(self, names=['None'], attrs=[{'href': '/centos-web/'}]):
8f6ee0
        """Returns page trails (a.k.a. breadcrumbs).
cc10e6
    
e1d4e1
        The page breadcrumbs record the last pages the user visited
e1d4e1
        inside the current web application. Notice that page
e1d4e1
        breadcrumbs are user-specific information, so it isn't
e1d4e1
        possible to implement them until a way to manage user sessions
e1d4e1
        be implemeneted inside `centos-web.cgi' script. Until then,
e1d4e1
        keep the tag construction commented and return an empty value.
e1d4e1
e1d4e1
        names: List with trail link names.
e1d4e1
e1d4e1
        attrs: Dictionary with trail link attributes.
8f6ee0
8f6ee0
        """
8f6ee0
        links = ''
8f6ee0
8f6ee0
        for i in range(len(names)):
8f6ee0
            if i == len(names) - 1:
8c93bb
                content = self.tag_span({'class':'last'}, [16,1], self.tag_a(attrs[i], [20, 1], names[i]), 1)
8f6ee0
            else:
8c93bb
                content = self.tag_span('', [16,1], self.tag_a(attrs[i], [20, 1], names[i], 0), 1)
cffc47
            links += content
cc10e6
8c93bb
        return self.tag_div({'class': 'trail'}, [12,1], links, 1)
cc10e6
cc10e6
e1d4e1
    def user_links(self):
e1d4e1
        """Returns user related links.
cc10e6
8f6ee0
        The user links are specific to each web application. They are
e1d4e1
        shown over the application navigation bar.
cc10e6
8f6ee0
        """
e1d4e1
        userlinks = self.user_links_logs()
e1d4e1
        userlinks += self.user_links_session()
e1d4e1
        userlinks += self.user_links_trails()
cc10e6
8c93bb
        return self.tag_div({'class': 'userlinks'}, [8,1], userlinks, 1)
cc10e6
cc10e6
e1d4e1
    def page_navibar(self, names=['Welcome'], attrs=[{'href':'/centos-web/?p=welcome'}], focus='Welcome'):
e1d4e1
        """Returns navigation bar for application main pages.
e1d4e1
       
e1d4e1
        names: List containing link names.
e1d4e1
e1d4e1
        attrs: List containing one dictionary for each link name in
e1d4e1
            `names' argument. Dictionaries here contain the link
e1d4e1
            attributes needed to make linkable tabs once the page is
e1d4e1
            rendered.
8f6ee0
e1d4e1
        """
8c93bb
        navibar_app = self.navibar_tabs(names, attrs, focus)
8c93bb
        navibar_app += self.separator({'class': 'page-line white'}, [8,1])
8f6ee0
8f6ee0
        return navibar_app
8f6ee0
 
cc10e6
8c93bb
    def separator(self, attrs={'class': 'page-line'}, indent=[16,1]):
e1d4e1
        """Returns separator.
e1d4e1
e1d4e1
        The separator construction is mainly used to clear both sides
e1d4e1
        inside the page, specially when floating elements are around.
e1d4e1
        
e1d4e1
        attrs: Dictionary containing hr's div attributes.
e1d4e1
        
e1d4e1
        indent: List containing hr's div indentation values.
e1d4e1
        
e1d4e1
        """
8c93bb
        line = self.tag_hr({'style': 'display:none;'}, [0,0])
8c93bb
        line = self.tag_div(attrs, indent, line)
cc10e6
8c93bb
        return line
cc10e6
cc10e6
8c93bb
    def license(self):
e1d4e1
        """Retruns license link."""
8f6ee0
        license = 'Creative Commons Attribution-Share Alike 3.0 Unported License'
8c93bb
        license = self.tag_a({'href': 'http://creativecommons.org/licenses/by-sa/3.0/'}, [0,0], license) + '.'
cffc47
8f6ee0
        return license
cc10e6
cc10e6
8c93bb
    def metadata(self):
e1d4e1
        """Returns metadata."""
8c93bb
        metadata = self.tag_meta({'http-equiv': 'content-type', 'content': 'text/html; charset=UTF-8'}, [4,1])
8c93bb
        metadata += self.tag_meta({'http-equiv': 'content-style-type', 'content': 'text/css'}, [4,0])
8c93bb
        metadata += self.tag_meta({'http-equiv': 'content-language', 'content': str(self.language)}, [4,1])
8c93bb
        metadata += self.tag_meta({'name': 'keywords', 'content': str(self.keywords)}, [4,0])
8c93bb
        metadata += self.tag_meta({'name': 'description', 'content': str(self.description)}, [4,1])
8c93bb
        metadata += self.tag_meta({'name': 'copyright', 'content': 'Copyright © ' + str(self.copyright)}, [4,0])
8c93bb
        metadata += self.tag_title('', [4,1], self.title)
8c93bb
        metadata += self.tag_link({'href': '/centos-web-pub/stylesheet.css','rel': 'stylesheet', 'type': 'text/css'}, [4,0])
8c93bb
        metadata += self.tag_link({'href': '/centos-web-pub/Images/centos-fav.png', 'rel': 'shortcut icon', 'type': 'image/png'}, [4,1])
cc10e6
8c93bb
        return self.tag_head('', [0,1], metadata)
cc10e6
cc10e6
e1d4e1
    def qs_args(self, names={}):
e1d4e1
        """Returns query string arguments.
e1d4e1
e1d4e1
        The query string arguments are used to build links dynamically
e1d4e1
        and, this way, to create a browsable and logically organized
e1d4e1
        web environment.  Such a construction generally needs to
e1d4e1
        retrive some of the values previously passed to the query
e1d4e1
        string and add new ones to it.
e1d4e1
e1d4e1
        names: A dictionary containing the variable name and value
e1d4e1
            pair used to build a new query string. 
e1d4e1
            
e1d4e1
        When a variable is provied without a value, then its value is
e1d4e1
        retrived from the current query string. If a value isn't found
e1d4e1
        there neither, then the variable is removed from the new query
e1d4e1
        string.
e1d4e1
e1d4e1
        When a variable is provided with its value, then its value is
e1d4e1
        used to build the new query string.
e1d4e1
e1d4e1
        """
e1d4e1
        output = ''
3694e5
        names_keys = names.keys()
3694e5
        names_keys.sort()
3694e5
        for key in names_keys:
e1d4e1
            if names[key] == '':
e1d4e1
                if key in self.qs:
e1d4e1
                    names[key] = self.qs[key][0]
e1d4e1
                else:
e1d4e1
                    continue
e1d4e1
            if output == '':
e1d4e1
                output = '?'
e1d4e1
            else:
e1d4e1
                output += '&'
d59f66
            output += key + '=' + str(names[key])
e1d4e1
e1d4e1
        return output
e1d4e1
e1d4e1
d59f66
    def search(self, size=20):
3694e5
        """Returns search form.
d59f66
d59f66
        The search form redirects the search up to the search page. Is
d59f66
        there, in the search page, where the search occurs.
3694e5
        
d59f66
        size: A number discribe how large the search text box is.
e1d4e1
3694e5
        """
d59f66
        input = self.tag_input({'type':'text', 'value':'', 'size':size}, [24,1])
e1d4e1
d59f66
        action = self.tag_dt({}, [20,1], 'Search', 1)
d59f66
        action += self.tag_dd({}, [20,1], input)
d59f66
        output = self.tag_dl({'class':'search'}, [16,1], action, 1)
d59f66
        return self.tag_form({'action':'/centos-web/' + self.qs_args({'app':'', 'p':'search'}), 'method':'post', 'title':'Search'}, [12,1], output, 1)
e1d4e1
        
e1d4e1
d59f66
    def content_row(self, attrs, id, title, email, commit_date, update_date, category, comments, abstract):
d59f66
        """Return row of content.
d59f66
d59f66
        The row of content is used to build the list of content and is
d59f66
        made of the following information:
d59f66
d59f66
            attrs: (Required) A dictionary discribing the rows style.
d59f66
                This is useful to alternate the row background colors.
e1d4e1
e1d4e1
            id: (Required) A unique numerical value referring the
d59f66
                content identification. This is the value used on
e1d4e1
                administrative tasks like updating and deleting.
e1d4e1
        
e1d4e1
            title: (Required) A few words phrase describing the
e1d4e1
                content, up to 255 characters.
e1d4e1
            
e1d4e1
            author_email: (Required) A string referring the user email
e1d4e1
                address, up to 255 characters. The user email address
e1d4e1
                is used as id inside The CentOS User LDAP server,
e1d4e1
                where user specific information (e.g., surname,
e1d4e1
                lastname, office, phone, etc.) are stored in. This is
e1d4e1
                the field that connects the user with the content
e1d4e1
                he/she produces.
e1d4e1
            
e1d4e1
            commit_date: (Required). A string referring the date and
e1d4e1
                time the author_email published the article for time.
e1d4e1
e1d4e1
            update_date: (Optional) A string representing the date and
e1d4e1
                time the author_email updated/revised the article for
e1d4e1
                last time.
e1d4e1
d59f66
            comments: (Required) A number representing the number of
d59f66
                comments the content has received since its
d59f66
                publication.
d59f66
d59f66
            category: (Required) A string refering the category name
d59f66
                the author_email wrote the article for.
e1d4e1
e1d4e1
            abstract: (Optional) One or two paragraphs describing the
e1d4e1
                article content. This information is used to build the
e1d4e1
                page metadata information. When this value is not
e1d4e1
                provided no abstract information is displayed in the
e1d4e1
                page, but the <meta name="description".../> is built
e1d4e1
                using article's first 255 characters.
e1d4e1
e1d4e1
        The article's content itself is not displayed in the content
e1d4e1
        list view, but in the detailed view of content.
e1d4e1
e1d4e1
        """
d59f66
        email = self.tag_a({'href':'mailto:' + email}, [0,0], email)
d59f66
        title = self.tag_a({'href':'/centos-web/' + self.qs_args({'app':'', 'p':'', 'id':id})}, [0,0], title)
d59f66
        title = self.tag_h3({'class': 'title'}, [20,1], title, 0)
d59f66
        info = self.tag_span({'class':'author'}, [24,1], 'Written by ' + email)
d59f66
        if update_date != commit_date:
d59f66
            info += self.tag_span({'class':'date'}, [24,1], update_date)
d59f66
        else:
d59f66
            info += self.tag_span({'class':'date'}, [24,1], commit_date)
d59f66
        if comments == 1:
d59f66
            comments = self.tag_a({'href': self.qs_args({'app':'', 'p':'details', 'id':id}) + '#comments'}, [0,0], str(comments) + ' comment')
d59f66
        elif comments > 1:
d59f66
            comments = self.tag_a({'href': self.qs_args({'app':'', 'p':'', 'id':id}) + '#comments'}, [0,0], str(comments) + ' comments')
d59f66
        else:
d59f66
            comments = 'No comments'
d59f66
        info += self.tag_span({'class':'comments'}, [24,1], comments)
d59f66
        info = self.tag_div({'class': 'info'}, [20,1], info, 1)
d59f66
        abstract = self.tag_p({'class': 'abstract'}, [20,1], abstract)
d59f66
d59f66
        return self.tag_div(attrs, [16,1], title + info + abstract, 1)
d59f66
d59f66
d59f66
    def content_list(self, category='None.'):
d59f66
        """Returns list of content.
d59f66
        
d59f66
        The list of content is used to explore the content available
d59f66
        inside specific pages of specific web applications. The
d59f66
        information is displayed through paginated rows of content
d59f66
        that can be filtered to reduce the search results based on
d59f66
        patterns.  By default, the list of content displays 15 rows,
d59f66
        but this value can be changed in user's preferences.
d59f66
d59f66
        """
d59f66
        output = ''
d59f66
        count = 0
d59f66
        rows = []
d59f66
        rows.append((1, 'Introduction to CentOS Web Environment',
d59f66
                    'al@centos.org',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'articles',
d59f66
                    0,
d59f66
                    'This is the abstrac paragrah of content. '*10))
d59f66
        rows.append((2, 'Creating New Applications',
d59f66
                    'al@centos.org',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'articles',
d59f66
                    1,
d59f66
                    'This is the abstrac paragrah of content. '*5))
d59f66
        rows.append((3, 'Texinfo Documentation Backend',
d59f66
                    'al@centos.org',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'Tue Aug 30 12:33:11 CDT 2011',
d59f66
                    'articles',
d59f66
                    5,
d59f66
                    'This is the abstrac paragrah of content. '*8))
d59f66
d59f66
        for row in rows:
d59f66
            if count == 0:
d59f66
                attrs = {'class': 'dark row'}
d59f66
                count += 1
d59f66
            else:
d59f66
                attrs = {'class': 'light row'}
d59f66
                count = 0
d59f66
            output += self.content_row(attrs, *row)
d59f66
d59f66
        content_list = self.tag_div({'id':'content-list'}, [12,1], output, 1)
d59f66
        
d59f66
        return content_list
d59f66
d59f66
d59f66
    def content_list_2cols(self, category='None'):
d59f66
        """Return list of content in two columns.
d59f66
        
d59f66
        The left column is for listing content and the right column to
d59f66
        list related actions (e.g., search, categories, archives,
d59f66
        etc.).
d59f66
d59f66
        """
d59f66
        list = self.content_list()
d59f66
d59f66
        actions = self.search(15) + self.categories() + self.archives()
d59f66
        actions = self.tag_div({'id':'content-actions'}, [8,1], actions, 1)
d59f66
d59f66
        return actions  + list
d59f66
d59f66
d59f66
    def categories(self):
d59f66
        """Returns list of categories.
d59f66
        
d59f66
        """
d59f66
        categories = ['Articles', 'Erratas', 'Events']
d59f66
        dt = self.tag_dt({}, [12,1], 'Categories')
d59f66
        dd = ''
d59f66
        for id in range(len(categories)):
d59f66
            a = self.tag_a({'href': self.qs_args({'app':'', 'p':'categories', 'id':id})}, [16,1], categories[id] + ' (0)') 
d59f66
            dd += self.tag_dd({}, [12,1], a, 1)
d59f66
d59f66
        return self.tag_dl({},[8,1], dt + dd, 1)
d59f66
d59f66
d59f66
    def archives(self):
d59f66
        """Returns archives."""
d59f66
        archives = {}
d59f66
        archives['2011'] = ['January', 'February', 'March', 'April', 'May']
d59f66
        archives['2010'] = ['January', 'February']
d59f66
d59f66
        dt = self.tag_dt({}, [12,1], 'Archives')
d59f66
        year_dl = ''
d59f66
        year_dd = ''
d59f66
d59f66
        for key in archives.keys():
d59f66
            year_dt = self.tag_dt({},[12,1], key, 1)
d59f66
            for id in range(len(archives[key])):
d59f66
                a = self.tag_a({'href': self.qs_args({'app':'',
d59f66
                'p':'archives', 'year': key, 'month': id + 1})},
d59f66
                [16,1], archives[key][id] + ' (0)')
d59f66
                year_dd += self.tag_dd({}, [12,1], a)
d59f66
            year_dl += self.tag_dl({'class':'year'}, [12,1], year_dt + year_dd, 1)
d59f66
            year_dd = ''
d59f66
d59f66
        return self.tag_dl({},[8,1], dt + year_dl, 1)
e1d4e1
e1d4e1
e1d4e1
    def page_top(self):
e1d4e1
        """Returns page top anchor."""
e1d4e1
        return self.tag_a({'name':'top'}, [0,1])
e1d4e1
e1d4e1
e1d4e1
    def page_header(self):
d59f66
        """Returns page header.
d59f66
        
d59f66
        The page_header is common to all application modules and 
d59f66
        """
e1d4e1
        return self.tag_div({'id': 'page-header'}, [4,1], self.header, 1)
e1d4e1
e1d4e1
e1d4e1
    def page_body(self):
d59f66
        """Returns page body.
d59f66
        
d59f66
        The page_body is specific to each application module and is
d59f66
        there where it must be constructed. The construction itself
d59f66
        takes place through the page_content() function which does
d59f66
        a return through an instantiated `content_' prefixed method.
d59f66
        The `content_' prefixed method used depends on the kind of
d59f66
        content you want to print out (e.g., `content_list()' for a
d59f66
        content list, `content_detail()' for a detailed view of
d59f66
        content, etc.). Later, the `body' variable instantiated
d59f66
        from this class is reset in the `main()' function with the value
d59f66
        returned from `page_content()' so the desired content layout
d59f66
        can be printed out. 
d59f66
        
d59f66
        """
d59f66
        return self.tag_div({'id':'page-body'}, [4,1], self.body, 1)
e1d4e1
e1d4e1
e1d4e1
    def page_links(self):
d59f66
        """Returns page links."""
e1d4e1
        page_links = self.user_links()
e1d4e1
        return self.tag_div({'id': 'pagelinks'}, [8,1], page_links, 1)
e1d4e1
e1d4e1
    
e1d4e1
    def page_footer(self):
e1d4e1
        """Retruns page footer."""
e1d4e1
        return self.tag_div({'id': 'page-footer'}, [4,1], self.credits(), 1)
e1d4e1
e1d4e1
e1d4e1
    def page_wrap(self):
e1d4e1
        """Returns page wrap."""
e1d4e1
        return self.tag_div({'id': 'wrap'}, [0,1], self.page_header() + self.page_body() + self.page_footer(), 1)
cc10e6
cc10e6
8c93bb
    def admonition(self, title='Note', subtitle="", content=""):
cffc47
        """Returns page admonition.
cffc47
        
cffc47
        title: Admonition's title.
cffc47
cffc47
        subtitle: Admonition's subtitle. The value of this argument is
cffc47
            concatenated on the right side of title using a colon (:)
cffc47
            as separator. Notice that this value is expanded inside
cffc47
            the 

tag and there is no need to introduce extra tags

cffc47
            here.
cffc47
cffc47
        content: Admonition's content. The values passed through this
cffc47
            arguments needs to be XHTML code returned from
cffc47
            `self.tag()'. Preferably, paragraphs (p), tables (table),
cffc47
            lists (ul, ol, dl) and pre-formatted texts (pre).
cffc47
cffc47
        """
cffc47
        if title == '':
cffc47
            return ''
cffc47
        else:
cffc47
            title = str(title.capitalize())
cffc47
cffc47
        if subtitle != '':
cffc47
            subtitle = ': ' + str(subtitle.capitalize())
cffc47
cffc47
        if content != '':
cffc47
            content = str(content)
cffc47
cffc47
        admonitions = ['Note', 'Tip', 'Important', 'Caution', 'Warning', 'Redirected', 'Success', 'Error']
cffc47
        
cffc47
        if title in admonitions:
cffc47
            attrs = {'class': 'admonition ' + title.lower()}
8c93bb
            image = self.tag_img({'src': '/centos-web-pub/Images/' + title.lower() + '.png', 'alt': title}, [16,1])
8c93bb
            title = self.tag_h3({'class': 'title'}, [16,1], title + subtitle, 0)
8c93bb
            output = image + title + content + self.separator()
cffc47
        else:
cffc47
            attrs = {'class': 'admonition unknown'}
8c93bb
            title = self.tag_h3({'class': 'title'}, [16,1], title + subtitle, 1)
cffc47
            output = title + content
cffc47
        
8c93bb
        return self.tag_div(attrs, [12,1], output, 1)
cffc47
cffc47
8c93bb
    def credits(self):
8f6ee0
        """Returns page credits."""
8c93bb
        copyright = self.tag_p({'class': 'copyright'}, [12,1], 'Copyright © ' + str(self.copyright))
8c93bb
        license = self.tag_p({'class': 'license'}, [12,1], 'This website is licensed under a ' + str(self.license()))
8c93bb
        credits = self.tag_img({'src': '/centos-web-pub/Images/top.png', 'alt': 'Top'}, [0,0])
8c93bb
        credits = self.tag_a({'title': 'Top', 'href': '#top'}, [16,1], credits)
8c93bb
        credits = self.tag_div({'class': 'top'}, [12,1], credits, 1)
8f6ee0
        credits = str(credits) + str(copyright) + str(license) 
8c93bb
        credits = self.tag_div({'class': 'credits'}, [8,1], credits, 1)
7e8dd3
8f6ee0
        return credits
cc10e6
cc10e6
8f6ee0
    def page(self):
8f6ee0
        """Returns page final output."""
f19343
        html = self.doctype()
8c93bb
        html += self.tag_html({'xmlns': 'http://www.w3.org/1999/xhtml', 'dir': 'ltr', 
e1d4e1
                         'lang': str(self.language), 'xml:lang': str(self.language)}, [0,1], 
e1d4e1
                         self.metadata() + self.page_top() + self.page_wrap())
7e8dd3
8f6ee0
        return html