[Checkins] SVN: grok/trunk/ Merged admin-UI into trunk.
Uli Fouquet
uli at gnufix.de
Tue Aug 14 22:53:49 EDT 2007
Log message for revision 78831:
Merged admin-UI into trunk.
Changed:
U grok/trunk/grokwiki/src/grokwiki/page.py
U grok/trunk/grokwiki/src/grokwiki/page_templates/layout.pt
U grok/trunk/grokwiki/src/grokwiki/static/wiki.css
U grok/trunk/setup.py
U grok/trunk/src/grok/admin/README.txt
U grok/trunk/src/grok/admin/__init__.py
A grok/trunk/src/grok/admin/docgrok.py
A grok/trunk/src/grok/admin/docgrok.txt
A grok/trunk/src/grok/admin/inspect.txt
A grok/trunk/src/grok/admin/messages.py
A grok/trunk/src/grok/admin/messages_templates/
A grok/trunk/src/grok/admin/objectinfo.py
A grok/trunk/src/grok/admin/objectinfo.txt
A grok/trunk/src/grok/admin/static/
A grok/trunk/src/grok/admin/tests/
A grok/trunk/src/grok/admin/utilities.py
U grok/trunk/src/grok/admin/view.py
A grok/trunk/src/grok/admin/view_templates/applications.pt
A grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt
A grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt
A grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt
A grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt
A grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt
A grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt
A grok/trunk/src/grok/admin/view_templates/docgrokview.pt
A grok/trunk/src/grok/admin/view_templates/gaiaview.pt
U grok/trunk/src/grok/admin/view_templates/index.pt
A grok/trunk/src/grok/admin/view_templates/inspect.pt
A grok/trunk/src/grok/admin/view_templates/loginform.pt
A grok/trunk/src/grok/admin/view_templates/logout.pt
A grok/trunk/src/grok/admin/view_templates/macros.pt
A grok/trunk/src/grok/admin/view_templates/server.pt
U grok/trunk/src/grok/configure.zcml
D grok/trunk/src/grok/ftests/admin/admin.py
A grok/trunk/src/grok/ftests/admin/apps.py
A grok/trunk/src/grok/ftests/admin/loginlogout.py
A grok/trunk/src/grok/ftests/admin/objectbrowser.py
-=-
Modified: grok/trunk/grokwiki/src/grokwiki/page.py
===================================================================
--- grok/trunk/grokwiki/src/grokwiki/page.py 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/grokwiki/src/grokwiki/page.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -55,4 +55,5 @@
# Update the text and redirect
self.context.update(text)
+ self.flash('Saved.')
self.redirect(self.url(self.context))
Modified: grok/trunk/grokwiki/src/grokwiki/page_templates/layout.pt
===================================================================
--- grok/trunk/grokwiki/src/grokwiki/page_templates/layout.pt 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/grokwiki/src/grokwiki/page_templates/layout.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1,26 +1,37 @@
<html metal:define-macro="main">
- <head>
- <link rel="stylesheet" tal:attributes="href static/wiki.css" type="text/css">
- </head>
+ <head>
+ <link
+ rel="stylesheet"
+ tal:attributes="href static/wiki.css"
+ type="text/css"/>
+ </head>
- <body
- tal:define="wiki context/__parent__">
- <div metal:define-slot="content">
- Page content goes here ...
- </div>
+ <body
+ tal:define="wiki context/__parent__">
- <hr/>
- <h3>Other pages</h3>
- <p>
- <span tal:repeat="page wiki">
- <a tal:attributes="href python:view.url(wiki, page)"
- tal:content="page"
- />
- </span>
- </p>
- <hr/>
- <div id="footer">
- This Wiki was grokked by Zope 3.
- </div>
- </body>
+ <div id="messages" tal:content="structure context/@@messages" />
+
+ <div metal:define-slot="content">
+ Page content goes here ...
+ </div>
+
+ <hr/>
+
+ <h3>Other pages</h3>
+
+ <p>
+ <span tal:repeat="page wiki">
+ <a tal:attributes="href python:view.url(wiki, page)"
+ tal:content="page"
+ />
+ </span>
+ </p>
+
+ <hr/>
+
+ <div id="footer">
+ This wiki is powered by Grok.
+ </div>
+
+ </body>
</html>
Modified: grok/trunk/grokwiki/src/grokwiki/static/wiki.css
===================================================================
--- grok/trunk/grokwiki/src/grokwiki/static/wiki.css 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/grokwiki/src/grokwiki/static/wiki.css 2007-08-15 02:53:48 UTC (rev 78831)
@@ -13,3 +13,12 @@
font-size:70%;
color:#999999;
}
+
+#messages li {
+ border: 1px solid #999;
+ background-color:#EEE;
+ padding:0.25em 0.5em;
+ list-style-type: none;
+ margin-left:-1.5em;
+ margin-right:1em;
+}
Modified: grok/trunk/setup.py
===================================================================
--- grok/trunk/setup.py 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/setup.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -21,6 +21,10 @@
'pytz',
'ZODB3',
'zope.annotation',
+ 'zope.app.apidoc',
+ 'zope.app.applicationcontrol',
+ 'zope.app.appsetup',
+ 'zope.app.authentication',
'zope.app.catalog',
'zope.app.component',
'zope.app.container',
@@ -29,7 +33,12 @@
'zope.app.pagetemplate',
'zope.app.publication',
'zope.app.publisher',
+ 'zope.app.renderer',
+ 'zope.app.security',
+ 'zope.app.securitypolicy',
'zope.app.testing',
+ 'zope.app.twisted',
+ 'zope.app.zcmlfiles',
'zope.component',
'zope.configuration',
'zope.dottedname',
@@ -39,15 +48,14 @@
'zope.interface',
'zope.lifecycleevent',
'zope.pagetemplate',
+ 'zope.proxy',
'zope.publisher',
'zope.schema',
'zope.security',
'zope.testing',
'zope.traversing',
'zope.testbrowser',
- 'zope.app.twisted',
- 'zope.app.securitypolicy',
- 'zope.app.zcmlfiles',
'zc.catalog',
+ 'z3c.flashmessage >=1.0dev-r77761',
],
)
Modified: grok/trunk/src/grok/admin/README.txt
===================================================================
--- grok/trunk/src/grok/admin/README.txt 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/admin/README.txt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1,13 +1,112 @@
+=====================
A basic grok admin UI
-====================
+=====================
-Overview
---------
+The internal name of the admin UI is:
+Grok Application Interface Application or, for short GAIA.
-* List of all instanciated applications (grouped by application?)
+GAIA is itself a Grok application and a subproject to the core Grok
+development. Its main goal is making developing of Zope 3 and Grok
+applications a faster and smarter job with more fun for everybody.
-* "Add new application" form: drop down for selecting the application
- and a field for the id.
-* "Delete application" form: checkboxes displayed with listed installed
- applications. Selected items may be deleted.
+Login - what is my username/password?
+-------------------------------------
+
+Before you can use the admin UI, you first must log in.
+
+The username and password of the manager principal (kind of super
+user) can be found in the file ``buildout.cfg`` in the root of your
+subversion checkout.
+
+In case you do not know, what 'subversion checkout' means, look for a
+file ``site.zcml`` in your installation.
+
+Users of ``grokproject``, might find this file in
+``<installdir>/parts/app/site.zcml``.
+
+
+Using the admin-UI
+------------------
+
+After login you can log out or, before doing that, visit some of the
+main management topics, as described below:
+
+On top of the admin-UI you can always find three links to the main
+management activities currently possible with GAIA:
+
+
+Applications
+------------
+
+* List of all instanciated applications
+
+* You can add new instances of Grok applications
+
+* For each installed application you can directly call:
+
+ - the object browser (telling you more about this concrete object)
+
+ - the class browser (telling you more about the class of your app)
+
+* For each available application type you can directly call:
+
+ - the class browser (telling you more about the class of your app)
+
+* You can delete your installed applications.
+
+
+Server
+------
+
+* Start/Restart the server. Caution! This does not work, if the server
+ was started in 'foreground mode' (with 'zopectl fg').
+
+* Get basic information about the running Zope system.
+
+* Enter a message to be displayed on top. You can, for example, leave
+ a message here for your co-admins. To delete the message, just enter
+ the empty string in the appropriate input box.
+
+
+Documentation
+-------------
+
+* From here you get starting points to the more elaborated
+ documentation features of Grok, namely:
+
+ - The object browser:
+
+ helps browsing the ZODB and other objects.
+
+ - The class browser:
+
+ gives documentation to classes, packages and other things, which
+ are not instances.
+
+
+Bugs, Caveats and Ways to Get Help
+----------------------------------
+
+The Grok admin UI was developed basically during a Google Summer of
+Code project.
+
+It is still full of bugs.
+
+For bugreports use:
+
+ https://launchpad.net/grok
+
+For discussions subscribe to the ``grok-dev`` mailing list, hosted on:
+
+ http://lists.zope.org.
+
+The projects' home is the grok subversion repository at:
+
+ http://svn.zope.org/grok/
+
+Grok's cave can be found at
+
+ http://grok.zope.org/
+
+Enjoy!
Modified: grok/trunk/src/grok/admin/__init__.py
===================================================================
--- grok/trunk/src/grok/admin/__init__.py 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/admin/__init__.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1 +1,131 @@
-#make this a package
+"""Initialize grok admin application.
+
+The grok admin application provides a session based login, which
+eventually must be enabled using Pluggable Authentication
+Utilities. This is done here.
+"""
+
+from zope.component import adapter, provideHandler
+from zope.app.appsetup.interfaces import IDatabaseOpenedWithRootEvent
+
+
+def getPrincipalCredentialsFromZCML():
+ """Read all principals' attributes from site.zcml.
+ """
+ import xml.sax
+ from zope.app.appsetup.appsetup import getConfigSource
+
+ class SAXPrincipalFinder(xml.sax.ContentHandler):
+ """Parse an XML file and get attributes of ``principal`` tags.
+
+ The principal tags of site.xml contain the credentials of
+ principals as attributes. The attributes usually are 'id',
+ 'login', 'password', 'title' and other more. And usually only
+ one pricipal is defined: the manager.
+ """
+ result = []
+
+ def startElement(self, name, attrs):
+ if name != 'principal':
+ return
+ self.result.append(dict(attrs.copy()))
+
+ site_zcml_file = getConfigSource()
+ principal_finder = SAXPrincipalFinder()
+ xml.sax.parse(site_zcml_file, principal_finder)
+ return principal_finder.result
+
+
+def setupSessionAuthentication(root_folder=None,
+ principal_credentials=[{u'id': u'zope.manager',
+ u'login': u'grok',
+ u'password': u'grok',
+ u'title': u'Manager'
+ }],
+ auth_foldername=u'authentication',
+ userfolder_name=u'Users',
+ userfolder_prefix=u'grokadmin'
+ ):
+ """Add session authentication PAU to root_folder.
+
+ Add a PluggableAuthentication in site manager of
+ root_folder. ``auth_foldername`` gives the name of the PAU to
+ install, userfolder_prefix the prefix of the authenticator plugin
+ (a simple ``PrincipalFolder``), which will be created in the PAU
+ and gets name ``userfolder_name``. ``principal_credentials`` is a
+ list of dicts with, well, principal_credentials. The keys ``id``,
+ ``login``, ``password`` and ``title`` are required for each
+ element of this list.
+ """
+ from zope.component import getUtilitiesFor
+ from zope.security.proxy import removeSecurityProxy
+ from zope.app.security.interfaces import IAuthentication
+ from zope.app.securitypolicy.interfaces import IPrincipalRoleManager
+ from zope.app.securitypolicy.interfaces import IRole
+ from zope.app.authentication import PluggableAuthentication
+ from zope.app.authentication.interfaces import IAuthenticatorPlugin
+ from zope.app.authentication.principalfolder import PrincipalFolder
+ from zope.app.authentication.principalfolder import InternalPrincipal
+
+ sm = root_folder.getSiteManager()
+ if auth_foldername in sm.keys():
+ # There is already a folder of this name.
+ return
+
+ pau = PluggableAuthentication()
+ users = PrincipalFolder(userfolder_prefix)
+
+ # Add users into principals folder to enable login...
+ for user in principal_credentials:
+ # XXX make sure, the keys exist...
+ user['id'] = user['id'].rsplit('.',1)[-1]
+ user_title = user['title']
+ principal = InternalPrincipal(user['login'],
+ user['password'],
+ user['title'])
+ users[user['id']] = principal
+
+ # Configure the PAU...
+ pau.authenticatorPlugins = (userfolder_name,)
+ pau.credentialsPlugins = ("No Challenge if Authenticated",
+ "Session Credentials")
+
+ # Add the pau and its plugin to the root_folder...
+ sm[auth_foldername] = pau
+ sm[auth_foldername][userfolder_name] = users
+ pau.authenticatorPlugins = (users.__name__,)
+
+ # Register the PAU with the site...
+ sm.registerUtility(pau, IAuthentication)
+ sm.registerUtility(users, IAuthenticatorPlugin, name=userfolder_name)
+
+ # Add manager roles to new users...
+ # XXX the real roles could be obtained from site.zcml.
+ role_ids = [name for name, util in getUtilitiesFor(IRole, root_folder)]
+ user_ids = [users.prefix + p['id'] for p in principal_credentials]
+ role_manager = IPrincipalRoleManager(root_folder)
+ role_manager = removeSecurityProxy(role_manager)
+ for role in role_ids:
+ for user_id in user_ids:
+ role_manager.assignRoleToPrincipal(role,user_id)
+
+
+
+# If a new database is created, initialize a session based
+# authentication.
+#
+# First create an eventhandler `adminSetup`, that is
+# called, whenever a database is opened...
+ at adapter(IDatabaseOpenedWithRootEvent)
+def adminSetup(event):
+ from zope.app.appsetup.bootstrap import getInformationFromEvent
+
+ db, connection, root, root_folder = getInformationFromEvent(event)
+ principal_credentials = getPrincipalCredentialsFromZCML()
+ setupSessionAuthentication(root_folder = root_folder,
+ principal_credentials = principal_credentials)
+
+
+# ...then install the event handler:
+provideHandler(adminSetup)
+
Copied: grok/trunk/src/grok/admin/docgrok.py (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py)
===================================================================
--- grok/trunk/src/grok/admin/docgrok.py (rev 0)
+++ grok/trunk/src/grok/admin/docgrok.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,647 @@
+"""The Grok's Friendly Doctor.
+
+Ask DocGrok and he will try his best, to keep you well informed about
+everything hanging around in your Zope3 and your Grok Application.
+
+See file `docgrok.txt` in this package to learn more about docgrok.
+
+"""
+
+import os
+import sys # for sys.path
+import types
+import grok
+import inspect
+from urlparse import urlparse, urlunparse
+
+import zope.component
+from zope.app.folder.interfaces import IRootFolder
+from zope.dottedname.resolve import resolve
+from zope.interface.interface import InterfaceClass
+from zope.security.proxy import isinstance
+from zope.security.proxy import removeSecurityProxy
+from zope.proxy import removeAllProxies
+
+from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.apidoc.codemodule.module import Module
+from zope.app.apidoc.codemodule.class_ import Class
+from zope.app.apidoc.codemodule.text import TextFile
+from zope.app.apidoc.utilities import renderText
+from zope.app.apidoc.utilities import getFunctionSignature
+from zope.app.apidoc.utilities import getPythonPath, getPermissionIds
+from zope.app.apidoc.utilities import isReferencable
+
+import grok.interfaces
+from grok.interfaces import IApplication
+from martian.scan import is_package, ModuleInfo
+from martian import InstanceGrokker, ModuleGrokker
+from grok.admin.objectinfo import ZopeObjectInfo
+
+# This is the name under which the docgrok object-browser can be
+# reached.
+DOCGROK_ITEM_NAMESPACE = 'docgrok-obj'
+
+grok.context(IRootFolder)
+grok.define_permission('grok.ManageApplications')
+
+def find_filepath(dotted_path):
+ """Find the filepath for a dotted name.
+
+ If a dotted name denotes a filename we try to find its path
+ by concatenating it with the system paths and looking for an
+ existing file. Every dot in the filename thereby can be part
+ of the filename or of its path. Therefore we check the
+ several possible dirname/filename combinations possible.
+
+ Returns None if no suitable filepath can be found.
+
+ This functions does *not* look for Python elements like classes,
+ interfaces and the files where they were defined. Use `resolve()`
+ and the `__file__` attribute for examining this kind of stuff
+ instead.
+
+ This function finds the location of text files and the like, as
+ far as they are placed inside some Python path.
+ """
+ currpath = dotted_path
+ currname = ""
+ while '.' in currpath:
+ currpath, name = currpath.rsplit('.', 1)
+ if currname != "":
+ currname = "." + currname
+ currname = name + currname
+ tmp_path = ""
+ for elem in currpath.split('.'):
+ tmp_path = os.path.join(tmp_path, elem)
+ for syspath in sys.path:
+ filepath_to_check = os.path.join(syspath, tmp_path, currname)
+ if os.path.isfile(filepath_to_check):
+ return filepath_to_check
+ return None
+
+
+def handle_module(dotted_path, ob=None):
+ """Determine, whether the given path/obj references a Python module.
+ """
+ if ob is None:
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ return None
+ if not hasattr(ob, '__file__'):
+ return None
+ if not is_package(os.path.dirname(ob.__file__)):
+ return None
+ if os.path.basename(ob.__file__) in ['__init__.py',
+ '__init__.pyc',
+ '__init__.pyo']:
+ return None
+ return DocGrokModule(dotted_path)
+
+def handle_package(dotted_path, ob=None):
+ """Determine, whether the given path/obj references a Python package.
+ """
+ if ob is None:
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ return None
+ if not hasattr(ob, '__file__'):
+ return None
+ if not is_package(os.path.dirname(ob.__file__)):
+ return None
+ if os.path.basename(ob.__file__) not in ['__init__.py',
+ '__init__.pyc',
+ '__init__.pyo']:
+ return None
+ return DocGrokPackage(dotted_path)
+
+def handle_interface(dotted_path, ob=None):
+ """Determine, whether the given path/obj references an interface.
+ """
+ if ob is None:
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ return None
+ if not isinstance(
+ removeAllProxies(ob), InterfaceClass):
+ return None
+ return DocGrokInterface(dotted_path)
+
+def handle_class(dotted_path, ob=None):
+ """Determine, whether the given path/obj references a Python class.
+ """
+ if ob is None:
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ return None
+ if not isinstance(ob, (types.ClassType, type)):
+ return None
+ return DocGrokClass(dotted_path)
+
+def handle_grokapplication(dotted_path, ob=None):
+ """Determine, whether the given path/obj references a Grok application.
+ """
+ if ob is None:
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ None
+ try:
+ if not IApplication.implementedBy(ob):
+ return None
+ except TypeError:
+ return None
+ return DocGrokGrokApplication(dotted_path)
+
+def handle_textfile(dotted_path, ob=None):
+ if ob is not None:
+ # Textfiles that are objects, are not text files.
+ return None
+ if os.path.splitext(dotted_path)[1] != u'.txt':
+ return None
+ return DocGrokTextFile(dotted_path)
+
+# The docgroks registry.
+#
+# We register 'manually', because the handlers
+# are defined in the same module.
+docgrok_handlers = [
+ { 'name' : 'module',
+ 'handler' : handle_module },
+ { 'name' : 'package',
+ 'handler' : handle_package },
+ { 'name' : 'interface',
+ 'handler' : handle_interface },
+ { 'name' : 'grokapplication',
+ 'handler' : handle_grokapplication },
+ { 'name' : 'class',
+ 'handler' : handle_class },
+ { 'name' : 'textfile',
+ 'handler' : handle_textfile}]
+
+
+def handle(dotted_path):
+ """Find a doctor specialized for certain things.
+ """
+ try:
+ ob = resolve(dotted_path)
+ except ImportError:
+ # There is no object of that name. Give back 404.
+ # TODO: Do something more intelligent, offer a search.
+ if not find_filepath(dotted_path):
+ return None
+ ob = None
+ except:
+ return None
+
+ for handler in docgrok_handlers:
+ spec_handler = handler['handler']
+ doc_grok = spec_handler(dotted_path, ob)
+ if doc_grok is None:
+ continue
+ return doc_grok
+ return DocGrok(dotted_path)
+
+def getInterfaceInfo(iface):
+ if iface is None:
+ return None
+ path = getPythonPath(iface)
+ return {'path': path,
+ 'url': isReferencable(path) and path or None}
+
+
+class DocGrokGrokker(InstanceGrokker):
+ """A grokker that groks DocGroks.
+
+ This grokker can help to 'plugin' different docgroks in an easy
+ way. You can register docgroks for your special classes, modules,
+ things. All required, is a function, that determines the correct
+ kind of thing, you like to offer a docgrok for and returns a
+ specialized docgrok or None (in case the thing examined is not the
+ kind of thing your docgrok is a specialist for).
+
+ Unfortunately, order counts here. If one docgrok handler is able
+ to deliver a specialized docgrok object, no further invesitgation
+ will be done.
+
+ In principle, the following should work. First we import the
+ docgrok module, because it contains a more specific grokker: the
+ InstanceGrokker 'docgrok_grokker' ::
+
+ >>> from grok.admin import docgrok
+
+ Then we get create an (empty) 'ModuleGrokker'. 'ModuleGrokkers'
+ can grok whole modules. ::
+
+ >>> from martian import ModuleGrokker
+ >>> module_grokker = ModuleGrokker()
+
+ Then we register the 'docgrok_grokker', which should contain some
+ base handlers for modules, classes, etc. by default::
+
+ >>> module_grokker.register(docgrok.docgrok_grokker)
+
+ The 'docgrok_grokker' is an instance of 'DocGrokGrokker'::
+
+ >>> from grok.admin.docgrok import DocGrokGrokker
+ >>> isinstance(docgrok.docgrok_grokker, DocGrokGrokker)
+ True
+
+ Now imagine, you have your own DocGroks for special things, for
+ example for a class 'Mammoth'. You might have derived this class
+ from DocGrok (or a subclass thereof), but this is not a
+ requirement. Note however, that other programmers might expect
+ your DocGroks to be compatible in a certain manner, so it surely
+ is a good idea to derive your GrokDocs from the original one.
+
+ Let's assume, your DocGrokMammoth is defined in a module called
+ 'mammoth'::
+
+ >>> from grok.admin.docgrok import DocGrok
+ >>> class mammoth(FakeModule):
+ ... class Mammoth(object):
+ ... pass
+ ...
+ ... class MammothDocGrok(DocGrok):
+ ... def isMammoth(self):
+ ... return True
+ ...
+ ... def handle_mammoths(dotted_path,ob=None):
+ ... if not isinstance(ob, Mammoth):
+ ... return None
+ ... return MammothDocGrok(dotted_path)
+
+ This is a simple DocGrok ('MammothDocGrok') accompanied by a
+ thing, it is representing (class 'Mammoth') and a handler
+ function, which decides, whether a given dotted path denotes a
+ Mammoth or not. The FakeModule class is a workaround to emulate
+ modules in doctests. Just think of watching a module, when you see
+ a FakeModule class.
+
+ Now we want to register this new DocGrok with the 'global
+ machinery'. Easy::
+
+ >>> module_grokker.grok('mammoth_grokker', mammoth)
+ True
+
+ Now the 'handle_mammoths' function is considered to deliver a
+ valid DocGrok, whenever it is asked. Every time, someone asks the
+ docgroks 'handle()' function for a suitable docgrok for things
+ that happen to be Mammoths, a DocGrokMammoth will be served.
+
+ Even the default docgrok viewer that comes with the grok package
+ in the admin interface, now will deliver your special views for
+ mammoths (if you defined one; otherwise the default 'DocGrok'
+ template will be used to show mammoth information).
+
+ TODO: Show how to make a docgrok view.
+
+ That's it.
+
+ """
+ component_class = types.FunctionType
+
+ def grok(self, name, obj, **kw):
+ if not name.startswith('handle_'):
+ return False
+ if name in [x['name'] for x in docgrok_handlers]:
+ return False
+ docgrok_handlers.insert(0, {'name':name,
+ 'handler':obj})
+ return True
+
+
+class DocGrok(grok.Model):
+ """DocGrok helps us finding out things about ourselves.
+
+ There are DocGroks for packages, modules, interfaces, etc., each
+ one a specialist for a certain type of element. 'Pure' DocGroks
+ build the root of this specialist hierarchy and care for objects,
+ which can not be handled by other, more specialized DocGroks.
+
+ DocGrok offers a minimum of information but can easily be extended in
+ derived classes.
+ """
+ path = None
+ _traversal_root = None
+
+ def __init__(self, dotted_path):
+ self.path = dotted_path
+
+ def getPath(self):
+ return self.path
+
+ def getFilePath(self):
+ try:
+ ob = resolve(self.path)
+ return hasattr(ob, __file__) and os.path.dirname(ob.__file__) or None
+ except ImportError:
+ pass
+ return find_filepath(self.path)
+
+ def getDoc(self, heading_only=False):
+ """Get the doc string of the module STX formatted.
+ """
+ if hasattr(self, "apidoc") and hasattr(
+ self.apidoc, "getDocString"):
+ text = self.apidoc.getDocString()
+ else:
+ return None
+ if text is None:
+ return None
+ lines = text.strip().split('\n')
+ if len(lines) and heading_only:
+ # Find first empty line to separate heading from trailing text.
+ headlines = []
+ for line in lines:
+ if line.strip() == "":
+ break
+ headlines.append(line)
+ lines = headlines
+ # Get rid of possible CVS id.
+ lines = [line for line in lines if not line.startswith('$Id')]
+ return renderText('\n'.join(lines), self.path)
+
+
+ def traverse(self,patient):
+ """ Do special traversing inside the surgery.
+
+ Inside the docgrok-'namespace' we only accept DocGroks and
+ colleagues. Each DocGrok cares for a patient represented by a
+ path. This path might denote an object in the ZODB or in the
+ python path.
+
+ """
+ if patient == "index.html":
+ return self
+ if self.path is None:
+ newpath = patient
+ else:
+ newpath = '.'.join([self.path, patient])
+
+ doctor = handle(newpath)
+
+ if doctor is None:
+ # There is nothing of that name. Give back 404.
+ # XXX Do something more intelligent, offer a search.
+ return None
+ doctor.__parent__ = self
+ doctor.__name__ = patient
+ doctor._traversal_root = self._traversal_root
+ doctor.path = newpath
+ return doctor
+ pass
+
+def getItemLink(name, baseurl):
+ """Get a link to a docgrok item out of an item name and a base URL.
+
+ A docgrok item is any object, which is a 'subobject' of another
+ object, for example an attribute, a memberfunction, an annotation
+ or a sequence item (if the parent object is a sequence). Those
+ objects are not neccessarily directly accessible, but need to be
+ denoted for the object browser. We put 'docgrok-item' as marker at
+ the beginning of the URL to enable the docgrok traverser and to
+ handle those URLs in a special way. Such the objectbrowser can
+ handle those 'unaccessible' items.
+ """
+ url = list(urlparse(baseurl))
+ path = url[2]
+ if path.startswith('/' + DOCGROK_ITEM_NAMESPACE + '/'):
+ path = path[len(DOCGROK_ITEM_NAMESPACE) + 2:]
+ if path.endswith('/@@inspect.html') or path.endswith('/inspect.html'):
+ path = path.rsplit('/', 1)
+ path = "/%s/%s%s/@@inspect.html" % (DOCGROK_ITEM_NAMESPACE, path, name)
+ path = path.replace('//', '/')
+ url[2] = path
+ return urlunparse(url)
+
+
+class DocGrokTraverser(grok.Traverser):
+ """If first URL element is 'docgrok', handle over to DocGrok.
+
+ This traverser binds to the RootFolder, which means, it is only
+ asked, when the publisher looks for elements in the Zope root (or
+ another IRootFolder). The further traversing is done by the Docs'
+ own traverser in it's model. See method `traverse()` in DocGrok.
+ """
+ grok.context(IRootFolder)
+
+ def traverse(self,path):
+ if path == DOCGROK_ITEM_NAMESPACE:
+ # The objectbrowser is called...
+ obj_info = ZopeObjectInfo(self.context)
+ obj_info.__parent__ = self.context
+ obj_info.__name__ = DOCGROK_ITEM_NAMESPACE
+ return obj_info
+
+ if path == "docgrok":
+ doctor = DocGrok(None)
+ # Giving a __parent__ and a __name__, we make things
+ # locatable in sense of ILocatable.
+ doctor.__parent__ = self.context
+ doctor.__name__ = 'docgrok'
+ doctor._traversal_root = doctor
+ return doctor
+ return None
+
+
+class DocGrokPackage(DocGrok):
+ """This doctor cares for python packages.
+ """
+ path=None
+ apidoc = None
+ _traversal_root = None
+
+ def __init__(self,dotted_path):
+ self.path = dotted_path
+ self._module = resolve(self.path)
+ # In apidoc packages are handled like modules...
+ self.apidoc = Module(None, None, self._module, True)
+
+ def getDocString(self):
+ return self.apidoc.getDocString()
+
+ def getFilePath(self):
+ ob = resolve(self.path)
+ return os.path.dirname(ob.__file__) + '/'
+
+ def _getModuleInfos(self, filter_func=lambda x:x):
+ """Get modules and packages of a package.
+
+ The filter function will be applied to a list of modules and
+ packages of type grok.scan.ModuleInfo.
+ """
+ ob = resolve(self.path)
+ filename = ob.__file__
+ module_info = ModuleInfo(filename, self.path)
+ infos = module_info.getSubModuleInfos()
+ if filter_func is not None:
+ infos = filter(filter_func, infos)
+ result = []
+ for info in infos:
+ subresult = {}
+ # Build a url string from dotted path...
+ mod_path = "docgrok"
+ for path_part in info.dotted_name.split('.'):
+ mod_path = os.path.join(mod_path, path_part)
+ subresult = {
+ 'url' : mod_path,
+ 'name' : info.name,
+ 'dotted_name' : info.dotted_name
+ }
+ result.append(subresult)
+ return result
+
+
+ def getModuleInfos(self):
+ """Get the modules inside a package.
+ """
+ filter_func = lambda x: not x.isPackage()
+ return self._getModuleInfos(filter_func)
+
+ def getSubPackageInfos(self):
+ """Get the subpackages inside a package.
+ """
+ filter_func = lambda x: x.isPackage()
+ return self._getModuleInfos(filter_func)
+
+ def getTextFiles(self):
+ """Get the text files inside a package.
+ """
+ filter_func = lambda x: x.isinstance(TextFile)
+ return self._getModuleInfos(filter_func)
+
+ def getChildren(self):
+ result = self.apidoc.items()
+ result.sort(lambda x,y:cmp(x[0], y[0]))
+ return result
+
+
+
+class DocGrokModule(DocGrokPackage):
+ """This doctor cares for python modules.
+ """
+
+ def getFilePath(self):
+ ob = resolve(self.path)
+ filename = ob.__file__
+ if filename.endswith('o') or filename.endswith('c'):
+ filename = filename[:-1]
+ return filename
+
+
+class DocGrokClass(DocGrokPackage):
+ """This doctor cares for classes.
+ """
+ def __init__(self,dotted_path):
+ self.path = dotted_path
+ self.klass = resolve(self.path)
+ self.module_path, self.name = dotted_path.rsplit('.',1)
+ self.module = resolve(self.module_path)
+ mod_apidoc = Module(None, None, self.module, False)
+ self.apidoc = Class(mod_apidoc, self.name, self.klass)
+
+ def getFilePath(self):
+ if not hasattr(self.module, "__file__"):
+ return None
+ filename = self.module.__file__
+ if filename.endswith('o') or filename.endswith('c'):
+ filename = filename[:-1]
+ return filename
+
+ def getAttributes(self):
+ """Get the attributes of this class."""
+ attrs = []
+ # See remark in getMethods()
+ klass = removeSecurityProxy(self.apidoc)
+ for name, attr, iface in klass.getAttributes():
+ entry = {'name': name,
+ 'value': `attr`,
+ 'type': type(attr).__name__,
+ 'interface': iface
+ }
+ entry.update(getPermissionIds(name,klass.getSecurityChecker()))
+ attrs.append(entry)
+ return attrs
+
+ def getMethods(self):
+ """Get all methods of this class."""
+ methods = []
+ # remove the security proxy, so that `attr` is not proxied. We could
+ # unproxy `attr` for each turn, but that would be less efficient.
+ #
+ # `getPermissionIds()` also expects the class's security checker not
+ # to be proxied.
+ klass = removeSecurityProxy(self.apidoc)
+ for name, attr, iface in klass.getMethodDescriptors():
+ entry = {'name': name,
+ 'signature': "(...)",
+ 'doc':attr.__doc__ or '',
+ 'attr' : attr,
+ 'interface' : iface}
+ entry.update(getPermissionIds(name, klass.getSecurityChecker()))
+ methods.append(entry)
+
+ for name, attr, iface in klass.getMethods():
+ entry = {'name': name,
+ 'signature': getFunctionSignature(attr),
+ 'doc':attr.__doc__ or '',
+ 'attr' : attr,
+ 'interface' : iface}
+ entry.update(getPermissionIds(name, klass.getSecurityChecker()))
+ methods.append(entry)
+ return methods
+
+
+class DocGrokInterface(DocGrokClass):
+ """This doctor cares for interfaces.
+ """
+ def __init__(self,dotted_path):
+ self.path = dotted_path
+ self.klass = resolve(self.path)
+ self.module_path, self.name = dotted_path.rsplit('.',1)
+ self.module = resolve(self.module_path)
+ mod_apidoc = Module(None, None, self.module, False)
+ self.apidoc = Class(mod_apidoc, self.name, self.klass)
+
+ def getFilePath(self):
+ if not hasattr(self.module, "__file__"):
+ return None
+ filename = self.module.__file__
+ if filename.endswith('o') or filename.endswith('c'):
+ filename = filename[:-1]
+ return filename
+
+class DocGrokGrokApplication(DocGrokClass):
+ """This doctor cares for Grok applications and components.
+ """
+ pass
+
+class DocGrokTextFile(DocGrok):
+ """This doctor cares for text files.
+ """
+
+ def __init__(self,dotted_path):
+ self.path = dotted_path
+ self.filepath = find_filepath(self.path)
+ self.filename = os.path.basename(self.filepath)
+
+
+ def getPackagePath(self):
+ """Return package path as dotted name.
+ """
+ dot_num_in_filename = len([x for x in self.filename if x == '.'])
+ parts = self.path.rsplit('.', dot_num_in_filename + 1)
+ return parts[0]
+
+ def getContent(self):
+ """Get file content UTF-8 encoded.
+ """
+ file = open(self.filepath, 'rU')
+ content = file.read()
+ file.close()
+ return content.decode('utf-8')
+
Copied: grok/trunk/src/grok/admin/docgrok.txt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.txt)
===================================================================
--- grok/trunk/src/grok/admin/docgrok.txt (rev 0)
+++ grok/trunk/src/grok/admin/docgrok.txt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,295 @@
+=======
+DocGrok
+=======
+
+The Grok's personal doctor.
+
+DocGrok is meant as a friendly extension of the Zope 3 'builtin'
+apidoc feature. It should be easy to handle and informative in
+content. Main target are developers new to Grok.
+
+DocGrok is a helper to generate documentation for nearly everything
+living in a running Zope 3 instance. Basically it provides
+'information'-objects for different types of things, like modules,
+classes, functions, textfiles etc.
+
+A ``DocGrok`` therefore is an object wich normally is bound to a
+certain entity, which is describable by a dotted path. It provides
+special methods to get information about the thing, a certain
+``DocGrok`` is bound to.
+
+.. contents::
+
+
+How to Use it
+-------------
+
+DocGrok documentation can be accessed through the web, calling special
+URL paths in your Zope 3 instance or (surprise!) directly via Python.
+
+
+Calling DocGrok through the web
++++++++++++++++++++++++++++++++
+
+To get documentation about a special element, call docgrok simply with
+`docgrok` as first part of the URL path.
+
+For example documentation about the grok package can be reached using
+
+ http://localhost:8080/docgrok/grok
+
+The admin package, which is located in the grok package can be
+accessed directly such:
+
+ http://localhost:8080/docgrok/grok/admin
+
+In this way nearly all things can be described, which can be described
+by a dotted name notation and which are accessible at runtime.
+
+
+Calling the doctor directly
++++++++++++++++++++++++++++
+
+The doctor can also be reached via Python, naturally:
+
+ >>> import grok
+ >>> from grok.admin import docgrok
+ >>> doctor = docgrok.DocGrok('grok.admin.docgrok')
+
+This doctor has immediatly a patient, which is denoted by the dotted
+path `grok.admin.docgrok`. The dotted path might reference any thing
+which lives in the Python environment: a package, a module, a class, a
+function or even a file or some interface attribute:
+
+ >>> doctor.getPath()
+ 'grok.admin.docgrok'
+
+We can also get a filepath, using the `getFilePath()` method. Objects,
+which have no filepath always return `None`.
+
+There is not much more information to get from Doc Grok. This is,
+because a `DocGrok` only knows very little about the objects. The base
+doc is not a specialist, but cares for all objects and elements, which
+can not be handled by other specialists.
+
+If we like to get more detailed information, we have to call a
+specialist. For example a package doctor, who happens to be called
+`DocGrokPackage` :
+
+ >>> from grok.admin.docgrok import DocGrokPackage
+ >>> doctor = DocGrokPackage('grok.admin')
+ >>> doctor
+ <grok.admin.docgrok.DocGrokPackage ...>
+
+Using ``getPath()`` we get the dotted path of the thing, the doctor
+cares for:
+
+ >>> doctor.getPath()
+ 'grok.admin'
+
+Fine. Obviously DocGrokPackages know as much as DocGroks. That's
+little. But a ``DocGrokPackage`` knows also about package-things:
+
+ >>> info = doctor.getSubPackageInfos()
+
+will generate infos subpackages contained in the package we are
+examining. The ``info`` here is basically a list of dictionaries,
+where each dictionary contains a the keys ``url``, ``name`` and
+``dotted_name``:
+
+ >>> info
+ [{'url': '...', 'dotted_name': '...', 'name': '...'}...]
+
+The ``url`` element is meant as the path 'inside' the
+``docgrok``-browser, when ``docgrok`` documentation is watched in a
+browser. ``dotted_name`` and ``name`` give the dotted path of a
+subpackage and last element of this path respectively.
+
+As said, ``DocGrokPackage`` is only one possible type of 'documenter'
+already included in the docgrok module. The different ``DocGroks``
+included are documented below in section `the specialists`_ in detail.
+
+So, this is allright if we already know, what kind of thing (a module,
+package, etc.) the doctor should examine. Then we can create an
+appropriate doctor and retrieve all information we like.
+
+But how can we find out, what kind of thing we examine? Shouldn't this
+be a doctors job as well? Right! This is possible and you should *use*
+the possibility to determine the most appropriate doctor
+automatically.
+
+
+Getting the right specialist directly
++++++++++++++++++++++++++++++++++++++
+
+Often we don't want to visit the base doctor, but a specialist
+directly. But how can we tell, what specialist we need? Easy. We use
+the function ``handle()`` which delivers us a doctor, who
+can tell us more:
+
+ >>> from grok.admin.docgrok import handle
+ >>> thedoc = handle('grok.admin.docgrok')
+ >>> thedoc
+ <grok.admin.docgrok.DocGrokModule ...>
+
+This is correct. `docgrok` of course *is* a python module, so the best
+specialist we can get is a `DocGrokModule`. The mentioned function
+therefore is some kind of factory, which always gives us a doctor most
+appropriate for the kind of thing specified by a dotted path.
+
+We can, for example ask for a different doc like this:
+
+ >>> thedoc = handle('grok.admin.docgrok.DocGrok')
+ >>> thedoc
+ <grok.admin.docgrok.DocGrokClass ...>
+
+and get a class-specific doctor. Because
+``grok.admin.docgrok.DocGrok`` *is* a class, this is again the most
+appropriate doc we could get.
+
+
+How to create your own specialist docgrok
+-----------------------------------------
+
+At times you might want to create your own sort of DocGrok. This might
+be due to the fact, that you have written a special kind of 'thing' (a
+special class, module, whatever), whose special attributes, methods,
+etc. should be made available in documentation in a special way.
+
+Thanks to Martijn Faassens ``martian`` package, this can be done very
+easily. All you need is a handler function, which is able to determine
+from a dotted path, whether some 'thing' is of the type you want
+special documentation for, and, of course, a special docgrok, which is
+able to deliver the special information about your new 'thing'.
+
+Let's see how this works.
+
+Writing new DocGroks
+++++++++++++++++++++
+
+Consider we want to document ``grok.View`` classes. Since
+``grok.View`` is a class, we choose the DocGrokClass as base for our
+new DocGrok:
+
+ >>> from grok.admin.docgrok import DocGrokClass
+
+ >>> class DocGrokGrokView(DocGrokClass):
+ ... """"This doctor cares for grok.Views."""
+ ... pass
+
+Now we create a doctor:
+
+ >>> doctor = DocGrokGrokView()
+ Traceback (most recent call last):
+ [...]
+ TypeError: __init__() takes exactly 2 arguments (1 given)
+
+Oops! Well, we didn't specify, what kind of DocGrokView we want to
+examine. We can do that, giving a dotted name:
+
+ >>> doctor = DocGrokGrokView('grok.View')
+ >>> doctor
+ <DocGrokGrokView ...>
+
+The doctor is in. Fine. So let's see, what he can tell us about
+``grok.View``.
+
+ >>> doctor.getPath()
+ 'grok.View'
+
+Ah, yes, very interesting. We got the dotted path of the examined
+class. But, where can we find the definition of it in file system? Ask
+the doc:
+
+ >>> doctor.getFilePath()
+ '.../grok/__init__.py'
+
+This is not exactly, what we wanted to know, is it? We got the package
+location instead of the module. So the path is wrong. Really? When you
+have a look at the specified ``__init__.py``, you will discover, that
+there is indeed an assignment, which says, that ``grok.View`` is
+another name for ``grok.components.View``. It is simply a shortcut. So,
+as we asked for ``grok.View`` the answer was correct.
+
+To check it, let's see, what happens, if we give the real dotted path:
+
+ >>> doctor = DocGrokGrokView('grok.components.View')
+ >>> doctor.getFilePath()
+ '.../grok/components.py'
+
+Ah, right. This is, what we wanted. Now we can use some of the derived
+methods to gather some more information about this class:
+
+ >>> methods = doctor.getMethods()
+
+delivers us a list of all public methods defined in this class. Each
+list entry being a dictionary with keys ``name``, ``signature``,
+``doc``, ``interface``, ``attr``, ``read_perm`` and `` write_perm``.
+
+ >>> entry = [x for x in methods if x['name'] == 'url'][0]
+ >>> entry['name']
+ 'url'
+ >>> entry['doc']
+ ''
+ >>> entry['signature']
+ '(obj=None, name=None)'
+
+The other methods work as described in the ``DocGrokClass``
+documentation.
+
+This is all very well, but not too impressive. We could gather the
+information delivered relatively easy writing some simple
+snippets. So, what is it all about?
+
+One main reason is, that ``DocGroks`` are used by the Grok admin
+interface to provide easy accessible API documentation. Those
+documentation is basically able, to give some information about
+everything, which is describable as a dotted path. But some of the
+information is not very descriptive. That's it, why ``docgrok`` uses a
+set of helpers, to give more detailed information. If you, for
+instance, want that instead of the standard class-related docgrok your
+own docgrok is displayed, whenever a user wants to know something
+about ``grok.View`` and related classes, then you can register your
+docgrok and let it display documentation for ``grok.View``.
+
+The selection to register a docgrok for ``grok.View`` was very
+arbitrary. You can also register a docgrok, that handles all elements,
+whose name starts with the letter 'a' or those elements, which are
+classes and implement at least three different interfaces. It's
+completely up to you.
+
+To choose, which API elements your docgrok is able to handle, you have
+to define a handler function. This is what we want to do next.
+
+
+Create a handler
+++++++++++++++++
+
+XXX: to be written.
+
+
+Register your new DocGrok
++++++++++++++++++++++++++
+
+XXX: to be written.
+
+
+The Specialists
+---------------
+
+``DocGrokPackage`` - The package doctor
++++++++++++++++++++++++++++++++++++++++
+
+XXX: to be written.
+
+
+``DocGrokModule`` - The module doctor
++++++++++++++++++++++++++++++++++++++
+
+XXX: to be written.
+
+
+``DocGrokClass`` - The class doctor
++++++++++++++++++++++++++++++++++++
+
+XXX: to be written.
Copied: grok/trunk/src/grok/admin/inspect.txt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/inspect.txt)
===================================================================
--- grok/trunk/src/grok/admin/inspect.txt (rev 0)
+++ grok/trunk/src/grok/admin/inspect.txt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,101 @@
+=======
+Inspect
+=======
+
+Grok's own object inspector.
+
+The Grok object browser is basically simply a view on objects.
+
+.. contents::
+
+
+How to use it
+-------------
+
+You can use the object inspector through-the-web (TTW) or using
+Python. It was primarily designed for TTW-use. This is explained
+here.
+
+
+Simple usage: Inspecting easy reachable objects
+-----------------------------------------------
+
+The inspector is called by appending ``/@@inspect.html`` to an URL
+describing an object in the ZODB. For example, to watch the object
+description of your Zopes root folder, you can use something like:
+
+ http://localhost:8080/@@inspect.html
+
+Note, that not all URLs you type into your browser denote
+objects. They often describe only certain views of certain
+objects. Currently the browsing of views with the object inspector is
+not supported.
+
+If you want to examine another object, which is reachable 'via URL',
+just get the path you would normally enter into the browser and append
+``@@inspect.html``.
+
+Assuming you once created an object, which is reachable by::
+
+ http://localhost:8080/my_object
+
+then you can examine it via the object browser, using the following
+URL::
+
+ http://localhost:8080/my_object/@@inspect.html
+
+Easy, isn't it?
+
+In most cases you can also skip the 'eyes', the strange ``@@`` symbol
+in front of the view name (``inspect.html``). The 'eyes' are only
+neccessary, when you examine an object, which has got an own attribute
+called ``inspect.html``.
+
+
+Extended usage: Inspecting 'hidden' objects
+-------------------------------------------
+
+All objects we exmined so far, are 'easy reachable'. This means, they
+are reachable by entering a simple URL. However, some (to tell the
+truth: many) objects cannot be reached this way. Such objects might be
+'subobjects' of others, they might be handled by a special traverser,
+simply have no view or whatever. We call those objects here 'hidden
+objects'. What, if we want to know more about
+those hidden objects?
+
+For example, we might have the attribute of an object (which is an
+object as well), which is not directly callable::
+
+ http://localhost:8080/my_object/hidden_attribute
+
+might so generate an error. It does not help to append simply
+``@@inspect.html`` to the URL. Instead we have to call a special
+traverser, which is able to look up the object for us and helps
+out.
+
+This traverser is called by inserting ``docgrok-obj/`` as root of
+the path in the URL. The above mentioned hidden_attribute can
+therefore be reached using::
+
+ http://localhost:8080/docgrok_obj/my_object/hidden_attribute/@@inspect.html
+
+Such, you can reach even deeply nested subobjects of subobjects.
+
+Just think of ``docgrok-obj`` as a special URL-namespace like
+``++annotations++``, ``++view++`` or similar (in fact it is not a
+URL-namespace), which can examine the complete object tree stored in
+the ZODB. In fact this 'namespace' can be used with every object, also
+with 'visible' ones. The above call to see ``my_object`` could
+therefore be reached (in a more complicated way) as::
+
+ http://localhost:8080/docgrok_obj/my_object/@@inspect.html
+
+This is the most general form to call the object browser:
+
+ http://<hostname>[:<port>]/docgrok_obj/<object-path>/@@inspect.html
+
+where 'docgrok-obj/' and '@@inspect.html' are the object browser
+specific parts.
+
+
+
Copied: grok/trunk/src/grok/admin/messages.py (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/messages.py)
===================================================================
--- grok/trunk/src/grok/admin/messages.py (rev 0)
+++ grok/trunk/src/grok/admin/messages.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,31 @@
+# -*- coding: latin-1 -*-
+# Copyright (c) 2007 gocept gmbh & co. kg
+# See also LICENSE.txt
+# $Id$
+"""Support for flash-messages in the grok admin UI."""
+
+import zope.interface
+import zope.component
+
+import grok
+
+import z3c.flashmessage.interfaces
+import z3c.flashmessage.sources
+import z3c.flashmessage.receiver
+
+
+class Messages(grok.View):
+
+ grok.context(zope.interface.Interface)
+
+ @property
+ def messages(self):
+ receiver = zope.component.getUtility(
+ z3c.flashmessage.interfaces.IMessageReceiver)
+ return receiver.receive()
+
+
+
+grok.global_utility(z3c.flashmessage.sources.SessionMessageSource,
+ name='session')
+grok.global_utility(z3c.flashmessage.receiver.GlobalMessageReceiver)
Copied: grok/trunk/src/grok/admin/messages_templates (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/messages_templates)
Copied: grok/trunk/src/grok/admin/objectinfo.py (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.py)
===================================================================
--- grok/trunk/src/grok/admin/objectinfo.py (rev 0)
+++ grok/trunk/src/grok/admin/objectinfo.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,805 @@
+"""Information about objects.
+
+This module provides wrappers/proies to give information about
+objects at runtime. See inspect.txt to learn more about retrieving
+object information using ObjectInfos.
+"""
+import inspect
+import re
+import types
+from types import MethodType
+
+import grok
+from grok.admin.utilities import isContainingEvilRegExpChars
+
+import zope
+from zope.interface import Interface, implementedBy
+from zope.interface.common.sequence import IExtendedReadSequence
+from zope.interface.common.mapping import IEnumerableMapping
+from zope.annotation.interfaces import IAnnotatable, IAnnotations
+from zope.app.apidoc import utilities
+from zope.location import location
+from zope.schema import getFields
+from zope.security.checker import getCheckerForInstancesOf
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.api import getParent, getRoot
+from zope.traversing.interfaces import IPhysicallyLocatable
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.app.folder import rootFolder
+
+
+
+class ObjectInfo(grok.Model):
+ """An inspect proxy to provide object specific data.
+
+ This is a base proxy, which can handle all (and only)
+ Python-related data. See objectinfo.txt to learn more about
+ ObjectInfos.
+ """
+ obj = None
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def getmembers(self, predicate=None):
+ """Return all the members of an object in a list of (name,
+ value) pairs sorted by name.
+
+ If the optional predicate argument is supplied, only members
+ for which the predicate returns a true value are included.
+ """
+ return inspect.getmembers(self.obj, predicate)
+
+ def getmoduleinfo(self):
+ """Get information about modules.
+
+ Return a tuple of values that describe how Python will
+ interpret the file identified by path if it is a module, or
+ None if it would not be identified as a module. The return
+ tuple is (name, suffix, mode, mtype), where name is the name
+ of the module without the name of any enclosing package,
+ suffix is the trailing part of the file name (which may not be
+ a dot-delimited extension), mode is the open() mode that would
+ be used ('r' or 'rb'), and mtype is an integer giving the type
+ of the module. mtype will have a value which can be compared
+ to the constants defined in the imp module; see the
+ documentation for that module for more information on module
+ types.
+ """
+ path = getattr(self.obj, '__file__', None)
+ if path is None:
+ return None
+ return inspect.getmoduleinfo(path)
+
+ def getmodulename(self):
+ """Return the name of the module named by the file path,
+ without including the names of enclosing packages. This uses
+ the same algorithm as the interpreter uses when searching for
+ modules. If the name cannot be matched according to the
+ interpreter's rules, None is returned.
+ """
+ path = getattr(self.obj, '__file__', None)
+ if path is None:
+ return None
+ return inspect.getmodulename(path)
+
+ def ismodule(self):
+ """Return true if the object is a module.
+ """
+ return inspect.ismodule(self.obj)
+
+ def isclass(self):
+ """Return true if the object is a class.
+ """
+ return inspect.isclass(self.obj)
+
+ def ismethod(self):
+ """Return true if the object is a method.
+ """
+ return inspect.ismethod(self.obj)
+
+ def isfunction(self):
+ """Return true if the object is a Python function or unnamed
+ (lambda) function.
+ """
+ return inspect.isfunction(self.obj)
+
+ def istraceback(self):
+ """Return true if the object is a traceback.
+ """
+ return inspect.istraceback(self.obj)
+
+ def isframe(self):
+ """Return true if the object is a frame.
+ """
+ return inspect.isframe(self.obj)
+
+ def iscode(self):
+ """Return true if the object is a code.
+ """
+ return inspect.iscode(self.obj)
+
+ def isbuiltin(self):
+ """Return true if the object is a built-in function.
+ """
+ return inspect.isbuiltin(self.obj)
+
+ def isroutine(self):
+ """Return true if the object is a user-defined or built-in
+ function or method.
+ """
+ return inspect.isroutine(self.obj)
+
+ def ismethoddescriptor(self):
+ """Return true if the object is a method descriptor, but not
+ if ismethod() or isclass() or isfunction() are true.
+
+ This is new as of Python 2.2, and, for example, is true of
+ int.__add__. An object passing this test has a __get__
+ attribute but not a __set__ attribute, but beyond that the set
+ of attributes varies. __name__ is usually sensible, and
+ __doc__ often is.
+
+ Methods implemented via descriptors that also pass one of the
+ other tests return false from the ismethoddescriptor() test,
+ simply because the other tests promise more - you can, e.g.,
+ count on having the im_func attribute (etc) when an object
+ passes ismethod().
+ """
+ return inspect.ismethoddescriptor(self.obj)
+
+ def isdatadescriptor(self):
+ """Return true if the object is a data descriptor.
+
+ Data descriptors have both a __get__ and a __set__
+ attribute. Examples are properties (defined in Python),
+ getsets, and members. The latter two are defined in C and
+ there are more specific tests available for those types, which
+ is robust across Python implementations. Typically, data
+ descriptors will also have __name__ and __doc__ attributes
+ (properties, getsets, and members have both of these
+ attributes), but this is not guaranteed. New in version 2.3.
+ """
+ return inspect.isdatadescriptor(self.obj)
+
+ def isgetsetdescriptor(self):
+ """Return true if the object is a getset descriptor.
+
+ getsets are attributes defined in extension modules via
+ PyGetSetDef structures. For Python implementations without
+ such types, this method will always return False.
+
+ New in version 2.5.
+ """
+ return inspect.isgetsetdescriptor(self.obj)
+
+ def ismemberdescriptor(self):
+ """Return true if the object is a member descriptor.
+
+ Member descriptors are attributes defined in extension modules
+ via PyMemberDef structures. For Python implementations without
+ such types, this method will always return False.
+
+ New in version 2.5.
+ """
+ return inspect.ismemberdescriptor(self.obj)
+
+ #
+ # Source code related...
+ #
+ def getdoc(self):
+ """Get the documentation string for an object.
+
+ All tabs are expanded to spaces. To clean up docstrings that
+ are indented to line up with blocks of code, any whitespace
+ than can be uniformly removed from the second line onwards is
+ removed.
+ """
+ return inspect.getdoc(self.obj)
+
+ def getcomments(self):
+ """Get comments for an object.
+
+ Return in a single string any lines of comments immediately
+ preceding the object's source code (for a class, function, or
+ method), or at the top of the Python source file (if the
+ object is a module).
+
+ Due to a bug in ``inspect.getsource()`` no objects can be
+ handled, which contain a regular expression specific char in
+ their string representation.
+
+ """
+ if isContainingEvilRegExpChars(str(self.obj)):
+ return None
+ return inspect.getcomments(self.obj)
+
+ def getfile(self):
+ """Return the name of the (text or binary) file in which an
+ object was defined.
+
+ If the object is a built-in module, class or function,
+ ``None`` will be returned.
+ """
+ try:
+ return inspect.getfile(self.obj)
+ except TypeError:
+ return
+
+ def getmodule(self):
+ """Try to guess which module an object was defined in.
+ """
+ return inspect.getmodule(self.obj)
+
+ def getsourcefile(self):
+ """Return the name of the Python source file in which an
+ object was defined.
+
+ If the object is a built-in module, class or function,
+ ``None`` will be returned.
+ """
+ try:
+ return inspect.getsourcefile(self.obj)
+ except TypeError:
+ return
+
+ def getsourcelines(self):
+ """Return a list of source lines and starting line number for
+ an object.
+
+ The argument may be a module, class, method, function,
+ traceback, frame, or code object. The source code is returned
+ as a list of the lines corresponding to the object and the
+ line number indicates where in the original source file the
+ first line of code was found. An IOError is raised if the
+ source code cannot be retrieved.
+ """
+ try:
+ return inspect.getsourcelines(self.obj)
+ except TypeError:
+ return
+ return
+
+ def getsource(self):
+ """Return the text of the source code for an object.
+
+ The argument may be a module, class, method, function,
+ traceback, frame, or code object. The source code is returned
+ as a single string. An IOError is raised if the source code
+ cannot be retrieved.
+
+ Due to a bug in ``inspect.getsource()`` no objects can be
+ handled, which contain a regular expression specific char in
+ their string representation.
+ """
+ if isContainingEvilRegExpChars(str(self.obj)):
+ return None
+
+ try:
+ return inspect.getsource(self.obj)
+ except TypeError:
+ return
+ return
+
+ def traverse(self, name):
+ new_obj = None
+
+ # Try to get name as dict entry...
+ keygetter = getattr(self.obj, 'keys', None)
+ if inspect.ismethod(keygetter):
+ if name in keygetter():
+ new_obj = self.obj[name]
+
+ # Try to get name as sequence entry...
+ if not new_obj:
+ # This is not the appropriate way to handle iterators. We
+ # must find somehing to handle them too.
+ try:
+ name_int = int(name)
+ if name_int in range(0, len(self.obj)):
+ new_obj = self.obj[name_int]
+ except ValueError:
+ pass
+
+ # Get name as obj attribute...
+ if not new_obj and hasattr(self.obj, name):
+ new_obj = getattr(self.obj, name, None)
+
+ # Get name as annotation...
+ if not new_obj:
+ naked = zope.security.proxy.removeSecurityProxy(self.obj)
+ try:
+ annotations = IAnnotations(naked)
+ new_obj = name and name in annotations and annotations[name]
+ if not new_obj:
+ new_obj = annotations
+ except TypeError:
+ pass
+
+ # Give obj a location...
+ if new_obj:
+ if not IPhysicallyLocatable(new_obj, False):
+ new_obj = location.LocationProxy(
+ new_obj, self.obj, name)
+
+ new_info = ZopeObjectInfo(new_obj)
+ new_info.__parent__ = self
+ new_info.__name__ = name
+ return new_info
+
+ # name not found...
+ return
+
+
+
+
+
+class ZopeObjectInfo(ObjectInfo):
+ """Zope specific data.
+ """
+
+ def __init__(self, obj):
+ self.obj = obj
+ self.__klass = getattr(obj, '__class__', None) or type(obj)
+ self.__interfaces = tuple(implementedBy(self.__klass))
+
+ def getTypeLink(self, obj_type):
+ if obj_type is types.NoneType:
+ return None
+ path = utilities.getPythonPath(obj_type)
+ return path.replace('.', '/')
+
+ def isLinkable(self, obj):
+ """We consider all but some basic types to be linkable for docgrok.
+
+ Although even simple strings can be displayed by a docgrok, it
+ does not make much sense. We therefore simply forbid such
+ links, filtering objects of basic types out.
+ """
+ for typespec in [types.NoneType, types.TypeType, types.BooleanType,
+ types.IntType, types.LongType, types.FloatType,
+ types.ComplexType, types.StringTypes,
+ types.MethodType, types.BuiltinFunctionType,
+ types.LambdaType, types.GeneratorType, types.CodeType,
+ types.FileType, types.TracebackType, types.FrameType,
+ types.BufferType, types.NotImplementedType]:
+ if isinstance(obj, typespec):
+ return False
+ return True
+
+ def getParent(self):
+ return getParent(self.obj)
+
+ def getPythonPath(self):
+ return utilities.getPythonPath(self.obj)
+
+ def getDirectlyProvidedInterfaces(self):
+ # This comes from apidoc...
+ # Getting the directly provided interfaces works only on naked objects
+ naked = removeSecurityProxy(self.obj)
+ return [utilities.getPythonPath(iface)
+ for iface in zope.interface.directlyProvidedBy(naked)]
+
+ def getProvidedInterfaces(self):
+ return self.__interfaces
+
+ def getBases(self):
+ """Get base classes.
+ """
+ klass = getattr(self.obj, '__class__', None)
+ return getattr(klass, '__bases__', None)
+
+ def getAttributes(self):
+ """Get all attributes of an object.
+
+ See objectinfo.txt to learn more.
+ """
+ klass = removeSecurityProxy(self.__klass)
+ obj = removeSecurityProxy(self.obj)
+ for name in dir(obj):
+ value = getattr(obj, name, None)
+ if value is None:
+ continue
+ if inspect.ismethod(value) or inspect.ismethoddescriptor(value):
+ continue
+ entry = {
+ 'name': name,
+ 'value': `value`,
+ 'value_linkable': self.isLinkable(value),
+ 'type' : type(value),
+ # type_link is a very browser oriented data
+ # element. Move it to a view?
+ 'type_link': self.getTypeLink(type(value)),
+ 'interface': utilities.getInterfaceForAttribute(
+ name, klass=klass)
+ }
+ entry.update(utilities.getPermissionIds(
+ name, getCheckerForInstancesOf(klass)))
+ yield entry
+
+ def getMethods(self):
+ """Get all methods of an object.
+
+ Get a list of dicts, describing the methods of an object. The
+ dicts support keys ``name`` (name of method), ``signature``,
+ ``doc`` (method's docstring or ``None``) and ``interface``
+ (the interface, where the method was defined or ``None``).
+ """
+ klass = removeSecurityProxy(self.__klass)
+ obj = removeSecurityProxy(self.obj)
+ for name in dir(obj):
+ value = getattr(obj, name, None)
+ if value is None:
+ continue
+ if not (inspect.ismethod(value)
+ and not inspect.ismethoddescriptor(value)):
+ continue
+ if inspect.ismethod(value):
+ signature = utilities.getFunctionSignature(value)
+ else:
+ signature = '(...)'
+
+ entry = {
+ 'name': name,
+ 'signature' : signature,
+ 'doc' : getattr(value, '__doc__', None),
+ 'interface': utilities.getInterfaceForAttribute(
+ name, klass=klass),
+ 'value_linkable': self.isLinkable(value),
+ }
+ entry.update(utilities.getPermissionIds(
+ name, getCheckerForInstancesOf(klass)))
+ yield entry
+
+ def isSequence(self):
+ """Is the object observed a sequence?
+ """
+ if isinstance(self.obj, types.ListType):
+ return True
+ if isinstance(self.obj, types.TupleType):
+ return True
+ return IExtendedReadSequence.providedBy(self.obj)
+
+ def getSequenceItems(self):
+ """Get the items of a sequence.
+
+ Returns a list of dicts, each representing one element of the
+ sequence.
+ """
+ if not self.isSequence():
+ return
+
+ elems = []
+ naked = removeSecurityProxy(self.obj)
+ for index in xrange(0, len(self.obj)):
+ value = naked[index]
+ elems.append({
+ 'index' : index,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ })
+ return elems
+
+ def isMapping(self):
+ """Is the object observed a mapping?
+
+ Mappings are those objects, which are dicts or provide
+ IEnumerableMapping.
+ """
+ if isinstance(self.obj, types.DictType):
+ return True
+ return IEnumerableMapping.providedBy(self.obj)
+
+ def getMappingItems(self):
+ """Get the elements of a mapping.
+
+ The elements are delivered as a list of dicts, each dict
+ containing the keys ``key``, ``key_string`` (the key as a
+ string), ``value``, ``value_type`` and ``value_type_link``.
+ """
+ elems = []
+ naked = removeSecurityProxy(self.obj)
+ if not hasattr(naked, 'items'):
+ return []
+ for key, value in naked.items():
+ elems.append({
+ 'key' : key,
+ 'key_string' : `key`,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ })
+ return elems
+
+ def isAnnotatable(self):
+ """Does the object observed expect to be annotated?
+ """
+ return IAnnotatable.providedBy(self.obj)
+
+
+ def getAnnotationsInfo(self):
+ """Get all annotations associated with an object.
+
+ If no annotations are associated with the object, ``None`` is
+ returned. Otherwise we get a list of dicts, each containing
+ keys ``key``, ``key_string`` (textual representation of key),
+ ``value``, ``value_type`` and ``value_type_link''.
+ """
+ if not self.isAnnotatable():
+ return []
+ naked = removeSecurityProxy(self.obj)
+ annotations = IAnnotations(naked)
+ if not hasattr(annotations, 'items'):
+ return []
+ elems = []
+ for key, value in annotations.items():
+ elems.append({
+ 'key' : key,
+ 'key_string' : `key`,
+ 'value' : value,
+ 'value_type' : type(value),
+ 'value_type_link' : self.getTypeLink(type(value)),
+ 'value_linkable': self.isLinkable(value),
+ 'obj' : annotations[key]
+ })
+ return elems
+
+ def getId(self):
+ """Try to determine some kind of name.
+ """
+ return (getattr(self.obj, '__name__', None)
+ or getattr(self.obj, 'id', None)
+ or getattr(self.obj, '_o_id', None))
+
+
+from zope.traversing.interfaces import ITraversable
+from zope.app.folder.interfaces import IRootFolder
+from zope.location import LocationProxy
+class AnnotationsTraverser(grok.Traverser):
+ """If first URL element is '++anno++', handle over to
+ ObjectBrowser.
+
+ This traverser binds to the RootFolder, which means, it is only
+ asked, when the publisher looks for elements in the Zope root (or
+ another IRootFolder). The further traversing is done by the Docs'
+ own traverser in it's model. See method `traverse()` in DocGrok.
+ """
+ grok.context(Interface)
+ #grok.name('anno')
+ #grok.provides(ITraversable)
+
+ def traverse(self,path):
+ namespace = 'anno'
+ print "TRAVERSE", path
+ if path.startswith(namespace):
+ name = path[len(namespace):]
+ naked = removeSecurityProxy(self.context)
+ annotations = IAnnotations(naked)
+ print annotations.items()
+ #obj = name and annotations[name] or annotations
+ #obj = path and annotations[name] or annotations
+ obj = ObjectInfo("Hello")
+ if not IPhysicallyLocatable(obj, False):
+ #obj = LocationProxy(
+ # obj, self.context, namespace + name)
+ obj = LocationProxy(
+ obj, self.context, 'anno' + name)
+ return obj
+ return
+
+ def render(self):
+ pass
+
+##
+## This comes from Dieter Maurer:
+##
+
+def determineClass(o):
+ return hasattr(o, '__class__') and o.__class__ or type(o)
+
+class Recorder(object):
+ def __init__(self): self._account = {}
+ def __call__(self, o):
+ a = self._account
+ class_ = determineClass(o)
+ if class_ in a: a[class_] += 1
+ else: a[class_] = 1
+ def account(self): return self._account
+
+def sortRecords(recorder):
+ return sorted(recorder.account().items(), key=lambda r: r[1], reverse=True)
+
+def fix((ty, no)):
+ '''some types apparently have a broken 'str'. Fix this.'''
+ try: tyn = str(ty)
+ except TypeError:
+ try: tyn = 'BROKEN-STR: %r' % ty
+ except TypeError: tyn = 'BROKEN-STR-AND-REPR'
+ return tyn, no
+
+def analyseObjects(limit=100):
+ '''analyses live objects and garbage.
+
+ The result is a pair with information for live and garbage objects, respectively.
+ The information is a dict with keys 'count' and 'sorted'.
+ 'count' is the total number of objects, 'sorted' a list with pairs
+ 'type' and 'instanceCount', inverse sorted by 'instanceCount'.
+ '''
+ result = []
+ import gc
+ for objs in gc.get_objects(), gc.garbage:
+ r = Recorder()
+ for o in objs: r(o)
+ result.append({
+ 'count':len(objs),
+ 'sorted':map(fix, sortRecords(r)[:limit]),
+ })
+ return result
+
+
+
+
+
+
+
+
+
+
+
+
+class ObjectInfo_obsolete(object):
+ """A wrapper to provide object specific information.
+
+ This is the base wrapper.
+
+ See inspect.txt to learn more about this kind of thing.
+ """
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ def getName(self):
+ """Try to determine the name of an object.
+ """
+ for key in ['__name__', 'id', 'title', '_o_id']:
+ name = getattr(self.obj, key, None)
+ if name is not None:
+ break
+ return name
+
+ def getDoc(self):
+ """Fetch any doc strings from the wrapped object.
+ """
+ if hasattr(self.obj, '__class__'):
+ return getattr(self.obj.__class__, '__doc__', None)
+ return
+
+ def getType(self):
+ """Get the wrapped objects' type as a string in dotted path
+ notation.
+ """
+ return type(self.obj)
+
+ def getParent(self):
+ """Get the parent of the wrapped object.
+
+ This might result in a `TypeError` if the wrapped object is
+ not locatable in sense of ILocation, i.e. if it doesn't
+ provide ``__parent__`` attribute.
+ """
+ return getParent(self.obj)
+
+ def getChildren(self):
+ """Returns a list of children objects.
+ """
+ return dir(self.obj)
+
+ def getRoot(self):
+ """Get the root obj of the wrapped object.
+ """
+ try:
+ return getRoot(self.obj)
+ except TypeError:
+ tmp = self.obj
+ while getattr(tmp, '__parent__', None):
+ tmp = tmp.__parent__
+ return tmp
+ return
+
+ def isRoot(self):
+ """Check, whether the wrapped object is the root within its branch.
+
+ This does of course not necessarily mean, it is also the root
+ of the whole instance.
+ """
+ try:
+ return self.getRoot() == self.obj
+ except TypeError:
+ if getattr(self.obj, '__parent__', True) is None:
+ return True
+ return False
+
+ def is_broken(self):
+ """Check, whether the wrapped object is broken.
+ """
+ # XXX to be implemented.
+ return
+
+
+class DCObjectInfo(ObjectInfo_obsolete):
+ """An object wrapper, that provides DublinCore related information.
+
+ If such information is not available (for instance, because the
+ type of object can't be adapted), None is returned for every
+ value. It is therefore safe to use this info type also for objects
+ not supporting DC.
+ """
+ obj = None
+ supports_dc = False
+ _metadata = None
+ _covered_keys = [ 'created', 'modified', 'effective', 'expires',
+ 'publisher', 'creators', 'subjects', 'contributors',
+ 'description', 'title']
+
+
+ def __init__(self, obj):
+
+ super(ObjectInfo, self).__init__(obj)
+ self.obj = obj
+ try:
+ dc = IZopeDublinCore(self.obj)
+ self._metadata = dict((field, getattr(dc, field))
+ for field in getFields(IZopeDublinCore) if hasattr(dc, field))
+ self.supports_dc = True
+ except TypeError:
+ # A type error occurs, when obj can't be adapted to
+ # IZopeDublinCore.
+ self._metadata = dict([(x,None) for x in self._covered_keys])
+
+
+ def _getDCEntry(self, category):
+ if category in self._metadata.keys() and self._metadata[category]:
+ return self._metadata[category]
+ return
+
+
+
+ def getDCMisc(self):
+ """Get dict of DC metadata not covered by other methods.
+ """
+ return dict([x for x in self._metadata.items()
+ if x[0] not in self.covered_keys])
+
+ def getDCTitle(self):
+ return self._getDCEntry('title') or u''
+
+ def getDCDescription(self):
+ return self._getDCEntry('description') or u''
+
+ def getDCCreators(self):
+ return self._getDCEntry('creators') or ()
+
+ def getDCContributors(self):
+ return self._getDCEntry('contributors') or ()
+
+ def getDCSubjects(self):
+ return self._getDCEntry('subjects') or ()
+
+ def getDCPublisher(self):
+ return self._getDCEntry('publisher') or u''
+
+ def getDCCreationDate(self):
+ return self._getDCEntry('created') or u''
+
+ def getDCModificationDate(self):
+ return self._getDCEntry('modified') or u''
+
+ def getDCEffectiveDate(self):
+ return self._getDCEntry('effective') or u''
+
+ def getDCExpiringDate(self):
+ return self._getDCEntry('expires') or u''
+
Copied: grok/trunk/src/grok/admin/objectinfo.txt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/objectinfo.txt)
===================================================================
--- grok/trunk/src/grok/admin/objectinfo.txt (rev 0)
+++ grok/trunk/src/grok/admin/objectinfo.txt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,651 @@
+============
+Object Infos
+============
+
+Retrieve information about objects.
+
+ObjectInfos are used by the Grok object browser to retrieve the data
+displayed in the ``inspect.html`` view. See inspect.txt to learn more
+about using the inspector.
+
+.. contents::
+
+
+ObjectInfo:
+-----------
+
+Object infos are in fact wrappers for the functionality offered by the
+Python standard library ``inspect``.
+
+Let's create a simple class:
+
+ >>> class Mammoth(object):
+ ... """A mammoth."""
+ ... pass
+
+Now create an object. Meet Manfred:
+
+ >>> manfred = Mammoth()
+ >>> manfred
+ <Mammoth object at ...>
+
+An ObjectInfo's job is it, to find out as much as possible about
+Manfred. We create an ObjectInfo by giving the object to examine as
+parameter:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> info = ObjectInfo(manfred)
+ >>> info
+ <grok.admin.objectinfo.ObjectInfo object at ...>
+
+What can we know about Manfred now?
+
+ >>> members = info.getmembers()
+ >>> ('__class__', Mammoth) in members
+ True
+
+ >>> info.getmoduleinfo() is None
+ True
+
+A Mammoth is not a module. Let's see, what happens when we examine a
+real module:
+
+ >>> import grok.admin.objectinfo
+ >>> ObjectInfo(grok.admin).getmoduleinfo() in [('__init__', '.pyc',
+ ... 'rb', 2), ('__init__', '.py', 'U', 1)]
+ True
+
+ >>> info.getmodulename() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo).getmodulename()
+ 'objectinfo'
+
+ >>> info.isclass()
+ False
+
+manfred is not a class, but an instance of a class. This information
+is correct.
+
+ >>> ObjectInfo(Mammoth).isclass()
+ True
+
+ >>> info.ismethod()
+ False
+
+ >>> ObjectInfo(info.ismethod).ismethod()
+ True
+
+ >>> info.isfunction()
+ False
+
+ >>> def f():
+ ... pass
+
+ >>> ObjectInfo(f).isfunction()
+ True
+
+ >>> info.istraceback()
+ False
+
+ >>> info.isframe()
+ False
+
+ >>> info.iscode()
+ False
+
+ >>> info.isbuiltin()
+ False
+
+ >>> ObjectInfo(dir).isbuiltin()
+ True
+
+ >>> info.isroutine()
+ False
+
+ >>> info.ismethoddescriptor()
+ False
+
+ >>> info.isdatadescriptor()
+ False
+
+ >>> info.getdoc()
+ 'A mammoth.'
+
+ >>> info.getcomments() is None
+ True
+
+We have a comment in the sources of ObjectInfo:
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.getdoc).getcomments()
+ '# Source code related...\n'
+
+A bug in ``inspect.getcomments()`` causes objects with regular
+expression chars ('+', '*', etc.) in their name to fail. A workaround
+should check this and give ``None`` instead:
+
+ >>> evil_obj = Mammoth()
+ >>> evil_obj.__str__ = '+++evil+++'
+ >>> ObjectInfo(evil_obj).getcomments() is None
+ True
+
+``getfile()`` gives the name of the file the object was defined
+in. Contrary to the inspect method, this one returns None, if the
+object is a built-in module, class or function.
+
+ >>> info.getfile() is None
+ True
+
+The filename can be a compiled file ('.pyc') or a Python source file
+('.py'):
+
+ >>> filename = ObjectInfo(grok.admin.objectinfo.ObjectInfo).getfile()
+ >>> filename = filename[-1] == 'c' and filename or filename + 'c'
+ >>> filename
+ '.../grok/admin/objectinfo.pyc'
+
+
+ >>> info.getmodule()
+ <module '__builtin__' (built-in)>
+
+
+Here the same applies as for getfile().
+
+ >>> info.getsourcefile() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo).getsourcefile()
+ '.../grok/admin/objectinfo.py'
+
+ >>> info.getsourcelines() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.isclass).getsourcelines()
+ ([' def isclass(self):\n',...)
+
+ >>> ObjectInfo(len).getsourcefile() is None
+ True
+
+ >>> info.getsource() is None
+ True
+
+ >>> ObjectInfo(grok.admin.objectinfo.ObjectInfo.isclass).getsource()
+ ' def isclass(self):\n...'
+
+A bug in ``inspect.getsource()`` causes objects with regular
+expression chars ('+', '*', etc.) in their name to fail. This is true
+for objects like '+++etc++site' and friends. A workaround should check
+this and give ``None`` instead:
+
+ >>> evil_obj = Mammoth()
+ >>> evil_obj.__str__ = '+++evil+++'
+ >>> ObjectInfo(evil_obj).getsource() is None
+ True
+
+
+ZopeObjectInfo
+--------------
+
+All information, which is special for Zope objects, can be digged
+using ``ZopeObjectInfo``. Zope specific are especially things related
+to the ZODB or interfaces.
+
+
+Preliminaries
++++++++++++++
+
+We first setup some objects, that we can examine thereafter. Let's
+create a ZopeObjectInfo for a typical Zope object, the root
+folder. First get the root folder:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> root
+ <zope.app.folder.folder.Folder object at ...>
+
+Then get an associated ZopeObjectInfo:
+
+ >>> from grok.admin.objectinfo import ZopeObjectInfo
+ >>> root_info = ZopeObjectInfo(root)
+ >>> root_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+We create a folder in the root:
+
+ >>> from zope.app.folder.folder import Folder
+ >>> subfolder = Folder()
+ >>> root['Savannah'] = subfolder
+
+and get a ZopeObjectInfo for it as well:
+
+ >>> subfolder_info = ZopeObjectInfo(subfolder)
+ >>> subfolder_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+Finally, we create some content in the subfolder. A cave, where
+Manfred can stay
+
+ >>> class Cave(grok.Container):
+ ... """A home, sweet home."""
+ ... pass
+
+ >>> import grok
+ >>> sweethome = Cave()
+ >>> subfolder['SweetHome'] = sweethome
+ >>> sweethome_info = ZopeObjectInfo(sweethome)
+ >>> sweethome_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+and put Manfred into the cave:
+
+ >>> sweethome['Owner'] = manfred
+ >>> manfred_info = ZopeObjectInfo(manfred)
+ >>> manfred_info
+ <grok.admin.objectinfo.ZopeObjectInfo object at ...>
+
+Now we can examine the objects further.
+
+getId()
++++++++
+
+Objects' names can be stored in a variety of attributes. It is
+therefore a bit difficult to get the right name (if any). ``getId``
+looks currently for ``__name__``, ``id`` and ``_o_id``. If all fails,
+``None`` is returned.
+
+ >>> root_info.getId() is None
+ True
+
+ >>> subfolder_info.getId()
+ u'Savannah'
+
+ >>> manfred_info.getId() is None
+ True
+
+getParent()
++++++++++++
+
+What is the parent object of the
+root?
+
+ >>> root_info.getParent() is None
+ True
+
+ >>> subfolder_info.getParent()
+ <zope.app.folder.folder.Folder object at ...>
+
+Oh, a folder. Is it the root?
+
+ >>> subfolder_info.getParent() == root
+ True
+
+What about the sweet home?
+
+ >>> sweethome_info.getParent()
+ <zope.app.folder.folder.Folder object at ...>
+
+ >>> sweethome_info.getParent() == subfolder
+ True
+
+Last not least, Manfred:
+
+ >>> manfred_info.getParent()
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Not enough context information to get parent', <Mammoth object at ...>)
+
+Oops! Well, mammoths are not too smart. They wander around all the
+time and so sometimes they forget where they are. Technically,
+Mammoths are not locatable in sense of ILocatable. We can make them
+locatable setting two attributes:
+
+ >>> manfred.__parent__ = sweethome
+ >>> manfred.__name__ = u'Manfred'
+
+Afterwards we can tell, where Manfred lives:
+
+ >>> manfred_info.getParent()
+ <Cave object at ...>
+
+
+getDirectlyProvidedInterfaces()
++++++++++++++++++++++++++++++++
+
+Gives a list of interfaces provided *directly* by the objects in
+dotted-path notation.
+
+ >>> root_info.getDirectlyProvidedInterfaces()
+ ['zope.app.folder.interfaces.IRootFolder']
+
+ >>> subfolder_info.getDirectlyProvidedInterfaces()
+ []
+
+ >>> sweethome_info.getDirectlyProvidedInterfaces()
+ []
+
+ >>> manfred_info.getDirectlyProvidedInterfaces()
+ []
+
+
+getInterfaces()
++++++++++++++++
+
+Gives a tuple of all interfaces provided by the object, not only the
+directly provided ones.
+
+ >>> root_info.getProvidedInterfaces()
+ (<InterfaceClass zope.app.folder.interfaces.IFolder>, <InterfaceClass persistent.interfaces.IPersistent>, <InterfaceClass zope.app.component.interfaces.IPossibleSite>, <InterfaceClass zope.app.container.interfaces.IContained>)
+
+ >>> sweethome_info.getProvidedInterfaces()
+ (<InterfaceClass zope.annotation.interfaces.IAttributeAnnotatable>, <InterfaceClass zope.app.container.interfaces.IContainer>, <InterfaceClass zope.app.container.interfaces.IContained>, <InterfaceClass persistent.interfaces.IPersistent>)
+
+Manfred again, is a bit too plain to give us interesting information:
+
+ >>> manfred_info.getProvidedInterfaces()
+ ()
+
+
+getBases()
+++++++++++
+
+What bases built our objects?
+
+ >>> root_info.getBases()
+ (<type 'persistent.Persistent'>, <class 'zope.app.component.site.SiteManagerContainer'>, <class 'zope.app.container.contained.Contained'>)
+
+ >>> sweethome_info.getBases()
+ (<class 'grok.components.Container'>,)
+
+ >>> manfred_info.getBases()
+ (<type 'object'>,)
+
+
+getAttributes()
++++++++++++++++
+
+Attributes are members, which are not methods nor methoddescriptors
+(according to ``inspect`` package). This method gives (contrary to the
+apidoc method with same name) *all* attributes, also the 'private'
+ones, because some private attributes can be of interest, when
+debugging happens.
+
+Attributes are given as an iterator over dicts, each dict containing
+the keys
+
+- ``name``: name of attribute.
+
+- ``value``: value of attribute as string.
+
+- ``value_linkable``:
+
+ a boolean indicating, whether the attribute is reachable directly,
+ for instance calling a special URL. Linkable values can be examined
+ further by calling the current URL and inserting the name of
+ attribute. Example: an object examined by
+
+ http://localhost:8080/someobj/inspect.html
+
+ which owns a linkable attribute ``myattr`` can be examined by
+
+ http://localhost:8080/someobj/myattr/inspect.html
+
+- ``type``: type of value as string.
+
+- ``type_link``: link to the type documentation as str.
+
+- ``interface``: name of the interface, this attribute was defined in
+ or ``None``.
+
+- ``read_perm`` and ``write_perm``: permissions to read/write the
+ attribute.
+
+To get the attributes as a list, you can use ``list()`` as shown
+below.
+
+We now look for the attributes of the root folder:
+
+ >>> attrs = list(root_info.getAttributes())
+
+There should be a ``data`` attribute available:
+
+ >>> attr_names = [x['name'] for x in attrs]
+ >>> 'data' in attr_names
+ True
+
+And the ``data`` attribute should have eight entries (as described
+above):
+
+ >>> data_attr = [x for x in attrs if x['name'] == 'data'][0]
+ >>> len(data_attr)
+ 8
+
+First, let's determine the name of the attribute:
+
+ >>> data_attr['name']
+ 'data'
+
+The value of the attribute:
+
+ >>> data_attr['value']
+ '<BTrees.OOBTree.OOBTree object at ...>'
+
+The value is directly reachable using the traverser:
+
+ >>> data_attr['value_linkable']
+ True
+
+We can get some information about the type of the value:
+
+ >>> data_attr['type']
+ <type 'BTrees.OOBTree.OOBTree'>
+
+ >>> data_attr['type_link']
+ 'BTrees/OOBTree/OOBTree'
+
+Is there an interface this attribute was defined in?
+
+ >>> data_attr['interface'] is None
+ True
+
+There are no special permissions defined to read or write the 'data'
+attribute.
+
+ >>> data_attr['read_perm'] is None
+ True
+
+ >>> data_attr['write_perm'] is None
+ True
+
+
+getMethods()
+++++++++++++
+
+This method gives (contrary to the apidoc method with same name) *all*
+methods, also the 'private' ones, because some private attributes can
+be of interest, when debugging happens.
+
+Methods are given as an iterator over dicts, each dict containing
+the keys
+
+- ``name``: the name of the method.
+
+- ``signature``: the signature of the methods as string.
+
+- ``doc``: an doc string of the method (if one exists)
+
+- ``interface``: the interface this method was implemented at (if
+ any).
+
+- ``read_perm`` and ``write_perm``: permissions to read/write the
+ method.
+
+We first get the methods of the root object. Because ``getMethods``
+returns an iterable, we form a list, using ``list()``:
+
+ >>> methods = list(root_info.getMethods())
+
+ >>> len(methods)
+ 13
+
+Okay, there are 13 methods defined in this object. We pick one to
+examine it further:
+
+ >>> items_method = [x for x in methods if x['name'] == 'items'][0]
+ >>> len(items_method)
+ 7
+
+There are six keys in the dict describing the ``items()`` method of
+the root folder.
+
+ >>> items_method['name']
+ 'items'
+
+The ``items()`` method takes no parameters:
+
+ >>> items_method['signature']
+ '()'
+
+The method is documented:
+
+ >>> items_method['doc']
+ 'Return a sequence-like object containing tuples of the form\n (name, object) for the objects that appear in the folder.\n '
+
+This is the raw documentation string, obviously. It can be formatted
+using render methods defined in the inspect view class.
+
+The method has no special interface where it was defined:
+
+ >>> items_method['interface'] is None
+ True
+
+And there are no special permissions set on this method:
+
+ >>> items_method['read_perm'] is None
+ True
+
+ >>> items_method['write_perm'] is None
+ True
+
+
+isSequence()
+++++++++++++
+
+Sequences are those objects, which provide the IExtendedReadSequence
+interface, are of tuple type or of list type.
+
+ >>> root_info.isSequence()
+ False
+
+ >>> ZopeObjectInfo(['a', 'list', 'of', 'values']).isSequence()
+ True
+
+ >>> ZopeObjectInfo(('a', 'tuple', 'of', 'values')).isSequence()
+ True
+
+ >>> ZopeObjectInfo({'a': 'dict', 'of': 'values'}).isSequence()
+ False
+
+
+getSequenceItems()
+++++++++++++++++++
+
+ >>> testlist = ['a', 'list', 'of', 'values']
+ >>> list_info = ZopeObjectInfo(testlist).getSequenceItems()
+ >>> len(list_info)
+ 4
+
+ >>> first_elem = list_info[0]
+ >>> first_elem['index']
+ 0
+
+ >>> first_elem['value_type']
+ <type 'str'>
+
+ >>> first_elem['value_type_link']
+ '__builtin__/str'
+
+ >>> first_elem['value']
+ 'a'
+
+
+isMapping()
++++++++++++
+
+ >>> root_info.isMapping()
+ True
+
+ >>> ZopeObjectInfo(['a', 'list', 'of', 'values']).isMapping()
+ False
+
+ >>> ZopeObjectInfo(('a', 'tuple', 'of', 'values')).isMapping()
+ False
+
+ >>> ZopeObjectInfo({'a': 'dict', 'of': 'values'}).isMapping()
+ True
+
+ >>> ZopeObjectInfo(root.data).isMapping()
+ False
+
+
+getMappingItems()
++++++++++++++++++
+
+ >>> map_elems = root_info.getMappingItems()
+ >>> u'Savannah' in [x['key'] for x in map_elems]
+ True
+
+ >>> map_elem = [x for x in map_elems if x['key'] == u'Savannah'][0]
+ >>> map_elem['key']
+ u'Savannah'
+
+ >>> map_elem['key_string']
+ "u'Savannah'"
+
+ >>> map_elem['value']
+ <zope.app.folder.folder.Folder object at ...>
+
+ >>> map_elem['value_type']
+ <class 'zope.app.folder.folder.Folder'>
+
+ >>> map_elem['value_type_link']
+ 'zope/app/folder/folder/Folder'
+
+Objects, which are not mappings, should return the empty list:
+
+ >>> ZopeObjectInfo('a string').getMappingItems()
+ []
+
+isAnnotatable()
++++++++++++++++
+
+Checks for the interface IAnnotatable. Most 'usual' Zope objects are
+annotatable...
+
+ >>> root_info.isAnnotatable()
+ True
+
+ >>> sweethome_info.isAnnotatable()
+ True
+
+...but some very simple ones are not (or have not declared to be
+annotatable although they are):
+
+ >>> manfred_info.isAnnotatable()
+ False
+
+
+getAnnotationsInfo()
+++++++++++++++++++++
+
+ >>> root_info.getAnnotationsInfo()
+ []
+
+ >>> sweethome_info.getAnnotationsInfo()
+ []
+
+Manfred is not annotatable, but instead of an error we get an empty
+list:
+
+ >>> manfred_info.getAnnotationsInfo()
+ []
+
Copied: grok/trunk/src/grok/admin/static (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/static)
Copied: grok/trunk/src/grok/admin/tests (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/tests)
Copied: grok/trunk/src/grok/admin/utilities.py (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/utilities.py)
===================================================================
--- grok/trunk/src/grok/admin/utilities.py (rev 0)
+++ grok/trunk/src/grok/admin/utilities.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,240 @@
+"""Helper functions for grok admin.
+"""
+import re
+from zope.tal.taldefs import attrEscape
+from urlparse import urlparse, urlunparse
+
+def getPathLinksForObject(obj, root_url=''):
+ """Given an object, this function returns HTML code with links to
+ documentation.
+
+ The object must provide a string representation like 'foo.blah
+ object at 0x9999999'. Returned is then a string, where 'foo' and
+ 'blah' are embedded in HTML links to docgrok documentation for foo
+ and foo.blah.
+
+ The (optional) ``root_url`` is used to create the links to docgrok
+ documentation. It is expected to be the URL, which can generate
+ docgrok documentation by appending '/docgrok' to the URL.
+
+ We can use ObjectInfo objects to check this:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> obj = ObjectInfo(None)
+ >>> obj
+ <grok.admin.objectinfo.ObjectInfo object at ...>
+
+ Obviously we have a string representation of the required form
+ here. So we can get HTML with links to the documentation for
+ ``grok``, ``grok.admin`` and so on.
+
+ >>> from grok.admin.utilities import getPathLinksForObject
+ >>> link = getPathLinksForObject(obj)
+ >>> link
+ "<<a href='/docgrok/grok/'>grok</a>... object at ..."
+
+ We got a link to the ``grok`` documentation. Also links to
+ ``grok.admin``, ``grok.admin.objectinfo`` and
+ ``grok.admin.objectinfo.ObjectInfo`` are provided:
+
+ >>> link
+ "<...<a href='/docgrok/grok/admin/'>admin</a>... object at ..."
+
+ If we provide a root_url, we will find it in the links:
+
+ >>> link = getPathLinksForObject(obj, 'http://localhost:8080')
+ >>> link
+ "<<a href='http://localhost:8080/docgrok/grok/'>grok</a>..."
+
+ If no dotted path is included in objects strings representation, a
+ simple string without links is returned:
+
+ >>> getPathLinksForObject(None)
+ "'None'"
+
+ HTML entities should be encoded. We set up a site-manager to get
+ an 'illegal' object representation including regular expression
+ chars ('+') and no dotted path:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> from zope.app.component import site
+ >>> sm = site.LocalSiteManager(root)
+ >>> root.setSiteManager(sm)
+ >>> sm
+ <LocalSiteManager ++etc++site>
+
+ This is a strange object identifier. Anyway:
+
+ >>> getPathLinksForObject(sm)
+ "'<LocalSiteManager ++etc++site>'"
+
+ """
+ r_exp = re.compile("'<(.+)( object at .*)>'")
+
+ raw = `str(obj)`
+ match = r_exp.match(raw)
+ if match is None:
+ return attrEscape(raw)
+
+ result = "<"
+ url = root_url + '/docgrok/'
+ for part in match.group(1).split('.'):
+ url = url + part + '/'
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result[-1] == '.':
+ result = "%s%s>" % (result[:-1], match.group(2))
+ return result
+ return raw
+
+def getPathLinksForClass(klass, root_url=''):
+ """Given a class or classlike object, this function returns HTML
+ code with links to documentation.
+
+ The klass object must provide a string representation like '<class
+ foo.Bar>'. Returned is then a string, where 'foo' and
+ 'Bar' are embedded in HTML links to docgrok documentation for foo
+ and foo.Bar.
+
+ The (optional) ``root_url`` is used to create the links to docgrok
+ documentation. It is expected to be the URL, which can generate
+ docgrok documentation by appending '/docgrok' to the URL.
+
+ We can use class ObjectInfo to check this:
+
+ >>> from grok.admin.objectinfo import ObjectInfo
+ >>> ObjectInfo
+ <class 'grok.admin.objectinfo.ObjectInfo'>
+
+ >>> from grok.admin.utilities import getPathLinksForClass
+ >>> htmlcode = getPathLinksForClass(ObjectInfo)
+ >>> htmlcode
+ "<class '<a href='/docgrok/grok/'>grok</a>...'>"
+
+ When we provide a root_url the link will include it in the
+ href-attribute:
+
+ >>> getPathLinksForClass(ObjectInfo, 'http://localhost')
+ "<class '<a href='http://localhost/docgrok/grok/'>grok</a>...'>"
+
+ If the class does not provide an appropriate string
+ representation, we will get the representation without any links:
+
+ >>> getPathLinksForClass(None, 'http://localhost')
+ "'None'"
+
+ This also works with 'class-like' objects, for instance interfaces
+ and their interface-classes:
+
+ >>> from zope.app.folder import rootFolder
+ >>> from zope.interface import providedBy
+ >>> root = rootFolder()
+ >>> iface = list(providedBy(root))[0]
+ >>> iface
+ <InterfaceClass zope.app.folder.interfaces.IRootFolder>
+
+ >>> getPathLinksForClass(iface)
+ "<InterfaceClass '<a href='/docgrok/zope/'>zope</a>...'>"
+
+ HTML entities should be encoded. We set up a site-manager to get
+ an 'illegal' object representation including regular expression
+ chars ('+') and no dotted path:
+
+ >>> from zope.app.folder import rootFolder
+ >>> root = rootFolder()
+ >>> from zope.app.component import site
+ >>> sm = site.LocalSiteManager(root)
+ >>> root.setSiteManager(sm)
+ >>> sm
+ <LocalSiteManager ++etc++site>
+
+ This is a strange object identifier. Anyway:
+
+ >>> getPathLinksForClass(sm)
+ "<LocalSiteManager '<a href='/docgrok/++etc++site/'>...</a>'>"
+
+ """
+ r_exp = re.compile(".*<(.*) '?(.+)'?(.*)>.*")
+ raw = `str(klass)`
+ match = r_exp.match(raw)
+ if match is None:
+ return attrEscape(raw)
+
+ result = "<%s '" % (match.group(1),)
+ url = root_url + '/docgrok/'
+ for part in match.group(2).split('.'):
+ url = "%s%s/" % (url, part)
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result[-1] == '.':
+ result = "%s'%s>" % (result[:-1], match.group(3))
+ return result
+ return raw
+
+def getPathLinksForDottedName(name, root_url=''):
+ """
+ """
+ if name is None:
+ return ''
+ result = ''
+ url = root_url + '/docgrok/'
+ for part in name.split('.'):
+ url = "%s%s/" % (url, part)
+ result += "<a href='%s'>%s</a>." % (url, part)
+ if len(result) and result.endswith('.'):
+ result = result[:-1]
+ return result
+ return name
+
+def isContainingEvilRegExpChars(strval):
+ """Check whether a string contains evil chars.
+
+ 'Evil' with respect to regular expressions is a string, that
+ contains chars, with a special meaning in regular expressions.
+
+ We indeed must provide a string:
+
+ >>> from grok.admin.utilities import isContainingEvilRegExpChars
+ >>> isContainingEvilRegExpChars(None)
+ Traceback (most recent call last):
+ ...
+ TypeError: expected string or buffer
+
+ >>> isContainingEvilRegExpChars('foo')
+ False
+
+ >>> isContainingEvilRegExpChars('foo++etc++bar')
+ True
+
+ >>> isContainingEvilRegExpChars('foo*bar')
+ True
+
+ """
+ evil_chars = re.compile('.*(\*|\+|\(|\)|\{|\}).*')
+ if evil_chars.match(strval):
+ return True
+ return False
+
+
+def getParentURL(url):
+ """Compute the parent URL for an object described by URL.
+
+ >>> from grok.admin.utilities import getParentURL
+ >>> getParentURL('http://foo:8080/myobj')
+ 'http://foo:8080/'
+
+ >>> getParentURL('http://foo:8080/myfoo/mybar')
+ 'http://foo:8080/myfoo/'
+
+ We want an URL always to end with a slash:
+
+ >>> getParentURL('http://foo:8080')
+ 'http://foo:8080/'
+
+ """
+ url_list = list(urlparse(url))
+ path = url_list[2]
+ if path.endswith('/'):
+ path = path[:-1]
+ path = path.rsplit('/', 1)[0] + '/'
+ url_list[2] = path
+ return urlunparse(url_list)
Modified: grok/trunk/src/grok/admin/view.py
===================================================================
--- grok/trunk/src/grok/admin/view.py 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/admin/view.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1,36 +1,624 @@
+import grok
+import os
+import inspect
+from urllib import urlencode
+
+from grok.admin import docgrok
+from grok.admin.docgrok import DocGrok, DocGrokPackage, DocGrokModule
+from grok.admin.docgrok import DocGrokTextFile, DocGrokGrokApplication
+from grok.admin.docgrok import DocGrokClass, DocGrokInterface, getItemLink
+
+from grok.admin.objectinfo import ZopeObjectInfo
+from grok.admin.utilities import getPathLinksForObject, getPathLinksForClass
+from grok.admin.utilities import getPathLinksForDottedName, getParentURL
+
import zope.component
-import grok.interfaces
+from zope.interface import Interface
+from zope.app import zapi
+from zope.interface.interface import InterfaceClass
+from zope.app.applicationcontrol.interfaces import IServerControl
+from zope.app.applicationcontrol.applicationcontrol import applicationController
+from zope.app.applicationcontrol.runtimeinfo import RuntimeInfo
+from zope.app.applicationcontrol.browser.runtimeinfo import RuntimeInfoView
+from zope.app.apidoc import utilities, codemodule
+from zope.app.apidoc.utilities import getPythonPath, renderText, columnize
+from zope.app.apidoc.codemodule.module import Module
+from zope.app.apidoc.codemodule.class_ import Class
+from zope.app.apidoc.codemodule.function import Function
+from zope.app.apidoc.codemodule.text import TextFile
+from zope.app.apidoc.codemodule.zcml import ZCMLFile
from zope.app.folder.interfaces import IRootFolder
+from zope.app.security.interfaces import ILogout, IAuthentication
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
+from zope.proxy import removeAllProxies
+from zope.tal.taldefs import attrEscape
+import z3c.flashmessage.interfaces
+
+
grok.context(IRootFolder)
grok.define_permission('grok.ManageApplications')
-class Index(grok.View):
- grok.name('index.html') # the root folder isn't a grok.Model
- grok.require('grok.ManageApplications')
- def update(self):
- apps = zope.component.getAllUtilitiesRegisteredFor(
- grok.interfaces.IApplication)
- self.applications = ("%s.%s" % (x.__module__, x.__name__)
- for x in apps)
+class Add(grok.View):
+ """Add an application.
+ """
-class Add(grok.View):
grok.require('grok.ManageApplications')
- def render(self, application, name):
+ def update(self, inspectapp=None, application=None):
+ if inspectapp is not None:
+ self.redirect(self.url("docgrok") + "/%s/index"%(application.replace('.','/'),))
+ return
+
+ def render(self, application, name, inspectapp=None):
+ if name is None or name == "":
+ self.redirect(self.url(self.context))
+ return
app = zope.component.getUtility(grok.interfaces.IApplication,
name=application)
self.context[name] = app()
+ self.flash(u'Added %s `%s`.' % (application, name))
self.redirect(self.url(self.context))
+
class Delete(grok.View):
+ """Delete an application.
+ """
+
grok.require('grok.ManageApplications')
- def render(self, items):
+ def render(self, items=None):
+ if items is None:
+ self.redirect(self.url(self.context))
+ return
if not isinstance(items, list):
items = [items]
for name in items:
del self.context[name]
+ self.flash(u'Application %s was successfully deleted.' % (name,))
self.redirect(self.url(self.context))
+
+class GAIAView(grok.View):
+ """A grok.View with a special application_url.
+
+ We have to compute the application_url different from common
+ grok.Views, because we have no root application object in the
+ adminUI. To avoid mismatch, we also call it 'root_url'.
+
+ """
+
+ def root_url(self, name=None):
+ obj = self.context
+ result = ""
+ while obj is not None:
+ if __grok_context__.providedBy(obj):
+ return self.url(obj, name)
+ obj = obj.__parent__
+ raise ValueError("No application nor root element found.")
+
+ def in_docgrok(self):
+ return '/docgrok/' in self.url() or 'inspect.html' in self.url()
+
+ def is_authenticated(self):
+ """Check, wether we are authenticated.
+ """
+ return not IUnauthenticatedPrincipal.providedBy(self.request.principal)
+
+
+class Macros(GAIAView):
+ """Provides the o-wrap layout."""
+
+ grok.context(Interface)
+
+
+
+class Inspect(GAIAView):
+ """Basic object browser.
+ """
+
+ grok.context(Interface)
+ grok.name(u'inspect.html')
+ grok.require('grok.ManageApplications')
+
+ _metadata = None
+
+ def update(self, show_private=False, *args, **kw):
+ obj = self.context
+ if isinstance(self.context, ZopeObjectInfo):
+ # When the docgrok-object traverser delivers content, then
+ # we get a wrapped context: the meant object is wrapped
+ # into a ZopeObjectInfo.
+ obj = self.context.obj
+
+ self.ob_info = ZopeObjectInfo(obj)
+ ob_info = self.ob_info
+ self.show_private = show_private
+ root_url = self.root_url()
+ parent = ob_info.getParent()
+ parent = {'class_link':
+ parent and getPathLinksForObject(parent) or '',
+ 'obj_link' : getItemLink('',getParentURL(self.url(''))),
+ 'obj' : parent
+ }
+ bases = [getPathLinksForClass(x) for x in ob_info.getBases()]
+ bases.sort()
+
+ ifaces = [getPathLinksForClass(x) for x in
+ ob_info.getProvidedInterfaces()]
+ ifaces.sort()
+
+ methods = [x for x in list(ob_info.getMethods())
+ if self.show_private or not x['name'].startswith('_')]
+ for method in methods:
+ if method['interface']:
+ method['interface'] = getPathLinksForDottedName(
+ method['interface'], root_url)
+ if method['doc']:
+ method['doc'] = renderText(method['doc'], getattr(obj,'__module__', None))
+
+ attrs = [x for x in list(ob_info.getAttributes())
+ if self.show_private or not x['name'].startswith('_')
+ ]
+ for attr in attrs:
+ if '.' in str(attr['type']):
+ attr['type'] = getPathLinksForClass(attr['type'], root_url)
+ else:
+ attr['type'] = attrEscape(str(attr['type']))
+ if attr['interface']:
+ attr['interface'] = getPathLinksForDottedName(
+ attr['interface'], root_url)
+ attr['obj'] = getattr(obj, attr['name'], None)
+ attr['docgrok_link'] = getItemLink(attr['name'], self.url(''))
+ attrs.sort(lambda x,y: x['name']>y['name'])
+
+ seqitems = ob_info.getSequenceItems() or []
+ for item in seqitems:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['obj'] = obj[item['index']]
+ item['docgrok_link'] = getItemLink(item['index'], self.url(''))
+ seqitems.sort()
+
+ mapitems = [x for x in ob_info.getMappingItems()
+ if self.show_private or not x['key'].startswith('_')]
+ for item in mapitems:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['obj'] = obj[item['key']]
+ item['docgrok_link'] = getItemLink(item['key'], self.url(''))
+ mapitems.sort(lambda x,y: x['key']>y['key'])
+
+ annotations = [x for x in ob_info.getAnnotationsInfo()
+ if self.show_private or not x['key'].startswith('_')]
+ for item in annotations:
+ if '.' in str(item['value_type']):
+ item['value_type'] = getPathLinksForClass(item['value_type'],
+ root_url)
+ else:
+ item['value_type'] = attrEscape(str(item['value_type']))
+ item['docgrok_link'] = getItemLink(item['key'], self.url(''))
+ annotations.sort(lambda x,y: x['key']>y['key'])
+
+
+ self.info = {
+ 'name' : ob_info.getId() or u'<unnamed object>',
+ 'type' : getPathLinksForClass((getattr(obj,
+ '__class__',
+ None)
+ or type(obj)), root_url),
+ 'obj_link' : getPathLinksForObject(obj, root_url),
+ 'moduleinfo' : ob_info.getmoduleinfo(),
+ 'modulename' : ob_info.getmodulename(),
+ 'ismodule' : ob_info.ismodule(),
+ 'isclass' : ob_info.isclass(),
+ 'ismethod' : ob_info.ismethod(),
+ 'isfunction' : ob_info.isfunction(),
+ 'iscode' : ob_info.iscode(),
+ 'isbuiltin' : ob_info.isbuiltin(),
+ 'isroutine' : ob_info.isroutine(),
+ 'issequence' : ob_info.isSequence(),
+ 'ismapping' : ob_info.isMapping(),
+ 'isannotatable' : ob_info.isAnnotatable(),
+ 'doc' : renderText(ob_info.getdoc(),None),
+ 'comments' : ob_info.getcomments(),
+ 'module' : ob_info.getmodule(),
+ 'sourcefile' : ob_info.getsourcefile(),
+ 'source' : ob_info.getsource(),
+ 'parent' : parent,
+ 'dotted_path' : ob_info.getPythonPath(),
+ 'provided_interfaces' : ob_info.getDirectlyProvidedInterfaces(),
+ 'interfaces' : ifaces,
+ 'bases' : bases,
+ 'attributes' : attrs,
+ 'methods' : methods,
+ 'sequenceitems' : seqitems,
+ 'mappingitems' : mapitems,
+ 'annotations' : annotations
+ }
+
+
+class Index(GAIAView):
+ """A redirector to the real frontpage."""
+
+ grok.name('index.html') # The root folder is not a grok.Model
+ grok.require('grok.ManageApplications')
+
+ def update(self):
+ apps = zope.component.getAllUtilitiesRegisteredFor(
+ grok.interfaces.IApplication)
+ self.applications = ("%s.%s" % (x.__module__, x.__name__)
+ for x in apps)
+ # Go to the first page immediately.
+ self.redirect(self.url('applications'))
+
+
+class LoginForm(GAIAView):
+ """A login screen for session based authentication.
+
+ To activate loginForm, i.e. session based authentication, an
+ appropriate PluggableAuthenticationUtility (PAU) must be set up in
+ the applications root folder (which happens here to be the global
+ root folder). The setup is done for the admin app in __init__.py.
+ """
+ # 'loginForm.html' is the page template name, that standard
+ # session based authentication looks for. The form must provide an
+ # input field 'login' for the username and another input field
+ # 'password'.
+ grok.name('loginForm.html')
+
+ def update(self, login=None, password=None, camefrom=None):
+ request = self.request
+ if (not IUnauthenticatedPrincipal.providedBy(request.principal)):
+ camefrom = request.get('camefrom', '.')
+ self.redirect(camefrom)
+ return
+
+class Logout(GAIAView):
+ """Log out screen."""
+
+ grok.name('logout')
+
+ def update(self):
+ auth = zope.component.getUtility(IAuthentication)
+ logout = ILogout(auth)
+ logout.logout(self.request)
+ pass
+
+
+
+class Applications(GAIAView):
+ """View for application management."""
+
+ grok.name('applications')
+ grok.require('grok.ManageApplications')
+
+ def getDocOfApp(self, apppath, headonly = True):
+ doctor = docgrok.handle(apppath)
+ result = doctor.getDoc(headonly)
+ if result is None:
+ result = ""
+ return result
+
+ def update(self):
+ apps = zope.component.getAllUtilitiesRegisteredFor(
+ grok.interfaces.IApplication)
+ inst_apps = [x for x in self.context.values()
+ if hasattr(x, '__class__') and x.__class__ in apps]
+ self.applications = (
+ {'name': "%s.%s" % (x.__module__, x.__name__),
+ 'docurl':("%s.%s" % (x.__module__, x.__name__)).replace('.', '/')}
+ for x in apps)
+ self.installed_applications = inst_apps
+
+
+class AdminMessageSource(grok.GlobalUtility):
+
+ grok.name('admin')
+ zope.interface.implements(z3c.flashmessage.interfaces.IMessageSource)
+
+ message = None
+
+ def send(self, message, type='admin'):
+ self.message = z3c.flashmessage.message.PersistentMessage(message,
+ type)
+
+ def list(self, type=None):
+ if self.message is None:
+ return
+ if type is None or self.message.type == type:
+ yield self.message
+
+ def delete(self, message):
+ if message is self.message:
+ self.message = None
+ else:
+ raise KeyError(message)
+
+
+class Server(GAIAView):
+ """Zope3 management screen."""
+
+ grok.require('grok.ManageApplications')
+
+ @property
+ def server_control(self):
+ return zapi.getUtility(IServerControl)
+
+ @property
+ def runtime_info(self):
+ riv = RuntimeInfoView()
+ riv.context = applicationController
+ return riv.runtimeInfo()
+
+ @property
+ def current_message(self):
+ source = zope.component.getUtility(
+ z3c.flashmessage.interfaces.IMessageSource, name='admin')
+ messages = list(source.list())
+ if messages:
+ return messages[0]
+
+ def update(self, time=None, restart=None, shutdown=None,
+ admin_message=None, submitted=False):
+ if not submitted:
+ return
+ # Admin message control
+ source = zope.component.getUtility(
+ z3c.flashmessage.interfaces.IMessageSource, name='admin')
+ if admin_message is not None:
+ source.send(admin_message)
+ elif source.current_message:
+ source.delete(source.current_message)
+
+ # Restart control
+ if time is not None:
+ try:
+ time = int(time)
+ except:
+ time = 0
+ else:
+ time = 0
+
+ if restart is not None:
+ self.server_control.restart(time)
+ elif shutdown is not None:
+ self.server_control.shutdown(time)
+
+ self.redirect(self.url())
+
+
+
+def getDottedPathDict(dotted_path):
+ """Get a dict containing parts of a dotted path as links.
+ """
+ if dotted_path is None:
+ return {}
+
+ result = []
+ part_path = ""
+ for part in dotted_path.split('.'):
+ name = part
+ if part_path != "":
+ name = "." + part
+ part_path += part
+ result.append({
+ 'name':name,
+ 'url':"/docgrok/%s" % (part_path,)
+ })
+ part_path += "/"
+ return result
+
+
+class DocGrokView(GAIAView):
+ """A base DocGrok view.
+
+ This view is used for all things not covered by other, more
+ specialized views.
+ """
+
+ grok.context(DocGrok)
+ grok.name('index')
+ grok.require('grok.ManageApplications')
+
+ def getDoc(self, text=None, heading_only=False):
+ """Get the doc string of the module STX formatted."""
+ if text is None:
+ return None
+ if (hasattr(self.context, "apidoc") and
+ hasattr(self.context.apidoc, "getDocString")):
+ text = self.context.apidoc.getDocString()
+ else:
+ return None
+ lines = text.strip().split('\n')
+ if len(lines) and heading_only:
+ # Find first empty line to separate heading from trailing text.
+ headlines = []
+ for line in lines:
+ if line.strip() == "":
+ break
+ headlines.append(line)
+ lines = headlines
+ # Get rid of possible CVS id.
+ lines = [line for line in lines if not line.startswith('$Id')]
+ return renderText('\n'.join(lines), self.context.getPath())
+
+ def getDocHeading(self, text=None):
+ return self.getDoc(text, True)
+
+ def getPathParts(self, path=None):
+ """Get parts of a dotted name as url and name parts.
+ """
+ if path is None:
+ path = self.context.path
+ if path is None:
+ return None
+ return getDottedPathDict(path)
+ result = []
+ part_path = ""
+ for part in path.split('.'):
+ name = part
+ if part_path != "":
+ name = "." + part
+ part_path += part
+ result.append({
+ 'name':name,
+ 'url':"/docgrok/%s" % (part_path,)
+ })
+ part_path += "/"
+ return result
+
+ def getEntries(self, columns=True):
+ """Return info objects for all modules and classes in the
+ associated apidoc container.
+
+ """
+ if (not hasattr(self.context, "apidoc") or
+ not hasattr(self.context.apidoc, "items")):
+ return None
+ entries = []
+ for name, obj in self.context.apidoc.items():
+ entry = {
+ 'name': name,
+ 'obj' : obj,
+ 'path': getPythonPath(removeAllProxies(obj)),
+ 'url' : u'',
+ 'doc' : None,
+ 'ispackage' : False,
+ 'ismodule' : False,
+ 'isinterface' : False,
+ 'isclass' : False,
+ 'isfunction' : False,
+ 'istextfile' : False,
+ 'iszcmlfile' : False,
+ 'signature' : None
+ }
+ entry['url'] = "%s/%s" % (self.context.path.replace('.','/'), name)
+ if hasattr(obj,"getDocString"):
+ entry['doc'] = self.getDocHeading(obj.getDocString())
+ elif hasattr(obj, "getDoc") and isinstance(
+ removeAllProxies(obj), InterfaceClass):
+ entry['doc'] = self.getDocHeading(obj.getDoc())
+ if isinstance(obj, Class):
+ entry['isclass'] = True
+ elif isinstance(obj, TextFile):
+ entry['istextfile'] = True
+ elif isinstance(obj, ZCMLFile):
+ entry['iszcmlfile'] = True
+ elif isinstance(obj,Function):
+ entry['isfunction'] = True
+ if hasattr(obj, 'getSignature'):
+ entry['signature'] = obj.getSignature()
+ elif (isinstance(obj,Module) and
+ os.path.basename(obj.getFileName()) in
+ ['__init.py__', '__init__.pyc', '__init__.pyo']):
+ entry['ispackage'] = True
+ elif isinstance(obj,Module):
+ entry['ismodule'] = True
+ entries.append(entry)
+
+ entries.sort(lambda x, y: cmp(x['name'], y['name']))
+ return entries
+
+ def update(self):
+ self.docgrok_root = self.context._traversal_root
+ self.app_root = self.docgrok_root.__parent__
+ pass
+
+
+class DocGrokPackageView(DocGrokView):
+ """A view for packages handled by DocGrok."""
+
+ grok.context(DocGrokPackage)
+ grok.name('index')
+
+
+class DocGrokModuleView(DocGrokView):
+ """A view for modules handled by DocGrok."""
+
+ grok.context(DocGrokModule)
+ grok.name('index')
+
+
+class DocGrokClassView(DocGrokView):
+ """A view for classes handled by DocGrok."""
+
+ grok.context(DocGrokClass)
+ grok.name('index')
+
+ def getBases(self):
+ return self._listClasses(self.context.apidoc.getBases())
+
+ def getInterfaces(self):
+ return self._listClasses(
+ [iface for iface in self.context.apidoc.getInterfaces()])
+
+ def getAttributes(self):
+ attrs = self.context.getAttributes()
+ for a in attrs:
+ a['interface'] = self._listClasses([a['interface']])
+ return attrs
+
+ def getMethods(self):
+ methods = self.context.getMethods()
+ for m in methods:
+ m['doc'] = renderText(m['attr'].__doc__ or '',
+ inspect.getmodule(m['attr']))
+ m['interface'] = self._listClasses([m['interface']])
+ return methods
+
+ def _listClasses(self, classes):
+ info = []
+ for cls in classes:
+ unwrapped_cls = removeAllProxies(cls)
+ fullpath = getPythonPath(unwrapped_cls)
+ if not fullpath:
+ continue
+ path, name = fullpath.rsplit('.', 1)
+ info.append({
+ 'path': path or None,
+ 'path_parts' : self.getPathParts(path) or None,
+ 'name': name,
+ 'url': fullpath and fullpath.replace('.','/') or None,
+ 'doc': self.getDocHeading(cls.__doc__) or None
+ })
+ return info
+
+
+class DocGrokInterfaceView(DocGrokClassView):
+
+ grok.context(DocGrokInterface)
+ grok.name('index')
+
+
+class DocGrokGrokApplicationView(DocGrokClassView):
+
+ grok.context(DocGrokGrokApplication)
+ grok.name('index')
+
+
+class DocGrokTextFileView(DocGrokView):
+
+ grok.context(DocGrokTextFile)
+ grok.name('index')
+
+ def getContent(self):
+ lines = self.context.getContent()
+ if self.context.path.endswith('.stx'):
+ format = 'zope.source.stx'
+ else:
+ format = 'zope.source.rest'
+ return renderText(lines, format=format)
+
+ def getPackagePathParts(self):
+ return self.getPathParts(
+ self.context.getPackagePath())
+
+
Copied: grok/trunk/src/grok/admin/view_templates/applications.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/applications.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/applications.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/applications.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,66 @@
+<html metal:use-macro="context/@@macros/gaia-page">
+ <div metal:fill-slot="content">
+
+ <form tal:define="apps context/values"
+ tal:attributes="action string:${context/@@absolute_url}/delete"
+ tal:condition="apps|nothing">
+ <fieldset
+ tal:condition="python: len(view.installed_applications)">
+ <legend>Installed applications</legend>
+ <div tal:repeat="app view/installed_applications">
+ <input type="checkbox"
+ class="checkbox"
+ tal:attributes="value app/__name__;
+ name string:items" />
+ <a tal:attributes="href string:${context/@@absolute_url}/${app/__name__}">
+ <span tal:replace="app/__name__"/>
+ (<span tal:replace="app/__class__/__name__"/>)
+ </a>
+
+ [<a href=""
+ tal:attributes="href string:${context/@@absolute_url}/${app/__name__}/@@inspect.html"
+ >object browser</a>]
+ </div>
+
+ <p>
+ <input class="button" type="submit" value="Delete Selected"/></p>
+ </fieldset>
+ </form>
+
+ <fieldset
+ tal:condition="not: python: len(view.installed_applications)">
+ <legend>Installed applications</legend>
+ <p class="menu-description1">Currently no applications are installed.</p>
+ </fieldset>
+
+ <fieldset>
+ <legend>Add application</legend>
+
+ <div class="menu-box1"
+ tal:repeat="app view/applications">
+ <form action="" tal:attributes="action string:${context/@@absolute_url}/add;
+ name python: app['name'].split('.')[-1]">
+ <div class="menu-box2">
+ <div class="menu-head1"><a href=""
+ tal:attributes="href string:${context/@@absolute_url}/docgrok/${app/docurl}"
+ tal:content="app/name">Application Name</a></div>
+ <div class="menu-description1">
+ <span tal:replace="structure python: view.getDocOfApp(app['name']) or ''">
+ Application description here.
+ </span>
+ </div>
+ <div class="menu-box3">
+ <label>Name your new app: <input type="text" name="name"/></label>
+ <input type="hidden" name="application" value=""
+ tal:attributes="value app/name" />
+ <input class="button" type="submit" name="Add" value="Create"/>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ </fieldset>
+
+ </div>
+
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokclassview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,284 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <h1>
+ Class
+ <span class="docgrok-pathvalue">
+ <span class="docgrok-elemname1">
+ <span tal:replace="context/name">ClassName</span>
+ </span>
+ </span> in <span class="docgrok-pathvalue">
+ <span tal:repeat="part python: view.getPathParts()[:-1]"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span></span> (Python Class)
+ </h1>
+ <div class="docgrok-entry"
+ tal:content="structure python: view.getDoc(context.getDocString())">
+ Documentation string.
+ </div>
+ <div>
+ <h2>Paths</h2>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Python path:</span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/path">path.in.python</span>
+ <div class="docgrok-annotation2">
+ Use <span class="docgrok-pycode1">from <span tal:replace="context/module_path">path</span>
+ import <span tal:replace="context/name">path</span></span>
+ to use the functionality of this module in your application or component.
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Absolute file path: </span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/getFilePath">/absolute/file/path</span>
+ <div class="docgrok-annotation2">
+ This is the file, wherein this class is defined.
+ </div>
+ </div>
+
+ </div>
+ <div>
+
+ <h2>Base Classes</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getBases">
+ class
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part item/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="item/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ <h2>Interfaces</h2>
+
+ <div class="docgrok-annotation1">
+ This class implements the following interfaces:
+ </div>
+
+ <div class="docgrok-entry" tal:repeat="item view/getInterfaces">
+ interface
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part item/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="item/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+
+ <h2>Attributes:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getAttributes">
+ <div class="docgrok-description2">
+ <span class="docgrok-description1">
+ <span class="docgrok-pathvalue">
+ <span tal:content="item/name">attributename</span>
+ </span>
+ </span>
+ (type: <span class="docgrok-description2" tal:content="item/type">type</span>)
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">value:</span>
+ <span class="docgrok-pathvalue" tal:content="item/value">value</span>
+ </div>
+
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+
+
+ <h2>Functions:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isfunction">
+ <div class="docgrok-pathvalue">
+ function
+ <a href=""
+ tal:attributes="href
+ string:${view/root_url}/docgrok/${item/url}" >
+ <span tal:content="item/name">function_name</span><span tal:content="item/signature">(signature)</span>
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this function available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Methods:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getMethods">
+ <div class="docgrok-description1">
+ <div class="docgrok-pathvalue">
+ <span tal:content="item/name">methodname</span><span tal:content="item/signature">(signature)</span>
+ </div>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ Doc
+ </div>
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+
+
+ </div>
+
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokgrokapplicationview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,282 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <h1>
+ Class
+ <span class="docgrok-pathvalue">
+ <span class="docgrok-elemname1">
+ <span tal:replace="context/name">ClassName</span>
+ </span>
+ </span> in <span class="docgrok-pathvalue">
+ <span tal:repeat="part python: view.getPathParts()[:-1]"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span></span> (a Grok Application)
+ </h1>
+ <div class="docgrok-entry"
+ tal:content="structure python: view.getDoc(context.getDocString())">
+ Documentation string.
+ </div>
+ <div>
+ <h2>Paths</h2>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Python path:</span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/path">path.in.python</span>
+ <div class="docgrok-annotation2">
+ Use <span class="docgrok-pycode1">from <span tal:replace="context/module_path">path</span>
+ import <span tal:replace="context/name">path</span></span>
+ to use the functionality of this module in your application or component.
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Absolute file path: </span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/getFilePath">/absolute/file/path</span>
+ <div class="docgrok-annotation2">
+ This is the file, wherein this class is defined.
+ </div>
+ </div>
+
+ </div>
+ <div>
+
+ <h2>Base Classes</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getBases">
+ class
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part item/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="item/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ <h2>Interfaces</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getInterfaces">
+ interface
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part item/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="item/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+
+
+
+ <h2>Attributes:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getAttributes">
+ <div class="docgrok-description2">
+ <span class="docgrok-description1">
+ <span class="docgrok-pathvalue">
+ <span tal:content="item/name">attributename</span>
+ </span>
+ </span>
+ (type: <span class="docgrok-description2" tal:content="item/type">type</span>)
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">value:</span>
+ <span class="docgrok-pathvalue" tal:content="item/value">value</span>
+ </div>
+
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+
+
+
+ <h2>Functions:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isfunction">
+ <div class="docgrok-pathvalue">
+ function
+ <a href=""
+ tal:attributes="href
+ string:${view/root_url}/docgrok/${item/url}" >
+ <span tal:content="item/name">function_name</span><span tal:content="item/signature">(signature)</span>
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this function available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Methods:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getMethods">
+ <div class="docgrok-description1">
+ <div class="docgrok-pathvalue">
+ <span tal:content="item/name">methodname</span><span tal:content="item/signature">(signature)</span>
+ </div>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ Doc
+ </div>
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+ </div>
+
+
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokinterfaceview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,246 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <h1>
+ Interface
+ <span class="docgrok-pathvalue">
+ <span class="docgrok-elemname1">
+ <span tal:replace="context/name">ClassName</span>
+ </span>
+ </span> in <span class="docgrok-pathvalue">
+ <span tal:repeat="part python: view.getPathParts()[:-1]"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span></span> (Python Class)
+ </h1>
+ <div class="docgrok-entry"
+ tal:content="structure python: view.getDoc(context.getDocString())">
+ Documentation string.
+ </div>
+ <div>
+ <h2>Paths</h2>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Python path:</span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/path">path.in.python</span>
+ <div class="docgrok-annotation2">
+ Use <span class="docgrok-pycode1">from <span tal:replace="context/module_path">path</span>
+ import <span tal:replace="context/name">path</span></span>
+ to use the functionality of this module in your application or component.
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Absolute file path: </span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/getFilePath">/absolute/file/path</span>
+ <div class="docgrok-annotation2">
+ This is the file, wherein this class is defined.
+ </div>
+ </div>
+
+ </div>
+ <div>
+
+ <h2>Base Interfaces</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getBases">
+ class
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part item/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="item/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+
+ <h2>Attributes:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getAttributes">
+ <div class="docgrok-description2">
+ <span class="docgrok-description1">
+ <span class="docgrok-pathvalue">
+ <span tal:content="item/name">attributename</span>
+ </span>
+ </span>
+ (type: <span class="docgrok-description2" tal:content="item/type">type</span>)
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">value:</span>
+ <span class="docgrok-pathvalue" tal:content="item/value">value</span>
+ </div>
+
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+
+
+
+ <h2>Functions:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isfunction">
+ <div class="docgrok-pathvalue">
+ function
+ <a href=""
+ tal:attributes="href
+ string:${view/root_url}/docgrok/${item/url}" >
+ <span tal:content="item/name">function_name</span><span tal:content="item/signature">(signature)</span>
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this function available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Methods:</h2>
+ <div class="docgrok-entry" tal:repeat="item view/getMethods">
+ <div class="docgrok-description1">
+ <div class="docgrok-pathvalue">
+ <span tal:content="item/name">methodname</span><span tal:content="item/signature">(signature)</span>
+ </div>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ Doc
+ </div>
+ <div class="docgrok-annotation2">
+ <div class="docgrok-entry" tal:repeat="iface item/interface">
+ <span class="docgrok-description2">interface:</span>
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${iface/url}"
+ tal:content="iface/name">
+ ClassName
+ </a>
+ </span>
+ in
+ <span class="docgrok-pathvalue">
+ <a href=""
+ tal:repeat="part iface/path_parts"
+ tal:attributes="href string:${view/root_url}${part/url}">
+
+ <span tal:replace="part/name" />
+ </a>
+ </span>
+ <div class="docgrok-annotation2"
+ tal:condition="iface/doc"
+ tal:content="structure iface/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: iface/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="iface/path">x</span> import <span
+ tal:replace="iface/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+
+ </div>
+
+
+ <div class="docgrok-annotation2">
+ <span class="docgrok-description2">permissions:</span>
+ <div class="docgrok-annotation2">
+ read:
+ <span tal:content="item/read_perm">None</span>
+ </div>
+ <div class="docgrok-annotation2">
+ write:
+ <span tal:content="item/write_perm">None</span>
+ </div>
+ </div>
+
+
+ </div>
+
+ </div>
+
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokmoduleview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,129 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <h1>
+ <span class="docgrok-pathvalue">
+ <span tal:repeat="part view/getPathParts"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span></span> (Python Module)
+ </h1>
+ <div class="docgrok-entry"
+ tal:content="structure python: view.getDoc(context.getDocString())">
+ Documentation string.
+ </div>
+ <div>
+ <h2>Paths</h2>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Python path:</span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/path">path.in.python</span>
+ <div class="docgrok-annotation2">
+ Use <span class="docgrok-pycode1">import
+ <span tal:replace="context/path">path</span></span>
+ to use the functionality of this module in your application or component.
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Absolute file path: </span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/getFilePath">/absolute/file/path</span>
+ <div class="docgrok-annotation2">
+ This is the file, where this module is located in file system.
+ </div>
+ </div>
+
+ </div>
+ <div>
+
+ <h2>Functions:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isfunction">
+ <div class="docgrok-pathvalue">
+ function
+ <a href=""
+ tal:attributes="href
+ string:${view/root_url}/docgrok/${item/url}" >
+ <span tal:content="item/name">function_name</span><span tal:content="item/signature">(signature)</span>
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Interfaces:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isinterface">
+ <div class="docgrok-pathvalue">
+ interface
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ InterfaceName
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make this
+ interface definition available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Classes:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/isclass">
+ <div class="docgrok-pathvalue">
+ class
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="item/name">
+ ClassName
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ Use <span class="docgrok-pycode1">from <span
+ tal:replace="context/path">x</span> import <span
+ tal:replace="item/name">y</span></span> to make the
+ functionality of this class available in your application
+ or component.
+ </div>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,120 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrokPackage page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <h1>
+ <span class="docgrok-pathvalue">
+ <span tal:repeat="part view/getPathParts"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span>
+ </span>
+ (Python Package)
+ </h1>
+ <div>
+
+ <h2>Paths</h2>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Python path:</span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/path">path.in.python</span>
+ <div class="docgrok-annotation2">
+ Use <span class="docgrok-pycode1">import
+ <span tal:replace="context/path">path</span></span>
+ to use the functionality of this package in your application or component.
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">Absolute file path: </span>
+ <span class="docgrok-pathvalue"
+ tal:content="context/getFilePath">absolute/file/path</span>
+ <div class="docgrok-annotation2">
+ This is, where this package is located in file system.
+ </div>
+ </div>
+
+ <h2>Textfiles:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/istextfile">
+ <div class="docgrok-pathvalue">
+
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="string: ${item/name}">
+ moduleName
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ </div>
+ </div>
+ </div>
+
+
+ <h2>Subpackages:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/ispackage">
+ <div class="docgrok-pathvalue">
+ package
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="string: ${context/path}.${item/name}">
+ moduleName
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ You can use <span class="docgrok-pycode1">import <span
+ tal:replace="string: ${context/path}.${item/name}">a.b</span>
+ </span>
+ to make the elements of this package available in your
+ application or component.
+ </div>
+ </div>
+ </div>
+
+ <h2>Modules:</h2>
+
+ <div class="docgrok-entry" tal:repeat="item view/getEntries">
+ <div tal:condition="item/ismodule">
+ <div class="docgrok-pathvalue">
+ module
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/${item/url}"
+ tal:content="string: ${context/path}.${item/name}">
+ moduleName
+ </a>
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="item/doc"
+ tal:content="structure item/doc">
+ </div>
+ <div class="docgrok-annotation2"
+ tal:condition="not: item/doc">
+ You can use <span class="docgrok-pycode1">import <span
+ tal:replace="string: ${context/path}.${item/name}">a.b</span>
+ </span>
+ to make the elements of this module available in your
+ application or component.
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgroktextfileview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,40 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+
+ <div class="docgrok-sourceheader">
+ <h1>
+ <span class="docgrok-pathvalue"
+ tal:content="view/context/filename">
+ filename.txt
+ </span>
+ (Text file in
+ <span class="docgrok-pathvalue">
+ <span tal:repeat="part view/getPackagePathParts"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span>
+ </span>)
+ </h1>
+ <div class="docgrok-entry">
+ <span class="docgrok-description1">File path:</span>
+
+ <span class="docgrok-pathvalue"
+ tal:content="view/context/getFilePath"
+ >
+ /home/uli/blah...
+ </span>
+ <div class="docgrok-annotation1">
+ This is, where this text file can be found.
+ </div>
+ </div>
+ </div>
+ <div class="docgrok-sourcetext"
+ tal:content="structure view/getContent">
+ </div>
+ </div>
+ <div metal:fill-slot="footer"></div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/docgrokview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/docgrokview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,129 @@
+<html metal:use-macro="view/app_root/@@macros/gaia-page">
+ <head>
+ <title>DocGrok page title</title>
+ </head>
+ <body>
+ <div metal:fill-slot="content">
+ <div tal:condition="not:view/getPathParts">
+ <h1 >
+ Welcome to DocGrok...
+ </h1>
+ <div class="emph">
+ DocGrok is Grok's run-time documentation system.
+
+ </div>
+
+ <h2>
+ DocGrok Package Browser
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ Use the package browser to browse the locally installed
+ Python packages, their classes, members and included text
+ documentation. You can, for example,...
+ </p>
+ <div class="docgrok-annotation1">
+ <div>
+ <a href=""
+ class="emph"
+ tal:attributes="href string:${view/root_url}/docgrok/zope">
+ browse the zope package</a>
+ </div>
+ <div>
+ <a href=""
+ class="emph"
+ tal:attributes="href string:${view/root_url}/docgrok/grok">
+ browse the grok package</a>
+ </div>
+ </div>
+ <p>
+ See
+ <a href=""
+ class="emph"
+ tal:attributes="href string:
+ ${view/root_url}/docgrok/grok/admin/docgrok.txt">
+ docgrok documentation</a> to learn more
+ about this feature of Grok.
+ </p>
+ </div>
+
+ <h2>
+ DocGrok Object Browser
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ The DocGrok object browser supports discovering of objects
+ available in the runtime system. You can for example
+ examine the
+ </p>
+ <div class="docgrok-annotation1">
+ <div>
+ <a href=""
+ class="emph"
+ tal:attributes="href string:${view/root_url}/@@inspect.html">
+ ZODB root folder</a>
+ </div>
+ </div>
+ <p>
+ See <a href="" class="emph"
+ tal:attributes="href string:
+ ${view/root_url}/docgrok/grok/admin/inspect.txt">object
+ browsers documentation</a> to learn more about this
+ feature of Grok.
+ </p>
+ </div>
+
+ <h2>
+ External Documentation
+ </h2>
+
+ <div class="docgrok-annotation1">
+ <p>
+ Grok has the privilege to be supported by a very vivid
+ community, which is contributing also documentation and
+ help. To get you started with Grok, it is highly
+ recommended first to do the <a class="emph"
+ href="http://grok.zope.org/tutorial.html">Grok
+ Tutorial</a>. Afterwards you might find the special <a
+ class="emph"
+ href="http://grok.zope.org/minitutorials/index.html">Grok
+ HOWTOs</a> of value for your work.
+ </p>
+ <p>
+ If you need some more personal advice or want to get
+ involved into Grok core development, have a look at the <a
+ class="emph"
+ href="http://mail.zope.org/mailman/listinfo/grok-dev">Grok-dev
+ Mailinglist</a>. The Grok's headquarter is <a class="emph"
+ href="http://grok.zope.org/">Grok's Home Page</a>.
+ </p>
+ GROK SAY: Have fun!
+ </div>
+ </div>
+
+ <div tal:condition="view/getPathParts">
+ <h1>
+
+ DocGrok Documentation for
+ <span class="docgrok-pathvalue">
+ <span tal:repeat="part view/getPathParts"><a href=""
+ tal:attributes="href string:${view/root_url}${part/url}"
+ tal:content="part/name">part</a></span>
+ </span>
+ </h1>
+ <div class="Content">
+ <h2>Path</h2>
+ <div tal:content="context/path">
+ path.to.some.element
+ </div>
+ <div class="description1">
+ The python path of this element.
+ </div>
+ </div>
+ </div>
+ </div>
+ <div metal:fill-slot="footer">asda</div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/gaiaview.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/gaiaview.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/gaiaview.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/gaiaview.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1 @@
+<!-- Just a placeholder -->
Modified: grok/trunk/src/grok/admin/view_templates/index.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/index.pt 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/admin/view_templates/index.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1,50 +1,14 @@
-<html>
- <head>
- <title>grok administration interface</title>
- </head>
-
- <body tal:define="apps context/values">
- <h1>Grok Applications</h1>
-
- <form tal:attributes="action string:${context/@@absolute_url}/delete"
- tal:condition="apps|nothing">
- <fieldset>
- <legend>Installed applications</legend>
-
- <ul>
- <li tal:repeat="app apps">
- <input type="checkbox" tal:attributes="value app/__name__;
- name string:items" />
- <a tal:attributes="href string:${context/@@absolute_url}/${app/__name__}">
- <span tal:replace="app/__name__"/>
- (<span tal:replace="app/__class__/__name__"/>)
- </a>
- </li>
- </ul>
-
- <p><input type="submit" value="Delete Selected"/></p>
- </fieldset>
- </form>
- <form tal:attributes="action string:${context/@@absolute_url}/add">
- <fieldset>
- <legend>Add application</legend>
-
- <p>
- <label>Application:
- <select height="1" name="application">
- <option tal:repeat="app view/applications"
- tal:attributes="value app"
- tal:content="app"
- />
- </select>
- </label>
- </p>
-
- <p><label>Name: <input type="text" name="name"/></label></p>
-
- <p><input type="submit" value="Add"/></p>
-
- </fieldset>
- </form>
- </body>
+<html metal:use-macro="context/@@macros/gaia-page">
+ <div metal:fill-slot="content">
+ <h1></h1>
+ <div class="logo">
+ <a href="context/@@appsindex"
+ tal:attributes="href string:${context/@@absolute_url}/applications">
+ <img alt="Image of Grok relaxing..."
+ src="grok-relax.jpg"
+ tal:attributes="src view/static/grok-relax.png" />
+ </a>
+ </div>
+ </div>
+ <div metal:fill-slot="footer">asda</div>
</html>
Copied: grok/trunk/src/grok/admin/view_templates/inspect.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/inspect.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/inspect.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/inspect.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,216 @@
+<html metal:use-macro="context/@@macros/gaia-page">
+ <div metal:fill-slot="content">
+ <h1>
+ <span tal:content="view/info/name" />
+ <span tal:content="structure view/info/obj_link" />
+ </h1>
+
+ <div class="docgrok-entry">
+ <div class="emph"
+ tal:content="structure view/info/doc" />
+ </div>
+
+ <div>
+ <form method="post" action="">
+ <p>
+ <input type="checkbox" name="show_private"
+ id="show_private"
+ tal:attributes="checked view/show_private"/>
+ <label for="show_private">Show private attributes</label>
+ <input type="submit" value="update" />
+ </p>
+ </form>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Parent:
+ </h2>
+ <div class="docgrok-annotation1">
+ <div tal:define="parent view/info/parent/obj"
+ tal:condition="parent"
+ i18n:translate="">
+ <a href=""
+ i18n:name="parent"
+ tal:attributes="href view/info/parent/obj_link">
+ <span tal:replace="parent/zope:name"
+ tal:condition="parent/zope:name" />
+ <span tal:condition="not: parent/zope:name"
+ i18n:translate=""><unnamed object></span></a>
+ <span tal:content="structure view/info/parent/class_link" />
+ </div>
+
+ </div>
+
+ <div class="docgrok-entry"
+ tal:condition="not: view/info/parent/obj">
+ <div class="docgrok-annotation1">
+ No parent object
+ </div>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Base classes:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="klass view/info/bases">
+ <span tal:content="structure klass">Class</span>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Interfaces provided:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="iface view/info/interfaces">
+ <span tal:content="structure iface">Interface</span>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Attributes and Properties:
+ </h2>
+ <h3 class="docgrok-description">
+ Attributes
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="attr view/info/attributes">
+ <span class="emph">
+ <span tal:content="attr/name">Name</span>
+ </span>
+
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: attr['type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="attr/value_linkable">
+ <a href=""
+ tal:attributes="href attr/docgrok_link"
+ tal:content="attr/value">Value</a>
+ </span>
+ <span tal:condition="not: attr/value_linkable"
+ tal:content="attr/value">Value</span>
+ </div>
+ <div tal:condition="attr/interface">
+ interface: <span tal:content="structure attr/interface">
+ Interface
+ </span>
+ </div>
+ </div>
+ </div>
+
+
+ <h3 class="docgrok-description">
+ Mappings
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/mappingitems">
+ <span class="emph"
+ tal:content="item/key">
+ mapping-key
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: item['value_type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">Value</a>
+ </span>
+ <span tal:condition="not:item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ </div>
+
+ <h3 class="docgrok-description">
+ Sequences
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/sequenceitems">
+ <span class="emph"
+ tal:content="item/index">
+ sequence-index
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure python: item['value_type']">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">Value</a>
+ </span>
+ <span tal:condition="not:item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ </div>
+
+ <h3 class="docgrok-description">
+ Annotations
+ </h3>
+ <div class="docgrok-annotation1"
+ tal:repeat="item view/info/annotations">
+ <span class="emph"
+ tal:content="item/key">
+ annotation-key
+ </span>
+ <div class="docgrok-annotation1">
+ <div>
+ type: <span tal:content="structure item/value_type">
+ Type</span>
+ </div>
+ <div>
+ value:
+ <span tal:condition="item/value_linkable">
+ <a href=""
+ tal:attributes="href item/docgrok_link"
+ tal:content="item/value">value</a>
+ </span>
+ <span tal:condition="not: item/value_linkable"
+ tal:content="item/value" />
+ </div>
+ </div>
+ <p></p>
+ </div>
+ </div>
+
+ <div class="docgrok-entry">
+ <h2 class="docgrok-description">
+ Methods:
+ </h2>
+ <div class="docgrok-annotation1"
+ tal:repeat="method view/info/methods">
+ <span tal:content="method/name"
+ class="emph"
+ >method</span><span tal:content="method/signature"
+ class="emph"
+ >method</span>
+ <div tal:condition="method/doc"
+ tal:content="structure method/doc">Method documentation</div>
+ <div class="docgrok-annotation1"
+ tal:condition="method/interface">
+ interface: <span tal:content="structure method/interface">interface</span>
+ </div>
+ <p></p>
+ </div>
+ </div>
+
+ </div>
+ <div metal:fill-slot="footer">asda</div>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/loginform.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/loginform.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/loginform.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/loginform.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,35 @@
+<html metal:use-macro="context/@@macros/gaia-page">
+ <head>
+ <title metal:fill-slot="title">Grok Login</title>
+ </head>
+ <body>
+ <div metal:fill-slot="menu-links" />
+ <div metal:fill-slot="content">
+ <h1>Welcome to Grok</h1>
+ <form method="post">
+ <fieldset>
+ <legend>Login</legend>
+
+ <table>
+ <tr>
+ <td><label for="login">Username:</label></td>
+ <td><input id="login" type="text" name="login" /></td>
+ </tr>
+ <tr>
+ <td><label for="password">Password:</label></td>
+ <td><input id="password" type="password" name="password" /></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><input type="submit" value="Login"/></td>
+ </tr>
+ </table>
+ <p>
+ Note: To proceed you must have cookies enabled in your browser
+ </p>
+
+ </fieldset>
+ </form>
+ </div>
+ </body>
+</html>
\ No newline at end of file
Copied: grok/trunk/src/grok/admin/view_templates/logout.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/logout.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/logout.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/logout.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Logged out</title>
+</head>
+<body>
+ You have been logged out.
+</body>
+</html>
\ No newline at end of file
Copied: grok/trunk/src/grok/admin/view_templates/macros.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/macros.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/macros.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,104 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ i18n:domain="zope"
+ metal:define-macro="gaia-page">
+ <head>
+ <title
+ metal:define-slot="title"
+ >grok administration interface</title>
+ <link metal:define-slot="header"
+ rel="stylesheet" type="text/css" href="static/grok.css"
+ tal:attributes="href view/static/grok.css" />
+ <metal:block define-slot="extrahead">
+ </metal:block>
+ </head>
+
+ <body>
+ <div>
+ <div id="banner">
+ <a href="/" id="logo">
+ <img alt="Grok" src="images/grok-admin.jpg" height="40"
+ tal:attributes="src view/static/grok-admin.jpg" />
+ </a>
+ </div>
+
+ <div id="logout" metal:define-macro="logged_user">
+ <span tal:condition="view/is_authenticated">
+ <span i18n:translate="">User:
+ <span tal:replace="request/principal/title"
+ i18n:name="user_title">User</span>
+ </span>
+ [<a href=""
+ tal:attributes="href string:${view/root_url}/logout">log out</a>]
+ </span>
+ </div>
+
+ <div id="breadcrumbs">
+ <div id="banner-shadow">
+
+ </div>
+ </div>
+ <div id="fireplace">
+ <img alt="grok_relax_image" src="images/grok-relax5.gif"
+ tal:attributes="src view/static/grok-relax5.gif" />
+ </div>
+ <div id="menu-links"
+ metal:define-slot="menu-links"
+ tal:define="currview python:view.url()">
+ <span class="menu-link-inactive"
+ tal:define="target string:${view/root_url}/applications">
+ <a href="applications"
+ tal:condition="python: target != currview"
+ tal:attributes="href target"
+ >Applications</a>
+ <span class="emph"
+ tal:condition="python: target == currview">
+ Applications
+ </span>
+ </span>
+
+ <span class="menu-link-inactive"
+ tal:define="target string:${view/root_url}/server"
+ >
+ <a href="z3index"
+ tal:condition="python: target != currview"
+ tal:attributes="href target"
+ >Server Control</a>
+ <span class="emph"
+ tal:condition="python: target == currview">
+ Server Control
+ </span>
+ </span>
+<!--
+
+ <a href=""
+ tal:attributes="href view/root_url">Debug</a>
+-->
+
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/">
+ <span tal:attributes="class python:view.in_docgrok() and 'emph'"
+ >Documentation</span>
+ </a>
+ </div>
+
+ <div id="content">
+
+ <div id="messages" tal:content="structure context/@@messages" />
+
+ <div metal:define-slot="content">
+
+ <h1>Welcome to Grok!</h1>
+
+ <div>
+ Your friendly and easy way to Zope 3.
+ </div>
+
+ </div>
+ <div>
+ <p id="footer-copyright">© Copyright 2007, The Zope Foundation<br />Design inspired by Sebastian Ware</p>
+ </div>
+ </div>
+
+ </div>
+ </body>
+</html>
Copied: grok/trunk/src/grok/admin/view_templates/server.pt (from rev 78829, Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/server.pt)
===================================================================
--- grok/trunk/src/grok/admin/view_templates/server.pt (rev 0)
+++ grok/trunk/src/grok/admin/view_templates/server.pt 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,66 @@
+<html metal:use-macro="context/@@macros/gaia-page">
+ <div metal:fill-slot="content">
+ <h1>Manage your Zope 3 instance</h1>
+
+ <form method="post" action=""
+ tal:attributes="action string:${context/@@absolute_url}/server">
+ <fieldset>
+ <p>
+ <legend>Manage server process</legend>
+ <input type="submit" name="restart" class="button" value="Restart Zope 3" />
+ <input type="submit" name="shutdown" class="button" value="Stop Zope 3" />
+ after <input type="text" name="time" value="0" size="4" /> seconds
+ </p>
+ </fieldset>
+
+ <fieldset>
+ <legend>Admin message</legend>
+
+ <p>Let's you display an administrative message on all pages.<br />
+
+ <input
+ type="text"
+ name="admin_message"
+ tal:attributes="value view/current_message/message|nothing"
+ />
+
+ <input type="hidden" name="submitted" value="true"/>
+ <input type="submit" name="save_message" class="button" value="Save"/>
+
+ </fieldset>
+
+ <span class="header">Server process info</span>
+ <div id="server-processes">
+ <dl tal:define="ri view/runtime_info">
+ <dt class="emph">Uptime:</dt>
+ <dd tal:content="ri/Uptime">unknown</dd>
+ <dt class="emph">System platform:</dt>
+ <dd tal:content="ri/SystemPlatform">unknown</dd>
+ <dt class="emph">Zope version:</dt>
+ <dd tal:content="ri/ZopeVersion">unknown</dd>
+ <dt class="emph">Python version:</dt>
+ <dd tal:content="ri/PythonVersion">unknown</dd>
+ <dt class="emph">Command line:</dt>
+ <dd tal:content="ri/CommandLine">unknown</dd>
+ <dt class="emph">Preferred encoding:</dt>
+ <dd tal:content="ri/PreferredEncoding">unknown</dd>
+ <dt class="emph">File system encoding:</dt>
+ <dd tal:content="ri/FileSystemEncoding">unknown</dd>
+ <dt class="emph">Process ID:</dt>
+ <dd tal:content="ri/ProcessId">unknown</dd>
+ <dt class="emph">Python path:</dt>
+ <dd>
+ <ul>
+ <tal:block tal:repeat="path ri/PythonPath">
+ <li tal:content="path">unknown</li>
+ </tal:block>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+
+ </form>
+
+ </div>
+
+</html>
Modified: grok/trunk/src/grok/configure.zcml
===================================================================
--- grok/trunk/src/grok/configure.zcml 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/configure.zcml 2007-08-15 02:53:48 UTC (rev 78831)
@@ -22,6 +22,8 @@
<include package="zope.app.intid" />
<include package="zope.app.keyreference" />
<include package="zope.app.catalog" />
+ <include package="zope.app.renderer" />
+ <include package="zope.app.session" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
Deleted: grok/trunk/src/grok/ftests/admin/admin.py
===================================================================
--- grok/trunk/src/grok/ftests/admin/admin.py 2007-08-15 01:47:54 UTC (rev 78830)
+++ grok/trunk/src/grok/ftests/admin/admin.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -1,59 +0,0 @@
-"""
- >>> import grok
- >>> grok.grok('grok.ftests.admin.admin')
-
- >>> from zope.testbrowser.testing import Browser
- >>> browser = Browser()
- >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
- >>> browser.handleErrors = False
- >>> browser.open("http://localhost/")
- >>> print browser.contents
- <html>
- ...
- ...<legend>Add application</legend>
- ...
- >>> browser.getControl('Application').displayValue = ['grok.ftests.admin.admin.MammothManager']
- >>> browser.getControl('Name').value = 'my-mammoth-manager'
- >>> browser.getControl('Add').click()
- >>> print browser.contents
- <html>
- ...
- <li>
- <input type="checkbox" name="items" value="my-mammoth-manager" />
- <a href="http://localhost/my-mammoth-manager">
- my-mammoth-manager
- (MammothManager)
- </a>
- </li>
- ...
- >>> browser.getLink('my-mammoth-manager').click()
- >>> print browser.contents
- Let's manage some mammoths!
-
-We are able to delete installed applications.
-
- >>> browser.open("http://localhost/")
- >>> print browser.contents
- <html>
- ...
- ...<legend>Installed applications</legend>
- ...
- >>> ctrl = browser.getControl(name='items')
- >>> ctrl.getControl(value='my-mammoth-manager').selected = True
- >>> browser.getControl('Delete Selected').click()
- >>> print browser.contents
- <html>
- ...
- ...<legend>Add application</legend>
- ...
-
-"""
-import grok
-
-class MammothManager(grok.Application, grok.Container):
- pass
-
-class Index(grok.View):
-
- def render(self):
- return u"Let's manage some mammoths!"
Added: grok/trunk/src/grok/ftests/admin/apps.py
===================================================================
--- grok/trunk/src/grok/ftests/admin/apps.py (rev 0)
+++ grok/trunk/src/grok/ftests/admin/apps.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,96 @@
+"""
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.admin.apps')
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+We fetch the standard page, which should provide us a menu to get all
+installable grok applications/components.
+
+ >>> browser.open("http://localhost/")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <legend>Add application</legend>
+ ...
+
+The opening screen should inform us, that there are no applications
+installed yet:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <p ...>Currently no applications are installed.</p>
+ ...
+
+We are able to add a mammoth manager...
+
+ >>> browser.getControl('Name your new app:',index=13).value = 'my-mammoth-manager'
+ >>> browser.getControl('Create',index=13).click()
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <legend>Installed applications</legend>
+ ... <input type="checkbox" class="checkbox" name="items"
+ value="my-mammoth-manager" />
+ <a href="http://localhost/my-mammoth-manager">
+ my-mammoth-manager
+ (MammothManager)
+ </a>
+ ... <legend>Add application</legend>
+ ...
+
+Launch the added mammoth manager
+
+ >>> mylink = browser.getLink('my-mammoth-manager (MammothManager)').click()
+ >>> print browser.contents
+ Let's manage some mammoths!
+
+ >>> print browser.url
+ http://localhost/my-mammoth-manager
+
+We can go to the object browser for every installed application:
+
+ >>> browser.open("http://localhost/applications")
+ >>> browser.getLink('object browser').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<span ...>...<a href=...>MammothManager</a> object at ...></span>
+ ...
+
+We are able to delete installed mammoth-managers
+
+ >>> browser.open("http://localhost/applications")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <legend>Installed applications</legend>
+ ...
+ >>> ctrl = browser.getControl(name='items')
+ >>> ctrl.getControl(value='my-mammoth-manager').selected = True
+ >>> browser.getControl('Delete Selected').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <p ...>Currently no applications are installed.</p>
+ ...
+ ...<legend>Add application</legend>
+ ...
+
+"""
+
+import grok
+
+class MammothManager(grok.Application, grok.Container):
+ """A mammoth manager"""
+ pass
+
+class Index(grok.View):#
+
+ def render(self):
+ return u"Let's manage some mammoths!"
Added: grok/trunk/src/grok/ftests/admin/loginlogout.py
===================================================================
--- grok/trunk/src/grok/ftests/admin/loginlogout.py (rev 0)
+++ grok/trunk/src/grok/ftests/admin/loginlogout.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,92 @@
+"""
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.admin.loginlogout')
+
+First setup the pluggable authentication system for session based
+authentication. This is normaly invoked by an event
+handler. Unfortunately the event handler seems not to be called, if
+the ftesting setup is set up. We therefore set up the PAU manually.
+
+ >>> root = getRootFolder()
+ >>> root is not None
+ True
+
+ >>> import grok.admin
+ >>> principal_credentials = grok.admin.getPrincipalCredentialsFromZCML()
+ >>> principal_credentials
+ [{u'login': u'mgr', u'password': u'mgrpw', u'id': u'zope.mgr', u'title': u'Manager'}]
+
+ >>> grok.admin.setupSessionAuthentication(root_folder = root, principal_credentials = principal_credentials)
+
+We should get a login page if trying to get something unauthenticated.
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.handleErrors = True
+ >>> browser.open("http://localhost/")
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+
+Now try to log in using *wrong* credentials
+
+ >>> browser.getControl(name='login').value = 'dumbtry'
+ >>> browser.getControl('Login').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+
+Okay, we got the login screen again. What about the correct credentials?
+
+ >>> browser.getControl(name='login').value = 'mgr'
+ >>> browser.getControl(name='password').value = 'mgrpw'
+ >>> browser.getControl('Login').click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>grok administration interface</title>
+ ...
+
+The new screen should contain a link for logging out:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <span>User:
+ ...Manager
+ ...[<a href="http://localhost/logout">log out</a>]
+ ...
+
+Fine. Now we are authorized and can do, whatever we want. Let's log out:
+
+ >>> outlink = browser.getLink('log out')
+ >>> outlink
+ <Link text='log out' url='http://localhost/logout'>
+
+ >>> outlink.click()
+ >>> print browser.contents
+ <html>
+ ... You have been logged out.
+ ...
+
+Looks okay. But are we really logged out? Let's try to fetch a page:
+
+ >>> browser.open("http://localhost/")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ... <title>Grok Login</title>
+ ...
+ ... <td><input id="login" type="text" name="login" /></td>
+ ...
+
+Yes, we are.
+
+ ...
+ ... <legend>Add application</legend>
+ ...
+
+
+"""
+
Added: grok/trunk/src/grok/ftests/admin/objectbrowser.py
===================================================================
--- grok/trunk/src/grok/ftests/admin/objectbrowser.py (rev 0)
+++ grok/trunk/src/grok/ftests/admin/objectbrowser.py 2007-08-15 02:53:48 UTC (rev 78831)
@@ -0,0 +1,137 @@
+"""
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.admin.objectbrowser')
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+We fetch the documentation page, which should give us a tiny overview
+over documentation:
+
+ >>> browser.open("http://localhost/docgrok")
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... Welcome to DocGrok...
+ ...
+
+On the documentation page there should be a link to the ZODB root
+folder:
+
+ >>> root_link = browser.getLink('ZODB root folder')
+ >>> root_link
+ <Link text='ZODB root folder' url='http://localhost/@@inspect.html'>
+
+The root folder got no name:
+
+ >>> root_link.click()
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <span><unnamed object></span>
+ ...
+
+and is of type Folder.
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ... <span ...>...<a ...>Folder</a> object at ...></span>
+ ...
+
+It's class documentation should be linked in the head of page:
+
+ >>> browser.getLink('Folder').url
+ 'http://localhost/docgrok/zope/app/folder/folder/Folder/'
+
+We also get the docstring of the root folder:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<p>The standard Zope Folder implementation.</p>
+ ...
+
+A checkbox gives us control over private members and attributes of the
+object displayed:
+
+ >>> checkbox = browser.getControl('Show private attributes')
+ >>> checkbox
+ <ItemControl name='show_private' type='checkbox' optionValue='on' selected=False>
+
+By default the checkbox is not selected. Therefore we check for an
+arbitrary private method to be displayed or not. For example the
+``__dict__`` method. By default no __dict__ method will be displayed:
+
+ >>> '__dict__' in browser.contents
+ False
+
+Now let's tick the checkbox and update the view:
+
+ >>> checkbox.selected = True
+ >>> checkbox.selected
+ True
+
+ >>> browser.getControl('update').click()
+
+Now the private method should be displayed:
+
+ >>> '__dict__' in browser.contents
+ True
+
+Here we go :-)
+
+Okay, now let's examine the displayed data a bit. We are currently the
+object browser's view for the root folder. The root folder got no
+parent, which should be displayed:
+
+ >>> 'No parent object' in browser.contents
+ True
+
+One of the base classes of the root folder is the class
+``persistent.Persistent``. We not only want that displayed but also a
+link to the class documentation of that class:
+
+ >>> link = browser.getLink("Persistent'")
+ >>> link.url
+ 'http://localhost/docgrok/persistent/Persistent'
+
+The same for interfaces. The root folder implements
+``persistent.interfaces.IPersistent``:w
+
+ >>> link = browser.getLink('IPersistent')
+ >>> link.url
+ 'http://localhost/docgrok/persistent/interfaces/IPersistent/'
+
+Now the attributes and properties. The root folder got an attribute
+``data``:
+
+ >>> print browser.contents
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ ...
+ ...<h3 ...>
+ ...Attributes
+ ...</h3>
+ ... <span>data</span>
+ ... <div>
+ ... value:
+ ... <a href="http://localhost/docgrok-obj/data/@@inspect.html"><BTrees.OOBTree.OOBTree object at ...></a>
+ ... </div>
+ ...
+
+
+
+"""
+
+import grok
+
+class MammothManager(grok.Application, grok.Container):
+ """A mammoth manager"""
+ pass
+
+class Index(grok.View):#
+
+ def render(self):
+ return u"Let's manage some mammoths!"
More information about the Checkins
mailing list