982 lines
44 KiB
Python
Executable File
982 lines
44 KiB
Python
Executable File
#!/usr/bin/env 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.
|
|
|
|
|
|
__doc__ = """Glasnost Articles Server"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import base64
|
|
import fcntl
|
|
import os
|
|
import sys
|
|
import time
|
|
import re
|
|
|
|
glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
|
|
sys.path.insert(0, glasnostPythonDir)
|
|
|
|
import glasnost
|
|
|
|
from glasnost.common.ArticlesCommon import *
|
|
import glasnost.common.tools_new as commonTools
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.xhtmlgenerator as X
|
|
|
|
from glasnost.server.ObjectsServer import register, ObjectServerMixin, \
|
|
AdminServerMixin, ObjectsServer
|
|
from glasnost.server.tools import *
|
|
|
|
from glasnost.proxy.CacheProxy import invalidateValue
|
|
from glasnost.proxy.DispatcherProxy import MultiCall, \
|
|
getApplicationId, getApplicationToken
|
|
from glasnost.proxy.GroupsProxy import getSetContainedIds
|
|
|
|
|
|
applicationName = 'ArticlesServer'
|
|
applicationRole = 'articles'
|
|
dispatcher = None
|
|
|
|
|
|
class AdminArticles(AdminServerMixin, AdminArticlesCommon):
|
|
pass
|
|
register(AdminArticles)
|
|
|
|
|
|
class Article(ObjectServerMixin, ArticleCommon):
|
|
def acquireNonCore(self, objectDirectoryPath = None,
|
|
dataDirectoryPath = None, parentSlot = None):
|
|
ObjectServerMixin.acquireNonCore(
|
|
self, objectDirectoryPath = objectDirectoryPath,
|
|
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 = 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 = 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 = 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):
|
|
# FIXME: support conversion from every text format to DocBook
|
|
assert self.format == 'spip'
|
|
from glasnost.proxy.tools import makeDocBookFromSpip
|
|
return makeDocBookFromSpip(self.body, docType = 'chapter',
|
|
addHeader = 0, title = self.title)
|
|
|
|
def getLatexChapter(self):
|
|
# FIXME: support conversion from every text format to LaTeX
|
|
assert self.format == 'spip'
|
|
from glasnost.proxy.tools import makeLatexFromSpip
|
|
return makeLatexFromSpip(self.body, docType = 'chapter',
|
|
addHeader = 0, title = self.title)
|
|
|
|
def loadBody(self):
|
|
virtualServerId = self.getServer().computeVirtualServerId(self.id)
|
|
virtualServer = self.getServer().getVirtualServer(virtualServerId)
|
|
articlesDirectoryPath = os.path.join(
|
|
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
|
|
serverHostNameAndPort, serverRole, localId = splitId(self.id)
|
|
bodyFilePath = os.path.join(articlesDirectoryPath, localId)
|
|
try:
|
|
bodyFile = open(bodyFilePath, 'rb')
|
|
except IOError:
|
|
if self.__dict__.has_key('body'):
|
|
del self.body
|
|
else:
|
|
fcntl.lockf(bodyFile, fcntl.LOCK_SH)
|
|
self.body = bodyFile.read()
|
|
fcntl.lockf(bodyFile, fcntl.LOCK_UN)
|
|
bodyFile.close()
|
|
|
|
def modify(self, changes, givenSlotNames = None):
|
|
if (not givenSlotNames or 'body' in givenSlotNames) \
|
|
and changes.body != self.body:
|
|
if changes.body is None:
|
|
if self.lastEditorId:
|
|
del self.lastEditorId
|
|
if self.editionTime:
|
|
del self.editionTime
|
|
else:
|
|
userId = getProxyForServerRole('authentication').getUserId()
|
|
if userId:
|
|
self.lastEditorId = userId
|
|
self.editionTime = time.time()
|
|
ObjectServerMixin.modify(
|
|
self, changes, givenSlotNames = givenSlotNames)
|
|
|
|
def releaseNonCore(self, parentSlot = None):
|
|
if self.__dict__.has_key('body'):
|
|
del self.body
|
|
ObjectServerMixin.releaseNonCore(self, parentSlot = parentSlot)
|
|
|
|
def removeBodyFile(self):
|
|
virtualServerId = self.getServer().computeVirtualServerId(self.id)
|
|
virtualServer = self.getServer().getVirtualServer(virtualServerId)
|
|
articlesDirectoryPath = os.path.join(
|
|
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
|
|
serverHostNameAndPort, serverRole, localId = splitId(self.id)
|
|
bodyFilePath = os.path.join(articlesDirectoryPath, localId)
|
|
try:
|
|
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 = 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):
|
|
ObjectServerMixin.removeNonCore(
|
|
self, objectDirectoryPath = objectDirectoryPath,
|
|
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
|
|
self.removeBodyFile()
|
|
self.removeBodyHistory()
|
|
|
|
def saveBody(self):
|
|
if self.body is None:
|
|
self.removeBodyFile()
|
|
return
|
|
virtualServerId = self.getServer().computeVirtualServerId(self.id)
|
|
virtualServer = self.getServer().getVirtualServer(virtualServerId)
|
|
articlesDirectoryPath = os.path.join(
|
|
virtualServer.dataDirectoryPath, self.getServer().applicationRole)
|
|
if not os.access(virtualServer.dataDirectoryPath, os.F_OK):
|
|
os.mkdir(virtualServer.dataDirectoryPath)
|
|
os.chmod(virtualServer.dataDirectoryPath, 0750)
|
|
if not os.access(articlesDirectoryPath, os.F_OK):
|
|
os.mkdir(articlesDirectoryPath)
|
|
os.chmod(articlesDirectoryPath, 0750)
|
|
serverHostNameAndPort, serverRole, localId = splitId(self.id)
|
|
bodyFilePath = os.path.join(articlesDirectoryPath, localId)
|
|
bodyFile = open(bodyFilePath, 'wb')
|
|
os.chmod(bodyFilePath, 0640)
|
|
fcntl.lockf(bodyFile, fcntl.LOCK_EX)
|
|
bodyFile.write(self.body)
|
|
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 = 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(
|
|
self, objectDirectoryPath = objectDirectoryPath,
|
|
dataDirectoryPath = dataDirectoryPath, parentSlot = parentSlot)
|
|
self.saveBody()
|
|
register(Article)
|
|
|
|
|
|
class ArticlesServer(ArticlesCommonMixin, ObjectsServer):
|
|
def addObjectXmlRpc(self, objectImport):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = commonTools.importThing(objectImport)
|
|
if not self.canAddObject()or (
|
|
not self.isAdmin() and not (
|
|
getProxyForServerRole('authentication').setContainsUser(
|
|
self.getAdminCore().writersSet)
|
|
and getProxyForServerRole('authentication').setContainsUser(
|
|
object.writersSet))):
|
|
if not object.canBeCreatedByClient():
|
|
raise faults.UserAccessDenied()
|
|
object.checkAddIsPossible()
|
|
object.setAutomaticalSlots()
|
|
virtualServer.objects[object.id] = object
|
|
if object.body is not None:
|
|
userId = getProxyForServerRole('authentication').getUserId()
|
|
if userId:
|
|
object.lastEditorId = userId
|
|
object.editionTime = object.modificationTime
|
|
object.saveNonCore()
|
|
object.releaseNonCore()
|
|
virtualServer.markObjectAsDirty(object)
|
|
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(
|
|
self, sourceDispatcherId, destinationDispatcherId)
|
|
if exitCode is not None:
|
|
return exitCode
|
|
destinationVirtualServerId = '%s/%s' % (
|
|
destinationDispatcherId, self.applicationRole)
|
|
virtualServer = self.virtualServers[destinationVirtualServerId]
|
|
sourceHostName = extractApplicationHostName(sourceDispatcherId)
|
|
destinationHostName = extractApplicationHostName(
|
|
destinationDispatcherId)
|
|
sourceVirtualServerDataDirectoryPath = os.path.join(
|
|
self.dataDirectoryPath, sourceHostName)
|
|
destinationVirtualServerDataDirectoryPath = os.path.join(
|
|
self.dataDirectoryPath, destinationHostName)
|
|
sourceObjectsDirectoryPath = os.path.join(
|
|
sourceVirtualServerDataDirectoryPath, self.applicationRole)
|
|
destinationObjectsDirectoryPath = os.path.join(
|
|
destinationVirtualServerDataDirectoryPath, self.applicationRole)
|
|
for id in virtualServer.objects.keys():
|
|
serverHostNameAndPortNotUsed, serverRoleNotUsed, localId = \
|
|
splitId(id)
|
|
sourceBodyFilePath = os.path.join(
|
|
sourceObjectsDirectoryPath, localId)
|
|
destinationBodyFilePath = os.path.join(
|
|
destinationObjectsDirectoryPath, localId)
|
|
if not os.access(sourceBodyFilePath, os.F_OK):
|
|
continue
|
|
if not os.access(
|
|
destinationVirtualServerDataDirectoryPath, os.F_OK):
|
|
os.mkdir(destinationVirtualServerDataDirectoryPath)
|
|
os.chmod(destinationVirtualServerDataDirectoryPath, 0750)
|
|
if not os.access(destinationObjectsDirectoryPath, os.F_OK):
|
|
os.mkdir(destinationObjectsDirectoryPath)
|
|
os.chmod(destinationObjectsDirectoryPath, 0750)
|
|
sourceBodyFile = open(sourceBodyFilePath, 'rb')
|
|
destinationBodyFile = open(destinationBodyFilePath, 'wb')
|
|
os.chmod(destinationBodyFilePath, 0640)
|
|
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):
|
|
exitCode = ObjectsServer.exportVirtualServer(
|
|
self, virtualServerId, exportDirectoryPath)
|
|
if exitCode is not None:
|
|
return exitCode
|
|
virtualServer = self.virtualServers[virtualServerId]
|
|
hostName = extractApplicationHostName(virtualServerId)
|
|
virtualServerDataDirectoryPath = os.path.join(
|
|
self.dataDirectoryPath, hostName)
|
|
objectsDirectoryPath = os.path.join(
|
|
virtualServerDataDirectoryPath, self.applicationRole)
|
|
exportObjectsDirectoryPath = os.path.join(
|
|
exportDirectoryPath, self.applicationRole)
|
|
for id in virtualServer.objects.keys():
|
|
serverHostNameAndPortNotUsed, serverRoleNotUsed, localId = \
|
|
splitId(id)
|
|
sourceBodyFilePath = os.path.join(
|
|
objectsDirectoryPath, localId)
|
|
exportBodyFilePath = os.path.join(
|
|
exportObjectsDirectoryPath, localId)
|
|
if not os.access(sourceBodyFilePath, os.F_OK):
|
|
continue
|
|
if not os.access(exportDirectoryPath, os.F_OK):
|
|
os.mkdir(exportDirectoryPath)
|
|
os.chmod(exportDirectoryPath, 0750)
|
|
if not os.access(exportObjectsDirectoryPath, os.F_OK):
|
|
os.mkdir(exportObjectsDirectoryPath)
|
|
os.chmod(exportObjectsDirectoryPath, 0750)
|
|
sourceBodyFile = open(sourceBodyFilePath, 'rb')
|
|
exportBodyFile = open(exportBodyFilePath, 'wb')
|
|
os.chmod(exportBodyFilePath, 0640)
|
|
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,
|
|
possibleReadersSet, possibleWritersSet):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
isAdmin = self.isAdmin()
|
|
try:
|
|
possibleAuthorIds = getSetContainedIds(
|
|
possibleAuthorsSet, ['people'])
|
|
except faults.UncountableGroup:
|
|
possibleAuthorIds = 'everybody'
|
|
try:
|
|
possibleReaderIds = getSetContainedIds(
|
|
possibleReadersSet, ['people'])
|
|
except faults.UncountableGroup:
|
|
possibleReaderIds = 'everybody'
|
|
try:
|
|
possibleWriterIds = getSetContainedIds(
|
|
possibleWritersSet, ['people'])
|
|
except faults.UncountableGroup:
|
|
possibleWriterIds = 'everybody'
|
|
objectIds = virtualServer.objects.keys()
|
|
def modificationTimeSorter(xId, yId,
|
|
virtualServer = virtualServer):
|
|
return cmp(virtualServer.loadObjectCore(yId).modificationTime,
|
|
virtualServer.loadObjectCore(xId).modificationTime)
|
|
objectIds.sort(modificationTimeSorter)
|
|
result = []
|
|
for objectId in objectIds:
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if not isAdmin and not getProxyForServerRole(
|
|
'authentication').setContainsUser(object.readersSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleAuthorIds, 1, object.authorsSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleReaderIds, 1, object.readersSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleWriterIds, 1, object.writersSet):
|
|
continue
|
|
result.append(objectId)
|
|
if objectsCount != -1 and len(result) >= objectsCount:
|
|
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()
|
|
try:
|
|
result = object.getDocBookChapter()
|
|
finally:
|
|
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()
|
|
try:
|
|
result = object.getLatexChapter()
|
|
finally:
|
|
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)
|
|
if virtualServer is None:
|
|
return None
|
|
hostName = extractApplicationHostName(virtualServerId)
|
|
virtualServerDataDirectoryPath = os.path.join(
|
|
self.dataDirectoryPath, hostName)
|
|
importObjectsDirectoryPath = os.path.join(
|
|
importDirectoryPath, self.applicationRole)
|
|
objectsDirectoryPath = os.path.join(
|
|
virtualServerDataDirectoryPath, self.applicationRole)
|
|
for id in virtualServer.objects.keys():
|
|
serverHostNameAndPortNotUsed, serverRoleNotUsed, localId = \
|
|
splitId(id)
|
|
importBodyFilePath = os.path.join(
|
|
importObjectsDirectoryPath, localId)
|
|
destinationBodyFilePath = os.path.join(
|
|
objectsDirectoryPath, localId)
|
|
if not os.access(importBodyFilePath, os.F_OK):
|
|
continue
|
|
if not os.access(virtualServerDataDirectoryPath, os.F_OK):
|
|
os.mkdir(virtualServerDataDirectoryPath)
|
|
os.chmod(virtualServerDataDirectoryPath, 0750)
|
|
if not os.access(objectsDirectoryPath, os.F_OK):
|
|
os.mkdir(objectsDirectoryPath)
|
|
os.chmod(objectsDirectoryPath, 0750)
|
|
importBodyFile = open(importBodyFilePath, 'rb')
|
|
destinationBodyFile = open(destinationBodyFilePath, 'wb')
|
|
os.chmod(destinationBodyFilePath, 0640)
|
|
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 = \
|
|
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:
|
|
for object in virtualServer.objects.values():
|
|
if object.body is not None:
|
|
changed = 1
|
|
object.saveBody()
|
|
del object.body
|
|
object.lastEditorId = 'eePeopleServer/8' # = Manou
|
|
object.editionTime = object.modificationTime
|
|
elif object.__dict__.has_key('body'):
|
|
changed = 1
|
|
del object.body
|
|
if object.__dict__.has_key('state'):
|
|
changed = 1
|
|
object.writersSet = object.authorsSet
|
|
if object.writersSet is not None:
|
|
object.writersSet = object.writersSet[:]
|
|
if object.state != 'published':
|
|
object.readersSet = [
|
|
'eePeopleServer/1', # = Manou
|
|
]
|
|
del object.state
|
|
if version < 4000:
|
|
changed = virtualServer.admin.repair(4000) or changed
|
|
for id, object in virtualServer.objects.items():
|
|
newId = repairId(id)
|
|
if newId:
|
|
changed = 1
|
|
del virtualServer.objects[id]
|
|
virtualServer.objects[newId] = object
|
|
changed = object.repair(4000) or changed
|
|
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 = 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(
|
|
virtualServer.virtualServerId)
|
|
virtualServerDataDirectoryPath = os.path.join(
|
|
self.dataDirectoryPath, hostName)
|
|
objectsDirectoryPath = os.path.join(
|
|
virtualServerDataDirectoryPath, self.applicationRole)
|
|
for id, object in virtualServer.objects.items():
|
|
changed = object.repair(5004) or changed
|
|
serverHostNameAndPort, serverRole, localId = splitId(id)
|
|
bodyFilePath = os.path.join(objectsDirectoryPath, localId)
|
|
if not os.access(bodyFilePath, os.F_OK):
|
|
continue
|
|
file = open(bodyFilePath, 'rb')
|
|
body = file.read()
|
|
file.close()
|
|
repairedBody = body.replace('\r\n', '\n')
|
|
repairedBody = repairedBody.replace('\r', '\n')
|
|
if repairedBody != body:
|
|
file = open(bodyFilePath, 'wb')
|
|
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:
|
|
changed = 1
|
|
admin.id = '%s/__admin__' % virtualServer.virtualServerId
|
|
if changed:
|
|
virtualServer.markAllAsDirtyFIXME()
|
|
|
|
def search(self, searchTerms, scope, language):
|
|
searchTerms = iso8859_15(searchTerms)
|
|
|
|
serverId = getProxyForServerRole('translations').getServerId()
|
|
applicationToken = getApplicationToken()
|
|
|
|
params = []
|
|
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
isAdmin = self.isAdmin()
|
|
objectsLookedIn = []
|
|
|
|
# FIXME: Do not translate here. Should be done by the client.
|
|
multiCall = MultiCall()
|
|
for objectId, object in virtualServer.objects.items():
|
|
if not isAdmin \
|
|
and not getProxyForServerRole('authentication'
|
|
).setContainsUser(object.readersSet):
|
|
continue
|
|
if 'body' in scope:
|
|
object.loadBody()
|
|
for s in scope:
|
|
text = getattr(object, s)
|
|
if not text:
|
|
continue
|
|
sourcePath = 'self.%s' % s
|
|
getProxyForServerRole('translations').getTranslationInfos(
|
|
text, object.id, sourcePath, object.language,
|
|
[language], ignoreNew = 1, multiCall = multiCall)
|
|
objectsLookedIn.append(objectId)
|
|
if object.__dict__.has_key('body'):
|
|
del object.body
|
|
lazyResults = multiCall.call()
|
|
|
|
nbTerms = len(searchTerms.split(' '))
|
|
regexpOne = re.compile(
|
|
r'\b(%s)\b' % '|'.join(searchTerms.split(' ')),
|
|
re.LOCALE | re.IGNORECASE)
|
|
regexpAll = re.compile(searchTerms.replace(' ', '\s*'),
|
|
re.LOCALE | re.IGNORECASE)
|
|
|
|
result = []
|
|
scores = {}
|
|
for i in range(len(objectsLookedIn)):
|
|
objectId = objectsLookedIn[i]
|
|
translationInfos = lazyResults[i]()
|
|
text = translationInfos[0]
|
|
score = 0.0
|
|
if not text:
|
|
continue
|
|
|
|
rOne = regexpOne.findall(text)
|
|
if not rOne:
|
|
continue
|
|
rAll = regexpAll.findall(text)
|
|
if not rAll or nbTerms == 1:
|
|
score += 1.0*len(rOne)/nbTerms
|
|
else:
|
|
score += 1.3*len(rAll)+0.7*len(rOne)/nbTerms
|
|
|
|
score /= len(text)/200.0
|
|
scores[objectId] = scores.setdefault(objectId, 0) + score
|
|
|
|
return scores.items()
|
|
|
|
|
|
articlesServer = ArticlesServer()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
articlesServer.launch(applicationName, applicationRole)
|