[Checkins] SVN: Sandbox/ulif/grok-adminui/ Merged admin branch
against trunk.
Christian Theune
ct at gocept.com
Thu Jul 12 10:07:50 EDT 2007
Log message for revision 77742:
Merged admin branch against trunk.
Changed:
A Sandbox/ulif/grok-adminui/doc/groktut/building_forms_with_formlib/
A Sandbox/ulif/grok-adminui/doc/macros.txt
U Sandbox/ulif/grok-adminui/doc/tutorial.txt
U Sandbox/ulif/grok-adminui/src/grok/admin/README.txt
A Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py
A Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.txt
A Sandbox/ulif/grok-adminui/src/grok/admin/static/
U Sandbox/ulif/grok-adminui/src/grok/admin/view.py
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/appsindex.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokclassview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokgrokapplicationview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokinterfaceview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokmoduleview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgroktextfileview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/gaiaview.pt
U Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/index.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt
A Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/z3index.pt
U Sandbox/ulif/grok-adminui/src/grok/ftests/admin/admin.py
-=-
Copied: Sandbox/ulif/grok-adminui/doc/groktut/building_forms_with_formlib (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/doc/groktut/building_forms_with_formlib)
Copied: Sandbox/ulif/grok-adminui/doc/macros.txt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/doc/macros.txt)
===================================================================
--- Sandbox/ulif/grok-adminui/doc/macros.txt (rev 0)
+++ Sandbox/ulif/grok-adminui/doc/macros.txt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,247 @@
+============================
+Mini-Howto: Macros with Grok
+============================
+
+Intended Audience:
+
+ * Python Developers
+
+ * Zope 2 Developers
+
+ * Zope 3 Developers
+
+
+
+Introduction
+------------
+
+Macros are a way to define a chunk of presentation in one template,
+and share it in others. Changes to the macro are immediately reflected
+in all of the places, that share it.
+
+Such, a developer can easily write some sort of 'page-skeleton' (the
+macro), which can be reused in other pages to take care, for instance,
+for a certain and consistent look-and-feel of a whole site with less
+maintenance.
+
+Technically, macros are made available by ``METAL``, a Macro Expansion
+for TAL, which ships with nearly every Zope installation. They can be
+used in Zope Page Templates. See the `TAL`_ and `METAL`_ pages for
+details.
+
+.. _`TAL`: http://wiki.zope.org/ZPT/TAL
+
+.. _`METAL`: http://wiki.zope.org/ZPT/METAL
+
+
+
+Defining a simple macro
+-----------------------
+
+In Grok macros are defined in usual views, where the associated page
+templates contain `metal`-statements to define the desired
+macros. Macros generally are attributes of the page template wherein they
+are defined, but to get them rendered, we usually use views.
+
+We define a view ``MyMacros`` the usual way in ``app.py``::
+
+ import grok
+
+ class Sample(grok.Application, grok.Container):
+ pass
+
+ class Index(grok.View):
+ pass # see app_templates/index.pt
+
+ class MyMacros(grok.View):
+ """The macro holder"""
+ grok.context(Sample) # The default model this view is bound to.
+
+In the associated page template ``app_templates/mymacros.pt`` we
+define the macros we like to have available. You define macros with
+the ``METAL`` attribute::
+
+ metal:define-macro="<macro-name>"
+
+and the slots therein with::
+
+ metal:define-slots=<slot-name>
+
+Let's define a very plain page macro::
+
+ <html metal:define-macro="mypage">
+ <head></head>
+ <body>
+ The content:
+ <div metal:define-slot="mycontent">
+ Put your content here...
+ </div>
+ </body>
+ </html>
+
+Here we defined a single macro ``mypage`` with only one slot
+``mycontent``.
+
+If we restart our Zope instance (don't forget to put some ``index.pt``
+into ``app_templates/``) and have created our application as ``test``,
+we now can go to the following URL::
+
+ http://localhost:8080/test/mymacros
+
+and see the output::
+
+ The content:
+ Put your content here...
+
+Allright.
+
+
+Referencing a simple macro
+--------------------------
+
+In ``index.pt`` we now want to *use* the macro defined in
+``mymacros.pt``. Using a macro means to let it render a part of
+another page template, especially, filling the slots defined in
+``mymacros.pt`` with content defined in ``index.pt``. You call macros
+with the ``METAL`` attribute::
+
+ metal:use-macro="<macro-location>"
+
+Our ``app_templates/index.pt`` can be that simple::
+
+ <html metal:use-macro="context/@@mymacros/mypage">
+ </html>
+
+Watching::
+
+ http://localhost:8080/test/index
+
+should now give the same result as above, although we didn't call
+``mymacros`` in browser, but ``index``. That's what macros are made
+for.
+
+When we fill the slots, we get different content rendered within
+the same macro. You can fill slots using
+
+ metal:fill-slot="<slot-name>"
+
+where the slot-name must be defined in the macro. Now, change
+``indext.pt`` to::
+
+ <html metal:use-macro="context/@@mymacros/mypage">
+ <body>
+ <div metal:fill-slot="body">
+ My content from index.pt
+ </div>
+ </body>
+ </html>
+
+and you will get the output::
+
+ The content:
+ My content from index.pt
+
+The pattern of the macro reference (the <macro-location>) used here
+is::
+
+ context/<view-name>/<macro-name>
+
+whereas ``context`` references the object being viewed, which in our
+case is the ``Sample`` application. In plain English we want Zope to
+look for a view for a ``Sample`` application object (``test``) which
+is called ``mymacros`` and contains a macro called ``mypage``.
+
+The logic behind this is, that views are always registered for certain
+object types. Registering a view with some kind of object (using
+``grok.context()`` for example), means, that we promise, that this
+view can render objects of that type. (It is up to you to make sure,
+that the view can handle rendering of that object type).
+
+It is not a bad idea to register views for interfaces (instead of
+implementing classes), because it means, that a view will remain
+usable, while an implementation of an interface can change. [FIXME: Is
+this a lie?] This is done in the section `Defining 'all-purpose'
+macros`_ below.
+
+
+Background: how ``grok.View`` and macros interact
+-------------------------------------------------
+
+In case of ``grok.View`` views are in fact ``BrowserPage`` objects
+with an attribute ``template``, which is the associated page
+template. The associated page template in turn has got an attribute
+``macros``, which is a dictionary containing all the macros defined in
+the page template with their names as keys (or ``None``).
+
+This means, that you can also reference a macro of a ``grok.View``
+using::
+
+ context/<view-name>/template/macros/<macro-name>
+
+Grok shortens this path for you by mapping the ``macros`` dictionary
+keys to the associated ``grok.View``. If you define a macro
+``mymacro`` in a template associated with a ``grok.View`` called
+``myview``, this view will map ``mymacro`` as an own attribute, so
+that you can ask for the attribute ``mymacro`` of the *view*, wheras
+it is in fact an attribute of the associated template.
+
+Such, you can write in short for the above pattern::
+
+ context/<view-name>/<macro-name>
+
+View names always start with the 'eyes' (``@@``) which is a shortcut
+for ``++view++<view-name>``.
+
+
+Defining 'all-purpose' macros
+------------------------------
+
+To define an 'all-purpose' macro, i.e. a macro, that can render
+objects of (nearly) any type and thus be accessed from any
+other page template, just set a very general context for your macro
+view::
+
+ from zope.interface import Interface
+ import grok
+
+ class Master(grok.View):
+ grok.context(Interface)
+
+and reference the macros of the associated pagetemplate like this::
+
+ context/@@master/<macro-name>
+
+Because the macros in ``Master`` now are 'bound' (in fact their view
+is bound) to ``Interface`` and every Grok application, model or
+container implements some interface, the ``Master`` macros will be
+accessible from nearly every other context. ``Master`` promises to be
+a view for every object, which implements ``Interface``.
+
+
+Accessing Zope3 standard macros
+-------------------------------
+
+The standard macros of Zope 3, which render also the default ZMI
+pages, are accessible under the view-name ``standard_macros`` and usually
+provide slots ``title``, ``headers`` and ``body``. It is
+good style to provide this slots with your homegrown views as well.
+
+To give your pages standard Zope3 look, you can do something like
+this in your page template::
+
+ <html metal:use-macro="context/@@standard_macros/page">
+ <head>
+ <title metal:fill-slot="title">
+ Document Title
+ </title>
+ <metal:headers fill-slot="headers">
+ <!-- Additional headers here... -->
+ </metal:headers>
+ </head>
+ <body>
+ <div metal:fill-slot="body">
+ Your content here...
+ </div>
+ </body>
+ </html>
+
Modified: Sandbox/ulif/grok-adminui/doc/tutorial.txt
===================================================================
--- Sandbox/ulif/grok-adminui/doc/tutorial.txt 2007-07-12 13:55:41 UTC (rev 77741)
+++ Sandbox/ulif/grok-adminui/doc/tutorial.txt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -78,14 +78,15 @@
.. _`ez_setup.py`: http://peak.telecommunity.com/dist/ez_setup.py
- This will make ``easy_install`` available to you.
+ This will make ``easy_install-2.4`` available to you.
**Note**: Sometimes you have ``easy_install`` installed but you need
a newer version of the underlying setuptools infrastructure to make
Grok work. You can automatically upgrade setuptools this by doing::
- $ sudo easy_install -U setuptools
+ $ sudo easy_install-2.4 -U setuptools
+
Setting up grok on a Unix-like (Linux, Mac OS X) environment is
easy.
@@ -116,6 +117,7 @@
$ grokproject Sample
+
This tells grokproject to create a new subdirectory called ``Sample``
and set up the project in there. grokproject will automatically
download and install Zope 3 and Grok into the project area.
@@ -123,6 +125,39 @@
.. XXX when grokproject gains a switch for pointing to a shared egg
directory, mention this here.
+.. sidebar:: Problems running ``grokproject``
+
+ During the run of ``grokproject`` many things can go wrong. Problems
+ that happen easily:
+
+ * No subversion installed
+
+ If you don't have a Zope 3 installation already installed, you need
+ ``Subversion`` installed on the computer, where you want to run
+ ``grokproject``, because the sources of Zope 3 are fetched from a
+ subversion repository during install. Visit the `Subversion site`_
+ to get an appropriate ``Subversion`` client.
+
+ .. _`Subversion site`: http://subversion.tigris.org/
+
+ * Missing ``mkzopeinstance``
+
+ The install of Grok might fail, complaining about a missing file
+ ``mkzopeinstance``. If this happens, it is likely, that you have a
+ configuration file ``.pydistutils.cfg`` in your home directory
+ containing a directive for where to install binaries. Disable this
+ directive putting a comment char (``#``) at the beginning of the
+ line which contains ``install_scripts``. Especially users of MacOS
+ X might encounter this problem. This can also happen, if you use
+ an already installed Zope 3.
+
+ For background information concerning this issue, have a look at
+ the `EasyInstall Custom Installation Locations`_ on the
+ EasyInstall site.
+
+ .. _`EasyInstall Custom Installation Locations`: http://peak.telecommunity.com/DevCenter/EasyInstall#mac-os-x-user-installation
+
+
grokproject will tell you what it will be creating::
Selected and implied templates:
@@ -1301,7 +1336,37 @@
From the perspective of Python, you can think of containers as
dictionaries. They allow item access (``container['key']``) to get at
its contents. They also define methods such as ``keys()`` and
-``values()``. Containers do a lot more than Python dictionaries
+``values()``.
+
+.. sidebar:: Containers are not dictionaries
+
+ Although containers mainly behave like dictionaries, they
+ are in fact ``BTrees``, balanced trees, which makes them
+ (beside other advantages) capable of maintaining really
+ large amounts of objects in an efficient manner. The
+ difference to ordinary dictionaries counts, where you
+ want to do something like this::
+
+ for key in mycontainer.keys():
+ do_something(mycontainer[key])
+
+ The thing returned by the call to ``keys()`` is *not* a
+ list (as it is, when you call a dictionarys ``keys()``
+ method), but an iterable. On each loop the mycontainers
+ ``next()`` method is called to get the next key. So, if
+ you, for example, change the number of elements stored in
+ the container while calling ``do_something()``, you
+ easily run into trouble. Especially calling
+ ``del(mycontainer[key])`` will not do, what you might
+ expect. Instead do the following::
+
+ for key in list(mycontainer.keys()):
+ del(mycontainer[key])
+
+ Such, using the ``list()`` function, you get a list of
+ keys instead of an iterable.
+
+Containers do a lot more than Python dictionaries
though: they are persistent, and when you modify them, you don't have
to use `_p_changed` anywhere to notice you changed them. They also
send out special events that you can listen to when items are placed
@@ -1373,3 +1438,95 @@
instances of ``Entry``, but also other containers. If you made those
modifications, you would be on your way to building your own content
management system with Grok.
+
+
+Building forms with formlib
+===========================
+
+Up to now, we created forms using basically plain HTML and
+implementing every apect of 'logic' using Python code. This is fine
+for some cases but often you would like to have a more automatic way
+to create forms. There is still a lot of repetition in the code. For
+instance we tell several times what values we would like to
+edit/display and what kind of data we would like to have stored
+therein. Furthermore, when the model we use changes slightly, we have
+to maintain a lot of associated logic. Every piece of validation code
+has to be reviewed and eventually rewritten. Last not least you cannot
+easily reuse your existing validation for other models, where you have
+a similar kind of data.
+
+It would be nice, if we could just tell what kind of data we
+have and get appropriate forms without too much hassle. This is
+possible with grok and it is easy.
+
+.. sidebar:: `zope.formlib`: the name of the magic behind forms.
+
+
+ Grok does nearly nothing to provide the magic of forms as discussed
+ here. Instead it makes use of a package quite popular in Zope 3, the
+ `zope.formlib` package.
+
+ You can learn more about ``formlib`` in the documentation, which
+ comes with the package itself:
+
+ http://svn.zope.org/zope.formlib/trunk/src/zope/formlib/form.txt
+
+
+In Grok the creation of forms is supported by special form objects:
+``grok.EditForm``, ``grok.DisplayForm`` and ``grok.AddForm``. These
+are in fact ``grok.Views`` as well, but offer in addition a special
+machinery, which helps you to generate forms (in extreme) without even
+a single line of HTML code.
+
+
+Let's create a simple Form using ``grok.EditForm``. For that purpose
+we start with the application as coded above in section `Storing
+data`_ but we turn the ``Edit`` class in ``app.py``, which is a
+``grok.View`` into a ``grok.EditForm`` and import a ``schema`` called
+``TextLine`` at the beginning:
+
+.. include:: groktut/building_forms_with_formlib/src/sample/app.py
+ :literal:
+
+When you view the edit form of this application going to
+
+ http://localhost:8080/test/edit
+
+you will see an input field labeled `Text to store:` and a button
+entitled `Store`. This is nothing new. Ugh? What happened? The reason
+is, that there is still a template ``edit.pt`` associated with our new
+form, which renders the form the good old fashioned way and makes no
+use at all from our new class. You *can* specify page templates to
+render the output of forms, just as you can do with ordinary
+``grok.View``, but you don't have to. Now remove the page template
+``edit.pt``.
+
+ $ rm app_templates/edit.pt
+
+and restart your Zope instance.
+
+When you look again at
+
+ http://localhost:8080/test/edit
+
+you should be able to notice some changes: There will be an asterisk
+(`*`) in front of the field label, indicating that this field is a
+required one. The text of the submit button should have turned to
+`Apply`. This is the look of a form that was completely
+autogenerated. Note, that we didn't write one line of HTML to get a
+complete form. And it works.
+
+If you leave the text in the field untouched and click the `Apply`
+button, you will get a message on top, telling you that there were no
+changes.
+
+If you change the text and apply a new value, the form will display a
+timestamp instead, telling that the data was
+updated. Congratulations. You created your first *real* form with
+Grok. Looking back to the first forms created above, they appear
+nowmere as some conglomeration of Python code and HTML wich just
+accidently happens to be a HTML form.
+
+Defining field types using schemas
+----------------------------------
+
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/README.txt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/README.txt 2007-07-12 13:55:41 UTC (rev 77741)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/README.txt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -1,6 +1,9 @@
A basic grok admin UI
-====================
+=====================
+The internal name of the admin UI is:
+Grok Application Interface Application or, for short gaia.
+
Overview
--------
@@ -11,3 +14,29 @@
* "Delete application" form: checkboxes displayed with listed installed
applications. Selected items may be deleted.
+
+
+To Do
+-----
+
+* Better application handling
+
+ - Configure apps.
+
+* Maintenance tools
+
+ - Start/stop/restart Zope3.
+
+* Debugging tools
+
+ - Show error logs.
+
+* Introspection tool
+
+ - Give information concerning installed apps, their containers
+ and contained objects.
+
+* Nicer layout
+
+* AJAXification using some framework (MojiKit or KSS most probably)
+
Copied: Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/docgrok.py)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.py 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,560 @@
+"""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.
+"""
+
+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.proxy import removeAllProxies
+
+import os
+import sys # for sys.path
+import types
+import grok
+import inspect
+import grok.interfaces
+from grok.interfaces import IApplication
+from martian.scan import is_package, ModuleInfo
+from martian import InstanceGrokker, ModuleGrokker
+
+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
+
+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
+ if not IApplication.implementedBy( ob ):
+ 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.
+ # XXX 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)
+
+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).
+
+ XXX 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[name] = obj
+ 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.
+ """
+ msg = "I am Dr. Grok. How can I help you?"
+ path = None
+ _traversal_root = None
+ #_children = {}
+
+ def __init__(self, dotted_path ):
+ #super( DocGrok, self ).__init__()
+ self.path = dotted_path
+
+ def getPath(self):
+ return self.path
+
+ def getMsg(self):
+ return self.msg
+
+ 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 = getDocGrokForDottedPath( newpath )
+ 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.msg = "Do more grokking!"
+ doctor.__parent__ = self
+ doctor.__name__ = patient
+ doctor._traversal_root = self._traversal_root
+ doctor.path = newpath
+ return doctor
+ pass
+
+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":
+ 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.
+ """
+ msg = "I am a Package of the Doc"
+ 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)
+ #infos = [x for x in infos if not x.isPackage()]
+ 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
+
+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.
+ """
+ #return os.path.dirname( self.filepath )
+ 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: Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.txt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/docgrok.txt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.txt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/docgrok.txt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,100 @@
+=======
+DocGrok
+=======
+
+The Grok's personal doctor.
+
+What is it?
+-----------
+
+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 there to generate documentation for nearly everything
+living in a running Zope 3 instance.
+
+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
+
+
+Calling the doctor directly
++++++++++++++++++++++++++++
+
+The doctor can also be reached via Python, naturally.
+
+ >>> 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.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`:
+
+ >>> doctor = DocGrokPackage('grok.admin')
+
+
+ >>> doctor.path = 'grok.admin'
+ >>> doctor.getPath()
+ 'grok.admin'
+
+Fine. Obviously DocGrokPackages know as much as DocGroks. That's
+little. But a package knows about package-things too:
+
+ >>> info = doctor.getSubPackageInfo()
+
+will generate infos concerning subpackages in the package formerly
+set.
+
+
+Getting a 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 getDocGrokForDottedPath which delivers us a doctor, who
+can tell us more:
+
+ >>> from grok.admin.docgrok import getDocGrokForDottedPath
+ >>> thedoc = getDocGrokForDottedPath( 'grok.admin.docgrok' )
+ >>> type( thedoc )
+ <class 'grok.admin.docgrok.DocGrokModule'>
+
+This is correct. `docgrok` 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.
Copied: Sandbox/ulif/grok-adminui/src/grok/admin/static (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/static)
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view.py 2007-07-12 13:55:41 UTC (rev 77741)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view.py 2007-07-12 14:07:50 UTC (rev 77742)
@@ -1,36 +1,344 @@
+import grok
+import os
+from grok.admin.docgrok import DocGrok, DocGrokPackage, DocGrokModule
+from grok.admin.docgrok import DocGrokClass, DocGrokInterface, DocGrokGrokApplication
+from grok.admin.docgrok import DocGrokTextFile
+
import zope.component
-import grok.interfaces
from zope.app.folder.interfaces import IRootFolder
+from zope.app import zapi
+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.interface.interface import InterfaceClass
+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.proxy import removeAllProxies
+
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):
-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.redirect(self.url(self.context))
+
class Delete(grok.View):
+
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.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()
+
+
+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('appsindex'))
+
+
+class AppsIndex(GAIAView):
+ """View for application management."""
+
+ grok.name('appsindex')
+ grok.require('grok.ManageApplications')
+
+ def getDocOfApp(self, apppath, headonly = True):
+ from grok.admin import docgrok
+ 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 Z3Index(GAIAView):
+ """Zope3 management screen."""
+
+ grok.name('z3index')
+ grok.require('grok.ManageApplications')
+
+ riv = RuntimeInfoView()
+
+ def serverControl(self):
+ return zapi.getUtility(IServerControl)
+
+ def runtimeInfo(self):
+ self.riv.context = applicationController
+ return self.riv.runtimeInfo()
+
+ def update(self, time=None, restart=None, shutdown=None):
+ self.ri = self.runtimeInfo()
+
+ if time is None:
+ return
+ try:
+ time = int(time)
+ except:
+ return
+ control = self.serverControl()
+ if restart is not None:
+ control.restart(time)
+ elif shutdown is not None:
+ control.shutdown(time)
+ self.redirect(self.url())
+
+
+class Macros(GAIAView):
+ """Provides the o-wrap layout."""
+
+ grok.context(IRootFolder)
+
+
+class DocGrokView(GAIAView):
+
+ grok.context(DocGrok)
+ grok.name( 'index' )
+
+ 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
+ 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):
+
+ grok.context(DocGrokPackage)
+ grok.name( 'index' )
+
+
+class DocGrokModuleView(DocGrokView):
+
+ grok.context(DocGrokModule)
+ grok.name( 'index' )
+
+
+class DocGrokClassView(DocGrokView):
+
+ 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 _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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/appsindex.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/appsindex.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/appsindex.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/appsindex.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,56 @@
+<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>
+
+ </div>
+
+ <p>
+ <input class="button" type="submit" value="Delete Selected"/></p>
+ </fieldset>
+ </form>
+ <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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokclassview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokclassview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokclassview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokclassview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,206 @@
+<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-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>
+
+ <div tal:repeat="iface view/getInterfaces">
+ <div tal:content="python: str(iface)">asd</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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokgrokapplicationview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokgrokapplicationview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokgrokapplicationview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokgrokapplicationview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,206 @@
+<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>
+
+ <div tal:repeat="iface view/getInterfaces">
+ <div tal:content="python: str(iface)">asd</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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokinterfaceview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokinterfaceview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokinterfaceview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokinterfaceview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,206 @@
+<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 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>
+
+ <div tal:repeat="iface view/getInterfaces">
+ <div tal:content="python: str(iface)">asd</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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokmoduleview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokmoduleview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokmoduleview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokmoduleview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokpackageview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokpackageview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,130 @@
+<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>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>
+
+ <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">
+<!--
+ 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>
+
+<!--
+ <div tal:content="context/msg" />
+-->
+
+ </div>
+ </div>
+ </body>
+</html>
Copied: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgroktextfileview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgroktextfileview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgroktextfileview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgroktextfileview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/docgrokview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/docgrokview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,51 @@
+<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>
+ you might want to discover the following trails...
+ <ul>
+ <li>
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/zope">
+ The zope package</a>
+ </li>
+ <li>
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/grok">
+ The Grok package</a>
+ </li>
+ </ul>
+ </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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/gaiaview.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/gaiaview.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/gaiaview.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/gaiaview.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1 @@
+<!-- Just a placeholder -->
Modified: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/index.pt
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/index.pt 2007-07-12 13:55:41 UTC (rev 77741)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/index.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -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}/appsindex">
+ <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: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/macros.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/macros.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,92 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ 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"
+ tal:attributes="src view/static/grok-admin.jpg" />
+ </a>
+ </div>
+ <div id="Breadcrumbs">
+ <div id="Banner-Shadow">
+
+ </div>
+ </div>
+ <div id="Fireplace">
+ <img alt="Fire" src="images/Fire.gif"
+ tal:attributes="src view/static/grok-relax5.png" />
+ </div>
+ <p></p>
+ <center tal:define="currview python:view.url()">
+ <span class="menu_link_inactive"
+ tal:define="target string:${view/root_url}/appsindex">
+ <a href="appsindex"
+ tal:condition="python: target != currview"
+ tal:attributes="href target"
+ >Applications</a>
+ <span class="emph"
+ tal:condition="python: target == currview">
+ Applications
+ </span>
+ </span>
+
+ <!--<span class="menu_link_inactive"
+ tal:define="target python:view.root_url('z3index')"> -->
+ <!-- <span class="menu_link_inactive"
+ tal:define="target python:view.context.url('z3index')"> -->
+ <span class="menu_link_inactive"
+ tal:define="target string:${view/root_url}/z3index"
+ talasd:define="target string:${view/context/@@absolute_url}/z3index"
+ >
+ <a href="z3index"
+ tal:condition="python: target != currview"
+ tal:attributes="href target"
+ >Server Control</a>
+ <span class="emph"
+ tal:condition="python: target == currview">
+ Server Control
+ </span>
+ </span>
+
+ <a href=""
+ tal:attributes="href view/root_url">Debug</a>
+
+ <a href=""
+ tal:attributes="href string:${view/root_url}/docgrok/">
+ <span tal:attributes="class python:view.in_docgrok() and 'emph'"
+ >Documentation</span>
+ </a>
+ </center>
+
+ <center>
+ <div id="Content">
+ <div metal:define-slot="content">
+
+ <h1>Welcome to Grok!</h1>
+
+ <div>
+ Your friendly and easy way to Zope 3.
+ </div>
+
+ </div>
+ <div>
+ <p id="Footer-copyright">© Copyright 2007, The Zope Foundation<br />Design inspired by Sebastian Ware</p>
+ </div>
+ </div>
+ </center>
+
+
+ </body>
+ </html>
Copied: Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/z3index.pt (from rev 77741, Sandbox/ulif/grok-adminui-mergeprepare/src/grok/admin/view_templates/z3index.pt)
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/z3index.pt (rev 0)
+++ Sandbox/ulif/grok-adminui/src/grok/admin/view_templates/z3index.pt 2007-07-12 14:07:50 UTC (rev 77742)
@@ -0,0 +1,60 @@
+<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}/z3index">
+ <fieldset>
+ <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
+ </fieldset>
+
+ <fieldset tal:define="ri view/ri">
+ <legend>Server Process Info</legend>
+ <div>
+ <span class="emph">Uptime:</span>
+ <span tal:content="ri/Uptime">unknown</span>
+ </div>
+ <div>
+ <span class="emph">System Platform:</span>
+ <span tal:content="ri/SystemPlatform">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Zope Version:</span>
+ <span tal:content="ri/ZopeVersion">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Python Version:</span>
+ <span tal:content="ri/PythonVersion">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Command Line:</span>
+ <span tal:content="ri/CommandLine">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Preferred Encoding:</span>
+ <span tal:content="ri/PreferredEncoding">unknown</span>
+ </div>
+ <div>
+ <span class="emph">File System Encoding:</span>
+ <span tal:content="ri/FileSystemEncoding">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Process Id:</span>
+ <span tal:content="ri/ProcessId">unknown</span>
+ </div>
+ <div>
+ <span class="emph">Python Path:</span>
+ <div tal:repeat="path ri/PythonPath">
+ <span tal:content="path">unknown</span>
+ </div>
+ </div>
+ </fieldset>
+
+ </form>
+
+ </div>
+
+</html>
Modified: Sandbox/ulif/grok-adminui/src/grok/ftests/admin/admin.py
===================================================================
--- Sandbox/ulif/grok-adminui/src/grok/ftests/admin/admin.py 2007-07-12 13:55:41 UTC (rev 77741)
+++ Sandbox/ulif/grok-adminui/src/grok/ftests/admin/admin.py 2007-07-12 14:07:50 UTC (rev 77742)
@@ -2,47 +2,60 @@
>>> import grok
>>> grok.grok('grok.ftests.admin.admin')
+We fetch the standard page
+
>>> 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>
+ <html xmlns="http://www.w3.org/1999/xhtml">
...
- ...<legend>Add application</legend>
+ ... <legend>Add application</legend>
...
- >>> browser.getControl('Application').displayValue = ['grok.ftests.admin.admin.MammothManager']
- >>> browser.getControl('Name').value = 'my-mammoth-manager'
- >>> browser.getControl('Add').click()
+
+ >>> browser.getControl('Name your new app:',index=12).value = 'my-mammoth-manager'
+
+We are able to add a mammoth manager...
+
+ >>> browser.getControl('Create',index=12).click()
+
>>> print browser.contents
- <html>
+ <html xmlns="http://www.w3.org/1999/xhtml">
...
- <li>
- <input type="checkbox" name="items" value="my-mammoth-manager" />
- <a href="http://localhost/my-mammoth-manager">
- my-mammoth-manager
- (MammothManager)
+ ... <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>
- </li>
+ ... <legend>Add application</legend>
...
- >>> browser.getLink('my-mammoth-manager').click()
+
+Launch the added mammoth manager
+
+ >>> mylink = browser.getLink('my-mammoth-manager (MammothManager)').click()
>>> print browser.contents
Let's manage some mammoths!
-We are able to delete installed applications.
+ >>> print browser.url
+ http://localhost/my-mammoth-manager
+We are able to delete installed mammoth-mnagers
+
>>> browser.open("http://localhost/")
>>> print browser.contents
- <html>
+ <html xmlns="http://www.w3.org/1999/xhtml">
...
- ...<legend>Installed applications</legend>
+ ... <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>
+ <html xmlns="http://www.w3.org/1999/xhtml">
...
...<legend>Add application</legend>
...
@@ -51,6 +64,7 @@
import grok
class MammothManager(grok.Application, grok.Container):
+ """"A mammoth manager"""
pass
class Index(grok.View):
More information about the Checkins
mailing list