This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/web/tools.py

1044 lines
34 KiB
Python

# -*- coding: iso-8859-15 -*-
# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
# Romain Chantereau <rchantereau@entrouvert.com>
# Nicolas Clapiès <nclapies@easter-eggs.org>
# Pierre-Antoine Dejace <padejace@entrouvert.be>
# Thierry Dulieu <tdulieu@easter-eggs.com>
# Florent Monnier <monnier@codelutin.com>
# Cédric Musso <cmusso@easter-eggs.org>
# Frédéric Péters <fpeters@entrouvert.be>
# Benjamin Poussin <poussin@codelutin.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Régnier <regnier@codelutin.com>
# Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
# Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
# Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import nested_scopes
# lambdas are so much fun with nested scopes...
__doc__ = """Glasnost Web Tools"""
__version__ = '$Revision$'[11:-2]
import base64
import commands
import copy
import Cookie
from cStringIO import StringIO
import imp
import locale
import os
import posix
import re
from stat import *
import sys
import time
import traceback
import types
import urllib
import warnings
try:
from mod_python import apache
except ImportError:
apache = None
# zLOG is used by TAL that considers Zope is always there. How wrong.
sys.modules['zLOG'] = imp.new_module('zLOG')
import glasnost.common.context as context
import glasnost.common.faults as faults
import glasnost.common.tools_new as commonTools
import glasnost.common.parsers as parsers
import glasnost.common.xhtmlgenerator as X
from glasnost.proxy.tools import *
import glasnost.web.tools_new as webTools
Fault = faults.Fault
_cachedWebsByServerRole = {}
currentTemplates = {}
HTTP_CONTINUE = 100
HTTP_SWITCHING_PROTOCOLS = 101
HTTP_PROCESSING = 102
HTTP_OK = 200
HTTP_CREATED = 201
HTTP_ACCEPTED = 202
HTTP_NON_AUTHORITATIVE = 203
HTTP_NO_CONTENT = 204
HTTP_RESET_CONTENT = 205
HTTP_PARTIAL_CONTENT = 206
HTTP_MULTI_STATUS = 207
HTTP_MULTIPLE_CHOICES = 300
HTTP_MOVED_PERMANENTLY = 301
HTTP_MOVED_TEMPORARILY = 302
HTTP_SEE_OTHER = 303
HTTP_NOT_MODIFIED = 304
HTTP_USE_PROXY = 305
HTTP_TEMPORARY_REDIRECT = 307
HTTP_BAD_REQUEST = 400
HTTP_UNAUTHORIZED = 401
HTTP_PAYMENT_REQUIRED = 402
HTTP_FORBIDDEN = 403
HTTP_NOT_FOUND = 404
HTTP_METHOD_NOT_ALLOWED = 405
HTTP_NOT_ACCEPTABLE = 406
HTTP_PROXY_AUTHENTICATION_REQUIRED= 407
HTTP_REQUEST_TIME_OUT = 408
HTTP_CONFLICT = 409
HTTP_GONE = 410
HTTP_LENGTH_REQUIRED = 411
HTTP_PRECONDITION_FAILED = 412
HTTP_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_REQUEST_URI_TOO_LARGE = 414
HTTP_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_RANGE_NOT_SATISFIABLE = 416
HTTP_EXPECTATION_FAILED = 417
HTTP_UNPROCESSABLE_ENTITY = 422
HTTP_LOCKED = 423
HTTP_FAILED_DEPENDENCY = 424
HTTP_INTERNAL_SERVER_ERROR = 500
HTTP_NOT_IMPLEMENTED = 501
HTTP_BAD_GATEWAY = 502
HTTP_SERVICE_UNAVAILABLE = 503
HTTP_GATEWAY_TIME_OUT = 504
HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_VARIANT_ALSO_VARIES = 506
HTTP_INSUFFICIENT_STORAGE = 507
HTTP_NOT_EXTENDED = 510
OK = 0
def accessForbidden(dontAskForLogin = 0):
userToken = context.getVar('userToken', default = '')
if userToken or dontAskForLogin:
layout = X.array()
# TODO: nice text telling the user he should move away...
return writePageLayout(layout, _('Access Forbidden!'), canCache = 0)
cleanedUpUri = cleanUpUnparsedUri([], 'http')
cleanedUpUri = cleanedUpUri.replace('/people/submit', '/').replace(
'/index.py', '/')
loginUrl = X.roleUrl('login').add('nextUri', cleanedUpUri).add(
'access', 'forbidden')
hostNameAndPort = commonTools.makeHttpHostNameAndPort(
context.getVar('httpHostName'),
context.getVar('httpPort'))
if context.getVar('virtualHost').useHTTPS:
loginUrl = 'https://%(hostNameAndPort)s%(nameAndQuery)s' % {
'hostNameAndPort': hostNameAndPort,
'nameAndQuery': loginUrl,
}
return redirect(loginUrl)
def appendToUri(uri, item):
if '?' in uri:
separator = '&'
else:
separator = '?'
return uri + separator + item
def cleanUpUnparsedUri(trashList, protocol = None):
hostNameAndPort = commonTools.makeHttpHostNameAndPort(
context.getVar('httpHostName'),
context.getVar('httpPort'))
pathAndQuery = commonTools.makeHttpPathAndQuery(
context.getVar('httpPath'),
context.getVar('httpQuery'))
if pathAndQuery is None:
pathAndQuery = ''
if not hostNameAndPort:
uri = pathAndQuery
elif protocol == 'http':
uri = 'http://%(hostNameAndPort)s%(pathAndQuery)s' % {
'hostNameAndPort': hostNameAndPort,
'pathAndQuery': pathAndQuery,
}
elif protocol == 'https':
uri = 'https://%(hostNameAndPort)s%(pathAndQuery)s' % {
'hostNameAndPort': hostNameAndPort,
'pathAndQuery': pathAndQuery,
}
else:
uri = pathAndQuery
return cleanUpUri(uri, trashList)
def cleanUpUri(uri, trashList):
for s in trashList:
s = s + '='
i = uri.find(s)
if i >= 0:
j = uri.find('&', i + len(s))
if j < 0:
s = uri[i:]
if i > 0:
c = uri[i - 1]
if c in ['?', '&']:
uri = uri[:i - 1]
else:
uri = uri[:i] + uri[j + 1:]
return uri
def confirmDelete(deleteUrl, id = None, objectName = None):
backUrl = copy.copy(deleteUrl)
backUrl.action = None # Remove the 'delete' action.
if objectName is None:
objectName = N_('entry')
if not id:
message = _('Are you sure you want to delete the %s?') % _(objectName)
else:
message = _('Are you sure you want to delete the %s "%s" ?') % (
_(objectName),
getObjectLabelTranslated(id,
context.getVar('readLanguages')) )
layout = X.array()
layout += X.p(_class = 'alert')(message)
layout += X.buttonStandalone('delete', deleteUrl)
layout += X.buttonStandalone('cancel', backUrl)
return writePageLayout(layout, _('Confirm Deletion'), canCache = 0)
def failure(message, url):
layout = X.array()
layout += X.p(_class = 'alert')(message)
layout += X.buttonStandalone('ok', url)
return writePageLayout(layout, _('Failure'), canCache = 0)
def getDefaultDispatcherHostNameAndPort():
return context.getVar('defaultDispatcherId')[11:]
def getObject(objectId):
web = getWeb(objectId)
return web.getObject(objectId)
def getWeb(id):
context.push(dispatcherId = commonTools.extractDispatcherId(id))
try:
web = getWebForServerRole(commonTools.extractRole(id))
finally:
context.pull()
return web
def getWebForServerRole(serverRole):
# FIXME: We should clear _cachedWebsByServerRole when userToken value
# changes. Because different users can use the same process, or a user can
# login or logout.
if context.getVar('knownRoles') and \
not serverRole in context.getVar('knownRoles'):
return None
virtualHost = context.getVar('virtualHost')
if virtualHost is not None and virtualHost.customWebs and \
serverRole in virtualHost.customWebs.keys():
serverRoleWeb = virtualHost.customWebs[serverRole].lower()
else:
serverRoleWeb = serverRole
serverRoleWeb = serverRoleWeb.replace('-', '')
if _cachedWebsByServerRole.has_key(serverRoleWeb):
return _cachedWebsByServerRole[serverRoleWeb]
# Code inspired from the module knee.
import glasnost.web
webFileNames = os.listdir(glasnost.web.__path__[0])
for webFileName in webFileNames:
if webFileName.endswith('Web.py') \
and webFileName[:-6].lower() == serverRoleWeb:
webName = webFileName[:-3]
if hasattr(glasnost.web, webName):
module = getattr(glasnost.web, webName)
else:
moduleTriplet = imp.find_module(
webName, glasnost.web.__path__)
try:
module = imp.load_module(
'glasnost.web.%s' % webName, moduleTriplet[0],
moduleTriplet[1], moduleTriplet[2])
finally:
if moduleTriplet[0]:
moduleTriplet[0].close()
setattr(glasnost.web, webName, module)
web = module.__dict__[webName]()
break
else:
web = getProxyForServerRole(serverRole)
_cachedWebsByServerRole[serverRoleWeb] = web
return _cachedWebsByServerRole[serverRoleWeb]
def isButtonSelected(buttonName, keywords):
selected = 0
if keywords.has_key('button') and keywords['button'] == buttonName \
or keywords.has_key(buttonName) and keywords[buttonName] \
or keywords.has_key(buttonName + '.x') and keywords[buttonName + '.x']:
selected = 1
if keywords.has_key('button') and keywords['button'] == buttonName:
del keywords['button']
if keywords.has_key(buttonName):
del keywords[buttonName]
if keywords.has_key(buttonName + '.x'):
del keywords[buttonName + '.x']
if keywords.has_key(buttonName + '.y'):
del keywords[buttonName + '.y']
return selected
def isTypeOfMimeType(mimeType, type):
if mimeType is None or len(mimeType) < len(type):
return 0
return mimeType.startswith(type)
def pageNotFound():
from mod_python import apache
raise apache.SERVER_RETURN, HTTP_NOT_FOUND
def redirect(url):
if type(url) == types.InstanceType and hasattr(url, 'getAsUrl'):
url = url.getAsUrl()
else:
url = str(url)
req = context.getVar('req')
req.err_headers_out['Location'] = url
setHttpCookie()
req.status = HTTP_MOVED_TEMPORARILY
req.send_http_header()
return OK
def redirectPermanently(url):
if type(url) == types.InstanceType and hasattr(url, 'getAsUrl'):
url = url.getAsUrl()
else:
url = str(url)
req = context.getVar('req')
req.err_headers_out['location'] = url
req.status = HTTP_MOVED_PERMANENTLY
setHttpCookie()
req.send_http_header()
return OK
def rememberObject(id):
preferencesWeb = getWebForServerRole('preferences')
if not preferencesWeb:
return
preferencesWeb.rememberId(id)
def repairMimeType(fieldValue, fileName):
# FIXME: Integrate this function in uploadFiles object
# to correct mime type sent by the web browser.
def getFileExt(fileName):
return fileName[fileName.rfind('.')+1:]
mimeTypesByExt = {
'avi': 'video/x-msvideo',
'bz2': 'application/bzip2',
'doc': 'application/msword',
'mpeg': 'video/mpeg ',
'mpg': 'video/mpeg ',
'mov': 'video/quicktime',
'pdf': 'application/pdf',
'ppt': 'application/vnd.ms-powerpoint',
'ps': 'application/postscript',
'qt': 'video/quicktime',
'tar': 'application/x-tar',
'tex': 'application/tex',
'xls': 'application/vnd.ms-excel',
'xml': 'text/xml ',
'xsl': 'text/xml',
'Z': 'application/zoo',
}
if len(fieldValue) < 12:
return
if fieldValue[12:] == 'octet-stream':
ext = getFileExt()
raise str(ext) + ' - ' + fieldValue + ' - ' + fileName
if mimeTypesByExt.has_key(ext):
fieldValue = mimeTypesByExt[getFileExt()]
if fieldValue is not None:
return fielValue
return fieldValue
def replaceSpecialTags(text):
text = text.replace('[{glasnost:defaultdispatchernameandport}]',
getDefaultDispatcherHostNameAndPort())
text = text.replace(
'[{glasnost:localuri}]/',
context.getVar('httpScriptDirectoryPath'))
text = text.replace(
'[{glasnost:localuri}]',
context.getVar('httpScriptDirectoryPath'))
def aliasLabelReplacer(matchObject):
alias = matchObject.group('alias')
name = ''
if 'pagenames' in context.getVar('knownRoles'):
id = getProxyForServerRole('pagenames').getIdByName(alias)
if id:
try:
name = getObjectLabelTranslated(
id, context.getVar('readLanguages'))
except faults.MissingItem:
name = _('(missing item)')
name = name.replace('&', '&amp;')
name = name.replace('<', '&lt;')
name = name.replace('"', '&quot;')
return name
text = re.sub(
r'\[{glasnost:aliaslabel:(?P<host>.*?):(?P<alias>.*?)}\]',
aliasLabelReplacer, text)
def aliasReplacer(matchObject):
alias = matchObject.group('alias')
action = matchObject.group('action')
return X.aliasUrl(alias, action).getAsUrl()
text = re.sub(
r'\[{glasnost:alias:(?P<host>.*?):(?P<alias>.*?)(:(?P<action>.*?))?}\]',
aliasReplacer, text)
def imageReplacer(matchObject):
host = matchObject.group('host')
serverRole = matchObject.group('serverRole')
localId = matchObject.group('localId')
objectId = 'glasnost://%s/%s/%s' % (host, serverRole, localId)
web = getWebForServerRole(serverRole)
if web is None:
return ''
fileName = ''
try:
object = web.getPartialObject(objectId, ['dataFileName'])
if hasattr(object, 'dataFileName') and object.dataFileName:
fileName = '/%s' % object.dataFileName
except faults.Fault:
pass
return X.idUrl(objectId, 'image%s' % fileName).getAsUrl()
text = re.sub(
'\[\{glasnost\:image\:(?P<host>.*?)\:(?P<serverRole>.*?)\:(?P<localId>.*?)\}\]',
imageReplacer, text)
def labelReplacer(matchObject):
serverRole = matchObject.group('serverRole')
localId = matchObject.group('localId')
host = matchObject.group('host')
try:
name = getObjectLabelTranslated(
'glasnost://%s/%s/%s' % (
host, serverRole, localId),
context.getVar('readLanguages'))
except faults.MissingItem:
name = _('(missing item)')
name = name.replace('&', '&amp;')
name = name.replace('<', '&lt;')
name = name.replace('"', '&quot;')
return name
text = re.sub(
'\[\{glasnost\:label\:(?P<host>.*?)\:(?P<serverRole>.*?)\:(?P<localId>.*?)\}\]',
labelReplacer, text)
def partialIdReplacer(matchObject):
host = matchObject.group('host')
serverRole = matchObject.group('serverRole')
localId = matchObject.group('localId')
action = matchObject.group('action')
computedId = 'glasnost://%s/%s/%s' % (host, serverRole, localId)
return X.idUrl(computedId, action).getAsUrl()
text = re.sub(
r'\[{glasnost:partialid:(?P<host>.*?):(?P<serverRole>.*?):'\
'(?P<localId>.*?)(:(?P<action>.*))?}\]',
partialIdReplacer, text)
def thumbnailReplacer(matchObject):
host = matchObject.group('host')
serverRole = matchObject.group('serverRole')
localId = matchObject.group('localId')
width = matchObject.group('width')
height = matchObject.group('height')
web = getWebForServerRole(serverRole)
if web is None:
return ''
return X.roleUrl(web.serverRole, 'thumbnail').add(
'id',
'glasnost://%s/%s/%s' % (host, serverRole, localId)).add(
'width', width).add('height', height).getAsUrl()
text = re.sub(
'\[\{glasnost\:thumbnail\:(?P<host>.*?)\:(?P<serverRole>.*?)\:(?P<localId>.*?)'
'\:width=(?P<width>.*?)\:height=(?P<height>.*?)\}\]',
thumbnailReplacer, text)
canUseCookie = context.getVar('canUseCookie', default = 0)
tag = urllib.quote('[{glasnost:sessiontoken}]')
sessionToken = context.getVar('sessionToken')
if not canUseCookie and sessionToken is not None:
text = text.replace(tag, sessionToken)
else:
text = text.replace('?sessionToken=' + tag + '&', '?')
text = text.replace('?sessionToken=' + tag, '')
text = text.replace('&sessionToken=' + tag, '')
return text
def setHttpCookie():
if not context.getVar('canUseCookie', default = 0):
return
req = context.getVar('req')
oldCookie = context.getVar('cookie')
cookie = Cookie.SimpleCookie()
cookieContent = {}
sessionToken = context.getVar('sessionToken')
if sessionToken is not None:
cookieContent['sessionToken'] = sessionToken
for k, v in cookieContent.items():
cookie[k] = v
cookie[k]['path'] = '/'
if not cookieContent:
if oldCookie:
for k, v in oldCookie.items():
cookie[k] = v
cookie[k]['max-age'] = 0
else:
cookie = None
if cookie != oldCookie:
for morsel in cookie.values():
req.headers_out.add('Set-Cookie', morsel.output(header = '')[1:])
context.setVar('cookie', cookie)
def success(message, url):
layout = X.array()
layout += X.p(_class = 'alert')(message)
layout += X.buttonStandalone('ok', url)
return writePageLayout(layout, _('Success'), canCache = 0)
import WebAPI
def getTemplateVars():
userId = context.getVar('userId', default = '')
if userId:
userSet = [userId]
else:
userSet = None
if context.getVar('virtualHost').useHTTPS:
cleanedUpUri = cleanUpUnparsedUri([], 'https')
else:
cleanedUpUri = cleanUpUnparsedUri([])
cleanedUpUri = cleanedUpUri.replace('/people/submit', '/').replace(
'/index.py', '/')
loginUrl = X.roleUrl('login').add('nextUri', cleanedUpUri)
hostNameAndPort = commonTools.makeHttpHostNameAndPort(
context.getVar('httpHostName'),
context.getVar('httpPort'))
if context.getVar('virtualHost').useHTTPS:
loginUrl = 'https://%(hostNameAndPort)s%(nameAndQuery)s' % {
'hostNameAndPort': hostNameAndPort,
'nameAndQuery': loginUrl,
}
logoutUrl = X.roleUrl('authentication', 'logout')
httpScriptDirectoryPath = context.getVar('httpScriptDirectoryPath')
aboutButton = X.buttonStandalone('about', X.roleUrl('about')).getAsXml()
aboutUrl = X.roleUrl('about').getAsUrl()
logoutButton = X.buttonStandalone('logout', logoutUrl).getAsXml()
prefsButton = X.buttonStandalone('prefs', X.roleUrl('preferences')
).getAsXml()
prefsUrl = X.roleUrl('preferences').getAsUrl()
loginButton = X.buttonStandalone('login', loginUrl).getAsXml()
# FIXME: should take the favourite authentication method
newAccountUrl = X.roleUrl('authentication-login-password', 'newAccount')
newAccountButton = X.buttonStandalone(
'new-account', newAccountUrl).getAsXml()
newAccountUrl = newAccountUrl.getAsUrl()
shortNewsLabel = _('Short News')
if userId:
try:
userName = getObject(userId).getLabel()
except (faults.MissingItem, faults.UserAccessDenied):
userName = _('Unknown')
else:
userName = ''
return locals()
def writePageException(exception):
req = context.getVar('req')
exception = X.convertStringToXml(exception)
req.write("""<html><head><title>Error</title></head>
<body>
<h1>Error processing this page</h1>
<pre>
%s
</pre>
</body>
</html>""" % exception)
def writePageLayout(layout, title, canCache = 1):
dict = getTemplateVars()
dict['X'] = X
dict['title'] = title
req = context.getVar('req')
req.content_type = 'text/html'
setHttpCookie()
req.send_http_header()
if req.method == 'HEAD':
return OK
if req.caching:
req.openCachePage()
# we need to chdir for included files
#os.chdir(context.getVar('webDirectoryPath'))
os.chdir( context.getVar('templatesDirectoryPath') )
dict['contextualHeaders'] = '\n'.join(context.getVar('htmlHeaders'))
dict['body'] = layout.getAsXml()
fileName = context.getVar('templateFileName')
if not currentTemplates.has_key(fileName):
# caching this in currentTemplates improves rendering time by about
# 1.2 seconds on my (fpeters) computer. Not bad
### (benchmark on 2002-11-16)
templateDirectoryName = context.getVar('templateDirectoryName')
path = context.getVar('templatesDirectoryPath')
fallbackPath = context.getVar('fallbackTemplatesDirectoryPath')
try:
file = open(os.path.join(path, templateDirectoryName, fileName))
except IOError:
file = open(os.path.join(fallbackPath, 'default', fileName))
# should raise 404 if it fails
file = file.read()
from TAL.HTMLTALParser import HTMLTALParser
from glasnost.web.GlasnostTALGenerator import GlasnostTALGenerator
t = HTMLTALParser(gen = GlasnostTALGenerator(xml=0))
t.parseString(file)
currentTemplates[fileName] = t
t = currentTemplates[fileName]
program, macros = t.getCode()
# this takes roughly 0.01 seconds, not worth caching
from GlasnostTALInterpreter import GlasnostTALInterpreter
from TAL.HTMLParser import HTMLParseError
from GlasnostTALEngine import GlasnostTALEngine, TALError
engine = GlasnostTALEngine(macros)
context.setVar('talEngine', engine)
engine.locals = WebAPI.getAPIDict()
engine.locals['currentObject'] = context.getVar('currentObject')
for k, v in dict.items():
engine.locals[k] = v
interp = GlasnostTALInterpreter(program, macros, engine, stream=req, wrap=80)
try:
interp()
except TALError:
if not context.getVar('debug'):
raise
info = sys.exc_info()
exception = traceback.format_exception_only(
info[0], info[1])[0].strip()
writePageException(exception)
if req.caching:
req.closeCachePage()
return OK
def getAppropriateTalFile(serverRole, idValue, action):
if idValue:
id = commonTools.splitId(idValue)
serverRole = id[1]
fileNames = (
'%s-%s.%s.%s.tal' % (serverRole, id[0], id[2], action),
# articles-our.website.url.211.print.tal
'%s-%s.%s.tal' % (serverRole, id[0], action),
# articles-our.website.url.print.tal
'%s-%s.%s.tal' % (serverRole, id[2], action),
# articles-211.print.tal
'%s.%s.tal' % (serverRole, action),
# articles.print.tal
'%s-%s.%s.%s.xtal' % (serverRole, id[0], id[2], action),
# articles-our.website.url.211.print.xtal
'%s-%s.%s.xtal' % (serverRole, id[0], action),
# articles-our.website.url.print.xtal
'%s-%s.%s.xtal' % (serverRole, id[2], action),
# articles-211.print.xtal
'%s.%s.xtal' % (serverRole, action),
# articles.print.xtal
)
else:
id = ('',)
fileNames = (
'%s-%s.all.%s.tal' % (serverRole, id[0], action),
# articles-our.website.url.211.print.tal
'%s-all.%s.tal' % (serverRole, action),
# articles-all.print.tal
'%s-%s.all.%s.xtal' % (serverRole, id[0], action),
# articles-our.website.url.211.print.xtal
'%s-all.%s.xtal' % (serverRole, action),
# articles-all.print.xtal
)
directories = context.getVar('webDirectoryPaths') + \
[context.getVar('templatesDirectoryPath')]
for directory in directories:
if not directory:
continue
for fileName in fileNames:
fileName = directory + '/' + fileName
if os.path.exists(fileName):
return fileName
return None
def getAppropriatePyFile(serverRole, idValue, action):
if idValue:
id = commonTools.splitId(idValue)
serverRole = id[1]
fileNames = (
'%s-%s.%s.%s.py' % (serverRole, id[0], id[2], action),
# articles-our.website.url.211.print.py
'%s-%s.%s.py' % (serverRole, id[0], action),
# articles-our.website.url.print.py
'%s-%s.%s.py' % (serverRole, id[2], action),
# articles-211.print.py
'%s.%s.py' % (serverRole, action),
# articles.print.py
)
else:
id = ('',)
fileNames = (
'%s-%s.all.%s.py' % (serverRole, id[0], action),
# articles-our.website.url.211.print.py
'%s-all.%s.py' % (serverRole, action),
# articles-all.print.py
)
directoryPaths = context.getVar('scriptsDirectoryPaths')
for directoryPath in directoryPaths:
for fileName in fileNames:
filePath = os.path.join(directoryPath, fileName)
if os.path.exists(filePath):
return filePath
return None
def getAppropriateFunction(serverRole, idValue, action):
if not action:
action = 'default'
web = context.getVar('web')
talFileName = getAppropriateTalFile(serverRole, idValue, action)
if talFileName:
context.setVar('TALFile', open(talFileName).read())
context.setVar(
'TALFileIsXTAL', talFileName.endswith('.xtal'))
if idValue:
return web.viewTal
else:
return web.viewAllTal
pyFileName = getAppropriatePyFile(serverRole, idValue, action)
if pyFileName:
context.setVar('PyFile', pyFileName)
if idValue:
return web.viewPy
else:
return web.viewAllPy
method = context.getVar('httpMethod')
if action == 'default':
if idValue:
if method == 'DELETE':
action = 'delete'
else:
action = 'use'
else:
action = 'viewAll'
if context.getVar('xmlPost', default = None) and action == 'use':
action = 'postXml'
if hasattr(web, action):
function = getattr(web, action)
if not hasattr(function, 'isPublicForWeb'):
return None
if method in ('GET', 'HEAD', 'POST') and function.isPublicForWeb == 1:
return function
if type(function.isPublicForWeb) in (list, tuple) and \
method in function.isPublicForWeb:
return function
elif method not in ('GET', 'HEAD', 'POST'):
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
return None
def getGotoObjectsLabelsAndLinks():
labelsAndLinks = []
# Standard servers.
roles = context.getVar('knownRoles')
for role in roles:
try:
web = getWebForServerRole(role)
except: # TODO: tighter check.
continue
if not web:
continue
if not web.canViewAll():
continue
labelsAndLinks.append((_(web.objectsNameCapitalized), X.roleUrl(role)))
# Cards Models.
cardsWeb = getWebForServerRole('cards')
if cardsWeb is not None:
cardIds = cardsWeb.getAdmin().prototypeIds
if cardIds:
cardLabels = cardsWeb.getObjectLabelsTranslated(
cardIds, context.getVar('readLanguages'))
for cardId, cardLabel in cardLabels.items():
labelsAndLinks.append((cardLabel,
X.idUrl(cardId, 'implementations')))
labelsAndLinks.sort(lambda x, y: locale.strcoll(x[0], y[0]))
return labelsAndLinks
def getNewObjectLabelsAndLinks():
labelsAndLinks = []
# Standard servers.
roles = context.getVar('knownRoles')
for role in roles:
try:
web = getWebForServerRole(role)
except: # TODO: tighter check.
continue
if not web:
continue
try:
if not web.canAddObject():
continue
except: # TODO: tighter check.
continue
labelsAndLinks.append((_(web.objectNameCapitalized),
X.roleUrl(role, 'edit')))
# Cards Models.
cardsWeb = getWebForServerRole('cards')
if cardsWeb is not None:
cardIds = cardsWeb.getAdmin().prototypeIds
if cardIds:
cardLabels = cardsWeb.getObjectLabelsTranslated(
cardIds, context.getVar('readLanguages'))
for cardId, cardLabel in cardLabels.items():
labelsAndLinks.append((cardLabel, X.idUrl(cardId, 'new')))
labelsAndLinks.sort(lambda x, y: locale.strcoll(x[0], y[0]))
return labelsAndLinks
def processTALFile(fileName, file = None, xtal = None, **keywords):
req = context.getVar('req')
if xtal:
req.content_type = 'text/xml'
else:
req.content_type = 'text/html'
setHttpCookie()
req.send_http_header()
if req.method == 'HEAD':
return OK
if req.caching:
req.openCachePage()
for dir in context.getVar('webDirectoryPaths'):
os.chdir(dir)
if os.path.exists(fileName):
file = open(fileName).read()
break
from TAL.TALParser import TALParser
from TAL.HTMLTALParser import HTMLTALParser
from glasnost.web.GlasnostTALGenerator import GlasnostTALGenerator
if xtal:
t = TALParser(gen = GlasnostTALGenerator(xml=1))
else:
t = HTMLTALParser(gen = GlasnostTALGenerator(xml=0))
t.parseString(file)
program, macros = t.getCode()
from GlasnostTALInterpreter import GlasnostTALInterpreter
from GlasnostTALEngine import GlasnostTALEngine, TALError
engine = GlasnostTALEngine(macros)
context.setVar('talEngine', engine)
engine.locals = WebAPI.getAPIDict()
engine.locals['currentObject'] = None
for k, v in keywords.items():
engine.locals[k] = v
dict = getTemplateVars()
for k, v in dict.items():
engine.locals[k] = v
interp = GlasnostTALInterpreter(program, macros, engine, stream=req, wrap=80)
try:
interp()
except TALError:
if not context.getVar('debug'):
raise
info = sys.exc_info()
exception = traceback.format_exception_only(
info[0], info[1])[0].strip()
writePageException(exception)
if req.caching:
req.closeCachePage()
return OK
def processXTALFile(fileName, file = None, **keywords):
return processTALFile(fileName, file = file, xtal = 1, **keywords)
def spellcheck(text, language = 'en'):
if not context.getVar('enableSpellChecking'):
return text
# generate a temp file name
warnings.filterwarnings('ignore','tmpnam')
tmpnam = posix.tmpnam()
# write text out to a temporary file
tmp = open(tmpnam,'w')
tmp.write(text)
tmp.close()
# call out to aspell program
status, aspell = commands.getstatusoutput(
'aspell --lang=%s --home-dir=/tmp/ -H -a < %s' % (
language, tmpnam))
os.remove(tmpnam)
if status != 0:
return text
# split out all questionable words and non-html text
aspell = re.compile('^& (\w+) \d+ \d+: (.*)', re.M).findall(aspell)
text = re.compile('(<.*?>|&.*?;)', re.S).split(text)
spanSpellcheck = re.compile('<span class="spellcheck".*?</span>')
i = 0
while i < len(text):
if text[i].endswith('/>') and i%2-1:
text.insert(i+1, '')
text.insert(i, '')
i += 2
i += 1
# substitute questionable words in text with a titled span
alreadySeen = []
for found, suggest in aspell:
if found in alreadySeen:
continue
alreadySeen.append(found)
match = re.compile(r'\b%s\b' % found)
replace = '<span class="spellcheck" title="%s">%s</span>' % (
suggest, found)
for i in range(0, len(text), 2):
t, n = match.subn(replace, text[i])
if not n:
continue
parts = spanSpellcheck.split(t)
parts.reverse()
del text[i]
for p in parts[:-1]:
text.insert(i, p)
text.insert(i, replace)
text.insert(i, parts[-1])
# return the concatenated result
return ''.join(text)
def xmlEncode(string):
for c in string:
if ord(c) > 128:
string = string.replace(c, '&#%s;' % ord(c))
return string