604 lines
18 KiB
Python
604 lines
18 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 API Module"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import locale
|
|
import imp
|
|
import math
|
|
import os
|
|
import re
|
|
import sys
|
|
import time
|
|
|
|
import glasnost.common.context as context
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.parsers as parsers
|
|
import glasnost.common.tools_new as commonTools
|
|
from glasnost.common.translation import languageLabels
|
|
import glasnost.common.xhtmlgenerator as X
|
|
|
|
import glasnost.proxy.GroupsProxy
|
|
|
|
import tools_new as webTools
|
|
import tools
|
|
|
|
|
|
class GlasnostTime:
|
|
def __init__(self, time):
|
|
self._time = time
|
|
|
|
def __repr__(self):
|
|
return self.isoDate
|
|
|
|
def __cmp__(self, other):
|
|
return cmp(self._time, other._time)
|
|
|
|
def __getattr__(self, attr):
|
|
formats = {
|
|
'isoDateTime': '%Y-%m-%d %H:%M:%S',
|
|
'isoDate': '%Y-%m-%d',
|
|
'euroDate': '%d-%m-%Y',
|
|
'usDate': '%m-%d-%Y',
|
|
'time': '%H:%M:%S',
|
|
'yearMonth': '%Y-%m'
|
|
}
|
|
if formats.has_key(attr):
|
|
setattr(self, attr, time.strftime(formats[attr],
|
|
time.localtime(self._time)))
|
|
return getattr(self, attr)
|
|
if attr == 'euro':
|
|
self.euro = '%s %s' % (self.euroDate, self.time)
|
|
return self.euro
|
|
if attr == 'us':
|
|
self.us = '%s %s' % (self.usDate, self.time)
|
|
return self.us
|
|
|
|
|
|
raise AttributeError, 'no such attr'
|
|
|
|
|
|
class GlasnostServerRole:
|
|
def __init__(self, web):
|
|
if type(web) is type(''):
|
|
web = tools.getWebForServerRole(web)
|
|
self.web = web
|
|
self.labelPlural = _(web.objectsNameCapitalized)
|
|
if hasattr(web, 'objectNameCapitalized'):
|
|
self.labelSingular = _(web.objectNameCapitalized)
|
|
else:
|
|
self.labelSingular = self.labelPlural
|
|
self.label = self.labelPlural
|
|
self.url = X.roleUrl(web.serverRole)
|
|
self.urlNew = X.roleUrl(web.serverRole, 'edit')
|
|
|
|
def getUrl(self):
|
|
return self.url
|
|
|
|
|
|
class GlasnostObject:
|
|
def __init__(self, id='', object=None):
|
|
if object:
|
|
self.objectId = object.id
|
|
self.resolve(object)
|
|
elif id:
|
|
if id[0] == '/':
|
|
id = context.getVar('dispatcherId') + id
|
|
self.objectId = id
|
|
self.resolved = 0
|
|
else:
|
|
raise "You should give an id or an object!"
|
|
try:
|
|
self.serverRole = commonTools.extractRole(self.objectId)
|
|
except IndexError:
|
|
pass
|
|
except:
|
|
# FIXME: this happens with virtualhosts where self.objectId ==
|
|
# 'glasnost://www.entrouvert.be' (for example)
|
|
self.serverRole = ''
|
|
|
|
def __getattr__(self, attr):
|
|
if attr == 'isAccessible':
|
|
self.isAccessible = self._isAccessible()
|
|
return self.isAccessible
|
|
if attr.startswith('__'):
|
|
raise AttributeError, 'No such attribute'
|
|
if attr == 'label':
|
|
if not self.objectId:
|
|
return self.getLabel()
|
|
try:
|
|
self.label = tools.getObjectLabelTranslated(
|
|
self.objectId,
|
|
context.getVar('readLanguages'))
|
|
except faults.MissingItem:
|
|
self.label = '[deleted object]'
|
|
return self.label
|
|
if attr.startswith('t8d_'):
|
|
# won't work on list (ie Multi fields) for now
|
|
realAttr = attr[4:]
|
|
fieldValue = getattr(self, realAttr)
|
|
if not self.objectId:
|
|
return fieldValue
|
|
path = 'self.%s' % realAttr
|
|
translationsProxy = tools.getProxyForServerRole('translations')
|
|
if translationsProxy:
|
|
fieldValue, destinationLanguage, state = \
|
|
translationsProxy.getTranslationInfos(
|
|
fieldValue, self.objectId, path, self.language,
|
|
context.getVar('readLanguages'))
|
|
#language = context.getVar('readLanguages')[0]
|
|
|
|
setattr(self, attr, fieldValue)
|
|
return getattr(self, attr)
|
|
|
|
if self.resolved and hasattr(self.object, attr):
|
|
return getattr(self.object, attr)
|
|
if self.resolved:
|
|
raise AttributeError, "I don't have a %s attribute" % attr
|
|
self.resolve()
|
|
if self.__dict__.has_key(attr):
|
|
return self.__dict__[attr]
|
|
return getattr(self.object, attr)
|
|
|
|
def _isAccessible(self):
|
|
web = tools.getWeb(self.objectId)
|
|
return web.canGetObject(self.objectId)
|
|
|
|
def resolve(self, object = None):
|
|
self.resolved = 1
|
|
if not object:
|
|
web = tools.getWeb(self.objectId)
|
|
self.object = web.getObject(self.objectId)
|
|
else:
|
|
self.object = object
|
|
for key, val in self.object.__dict__.items():
|
|
# dumb
|
|
setattr(self, key, val)
|
|
|
|
if hasattr(self.object, 'getSlotNames'):
|
|
# smart
|
|
for slotName in self.object.getSlotNames():
|
|
if not hasattr(self.object, slotName) \
|
|
or not getattr(self.object, slotName):
|
|
continue
|
|
if slotName == 'id':
|
|
continue
|
|
slot = self.getSlot(slotName)
|
|
kind = slot.getKind()
|
|
if kind.__class__.__name__ == 'Id':
|
|
setattr(self, slotName,
|
|
GlasnostObject(getattr(self.object, slotName)))
|
|
elif kind.__class__.__name__ == 'Sequence' and \
|
|
kind.itemKind.__class__.__name__ == 'Id':
|
|
kind = [GlasnostObject(x)
|
|
for x in getattr(self.object, slotName)]
|
|
setattr(self, slotName, kind)
|
|
elif kind.__class__.__name__ in (
|
|
'Time', 'ModificationTime', 'CreationTime'):
|
|
kind = GlasnostTime(getattr(self.object, slotName))
|
|
setattr(self, slotName, kind)
|
|
|
|
def __repr__(self):
|
|
return '<GlasnostObject (%s) (resolved: %d)>' % (
|
|
self.objectId, self.resolved)
|
|
|
|
def __cmp__(self, other):
|
|
if not self: return -1
|
|
if not other: return 1
|
|
return cmp(self.objectId, other.objectId)
|
|
|
|
def __str__(self):
|
|
if self.serverRole == 'virtualhosts':
|
|
return self.t8d_title
|
|
return self.label
|
|
|
|
def getUrl(self, action='', args=None, absolute=0):
|
|
t = X.idUrl(self.objectId, action)
|
|
if args:
|
|
for k,v in args.items():
|
|
t.add(k,v)
|
|
if absolute:
|
|
return t.getAsAbsoluteUrl()
|
|
else:
|
|
return t.getAsUrl()
|
|
|
|
|
|
def canAddObject(objectType):
|
|
if isinstance(objectType, GlasnostServerRole):
|
|
web = objectType.web
|
|
else:
|
|
web = tools.getWebForServerRole(objectType)
|
|
try:
|
|
return web.canAddObject()
|
|
except: # TODO: tighter check
|
|
return 0
|
|
|
|
|
|
def getAPIDict():
|
|
module = sys.modules[__name__]
|
|
api = {}
|
|
for k,v in module.__dict__.items():
|
|
if k[0] == '_':
|
|
continue
|
|
if type(v) not in (type(getMainRubric), type(GlasnostObject)) :
|
|
continue
|
|
api[k] = v
|
|
api['userToken'] = context.getVar('userToken')
|
|
api['fileName'] = context.getVar('webFileName')
|
|
try:
|
|
api['user'] = GlasnostObject(id = context.getVar(
|
|
'userId', default = ''))
|
|
except: # TODO: tighter check
|
|
api['user'] = None
|
|
virtualHost = context.getVar('virtualHost')
|
|
if virtualHost is not None:
|
|
api['virtualHost'] = GlasnostObject(object = virtualHost)
|
|
else:
|
|
api['virtualHost'] = ''
|
|
api['currentURI'] = context.getVar('req')._req.uri
|
|
api['math'] = math
|
|
api['time'] = time
|
|
api['urlargs'] = UrlArgs(api)
|
|
api['context'] = context
|
|
api['parsers'] = parsers
|
|
api['webTools'] = webTools
|
|
api['X'] = X
|
|
return api
|
|
|
|
|
|
def getGotoObjectsLabelsAndLinks():
|
|
return tools.getGotoObjectsLabelsAndLinks()
|
|
|
|
|
|
def getLastObjects(serverRole, number):
|
|
web = tools.getWebForServerRole(serverRole)
|
|
if not hasattr(web, 'getLastObjects'):
|
|
return []
|
|
userId = context.getVar('userId', default = '')
|
|
if userId:
|
|
userSet = [userId]
|
|
else:
|
|
userSet = None
|
|
|
|
return [
|
|
GlasnostObject(object = x)
|
|
for x in web.getLastObjects(number, None, userSet, None)]
|
|
|
|
|
|
def getMainRubric():
|
|
try:
|
|
object = tools.getWebForServerRole('rubrics').getMainObject()
|
|
except: # TODO: should check the exception is MainRubricMissing
|
|
return None
|
|
glasnostObject = GlasnostObject(object=object)
|
|
return glasnostObject
|
|
|
|
|
|
def getMenuIds(roles):
|
|
return X.menuIds(roles).getAsXml()
|
|
|
|
|
|
def getNewObjectLabelsAndLinks():
|
|
return tools.getNewObjectLabelsAndLinks()
|
|
|
|
|
|
cachedPath = {}
|
|
|
|
|
|
def _getPathToObject(object, source):
|
|
if not object:
|
|
return None
|
|
try:
|
|
source.membersSet
|
|
except: # TODO: tighter check
|
|
# this probably means we don't have read access to this source
|
|
return None
|
|
if object == source.contentId:
|
|
return [source]
|
|
if not source.membersSet:
|
|
return None
|
|
if object in source.membersSet:
|
|
return [source]
|
|
for m in source.membersSet:
|
|
if commonTools.extractRole(m.objectId) != 'rubrics':
|
|
# we don't go through getSetContainedIds here
|
|
continue
|
|
t = _getPathToObject(object, m)
|
|
if t:
|
|
return [source] + t
|
|
return None
|
|
|
|
|
|
def getPathToObject(object):
|
|
# this benefits heavily from the cache
|
|
mR = getMainRubric()
|
|
path = _getPathToObject(object, mR)
|
|
if not path:
|
|
if not object or not object.serverRole:
|
|
return []
|
|
path = [ GlasnostServerRole(object.serverRole), ]
|
|
return path
|
|
|
|
|
|
def getTree(source, done = None, level=5):
|
|
if done is None:
|
|
done = []
|
|
if commonTools.extractRole(source.objectId) != 'rubrics' or \
|
|
level == 0 or source in done:
|
|
return (source, [])
|
|
try:
|
|
source.membersSet
|
|
except: # TODO: tighter check
|
|
# this probably means we don't have read access to this source
|
|
return (source, [])
|
|
if not source.membersSet:
|
|
return (source, [])
|
|
done.append(source)
|
|
return (source, [getTree(x, done, level-1) for x in source.membersSet])
|
|
|
|
|
|
def flattenTree(tree, list, indent=0):
|
|
list.append( (indent, tree[0]) )
|
|
for x in tree[1]:
|
|
flattenTree(x, list, indent+1)
|
|
return list
|
|
|
|
|
|
def getServerRoles():
|
|
serverIds = []
|
|
roles = context.getVar('knownRoles')[:]
|
|
for role in roles:
|
|
try:
|
|
web = tools.getWebForServerRole(role)
|
|
except: # TODO: tighter check
|
|
continue
|
|
if not web:
|
|
continue
|
|
if hasattr(web, 'objectsNameCapitalized'):
|
|
s = GlasnostServerRole(web)
|
|
serverIds.append(s)
|
|
|
|
serverIds.sort(lambda x, y: locale.strcoll(x.label, y.label))
|
|
return serverIds
|
|
|
|
|
|
def getSetContainedObjects(set, serverRoles=[], sortOn=None):
|
|
if not set:
|
|
return []
|
|
set = [ x.objectId for x in set ]
|
|
result = glasnost.proxy.GroupsProxy.getSetContainedIds(set, serverRoles)
|
|
result = [ GlasnostObject(x) for x in result ]
|
|
if len(result) > 1 and sortOn:
|
|
cmpFct = cmp
|
|
if sortOn[0] == '-':
|
|
sortOn = sortOn[1:]
|
|
coeff = -1
|
|
else:
|
|
coeff = 1
|
|
if sortOn == 'label':
|
|
cmpFct = locale.strcoll
|
|
elif hasattr(result[0].object, sortOn + '_kind'):
|
|
kind = getattr(result[0].object, sortOn + '_kind')
|
|
cmpFct = kind.getCmpFunction()
|
|
|
|
result.sort(lambda x, y: coeff*cmpFct(
|
|
getattr(x, sortOn),
|
|
getattr(y, sortOn)))
|
|
#elif sortOn:
|
|
# result.sort(lambda x, y: cmp(
|
|
# eval(sortOn, {'self': x}),
|
|
# eval(sortOn, {'self': y})) )
|
|
|
|
return result
|
|
|
|
|
|
def loadExtension(name):
|
|
directoryPaths = context.getVar('talExtensionsDirectoryPaths')
|
|
fileName = name + '.py'
|
|
for directoryPath in directoryPaths:
|
|
filePath = os.path.join(directoryPath, fileName)
|
|
if os.path.exists(filePath):
|
|
break
|
|
else:
|
|
raise Exception('Unable to load extension %s' % filePath)
|
|
return imp.load_source(name, filePath)
|
|
|
|
|
|
def pretion(text):
|
|
pretion = '<hr />' + text + '<hr />'
|
|
hr = re.compile("<hr.*?>", re.I + re.M)
|
|
pretion = hr.split(pretion)
|
|
return pretion[1:-1]
|
|
# pretion = pretion[0] + '<div class="page">' \
|
|
# + '</div><div class="page">'.join(pretion[1:-1]) \
|
|
# + '</div>' + pretion[-1]
|
|
|
|
|
|
def sortList(l, sortOn):
|
|
if len(l) == 0:
|
|
return l
|
|
|
|
cmpFct = cmp
|
|
if sortOn[0] == '-':
|
|
sortOn = sortOn[1:]
|
|
coeff = -1
|
|
else:
|
|
coeff = 1
|
|
if sortOn == 'label':
|
|
cmpFct = locale.strcoll
|
|
elif hasattr(l[0].object, sortOn + '_kind'):
|
|
kind = getattr(l[0].object, sortOn + '_kind')
|
|
cmpFct = kind.getCmpFunction()
|
|
|
|
l.sort(lambda x, y: coeff*cmpFct(
|
|
getattr(x, sortOn),
|
|
getattr(y, sortOn)))
|
|
return l
|
|
|
|
|
|
compiledFiles = {}
|
|
|
|
|
|
def template(filename, xtal=0, **keywords):
|
|
from TAL.TALParser import TALParser
|
|
from TAL.HTMLTALParser import HTMLTALParser
|
|
from glasnost.web.GlasnostTALGenerator import GlasnostTALGenerator
|
|
|
|
if not os.path.exists(filename):
|
|
fallbackPath = context.getVar(
|
|
'fallbackTemplatesDirectoryPath')
|
|
filename = filename.split('/')[-1]
|
|
if os.path.exists('%s/default/%s' % (fallbackPath, filename)):
|
|
filename = '%s/default/%s' % (fallbackPath, filename)
|
|
else:
|
|
raise Exception('Function template() called for inexistant file')
|
|
|
|
if 0 and compiledFiles.has_key(filename):
|
|
t = compiledFiles[filename]
|
|
else:
|
|
if xtal:
|
|
t = TALParser(gen = GlasnostTALGenerator(xml=1))
|
|
else:
|
|
t = HTMLTALParser(gen = GlasnostTALGenerator(xml=0))
|
|
t.parseString(open(filename).read())
|
|
compiledFiles[filename] = t
|
|
|
|
program, macros = t.getCode()
|
|
|
|
from GlasnostTALInterpreter import GlasnostTALInterpreter
|
|
from GlasnostTALEngine import GlasnostTALEngine
|
|
engine = GlasnostTALEngine(macros)
|
|
#context.setVar('talEngine', engine)
|
|
engine.locals = getAPIDict()
|
|
for k, v in keywords.items():
|
|
engine.locals[k] = v
|
|
stream = tools.StringIO()
|
|
interp = GlasnostTALInterpreter(program, macros, engine, stream=stream, wrap=80)
|
|
interp()
|
|
return stream.getvalue()
|
|
|
|
|
|
def aliasUrl(alias, action = None, **keywords):
|
|
url = X.aliasUrl(alias, action)
|
|
for key, value in keywords.items():
|
|
url.add(key, value)
|
|
return url
|
|
|
|
|
|
def idUrl(id, action = None, **keywords):
|
|
url = X.idUrl(id, action)
|
|
for key, value in keywords.items():
|
|
url.add(key, value)
|
|
return url
|
|
|
|
|
|
def roleUrl(role, action = None, **keywords):
|
|
url = X.roleUrl(role, action)
|
|
for key, value in keywords.items():
|
|
url.add(key, value)
|
|
return url
|
|
|
|
|
|
def rootUrl():
|
|
return X.rootUrl().getAsUrl()
|
|
|
|
|
|
class UrlArgs:
|
|
def __init__(self, dict):
|
|
self.dict = dict
|
|
|
|
def hasArg(self, name):
|
|
return self.dict.has_key(name)
|
|
|
|
def getArg(self, name, default=None):
|
|
if self.dict.has_key(name):
|
|
return self.dict[name]
|
|
else:
|
|
return default
|
|
|
|
def __getattr__(self, name):
|
|
return self.dict[name]
|
|
|
|
|
|
def cacheDepends(args):
|
|
ids = []
|
|
for a in args:
|
|
if isinstance(a, GlasnostObject):
|
|
ids.append(a.objectId)
|
|
else:
|
|
ids.append(a)
|
|
req = context.getVar('req')
|
|
req.depends += ids
|
|
|
|
|
|
def exists(varname):
|
|
engine = context.getVar('talEngine')
|
|
if not engine:
|
|
return 0
|
|
return (engine.locals.has_key(varname) or engine.globals.has_key(varname))
|
|
|
|
def getPossibleLanguagesLabelAndUrl(objectId = None, notSelf = 0):
|
|
translationsProxy = tools.getProxyForServerRole('translations')
|
|
if not objectId:
|
|
objectId = context.getVar('objectId')
|
|
if objectId:
|
|
languages = translationsProxy.getLanguagesForObjectId(objectId)
|
|
else:
|
|
languages = translationsProxy.getPossibleLanguages()
|
|
if notSelf and context.getVar('readLanguages')[0] in languages:
|
|
languages.remove(context.getVar('readLanguages')[0])
|
|
result = []
|
|
cleanedUpUri = tools.cleanUpUnparsedUri([], 'http')
|
|
for l in languages:
|
|
result.append((l, _(languageLabels[l]),
|
|
X.roleUrl('switchLang').add('lang', l).add(
|
|
'nextUri', cleanedUpUri)))
|
|
return result
|
|
|