[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
+      "&lt;<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
+      "&lt;...<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
+      "&lt;<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)
+      "'&lt;LocalSiteManager ++etc++site&gt;'"
+      
+    """
+    r_exp = re.compile("'<(.+)( object at .*)>'")
+    
+    raw = `str(obj)`
+    match = r_exp.match(raw)
+    if match is None:
+        return attrEscape(raw)
+
+    result = "&lt;"
+    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&gt;" % (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
+      "&lt;class '<a href='/docgrok/grok/'>grok</a>...'&gt;"
+
+    When we provide a root_url the link will include it in the
+    href-attribute:
+
+      >>> getPathLinksForClass(ObjectInfo, 'http://localhost')
+      "&lt;class '<a href='http://localhost/docgrok/grok/'>grok</a>...'&gt;"
+
+    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)
+      "&lt;InterfaceClass '<a href='/docgrok/zope/'>zope</a>...'&gt;"
+
+    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)
+      "&lt;LocalSiteManager '<a href='/docgrok/++etc++site/'>...</a>'&gt;"
+
+    """
+    r_exp = re.compile(".*<(.*) '?(.+)'?(.*)>.*")
+    raw = `str(klass)`
+    match = r_exp.match(raw)
+    if match is None:
+        return attrEscape(raw)
+
+    result = "&lt;%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&gt;" % (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>
+	  &nbsp;&nbsp;
+	  [<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="">&lt;unnamed object&gt;</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>&nbsp;
+	  [<a href=""
+	  tal:attributes="href string:${view/root_url}/logout">log out</a>]
+	</span>
+      </div>
+
+      <div id="breadcrumbs">
+        <div id="banner-shadow">
+          &nbsp;
+        </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>
+        &nbsp;&nbsp;
+        <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>
+<!--
+        &nbsp;&nbsp;
+        <a href=""
+          tal:attributes="href view/root_url">Debug</a>
+-->
+        &nbsp;&nbsp;
+        <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">&copy; 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>&lt;unnamed object&gt;</span>
+  ...
+
+and is of type Folder.
+
+  >>> print browser.contents
+  <html xmlns="http://www.w3.org/1999/xhtml">
+  ...
+  ... <span ...>...<a ...>Folder</a> object at ...&gt;</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">&lt;BTrees.OOBTree.OOBTree object at ...&gt;</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