diff --git a/organize/tracking/access.py b/organize/tracking/access.py
index 6ab816e..fad16fc 100644
--- a/organize/tracking/access.py
+++ b/organize/tracking/access.py
@@ -22,10 +22,13 @@ Recording changes to loops objects.
$Id$
"""
+import logging
import os
+import time
+import transaction
from zope.app.publication.interfaces import IEndRequestEvent
-from zope.interface import Interface
+from zope.interface import Interface, implements
from zope.cachedescriptors.property import Lazy
from zope.component import adapter
from zope.security.proxy import removeSecurityProxy
@@ -35,13 +38,14 @@ from cybertools.tracking.btree import Track, getTimeStamp
from cybertools.tracking.interfaces import ITrack
from cybertools.tracking.logfile import Logger, loggers
from loops.interfaces import ILoopsObject
-from loops.organize.party import getPersonForUser
-from loops.security.common import getCurrentPrincipal
+from loops.organize.tracking.base import BaseRecordManager
from loops import util
+# logging
+
+logfile_option = 'organize.tracking.logfile'
request_key = 'loops.organize.tracking.access'
-loggers_key = 'loops.access'
fields = {
'001': ('principal', 'node', 'target', 'view', 'params'),
@@ -71,11 +75,12 @@ def logAccess(event):
logger = loggers.get(loggers_key)
if not logger:
options = IOptions(context.getLoopsRoot())
- logfile = options('organize.tracking.logfile')
+ logfile = options(logfile_option)
if not logfile:
return
- path = os.path.join(util.getVarDirectory(), logfile[0])
- logger = loggers[loggers_key] = Logger(loggers_key, path)
+ fn = logfile[0]
+ path = os.path.join(util.getVarDirectory(), fn)
+ logger = loggers[fn] = Logger(fn, path)
logger.log(marshall(data))
@@ -85,3 +90,88 @@ def marshall(data):
for key in fields[version]:
values.append(data.get(key) or '')
return ';'.join(values)
+
+
+# record manager
+
+class AccessRecordManager(BaseRecordManager):
+
+ storageName = 'access'
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ @Lazy
+ def logfile(self):
+ value = self.options(logfile_option)
+ return value and value[0] or None
+
+ @Lazy
+ def valid(self):
+ return self.storage is not None and self.logfile
+
+ def loadRecordsFromLog(self):
+ if not self.valid:
+ return
+ fn = self.logfile
+ path = os.path.join(util.getVarDirectory(), fn)
+ logger = loggers.get(fn)
+ if not logger:
+ logger = loggers[fn] = Logger(fn, path)
+ if not os.path.exists(path):
+ return
+ lf = open(path, 'r')
+ for idx, line in enumerate(lf):
+ self.processLogRecord(idx, line)
+ lf.close()
+ transaction.commit()
+ logger.doRollover()
+
+ def processLogRecord(self, idx, line):
+ if not line:
+ return
+ values = line.split(';')
+ timeString = values.pop(0)
+ version = values.pop(0)
+ if version not in fields:
+ logging.getLogger('AccessRecordManager').warn(
+ 'Undefined logging record version %r on record %i.'
+ % (version, idx))
+ return
+ if len(values) != len(fields[version]):
+ logging.getLogger('AccessRecordManager').warn(
+ 'Length of record %i does not match version %r.'
+ % (idx, version))
+ return
+ data = {}
+ for idx, field in enumerate(fields[version]):
+ data[field] = values[idx]
+ timeStamp = timeStringToTimeStamp(timeString)
+ personId = self.getPersonId(data['principal'])
+ taskId = data['target'] or data['node']
+ existing = self.storage.query(taskId=taskId, userName=self.personId,
+ timeStamp=timeStamp)
+ for track in existing:
+ if track.data == data: # has been recorded already
+ return
+ self.storage.saveUserTrack(taskId, 0, personId, data,
+ timeStamp=timeStamp)
+
+
+class IAccessRecord(ITrack):
+
+ pass
+
+
+class AccessRecord(Track):
+
+ implements(IAccessRecord)
+
+ typeName = 'AccessRecord'
+
+
+def timeStringToTimeStamp(timeString):
+ s, decimal = timeString.split(',')
+ t = time.strptime(s, '%Y-%m-%d %H:%M:%S')
+ return int(time.mktime(t))
diff --git a/organize/tracking/base.py b/organize/tracking/base.py
new file mode 100644
index 0000000..ab3c2c6
--- /dev/null
+++ b/organize/tracking/base.py
@@ -0,0 +1,71 @@
+#
+# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
+#
+# 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
+#
+
+"""
+Base class(es) for track/record managers.
+
+$Id$
+"""
+
+from zope.cachedescriptors.property import Lazy
+
+from cybertools.meta.interfaces import IOptions
+from loops.organize.party import getPersonForUser
+from loops.organize.util import getPrincipalForUserId
+from loops.security.common import getCurrentPrincipal
+from loops import util
+
+
+class BaseRecordManager(object):
+
+ context = None
+ valid = True
+ storageName = None
+
+ @Lazy
+ def options(self):
+ #return IOptions(self.context)
+ return IOptions(self.loopsRoot)
+
+ @Lazy
+ def loopsRoot(self):
+ return self.context.getLoopsRoot()
+
+ @Lazy
+ def storage(self):
+ records = self.loopsRoot.getRecordManager()
+ if records is not None:
+ return records.get(self.storageName)
+ return None
+
+ @Lazy
+ def personId(self):
+ return self.getPersonId()
+
+ def getPersonId(self, userId=None):
+ if userId is None:
+ principal = getCurrentPrincipal()
+ else:
+ principal = getPrincipalForUserId(userId, context=self.context)
+ if principal is not None:
+ person = getPersonForUser(self.context, principal=principal)
+ if person is None:
+ return principal.id
+ return util.getUidForObject(person)
+ return None
+
diff --git a/organize/tracking/change.py b/organize/tracking/change.py
index fb9a195..b3e3057 100644
--- a/organize/tracking/change.py
+++ b/organize/tracking/change.py
@@ -37,24 +37,20 @@ from loops.resource import ResourceManager
from loops.interfaces import IAssignmentEvent, IDeassignmentEvent
from loops.interfaces import ILoopsObject
from loops.organize.party import getPersonForUser
+from loops.organize.tracking.base import BaseRecordManager
from loops.security.common import getCurrentPrincipal
from loops import util
-class ChangeManager(object):
+class ChangeManager(BaseRecordManager):
- context = None
+ storageName = 'changes'
def __init__(self, context):
if isinstance(context, (ConceptManager, ResourceManager)):
return
self.context = context
- @Lazy
- def options(self):
- #return IOptions(self.context)
- return IOptions(self.loopsRoot)
-
@Lazy
def valid(self):
return (not (self.context is None or
@@ -62,27 +58,6 @@ class ChangeManager(object):
self.personId is None)
and self.options('organize.tracking.changes'))
- @Lazy
- def loopsRoot(self):
- return self.context.getLoopsRoot()
-
- @Lazy
- def storage(self):
- records = self.loopsRoot.getRecordManager()
- if records is not None:
- return records.get('changes')
- return None
-
- @Lazy
- def personId(self):
- principal = getCurrentPrincipal()
- if principal is not None:
- person = getPersonForUser(self.context, principal=principal)
- if person is None:
- return principal.id
- return util.getUidForObject(person)
- return None
-
def recordModification(self, action='modify', **kw):
if not self.valid:
return
@@ -117,10 +92,6 @@ class ChangeRecord(Track):
def recordModification(obj, event):
ChangeManager(obj).recordModification()
-#@adapter(ILoopsObject, IObjectCreatedEvent)
-#def recordCreation(obj, event):
-# ChangeManager(obj).recordModification('create')
-
@adapter(ILoopsObject, IObjectAddedEvent)
def recordAdding(obj, event):
ChangeManager(obj).recordModification('add')
diff --git a/organize/tracking/configure.zcml b/organize/tracking/configure.zcml
index d1b4fea..567501c 100644
--- a/organize/tracking/configure.zcml
+++ b/organize/tracking/configure.zcml
@@ -26,6 +26,13 @@
+
+
diff --git a/organize/tracking/setup.py b/organize/tracking/setup.py
index 6f4fbdf..7a847e7 100644
--- a/organize/tracking/setup.py
+++ b/organize/tracking/setup.py
@@ -27,6 +27,7 @@ from zope.interface import implements, Interface
from cybertools.tracking.btree import TrackingStorage
from loops.organize.tracking.change import ChangeRecord
+from loops.organize.tracking.access import AccessRecord
from loops.setup import SetupManager as BaseSetupManager
@@ -36,3 +37,5 @@ class SetupManager(BaseSetupManager):
records = self.context.getRecordManager()
changes = self.addObject(records, TrackingStorage, 'changes',
trackFactory=ChangeRecord)
+ access = self.addObject(records, TrackingStorage, 'access',
+ trackFactory=AccessRecord)
diff --git a/organize/util.py b/organize/util.py
index d304342..ddcc10d 100644
--- a/organize/util.py
+++ b/organize/util.py
@@ -84,6 +84,14 @@ def getInternalPrincipal(id, context=None):
raise PrincipalLookupError(id)
+def getPrincipalForUserId(id, context=None):
+ auth = component.getUtility(IAuthentication, context=context)
+ try:
+ return auth.getPrincipal(id)
+ except PrincipalLookupError:
+ return None
+
+
def getTrackingStorage(obj, name):
records = obj.getLoopsRoot().getRecordManager()
if records is not None: