| 1 |
# (C) Copyright 2006 Nuxeo SAS <http://nuxeo.com> |
|---|
| 2 |
# Author: Julien Anguenot <ja@nuxeo.com> |
|---|
| 3 |
# |
|---|
| 4 |
# This program is free software; you can redistribute it and/or modify |
|---|
| 5 |
# it under the terms of the GNU General Public License version 2 as published |
|---|
| 6 |
# by the Free Software Foundation. |
|---|
| 7 |
# |
|---|
| 8 |
# This program is distributed in the hope that it will be useful, |
|---|
| 9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 |
# GNU General Public License for more details. |
|---|
| 12 |
# |
|---|
| 13 |
# You should have received a copy of the GNU General Public License |
|---|
| 14 |
# along with this program; if not, write to the Free Software |
|---|
| 15 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
|---|
| 16 |
# 02111-1307, USA. |
|---|
| 17 |
# |
|---|
| 18 |
# $Id: catalog.py 31177 2006-03-10 15:39:31Z janguenot $ |
|---|
| 19 |
"""Indexable Object Wrapper for CPS. |
|---|
| 20 |
""" |
|---|
| 21 |
|
|---|
| 22 |
from zLOG import LOG, WARNING |
|---|
| 23 |
|
|---|
| 24 |
from Acquisition import aq_parent |
|---|
| 25 |
from Acquisition import aq_inner |
|---|
| 26 |
from Acquisition import aq_base |
|---|
| 27 |
|
|---|
| 28 |
from DateTime.DateTime import DateTime |
|---|
| 29 |
|
|---|
| 30 |
from Products.CMFCore.utils import getToolByName |
|---|
| 31 |
|
|---|
| 32 |
from Products.CPSCore.ProxyBase import ProxyBase |
|---|
| 33 |
from Products.CPSCore import utils as cpsutils |
|---|
| 34 |
from Products.CPSDocument.CPSDocument import CPSDocument |
|---|
| 35 |
|
|---|
| 36 |
DUBLIN_CORE_DATES = ('created', 'effective', 'expires', 'modified') |
|---|
| 37 |
|
|---|
| 38 |
class IndexableObjectWrapper: |
|---|
| 39 |
"""This is a CPS adaptation of |
|---|
| 40 |
CMFCore.CatalogTool.IndexableObjectWrapper""" |
|---|
| 41 |
|
|---|
| 42 |
def __init__(self, vars, ob, lang=None, uid=None): |
|---|
| 43 |
self.__vars = vars |
|---|
| 44 |
self.__ob = ob |
|---|
| 45 |
self.__lang = lang |
|---|
| 46 |
self.__uid = uid |
|---|
| 47 |
|
|---|
| 48 |
# lazy attributes (getattr makes infinite loops) |
|---|
| 49 |
self.__ob_repo = None |
|---|
| 50 |
self.__datamodel = None |
|---|
| 51 |
|
|---|
| 52 |
def getRepoDoc(self): |
|---|
| 53 |
doc = self.__ob_repo |
|---|
| 54 |
if doc is not None: |
|---|
| 55 |
return doc |
|---|
| 56 |
|
|---|
| 57 |
self.__ob_repo = self.__ob.getContent(lang=self.__lang) |
|---|
| 58 |
return self.__ob_repo |
|---|
| 59 |
|
|---|
| 60 |
def getDataModel(self): |
|---|
| 61 |
dm = self.__datamodel |
|---|
| 62 |
if dm is not None: |
|---|
| 63 |
return dm |
|---|
| 64 |
|
|---|
| 65 |
self.__datamodel = self.getRepoDoc().getDataModel(proxy=self.__ob) |
|---|
| 66 |
return self.__datamodel |
|---|
| 67 |
|
|---|
| 68 |
def __getattr__(self, name): |
|---|
| 69 |
"""This is the indexable wrapper getter for CPS, |
|---|
| 70 |
proxy try to get the repository document attributes, |
|---|
| 71 |
document in the repository hide some attributes to save some space.""" |
|---|
| 72 |
vars = self.__vars |
|---|
| 73 |
if vars.has_key(name): |
|---|
| 74 |
ret = vars[name] |
|---|
| 75 |
# Here, deal with DateTime object values. |
|---|
| 76 |
if isinstance(ret, DateTime): |
|---|
| 77 |
ret = ret.ISO() |
|---|
| 78 |
return ret |
|---|
| 79 |
ob = self.__ob |
|---|
| 80 |
proxy = None |
|---|
| 81 |
if isinstance(ob, ProxyBase): |
|---|
| 82 |
proxy = ob |
|---|
| 83 |
if name not in ('getId', 'id', 'getPhysicalPath', 'uid', |
|---|
| 84 |
'modified', |
|---|
| 85 |
'getDocid', 'isCPSFolderish'): |
|---|
| 86 |
# These attributes are computed from the proxy |
|---|
| 87 |
ob = self.getRepoDoc() |
|---|
| 88 |
|
|---|
| 89 |
if isinstance(ob, CPSDocument): |
|---|
| 90 |
# get the computed field value |
|---|
| 91 |
ret = self.getDataModel().get(name) |
|---|
| 92 |
if not isinstance(ob, CPSDocument) or ret is None: |
|---|
| 93 |
try: |
|---|
| 94 |
ret = getattr(ob, name) |
|---|
| 95 |
except AttributeError: |
|---|
| 96 |
if name == 'meta_type': |
|---|
| 97 |
# this is a fix for TextIndexNG2 |
|---|
| 98 |
return None |
|---|
| 99 |
raise |
|---|
| 100 |
|
|---|
| 101 |
if proxy is not None and name == 'SearchableText': |
|---|
| 102 |
# we add proxy id to searchableText |
|---|
| 103 |
ret = ret() + ' ' + proxy.getId() |
|---|
| 104 |
|
|---|
| 105 |
# use callable result if needed |
|---|
| 106 |
# XXX GR: problem when it is portal_url for instance (think ack). |
|---|
| 107 |
# It's useless except in the case of DateTime instances, because |
|---|
| 108 |
# it is done by the next layer, nuxeo.lucene, which doesn't know about DateTime. |
|---|
| 109 |
if callable(ret) and name in DUBLIN_CORE_DATES: |
|---|
| 110 |
ret = ret() |
|---|
| 111 |
|
|---|
| 112 |
# Check here if it's a date and return a string representation |
|---|
| 113 |
# of the date since DateTime is not a Python standard object |
|---|
| 114 |
if isinstance(ret, DateTime): |
|---|
| 115 |
ret = ret.ISO() |
|---|
| 116 |
|
|---|
| 117 |
return ret |
|---|
| 118 |
|
|---|
| 119 |
def allowedRolesAndUsers(self): |
|---|
| 120 |
""" |
|---|
| 121 |
Return a list of roles, users and groups with View permission. |
|---|
| 122 |
Used by PortalCatalog to filter out items you're not allowed to see. |
|---|
| 123 |
""" |
|---|
| 124 |
return cpsutils.getAllowedRolesAndUsersOfObject(self.__ob) |
|---|
| 125 |
|
|---|
| 126 |
def localUsersWithRoles(self): |
|---|
| 127 |
""" |
|---|
| 128 |
Return a list of users and groups having local roles. |
|---|
| 129 |
Used by PortalCatalog to find which objects have roles for given |
|---|
| 130 |
users and groups. |
|---|
| 131 |
Only return proxies: see above __getattr__ raises |
|---|
| 132 |
AttributeError when accessing this attribute. |
|---|
| 133 |
""" |
|---|
| 134 |
ob = self.__ob |
|---|
| 135 |
local_roles = ['user:%s' % r[0] for r in ob.get_local_roles()] |
|---|
| 136 |
local_roles.extend( |
|---|
| 137 |
['group:%s' % r[0] for r in ob.get_local_group_roles()]) |
|---|
| 138 |
return local_roles |
|---|
| 139 |
|
|---|
| 140 |
def path(self): |
|---|
| 141 |
"""PathIndex needs a path attribute, otherwise it uses |
|---|
| 142 |
getPhysicalPath which fails for viewLanguage paths.""" |
|---|
| 143 |
if self.__uid is not None: |
|---|
| 144 |
return self.__uid |
|---|
| 145 |
else: |
|---|
| 146 |
return self.__ob.getPhysicalPath() |
|---|
| 147 |
|
|---|
| 148 |
def container_path(self): |
|---|
| 149 |
"""This is used to produce an index |
|---|
| 150 |
return the parent full path.""" |
|---|
| 151 |
return '/'.join(self.__ob.getPhysicalPath()[:-1]) |
|---|
| 152 |
|
|---|
| 153 |
def relative_path(self): |
|---|
| 154 |
"""This is used to produce a metadata |
|---|
| 155 |
return a path relative to the portal.""" |
|---|
| 156 |
utool = getToolByName(self, 'portal_url', None) |
|---|
| 157 |
ret = '' |
|---|
| 158 |
if utool: |
|---|
| 159 |
# broken object can't aquire portal_url |
|---|
| 160 |
ret = utool.getRelativeContentURL(self.__ob) |
|---|
| 161 |
return ret |
|---|
| 162 |
|
|---|
| 163 |
def relative_path_depth(self): |
|---|
| 164 |
"""This is used to produce an index |
|---|
| 165 |
return the path depth relative to the portal.""" |
|---|
| 166 |
rpath = self.relative_path() |
|---|
| 167 |
ret = -1 |
|---|
| 168 |
if rpath: |
|---|
| 169 |
ret = rpath.count('/')+1 |
|---|
| 170 |
return ret |
|---|
| 171 |
|
|---|
| 172 |
def position_in_container(self): |
|---|
| 173 |
"""Return the object position in the container.""" |
|---|
| 174 |
ob = self.__ob |
|---|
| 175 |
container = aq_parent(aq_inner(ob)) |
|---|
| 176 |
if getattr(aq_base(container), 'getObjectPosition', None) is not None: |
|---|
| 177 |
try: |
|---|
| 178 |
return container.getObjectPosition(ob.getId()) |
|---|
| 179 |
except ValueError, err: |
|---|
| 180 |
# Trying to index a doc before it is created ? |
|---|
| 181 |
LOG('position_in_container', WARNING, |
|---|
| 182 |
'got a Value Error %s' % err) |
|---|
| 183 |
return 0 |
|---|
| 184 |
except AttributeError, err: |
|---|
| 185 |
# Container without ordering support such as |
|---|
| 186 |
# BTreeFolder based folders. (proxy or not) |
|---|
| 187 |
LOG('position_in_container', WARNING, |
|---|
| 188 |
'got an AttributeError %s' % err) |
|---|
| 189 |
return 0 |
|---|
| 190 |
return 0 |
|---|
| 191 |
|
|---|
| 192 |
def match_languages(self): |
|---|
| 193 |
"""Return a list of languages that the proxy matches.""" |
|---|
| 194 |
ob = self.__ob |
|---|
| 195 |
proxy_language = self.__lang |
|---|
| 196 |
if proxy_language is None: |
|---|
| 197 |
utool = getToolByName(self, 'portal_url', None) |
|---|
| 198 |
if utool: |
|---|
| 199 |
# match_languages is used only with UI locales |
|---|
| 200 |
portal = utool.getPortalObject() |
|---|
| 201 |
return portal.getProperty('available_languages', |
|---|
| 202 |
cpsutils.ALL_LOCALES) |
|---|
| 203 |
return cpsutils.ALL_LOCALES |
|---|
| 204 |
languages = [proxy_language] |
|---|
| 205 |
if ob.getDefaultLanguage() == proxy_language: |
|---|
| 206 |
languages.extend([lang for lang in cpsutils.ALL_LOCALES |
|---|
| 207 |
if lang not in ob.getProxyLanguages()]) |
|---|
| 208 |
return languages |
|---|
| 209 |
|
|---|