diff --git a/browser/common.py b/browser/common.py
index dde524e..ce37cbb 100644
--- a/browser/common.py
+++ b/browser/common.py
@@ -58,6 +58,7 @@ from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted
from loops.i18n.browser import I18NView
from loops.interfaces import IResource, IView, INode
+from loops.organize.tracking import access
from loops.resource import Resource
from loops.security.common import canAccessObject, canListObject, canWriteObject
from loops.type import ITypeConcept
@@ -130,6 +131,14 @@ class BaseView(GenericView, I18NView):
def name(self):
return getName(self.context)
+ @Lazy
+ def principalId(self):
+ principal = self.request.principal
+ return principal and principal.id or ''
+
+ def recordAccess(self, viewName, **kw):
+ access.record(self.request, principal=self.principalId, view=viewName, **kw)
+
@Lazy
def versions(self):
return version.versions
diff --git a/browser/node.py b/browser/node.py
index 2eb669f..43f6c92 100644
--- a/browser/node.py
+++ b/browser/node.py
@@ -60,6 +60,7 @@ from loops import util
from loops.util import _
from loops.browser.common import BaseView
from loops.browser.concept import ConceptView
+from loops.organize.tracking import access
from loops.versioning.util import getVersion
@@ -82,6 +83,19 @@ class NodeView(BaseView):
def macro(self):
return self.template.macros['content']
+ def update(self):
+ result = super(NodeView, self).update()
+ self.recordAccess()
+ return result
+
+ def recordAccess(self, viewName=''):
+ target = self.virtualTargetObject
+ targetUid = target is not None and util.getUidForObject(target) or ''
+ access.record(self.request, principal=self.principalId,
+ node=self.uniqueId,
+ target=targetUid,
+ view=viewName)
+
def setupController(self):
cm = self.controller.macros
cm.register('css', identifier='loops.css', resourceName='loops.css',
@@ -462,6 +476,7 @@ class NodeView(BaseView):
if ti is not None:
target = ti(target)
url = self.virtualTargetUrl
+ self.recordAccess('external_edit')
return ExternalEditorView(target, self.request).load(url=url)
diff --git a/browser/resource.py b/browser/resource.py
index 25d8ccb..b38876a 100644
--- a/browser/resource.py
+++ b/browser/resource.py
@@ -146,6 +146,7 @@ class ResourceView(BaseView):
def show(self, useAttachment=False):
""" show means: "download"..."""
context = self.context
+ self.recordAccess('show', target=self.uniqueId)
ti = IType(context).typeInterface
if ti is not None:
context = ti(context)
@@ -289,6 +290,7 @@ class DocumentView(ResourceView):
def render(self):
""" Return the rendered content (data) of the context object.
"""
+ self.recordAccess('render', target=self.uniqueId)
ctx = adapted(self.context)
text = ctx.data
contentType = ctx.contentType
@@ -301,11 +303,12 @@ class DocumentView(ResourceView):
and canWrite(self.context, 'data'))
-class ExternalEditorView(ExternalEditorView):
+class ExternalEditorView(ExternalEditorView, BaseView):
def load(self, url=None):
#context = removeSecurityProxy(self.context)
context = self.context
+ self.recordAccess('external_edit', target=self.uniqueId)
data = adapted(context).data
r = []
context = removeSecurityProxy(context)
diff --git a/organize/tracking/README.txt b/organize/tracking/README.txt
index 21334f1..1b08688 100644
--- a/organize/tracking/README.txt
+++ b/organize/tracking/README.txt
@@ -24,8 +24,8 @@ ZCML setup):
>>> concepts, resources, views = t.setup()
-Tracking Changes and Object Access
-==================================
+Tracking Changes
+================
>>> loopsRoot = concepts.getLoopsRoot()
>>> records = loopsRoot.getRecordManager()
@@ -48,7 +48,7 @@ Recording changes to objects
>>> from loops.organize.tracking.change import recordModification
>>> component.provideHandler(recordModification)
- >>> loopsRoot.options = ['organize.tracking:changes']
+ >>> loopsRoot.options = ['organize.tracking.changes']
>>> tTask = concepts['task']
>>> from loops.concept import Concept
@@ -71,6 +71,12 @@ Recording assignment changes
2
+Tracking Object Access
+======================
+
+ >>> from loops.organize.tracking.access import record
+
+
Fin de partie
=============
diff --git a/organize/tracking/access.py b/organize/tracking/access.py
new file mode 100644
index 0000000..6ab816e
--- /dev/null
+++ b/organize/tracking/access.py
@@ -0,0 +1,87 @@
+#
+# 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
+#
+
+"""
+Recording changes to loops objects.
+
+$Id$
+"""
+
+import os
+
+from zope.app.publication.interfaces import IEndRequestEvent
+from zope.interface import Interface
+from zope.cachedescriptors.property import Lazy
+from zope.component import adapter
+from zope.security.proxy import removeSecurityProxy
+
+from cybertools.meta.interfaces import IOptions
+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 import util
+
+
+request_key = 'loops.organize.tracking.access'
+loggers_key = 'loops.access'
+
+fields = {
+ '001': ('principal', 'node', 'target', 'view', 'params'),
+}
+
+
+def record(request, **kw):
+ data = request.annotations.setdefault(request_key, {})
+ for k, v in kw.items():
+ data[k] = v
+
+
+@adapter(IEndRequestEvent)
+def logAccess(event):
+ object = removeSecurityProxy(event.object)
+ context = getattr(object, 'context', None)
+ if context is None:
+ object = getattr(object, 'im_self', None)
+ context = getattr(object, 'context', None)
+ if context is None:
+ return
+ if not ILoopsObject.providedBy(context):
+ return
+ data = event.request.annotations.get(request_key)
+ if not data:
+ return
+ logger = loggers.get(loggers_key)
+ if not logger:
+ options = IOptions(context.getLoopsRoot())
+ logfile = options('organize.tracking.logfile')
+ if not logfile:
+ return
+ path = os.path.join(util.getVarDirectory(), logfile[0])
+ logger = loggers[loggers_key] = Logger(loggers_key, path)
+ logger.log(marshall(data))
+
+
+def marshall(data):
+ version = '001'
+ values = [version]
+ for key in fields[version]:
+ values.append(data.get(key) or '')
+ return ';'.join(values)
diff --git a/organize/tracking/change.py b/organize/tracking/change.py
index 38dcc63..fb9a195 100644
--- a/organize/tracking/change.py
+++ b/organize/tracking/change.py
@@ -60,7 +60,7 @@ class ChangeManager(object):
return (not (self.context is None or
self.storage is None or
self.personId is None)
- and 'changes' in self.options('organize.tracking', ()))
+ and self.options('organize.tracking.changes'))
@Lazy
def loopsRoot(self):
diff --git a/organize/tracking/configure.zcml b/organize/tracking/configure.zcml
index caa49b2..d1b4fea 100644
--- a/organize/tracking/configure.zcml
+++ b/organize/tracking/configure.zcml
@@ -24,6 +24,8 @@
+
+