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

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
22389d
# the Free Software Foundation; either version 2 of the License, or
22389d
# (at 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
#
22389d
# ------------------------------------------------------------------
cc10e6
# $Id$
22389d
# ------------------------------------------------------------------
22389d
"""This module provides support to pages construction.
22389d
22389d
To this module, a page is an XHTML document consisting of several
22389d
independent components that, when put together, provide organization
22389d
to content. Each of these components is set as a method of Layout
22389d
class that can be instantiated later from application specific modules.
22389d
22389d
When you create a new application package, you need to create a page
22389d
module for it and instantiate the Layout class provided inside it.
22389d
Later, the following functions must be created: page_content(),
22389d
page_navibar() and main(). These functions are used to define the
22389d
content of your application and its navigation, as well. Both
22389d
application content and application navigation are logically organized
22389d
using variables passed through the URL.
22389d
22389d
Application
22389d
===========
22389d
22389d
URL variable: app
22389d
22389d
This variable contains the application id. It is a unique numerical
22389d
value that starts on 0 and increments one for each new application
22389d
that might be added. The application identified by number 0 is the one
22389d
used as default when no other application is provided.  The
22389d
application identified by number 0 is added to database the first time
22389d
it is created as part of the initial configuration process.
22389d
22389d
Application is the highest level of organization inside
22389d
`centos-web.cgi' script. Inside applications, it is defined pages and
22389d
contents. In other words, both pages and contents are specific to
22389d
applications.
22389d
22389d
Pages
22389d
=====
22389d
22389d
URL variable: page
22389d
22389d
This variable contains the page id. It is a unique numerical value
22389d
that starts on 0 and increments in one for each new page added to the
22389d
application. In contrast to applications, the page identified by
22389d
number 0 is not used as default page when no other page is provided.
22389d
This configuration is specific to each application and can be
22389d
customized inside each application individually. Generally, when a
22389d
page variable isn't passed through the URL, the application module
22389d
uses the `content_list()' method to display a list of all
22389d
related contents while links to pages are displayed in the related
22389d
navigation bar in order for users to access them.  The unique
22389d
numerical value of pages is specific to each application, so there is
22389d
one page 0 for each application available. No page is added to
22389d
database the first time the database is created as part of the initial
22389d
configuration process.
22389d
22389d
Pages contain similar information to that described by contents with
22389d
few exceptions. Pages, in contrast to contents, can differentiate the
22389d
page title from the page name. The page title goes in the page content
22389d
itself and describes what the page is about with a phrase. On the
22389d
other hand, the page name is generaly one word describing the page
22389d
content and is used as link on the related application navigation bar.
22389d
When no page name is explicitly provided, the first word of page title
22389d
is used instead.
22389d
    
22389d
Pages are always accessible inside the same application while contents
22389d
aren't.  Pages are permanently visible and linkend from each
22389d
application specific navigation bar.  This kind of pages can be
22389d
managed by editors or administrators and can be marked as `draft' to
22389d
put it on a special state where it is possible for editors and authors
22389d
to work on it, but impossible for others to read it until the page be
22389d
marked as `published' by either the page author or any members of
22389d
editor's or administrator's groups.
22389d
22389d
Pages can be converted to contents and the oposite. When convertion
22389d
occurs, unused information looses its meaning and is kept for
22389d
informative purpose, specially in situations when it might be needed
22389d
to realize a convertion back into the former state. Notice that in
22389d
order to realize such a backward and forward convertion it is required
22389d
that both pages and entires share the same definition structure.  In
22389d
fact, that be the same thing, but able to differentiate themselves
22389d
either as page or entry (e.g., through `type' field.).
22389d
22389d
Pages content is under version controlled. When a page (or entry) is
22389d
changed, a verification is performed to determine whether the
22389d
information entered in edition matches the last record in the page
22389d
history table. When both the information coming from edition and the
22389d
last record in the page history table are the same (e.g., no change
22389d
happens) the edition action is cancelled and a message is printed out
22389d
to notify the action.  Otherwise, when the information entered in
22389d
edition differs from the last record in the page history table, the
22389d
information comming from edition passes to be the last record in the
22389d
page history table.  In case, a page be reverted to a revision
22389d
different to that one being currently the active page, the reverted
22389d
revision becomes the active page (e.g., by changing a `status' field
22389d
from `false' to `true' in the history table).
22389d
22389d
Categories
22389d
==========
22389d
22389d
Categories exists to organize contents. When an entry is created it is
22389d
automatically linked to a category. Categories are managed by
22389d
administrators and editors only. Categories can be nested one another
22389d
and provide another way of finding information inside the web
22389d
environment.  Categories are specific to each web application, just as
22389d
contents and pages are. The `Unknown' category is created when the
22389d
categories table is created for first time, as part of the initial
22389d
configuration process so if no explicit category assignation is set by
22389d
the user, a default value (the `Unknown' category in this case) is
22389d
used to satisfy the connection between contents and categories.
22389d
22389d
Referential integrity
22389d
=====================
22389d
22389d
Referential integrity is not handle in the logic layer provided by
22389d
this module, but set inside the database system used to store the
22389d
information handled by this module. The most we do about it here, is
22389d
to display a confirmation message before committing such actions, so
22389d
you can be aware of them.
22389d
22389d
"""
cc10e6
cc10e6
import cgi
cc10e6
import cgitb; cgitb.enable()
8f6ee0
8c93bb
from Apps import xhtml
8c93bb
8c93bb
class Layout(xhtml.Strict):
22389d
    """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)):
22389d
            output = self.tag_span('', [0,0], str(names[i]))
22389d
            output = self.tag_a(attrs[i], [16,1], output)
8f6ee0
            if str(names[i]).lower() == focus.lower():
22389d
                output = self.tag_span({'class': 'current'}, [12,1], output, 1)
8f6ee0
            else:
22389d
                output = self.tag_span('', [12,1], output, 1)
22389d
            navibar_tabs += output
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')
22389d
        rsslink = self.tag_a({'href': 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
        """
22389d
        last_visit = self.tag_a({'href': 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?')
22389d
        attrs.append({'href': self.qs_args({'app':'', 'p':'lostpwd'})})
8f6ee0
        names.append('Register')
22389d
        attrs.append({'href': self.qs_args({'app':'', 'p':'register'})})
8f6ee0
        names.append('Login')
22389d
        attrs.append({'href': 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:
22389d
                output = self.tag_span({'class':'last'}, [16,1], self.tag_a(attrs[i], [20, 1], names[i]), 1)
8f6ee0
            else:
22389d
                output = self.tag_span('', [16,1], self.tag_a(attrs[i], [20, 1], names[i], 0), 1)
22389d
            links += output
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
22389d
        return '/centos-web/' + output
e1d4e1
e1d4e1
22389d
    def searchform(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)
22389d
        action = self.tag_dl({'class':'search'}, [16,1], action, 1)
22389d
22389d
        return self.tag_form({'action': self.qs_args({'app':'', 'p':'search'}), 
22389d
                              'method':'post', 'title':'Search'},
22389d
                              [12,1], action, 1)
e1d4e1
        
e1d4e1
22389d
    def content_resumen(self, attrs, id, title, user_id, commit_date,
22389d
                        update_date, category_id, comments, abstract):
22389d
        """Returns content resumen.
d59f66
22389d
        The content resumen is used to build the list of contents
22389d
        output by `content_list()'. The content resumen pretends to be
22389d
        concise and informative so the user can grab an idea what the
22389d
        content is about. The content resumen is made of the following
22389d
        information:
d59f66
22389d
            attrs: A dictionary discribing the rows style.  This is
22389d
                useful to alternate the row background colors.
e1d4e1
22389d
            id: A unique numerical value referring the content
22389d
                identification. This is the value used on
e1d4e1
                administrative tasks like updating and deleting.
e1d4e1
        
22389d
            title: A few words phrase describing the content,
22389d
                up to 255 characters.
e1d4e1
            
22389d
            author_id: A string referring the user email address, as
22389d
                specified by RFC2822. The user email address is used
22389d
                as id inside The CentOS User LDAP server, where user
22389d
                specific information (e.g., surname, lastname, office,
22389d
                phone, etc.) are stored in. This is the field that
22389d
                bonds the user with the content he/she produces.
e1d4e1
            
22389d
            commit_date: A string referring the timestamp the content
22389d
                arrived to database for time.
e1d4e1
22389d
            update_date: A string representing the timestamp the
22389d
                content was updated/revised for last time.
e1d4e1
22389d
            category_id: A number refering the category id the
22389d
                content is attached to.
d59f66
22389d
            abstract: One paragraphs describing the content.  This
22389d
                information is used to build the page metadata
22389d
                information. When this value is not provided no
22389d
                abstract information is displayed in the page, but the
22389d
                <meta name="description".../> is built using article's
22389d
                first 255 characters.
e1d4e1
22389d
            comments: A number representing how many comments the
22389d
                content has received since it is in the database.
e1d4e1
22389d
        The content itself is not displayed in the resumen, but in
22389d
        `content_detailed()'.
e1d4e1
e1d4e1
        """
22389d
        title = self.tag_a({'href': self.qs_args({'app':'', 'p':'entry', 'id':id})}, [0,0], title)
d59f66
        title = self.tag_h3({'class': 'title'}, [20,1], title, 0)
22389d
        info = self.content_info(id, user_id, commit_date, update_date, category_id, comments, abstract)
22389d
        return self.tag_div(attrs, [16,1], title + info, 1)
22389d
22389d
22389d
    def pagination(self):
22389d
        """Return content pagination."""
22389d
        previous = self.tag_a({'href':''}, [12,1], 'Previous')
22389d
        previous = self.tag_span({'class':'previous'}, [12,1], previous, 1)
22389d
        next = self.tag_a({'href':''}, [12,1], 'Next')
22389d
        next = self.tag_span({'class':'next'}, [12,1], next, 1)
22389d
        return self.tag_div({'class':'pagination'}, [12,1], previous + next + self.separator(), 1)
22389d
22389d
22389d
    def content_info(self, content_id, user_id, commit_date, update_date, category_id, comments, abstract):
22389d
        """Return content information.
22389d
22389d
        The content information provides a reduced view of content so
22389d
        people can make themselves an idea of what the content talks
22389d
        about. The content information displays content's title,
22389d
        author, timestamp, related category, number of comments and an
22389d
        abstract of the whole content.
22389d
22389d
        """
22389d
        categories = []
22389d
        categories.append('Unknown')
22389d
        categories.append('Erratas')
22389d
        categories.append('Articles')
22389d
        categories.append('Events')
22389d
22389d
        if category_id <= len(categories):
22389d
            category_name = categories[category_id].capitalize()
22389d
        else:
22389d
            category_id = 0
22389d
            category_name = categories[category_id].capitalize()
22389d
22389d
        category_name = self.tag_a({'href': self.qs_args({'app':'', 'p':'categories', 'id':category_id})}, [0,0], category_name)
22389d
        category_name = self.tag_span({'class':'category'}, [24,1], category_name) 
22389d
22389d
        users = {}
22389d
        users['al@centos.org'] = 'Alain Reguera Delgado'
22389d
        users['ana@centos.org'] = 'Ana Tamara Reguera Gattorno'
22389d
        users['alina@centos.org'] = 'Alina Reguera Gattorno'
22389d
22389d
        if user_id in users.keys():
22389d
            user_name = self.tag_a({'href':'mailto:' + user_id}, [0,0], users[user_id])
22389d
            user_name = self.tag_span({'class':'author'}, [24,1], 'Written by ' + user_name)
22389d
d59f66
        if update_date != commit_date:
22389d
            date = self.tag_span({'class':'date'}, [24,1], update_date)
d59f66
        else:
22389d
            date = self.tag_span({'class':'date'}, [24,1], commit_date)
22389d
22389d
            
22389d
        comments_attrs = {'href': self.qs_args({'app':'', 'p':'entry', 'id':content_id}) + '#comments'}
d59f66
        if comments == 1:
22389d
            comments = self.tag_a(comments_attrs, [0,0], str(comments) + ' comment')
d59f66
        elif comments > 1:
22389d
            comments = self.tag_a(comments_attrs, [0,0], str(comments) + ' comments')
d59f66
        else:
d59f66
            comments = 'No comments'
22389d
        comments = self.tag_span({'class':'comment'}, [24,1], comments)
22389d
22389d
        abstract = self.tag_p({'class':'abstract'}, [24,1], abstract)
d59f66
22389d
        return self.tag_div({'class': 'info'}, [20,1], user_name + date + category_name + comments + abstract, 1)
d59f66
d59f66
22389d
    def content_list(self):
22389d
        """Return 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 = []
22389d
        rows.append([0, 'Introduction to CentOS Web Environment',
d59f66
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
d59f66
                    0,
22389d
                    0,
22389d
                    'This is the abstract paragrah of content. '*10])
22389d
        rows.append([1, 'Creating New Applications',
d59f66
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
22389d
                    2,
d59f66
                    1,
22389d
                    'This is the abstract paragrah of content. '*5])
22389d
        rows.append([2, 'Texinfo Documentation Backend',
d59f66
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
22389d
                    1,
d59f66
                    5,
22389d
                    'This is the abstract 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
22389d
            output += self.content_resumen(attrs, *row)
d59f66
22389d
        list = output + self.pagination() + self.separator()
22389d
        list = self.tag_div({'id':'content-list'}, [12,1], list, 1)
22389d
        actions = self.searchform(16) + self.categories() + self.archives()
22389d
        actions = self.tag_div({'id':'content-actions'}, [8,1], actions, 1)
d59f66
22389d
        return actions + list
d59f66
d59f66
22389d
    def content_details(self):
22389d
        """Return content details.
22389d
        
22389d
        The content detail is shown for contents and pages.
d59f66
        """
22389d
        output = ''
22389d
        rows = []
22389d
        rows.append([0, 'Introduction to CentOS Web Environment',
22389d
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
22389d
                    0,
22389d
                    0,
22389d
                    'This is the abstract paragrah of content. '*10,
22389d
                    'This is the first paragraph of content'*10 + "\n"
22389d
                    'This is the second paragraph of content'*20 +
22389d
                    "\n" + 'This is the third paragraph of content.'*10 + "\n"])
22389d
        rows.append([1, 'Creating New Applications',
22389d
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
22389d
                    2,
22389d
                    1,
22389d
                    'This is the abstract paragrah of content. '*5,
22389d
                    "This is the first paragraph of content\n\
22389d
                    This is the second paragraph of content.\n\
22389d
                    This is the third paragraph of content."])
22389d
        rows.append([2, 'Texinfo Documentation Backend',
22389d
                    'al@centos.org',
22389d
                    '2011-8-30 12:33:11', 
22389d
                    '2011-8-30 12:33:11', 
22389d
                    1,
22389d
                    5,
22389d
                    'This is the abstract paragrah of content. '*8,
22389d
                    "This is the first paragraph of content.\n\
22389d
                    This is the second paragraph of content.\n\
22389d
                    This is the third paragraph of content."])
d59f66
22389d
        if 'id' in self.qs:
22389d
            id = int(self.qs['id'][0])
22389d
            title = rows[id][1]
22389d
            email = rows[id][2]
22389d
            commit_date = rows[id][3]
22389d
            update_date = rows[id][4]
22389d
            category = rows[id][5]
22389d
            comments = rows[id][6]
22389d
            abstract = self.tag_p({}, [0,0], rows[id][7])
22389d
22389d
            output = self.tag_h1({'class':'title'}, [12,1], title)
22389d
            output += self.content_info(id, email, commit_date, update_date, category, comments, abstract)
22389d
            output += self.tag_p({}, [20,1], rows[id][8])
22389d
            output += self.comments()
22389d
22389d
        return self.tag_div({'id':'content-details'}, [12,1], output, 1)
d59f66
22389d
22389d
    def comments(self):
22389d
        """Returns content specific list of comments.
22389d
22389d
        """
22389d
        output = self.tag_a({'name':'comment'}, [0,0], 'Comments')
22389d
        output = self.tag_h2({'class':'title comments'}, [12,1], output, 0) 
22389d
22389d
        return output
d59f66
d59f66
d59f66
    def categories(self):
d59f66
        """Returns list of categories.
d59f66
        
d59f66
        """
22389d
        categories = ['Unknown', 'Articles', 'Erratas', 'Events']
d59f66
        dt = self.tag_dt({}, [12,1], 'Categories')
d59f66
        dd = ''
d59f66
        for id in range(len(categories)):
22389d
            category_attrs = {'href': self.qs_args({'app':'', 'p':'categories', 'id':id})}
22389d
            a = self.tag_a(category_attrs, [16,0], 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
22389d
        takes place through the `page_content()' function which does a
22389d
        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
22389d
        content list, `detail()' for a detailed view of content,
22389d
        etc.). Later, the `body' variable instantiated from this class
22389d
        is reset in the `main()' function with the value returned from
22389d
        `page_content()' so the desired content layout can be printed
22389d
        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
22389d
    def admonition(self, title='Note', subtitle="", body=""):
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
22389d
        body: Admonition's body. 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
22389d
        if body != '':
22389d
            body = str(body)
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)
22389d
            output = image + title + body + self.separator()
cffc47
        else:
cffc47
            attrs = {'class': 'admonition unknown'}
8c93bb
            title = self.tag_h3({'class': 'title'}, [16,1], title + subtitle, 1)
22389d
            output = title + body
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