Cf le mail sur glasnost-devel

This commit is contained in:
fpeters 2003-12-02 09:54:54 +00:00
parent 8abdaed5ff
commit 9b5af4b22d
62 changed files with 745 additions and 930 deletions

View File

@ -116,7 +116,8 @@ class Application(applications.Application):
#locale.setlocale(locale.LC_ALL, '')
domains = context.getVar('gettextDomains')
translation = commonTools.translation(domains + ['gnome-libs'])
translation = commonTools.translation(domains + ['gnome-libs'],
context.getVar('readLanguages'))
__builtin__.__dict__['_'] = lambda x: utf8(translation.gettext(x))
imagesDirectory = glasnost.gtk.MainWindow.__file__

View File

@ -178,7 +178,10 @@ class Application(applications.Application):
if req.buffered:
req.finish()
return HTTP_FORBIDDEN
except IOError:
except IOError, exc:
if exc.filename:
# TODO: check this doesn't catch IOError to client socket
raise
# Write failed, client closed connection.
if req.caching:
req.cancel()
@ -492,13 +495,22 @@ class Application(applications.Application):
context.setVar('session', session)
context.setVar('sessionToken', sessionToken)
context.setVar('sessionTokenInCookie', sessionTokenInCookie)
userId = ''
if session and session.has_key('userId'):
userId = session['userId']
context.setVar('userId', userId)
userToken = ''
if session and session.has_key('userToken'):
userId = None
if session and session.has_key('userToken') and session['userToken']:
userToken = session['userToken']
context.setVar('userToken', userToken)
userId = getProxyForServerRole('authentication').getUserId()
if userId:
userToken = session['userToken']
else:
userToken = None
userId = None
del session['userToken']
if session.has_key('userId'):
del session['userId']
session['isDirty'] = 1
context.setVar('userId', userId)
context.setVar('userToken', userToken)
if not userToken and req.headers_in.has_key('Authorization'):
@ -559,17 +571,11 @@ class Application(applications.Application):
if not virtualHost.cacheFiles:
reqCache.caching = -1
modTime = reqCache.checkDepends()
reqCache = None
else:
req.headers_out['Expires'] = reqCache.getExpires()
modTime = None
if reqCache.isPageCached():
# and \
# not (req.headers_in.has_key('Cache-Control') and \
# req.headers_in['Cache-Control'] == 'max-age=0') and \
# not (req.headers_in.has_key('Cache-Control') and \
# req.headers_in['Cache-Control'] == 'no-cache') and \
# not (req.headers_in.has_key('Pragma') and \
# req.headers_in['Pragma'] == 'no-cache'):
if reqCache and reqCache.isPageCached():
try:
cachedPage, contentType = reqCache.getCachePage()
except OSError:
@ -589,7 +595,7 @@ class Application(applications.Application):
if reqCache is None:
# The system caching is not activated when a session is open.
reqCache = req
reqCache = Request(req)
reqCache.caching = 0
reqCache.depends = []
reqCache.buffered = 0
@ -1148,7 +1154,12 @@ class BufferedRequest:
self.__dict__['_req'] = val
except AttributeError:
self.__dict__[attr] = val
class Request(apache.Request):
def write(self, st):
if type(st) is type(u''):
st = st.encode('iso-8859-1')
return self._req.write(st)
class RequestCache:
buffered = 0

View File

@ -58,48 +58,20 @@ input.button:hover {
}
/*** diffs ***/
div.diff-context {
}
div.diff-error {
font-size: 80%;
}
div.diff-new {
background-color: #80ff80;
border-style: none;
border-width: thin;
width: 100%;
}
div.diff-old {
background-color: #ffff80;
border-style: none;
border-width: thin;
width: 100%;
}
hr {
clear: both;
}
hr.diff {
}
span.diff-equal {
white-space: pre;
pre.diff {
width: 95%;
}
span.diff-new {
background-color: #80ff80;
white-space: pre;
background-color: #8f8;
}
span.diff-old {
background-color: #ffff80;
white-space: pre;
background-color: #f88;
}
@ -250,66 +222,6 @@ div.clear {
clear: both;
}
div.row {
clear: both;
padding-top: 5px;
}
div.row span.label,
div.row label {
float: left;
width: 20%;
text-align: right;
padding-right: 1em;
font-weight: bold;
}
div.row .formw {
float: right;
margin-left: auto;
text-align: left;
}
div.cell,
table.cell,
fieldset.cell,
span.cell,
input.cell,
select.cell {
text-align: left;
float: right;
width: 76%;
}
.cell .cell {
text-align: left;
float: none;
width: auto;
}
div.cell ul {
margin: 0;
padding: 0;
list-style-type: none;
}
div.cell ul.inline li {
display: inline;
}
fieldset.inline span {
display: inline;
}
table.cell {
margin: 0;
padding: 0;
margin: 0;
padding: 0;
position: relative;
left: -3px;
}
div.buttons-bar {
padding-top: 1em;
clear: both;
@ -342,21 +254,6 @@ div.comment-block a {
font-size: 80%;
}
fieldset {
border: 0;
padding: 0;
margin: 0;
}
fieldset span.cell,
fieldset input.cell,
fieldset select.cell,
fieldset div.cell {
width: auto;
text-align: left;
float: none;
}
span.spellcheck {
cursor: help;
border-bottom: 1px dashed red;
@ -400,22 +297,6 @@ h2, h3, h4, h5, h6 {
clear: both;
}
div.row div.fullwidth,
div.row span.fullwidth {
text-align: inherit;
width: inherit;
clear: both;
float: none;
}
ul.multi>li {
clear: both;
}
ul.multi>li>div{
margin-bottom: 2em;
}
span.comment-no {
display: none;
}
@ -424,3 +305,13 @@ ul.article-meta {
list-style: none;
}
table.diff {
width: 95%;
}
table.diff td {
width: 45%;
border: 1px solid black;
vertical-align: top;
}

View File

@ -106,7 +106,9 @@ def index():
if object.getLabel() == mainRubric.getLabel():
context.setVar('pageTitle', mainRubric.getLabel())
object.title = ''
context.push(sectionLevel = context.getVar('sectionLevel')+1)
layout += object.getEmbeddedViewLayout()
context.pull()
else:
mainRubric = None

View File

@ -91,126 +91,6 @@ class Article(ObjectServerMixin, ArticleCommon):
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
self.loadBody()
def getBodyDiff(self, editionTime):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
virtualServer = self.getServer().getVirtualServer(virtualServerId)
articlesDirectoryPath = os.path.join(
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyFilePath = os.path.join(articlesDirectoryPath, localId)
if not os.access(bodyFilePath, os.F_OK):
return None
bodyHistoryDirectoryPath = os.path.join(
articlesDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
return None
fileNames = os.listdir(bodyHistoryDirectoryPath)
if not fileNames:
return None
editionTimeString = time.strftime('%Y%m%d%H%M%S',
time.localtime(editionTime))
editionTimeLen = len(editionTimeString)
for fileName in fileNames:
if fileName[:editionTimeLen] == editionTimeString \
and fileName[editionTimeLen] == '-':
editionTimeString, editorIdEncoded, format = \
fileName.split('-')
editorId = base64.decodestring(editorIdEncoded)
bodyVersionFilePath = os.path.join(bodyHistoryDirectoryPath,
fileName)
command = 'diff -u %(backup)s %(current)s' % {
'backup': bodyVersionFilePath,
'current': bodyFilePath,
}
diffFile = os.popen(command, 'r')
diff = diffFile.readlines()
result = diffFile.close()
if not diff:
return None
version = {
'diff': diff[2:],
'editionTime': editionTime,
'format': format,
}
if editorId:
version['editorId'] = editorId
return version
return None
def getBodyHistory(self):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
virtualServer = self.getServer().getVirtualServer(virtualServerId)
articlesDirectoryPath = os.path.join(
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyHistoryDirectoryPath = os.path.join(
articlesDirectoryPath, localId + '-history')
history = []
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
return history
fileNames = os.listdir(bodyHistoryDirectoryPath)
if fileNames:
fileNames.sort()
for fileName in fileNames:
editionTimeString, editorIdEncoded, format = \
fileName.split('-')
t = time.strptime(editionTimeString, '%Y%m%d%H%M%S')
# Set Daylight Saving Time to -1, so that it is handled
# correctly by mktime.
t = tuple(list(t[0:-1]) + [-1])
editionTime = time.mktime(t)
editorId = base64.decodestring(editorIdEncoded)
version = {
'editionTime': editionTime,
'format': format,
}
if editorId:
version['editorId'] = editorId
history.append(version)
return history
def getBodyVersion(self, editionTime):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
virtualServer = self.getServer().getVirtualServer(virtualServerId)
articlesDirectoryPath = os.path.join(
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyHistoryDirectoryPath = os.path.join(
articlesDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
return None
fileNames = os.listdir(bodyHistoryDirectoryPath)
if not fileNames:
return None
editionTimeString = time.strftime('%Y%m%d%H%M%S',
time.localtime(editionTime))
editionTimeLen = len(editionTimeString)
for fileName in fileNames:
if fileName[:editionTimeLen] == editionTimeString \
and fileName[editionTimeLen] == '-':
editionTimeString, editorIdEncoded, format = \
fileName.split('-')
editorId = base64.decodestring(editorIdEncoded)
bodyVersionFilePath = os.path.join(bodyHistoryDirectoryPath,
fileName)
bodyVersionFile = open(bodyVersionFilePath, 'rb')
fcntl.lockf(bodyVersionFile, fcntl.LOCK_SH)
body = bodyVersionFile.read()
fcntl.lockf(bodyVersionFile, fcntl.LOCK_UN)
bodyVersionFile.close()
version = {
'body': body,
'editionTime': editionTime,
'format': format,
}
if editorId:
version['editorId'] = editorId
return version
return None
def getDocBookChapter(self):
# TODO: support conversion from every text format to DocBook
assert self.format == 'spip'
@ -273,30 +153,8 @@ class Article(ObjectServerMixin, ArticleCommon):
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyFilePath = os.path.join(articlesDirectoryPath, localId)
try:
if os.path.exists(bodyFilePath):
os.remove(bodyFilePath)
except OSError, error:
# Ignore 'No such file or directory' error.
if error.errno != 2:
raise
def removeBodyHistory(self):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
virtualServer = self.getServer().getVirtualServer(virtualServerId)
articlesDirectoryPath = os.path.join(
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyHistoryDirectoryPath = os.path.join(
articlesDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
return
fileNames = os.listdir(bodyHistoryDirectoryPath)
if fileNames:
for fileName in fileNames:
filePath = os.path.join(bodyHistoryDirectoryPath, fileName)
os.remove(filePath)
os.rmdir(bodyHistoryDirectoryPath)
def removeNonCore(self, objectDirectoryPath = None,
dataDirectoryPath = None, parentSlot = None):
@ -304,7 +162,6 @@ class Article(ObjectServerMixin, ArticleCommon):
self, objectDirectoryPath = objectDirectoryPath,
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
self.removeBodyFile()
self.removeBodyHistory()
def saveBody(self):
if self.body is None:
@ -330,37 +187,6 @@ class Article(ObjectServerMixin, ArticleCommon):
fcntl.lockf(bodyFile, fcntl.LOCK_UN)
bodyFile.close()
def saveBodyHistory(self, format, body, editorId, editionTime):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
virtualServer = self.getServer().getVirtualServer(virtualServerId)
articlesDirectoryPath = os.path.join(
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(self.id)
bodyHistoryDirectoryPath = os.path.join(
articlesDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
os.mkdir(bodyHistoryDirectoryPath)
os.chmod(bodyHistoryDirectoryPath, 0750)
editionTimeString = time.strftime('%Y%m%d%H%M%S',
time.localtime(editionTime))
if editorId is None:
editorId = ''
bodyHistoryFileName = \
'%(editionTime)s-%(editorIdEncoded)s-%(format)s' % {
'editionTime': editionTimeString,
'editorIdEncoded': base64.encodestring(editorId).strip(),
'format': format,
}
bodyHistoryFilePath = os.path.join(bodyHistoryDirectoryPath,
bodyHistoryFileName)
bodyHistoryFile = open(bodyHistoryFilePath, 'wb')
os.chmod(bodyHistoryFilePath, 0640)
fcntl.lockf(bodyHistoryFile, fcntl.LOCK_EX)
bodyHistoryFile.write(body)
fcntl.lockf(bodyHistoryFile, fcntl.LOCK_UN)
bodyHistoryFile.close()
def saveNonCore(self, objectDirectoryPath = None, dataDirectoryPath = None,
parentSlot = None):
ObjectServerMixin.saveNonCore(
@ -397,27 +223,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
virtualServer.markCoreAsDirty()
return object.id
def canAddObject(self):
virtualServerId = context.getVar('applicationId')
applicationName = extractApplicationHostName(virtualServerId)
nbArticle = int(commonTools.getConfigNoCache(
'%s' % applicationName, 'Limit-%s' % applicationRole, '0'))
if nbArticle:
virtualServer = self.getVirtualServer(virtualServerId)
if len(virtualServer.objects.keys()) >= nbArticle:
return 0
return ObjectsServer.canAddObject(self)
def canGetObjectHistory(self, objectId):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
if not virtualServer.canLoadObjectCore(objectId):
return 0
object = virtualServer.loadObjectCore(objectId)
return self.isAdmin() \
or getProxyForServerRole('authentication').setContainsUser(
object.writersSet)
def convertVirtualServersIds(
self, sourceDispatcherId, destinationDispatcherId):
exitCode = ObjectsServer.convertVirtualServersIds(
@ -460,42 +265,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
destinationBodyFile.write(sourceBodyFile.read())
sourceBodyFile.close()
destinationBodyFile.close()
sourceBodyHistoryDirectoryPath = os.path.join(
sourceObjectsDirectoryPath, localId + '-history')
destinationBodyHistoryDirectoryPath = os.path.join(
destinationObjectsDirectoryPath, localId + '-history')
if not os.access(sourceBodyHistoryDirectoryPath, os.F_OK):
continue
if not os.access(destinationBodyHistoryDirectoryPath, os.F_OK):
os.mkdir(destinationBodyHistoryDirectoryPath)
os.chmod(destinationBodyHistoryDirectoryPath, 0750)
sourceBodyHistoryFileNames = os.listdir(
sourceBodyHistoryDirectoryPath)
for sourceBodyHistoryFileName in sourceBodyHistoryFileNames:
editionTimeString, sourceEditorIdEncoded, format = \
sourceBodyHistoryFileName.split('-')
sourceEditorId = base64.decodestring(sourceEditorIdEncoded)
destinationEditorId = sourceEditorId.replace(
sourceDispatcherId, destinationDispatcherId)
destinationBodyHistoryFileName = \
'%(editionTime)s-%(editorIdEncoded)s-%(format)s' % {
'editionTime': editionTimeString,
'editorIdEncoded': base64.encodestring(
destinationEditorId).strip(),
'format': format,
}
sourceBodyHistoryFilePath = os.path.join(
sourceBodyHistoryDirectoryPath, sourceBodyHistoryFileName)
destinationBodyHistoryFilePath = os.path.join(
destinationBodyHistoryDirectoryPath,
destinationBodyHistoryFileName)
sourceBodyHistoryFile = open(sourceBodyHistoryFilePath, 'rb')
destinationBodyHistoryFile = open(
destinationBodyHistoryFilePath, 'wb')
destinationBodyHistoryFile.write(sourceBodyHistoryFile.read())
sourceBodyHistoryFile.close()
destinationBodyHistoryFile.close()
return None
def exportVirtualServer(self, virtualServerId, exportDirectoryPath):
@ -532,29 +301,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
exportBodyFile.write(sourceBodyFile.read())
sourceBodyFile.close()
exportBodyFile.close()
sourceBodyHistoryDirectoryPath = os.path.join(
objectsDirectoryPath, localId + '-history')
exportBodyHistoryDirectoryPath = os.path.join(
exportObjectsDirectoryPath, localId + '-history')
if not os.access(sourceBodyHistoryDirectoryPath, os.F_OK):
continue
if not os.access(exportBodyHistoryDirectoryPath, os.F_OK):
os.mkdir(exportBodyHistoryDirectoryPath)
os.chmod(exportBodyHistoryDirectoryPath, 0750)
bodyHistoryFileNames = os.listdir(
sourceBodyHistoryDirectoryPath)
for bodyHistoryFileName in bodyHistoryFileNames:
sourceBodyHistoryFilePath = os.path.join(
sourceBodyHistoryDirectoryPath, bodyHistoryFileName)
exportBodyHistoryFilePath = os.path.join(
exportBodyHistoryDirectoryPath, bodyHistoryFileName)
sourceBodyHistoryFile = open(sourceBodyHistoryFilePath, 'rb')
exportBodyHistoryFile = open(exportBodyHistoryFilePath, 'wb')
os.chmod(exportBodyHistoryFilePath, 0640)
exportBodyHistoryFile.write(sourceBodyHistoryFile.read())
sourceBodyHistoryFile.close()
exportBodyHistoryFile.close()
return None
def getLastObjectIds(self, objectsCount, possibleAuthorsSet,
@ -603,20 +349,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
break
return result
def getObjectDiffXmlRpc(self, objectId, editionTime):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
object = virtualServer.loadObjectCore(objectId)
if not self.isAdmin() \
and not getProxyForServerRole('authentication'
).setContainsUser(object.writersSet):
raise faults.UserAccessDenied()
result = object.getBodyDiff(editionTime)
if result is None:
raise faults.MissingItem(objectId)
result['diff'] = [ utf8(line) for line in result['diff']]
return result
def getObjectDocBookChapterXmlRpc(self, objectId):
object = self.getObjectCore(objectId)
object.acquireNonCore()
@ -626,16 +358,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
object.releaseNonCore()
return utf8(result)
def getObjectHistory(self, objectId):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
object = virtualServer.loadObjectCore(objectId)
if not self.isAdmin() \
and not getProxyForServerRole('authentication'
).setContainsUser(object.writersSet):
raise faults.UserAccessDenied()
return object.getBodyHistory()
def getObjectLatexChapterXmlRpc(self, objectId):
object = self.getObjectCore(objectId)
object.acquireNonCore()
@ -645,20 +367,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
object.releaseNonCore()
return utf8(result)
def getObjectVersionXmlRpc(self, objectId, editionTime):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
object = virtualServer.loadObjectCore(objectId)
if not self.isAdmin() \
and not getProxyForServerRole('authentication'
).setContainsUser(object.writersSet):
raise faults.UserAccessDenied()
result = object.getBodyVersion(editionTime)
if result is None:
raise faults.MissingItem(objectId)
result['body'] = utf8(result['body'])
return result
def importVirtualServer(self, virtualServerId, importDirectoryPath):
virtualServer = ObjectsServer.importVirtualServer(
self, virtualServerId, importDirectoryPath)
@ -692,106 +400,16 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
destinationBodyFile.write(importBodyFile.read())
importBodyFile.close()
destinationBodyFile.close()
importBodyHistoryDirectoryPath = os.path.join(
importObjectsDirectoryPath, localId + '-history')
destinationBodyHistoryDirectoryPath = os.path.join(
objectsDirectoryPath, localId + '-history')
if not os.access(importBodyHistoryDirectoryPath, os.F_OK):
continue
if not os.access(destinationBodyHistoryDirectoryPath, os.F_OK):
os.mkdir(destinationBodyHistoryDirectoryPath)
os.chmod(destinationBodyHistoryDirectoryPath, 0750)
bodyHistoryFileNames = os.listdir(
importBodyHistoryDirectoryPath)
for bodyHistoryFileName in bodyHistoryFileNames:
importBodyHistoryFilePath = os.path.join(
importBodyHistoryDirectoryPath, bodyHistoryFileName)
destinationBodyHistoryFilePath = os.path.join(
destinationBodyHistoryDirectoryPath, bodyHistoryFileName)
importBodyHistoryFile = open(importBodyHistoryFilePath, 'rb')
destinationBodyHistoryFile = open(
destinationBodyHistoryFilePath, 'wb')
destinationBodyHistoryFile.write(importBodyHistoryFile.read())
importBodyHistoryFile.close()
destinationBodyHistoryFile.close()
return virtualServer
def modifyObjectXmlRpc(self, objectImport):
objectChanges = commonTools.importThing(objectImport)
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
object = virtualServer.loadObjectCore(objectChanges.id)
object.acquireNonCore()
try:
if not object.canBeModified():
raise faults.ReadOnlyObject()
if not self.canModifyObject(object.id) or not (
self.isAdmin()
or not objectChanges.hasSlotName('writersSet')
or getProxyForServerRole('authentication'
).setContainsUser(
objectChanges.getSlot('writersSet').getValue())):
if not object.canBeModifiedByClient():
raise faults.UserAccessDenied()
object.checkModifyIsPossible(objectChanges)
oldFormat = object.format
oldBody = object.body
oldLastEditorId = object.lastEditorId
oldEditionTime = object.editionTime
object.modify(objectChanges)
if object.body != oldBody:
if oldBody is not None:
object.saveBodyHistory(oldFormat, oldBody, oldLastEditorId,
oldEditionTime)
object.saveNonCore()
finally:
object.releaseNonCore()
virtualServer.markObjectAsDirty(object)
invalidateValue(object.id)
return object.version
def registerPublicMethods(self):
ObjectsServer.registerPublicMethods(self)
self.registerPublicMethod('canGetObjectHistory')
self.registerPublicMethod('getObjectDiff',
self.getObjectDiffXmlRpc)
self.registerPublicMethod('getObjectDocBookChapter',
self.getObjectDocBookChapterXmlRpc)
self.registerPublicMethod('getObjectHistory')
self.registerPublicMethod('getObjectLatexChapter',
self.getObjectLatexChapterXmlRpc)
self.registerPublicMethod('getObjectVersion',
self.getObjectVersionXmlRpc)
self.registerPublicMethod('search')
def removeVirtualServerHistory(self, dispatcherId):
exitCode = ObjectsServer.removeVirtualServerHistory(
self, dispatcherId)
if exitCode is not None:
return exitCode
virtualServerId = '%s/%s' % (dispatcherId, self.applicationRole)
virtualServer = self.getVirtualServer(virtualServerId)
hostName = extractApplicationHostName(dispatcherId)
virtualServerDataDirectoryPath = os.path.join(
self.dataDirectoryPath, hostName)
objectsDirectoryPath = os.path.join(
virtualServerDataDirectoryPath, self.applicationRole)
for id in virtualServer.objects.keys():
serverHostNameAndPortNotUsed, serverRoleNotUsed, localId = \
commonTools.splitId(id)
bodyHistoryDirectoryPath = os.path.join(
objectsDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
continue
bodyHistoryFileNames = os.listdir(bodyHistoryDirectoryPath)
for bodyHistoryFileName in bodyHistoryFileNames:
bodyHistoryFilePath = os.path.join(
bodyHistoryDirectoryPath, bodyHistoryFileName)
os.remove(bodyHistoryFilePath)
os.rmdir(bodyHistoryDirectoryPath)
return None
def repairVirtualServer(self, virtualServer, version):
changed = 0
if version < 3000:
@ -827,44 +445,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
if not object.__dict__.has_key('language'):
changed = 1
object.language = 'fr'
if version < 5001:
hostName = extractApplicationHostName(
virtualServer.virtualServerId)
virtualServerDataDirectoryPath = os.path.join(
self.dataDirectoryPath, hostName)
objectsDirectoryPath = os.path.join(
virtualServerDataDirectoryPath, self.applicationRole)
for id in virtualServer.objects.keys():
serverHostNameAndPort, serverRole, localId = \
commonTools.splitId(id)
bodyFilePath = os.path.join(objectsDirectoryPath, localId)
if not os.access(bodyFilePath, os.F_OK):
continue
bodyHistoryDirectoryPath = os.path.join(
objectsDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
continue
fileNames = os.listdir(bodyHistoryDirectoryPath)
if not fileNames:
continue
for fileName in fileNames:
editionTimeString, editorLocalId, format = \
fileName.split('-')
if len(editorLocalId) > 4:
# The editorId is already encoded into base64.
continue
editorId = 'glasnost://%s/people/%s' % (
hostName, editorLocalId)
newFileName = \
'%(editionTime)s-%(editorIdEncoded)s-%(format)s' % {
'editionTime': editionTimeString,
'editorIdEncoded': base64.encodestring(
editorId).strip(),
'format': format,
}
os.rename(
os.path.join(bodyHistoryDirectoryPath, fileName),
os.path.join(bodyHistoryDirectoryPath, newFileName))
if version < 5004:
changed = virtualServer.admin.repair(5004) or changed
hostName = extractApplicationHostName(
@ -890,25 +470,6 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
os.chmod(bodyFilePath, 0640)
file.write(repairedBody)
file.close()
bodyHistoryDirectoryPath = os.path.join(
objectsDirectoryPath, localId + '-history')
if not os.access(bodyHistoryDirectoryPath, os.F_OK):
continue
fileNames = os.listdir(bodyHistoryDirectoryPath)
if not fileNames:
continue
for fileName in fileNames:
filePath = os.path.join(bodyHistoryDirectoryPath, fileName)
file = open(filePath, 'rb')
body = file.read()
file.close()
repairedBody = body.replace('\r\n', '\n')
repairedBody = repairedBody.replace('\r', '\n')
if repairedBody != body:
file = open(filePath, 'wb')
os.chmod(filePath, 0640)
file.write(repairedBody)
file.close()
if version <= 1021000:
admin = virtualServer.admin
if admin.id is None:

View File

@ -489,7 +489,7 @@ class Dispatcher(Server):
rolesToRemove = []
for r in currentRolesUniq.keys():
if not r in futureRoles:
rolestoRemove.append(r)
rolesToRemove.append(r)
for serverInfo in virtualServer.serverInfos.values():
serverAccessor = serverInfo['accessor']

View File

@ -323,7 +323,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer):
gpg = Gpg(email = object.email, fingerprint = object.fingerprint)
gpg.addKey()
except: # raise Exception('GnuPG not configured for the user')
# it should be a real fault that could be catched specifically
# it should be a real fault that could then be catched specifically
pass
return object.id
@ -669,3 +669,4 @@ peopleServer = PeopleServer()
if __name__ == "__main__":
peopleServer.launch(applicationName, applicationRole)

View File

@ -379,6 +379,8 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin,
[translation.sourceStringDigest,
utf8(translation.getLabel()),
len(translation.sourceString.split())])
if digestsAndLabelsCount == -1:
continue
if len(digestsAndLabels) >= digestsAndLabelsCount:
break
return digestsAndLabels

View File

@ -94,6 +94,7 @@ class UploadFile(ObjectServerMixin, UploadFileCommon):
self, objectDirectoryPath = objectDirectoryPath,
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
self.loadData()
self.loadProperties()
def loadData(self):
virtualServerId = self.getServer().computeVirtualServerId(self.id)
@ -108,10 +109,6 @@ class UploadFile(ObjectServerMixin, UploadFileCommon):
except IOError:
if self.__dict__.has_key('data'):
del self.data
if self.__dict__.has_key('height'):
del self.height
if self.__dict__.has_key('width'):
del self.width
if self.__dict__.has_key('size'):
del self.size
else:
@ -121,18 +118,51 @@ class UploadFile(ObjectServerMixin, UploadFileCommon):
dataFile.close()
# XMLRPC doesn't implements the handling of long integers.
self.size = int(os.stat(dataFilePath)[ST_SIZE])
if self.isType('image') and PILImage:
uploadFileFile = cStringIO.StringIO(self.data)
try:
uploadFileObject = PILImage.open(uploadFileFile)
except IOError:
pass
else:
self.width, self.height = uploadFileObject.size
def loadProperties(self):
self.properties = []
self.values = {}
def newProperty(name, kindName, label, value):
property = commonTools.newThing('other', 'Property')
property.name = name
property.kind = commonTools.newThing('kind', kindName)
property.kind.isTranslatable = 0
property.kind.label = label
self.values[name] = value
self.properties.append(property)
if self.isType('image') and PILImage and self.data:
uploadFileFile = cStringIO.StringIO(self.data)
try:
uploadFileObject = PILImage.open(uploadFileFile)
except IOError:
pass
else:
width, height = uploadFileObject.size
newProperty('extraWidth', 'Integer', N_('Width'), width)
newProperty('extraHeight', 'Integer', N_('Height'), height)
if hasattr(uploadFileObject, '_getexif'):
exifData = uploadFileObject._getexif()
exifKeys = { # gotten experimentally from gthumb;
# TODO: read libexif
271: ('make', N_('Make')),
272: ('model', N_('Model')),
306: ('datetime', N_('Date')),
}
for key, value in exifKeys.items():
if not exifData.has_key(key):
continue
newProperty(value[0], 'String', value[1],
str(exifData[key]))
def releaseNonCore(self, parentSlot = None):
if self.__dict__.has_key('data'):
del self.data
if self.__dict__.has_key('properties'):
del self.properties
ObjectServerMixin.releaseNonCore(self, parentSlot)
def removeDataFile(self):
@ -182,14 +212,6 @@ class UploadFile(ObjectServerMixin, UploadFileCommon):
dataFile.close()
# XMLRPC doesn't implements the handling of long integers.
self.size = int(os.stat(dataFilePath)[ST_SIZE])
if self.isType('image') and PILImage:
uploadFileFile = cStringIO.StringIO(self.data)
try:
uploadFileObject = PILImage.open(uploadFileFile)
except IOError:
pass
else:
self.width, self.height = uploadFileObject.size
def saveNonCore(self, objectDirectoryPath = None, dataDirectoryPath = None,
parentSlot = None):
@ -466,3 +488,4 @@ uploadFilesServer = UploadFilesServer()
if __name__ == "__main__":
uploadFilesServer.launch(applicationName, applicationRole)

View File

@ -45,7 +45,10 @@ __doc__ = """Glasnost Upload Files Common Models"""
__version__ = '$Revision$'[11:-2]
import faults
from ObjectsCommon import AdminCommon, ObjectCommon, ObjectsCommonMixin
import slots
import tools_new as commonTools
class AdminUploadFilesCommon(AdminCommon):
@ -74,14 +77,16 @@ class UploadFileCommon(ObjectCommon):
dataType_kind_isTranslatable = 0
dataType_kindName = 'String'
height = None
height_kind_importExport = 'from-server-only'
height_kind_useCustomStorage = 1
height_kindName = 'Integer'
modificationTime = None
modificationTime_kindName = 'ModificationTime'
properties = None
properties_kind_importExport = 'from-server-only'
properties_kind_label = N_('Extra')
properties_kindName = 'Properties'
values = None
readersSet = None
readersSet_kindName = 'ReadersSet'
@ -97,11 +102,6 @@ class UploadFileCommon(ObjectCommon):
title_kind_isRequired = 1
title_kindName = 'String'
width = None
width_kind_importExport = 'from-server-only'
width_kind_useCustomStorage = 1
width_kindName = 'Integer'
writersSet = None
writersSet_kindName = 'WritersSet'
@ -126,6 +126,128 @@ class UploadFileCommon(ObjectCommon):
def isType(self, type):
return self.dataType and self.dataType.startswith(type)
def importFromXmlRpc(self, dataImport, parentSlot = None):
# The slots 'properties' must be imported first,
# because it is needed to compute the slotNames list.
if dataImport.has_key('__noneSlotNames__'):
noneSlotNames = dataImport['__noneSlotNames__']
else:
noneSlotNames = []
for slotName in ['properties']:
if dataImport.has_key(slotName):
exportedValue = dataImport[slotName]
elif slotName in noneSlotNames:
exportedValue = None
else:
continue
slot = self.getSlot(slotName, parentSlot = parentSlot)
kind = slot.getKind()
if not kind.isImportable():
continue
slot.setValue(kind.importValueFromXmlRpc(slot, exportedValue))
return ObjectCommon.importFromXmlRpc(
self, dataImport, parentSlot = parentSlot)
### TODO: those methods could be moved to ObjectsCommon (?)
def getDirectPropertyNames(self):
if self.properties is None:
return []
return [property.name for property in self.properties]
def getDirectPropertyValue(self, propertyName):
if not self.hasDirectPropertyValue(propertyName):
raise faults.MissingItem(propertyName)
return self.values[propertyName]
def getDirectPropertyValueKind(self, propertyName):
if self.properties:
for property in self.properties:
if property.name == propertyName:
return property.kind
return None
def getPropertiesCount(self):
return len(self.getPropertyNames())
def getPropertyNames(self):
if self.properties is None:
return []
return [property.name for property in self.properties]
def getPropertySlot(self, propertyName, parentSlot = None):
propertiesSlot = self.getSlot('properties', parentSlot = parentSlot)
return slots.Property(propertyName, parent = propertiesSlot)
def getPropertyValue(self, propertyName, propertyKind):
return self.getDirectPropertyValue(propertyName)
def getPropertyValueKind(self, propertyName):
kind = self.getDirectPropertyValueKind(propertyName)
if kind is not None:
return kind
raise Exception('Property "%s" of %s has no kind' % (
propertyName, self))
def getPropertyValueSlot(self, propertyName, parentSlot = None):
if parentSlot is None:
container = self
else:
container = None
return slots.PropertyValue(
propertyName, container = container, parent = parentSlot)
def getSlot(self, attributeName, parentSlot = None):
if attributeName in ObjectCommon.getSlotNames(
self, parentSlot = parentSlot):
return ObjectCommon.getSlot(
self, attributeName, parentSlot = parentSlot)
return self.getPropertyValueSlot(
attributeName, parentSlot = parentSlot)
def getSlotNames(self, parentSlot = None):
slotNames = ObjectCommon.getSlotNames(self, parentSlot = parentSlot)
return slotNames + self.getPropertyNames()
def hasDirectPropertyValue(self, propertyName):
return self.values and self.values.has_key(propertyName)
def setDirectPropertyValue(self, propertyName, value):
if self.values is None:
self.values = {}
self.values[propertyName] = value
def setDirectPropertyValueKind(self, propertyName, propertyKind):
if self.properties is None:
self.properties = []
else:
for i in range(len(self.properties)):
if self.properties[i].name == propertyName:
if propertyKind is None:
del self.properties[i]
return
else:
property = self.properties[i]
property.kind = propertyKind
return
if propertyKind is not None:
property = commonTools.newThing('other', 'Property')
property.kind = propertyKind
property.name = propertyName
self.properties.append(property)
def setPropertyValue(self, propertyName, propertyKind, value):
if self.values is None:
self.values = {}
if value == self.getInheritedPropertyValue(propertyName, propertyKind):
if self.values.has_key(propertyName):
del self.values[propertyName]
if not self.values:
del self.values
else:
self.values[propertyName] = value
class UploadFilesCommonMixin(ObjectsCommonMixin):
adminClassName = 'AdminUploadFiles'
@ -136,3 +258,4 @@ class UploadFilesCommonMixin(ObjectsCommonMixin):
objectsName = N_('files')
objectsNameCapitalized = N_('Files')
serverRole = 'uploadfiles'

View File

@ -91,6 +91,7 @@ class Cache:
del self.cachedValues[k]
except faults.UnknownServerId:
self.disabled = 1
self.cachedValues = {}
cache = Cache()

View File

@ -93,6 +93,7 @@ faultCodeUnknownCommandAction = 40
faultCodeValueTooBig = 41
faultCodeValueTooSmall = 42
faultCodeRoleNotInProfiles = 43
faultCodeUnknownObjectVersion = 44
faultCodeUnknownVoteToken = 1000
faultCodeUnknownVoterToken = 1001
@ -560,6 +561,15 @@ class RoleNotInProfiles(BaseFault):
def makeFaultString(self, role):
return 'Role (%s) not in profiles' % role
class UnknownObjectVersion(BaseFault):
faultCode = faultCodeUnknownObjectVersion
uiFaultString = N_('Unknown object revision')
def makeFaultString(self, objectId, versionNumber):
return 'Unknown version (%d) for object %s' % (versionNumber, objectId)
# Dataflow.

View File

@ -1391,8 +1391,10 @@ class SpipParser:
if localId:
dispatcherHost = context.getVar('dispatcherId')[11:]
serverRole = 'uploadfiles'
id = '[{glasnost:partialid:%s:%s}]' % (serverRole, localId)
name = '[{glasnost:label:%s:%s}]' % (serverRole, localId)
id = '[{glasnost:partialid:%s:%s:%s}]' % (
dispatcherHost, serverRole, localId)
name = '[{glasnost:label:%s:%s:%s}]' % (
dispatcherHost, serverRole, localId)
if (width or height) and self.formatter.prescaleImage():
url = '[{glasnost:thumbnail:%s:%s:%s:width=%s:height=%s}]' % (
dispatcherHost, serverRole, localId, width, height)

View File

@ -77,3 +77,4 @@ class Property(things.BaseThing):
slotNames += ['name', 'kind']
return slotNames
things.register(Property)

View File

@ -104,17 +104,6 @@ class ArticlesProxy(ArticlesCommonMixin, ObjectsProxy):
objectId, requiredSlotNames, multiCall = multiCall)
return [lazyObject() for lazyObject in multiCall.call()]
def getObjectDiff(self, objectId, editionTime):
userToken = context.getVar('userToken', default = '')
result = callServer(
commonTools.extractServerId(objectId),
'getObjectDiff',
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId,
editionTime])
result['diff'] = [ iso8859_15(line) for line in result['diff']]
return result
def getObjectDocBookChapter(self, objectId):
userToken = context.getVar('userToken', default = '')
return iso8859_15(callServer(
@ -123,14 +112,6 @@ class ArticlesProxy(ArticlesCommonMixin, ObjectsProxy):
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId]))
def getObjectHistory(self, objectId):
userToken = context.getVar('userToken', default = '')
return callServer(
commonTools.extractServerId(objectId),
'getObjectHistory',
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId])
def getObjectLatexChapter(self, objectId):
userToken = context.getVar('userToken', default = '')
return iso8859_15(callServer(
@ -139,17 +120,6 @@ class ArticlesProxy(ArticlesCommonMixin, ObjectsProxy):
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId]))
def getObjectVersion(self, objectId, editionTime):
userToken = context.getVar('userToken', default = '')
result = callServer(
commonTools.extractServerId(objectId),
'getObjectVersion',
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId,
editionTime])
result['body'] = iso8859_15(result['body'])
return result
def search(self, searchTerms, scope, language, serverId = None):
userToken = context.getVar('userToken', default = '')
serverId = self.getServerId(serverId = serverId)

View File

@ -1391,6 +1391,15 @@ class ObjectsProxy(ObjectsCommonMixin, AdministrableProxyMixin, Proxy):
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId, utf8(path), digest]))
def getObjectWithVersion(self, objectId, versionNumber):
userToken = context.getVar('userToken', default = '')
objectImport = callServer(
commonTools.extractServerId(objectId),
'getObjectWithVersion',
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId, versionNumber])
return commonTools.importThing(objectImport)
def getPartialObject(self, objectId, requiredSlotNames, multiCall = None):
"""Retrieve partial object instance from the server.
@ -1449,6 +1458,14 @@ class ObjectsProxy(ObjectsCommonMixin, AdministrableProxyMixin, Proxy):
resultHandler = handleResult,
multiCall = multiCall)
def getRevisionsInfos(self, objectId):
userToken = context.getVar('userToken', default = '')
return callServer(
commonTools.extractServerId(objectId),
'getRevisionsInfos',
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId])
def hasObject(self, objectId):
"""Test if the server contains an object.

View File

@ -73,3 +73,4 @@ class UploadFilesProxy(UploadFilesCommonMixin, ObjectsProxy):
[commonTools.extractServerId(objectId), getApplicationToken(),
userToken, objectId, width, height])
return commonTools.importThing(objectImport)

View File

@ -46,6 +46,7 @@ __version__ = '$Revision$'[11:-2]
import copy
import cPickle
import fcntl
import getopt
import os
@ -53,6 +54,7 @@ import re
import shutil
import signal
import sys
import time
from threading import Lock
import traceback
import UserDict
@ -632,6 +634,50 @@ class ObjectsVirtualServer(AdministrableVirtualServer):
self.nextLocalId += 1
return result
def getObjectWithVersion(self, objectId, versionNumber):
server = context.getVar('server')
rcFilePath = os.path.join(
self.dataDirectoryPath,
server.applicationName + '.revisions.pickle')
try:
fd = open(rcFilePath)
except IOError:
return None
while 1:
try:
revision = cPickle.load(fd)
except EOFError:
break
if revision['object'].id != objectId:
continue
if revision['object'].version != versionNumber:
continue
return revision['object']
return None
def getRevisionsInfos(self, objectId):
server = context.getVar('server')
rcFilePath = os.path.join(
self.dataDirectoryPath,
server.applicationName + '.revisions.pickle')
try:
fd = open(rcFilePath)
except IOError:
return []
revisions = []
while 1:
try:
revision = cPickle.load(fd)
except EOFError:
break
if revision['object'].id != objectId:
continue
revision['version'] = revision['object'].version
del revision['object']
revisions.append(revision)
fd.close()
return revisions
def importNonCore(self, importDirectoryPath):
AdministrableVirtualServer.importNonCore(self, importDirectoryPath)
for object in self.objects.values():
@ -671,8 +717,41 @@ class ObjectsVirtualServer(AdministrableVirtualServer):
for object in self.objects.values():
object.removeIds(rolesToKeep)
def saveObjectRevision(self, objectId):
if self.isReadOnly:
return
server = context.getVar('server')
if not server.useDataFile:
return
object = self.loadObjectCore(objectId)
object.acquireNonCore()
data = {
'time': time.time(),
'userId': getProxyForServerRole('authentication').getUserId(),
'object': object
}
try:
if not os.access(self.dataDirectoryPath, os.F_OK):
os.mkdir(self.dataDirectoryPath)
os.chmod(self.dataDirectoryPath, 0750)
rcFilePath = os.path.join(
self.dataDirectoryPath,
server.applicationName + '.revisions.pickle')
self.lock.acquire()
rcFile = open(rcFilePath, 'a')
cPickle.dump(data, rcFile)
rcFile.close()
os.chmod(rcFilePath, 0640)
self.lock.release()
finally:
object.releaseNonCore()
def savePendingRequests(self, saveNeeded = 0):
if self.objectSaveActions is not None:
for objectId in self.objectSaveActions.keys():
if self.objectSaveActions[objectId] != 'save':
continue
self.saveObjectRevision(objectId)
del self.objectSaveActions
saveNeeded = 1
# To remove as soon as the saveAllFIXME method will be removed.
@ -2282,6 +2361,23 @@ class ObjectsServer(AdministrableServerMixin, Server):
object.releaseNonCore()
return result
def getObjectWithVersionXmlRpc(self, objectId, versionNumber):
if not self.canGetObject(objectId):
raise faults.UserAccessDenied()
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
object = virtualServer.getObjectWithVersion(objectId, versionNumber)
if not object:
raise faults.UnknownObjectVersion(objectId, versionNumber)
return object.exportToXmlRpc()
def getRevisionsInfos(self, objectId):
if not self.canGetObject(objectId):
raise faults.UserAccessDenied()
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
return virtualServer.getRevisionsInfos(objectId)
def hasObject(self, objectId):
"""Indicate whether the server contains an object.
@ -2348,8 +2444,7 @@ class ObjectsServer(AdministrableServerMixin, Server):
if not self.canModifyObject(object.id) or not (
self.isAdmin()
or not objectChanges.hasSlotName('writersSet')
or getProxyForServerRole('authentication'
).setContainsUser(
or getProxyForServerRole('authentication').setContainsUser(
objectChanges.getSlot('writersSet').getValue())):
if not object.canBeModifiedByClient():
raise faults.UserAccessDenied()
@ -2484,6 +2579,9 @@ class ObjectsServer(AdministrableServerMixin, Server):
self.getObjectStringFromDigestXmlRpc)
self.registerPublicMethod('getPartialObject',
self.getPartialObjectXmlRpc)
self.registerPublicMethod('getObjectWithVersion',
self.getObjectWithVersionXmlRpc)
self.registerPublicMethod('getRevisionsInfos')
self.registerPublicMethod('hasObject')
self.registerPublicMethod('hasObjectSlot')
self.registerPublicMethod('modifyObject',

View File

@ -135,7 +135,7 @@ class Article(ObjectWebMixin, Article):
context.setVar('pageTitle', title)
sectionLevel = context.getVar('sectionLevel')
titleTag = getattr(X, 'h%s' % (sectionLevel+1))
titleTag = getattr(X, 'h%s' % (sectionLevel))
if not (parentSlot and parentSlot.getLabel() == 'Root') and title:
layout += titleTag(_class = 'title')(title)
@ -192,54 +192,6 @@ class ArticlesWeb(ObjectsWebMixin, CommentableObjectMixin, ArticlesProxy):
return ObjectsWebMixin.viewAll(self, slotNames = ['editionTime'])
all.isPublicForWeb = 1
def diff(self, id, editionTime):
try:
editionTime = float(editionTime)
except ValueError:
return pageNotFound()
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObjectHistory(id):
return accessForbidden()
article = self.getObject(id)
version = self.getObjectDiff(id, editionTime)
label = article.getLabelTranslated(
context.getVar('readLanguages'))
layout = X.array()
if version['diff']:
for line in version['diff']:
marker = line[0]
line = line[1:]
while line and line[-1] in ['\r', '\n']:
line = line[:-1]
stripped = line.lstrip()
if len(line) - len(stripped) > 0:
newLine = X.array()
for i in range(len(line) - len(stripped)):
newLine += X.nbsp
newLine += stripped
line = newLine
if not line:
line = X.nbsp
if marker == '@':
layout += X.hr(_class = 'diff')
elif marker == '\\':
if stripped == 'No newline at end of file':
continue
layout += X.div(_class = 'diff-error')(line)
elif marker == '+':
layout += X.div(_class = 'diff-new')(line)
elif marker == '-':
layout += X.div(_class = 'diff-old')(line)
else:
layout += X.div(_class = 'diff-context')(line)
layout += X.hr(_class = 'diff')
layout += X.br()
return writePageLayout(layout, _('Differences - %s') % label)
diff.isPublicForWeb = 1
def docBook(self, id):
if not self.hasObject(id):
return pageNotFound()
@ -279,7 +231,7 @@ class ArticlesWeb(ObjectsWebMixin, CommentableObjectMixin, ArticlesProxy):
context.getVar('readLanguages'))
layout = X.array()
layout += X.pre(_class = 'spip')(docBook)
layout += X.pre(docBook)
return writePageLayout(layout, _('DocBook Source - %s') % label)
docBookSource.isPublicForWeb = 1
@ -293,62 +245,15 @@ class ArticlesWeb(ObjectsWebMixin, CommentableObjectMixin, ArticlesProxy):
layout = X.array()
layout += ObjectsWebMixin.getViewNavigationButtonsBarLayout(
self, object, fields)
if self.canGetObjectHistory(object.id):
layout += X.buttonStandalone(
'history', X.idUrl(object.id, 'history'))
#if self.canGetObjectHistory(object.id):
# layout += X.buttonStandalone(
# 'history', X.idUrl(object.id, 'history'))
userToken = context.getVar('userToken', default = '')
if userToken and object.format and object.format != 'text':
layout += X.buttonStandalone(
'source', X.idUrl(object.id, 'source'))
return layout
def history(self, id):
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObjectHistory(id):
return accessForbidden()
article = self.getObject(id)
history = self.getObjectHistory(id)
label = article.getLabelTranslated(
context.getVar('readLanguages'))
layout = X.array()
if len(history) > 0:
table = X.table(_class = 'history')
layout += table
thead = X.thead()
table += thead
thead += X.tr(
X.th(_('Date')),
X.th(_('Author')),
X.th(''),
X.th('') )
tbody = X.tbody()
table += tbody
history.reverse()
for version in history:
editionTime = time.strftime(
'%Y-%m-%d %H:%M:%S',
time.localtime(version['editionTime']))
if version.has_key('editorId'):
editorHypertextLabel= X.objectHypertextLabel(
version['editorId'])
else:
editorHypertextLabel = None
tbody += X.tr(
X.td(editionTime),
X.td(editorHypertextLabel),
X.td(X.buttonStandalone(
'version', X.idUrl(id, 'version').add(
'editionTime', version['editionTime']))),
X.td(X.buttonStandalone('diff', X.idUrl(id, 'diff').add(
'editionTime', version['editionTime']))),
)
layout += X.br()
return writePageLayout(layout, _('History - %s') % label)
history.isPublicForWeb = 1
def source(self, id):
if not self.hasObject(id):
return pageNotFound()
@ -365,41 +270,10 @@ class ArticlesWeb(ObjectsWebMixin, CommentableObjectMixin, ArticlesProxy):
label = article.getLabel()
layout = X.array()
layout += X.pre(_class = 'spip')(body)
layout += X.pre(body)
return writePageLayout(layout, _('Source - %s') % label)
source.isPublicForWeb = 1
def version(self, id, editionTime):
try:
editionTime = float(editionTime)
except ValueError:
return pageNotFound()
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObjectHistory(id):
return accessForbidden()
article = self.getObject(id)
version = self.getObjectVersion(id, editionTime)
article.format = version['format']
article.body = version['body']
slot = article.getSlot('body')
article.body_kind = copy.copy(article.body_kind)
# Deleted in article.getViewLayout() below.
article.body_kind.isTranslatable = 0
if version.has_key('editorId'):
article.lastEditorId = version['editorId']
article.editionTime = version['editionTime']
label = article.getLabelTranslated(context.getVar('readLanguages'))
layout = X.array()
slot = slots.Root(article)
widget = slot.getWidget()
layout += widget.getModelPageBodyLayout(slot, None)
return writePageLayout(layout, _('Version - %s') % label)
version.isPublicForWeb = 1
def viewAll(self):
context.push(_level = 'viewAll',
defaultDispatcherId = context.getVar('dispatcherId'))
@ -435,7 +309,7 @@ class ArticlesWeb(ObjectsWebMixin, CommentableObjectMixin, ArticlesProxy):
self.getViewAllNavigationButtonsBarLayout = \
self.getViewAllNavigationButtonsBarLayoutSomeTimes
layout += self.getViewAllButtonsBarLayout()
del(self.getViewAllNavigationButtonsBarLayout)
del self.getViewAllNavigationButtonsBarLayout
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout, _('Articles'))

View File

@ -107,7 +107,6 @@ class AuthenticationWeb(AdministrableWebMixin, AuthenticationProxy):
userToken = self.getUserToken(authMethod, authObject)
session = context.getVar('session')
sessionToken = context.getVar('sessionToken')
oldSession = session
if session is None:
req = context.getVar('req')
sessionsProxy = getProxyForServerRole('sessions')
@ -122,9 +121,9 @@ class AuthenticationWeb(AdministrableWebMixin, AuthenticationProxy):
context.setVar('session', session)
context.setVar('userToken', userToken)
userId = self.getUserId()
session['userId'] = userId
context.setVar('userId', userId)
session['userToken'] = userToken
session['userId'] = userId
session['isDirty'] = 1
nextUri = context.getVar('nextUri') or ''
if not nextUri:
@ -163,13 +162,13 @@ class AuthenticationWeb(AdministrableWebMixin, AuthenticationProxy):
# getProxyForServerRole('sessions').deleteSession(sessionToken)
# context.delVar('sessionToken')
# context.delVar('session')
if session.has_key('userId'):
del session['userId']
session['isDirty'] = 1
context.setVar('userId', '')
if session.has_key('userToken'):
del session['userToken']
session['isDirty'] = 1
if session.has_key('userId'):
del session['userId']
session['isDirty'] = 1
context.setVar('userToken', '')
# if not context.getVar('sessionTokenInCookie', default = 0):
# # The sessionToken was not stored in a cookie, so don't try to

View File

@ -58,7 +58,7 @@ class GlasnostTALInterpreter(TALInterpreter):
try:
return TALInterpreter.insertHTMLStructure(self, text, repldict)
except HTMLParseError, e:
webTools.sendTalkBack(context.getVar(req),
webTools.sendTalkBack(context.getVar('req'),
'%s\n\n%s\n' % (str(e), text))
if not context.getVar('debug'):
self.stream_write('\n<!-- errors detected in HTML -->\n')

View File

@ -273,16 +273,18 @@ class ObjectsWebMixin(AdministrableWebMixin):
objectName = self.objectName)
confirmDelete.isPublicForWeb = 1
def confirmDelete(self, id):
if id and not self.hasObject(id):
return pageNotFound()
return confirmDelete(
X.idUrl(id, 'delete'),
id = id,
objectName = self.objectName)
confirmDelete.isPublicForWeb = 1
def confirmMakeVersionCurrent(self, id, versionNumber):
layout = X.array()
object = self.getObject(id)
layout += X.p(_('Are you sure you want to replace the current version (%s) of "%s" by this previous version (%s) ?') % (object.version, object.getLabel(), versionNumber))
layout += X.div(_class = 'buttons-bar')(
X.buttonStandalone('replace',
X.idUrl(id, 'makeVersionCurrent/%s' % versionNumber)),
X.buttonStandalone('cancel',
X.idUrl(id, 'version/%s' % versionNumber)) )
return writePageLayout(layout, _('Confirm Replacement'))
confirmMakeVersionCurrent.isPublicForWeb = 1
def delete(self, id):
method = context.getVar('httpMethod')
if id and self.hasObject(id):
@ -302,6 +304,46 @@ class ObjectsWebMixin(AdministrableWebMixin):
return pageNotFound()
delete.isPublicForWeb = 1
def diff(self, id, newVersionNumber, oldVersionNumber = ''):
newVersionNumber = int(newVersionNumber)
if oldVersionNumber:
oldVersionNumber = int(oldVersionNumber)
else:
oldVersionNumber = newVersionNumber - 1
newObject = self.getObjectWithVersion(id, int(newVersionNumber))
oldObject = self.getObjectWithVersion(id, int(oldVersionNumber))
slotNames = newObject.getLayoutSlotNames(None)
if 'modificationTime' in slotNames:
slotNames.remove('modificationTime')
slotNames.insert(slotNames.index('version')+1, 'modificationTime')
layout = X.array()
for slotName in slotNames:
newObjectSlot = newObject.getSlot(slotName)
newValue = newObjectSlot.getValue()
oldObjectSlot = oldObject.getSlot(slotName)
oldValue = oldObjectSlot.getValue()
if newValue == oldValue:
continue
widget = newObjectSlot.getWidget()
newObjectSlot.getKind().isTranslatable = 0
oldObjectSlot.getKind().isTranslatable = 0
diffValue = widget.getHtmlViewDiffValue(
oldObjectSlot, newObjectSlot)
row = X.div(_class = 'row')
layout += row
row += X.span(_class = 'label')(
_(widget.getModelLabel(newObjectSlot)))
cell = X.enclose(diffValue, _class = 'cell')
row += cell
return writePageLayout(layout,
_('%s - Differences between versions') % newObject.getLabel())
diff.isPublicForWeb = 1
def download(self, id, filename = '', path = 'self'):
localId = commonTools.extractLocalId(id)
if localId == '__admin__':
@ -644,11 +686,48 @@ class ObjectsWebMixin(AdministrableWebMixin):
userToken = context.getVar('userToken', default = '')
if userToken:
layout += X.buttonStandalone('view-list', X.actionUrl())
layout += X.buttonStandalone(
'history', X.idUrl(object.id, 'history'))
return layout
def getViewOtherActionButtonsBarLayout(self, object, fields):
return None
def history(self, id):
revisions = self.getRevisionsInfos(id)
revisions.reverse()
object = self.getObject(id)
label = object.getLabelTranslated(context.getVar('readLanguages'))
pageTitle = '%s - %s - %s' % (
_('History'), _(self.objectNameCapitalized), label)
if not revisions:
layout = X.p(_("""No revision informations are available for this object."""))
return writePageLayout(layout, pageTitle)
table = X.table()
table += X.tr(
X.th(_('Date')),
X.th(_('User')),
X.th(''))
for revision in revisions:
revisionTime = time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(revision['time']))
if revision['userId']:
userLabel = X.objectHypertextLabel(revision['userId'])
else:
userLabel = _('Unknown')
td = X.td(X.buttonStandalone('version',
X.idUrl(id,
'version/%d' % revision['version'])))
if revision is not revisions[-1]:
td += X.buttonStandalone('diff',
X.idUrl(id, 'diff/%d' % revision['version']))
table += X.tr(
X.td(revisionTime),
X.td(userLabel),
td)
return writePageLayout(table, pageTitle)
history.isPublicForWeb = 1
def id(self, id):
if not self.hasObject(id):
return pageNotFound()
@ -701,6 +780,14 @@ class ObjectsWebMixin(AdministrableWebMixin):
req.write(data)
return OK
imageEdit.isPublicForWeb = 1
def makeVersionCurrent(self, id, versionNumber):
oldObject = self.getObjectWithVersion(id, int(versionNumber))
currentObject = self.getObject(id)
oldObject.version = currentObject.version
self.modifyObject(oldObject)
return redirect(X.idUrl(id))
makeVersionCurrent.isPublicForWeb = 1
def rss(self):
lastObjects = self.getLastObjects(20, None, None, None)
@ -1013,6 +1100,21 @@ class ObjectsWebMixin(AdministrableWebMixin):
return self.view(id)
use.isPublicForWeb = 1
def version(self, id, versionNumber):
#return self.viewObject(object)
object = self.getObjectWithVersion(id, int(versionNumber))
slot = slots.Root(object)
widget = slot.getWidget()
layout = widget.getModelPageBodyLayout(slot, None)
buttonsBar = X.div(_class = 'buttons-bar')
layout += buttonsBar
buttonsBar += X.buttonStandalone('history', X.idUrl(id, 'history'))
buttonsBar += X.buttonStandalone(_('Make this version current'),
X.idUrl(id, 'confirmMakeVersionCurrent/%s' % versionNumber))
return writePageLayout(layout,
_('%s - Version %d') % (object.getLabel(), object.version))
version.isPublicForWeb = 1
def view(self, id):
if not self.hasObject(id):
return pageNotFound()
@ -1020,7 +1122,10 @@ class ObjectsWebMixin(AdministrableWebMixin):
return accessForbidden()
object = self.getObject(id)
rememberObject(id)
return self.viewObject(object)
view.isPublicForWeb = 1
def viewObject(self, object):
label = object.getLabelTranslated(context.getVar('readLanguages'))
layout = X.array()
@ -1043,7 +1148,6 @@ class ObjectsWebMixin(AdministrableWebMixin):
layout = writePageLayout(layout, pageTitle)
context.pull()
return layout
view.isPublicForWeb = 1
def viewAll(self, slotNames = None):
if type(slotNames) is not types.ListType:

View File

@ -88,8 +88,11 @@ class Rubric(ObjectWebMixin, Rubric):
getObjectLabelTranslated(self.id,
context.getVar('readLanguages')))
object.title = ''
context.push()
else:
context.push(sectionLevel = context.getVar('sectionLevel')+1)
layout += object.getEmbeddedViewLayout()
context.pull()
if web.canModifyObject(self.contentId):
layout += X.buttonStandalone(

View File

@ -325,6 +325,7 @@ register(Localization)
class TranslationsWeb(ObjectsWebMixin, TranslationsProxy):
def canViewAll(self, serverId = None):
userToken = context.getVar('userToken', default = '')
if userToken:
@ -394,6 +395,26 @@ class TranslationsWeb(ObjectsWebMixin, TranslationsProxy):
finally:
context.pull(_level = 'edit')
def everyString(self, localizationKey):
fromLanguage = localizationKey[:2]
toLanguage = localizationKey[2:4]
pageTitle = '%s: %s' % (
_('Translations'),
_('%(from)s to %(to)s') % {
'from': _(translation.languageLabels[
fromLanguage]),
'to': _(translation.languageLabels[toLanguage]),
})
digestsAndLabels = self.getSomeDigestsAndLabels(
localizationKey, -1, ['untranslated'])
layout = self.getObjectsSectionLayout(
localizationKey, digestsAndLabels,
_("""Strings yet to translate"""))
return writePageLayout(layout, pageTitle)
everyString.isPublicForWeb = 1
def getAdmin(self, serverId = None):
admin = TranslationsProxy.getAdmin(self, serverId = serverId)
admin.translationLanguages = []
@ -616,9 +637,16 @@ class TranslationsWeb(ObjectsWebMixin, TranslationsProxy):
otherLocalization['label'])
layout += X.br()
buttonsBar = X.div(_class = 'buttons-bar')()
if localizationKey:
buttonsBar += X.buttonStandalone(
_('Every untranslated string'),
X.actionUrl('everyString/%s' % localizationKey))
if self.canGetAdmin():
layout += X.div(_class = 'buttons-bar')(
X.buttonStandalone('settings', X.actionUrl('admin')))
buttonsBar += X.buttonStandalone(
'settings', X.actionUrl('admin'))
if buttonsBar.children:
layout += buttonsBar
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout, pageTitle)

View File

@ -87,15 +87,6 @@ class UploadFile(ObjectWebMixin, UploadFile):
dataType_kind_widget_fieldLabel = N_('Mime Type')
dataType_kind_widgetName = 'InputText'
height_kind_hasToRepairField = 0
height_kind_hasToSubmitField = 0
height_kind_stateInEditMode = 'read-only'
height_kind_min = 0
height_kind_textMaxLength = 4
height_kind_widget_fieldLabel = N_('Height')
height_kind_widget_size = 4
height_kind_widgetName = 'InputText'
size_kind_hasToSubmitField = 0
size_kind_stateInEditMode = 'read-only'
size_kind_min = 0
@ -108,21 +99,12 @@ class UploadFile(ObjectWebMixin, UploadFile):
title_kind_widget_size = 40
title_kind_widgetName = 'InputText'
width_kind_hasToRepairField = 0
width_kind_hasToSubmitField = 0
width_kind_stateInEditMode = 'read-only'
width_kind_min = 0
width_kind_textMaxLength = 4
width_kind_widget_fieldLabel = N_('Width')
width_kind_widget_size = 4
width_kind_widgetName = 'InputText'
def getEditLayoutSlotNames(self, fields, parentSlot = None):
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
hiddenSlotNames = []
for slotName in ('height', 'size', 'width'):
for slotName in ['size', 'properties'] + self.getPropertyNames():
hiddenSlotNames.append(slotName)
if self.id is None:
for slotName in ('creationTime', 'dataFileName', 'dataType',
@ -138,16 +120,13 @@ class UploadFile(ObjectWebMixin, UploadFile):
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
dataType = self.getSlot('dataType').getValue()
if not isTypeOfMimeType(dataType, 'image'):
if 'height' in slotNames:
slotNames.remove('height')
if 'width' in slotNames:
slotNames.remove('width')
userToken = context.getVar('userToken', default = '')
if 'properties' in slotNames:
slotNames.remove('properties')
if not userToken or context.getVar('useCompactLayout', default = 0):
for slotName in [
'creationTime', 'dataFileName', 'dataType', 'height',
'size', 'modificationTime', 'readersSet', 'width',
'creationTime', 'dataFileName', 'dataType',
'size', 'modificationTime', 'readersSet',
'writersSet']:
if slotName in slotNames:
slotNames.remove(slotName)
@ -199,18 +178,22 @@ class UploadFilesWeb(ObjectsWebMixin, UploadFilesProxy):
ids = self.getObjectIds()
partialObjects = {}
for id in ids:
partialObjects[id] = self.getPartialObject(id, ['title'])
partialObjects[id] = self.getPartialObject(
id, ['title', 'dataType', 'size'])
ids = self.getSortedIds(partialObjects)
layout += self.getObjectsLayout(partialObjects, ids)
layout += self.getObjectsLayout(partialObjects, ids,
slotNames = ['dataType', 'size'])
layout += self.getViewAllButtonsBarLayout()
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout, _(self.objectsNameCapitalized))
all.isPublicForWeb = 1
#uploadFileFile = cStringIO.StringIO(data)
#uploadFileObject = PILImage.open(uploadFileFile)
#width, height = uploadFileObject.size
def getViewAllNavigationButtonsBarLayoutSomeTimes(self):
layout = X.array()
layout += X.buttonStandalone('every-uploadfiles', X.actionUrl('all'))
layout += ObjectsWebMixin.getViewAllNavigationButtonsBarLayout(self)
return layout
def image(self, id, fileName = None):
if not self.hasObject(id):
@ -295,8 +278,7 @@ class UploadFilesWeb(ObjectsWebMixin, UploadFilesProxy):
height = int(height)
except ValueError:
height = 128
object = self.getObjectThumbnail(
id, width, height)
object = self.getObjectThumbnail(id, width, height)
rememberObject(id)
req = context.getVar('req')
@ -315,16 +297,18 @@ class UploadFilesWeb(ObjectsWebMixin, UploadFilesProxy):
if not self.canGetObject(id):
return accessForbidden()
object = UploadFile()
slotNames = [x for x in object.getSlotNames() if x != 'data']
object = self.getPartialObject(id, slotNames)
#object = UploadFile()
#slotNames = [x for x in object.getSlotNames() if x != 'data']
#object = self.getPartialObject(id, slotNames)
# Force the presence of the download link in
# web/widgets.py/UploadFile.getHtmlViewValue().
# note that it fucks up previewing inline OpenOffice.org documents
# that could be converted to html
object.data = 'fake data'
if object.dataType == 'application/vnd.sun.xml.writer':
object = self.getObject(id)
#object.data = 'fake data'
#if object.dataType == 'application/vnd.sun.xml.writer':
# object = self.getObject(id)
object = self.getObject(id)
rememberObject(id)
if isTypeOfMimeType(object.dataType, 'image'):
@ -380,7 +364,11 @@ class UploadFilesWeb(ObjectsWebMixin, UploadFilesProxy):
_("""The last files"""),
displayedSlotNames)
# ArticlesWeb did that; I don't remember why.
self.getViewAllNavigationButtonsBarLayout = \
self.getViewAllNavigationButtonsBarLayoutSomeTimes
layout += self.getViewAllButtonsBarLayout()
del self.getViewAllNavigationButtonsBarLayout
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout, _('Files'))

View File

@ -662,6 +662,11 @@ def writePageLayout(layout, title, canCache = 1):
dict['contextualHeaders'] = '\n'.join(context.getVar('htmlHeaders'))
dict['body'] = layout.getAsXml()
try:
import GlasnostNewTal
except ImportError:
pass
fileName = context.getVar('templateFileName')
if not currentTemplates.has_key(fileName):
# caching this in currentTemplates improves rendering time by about
@ -672,20 +677,22 @@ def writePageLayout(layout, title, canCache = 1):
fallbackPath = context.getVar('fallbackTemplatesDirectoryPath')
try:
file = open(os.path.join(path, templateDirectoryName, fileName))
fd = open(os.path.join(path, templateDirectoryName, fileName))
except IOError:
file = open(os.path.join(fallbackPath, 'default', fileName))
fd = 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)
t.parseString(fd.read())
### with python-simpletal, it would be
### t = GlasnostNewTal.compileHTMLTemplate(fd)
currentTemplates[fileName] = t
t = currentTemplates[fileName]
program, macros = t.getCode()
# this takes roughly 0.01 seconds, not worth caching
@ -711,6 +718,14 @@ def writePageLayout(layout, title, canCache = 1):
info[0], info[1])[0].strip()
writePageException(exception)
### with python-simpletal:
### ctx = GlasnostNewTal.Context()
### for k, v in dict.items() + WebAPI.getAPIDict().items():
### ctx.addGlobal(k, v)
### interpreter = GlasnostNewTal.TemplateInterpreter()
### interpreter.initialise(ctx, req)
### t.expand(ctx, req, interpreter = interpreter)
if req.caching:
req.closeCachePage()
return OK

View File

@ -168,7 +168,7 @@ def sendTalkBack(req, message):
if req.headers_in.has_key('User-Agent'):
userAgent = req.headers_in['User-Agent']
if req.headers_in.has_key('Host'):
host = '%s (from host header)' % req.headers_in['Host']
host = req.headers_in['Host']
else:
host = '%s (from socket.getfqdn())' % socket.getfqdn()
message = """From: %(from)s

View File

@ -48,6 +48,7 @@ __doc__ = """Glasnost Web Widgets"""
__version__ = '$Revision$'[11:-2]
import difflib
import locale
import re
import time
@ -170,6 +171,11 @@ class WidgetMixin(things.ThingMixin):
else:
return self.getHtmlFormValue(slot, fields, **keywords)
def getHtmlViewDiffValue(self, oldSlot, newSlot, **keywords):
return X.table(_class = 'diff')(
X.td(self.getHtmlViewValue(oldSlot, None, **keywords)),
X.td(self.getHtmlViewValue(newSlot, None, **keywords)) )
def getHtmlViewValue(self, slot, fields, **keywords):
fieldValue = slot.getValue() or ''
format = slot.getKind().getTextFormat(slot)
@ -1535,6 +1541,30 @@ register(SelectId)
class TextArea(WidgetMixin, proxyWidgets.TextArea):
def getHtmlViewDiffValue(self, oldSlot, newSlot, **keywords):
newValue = newSlot.getValue().split('\n')
oldValue = oldSlot.getValue().split('\n')
try:
difflib.ndiff
except AttributeError:
# no ndiff function in Python 2.1
return WidgetMixin.getHtmlViewDiffValue(
self, oldSlot, newSlot, **keywords)
result = []
for line in difflib.ndiff(oldValue, newValue):
marker, line = line[0], line[2:]
if marker == ' ':
result.append(line)
elif marker == '-':
result.append('<span class="diff-old">%s</span>' % line)
elif marker == '+':
result.append('<span class="diff-new">%s</span>' % line)
elif marker == '?':
continue
return X.pre(_class = 'diff')(X.asIs('\n'.join(result)))
def getHtmlViewValue(self, slot, fields, **keywords):
fieldValue, translationBar = \
translation.getFieldValueAndTranslationBar(slot, fields)

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
width: 90%;

View File

@ -1,3 +1,5 @@
@import "form-tb.css";
html, body {
margin: 0;
background: #1f0f10 url('/images/autumn.jpeg') repeat fixed;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
/*background: #170434;*/

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
color: #000000;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -3,7 +3,7 @@
<span metal:define-macro="breadCrumb"
tal:define="objectPath getPathToObject(currentObject)"
tal:condition="objectPath">
<span tal:condition="exists('breadCrumbPrefix')" tal:replace="breadCrumbPrefix" />
<span tal:condition="exists('breadCrumbPrefix')" tal:replace="breadCrumbPrefix">a</span>
<div tal:condition="currentObject" tal:omit-tag="" tal:repeat="o objectPath">
<a tal:attributes="href o.getUrl()" tal:content="o.label">breadCrumb</a> &rarr;
</div>

View File

@ -1,4 +1,4 @@
<doc metal:use-macro="StandardLookAndFeel.html/pre"/>
<doc metal:use-macro="StandardLookAndFeel.html/pre"></doc>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
metal:use-macro="StandardLookAndFeel.html/master">
@ -9,7 +9,7 @@
<body>
<div metal:fill-slot="main" tal:omit-tag="">
<h1 tal:condition="title" tal:content="title">the title goes here</h1>
<span tal:replace="structure body" />
<span tal:replace="structure body">body</span>
</div>
</body>
</html>

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -854,3 +854,18 @@ ul.article-meta li {
line-height: 1em;
}
table.diff {
width: 95%;
}
table.diff td {
width: 45%;
border: 1px solid black;
vertical-align: top;
}
table.diff div.changed {
margin: 0;
background-color: #ffff80;
}

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* Default */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
background: white;

View File

@ -48,11 +48,11 @@
</div>
<div id="footer">
<span metal:use-macro="buttons.tal/preferences" />
<span metal:use-macro="buttons.tal/admin" />
<span metal:use-macro="buttons.tal/logout" />
<span metal:use-macro="buttons.tal/newAccount" />
<span metal:use-macro="buttons.tal/login" />
<span metal:use-macro="buttons.tal/preferences"></span>
<span metal:use-macro="buttons.tal/admin"></span>
<span metal:use-macro="buttons.tal/logout"></span>
<span metal:use-macro="buttons.tal/newAccount"></span>
<span metal:use-macro="buttons.tal/login"></span>
</div>
</body>
</html>

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
html, body {
margin: 0;
@ -208,12 +209,12 @@ div#commands a:active {
}
div#main-content a.button, input.button {
div#main-content a.button, div#main-content input.button {
color: black;
border: 1px outset #aaa;
}
div#main-content a.button:active, input.button:active {
div#main-content a.button:active, div#main-content input.button:active {
border: 1px inset #aaa;
}

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
color: black;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
html, body {
background: #f5b72e;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
width: 90%;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
html, body {
margin: 0;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
html, body {
margin: 0;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -34,28 +34,28 @@
<div id="tabs">
<a tal:attributes="href rootUrl()" tal:translate="">Home</a>
<div metal:use-macro="nav.tal/aBlockOneLevelAllObjects" />
<div metal:use-macro="nav.tal/aBlockOneLevelAllObjects"></div>
</div>
<div id="personalBar">
<a class="button user" tal:condition="user" tal:attributes="href user.getUrl()" tal:content="user">userName</a>
<span tal:condition="not user" tal:translate="">You are not logged in</span>
<span metal:use-macro="buttons.tal/login" />
<span metal:use-macro="buttons.tal/preferences" />
<span metal:use-macro="buttons.tal/admin" />
<span metal:use-macro="buttons.tal/logout" />
<span metal:use-macro="buttons.tal/about" />
<span metal:use-macro="buttons.tal/login">login</span>
<span metal:use-macro="buttons.tal/preferences">preferences</span>
<span metal:use-macro="buttons.tal/admin">admin</span>
<span metal:use-macro="buttons.tal/logout">logout</span>
<span metal:use-macro="buttons.tal/about">about</span>
</div>
<div id="pathBar" tal:condition="fileName.startswith('index') or currentObject"
tal:define="breadCrumbPrefix 'Vous êtes ici: '">
<span metal:use-macro="misc.tal/breadCrumb" />
<span metal:use-macro="misc.tal/breadCrumb">breadCrumb</span>
</div>
<div id="leftBar">
<div id="navBox" tal:condition="user">
<span class="boxTitle">Navigation</span>
<ul metal:use-macro="nav.tal/ulRoles" />
<ul metal:use-macro="nav.tal/ulRoles"></ul>
</div>
</div>

View File

@ -1,4 +1,5 @@
@import url("/css/balloonHelp.css");
@import "balloonHelp.css";
@import "form-tb.css";
body {
font-family: sans-serif;
@ -395,16 +396,16 @@ div#loginBox div a.button {
border: 0;
}
table.rst, table.spip, table.objects-table {
table {
border: 1px solid #8cacbb;
border-collapse: collapse;
}
table.spip a, table.objects-table a {
table a {
text-decoration: none;
}
table.rst th, table.spip th, table.objects-table th {
th {
background: #dee7ec;
border: 1px solid #8cacbb;
color: black;
@ -414,7 +415,7 @@ table.rst th, table.spip th, table.objects-table th {
white-space: nowrap;
}
table.rst td, table.spip td, table.objects-table td {
td {
border-left: 1px solid #8cacbb;
padding: 0em 1em;
text-align: left;
@ -679,36 +680,26 @@ div.system-message {
font-size: 80%;
}
div.diff-context {
table.diff {
width: 95%;
}
div.diff-error {
font-size: 80%;
table.diff td {
width: 45%;
vertical-align: top;
}
div.diff-new {
background-color: #80ff80;
}
div.diff-old {
background-color: #ffff80;
}
hr.diff {
}
span.diff-equal {
white-space: pre;
pre.diff {
background: inherit;
width: 95%;
}
span.diff-new {
background-color: #80ff80;
white-space: pre;
background-color: #8f8;
}
span.diff-old {
background-color: #ffff80;
white-space: pre;
background-color: #f88;
}
span.spellcheck {

View File

@ -1,4 +1,5 @@
@import url("/css/balloonHelp.css");
@import "balloonHelp.css";
@import "form-tb.css";
html, body {
margin: 0;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
background: white;

View File

@ -1,3 +1,5 @@
@import "form-tb.css";
html, body {
margin: 0;
font-family: sans-serif;
@ -109,34 +111,6 @@ div#content form {
margin-top: 1em;
}
div.row {
margin-bottom: 0.5em;
}
div.row .label {
display: block;
}
div.row .tooltip {
display: block;
font-style: italic;
font-size: smaller;
margin-left: 2em;
display: none;
}
div.row .cell {
margin-left: 2em;
}
div.row div.cell span.cell {
margin: 0;
}
div.fieldset span.cell {
display: block;
}
div#content div.error-message h1 {
color: red;
border: 0;
@ -270,3 +244,68 @@ pre {
padding: 0.5em;
}
div.dropdown {
margin: 0;
padding: 0;
width: 10em;
float: left;
margin-right: 1em;
font-size: 80%;
}
div.dropdown ul {
list-style: none;
margin: 0;
padding: 0;
border: 1px solid #ccc;
border-top: 0;
display: none;
}
div.dropdown span {
display: block;
border: 1px solid #ccc;
background: white url('/images/v.png') top right no-repeat;
}
div.dropdown:hover span {
background: #eee;
}
div.dropdown span:after {
content: "...";
}
div.dropdown:hover span:after {
content: "";
}
div.dropdown li {
padding: 0;
}
div.dropdown:hover ul {
display: block;
background: white;
/*background: url('/images/ntbg.png');*/
}
div#commands a {
display: block;
color: black;
border: 1px solid white;
text-decoration: none;
padding-left: 0.3em;
}
div#commands a:hover {
background: #eee;
border: 1px outset black;
}
div#commands a:active {
border: 1px inset black;
}

View File

@ -1,4 +1,5 @@
@import url("/css/balloonHelp.css");
@import "balloonHelp.css";
@import "form-lr.css";
/* 2 columns, header and footer layout working with NN4
thanks to http://realworldstyle.com/ */

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
body {
background: white url(/images/upthings.jpeg) fixed top left no-repeat;

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-lr.css";
html, body {
background: #10743d;

View File

@ -1,4 +1,5 @@
@import url("/css/balloonHelp.css");
@import "balloonHelp.css";
@import "form-lr.css";
html, body {
font-family: sans-serif;
@ -135,16 +136,7 @@ div#content form {
}
div.row .label {
width: 9em;
float: left;
font-weight: bold;
text-align: right;
padding-right: 1em;
}
div.row .cell {
display: block;
margin-left: 10em;
}
div.row {

View File

@ -1,4 +1,5 @@
@import "default.css";
@import "form-tb.css";
/********************************************
* entrouvert css
*

View File

@ -11,10 +11,6 @@ testSuites = (
'GroupsTests',
'KindsTests',
'VirtualHostsTests',
'CardsTests',
'GroupsTests',
'KindsTests',
'VirtualHostsTests',
'AuthenticationLoginPasswordTests',
'ArticlesTests',
'SpipTests',