[doc] Continued work on doc, website and look.

This commit is contained in:
Gaetan Delannay 2012-09-13 14:43:40 +02:00
parent dc76ca542a
commit 9872f8ea66
23 changed files with 107 additions and 168 deletions

View file

@ -1,140 +1,87 @@
<html> <html>
<head> <head>
<title>An introduction to <b>gen</b></title> <title><b>gen</b> - Getting started</title>
<link rel="stylesheet" href="appy.css" type="text/css"> <link rel="stylesheet" href="appy.css" type="text/css">
</head> </head>
<body>
<h1>What is gen ?</h1>
<p><b>gen</b> is a code <b>gen</b>erator that allows you write web applications without having to face and understand the plumbery of a given web framework. <b>gen</b> protects you. Concentrate on functionalities that need to be implemented: <b>gen</b> will fight for you, by itself, against the low-level twisted machineries and will let you evolve in your pure, elegant and minimalistic Python world.</p> <body>
<h1>What is gen ?</h1>
<h1>OK, but concretely, on what technologies is <b>gen</b> built upon?</h1> <p><b>gen</b> is a code <b>gen</b>erator that allows you write web apps without having to face and understand the plumbery of a given web framework. <b>gen</b> protects you. Concentrate on functionalities that need to be implemented: <b>gen</b> will fight for you against the low-level twisted machineries and will let you evolve in your pure, elegant and minimalistic Python world.</p>
<p><b>gen</b> generates code that will run on <a href="http://plone.org" target="_blank">Plone 2.5</a>. Soon, the code will also be compatible with the latest Plone version. In my point of view, Plone 2.5 has reached the maximum level of complexity a Python developer may tolerate. Plone 2.5 heavily relies on Zope 2. While Plone 3 still runs on Zope 2, it has become heavily based on Zope 3 through the use of the "Five" product (=Zope 2 + Zope 3), that allows to use Zope 3 functionalities within Zope 2. Some people began to be angry about how complex certain tasks (like creating a portlet) became with Plone 3 (consult <a href="http://www.upfrontsystems.co.za/Members/roche/where-im-calling-from/meeting-plone-3" target="_blank">this</a>, for instance.) In order to fight against this trend, we decided to create a new code generator (a kind of concurrent to ArchGenXML so) that already makes sense for Plone 2.5 and will be more and more relevant for the current and future Plone versions, as the Plone community took the debatable decision to move to Zope 3.</p> <h1>Download and install</h1>
<h1>Getting started with gen</h1> <p>As a prerequisite, your machine must be able to compile. If it is not the case, install the necessary packages. On Ubuntu, for example:</p>
<p>Read only this if you want to run a "Hello world" gen application on your machine. This section is dedicated to Joel, a tremedous application and framework tester that ignores the essence of his gift.</p> <p class="code codePara">sudo apt-get install build-essential</p>
<h2>Note for Windows users</h2> <p>Install Zope 2.9. On Unix/Linux, the easiest way to do that is to download the <a href="https://launchpad.net/plone/2.5/2.5.5/+download/Plone-2.5.5-UnifiedInstaller.tgz">Plone unified installer</a>, that includes Zope.</p>
<p>I've never installed or tested gen on Windows. Feedback is welcome!</p> <p class="code codePara">
tar xvfz Plone-2.5.5-UnifiedInstaller.tgz<br/>
cd Plone-2.5.5-UnifiedInstaller<br/>
./install.sh
</p>
<h2>First step: download and install Plone</h2> <p>Don't be afraid, you just installed a lot of lines of code, but we will only use a very small subset of it: absolutely no line from Plone, and a tiny subset of Zope, and the Python interpreter that was also included.</p>
<p>You need to get Plone 2.5.5 installed on your system. The simplest way to do it is run the unified installer available <a href="http://plone.org/products/plone/releases/2.5.5" target="_blank">here</a>. If a message warns you that the release is not supported anymore, please send a mail to plone.org asking them why the official Plone site still uses a dangerous unsupported Plone version (at this time of writing, December 8th 2008).</p> <p>Install Appy. Download it <a href="https://launchpad.net/appy">here</a>, unzip it and install it in the Python interpreter previously mentioned. If you have installed Zope in its standard location at <span class="code">/opt/Plone-2.5.5</span>:</li>
<p>Let's suppose you have Plone, Zope and Python now installed in /opt/Plone-2.5.5. The unifier installed created a ZEO cluster in /opt/Plone-2.5.5/zeocluster. A ZEO cluster is a kind of load balancer that runs in front of several Zope servers, also called Zope "instances" (and also called "ZEO clients" in this context). For developement, a ZEO cluster is not needed; it is preferable to start from a fresh Zope server (from now on I will use the term "Zope instance") that you will install somewhere in your home folder.</p> <p class="code codePara">
unzip appy0.8.1.zip<br/>
mv appy /opt/Plone-2.5.5/Python-2.4.4/lib/python2.4/site-packages
</p>
<p>Create a new Zope instance by typing <span class="code">/opt/Plone-2.5.5/Python-2.4.4/bin/python /opt/Plone-2.5.5/bin/mkzopeinstance.py</span>. This is important to run this Python script with the Python interpreter that will run your Zope instance, ie the one installed in /opt/Plone-2.5.5/Python2.4.4. I will suppose you have created it in <span class="code">[myZopeInstance]</span>. You will use the username and password asked by the script for connecting to the Zope instance as administrator. A Zope instance has the following structure: <p>Create a symbolic link, in /usr/bin, of your Python interpreter.</p>
<table class="appyTable">
<tr>
<td class="code">bin</td>
<td>contains the script for starting/stopping the Zope instance. Go to this folder and type <span class="code">./zopectl fg</span>. It will start Zope in debug mode, in 'foreground' in your shell, by default on port 8080. If you want to start and stop the instance normally, (without being tied to your shell) use <span class="code">./zopectl start</span> and <span class="code">./zopectl stop</span>.</td>
</tr>
<tr>
<td class="code">etc</td>
<td>contains <span class="code">zope.conf</span>, the configuration file of your Zope instance. Every time you modify this file you will need to restart the server. It is well documented; edit this if, for example, you need to change the port on which the instance listens.</td>
</tr>
<tr>
<td class="code">Extensions</td>
<td>I don't care about this.</td>
</tr>
<tr>
<td class="code">import</td>
<td>I don't care about this.</td>
</tr>
<tr>
<td class="code">lib</td>
<td>, and more specifically <span class="code">lib/python</span>, is the folder where additional Python packages used by your Zope instance will lie. As gen-applications are standard Python packages, this is the typical place where you will store them.</td>
</tr>
<tr>
<td class="code">log</td>
<td>contains the log files of your instance: <span class="code">Z2.log</span> is the web server log (every HTTP request dumps a line into it); <span class="code">event.log</span> contains more relevant, "application-level" information (debug info, warnings, infos, etc). When running the instance in foreground, events dumped in event.log will also show up in your shell.</td>
</tr>
<tr>
<td class="code">Products</td>
<td>is the folder where Zope "add-ons" will reside. Although a Zope "product" is also a Python package, it contains additional ugly code that transforms it into a real Zope add-on. For every gen-application that you will create or sim-link in <span class="code">lib/python</span>, gen will create and maintain for you the corresponding Zope Product in <span class="code">Products</span>. There is a 1-1 relationship between a gen-application and the generated Zope product.</td>
</tr>
<tr>
<td class="code">var</td>
<td>is where Zope stores its database (DB) and related files. Unlike other DB technologies, there is no separate process that controls the DB and that needs to be called by the web/application server for reading or writing into the DB. Here, the Zope instance itself is directly connected to the DB, which is a single file named <span class="code">Data.fs</span>. The DB technology used by Zope is named ZODB (which stands for Zope Object DataBase).</td>
</tr>
</table>
<p>But what about Plone, huh? Plone is simply a bunch of Zope Products that add plenty of nice predefined functionalities and pages to Zope which is a bit arid as-is. So let's take a look at the <span class="code">Products</span> folder of your Zope instance. It is empty! For transforming it into a Plone-ready Zope instance, simply copy the Plone products from /opt/Plone-2.5.5/zeocluster/Products. Go to <span class="code">[myZopeInstance]/Products</span> and type <span class="code">cp -R /opt/Plone-2.5.5/zeocluster/Products/* .</span></p>
<p>Your Zope instance is now ready-to-use. Start it and go to <span class="code">http://localhost:8080/manage</span> with a web browser. Type the username and password you have entered while creating the instance and you will arrive in the ZMI (the Zope Management Interface). You may see the ZMI as a database viewer that shows you the content of Data.fs. You may also see it as an admin interface allowing you to trigger administrative functions. Because the ZODB is an object-oriented database, both visions are merged: functions and data are bundled into objects that are organized into a hierarchy (and more). The figure below shows the ZMI.</p> <p class="code codePara">ln -s /opt/Plone-2.5.5/Python-2.4.4/bin/python2.4 /usr/bin/python2.4</p>
<p align="center"><img src="img/zmi.png" align="center"/></p> <p>Create a Zope instance. A Zope instance is a web server that will listen for browser requests on some port. Launch the script named <span class="code">mkzopeinstance.py</span> that ships with Zope. The following lines of code create a Zope instance in <span class="code">/home/gdy/instances/RegInstance</span>.</p>
<p>You will notice that an empty Zope database still contains some predefined objects and folders: a "control panel", a folder (acl_users) where users are stored (it contains currently only one user), a web page "index_html" that is shown if you go to http://localhost:8080, etc. Everything is an object there, even the main error_log which is a nice way to browse through-the-web the log entries also dumped on the file system in <span class="code">[myZopeInstance]/log/event.log</span>.</p> <p class="code codePara">
python2.4 /opt/Plone-2.5.5/bin/mkzopeinstance.py<br/>
[answer script's questions:]<br/>
Directory: /home/gdy/instances/RegInstance<br/>
[also: username and password of the admin]
</p>
<p>A Plone site is simply one more object to create within the Zope hierarchy of objects. In the ZMI, select, in the listbox at the right-top corner, "Plone site" and click on the "Add" button. Choose "Appy" as Id and "Hello world" as Title and click on "Add Plone Site". You have now created a Plone site! If you want to access a given object through-the-web, you simply have to type the "path" of the object within the Zope hierarchy. So now, go to <span class="code">http://localhost:8080/Appy</span> and you will see the main page of your Plone site, like shown below.</p> <p>Type anything as username and password: Appy will ignore it and create user <span class="code">admin</span>, password <span class="code">admin</span>.</p>
<p align="center"><img src="img/plone.png" align="center"/></p> <p>Your instance is ready! It will run on port 8080 by default.</p>
<h2>Second step: download and install Appy</h2> <h1>Create a webapp</h1>
<p>The underlying technologies required by gen are now up-and-running. Let's install gen. gen is a simple Python package available as a zip file (and more) <a href="version.html">here</a>. For example, you may unzip it in <span class="code">[myZopeInstance]/lib/python</span> or in <span class="code">/opt/Plone-2.5.5/Python-2.4.4/lib/python2.4/site-packages</span>. If you put it in the latter place, it will be enabled for all future Zope instances you may use or create (including the ZEO cluster in /opt/Plone-2.5.5/zeocluster).</p> <p>Now, we need to write a webapp and install it into this instance. We will create a small webapp, called Registration, that will allow anonymous people to register to some event through the net. The administrator of the event will be able to consult and search registrations. He will also be able to retrieve a PDF version of every registration.</p>
<h2>Third step: develop the "Hello world" application</h2> <p>A Appy webapp is simply a Python package. So create a Python package, for example in <span class="code">/home/gdy/projets/Registration</span>.</p>
<p>We are ready to create our first gen-application. Imagine we are a software company that creates components using Zope 3. The company is a start-up, but after a few months, it has already developed hundreds of Zope 3 components (indeed, every single web page is considered a component). The company decides to create a simple tool for managing those small pieces of code. Let's create a simple gen-application for this, in a file named <span class="code">ZopeComponent.py</span>:</p> <p class="code codePara">
cd /home/gdy/projets<br/>
mkdir Registration<br/>
cd Registration<br/>
touch __init__.py<br/>
touch Registration.py<br/>
</p>
<p class="code"> <p>File <span class="code">__init__.py</span> is required by Python, to tranform folder Registration into a Python package. File <span class="code">Registration.py</span> will contain the definition of the class Registration: one instance of this class will be created and stored in the database every time a user registers itself though the web.</p>
01&nbsp;<b>from</b> appy.gen <b>import</b> *<br/>
02&nbsp;<br/>
03&nbsp;<b>class</b> ZopeComponent:<br/>
04&nbsp;&nbsp;&nbsp;root = True<br/>
05&nbsp;&nbsp;&nbsp;description = String()<br/>
06&nbsp;&nbsp;&nbsp;technicalDescription = String(format=String.XHTML)<br/>
07&nbsp;&nbsp;&nbsp;status = String(validator=['underDevelopement', 'stillSomeWorkToPerform',<br/>
08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'weAreAlmostFinished', 'alphaReleaseIsBugged', 'whereIsTheClient'])<br/>
09&nbsp;&nbsp;&nbsp;funeralDate = Date()<br/>
</p>
<p>9 lines of code (with a blank one; please note that I'm obsessed by lines that do not span more than 80 characters): yes, you are done. Every Zope component will be described by this bunch of attributes. Specifying the class as <span class="code">root</span> makes it a class of special importance to gen; it will be treated with some honors. We will see how in a moment.</p> <p class="code codePara">
# ------------------------------------------------------------------------------<br/>
<h2>Fourth step: generate the Zope/Plone product</h2> <b>from</b> appy.gen <b>import</b> *<br/>
<br/>
<p>Please put <span class="code">ZopeComponent.py</span> in <span class="code">[myZopeInstance]/lib/python</span>, cd into this folder and type the following line.</p> # ------------------------------------------------------------------------------<br/>
<b>class</b> Registration:<br/>
<p class="code">python [whereYouInstalledAppy]/appy/gen/generator.py ZopeComponent.py plone25 ../../Products</p> &nbsp;&nbsp;&nbsp;&nbsp;root = True<br/>
&nbsp;&nbsp;&nbsp;&nbsp;creators = ['Anonymous', 'Manager']<br/>
<p>You will get an output similar to this:</p> <br/>
&nbsp;&nbsp;&nbsp;&nbsp;p = {'multiplicity': (1,1)}<br/>
<p class="code"> &nbsp;&nbsp;&nbsp;&nbsp;applicantName = String(**p)<br/>
Generating product in /home/gde/ZopeInstance2/Products...<br/> &nbsp;&nbsp;&nbsp;&nbsp;applicantEmail = String(validator=String.EMAIL, **p)<br/>
Generating class ZopeComponent.ZopeComponent...<br/> &nbsp;&nbsp;&nbsp;&nbsp;companyName = String(**p)<br/>
Done.<br/> &nbsp;&nbsp;&nbsp;&nbsp;companyUrl = String(validator=String.URL, **p)<br/>
</p> &nbsp;&nbsp;&nbsp;&nbsp;webappDescription = String(format=String.XHTML, **p)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;companyDescription = String(format=String.XHTML, **p)<br/>
<p>If you are familiar with Zope/Plone products, you may take a look to the one gen has generated in <span class="code">[myZopeInstance]/Products/ZopeComponent</span>.</p> # ------------------------------------------------------------------------------<br/>
</p>
<h2>Fifth step: admire the result</h2> </body>
<p>Restart your Zope instance, go to <span class="code">http://localhost:8080/Appy</span>, log in with your Zope admin password and click on the link "site setup" in the top-right corner: you arrive in the Plone configuration panel. Click on "Add/Remove products". Among the "products available for install" you will find <span class="code">ZopeComponent</span>:select it and install it. Right. Click now on the Plone logo to come back to the main page, and see what's new. On the left, you see a new "portlet" entitled "zope component": this is the main entry point for your application. gen generates a portlet for every gen-application. The link "Zope component" allows you to access a kind of "dashboard" displaying all Zope components. The page is empty for the moment: no component was created yet:</p>
<p align="center"><img src="img/emptyQuery.png" align="center"/></p>
<p>Click on the "plus" sign for creating your first Zope component. A form allows you to enter information according to your Python class definition in ZopeComponent.py.</p>
<p align="center"><img src="img/zopeComponentEdit.png" align="center"/></p>
<p>Note that the order of the widgets correspond to the order of the fields in the Python class definition. Clicking on "Save" creates your first Zope 3 component, and brings you to the "view" page for your component. Clicking again on the portlet link will show you this:</p>
<p align="center"><img src="img/filledQuery.png" align="center"/></p>
<p>On this screen, every Python class you declare as "root" in your gen-application will get a tab allowing you to view instances of this class or add new ones. If you've defined more than one root class, a global tab will precede all others and display all instances of all root classes. Table columns are all sortable; they also contain special fields used for filtering rows according to type entered. Actions the user may trigger on Zope components are available in the last column. Clicking on the component title will bring you back to the "view" page for it.</p>
<p>Besides the view/edit pages and this dashboard, gen also generates a configuration panel for your application. It is directly accessible through the portlet by clicking on the hammer.</p>
<p align="center"><img src="img/tool.png" align="center"/></p>
<p>This configuration panel is called a "tool". By default, the ZopeComponent tool defines one "flavour" named "ZopeComponent". A flavour is a particular set of configuration options that may apply only to a subset of your application objects. The concept of flavour allows you to get, in a single web application, several variants of it running concurrently, all with distinct configuration options. If, for example, you create a software for managing the agenda and decisions of a company's meetings, you may need to create one flavour for each meeting type (IT department meetings, HR department meetings, board of directors, etc): every meeting type will get its own configuration options. In the application portlet, one link is created for every flavour defined in the tool. If you have only one flavour, it makes sense to rename it with something like "All components". You may do it by clicking on the pen besides the flavour name. This way, the application portlet will look like this:</p>
<p align="center"><img src="img/portlet.png" align="center"/></p>
<p>A the tool level, some general configuration options are defined (they apply to all defined flavours), like the way Plone will contact OpenOffice in server mode for producing documents (document-generation through <a href="pod.html">appy.pod</a> is built-in for every gen-app) or the number of elements to display per page on the dashboard. If you click on the flavour title, you will discover some configuration options that you may customize at the flavour level (the whole set of options in the flavour depends on options specified in your Python classes). For example, clicking on the pen within the "user interface" tab allows you to customize the columns shown in the dashboard for objects corresponding to this flavour. Select one or more columns here (keep the "control" key pressed if you want to select several columns), save the result and see how the dashboard evolves accordingly.</p>
</body>
</html> </html>

View file

@ -462,7 +462,7 @@
<td>By default, the main Flavour page (corresponding to the left-most tab) only shows the Flavour name and a link to go back to the Tool.</td> <td>By default, the main Flavour page (corresponding to the left-most tab) only shows the Flavour name and a link to go back to the Tool.</td>
</tr> </tr>
<tr> <tr>
<td class="code">documentGeneration&nbsp;&nbsp;</td> <td class="code">documents&nbsp;&nbsp;</td>
<td>All stuff tied to generation of documents from POD templates.</td> <td>All stuff tied to generation of documents from POD templates.</td>
</tr> </tr>
<tr> <tr>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

BIN
doc/img/appy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

View file

@ -5,7 +5,7 @@
</head> </head>
<body> <body>
<!-- gen --> <!-- gen -->
<table cellpadding="0" cellspacing="0" align="center" width="80%" class="homeTable"> <table cellpadding="0" cellspacing="0" align="center" width="80%" class="homeTable" style="margin-bottom: 10px">
<tr valign="top"> <tr valign="top">
<td width="100px" align="center" style="font-size: 95%"> <td width="100px" align="center" style="font-size: 95%">
<img src="img/gen.png" style="margin-bottom: 7px"/><br/> <img src="img/gen.png" style="margin-bottom: 7px"/><br/>

View file

@ -24,30 +24,24 @@
<p><a href="gen.html">gen</a> is the largest part of Appy and allows to build web apps. The idea is to generate, from a set of simple Python classes, a complete web app, from the web user interface to the database. Integration with appy.pod allows to generate documents from the web app. gen currently generates web apps for the Zope web application server. We had the following ideas in mind while developing it.</p><br/> <p><a href="gen.html">gen</a> is the largest part of Appy and allows to build web apps. The idea is to generate, from a set of simple Python classes, a complete web app, from the web user interface to the database. Integration with appy.pod allows to generate documents from the web app. gen currently generates web apps for the Zope web application server. We had the following ideas in mind while developing it.</p><br/>
<ul> <p class="focus" align="center">A code-centric but conceptual approach.</p><br/>
<li><b>The code-centric but conceptual approach</b>. appy.gen works at a higher level of abstraction than most other frameworks or approaches. With gen, you are not going to write a function that returns dynamic HTML when some URL is hit. You are going to define classes and relationships, and methods will be like hooks that will react when some events occur (an object is created, edited, deleted, etc). You don't have to write a single line of HTML, CSS or Javascript to write basic Appy applications. Only pure Python. This is what I call <i>conceptual</i>. But unlike most conceptual approaches, you will not draw any diagram or anything that is not Python code. Pure Python, pure simplicity, no repetition, no transformation: optimal efficiency, optimal maintainability. Other conceptual approaches like the idea of starting from some abstract vision like a (graphical) model (boxes, arrows...); from it, tools generate code skeletons that need to be completed in a subsequent "development" phase. Such "transformational" approaches (I vaguely know some buzzwords for it: MDA, MDD I think) may even count more than two phases and manipulate different models at different abstraction levels before producing code (this is what I have learned at the university). Such approaches spread the information in several places, because every model or representation has limited expressivity. In my opinion, it produces redundancy and eventually leads to maintenance problems. It violates the <a href="http://c2.com/cgi/wiki?DontRepeatYourself" target="_blank">DRY principle</a>, which is closely related to our Null-IT principle (see above) On the contrary, gen knows only about code. The "model" you write is a Python package. Being written in a high-level programming language, it does not constrain your expressivity in any way. More important: this code <i>is</i> the running code, and thus the only place where you describe your software. Simply, "wrappers" are added to it in order to plug him into the low-level Zope machinery. A gen-powered Python program acts like a comedian manipulating a tuned marionette: simple moves from the comedian produce complex, cascading and funny behaviours at the puppet level. Moreover, a code-based approach has the following benefits:</li><br/>
<ul>
<li>when using a graphical model, you are emprisoned into a poorly expressive notation. Let's take an example. If you express a state machine with a UML state diagram, how will you be able to define another state machine based on the previous one? If you express it with code, it is as simple as using class inheritance. Typically, with appy.gen, you may achieve nice results like workflow inheritance. Of course, using graphical models for communicating or focusing on some parts of your program may be very valuable; this is why we foresee to implement model generation (class diagrams, statecharts, etc) from a appy.gen application. This is our way to see how to use graphical models: as views generated from the code. We don't believe at all in approaches like generating code from models or round-trip engineering.</li>
<li>when using some centralized code repository like subversion, a UML model, for example, is typically stored as a binary file. So it is impossible to work concurrently on various parts of it; nor is it possible to view who has changed what in it, etc;</li>
<li>factoring similar groups of attributes or doing other intelligent treatment on a graphical model is not possible;</li>
<li>there is no need to write and maintain a model parser (like XMI);</li>
<li>yes, you can use cut-and-paste with any text editor! If you want to do similar things with a model, you will probably need to buy some expensive UML editor;</li>
</ul><br/>
<li><b>Violating the model-view-controller pattern (and a lot of other patterns, too)</b>. Design patterns are elegant low-level constructs used to overcome the limitations of programming languages (ie statically-typed languages like Java) or frameworks. Using them implies adding classes just for making the plumbery work; it augments code complexity and, again, spreads information at several places. Separating code describing data from code presenting it produces the same problem. appy.gen takes the approach of grouping everything at the same place. For example, information that dictates how a given field will be displayed is part of the field definition. <p>appy.gen works at a higher level of abstraction than most other frameworks or approaches. With gen, you are not going to write a function that returns dynamic HTML when some URL is hit. You are going to define classes and relationships, and methods will be like hooks that will react when some events occur (an object is created, edited, deleted, etc). You don't have to write a single line of HTML, CSS or Javascript to write basic Appy applications. Only pure Python. This is what I call <i>conceptual</i>. But unlike most conceptual approaches, you will not draw any diagram or anything that is not Python code. Pure Python, pure simplicity, no repetition, no transformation: optimal efficiency, optimal maintainability. Other conceptual approaches like the idea of starting from some abstract vision like a (graphical) model (boxes, arrows...); from it, tools generate code skeletons that need to be completed in a subsequent "development" phase. Such "transformational" approaches may even count more than two phases and manipulate different models at different abstraction levels before producing code (this is what I have learned at the university). Such approaches spread the information in several places, because every model or representation has limited expressivity. In my opinion, it produces redundancy and eventually leads to maintenance problems. It violates the <a href="http://c2.com/cgi/wiki?DontRepeatYourself" target="_blank">DRY principle</a>, which is closely related to our Null-IT principle (see above) On the contrary, gen knows only about code. The "model" you write is a Python package. Being written in a high-level programming language, it does not constrain your expressivity in any way. More important: this code <i>is</i> the running code, and thus the only place where you describe your software. Simply, "wrappers" are added to it in order to plug him into the low-level Zope machinery. A gen-powered Python program acts like a comedian manipulating a tuned marionette: simple moves from the comedian produce complex, cascading and funny behaviours at the puppet level.</p><br/>
</li><br/>
<li><b>All-in-one objects</b>. As a consequence of the two previous bullets, gen objects (which are Plain Old Python Objects: you do not even have to inherit from a base gen class!) are self-sufficient. If you want to understand some (group of) functionality within a well-designed gen Python program, you will not loose yourself walking through an intricate web of Python classes and XML definition files. <p class="focus" align="center">Violating the model-view-controller pattern (and a lot of other patterns, too).</p><br/>
</li><br/>
<li><b>Building complex and shared web applications</b>. While some out-of-the-box tools like Plone, Joomla or Wordpress are mainly targeted at building websites of specific categories (CMS, blogs, etc), they provide poor support for those who want to build <i>complex</i> and <i>shared</i> web applications. <p>Design patterns are elegant low-level constructs used to overcome the limitations of programming languages (ie statically-typed languages like Java) or frameworks. Using them implies adding classes just for making the plumbery work; it augments code complexity and, again, spreads information at several places. Separating code describing data from code presenting it produces the same problem. appy.gen takes the approach of grouping everything at the same place. For example, information that dictates how a given field will be displayed is part of the field definition.</p><br/>
</li>
<ul> <p class="focus" align="center">All-in-one objects.</p><br/>
<li>By <b>"complex"</b>, I mean real "business applications" (or "information systems", like accounting systems, HR systems or online booking systems) whose interfaces are web interfaces; a classical CMS-based website being a particular case whose main purpose is to provide information, with some limited degree of interaction with the user. Web business applications are characterized by (1) a rich conceptual model featuring a complex web of inter-related classes and (2) the need for complex querying and reporting facilities, be it through-the-web or within generated documents.
</li> <p>As a consequence of the two previous paragraphs, gen objects (which are Plain Old Python Objects: you do not even have to inherit from a base gen class!) are self-sufficient. If you want to understand some (group of) functionality within a well-designed gen Python program, you will not loose yourself walking through an intricate web of Python classes and XML definition files.</p><br/>
<li>Currently, free software is widespread within the "IT infrastructure" (operating systems, networking components, web servers, application servers...) and contaminates more and more general-purpose software applications, like word processors, spreadsheets or multimedia players. For the most of it, the free movement currently reaches domains where requirements are either perfectly known by the developers themselves, deduced from observing proprietary software or part of some general cultural background. In order to raise freedom at the higher levels of business and innovation, we need new mechanisms allowing to tackle (business-)specific requirements, while maintaining the possibility to spread and <b>share</b> software from one organization to the other. appy.gen was built to cope with this new scale of challenges and proposes a set of built-in constructs for creating generic business kernels implementing common requirements and allowing to tailor it to the specific needs of a given organization. This work results from experience gained from a deep involvement in the <a href="http://plonegov.org" target="_blank">PloneGov community</a>, an international group of public administrations developing and sharing free software for their own needs. It also benefits from a close collaboration with several research initiatives (the <a href="http://www.fundp.ac.be/en/precise/">PRECISE</a> research center and the <a href="http://moves.vub.ac.be/">MoVES</a> project) exploring Software Product Lines Engineering and inventing new ways to manage variability among software systems.
</li> <p class="focus" align="center">Building complex and shared web applications.</p><br/>
</ul>
</ul> <p>While some out-of-the-box tools like Plone, Joomla or Wordpress are mainly targeted at building websites of specific categories (informative sites, blogs...), they provide poor support for those who want to build <i>complex</i> and <i>shared</i> web applications.</p><br/>
<p>By <b>"complex"</b>, we mean real "business applications" (or "information systems", like accounting systems, HR systems or online booking systems) whose interfaces are web interfaces; a classical CMS-based website being a particular case whose main purpose is to provide information, with some limited degree of interaction with the user. Web business applications are characterized by (1) a rich conceptual model featuring a complex web of inter-related classes and (2) the need for complex querying and reporting facilities, be it through-the-web or within generated documents.</p><br/>
<p>Currently, free software is widespread within the "IT infrastructure" (operating systems, networking components, web servers, application servers...) and contaminates more and more general-purpose software applications, like word processors, spreadsheets or multimedia players. For the most of it, the free movement currently reaches domains where requirements are either perfectly known by the developers themselves, deduced from observing proprietary software or part of some general cultural background. In order to raise freedom at the higher levels of business and innovation, we need new mechanisms allowing to tackle (business-)specific requirements, while maintaining the possibility to spread and <b>share</b> software from one organization to the other. appy.gen was built to cope with this new scale of challenges and proposes a set of built-in constructs for creating generic business kernels implementing common requirements and allowing to tailor it to the specific needs of a given organization. This work results from experience gained from a deep involvement in the <a href="http://plonegov.org" target="_blank">PloneGov community</a>, an international group of public administrations developing and sharing free software for their own needs. It also benefits from a close collaboration with several research initiatives (the <a href="http://www.fundp.ac.be/en/precise/">PRECISE</a> research center and the <a href="http://moves.vub.ac.be/">MoVES</a> project) exploring Software Product Lines Engineering and inventing new ways to manage variability among software systems.</p>
</body> </body>
</html> </html>

View file

@ -11,12 +11,12 @@
<link rel="stylesheet" href="appy.css" type="text/css"> <link rel="stylesheet" href="appy.css" type="text/css">
</head> </head>
<body> <body>
<table class="main" align="center" width="100%" cellpadding="0" cellspacing="0"> <table class="main content" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr height="88px"> <tr height="88px">
<td> <td>
<table width="100%" cellpadding="0" cellspacing="0"> <table width="100%" cellpadding="0" cellspacing="0">
<tr valign="middle"> <tr valign="middle">
<td width="120px"><a href="/"><img src="img/appy.jpg"/></a> <td width="120px"><a href="/"><img src="img/appy.png"/></a>
</td> </td>
<td align="center"> <td align="center">
<div class="focus"> <div class="focus">

View file

@ -492,7 +492,7 @@ class ToolClassDescriptor(ClassDescriptor):
'''Adds the fields needed in the Tool for configuring a Pod field.''' '''Adds the fields needed in the Tool for configuring a Pod field.'''
className = fieldDescr.classDescr.name className = fieldDescr.classDescr.name
# On what page and group to display those fields ? # On what page and group to display those fields ?
pg = {'page': 'documentGeneration', pg = {'page': 'documents',
'group':gen.Group(fieldDescr.classDescr.klass.__name__,['50%']*2)} 'group':gen.Group(fieldDescr.classDescr.klass.__name__,['50%']*2)}
# Add the field that will store the pod template. # Add the field that will store the pod template.
fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName) fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName)

View file

@ -248,14 +248,9 @@ class ZopeInstaller:
appyTool = tool.appy() appyTool = tool.appy()
appyTool.log('Appy version is "%s".' % appy.version.short) appyTool.log('Appy version is "%s".' % appy.version.short)
# Create the admin user if no user exists. # Create the admin user if it does not exist.
try: if not appyTool.count('User', noSecurity=True, login='admin'):
users = self.app.acl_users.getUsers() print 'No admin!'
except:
# When Plone has installed PAS in acl_users this may fail. Plone
# may still be in the way for migration purposes.
users = ('admin',) # We suppose there is at least a user.
if not users:
appyTool.create('users', noSecurity=True, login='admin', appyTool.create('users', noSecurity=True, login='admin',
password1='admin', password2='admin', password1='admin', password2='admin',
email='admin@appyframework.org', roles=['Manager']) email='admin@appyframework.org', roles=['Manager'])

View file

@ -261,7 +261,7 @@ class Tool(ModelClass):
page=gen.Page('pages', show=isManager)) page=gen.Page('pages', show=isManager))
# Document generation page # Document generation page
dgp = {'page': gen.Page('documentGeneration', show=isManagerEdit)} dgp = {'page': gen.Page('documents', show=isManagerEdit)}
def validPythonWithUno(self, value): pass # Real method in the wrapper def validPythonWithUno(self, value): pass # Real method in the wrapper
unoEnabledPython = gen.String(show=False,validator=validPythonWithUno,**dgp) unoEnabledPython = gen.String(show=False,validator=validPythonWithUno,**dgp)
openOfficePort = gen.Integer(default=2002, show=False, **dgp) openOfficePort = gen.Integer(default=2002, show=False, **dgp)

View file

@ -19,21 +19,21 @@ input { font: 92% Helvetica,Arial,sans-serif }
input[type=image] { border: 0; background: none; cursor: pointer; } input[type=image] { border: 0; background: none; cursor: pointer; }
input[type=checkbox] { border: 0; background: none; cursor: pointer;} input[type=checkbox] { border: 0; background: none; cursor: pointer;}
input[type=radio] { border: 0; background: none; cursor: pointer;} input[type=radio] { border: 0; background: none; cursor: pointer;}
input[type=file] { border: 0px solid #D7DEE4; input[type=file] { border: 0px solid #d0d0d0;
background-color: #f8f8f8; cursor: pointer;} background-color: #f8f8f8; cursor: pointer;}
input[type=button] { border: 1px solid #D7DEE4; input[type=button] { border: 1px solid #d0d0d0;
background-color: #f8f8f8; cursor: pointer;} background-color: #f8f8f8; cursor: pointer;}
input[type=submit] { border: 1px solid #D7DEE4; background-color: #f8f8f8; input[type=submit] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
cursor: pointer; } cursor: pointer; }
input[type=password] { border: 1px solid #D7DEE4; background-color: #f8f8f8; input[type=password] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
font-family: Helvetica,Arial,sans-serif;} font-family: Helvetica,Arial,sans-serif;}
input[type=text] { border: 1px solid #D7DEE4; background-color: #f8f8f8; input[type=text] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
font-family: Helvetica,Arial,sans-serif; font-family: Helvetica,Arial,sans-serif;
margin-bottom: 1px} margin-bottom: 1px}
select { border: 1px solid #D7DEE4; background-color: #f8f8f8;} select { border: 1px solid #d0d0d0; background-color: #f8f8f8;}
textarea { width: 99%; font: 100% Helvetica,Arial,sans-serif; textarea { width: 99%; font: 100% Helvetica,Arial,sans-serif;
border: 1px solid #a79e9e; background-color: #f8f8f8;} border: 1px solid #d0d0d0; background-color: #f8f8f8;}
label { font-weight: 600; font-style: italic; line-height: 1.4em;} label { font-weight: 600; font-style: italic; line-height: 1.4em;}
legend { padding-bottom: 2px; padding-right: 3px; color: black;} legend { padding-bottom: 2px; padding-right: 3px; color: black;}
ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0; ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0;
@ -50,7 +50,7 @@ img { border: 0; vertical-align: middle}
.main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9; .main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9;
border-style: solid; border-width: 1px; border-color: grey} border-style: solid; border-width: 1px; border-color: grey}
.top { height: 75px; margin-left: 3em; vertical-align: top;} .top { height: 89px; margin-left: 3em; vertical-align: top;}
.lang { margin-right: 6px; } .lang { margin-right: 6px; }
.userStrip { background-color: #6282B3; height: 35px; .userStrip { background-color: #6282B3; height: 35px;
border-top: 3px solid #034984; border-bottom: 2px solid #034984; } border-top: 3px solid #034984; border-bottom: 2px solid #034984; }
@ -70,7 +70,8 @@ img { border: 0; vertical-align: middle}
.focus td { padding: 4px 0px 4px 4px } .focus td { padding: 4px 0px 4px 4px }
.discreet { font-size: 90%; } .discreet { font-size: 90%; }
.lostPassword a { font-size: 90%; color: white; padding-left: 1em;} .lostPassword a { font-size: 90%; color: white; padding-left: 1em;}
.portlet { width: 150px; border-right: 1px solid #5F7983;} .portlet { width: 150px; border-right: 1px solid #5F7983;
background-color: #ededed}
.portletContent { margin: 9px; } .portletContent { margin: 9px; }
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;} .portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
.portletCurrent { font-weight: bold; } .portletCurrent { font-weight: bold; }
@ -81,7 +82,7 @@ img { border: 0; vertical-align: middle}
.portletSearch { font-size: 90%; font-style: italic; padding-left: 1em} .portletSearch { font-size: 90%; font-style: italic; padding-left: 1em}
.phase { border-style: dashed; border-width: thin; padding: 4px 0.6em 5px 1em;} .phase { border-style: dashed; border-width: thin; padding: 4px 0.6em 5px 1em;}
.phaseSelected { background-color: #F4F5F6; } .phaseSelected { background-color: #F4F5F6; }
.content { padding: 14px 14px 9px 15px; } .content { padding: 14px 14px 9px 15px; background-color: #f1f1f1 }
.grey { display: none; position: absolute; left: 0px; top: 0px; .grey { display: none; position: absolute; left: 0px; top: 0px;
background:grey; opacity:0.5; -moz-opacity:0.5; -khtml-opacity:0.5; background:grey; opacity:0.5; -moz-opacity:0.5; -khtml-opacity:0.5;
filter:alpha(Opacity=50);} filter:alpha(Opacity=50);}
@ -135,5 +136,5 @@ img { border: 0; vertical-align: middle}
border-style: solid none; border-width: 1px medium; border-style: solid none; border-width: 1px medium;
color: #333333; line-height: 120%; color: #333333; line-height: 120%;
padding: 10px; margin: 10px 0 10px 0} padding: 10px; margin: 10px 0 10px 0}
.homeTable { background-color: #f2f5f8; border-top: 2px solid grey} .homeTable { background-color: #E3E3E3; border-top: 1px solid grey}
.homeTable td { padding: 10px 5px 10px 10px} .homeTable td { padding: 10px 5px 10px 10px}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,7 +1,9 @@
<tal:main define="tool python: context.config"> <tal:main define="tool python: context.config">
<html metal:use-macro="context/ui/template/macros/main"> <html metal:use-macro="context/ui/template/macros/main">
<div metal:fill-slot="content"> <table metal:fill-slot="content" width="300px" height="240px" align="center">
<span tal:replace="structure python: tool.translate('front_page_text')"/> <tr valign="middle">
</div> <td align="center" tal:content="structure python: tool.translate('front_page_text')"></td>
</tr>
</table>
</html> </html>
</tal:main> </tal:main>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 314 B