[gen] Authorize anyone to get static content (images, css, js...) from any Appy app.

This commit is contained in:
Gaetan Delannay 2014-12-26 13:51:40 +01:00
parent fd5e88928d
commit 4461cbb9a8
12 changed files with 57 additions and 14 deletions

View file

@ -574,10 +574,10 @@ class ZopeGenerator(Generator):
for name, appyType, klass in classDescr.getOrderedAppyAttributes(): for name, appyType, klass in classDescr.getOrderedAppyAttributes():
names.append(name) names.append(name)
if name == 'title': titleFound = True if name == 'title': titleFound = True
# Add the "title" mandatory field if not found # Add the 'title' mandatory field if not found
if not titleFound: names.insert(0, 'title') if not titleFound: names.insert(0, 'title')
# Add the 'state' attribute # Add the 'state' and 'SearchableText' attributes
names.append('state') names += ['state', 'SearchableText']
qNames = ['"%s"' % name for name in names] qNames = ['"%s"' % name for name in names]
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames))) attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
repls['attributes'] = ',\n '.join(attributes) repls['attributes'] = ',\n '.join(attributes)

View file

@ -286,6 +286,13 @@ class ZopeInstaller:
show='result') show='result')
state.init('state', None, 'workflow') state.init('state', None, 'workflow')
setattr(wrapperClass, 'state', state) setattr(wrapperClass, 'state', state)
# Special field "SearchableText" must be added fot every class and
# will allow to display a search widget for entering keywords for
# searhing in index "SearchableText".
searchable = gen.String(show=False)
searchable.init('SearchableText', None, 'appy')
setattr(wrapperClass, 'SearchableText', searchable)
# Set field "__fields__" on the wrapper class
names = self.config.attributes[wrapperClass.__name__[:-8]] names = self.config.attributes[wrapperClass.__name__[:-8]]
wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names] wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names]
# Post-initialise every Appy type # Post-initialise every Appy type

View file

@ -249,6 +249,8 @@ class ToolMixin(BaseMixin):
# Gather all the indexed fields on this class # Gather all the indexed fields on this class
fieldNames = [f.name for f in self.getAllAppyTypes(className) \ fieldNames = [f.name for f in self.getAllAppyTypes(className) \
if f.indexed] if f.indexed]
fieldNames.insert(0, 'SearchableText')
if 'title' in fieldNames: fieldNames.remove('title')
nbOfColumns = getattr(klass, 'numberOfSearchColumns', 3) nbOfColumns = getattr(klass, 'numberOfSearchColumns', 3)
for name in fieldNames: for name in fieldNames:
field = self.getAppyType(name, className=className) field = self.getAppyType(name, className=className)
@ -720,7 +722,7 @@ class ToolMixin(BaseMixin):
search or about a group of searches. search or about a group of searches.
''' '''
res = [] res = []
default = None # Also retrieve the default one here. default = None # Also retrieve the default one here
groups = {} # The already encountered groups groups = {} # The already encountered groups
page = Page('searches') # A dummy page required by class UiGroup page = Page('searches') # A dummy page required by class UiGroup
# Get the searches statically defined on the class # Get the searches statically defined on the class
@ -733,7 +735,7 @@ class ToolMixin(BaseMixin):
# Create the search descriptor # Create the search descriptor
uiSearch = UiSearch(search, className, self) uiSearch = UiSearch(search, className, self)
if not search.group: if not search.group:
# Insert the search at the highest level, not in any group. # Insert the search at the highest level, not in any group
res.append(uiSearch) res.append(uiSearch)
else: else:
uiGroup = search.group.insertInto(res, groups, page, className, uiGroup = search.group.insertInto(res, groups, page, className,
@ -779,7 +781,7 @@ class ToolMixin(BaseMixin):
def advancedSearchEnabledFor(self, klass): def advancedSearchEnabledFor(self, klass):
'''Is advanced search visible for p_klass ?''' '''Is advanced search visible for p_klass ?'''
# By default, advanced search is enabled. # By default, advanced search is enabled
if not hasattr(klass, 'searchAdvanced'): return True if not hasattr(klass, 'searchAdvanced'): return True
# Evaluate attribute "show" on this Search instance representing the # Evaluate attribute "show" on this Search instance representing the
# advanced search. # advanced search.
@ -911,7 +913,7 @@ class ToolMixin(BaseMixin):
cfg = self.getProductConfig(True).ldap cfg = self.getProductConfig(True).ldap
if cfg: user = cfg.getUser(self.appy(), login, password) if cfg: user = cfg.getUser(self.appy(), login, password)
elif source == 'any': elif source == 'any':
# Get the user object, be it really local or a copy of a LDAP user. # Get the user object, be it really local or a copy of a LDAP user
user = tool.search1('User', noSecurity=True, login=login) user = tool.search1('User', noSecurity=True, login=login)
if not user: return if not user: return
# Authentify the user if required # Authentify the user if required
@ -976,13 +978,11 @@ class ToolMixin(BaseMixin):
# This dict stores, for every logged user, the date/time of its last access # This dict stores, for every logged user, the date/time of its last access
loggedUsers = {} loggedUsers = {}
forgetAccessExtensions = ('.jpg', '.gif', '.png', '.js', '.css') staticExtensions = ('.jpg', '.jpeg', '.gif', '.png', '.js', '.css', '.htm',
def rememberAccess(self, id, user): '.html')
def rememberAccess(self, user):
'''Every time there is a hit on the server, this method is called in '''Every time there is a hit on the server, this method is called in
order to update global dict loggedUsers (see above).''' order to update global dict loggedUsers (see above).'''
if not id: return
if os.path.splitext(id)[-1].lower() in self.forgetAccessExtensions:
return
self.loggedUsers[user.login] = time.time() self.loggedUsers[user.login] = time.time()
# "Touch" the SESSION object. Else, expiration won't occur. # "Touch" the SESSION object. Else, expiration won't occur.
session = self.REQUEST.SESSION session = self.REQUEST.SESSION
@ -998,6 +998,10 @@ class ToolMixin(BaseMixin):
# a is the object the object was accessed through # a is the object the object was accessed through
# c is the physical container of the object # c is the physical container of the object
a, c, n, v = self._getobcontext(v, request) a, c, n, v = self._getobcontext(v, request)
# Authorize anyone to static content (image, js, css...)
id = a.getId()
if id and (os.path.splitext(id)[-1].lower() in tool.staticExtensions):
return self._nobody.__of__(self)
# Identify and authentify the user # Identify and authentify the user
user = tool.getUser(authentify=True, source='any') user = tool.getUser(authentify=True, source='any')
if not user: if not user:
@ -1011,7 +1015,7 @@ class ToolMixin(BaseMixin):
# We found a user and his password was correct. Try to authorize him # We found a user and his password was correct. Try to authorize him
# against the published object. By the way, remember its last access # against the published object. By the way, remember its last access
# to this system. # to this system.
tool.rememberAccess(a.getId(), user) tool.rememberAccess(user)
user = user.getZopeUser() user = user.getZopeUser()
if self.authorize(user, a, c, n, v, roles): if self.authorize(user, a, c, n, v, roles):
return user.__of__(self) return user.__of__(self)

View file

@ -47,6 +47,10 @@ msgstr ""
msgid "appy_ok" msgid "appy_ok"
msgstr "" msgstr ""
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "" msgstr ""

View file

@ -47,6 +47,10 @@ msgstr ""
msgid "appy_ok" msgid "appy_ok"
msgstr "" msgstr ""
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "" msgstr ""

View file

@ -47,6 +47,10 @@ msgstr "Titel"
msgid "appy_ok" msgid "appy_ok"
msgstr "OK" msgstr "OK"
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Änderung der Angaben" msgstr "Änderung der Angaben"

View file

@ -48,6 +48,10 @@ msgstr "Title"
msgid "appy_ok" msgid "appy_ok"
msgstr "Ok" msgstr "Ok"
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Data change" msgstr "Data change"

View file

@ -47,6 +47,10 @@ msgstr "Título"
msgid "appy_ok" msgid "appy_ok"
msgstr "" msgstr ""
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Cambio de datos" msgstr "Cambio de datos"

View file

@ -48,6 +48,10 @@ msgstr "Titre"
msgid "appy_ok" msgid "appy_ok"
msgstr "Ok" msgstr "Ok"
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr "Mots-clé"
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Changement de données" msgstr "Changement de données"

View file

@ -47,6 +47,10 @@ msgstr "Qualifica"
msgid "appy_ok" msgid "appy_ok"
msgstr "" msgstr ""
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Revisione dati" msgstr "Revisione dati"

View file

@ -47,6 +47,10 @@ msgstr "Titel"
msgid "appy_ok" msgid "appy_ok"
msgstr "OK" msgstr "OK"
#. Default: "Keywords"
msgid "appy_SearchableText"
msgstr ""
#. Default: "Data change" #. Default: "Data change"
msgid "data_change" msgid "data_change"
msgstr "Wijziging van de gegevens" msgstr "Wijziging van de gegevens"

View file

@ -62,7 +62,7 @@ input.button { color: #666666; height: 20px; margin-bottom: 5px; margin-top:2px;
background-color: white; background-repeat: no-repeat; background-color: white; background-repeat: no-repeat;
background-position: 8px 25%; box-shadow: 2px 2px 2px #888888} background-position: 8px 25%; box-shadow: 2px 2px 2px #888888}
input.buttonSmall { width: 100px !important; font-size: 85%; height: 18px; input.buttonSmall { width: 100px !important; font-size: 85%; height: 18px;
margin-bottom: 3px } margin-bottom: 5px }
.fake { background-color: #e6e6e6 !important ; cursor:help !important } .fake { background-color: #e6e6e6 !important ; cursor:help !important }
.xhtml { background-color: white; padding: 4px; font-size: 95% } .xhtml { background-color: white; padding: 4px; font-size: 95% }
.xhtml img { margin-right: 5px } .xhtml img { margin-right: 5px }