|
|
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 |
# ------------------------------------------------------------------
|
|
|
92991f |
"""Support page construction.
|
|
|
22389d |
|
|
|
92991f |
The page construction 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
|
|
|
92991f |
module for it and instantiate the Layout class provided here inside
|
|
|
92991f |
it. Later, the following functions must be created: page_content(),
|
|
|
22389d |
page_navibar() and main(). These functions are used to define the
|
|
|
92991f |
content and navigation bar of your application. Both application
|
|
|
92991f |
content and application navigation are logically organized using
|
|
|
92991f |
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
|
|
|
92991f |
value that starts at 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
|
|
|
bd2a79 |
`webenv.cgi' script. Inside applications, there is content in form
|
|
|
92991f |
of pages and entries. Content can be grouped by categories.
|
|
|
22389d |
|
|
|
22389d |
Pages
|
|
|
22389d |
=====
|
|
|
22389d |
|
|
|
22389d |
URL variable: page
|
|
|
22389d |
|
|
|
22389d |
This variable contains the page id. It is a unique numerical value
|
|
|
92991f |
that starts at 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
|
|
|
92991f |
customized inside each application individually, using string values
|
|
|
92991f |
instead of numerical values when passing values to page variable.
|
|
|
92991f |
|
|
|
92991f |
Generally, when a page variable isn't passed through the URL, the
|
|
|
92991f |
application module uses the `content_list()' method from Layout class
|
|
|
92991f |
to display a list of all available content entries while links to
|
|
|
92991f |
content pages are displayed in the application navigation bar so users
|
|
|
92991f |
can access them. The unique numerical value of content pages is
|
|
|
92991f |
specific to each application, so there is one page 0 for each
|
|
|
92991f |
application available. No page is added to database the first time the
|
|
|
92991f |
database is created as part of the initial configuration process.
|
|
|
22389d |
|
|
|
22389d |
Pages contain similar information to that described by contents with
|
|
|
92991f |
few exceptions. Pages, in contrast to entries, 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
|
|
|
92991f |
content and is used as link on the application navigation bar. When
|
|
|
92991f |
no page name is explicitly provided, the first word of page title is
|
|
|
92991f |
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
|
|
|
92991f |
put it on a special state where it is possible for administrator,
|
|
|
92991f |
editors and authors to work on it, but impossible for others to read
|
|
|
92991f |
it until the page be marked as `published' by either the page author
|
|
|
92991f |
or any members of editor's or administrator's groups.
|
|
|
22389d |
|
|
|
92991f |
Pages can be converted to entires 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
|
|
|
92991f |
order to realize such a back and forth convertion it is required that
|
|
|
92991f |
both pages and entires share the same definition structure. In fact,
|
|
|
92991f |
that they be the same thing, but able to differentiate themselves
|
|
|
92991f |
either as page or entry (e.g., through a `type' field.).
|
|
|
22389d |
|
|
|
92991f |
Pages content is under version control. 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
|
|
|
92991f |
happened) 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()
|
|
|
7e82d6 |
import ConfigParser
|
|
|
8c93bb |
from Apps import xhtml
|
|
|
8c93bb |
|
|
|
7e82d6 |
config = ConfigParser()
|
|
|
147f1a |
qs = cgi.parse()
|
|
|
147f1a |
|
|
|
715121 |
def qs_args(names={}):
|
|
|
147f1a |
"""Returns query string arguments.
|
|
|
147f1a |
|
|
|
147f1a |
The query string arguments are used to build links dynamically
|
|
|
147f1a |
and, this way, to create a browsable and logically organized web
|
|
|
147f1a |
environment. Such a construction generally needs to retrive some
|
|
|
147f1a |
of the values previously passed to the query string and add new
|
|
|
147f1a |
ones to it.
|
|
|
147f1a |
|
|
|
147f1a |
names: A dictionary containing the variable name and value pair
|
|
|
147f1a |
used to build a new query string.
|
|
|
147f1a |
|
|
|
147f1a |
When a variable is provied without a value, then its value is
|
|
|
147f1a |
retrived from the current query string. If a value isn't found
|
|
|
147f1a |
there neither, then the variable is removed from the new query
|
|
|
147f1a |
string.
|
|
|
147f1a |
|
|
|
147f1a |
When a variable is provided with its value, then its value is used
|
|
|
147f1a |
to build the new query string.
|
|
|
147f1a |
|
|
|
147f1a |
"""
|
|
|
147f1a |
output = ''
|
|
|
147f1a |
|
|
|
147f1a |
names_keys = names.keys()
|
|
|
147f1a |
names_keys.sort()
|
|
|
715121 |
|
|
|
147f1a |
for key in names_keys:
|
|
|
147f1a |
if names[key] == '':
|
|
|
147f1a |
if key in qs:
|
|
|
147f1a |
names[key] = qs[key][0]
|
|
|
147f1a |
else:
|
|
|
147f1a |
continue
|
|
|
147f1a |
if output == '':
|
|
|
147f1a |
output = '?'
|
|
|
147f1a |
else:
|
|
|
147f1a |
output += '&'
|
|
|
147f1a |
output += key + '=' + str(names[key])
|
|
|
147f1a |
|
|
|
7e82d6 |
return config.get('webserver', 'baseurl') + output
|
|
|
147f1a |
|
|
|
8c93bb |
class Layout(xhtml.Strict):
|
|
|
147f1a |
"""The Page Layout.
|
|
|
147f1a |
|
|
|
147f1a |
The page layout is made by combining XHTML tags in specific ways.
|
|
|
147f1a |
These specific combinations make the page components which in turn
|
|
|
147f1a |
can be also combined. Some of these components can be reused and
|
|
|
147f1a |
others don't. The goal of this class is to define what such
|
|
|
147f1a |
components are and describe them well in order to understand how
|
|
|
147f1a |
to use them from application modules when building XHTML documents
|
|
|
147f1a |
dynamically.
|
|
|
147f1a |
|
|
|
147f1a |
The page layout is initialized with a functional layout that can
|
|
|
147f1a |
be used as reference inside application modules, to create
|
|
|
147f1a |
variations of it. Generally, inside application packages, this
|
|
|
147f1a |
class is instantiated in a module named `page', variables are
|
|
|
147f1a |
reset and functions created in order to satisfy that application
|
|
|
147f1a |
needs. When you need to output one of the page components then you
|
|
|
147f1a |
use this class instantiated methods. When the method you need
|
|
|
147f1a |
doesn't exist in this class, then it is a good time for it to be
|
|
|
147f1a |
created, here ;).
|
|
|
147f1a |
|
|
|
147f1a |
Notice that most methods defined in this class make direct use of
|
|
|
147f1a |
methods defined by Strict class inside the `xhtml' module. The
|
|
|
147f1a |
Strict class inside xhtml module is inherited inside this class so
|
|
|
147f1a |
all the methods there are also available here. Methods which
|
|
|
147f1a |
doesn't make a direct use of Strict methods are dependencies of
|
|
|
147f1a |
those which do make direct use of Strict methods.
|
|
|
147f1a |
|
|
|
147f1a |
"""
|
|
|
8f6ee0 |
|
|
|
8f6ee0 |
def __init__(self):
|
|
|
8f6ee0 |
"""Initialize page data."""
|
|
|
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()
|
|
|
147f1a |
self.header += self.google_ad()
|
|
|
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 |
|
|
|
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'})
|
|
|
bd2a79 |
attrs.append({'title': 'Community Enterprise Operating System', 'href': '/webenv/'})
|
|
|
7e82d6 |
attrs.append({'src': config.get('webserver','baseurl') + 'public/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 |
|
|
|
147f1a |
def google_ad_example(self):
|
|
|
147f1a |
"""Returns Google advertisement for offline testings."""
|
|
|
92991f |
title = 'Google Advertisement'
|
|
|
7e82d6 |
url = config.get('webserver','baseurl') + 'public/images/ads-sample-468x60.png'
|
|
|
92991f |
image = self.tag_img({'src': url, 'alt': title}, [0,0])
|
|
|
92991f |
link = self.tag_a({'href': url, 'title': title}, [12,1], image)
|
|
|
92991f |
output = self.tag_div({'class':'google-ad'}, [8,1], link, 1)
|
|
|
147f1a |
output += self.separator({'class':'page-line'}, [8,1])
|
|
|
147f1a |
|
|
|
147f1a |
return output
|
|
|
147f1a |
|
|
|
147f1a |
def google_ad(self):
|
|
|
147f1a |
"""Returns Google advertisement for online using."""
|
|
|
147f1a |
|
|
|
147f1a |
properties = {}
|
|
|
147f1a |
properties['google_ad_client'] = 'pub-6973128787810819'
|
|
|
147f1a |
properties['google_ad_width'] = '468'
|
|
|
147f1a |
properties['google_ad_height'] = '60'
|
|
|
147f1a |
properties['google_ad_format'] = '468x60_as'
|
|
|
147f1a |
properties['google_ad_type'] = 'text_image'
|
|
|
147f1a |
properties['google_ad_channel'] = ''
|
|
|
147f1a |
properties['google_color_border'] = '204c8d'
|
|
|
147f1a |
properties['google_color_bg'] = '345c97'
|
|
|
147f1a |
properties['google_color_link'] = '0000FF'
|
|
|
147f1a |
properties['google_color_text'] = 'FFFFFF'
|
|
|
147f1a |
properties['google_color_url'] = '008000'
|
|
|
147f1a |
|
|
|
147f1a |
attrs = {}
|
|
|
147f1a |
attrs['type'] = "text/javascript"
|
|
|
147f1a |
|
|
|
147f1a |
output = '
|
|
|
147f1a |
for key, value in properties.iteritems():
|
|
|
147f1a |
output += ' '*16 + key + '="' + value + '";\n'
|
|
|
147f1a |
output += ' '*16 + '//-->\n'
|
|
|
147f1a |
|
|
|
147f1a |
properties = self.tag_script(attrs, [12,1], output, 1)
|
|
|
147f1a |
|
|
|
147f1a |
attrs['src'] = "http://pagead2.googlesyndication.com/pagead/show_ads.js"
|
|
|
147f1a |
|
|
|
147f1a |
source = self.tag_script(attrs, [12,1], ' ', 0)
|
|
|
147f1a |
|
|
|
147f1a |
output = self.tag_div({'class':'google-ad'}, [8,1], properties + source, 1)
|
|
|
147f1a |
output += self.separator({'class':'page-line'}, [8,1])
|
|
|
147f1a |
|
|
|
8f6ee0 |
return output
|
|
|
cc10e6 |
|
|
|
e1d4e1 |
def navibar(self):
|
|
|
92991f |
"""Returns webenv navigation bar.
|
|
|
cc10e6 |
|
|
|
92991f |
The webenv 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
|
|
|
92991f |
application the user be visiting (e.g., Wiki, Lists, Forums,
|
|
|
92991f |
Projects, Bugs, Docs, Downloads and Sponsors.). Notice that
|
|
|
bd2a79 |
some of these web applications are out of `webenv.cgi'
|
|
|
92991f |
scope and they need to code their own webenv navigation bars
|
|
|
bd2a79 |
in a way that coincide the one set by `webenv.cgi'.
|
|
|
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':
|
|
|
bd2a79 |
attrs.append({'href': '/webenv/'})
|
|
|
8c93bb |
else:
|
|
|
bd2a79 |
attrs.append({'href': '/webenv/?app=' + names[i].lower()})
|
|
|
8f6ee0 |
|
|
|
8c93bb |
tabs = self.navibar_tabs(names, attrs, focus)
|
|
|
8c93bb |
tabs += self.separator()
|
|
|
8f6ee0 |
|
|
|
8f6ee0 |
return tabs
|
|
|
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 |
|
|
|
147f1a |
def releases(self):
|
|
|
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 |
|
|
|
147f1a |
names = []
|
|
|
147f1a |
names.append('6.0')
|
|
|
147f1a |
|
|
|
147f1a |
attrs = []
|
|
|
147f1a |
attrs.append({'href': qs_args({'p':'releases', 'id': 6.0})})
|
|
|
147f1a |
|
|
|
147f1a |
|
|
|
147f1a |
title = self.tag_a({'href': qs_args({'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')
|
|
|
147f1a |
rsslink = self.tag_a({'href': 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 |
|
|
|
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 |
"""
|
|
|
147f1a |
last_visit = self.tag_a({'href': qs_args({'app':'', 'p':'logs'})}, [0,0], 'Logs')
|
|
|
e1d4e1 |
return self.tag_div({'class': 'logs'}, [12, 1], last_visit, 1)
|
|
|
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?')
|
|
|
147f1a |
attrs.append({'href': qs_args({'app':'', 'p':'lostpwd'})})
|
|
|
8f6ee0 |
names.append('Register')
|
|
|
147f1a |
attrs.append({'href': qs_args({'app':'', 'p':'register'})})
|
|
|
8f6ee0 |
names.append('Login')
|
|
|
147f1a |
attrs.append({'href': 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 |
|
|
|
bd2a79 |
def user_links_trails(self, names=['None'], attrs=[{'href': '/webenv/'}]):
|
|
|
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
|
|
|
bd2a79 |
be implemeneted inside `webenv.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 |
|
|
|
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 |
|
|
|
bd2a79 |
def page_navibar(self, names=['Welcome'], attrs=[{'href':'/webenv/?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
|
|
|
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 |
|
|
|
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 |
|
|
|
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)
|
|
|
7e82d6 |
metadata += self.tag_link({'href': config.get('webserver','baseurl') + 'public/stylesheet.css','rel': 'stylesheet', 'type': 'text/css'}, [4,0])
|
|
|
7e82d6 |
metadata += self.tag_link({'href': config.get('webserver','baseurl') + 'public/centos-fav.png', 'rel': 'shortcut icon', 'type': 'image/png'}, [4,1])
|
|
|
cc10e6 |
|
|
|
8c93bb |
return self.tag_head('', [0,1], metadata)
|
|
|
cc10e6 |
|
|
|
147f1a |
def searchform(self, size=15):
|
|
|
3694e5 |
"""Returns search form.
|
|
|
d59f66 |
|
|
|
147f1a |
The search form redirects user from the current page onto the
|
|
|
147f1a |
search page, where the keywords previously introduced in the
|
|
|
147f1a |
input field are processed then.
|
|
|
3694e5 |
|
|
|
147f1a |
size: A number discribing how large the search box is.
|
|
|
e1d4e1 |
|
|
|
3694e5 |
"""
|
|
|
147f1a |
input = self.tag_input({'type':'text', 'value':'', 'size':size}, [0,0])
|
|
|
e1d4e1 |
|
|
|
147f1a |
action = self.tag_dt({}, [20,1], 'Search')
|
|
|
d59f66 |
action += self.tag_dd({}, [20,1], input)
|
|
|
22389d |
action = self.tag_dl({'class':'search'}, [16,1], action, 1)
|
|
|
22389d |
|
|
|
147f1a |
return self.tag_form({'action': qs_args({'app':'', 'p':'search'}),
|
|
|
22389d |
'method':'post', 'title':'Search'},
|
|
|
22389d |
[12,1], action, 1)
|
|
|
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 |
|
|
|
147f1a |
The content resumen is used to build the list of contents,
|
|
|
147f1a |
output by `content_list()' method. The content resumen intends
|
|
|
147f1a |
to be concise and informative so the user can grab a general
|
|
|
147f1a |
idea about the related content and what it is about.
|
|
|
d59f66 |
|
|
|
147f1a |
attrs: A dictionary discribing the rows style. This is useful
|
|
|
147f1a |
to alternate the row background colors.
|
|
|
e1d4e1 |
|
|
|
147f1a |
id: A unique numerical value referring the content
|
|
|
147f1a |
identification. This is the value used on administrative
|
|
|
147f1a |
tasks like updating and deleting.
|
|
|
e1d4e1 |
|
|
|
147f1a |
title: A few words phrase describing the content, up to 255
|
|
|
147f1a |
characters.
|
|
|
e1d4e1 |
|
|
|
147f1a |
author_id: A string referring the user email address, as
|
|
|
147f1a |
specified by RFC2822. The user email address is used as id
|
|
|
147f1a |
inside The CentOS User LDAP server, where user specific
|
|
|
147f1a |
information (e.g., surname, lastname, office, phone, etc.)
|
|
|
147f1a |
are stored in. This is the field that bonds the user with
|
|
|
147f1a |
the content he/she produces.
|
|
|
e1d4e1 |
|
|
|
147f1a |
commit_date: A string referring the timestamp the content
|
|
|
147f1a |
arrived to database for time.
|
|
|
e1d4e1 |
|
|
|
147f1a |
update_date: A string representing the timestamp the content
|
|
|
147f1a |
was updated/revised for last time.
|
|
|
e1d4e1 |
|
|
|
147f1a |
category_id: A number refering the category id the content is
|
|
|
147f1a |
attached to.
|
|
|
d59f66 |
|
|
|
147f1a |
abstract: One paragraphs describing the content. This
|
|
|
147f1a |
information is used to build the page metadata
|
|
|
147f1a |
information. When this value is not provided no abstract
|
|
|
147f1a |
information is displayed in the page, but the
|
|
|
147f1a |
name="description".../> is built using article's first 255
|
|
|
147f1a |
characters.
|
|
|
e1d4e1 |
|
|
|
147f1a |
comments: A number representing how many comments the content
|
|
|
147f1a |
has received since it is in the database.
|
|
|
e1d4e1 |
|
|
|
22389d |
The content itself is not displayed in the resumen, but in
|
|
|
147f1a |
`content_details()'.
|
|
|
e1d4e1 |
|
|
|
e1d4e1 |
"""
|
|
|
147f1a |
title = self.tag_a({'href': qs_args({'app':'', 'p':'entry', 'id':id})}, [0,0], title)
|
|
|
d59f66 |
title = self.tag_h3({'class': 'title'}, [20,1], title, 0)
|
|
|
147f1a |
info = self.content_info(id, user_id, commit_date,
|
|
|
147f1a |
update_date, category_id, comments,
|
|
|
147f1a |
abstract)
|
|
|
22389d |
return self.tag_div(attrs, [16,1], title + info, 1)
|
|
|
22389d |
|
|
|
22389d |
def pagination(self):
|
|
|
22389d |
"""Return content pagination."""
|
|
|
147f1a |
previous = self.tag_a({'href':''}, [0,0], 'Previous')
|
|
|
147f1a |
previous = self.tag_span({'class':'previous'}, [20,1], previous)
|
|
|
147f1a |
next = self.tag_a({'href':''}, [0,0], 'Next')
|
|
|
147f1a |
next = self.tag_span({'class':'next'}, [20,1], next)
|
|
|
147f1a |
separator = self.separator({'class':'page-line'}, [20,1])
|
|
|
147f1a |
return self.tag_div({'class':'pagination'}, [16,1], previous +
|
|
|
147f1a |
next + separator, 1)
|
|
|
22389d |
|
|
|
147f1a |
def content_info(self, content_id, user_id, commit_date,
|
|
|
147f1a |
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 |
|
|
|
147f1a |
category_name = self.tag_a({'href': 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 |
|
|
|
147f1a |
comments_attrs = {'href': 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 |
|
|
|
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)
|
|
|
147f1a |
actions = self.searchform() + self.categories() + self.archives()
|
|
|
22389d |
actions = self.tag_div({'id':'content-actions'}, [8,1], actions, 1)
|
|
|
d59f66 |
|
|
|
22389d |
return actions + list
|
|
|
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 |
|
|
|
147f1a |
if 'id' in qs:
|
|
|
147f1a |
id = int(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 |
def comments(self):
|
|
|
22389d |
"""Returns content specific list of comments.
|
|
|
22389d |
|
|
|
22389d |
"""
|
|
|
466e28 |
output = self.tag_a({'name':'comments'}, [0,0], 'Comments')
|
|
|
22389d |
output = self.tag_h2({'class':'title comments'}, [12,1], output, 0)
|
|
|
22389d |
|
|
|
22389d |
return output
|
|
|
d59f66 |
|
|
|
d59f66 |
def categories(self):
|
|
|
d59f66 |
"""Returns list of categories.
|
|
|
d59f66 |
|
|
|
d59f66 |
"""
|
|
|
22389d |
categories = ['Unknown', 'Articles', 'Erratas', 'Events']
|
|
|
147f1a |
dt = self.tag_dt({}, [16,1], 'Categories')
|
|
|
d59f66 |
dd = ''
|
|
|
d59f66 |
for id in range(len(categories)):
|
|
|
147f1a |
category_attrs = {'href': qs_args({'app':'', 'p':'categories', 'id':id})}
|
|
|
147f1a |
a = self.tag_a(category_attrs, [0,0], categories[id] + ' (0)')
|
|
|
147f1a |
dd += self.tag_dd({}, [16,1], a)
|
|
|
d59f66 |
|
|
|
147f1a |
return self.tag_dl({},[12,1], dt + dd, 1)
|
|
|
d59f66 |
|
|
|
d59f66 |
def archives(self):
|
|
|
d59f66 |
"""Returns archives."""
|
|
|
d59f66 |
archives = {}
|
|
|
d59f66 |
archives['2011'] = ['January', 'February', 'March', 'April', 'May']
|
|
|
d59f66 |
archives['2010'] = ['January', 'February']
|
|
|
d59f66 |
|
|
|
147f1a |
dt = self.tag_dt({}, [16,1], 'Archives')
|
|
|
d59f66 |
year_dl = ''
|
|
|
d59f66 |
year_dd = ''
|
|
|
d59f66 |
|
|
|
d59f66 |
for key in archives.keys():
|
|
|
147f1a |
year_dt = self.tag_dt({},[20,1], key)
|
|
|
d59f66 |
for id in range(len(archives[key])):
|
|
|
147f1a |
a = self.tag_a({'href': qs_args({'app':'', 'p':'archives', 'year': key, 'month': id + 1})}, [0,0], archives[key][id] + ' (0)')
|
|
|
147f1a |
year_dd += self.tag_dd({}, [20,1], a)
|
|
|
147f1a |
year_dl += self.tag_dl({'class':'year'}, [16,1], year_dt + year_dd, 1)
|
|
|
d59f66 |
year_dd = ''
|
|
|
d59f66 |
|
|
|
147f1a |
return self.tag_dl({},[12,1], dt + year_dl, 1)
|
|
|
e1d4e1 |
|
|
|
e1d4e1 |
def page_top(self):
|
|
|
e1d4e1 |
"""Returns page top anchor."""
|
|
|
e1d4e1 |
return self.tag_a({'name':'top'}, [0,1])
|
|
|
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 |
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 |
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 |
def page_footer(self):
|
|
|
e1d4e1 |
"""Retruns page footer."""
|
|
|
e1d4e1 |
return self.tag_div({'id': 'page-footer'}, [4,1], self.credits(), 1)
|
|
|
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 |
|
|
|
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()}
|
|
|
7e82d6 |
image = self.tag_img({'src': config.get('webserver','baseurl') + 'public/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 |
|
|
|
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()))
|
|
|
7e82d6 |
credits = self.tag_img({'src': config.get('webserver','baseurl') + 'public/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 |
|
|
|
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
|