appy.gen: added the possibility to create and manage web pages into an app; every class can now define a method showPortlet. If the class does not define it or if it returns False, the portlet won't be shown when showing/editing instances of this class.
This commit is contained in:
parent
8aa03a091a
commit
1e9e4df5a6
|
@ -637,7 +637,7 @@ class TranslationClassDescriptor(ClassDescriptor):
|
|||
self.customized = False
|
||||
|
||||
def getParents(self, allClasses=()): return ('Translation',)
|
||||
|
||||
def isFolder(self, klass=None): return False
|
||||
def generateSchema(self):
|
||||
ClassDescriptor.generateSchema(self, configClass=True)
|
||||
|
||||
|
@ -681,4 +681,29 @@ class TranslationClassDescriptor(ClassDescriptor):
|
|||
params['format'] = gen.String.TEXT
|
||||
params['height'] = height
|
||||
self.addField(messageId, gen.String(**params))
|
||||
|
||||
class PageClassDescriptor(ClassDescriptor):
|
||||
'''Represents the class that corresponds to a Page.'''
|
||||
def __init__(self, klass, generator):
|
||||
ClassDescriptor.__init__(self,klass,klass._appy_attributes[:],generator)
|
||||
self.modelClass = self.klass
|
||||
self.predefined = True
|
||||
self.customized = False
|
||||
def getParents(self, allClasses=()):
|
||||
res = ['Page']
|
||||
if self.customized:
|
||||
res.append('%s.%s' % (self.klass.__module__, self.klass.__name__))
|
||||
return res
|
||||
def update(self, klass, attributes):
|
||||
'''This method is called by the generator when he finds a custom page
|
||||
definition. We must then add the custom page elements in this
|
||||
default Page descriptor.
|
||||
|
||||
NOTE: currently, it is not possible to define a custom Page class.'''
|
||||
self.orderedAttributes += attributes
|
||||
self.klass = klass
|
||||
self.customized = True
|
||||
def isFolder(self, klass=None): return True
|
||||
def generateSchema(self):
|
||||
ClassDescriptor.generateSchema(self, configClass=True)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -6,7 +6,7 @@ import appy.gen as gen
|
|||
from po import PoMessage, PoFile, PoParser
|
||||
from descriptors import *
|
||||
from utils import produceNiceMessage, getClassName
|
||||
from model import ModelClass, User, Group, Tool, Translation
|
||||
from model import ModelClass, User, Group, Tool, Translation, Page
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class GeneratorError(Exception): pass
|
||||
|
@ -353,11 +353,12 @@ class ZopeGenerator(Generator):
|
|||
Generator.__init__(self, *args, **kwargs)
|
||||
# Set our own Descriptor classes
|
||||
self.descriptorClasses['class'] = ClassDescriptor
|
||||
# Create our own Tool, User, Group and Translation instances
|
||||
# Create Tool, User, Group, Translation and Page instances.
|
||||
self.tool = ToolClassDescriptor(Tool, self)
|
||||
self.user = UserClassDescriptor(User, self)
|
||||
self.group = GroupClassDescriptor(Group, self)
|
||||
self.translation = TranslationClassDescriptor(Translation, self)
|
||||
self.page = PageClassDescriptor(Page, self)
|
||||
# i18n labels to generate
|
||||
self.labels = [] # i18n labels
|
||||
self.referers = {}
|
||||
|
@ -603,13 +604,14 @@ class ZopeGenerator(Generator):
|
|||
'''Returns the descriptors for all the classes in the generated
|
||||
gen-application. If p_include is:
|
||||
* "all" it includes the descriptors for the config-related
|
||||
classes (tool, user, group, translation)
|
||||
classes (tool, user, group, translation, page)
|
||||
* "allButTool" it includes the same descriptors, the tool excepted
|
||||
* "custom" it includes descriptors for the config-related classes
|
||||
for which the user has created a sub-class.'''
|
||||
if not include: return self.classes
|
||||
res = self.classes[:]
|
||||
configClasses = [self.tool, self.user, self.group, self.translation]
|
||||
configClasses = [self.tool, self.user, self.group, self.translation,
|
||||
self.page]
|
||||
if include == 'all':
|
||||
res += configClasses
|
||||
elif include == 'allButTool':
|
||||
|
@ -789,16 +791,22 @@ class ZopeGenerator(Generator):
|
|||
Tool.users.klass = self.user.klass
|
||||
Group.users.klass = self.user.klass
|
||||
|
||||
# Generate the Tool-related classes (User, Group, Translation)
|
||||
for klass in (self.user, self.group, self.translation):
|
||||
# Generate the Tool-related classes (User, Group, Translation, Page)
|
||||
for klass in (self.user, self.group, self.translation, self.page):
|
||||
klassType = klass.name[len(self.applicationName):]
|
||||
klass.generateSchema()
|
||||
self.labels += [ Msg(klass.name, '', klassType),
|
||||
Msg('%s_plural' % klass.name,'', klass.name+'s')]
|
||||
repls = self.repls.copy()
|
||||
if klass.isFolder():
|
||||
parents = 'BaseMixin, Folder'
|
||||
icon = 'folder.gif'
|
||||
else:
|
||||
parents = 'BaseMixin, SimpleItem'
|
||||
icon = 'object.gif'
|
||||
repls.update({'methods': klass.methods, 'genClassName': klass.name,
|
||||
'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
|
||||
'classDoc': 'Standard Appy class', 'icon':'object.gif'})
|
||||
'baseMixin':'BaseMixin', 'parents': parents,
|
||||
'classDoc': 'Standard Appy class', 'icon': icon})
|
||||
self.copyFile('Class.pyt', repls, destName='%s.py' % klass.name)
|
||||
|
||||
# Before generating the Tool class, finalize it with query result
|
||||
|
|
|
@ -223,14 +223,14 @@ class ToolMixin(BaseMixin):
|
|||
return [importParams['headers'], elems]
|
||||
|
||||
def showPortlet(self, context):
|
||||
if self.userIsAnon(): return False
|
||||
if context.id == 'ui': context = context.getParentNode()
|
||||
res = True
|
||||
if not self.getRootClasses():
|
||||
res = False
|
||||
# If there is no root class, show the portlet only if we are within
|
||||
# the configuration.
|
||||
if (self.id in context.absolute_url()): res = True
|
||||
if hasattr(context.aq_base, 'appy'):
|
||||
appyObj = context.appy()
|
||||
try:
|
||||
res = appyObj.showPortlet()
|
||||
except AttributeError:
|
||||
res = True
|
||||
return res
|
||||
|
||||
def getObject(self, uid, appy=False, brain=False):
|
||||
|
@ -1000,4 +1000,10 @@ class ToolMixin(BaseMixin):
|
|||
return '<table class="main" align="center" cellpadding="0"><tr>' \
|
||||
'<td style="padding: 1em 1em 1em 1em">An error occurred. %s' \
|
||||
'</td></tr></table>' % '\n'.join(htmlMessage)
|
||||
|
||||
def getMainPages(self):
|
||||
'''Returns the main pages.'''
|
||||
if hasattr(self.o.aq_base, 'pages') and self.o.pages:
|
||||
return [self.getObject(uid) for uid in self.o.pages ]
|
||||
return ()
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1312,6 +1312,9 @@ class BaseMixin:
|
|||
Else (if the object is stored directly within the tool or the root
|
||||
data folder) it returns None.'''
|
||||
parent = self.getParentNode()
|
||||
# Not-Managers can't navigate back to the tool
|
||||
if (parent.id == 'config') and not self.getUser().has_role('Manager'):
|
||||
return False
|
||||
if parent.meta_type != 'Folder': return parent
|
||||
|
||||
def index_html(self):
|
||||
|
|
37
gen/model.py
37
gen/model.py
|
@ -42,7 +42,7 @@ class ModelClass:
|
|||
in order to avoid name conflicts with user-defined parts of the
|
||||
application model.'''
|
||||
_appy_attributes = [] # We need to keep track of attributes order.
|
||||
|
||||
folder = False
|
||||
@classmethod
|
||||
def _appy_getTypeBody(klass, appyType, wrapperName):
|
||||
'''This method returns the code declaration for p_appyType.'''
|
||||
|
@ -67,6 +67,12 @@ class ModelClass:
|
|||
value = 'tfw'
|
||||
else:
|
||||
value = appyType.getInputLayouts()
|
||||
elif (name == 'klass') and value and (value == klass):
|
||||
# This is a auto-Ref (a Ref that references the klass itself).
|
||||
# At this time, we can't reference the class that is still being
|
||||
# defined. So we initialize it to None. The post-init of the
|
||||
# field must be done manually in wrappers.py.
|
||||
value = 'None'
|
||||
elif isinstance(value, basestring):
|
||||
value = '"%s"' % value
|
||||
elif isinstance(value, gen.Ref):
|
||||
|
@ -83,7 +89,7 @@ class ModelClass:
|
|||
elif isinstance(value, gen.Group):
|
||||
value = 'Grp("%s")' % value.name
|
||||
elif isinstance(value, gen.Page):
|
||||
value = 'pages["%s"]' % value.name
|
||||
value = 'pges["%s"]' % value.name
|
||||
elif callable(value):
|
||||
className = wrapperName
|
||||
if (appyType.type == 'Ref') and appyType.isBack:
|
||||
|
@ -104,7 +110,7 @@ class ModelClass:
|
|||
else: wrapperName = 'W%s' % className
|
||||
res = 'class %s(%s):\n' % (className, wrapperName)
|
||||
# Tool must be folderish
|
||||
if className == 'Tool': res += ' folder=True\n'
|
||||
if klass.folder: res += ' folder=True\n'
|
||||
# First, scan all attributes, determine all used pages and create a
|
||||
# dict with it. It will prevent us from creating a new Page instance
|
||||
# for every field.
|
||||
|
@ -114,12 +120,12 @@ class ModelClass:
|
|||
exec 'appyType = klass.%s' % name
|
||||
if appyType.page.name not in pages:
|
||||
pages[appyType.page.name] = appyType.page
|
||||
res += ' pages = {'
|
||||
res += ' pges = {'
|
||||
for page in pages.itervalues():
|
||||
# Determine page show
|
||||
pageShow = page.show
|
||||
if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow
|
||||
res += '"%s":Page("%s", show=%s),'% (page.name, page.name, pageShow)
|
||||
res += '"%s":Pge("%s", show=%s),'% (page.name, page.name, pageShow)
|
||||
res += '}\n'
|
||||
# Secondly, dump every attribute
|
||||
for name in klass._appy_attributes:
|
||||
|
@ -183,13 +189,27 @@ class Translation(ModelClass):
|
|||
def label(self): pass
|
||||
def show(self, name): pass
|
||||
|
||||
# The Page class ---------------------------------------------------------------
|
||||
class Page(ModelClass):
|
||||
_appy_attributes = ['title', 'content', 'pages']
|
||||
folder = True
|
||||
title = gen.String(show='edit', indexed=True)
|
||||
content = gen.String(format=gen.String.XHTML, layouts='f')
|
||||
# Pages can contain other pages.
|
||||
def showSubPages(self): pass
|
||||
pages = gen.Ref(None, multiplicity=(0,None), add=True, link=False,
|
||||
back=gen.Ref(attribute='parent', show=False),
|
||||
show=showSubPages)
|
||||
Page.pages.klass = Page
|
||||
setattr(Page, Page.pages.back.attribute, Page.pages.back)
|
||||
|
||||
# The Tool class ---------------------------------------------------------------
|
||||
# Prefixes of the fields generated on the Tool.
|
||||
toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns',
|
||||
'enableAdvancedSearch', 'numberOfSearchColumns',
|
||||
'searchFields', 'optionalFields', 'showWorkflow',
|
||||
'showAllStatesInPhase')
|
||||
defaultToolFields = ('title', 'users', 'groups', 'translations',
|
||||
defaultToolFields = ('title', 'users', 'groups', 'translations', 'pages',
|
||||
'enableNotifications', 'unoEnabledPython','openOfficePort',
|
||||
'numberOfResultsPerPage', 'listBoxesMaximumWidth',
|
||||
'appyVersion')
|
||||
|
@ -197,6 +217,7 @@ defaultToolFields = ('title', 'users', 'groups', 'translations',
|
|||
class Tool(ModelClass):
|
||||
# In a ModelClass we need to declare attributes in the following list.
|
||||
_appy_attributes = list(defaultToolFields)
|
||||
folder = True
|
||||
|
||||
# Tool attributes
|
||||
title = gen.String(show=False, page=gen.Page('main', show=False))
|
||||
|
@ -222,6 +243,9 @@ class Tool(ModelClass):
|
|||
link=False, show='view',
|
||||
back=gen.Ref(attribute='trToTool', show=False),
|
||||
page=gen.Page('translations', show='view'))
|
||||
pages = gen.Ref(Page, multiplicity=(0,None), add=True, link=False,
|
||||
show='view', back=gen.Ref(attribute='toTool3', show=False),
|
||||
page=gen.Page('pages', show='view'))
|
||||
enableNotifications = gen.Boolean(default=True,
|
||||
page=gen.Page('notifications', show=False))
|
||||
|
||||
|
@ -235,4 +259,5 @@ class Tool(ModelClass):
|
|||
for k in toClean:
|
||||
exec 'del klass.%s' % k
|
||||
klass._appy_attributes = list(defaultToolFields)
|
||||
klass.folder = True
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.gen import *
|
||||
Grp = Group # Avoid name clashes with the Group class below and appy.gen.Group
|
||||
Pge = Page # Avoid name clashes with the Page class below and appy.gen.Page
|
||||
from appy.gen.wrappers import AbstractWrapper
|
||||
from appy.gen.wrappers.ToolWrapper import ToolWrapper as WTool
|
||||
from appy.gen.wrappers.UserWrapper import UserWrapper as WUser
|
||||
from appy.gen.wrappers.GroupWrapper import GroupWrapper as WGroup
|
||||
from appy.gen.wrappers.TranslationWrapper import TranslationWrapper as WT
|
||||
from appy.gen.wrappers.PageWrapper import PageWrapper as WPage
|
||||
from Globals import InitializeClass
|
||||
from AccessControl import ClassSecurityInfo
|
||||
tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
||||
|
@ -14,6 +16,10 @@ tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
|||
<!User!>
|
||||
<!Group!>
|
||||
<!Translation!>
|
||||
<!Page!>
|
||||
Page.pages.klass = Page
|
||||
setattr(Page, Page.pages.back.attribute, Page.pages.back)
|
||||
|
||||
<!Tool!>
|
||||
<!wrappers!>
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
body { font: 75% Helvetica,Arial,sans-serif; background-color: #EAEAEA; }
|
||||
body { font: 75% Helvetica,Arial,sans-serif; background-color: #EAEAEA;
|
||||
margin-top: 18px}
|
||||
pre { font: 100% Helvetica,Arial,sans-serif; margin: 0}
|
||||
h1 { font-size: 11pt; margin:0;}
|
||||
h2 { font-size: 10pt; margin:0; font-style: italic; font-weight: normal;}
|
||||
|
@ -38,12 +39,12 @@ img {border: 0}
|
|||
.xhtml img { margin-right: 5px }
|
||||
|
||||
.main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9;
|
||||
border-style: solid; border-width: 1px; border-color: grey; }
|
||||
border-style: solid; border-width: 1px; border-color: grey}
|
||||
.top { height: 75px; margin-left: 3em; vertical-align: top;}
|
||||
.lang { margin-right: 3px; }
|
||||
.userStrip { background-color: #d7dee4; height: 40px;
|
||||
.userStrip { background-color: #d7dee4; height: 35px;
|
||||
border-top: 1px solid #5F7983; border-bottom: 1px solid #5F7983; }
|
||||
.login { margin-top: 2px; margin-bottom: 2px; color: white;}
|
||||
.login { margin-top: 2px; margin-bottom: 2px; color: black;}
|
||||
.buttons { margin-left: 4px;}
|
||||
.fakeButton { border: 1px solid #D7DEE4; background-color: #fde8e0;
|
||||
padding: 0px 8px 2px; font: italic 92% Helvetica,Arial,sans-serif}
|
||||
|
@ -104,3 +105,4 @@ img {border: 0}
|
|||
.history th { font-style: italic; text-align; left;}
|
||||
.topSpace { margin-top: 15px;}
|
||||
.discreet { color: grey}
|
||||
.pageLink { padding-left: 6px; font-style: italic}
|
||||
|
|
|
@ -29,7 +29,11 @@
|
|||
<td tal:attributes="style python: 'background-image: url(%s/ui/banner.jpg)' % appUrl">
|
||||
<table width="100%">
|
||||
<tr valign="top">
|
||||
<td></td>
|
||||
<tal:comment replace="nothing">Links to main pages</tal:comment>
|
||||
<td>
|
||||
<a tal:repeat="page tool/getMainPages" class="pageLink"
|
||||
tal:content="page/title" tal:attributes="href page/absolute_url"></a>
|
||||
</td>
|
||||
<tal:comment replace="nothing">Language selector (links or listbox)</tal:comment>
|
||||
<td align="right" tal:condition="tool/showLanguageSelector">
|
||||
<tal:lgs define="languages tool/getLanguages;
|
||||
|
|
10
gen/utils.py
10
gen/utils.py
|
@ -18,8 +18,14 @@ def createObject(folder, id, className, appName, wf=True):
|
|||
obj.portal_type = className
|
||||
obj.id = id
|
||||
obj._at_uid = id
|
||||
userId = obj.getUser().getId()
|
||||
# If user is anonymous, userIs is None
|
||||
user = obj.getUser()
|
||||
if not user.getId():
|
||||
if user.name == 'System Processes':
|
||||
userId = 'admin' # This is what happens when Zope is starting.
|
||||
else:
|
||||
userId = None # Anonymous.
|
||||
else:
|
||||
userId = user.getId()
|
||||
obj.creator = userId or 'Anonymous User'
|
||||
from DateTime import DateTime
|
||||
obj.created = DateTime()
|
||||
|
|
Loading…
Reference in a new issue