Jelajahi Sumber

[DOCS] Added tutorials
- Merged tutorials from zf-devteam branch
- Updated WindowsAzure manual build to use xinclude
- Sectioned manual into "parts" to accomodate tutorials

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@19766 44c647ce-9c0f-0410-b52a-842ac1e357ba

matthew 16 tahun lalu
induk
melakukan
48054cff4b
50 mengubah file dengan 6623 tambahan dan 81 penghapusan
  1. TEMPAT SAMPAH
      documentation/manual/en/figures/learning.quickstart.create-form.png
  2. TEMPAT SAMPAH
      documentation/manual/en/figures/learning.quickstart.create-model.png
  3. TEMPAT SAMPAH
      documentation/manual/en/figures/learning.quickstart.intro.mvc.png
  4. 100 6
      documentation/manual/en/manual.xml.in
  5. 6 2
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure.xml
  6. 27 27
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Blob.xml
  7. 14 14
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Queue.xml
  8. 32 32
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Table.xml
  9. 20 0
      documentation/manual/en/tutorials/autoloading-conclusion.xml
  10. 101 0
      documentation/manual/en/tutorials/autoloading-design.xml
  11. 32 0
      documentation/manual/en/tutorials/autoloading-intro.xml
  12. 106 0
      documentation/manual/en/tutorials/autoloading-resources.xml
  13. 158 0
      documentation/manual/en/tutorials/autoloading-usage.xml
  14. 384 0
      documentation/manual/en/tutorials/form-decorators-composite.xml
  15. 12 0
      documentation/manual/en/tutorials/form-decorators-conclusion.xml
  16. 283 0
      documentation/manual/en/tutorials/form-decorators-individual.xml
  17. 22 0
      documentation/manual/en/tutorials/form-decorators-intro.xml
  18. 362 0
      documentation/manual/en/tutorials/form-decorators-layering.xml
  19. 244 0
      documentation/manual/en/tutorials/form-decorators-simplest.xml
  20. 20 0
      documentation/manual/en/tutorials/layout-conclusions.xml
  21. 54 0
      documentation/manual/en/tutorials/layout-intro.xml
  22. 242 0
      documentation/manual/en/tutorials/layout-usage.xml
  23. 27 0
      documentation/manual/en/tutorials/lucene-index-opening.xml
  24. 116 0
      documentation/manual/en/tutorials/lucene-index-structure.xml
  25. 94 0
      documentation/manual/en/tutorials/lucene-indexing.xml
  26. 102 0
      documentation/manual/en/tutorials/lucene-intro.xml
  27. 48 0
      documentation/manual/en/tutorials/lucene-pagination.xml
  28. 228 0
      documentation/manual/en/tutorials/lucene-queries.xml
  29. 97 0
      documentation/manual/en/tutorials/lucene-searching.xml
  30. 182 0
      documentation/manual/en/tutorials/multiuser-authentication.xml
  31. 226 0
      documentation/manual/en/tutorials/multiuser-authorization.xml
  32. 71 0
      documentation/manual/en/tutorials/multiuser-intro.xml
  33. 133 0
      documentation/manual/en/tutorials/multiuser-sessions.xml
  34. 106 0
      documentation/manual/en/tutorials/paginator-control.xml
  35. 37 0
      documentation/manual/en/tutorials/paginator-intro.xml
  36. 124 0
      documentation/manual/en/tutorials/paginator-simple.xml
  37. 74 0
      documentation/manual/en/tutorials/paginator-together.xml
  38. 61 0
      documentation/manual/en/tutorials/plugins-conclusion.xml
  39. 54 0
      documentation/manual/en/tutorials/plugins-intro.xml
  40. 151 0
      documentation/manual/en/tutorials/plugins-usage.xml
  41. 15 0
      documentation/manual/en/tutorials/quickstart-conclusion.xml
  42. 171 0
      documentation/manual/en/tutorials/quickstart-create-form.xml
  43. 213 0
      documentation/manual/en/tutorials/quickstart-create-layout.xml
  44. 760 0
      documentation/manual/en/tutorials/quickstart-create-model.xml
  45. 429 0
      documentation/manual/en/tutorials/quickstart-create-project.xml
  46. 114 0
      documentation/manual/en/tutorials/quickstart-intro-mvc.xml
  47. 254 0
      documentation/manual/en/tutorials/view-placeholders-basics.xml
  48. 16 0
      documentation/manual/en/tutorials/view-placeholders-conclusion.xml
  49. 41 0
      documentation/manual/en/tutorials/view-placeholders-intro.xml
  50. 460 0
      documentation/manual/en/tutorials/view-placeholders-standard.xml

TEMPAT SAMPAH
documentation/manual/en/figures/learning.quickstart.create-form.png


TEMPAT SAMPAH
documentation/manual/en/figures/learning.quickstart.create-model.png


TEMPAT SAMPAH
documentation/manual/en/figures/learning.quickstart.intro.mvc.png


+ 100 - 6
documentation/manual/en/manual.xml.in

@@ -29,11 +29,106 @@
         -->
     </bookinfo>
 
-    <chapter id="introduction">
+    <part id="introduction">
         <title>Introduction to Zend Framework</title>
         <xi:include href="ref/overview.xml" />
         <xi:include href="ref/installation.xml" />
-    </chapter>
+    </part>
+
+    <part id="learning">
+        <title>Learning Zend Framework</title>
+
+        <chapter id="learning.quickstart">
+            <title>Zend Framework Quick Start</title>
+            <xi:include href="tutorials/quickstart-intro-mvc.xml" />
+            <xi:include href="tutorials/quickstart-create-project.xml" />
+            <xi:include href="tutorials/quickstart-create-layout.xml" />
+            <xi:include href="tutorials/quickstart-create-model.xml" />
+            <xi:include href="tutorials/quickstart-create-form.xml" />
+            <xi:include href="tutorials/quickstart-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.autoloading">
+            <title>Autoloading in Zend Framework</title>
+            <xi:include href="tutorials/autoloading-intro.xml" />
+            <xi:include href="tutorials/autoloading-design.xml" />
+            <xi:include href="tutorials/autoloading-usage.xml" />
+            <xi:include href="tutorials/autoloading-resources.xml" />
+            <xi:include href="tutorials/autoloading-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.plugins">
+            <title>Plugins in Zend Framework</title>
+            <xi:include href="tutorials/plugins-intro.xml" />
+            <xi:include href="tutorials/plugins-usage.xml" />
+            <xi:include href="tutorials/plugins-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.layout">
+            <title>Getting Started with Zend_Layout</title>
+            <xi:include href="tutorials/layout-intro.xml" />
+            <xi:include href="tutorials/layout-usage.xml" />
+            <xi:include href="tutorials/layout-conclusions.xml" />
+        </chapter>
+
+        <chapter id="learning.view.placeholders">
+            <title>Getting Started with Zend_View Placeholders</title>
+            <xi:include href="tutorials/view-placeholders-intro.xml" />
+            <xi:include href="tutorials/view-placeholders-basics.xml" />
+            <xi:include href="tutorials/view-placeholders-standard.xml" />
+            <xi:include href="tutorials/view-placeholders-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.form.decorators">
+            <title>Understanding and Using Zend Form Decorators</title>
+            <xi:include href="tutorials/form-decorators-intro.xml" />
+            <xi:include href="tutorials/form-decorators-simplest.xml" />
+            <xi:include href="tutorials/form-decorators-layering.xml" />
+            <xi:include href="tutorials/form-decorators-individual.xml" />
+            <xi:include href="tutorials/form-decorators-composite.xml" />
+            <xi:include href="tutorials/form-decorators-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.multiuser">
+            <title>Getting Started with Zend_Session, Zend_Auth, and Zend_Acl</title>
+            <xi:include href="tutorials/multiuser-intro.xml" />
+            <xi:include href="tutorials/multiuser-sessions.xml" />
+            <xi:include href="tutorials/multiuser-authentication.xml" />
+            <xi:include href="tutorials/multiuser-authorization.xml" />
+        </chapter>
+
+        <chapter id="learning.lucene">
+            <title>Getting Started with Zend_Search_Lucene</title>
+            <xi:include href="tutorials/lucene-intro.xml" />
+            <xi:include href="tutorials/lucene-index-structure.xml" />
+            <xi:include href="tutorials/lucene-index-opening.xml" />
+            <xi:include href="tutorials/lucene-indexing.xml" />
+            <xi:include href="tutorials/lucene-searching.xml" />
+            <xi:include href="tutorials/lucene-queries.xml" />
+            <xi:include href="tutorials/lucene-pagination.xml" />
+        </chapter>
+        
+        <chapter id="learning.paginator">
+            <title>Getting Started with Zend_Paginator</title>
+            <xi:include href="tutorials/paginator-intro.xml" />
+            <xi:include href="tutorials/paginator-simple.xml" />
+            <xi:include href="tutorials/paginator-control.xml" />
+            <xi:include href="tutorials/paginator-together.xml" />
+        </chapter>
+
+        <!--
+        Include new tutorials like this:
+        <chapter id="learning.foo">
+            <xi:include href="tutorials/foobar.xml" />
+        </chapter>
+
+        Thus, if a tutorial consists of multiple sections, they can still be
+        grouped within the same chapter.
+        -->
+    </part>
+
+    <part id="reference">
+        <title>Zend Framework Reference</title>
 
     <chapter id="zend.acl">
         <title>Zend_Acl</title>
@@ -489,10 +584,7 @@
         <xi:include href="module_specs/Zend_Service_StrikeIron-AdvancedUses.xml" />
         <xi:include href="module_specs/Zend_Service_Technorati.xml" />
         <xi:include href="module_specs/Zend_Service_Twitter.xml" parse="xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Blob.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Table.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Queue.xml" />
+        <xi:include href="module_specs/Zend_Service_WindowsAzure.xml" parse="xml" />
         <xi:include href="module_specs/Zend_Service_Yahoo.xml" />
     </chapter>
 
@@ -607,6 +699,8 @@
         <xi:include href="module_specs/Zend_XmlRpc_Client.xml" />
         <xi:include href="module_specs/Zend_XmlRpc_Server.xml" />
     </chapter>
+    </part>
+
     <xi:include href="ref/requirements.xml" />
     <appendix id="migration">
         <title>Zend Framework Migration Notes</title>

+ 6 - 2
documentation/manual/en/module_specs/Zend_Service_WindowsAzure.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure">
+<sect1 id="zend.service.windowsazure" xmlns:xi="http://www.w3.org/2001/XInclude">
     <title>Zend_Service_WindowsAzure</title>
 
     <sect2 id="zend.service.windowsazure.introduction">
@@ -111,5 +111,9 @@
             Windows Azure's features, no matter if it is hosted on the Windows Azure platform or on
             an in-premise web server.
         </para>
-  </sect2>
+    </sect2>
+
+    <xi:include href="Zend_Service_WindowsAzure_Blob.xml" />
+    <xi:include href="Zend_Service_WindowsAzure_Table.xml" />
+    <xi:include href="Zend_Service_WindowsAzure_Queue.xml" />
 </sect1>

+ 27 - 27
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Blob.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.blob">
+<sect2 id="zend.service.windowsazure.storage.blob">
     <title>Zend_Service_WindowsAzure_Storage_Blob</title>
 
     <para>
@@ -15,7 +15,7 @@
         order to provide a native PHP interface to the storage account.
     </para>
 
-    <sect2 id="zend.service.windowsazure.storage.blob.api">
+    <sect3 id="zend.service.windowsazure.storage.blob.api">
         <title>API Examples</title>
 
         <para>
@@ -25,7 +25,7 @@
             features.
         </para>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.create-container">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.create-container">
             <title>Creating a storage container</title>
 
             <para>
@@ -43,9 +43,9 @@ $result = $storageClient->createContainer('testcontainer');
 echo 'Container name is: ' . $result->Name;
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.delete-container">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.delete-container">
             <title>Deleting a storage container</title>
 
             <para>
@@ -61,9 +61,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Blob();
 $storageClient->deleteContainer('testcontainer');
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.storing-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.storing-blob">
             <title>Storing a blob</title>
 
             <para>
@@ -85,9 +85,9 @@ $result = $storageClient->putBlob(
 echo 'Blob name is: ' . $result->Name;
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.copy-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.copy-blob">
             <title>Copying a blob</title>
 
             <para>
@@ -111,9 +111,9 @@ $result = $storageClient->copyBlob(
 echo 'Copied blob name is: ' . $result->Name;
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.download-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.download-blob">
             <title>Downloading a blob</title>
 
             <para>
@@ -134,9 +134,9 @@ $storageClient->getBlob(
 );
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.public-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.public-blob">
             <title>Making a blob publicly available</title>
 
             <para>
@@ -161,10 +161,10 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Blob();
 $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Storage_Blob::ACL_PUBLIC);
 ]]></programlisting>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
   
-    <sect2 id="zend.service.windowsazure.storage.blob.root">
+    <sect3 id="zend.service.windowsazure.storage.blob.root">
         <title>Root container</title>
 
         <para>
@@ -179,9 +179,9 @@ $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Stora
             <varname>$root</varname>.  All other operations on the root container should be issued
             with the container name set to <varname>$root</varname>.
         </para>
-    </sect2>
+    </sect3>
   
-    <sect2 id="zend.service.windowsazure.storage.blob.wrapper">
+    <sect3 id="zend.service.windowsazure.storage.blob.wrapper">
         <title>Blob storage stream wrapper</title>
 
         <para>
@@ -189,7 +189,7 @@ $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Stora
             client as a PHP file stream wrapper. The blob storage stream wrapper provides
             support for using regular file operations on Windows Azure Blob Storage.
             For example, one can open a file from Windows Azure Blob Storage with
-            the <functionname>fopen()</functionname> function:
+            the <modulename>fopen()</modulename> function:
         </para>
 
         <example id="zend.service.windowsazure.storage.blob.wrapper.sample">
@@ -227,9 +227,9 @@ $storageClient->registerStreamWrapper('blob://'); // regiters blob:// on this st
             To unregister the stream wrapper, the <methodname>unregisterStreamWrapper()</methodname>
             method can be used.
         </para>
-    </sect2>
+    </sect3>
 
-    <sect2 id="zend.service.windowsazure.storage.blob.sharedaccesssig">
+    <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig">
         <title>Shared Access Signature</title>
 
         <para>
@@ -254,7 +254,7 @@ http://phpstorage.blob.core.windows.net/phpazuretestshared1?st=2009-08-17T09%3A0
             container of the "phpstorage" account.
         </para>
     
-        <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig.generate">
+        <sect4 id="zend.service.windowsazure.storage.blob.sharedaccesssig.generate">
             <title>Generating a Shared Access Signature</title>
 
             <para>
@@ -307,9 +307,9 @@ $sharedAccessUrl = storageClient->generateSharedAccessUrl(
 );
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
         
-        <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig.consume">
+        <sect4 id="zend.service.windowsazure.storage.blob.sharedaccesssig.consume">
             <title>Working with Shared Access Signatures from others</title>
 
             <para>
@@ -354,6 +354,6 @@ $storageClient->putBlob(
                 available for the latter, the Windows Azure SDK for PHP chose those credentials to
                 perform the request on Windows Azure blob storage.
             </para>
-        </sect3>
-    </sect2>
-</sect1>
+        </sect4>
+    </sect3>
+</sect2>

+ 14 - 14
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Queue.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.queue">
+<sect2 id="zend.service.windowsazure.storage.queue">
     <title>Zend_Service_WindowsAzure_Storage_Queue</title>
 
     <para>
@@ -22,7 +22,7 @@
         provide a native PHP interface to the storage account.
     </para>
 
-    <sect2 id="zend.service.windowsazure.storage.queue.api">
+    <sect3 id="zend.service.windowsazure.storage.queue.api">
         <title>API Examples</title>
 
         <para>
@@ -32,7 +32,7 @@
             features.
         </para>
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.create-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.create-queue">
             <title>Creating a queue</title>
 
             <para>
@@ -49,9 +49,9 @@ $result = $storageClient->createQueue('testqueue');
 echo 'Queue name is: ' . $result->Name;
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.delete-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.delete-queue">
             <title>Deleting a queue</title>
 
             <para>
@@ -66,9 +66,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Queue();
 $storageClient->deleteQueue('testqueue');
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.storing-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.storing-queue">
             <title>Adding a message to a queue</title>
 
             <para>
@@ -86,9 +86,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Queue();
 $storageClient->putMessage('testqueue', 'This is a test message', 3600); 
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.read-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.read-queue">
             <title>Reading a message from a queue</title>
 
             <para>
@@ -135,9 +135,9 @@ foreach ($messages as $message) {
 }
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.peek-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.peek-queue">
             <title>Check if there are messages in a queue</title>
 
             <para>
@@ -166,6 +166,6 @@ foreach ($messages as $message) {
                 <methodname>deleteMessage()</methodname> method.  To do this, use
                 <methodname>getMessages()</methodname> instead.
             </para>
-        </sect3>
-    </sect2>
-</sect1>
+        </sect4>
+    </sect3>
+</sect2>

+ 32 - 32
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Table.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.table">
+<sect2 id="zend.service.windowsazure.storage.table">
     <title>Zend_Service_WindowsAzure_Storage_Table</title>
 
     <para>
@@ -26,14 +26,14 @@
         Azure production table storage.
     </para>
 
-    <sect2 id="zend.service.windowsazure.storage.table.api">
+    <sect3 id="zend.service.windowsazure.storage.table.api">
         <title>Operations on tables</title>
 
         <para>
             This topic lists some samples of operations that can be executed on tables.
         </para>
 
-        <sect3 id="zend.service.windowsazure.storage.table.api.create">
+        <sect4 id="zend.service.windowsazure.storage.table.api.create">
             <title>Creating a table</title>
 
             <para>
@@ -53,9 +53,9 @@ $result = $storageClient->createTable('testtable');
 echo 'New table name is: ' . $result->Name;
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.table.api.list">
+        <sect4 id="zend.service.windowsazure.storage.table.api.list">
             <title>Listing all tables</title>
 
             <para>
@@ -76,10 +76,10 @@ foreach ($result as $table) {
 }
 ]]></programlisting>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
 
-    <sect2 id="zend.service.windowsazure.storage.table.entities">
+    <sect3 id="zend.service.windowsazure.storage.table.entities">
         <title>Operations on entities</title>
 
         <para>
@@ -147,7 +147,7 @@ class SampleEntity extends Zend_Service_WindowsAzure_Storage_TableEntity
             <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname>.
         </para>
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.enforced">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.enforced">
             <title>Enforced schema entities</title>
 
             <para>
@@ -323,16 +323,16 @@ public $Age;
                     </para>
                 </listitem>
             </itemizedlist>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.dynamic">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.dynamic">
             <title>No enforced schema entities (a.k.a. DynamicEntity)</title>
 
             <para>
                 To use the <classname>Zend_Service_WindowsAzure_Storage_Table</classname> class
                 without defining a schema, you can make use of the
                 <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname> class.
-                This class inherits <classname>Zend_Service_WindowsAzure_Storage_TableEntity</code>
+                This class inherits <classname>Zend_Service_WindowsAzure_Storage_TableEntity</classname>
                 like an enforced schema class does, but contains additional logic to make it dynamic
                 and not bound to a schema.
             </para>
@@ -414,12 +414,12 @@ $target->setAzurePropertyType('Age', 'Edm.Int64');
                 <classname>Zend_Service_WindowsAzure_Storage_TableEntity</classname> if no specific
                 class is passed into Table Storage methods.
             </para>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.api">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.api">
             <title>Entities API examples</title>
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.insert">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.insert">
                 <title>Inserting an entity</title>
 
                 <para>
@@ -446,9 +446,9 @@ echo 'Timestamp: ' . $result->getTimestamp() . "\n";
 echo 'Etag: ' . $result->getEtag() . "\n";
 ]]></programlisting>
                 </example>
-            </sect4>
+            </sect5>
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.retrieve-by-id">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.retrieve-by-id">
                 <title>Retrieving an entity by partition key and row key</title>
 
                 <para>
@@ -468,9 +468,9 @@ $entity= $storageClient->retrieveEntityById(
 );
 ]]></programlisting>
                 </example>
-            </sect4>
+            </sect5>
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.updating">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.updating">
                 <title>Updating an entity</title>
 
                 <para>
@@ -518,9 +518,9 @@ $entity->Name = 'New name';
 $result = $storageClient->updateEntity('testtable', $entity, true); 
 ]]></programlisting>
                 </example>
-            </sect4>
+            </sect5>
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.delete">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.delete">
                 <title>Deleting an entity</title>
 
                 <para>
@@ -541,10 +541,10 @@ $entity = $storageClient->retrieveEntityById(
 $result = $storageClient->deleteEntity('testtable', $entity);
 ]]></programlisting>
                 </example>
-            </sect4>
-        </sect3>
+            </sect5>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.querying">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.querying">
             <title>Performing queries</title>
 
             <para>
@@ -618,9 +618,9 @@ foreach ($entities as $entity) {
 }
 ]]></programlisting>
             </example>
-        </sect3>
+        </sect4>
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.batch">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.batch">
             <title>Batch operations</title>
 
             <para>
@@ -657,10 +657,10 @@ foreach ($entities as $entity) {
 $batch->commit();
 ]]></programlisting>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
   
-    <sect2 id="zend.service.windowsazure.storage.table.sessionhandler">
+    <sect3 id="zend.service.windowsazure.storage.table.sessionhandler">
         <title>Table storage session handler</title>
 
         <para>
@@ -730,9 +730,9 @@ if (!isset($_SESSION['firstVisit'])) {
         <warning>
             <para>
                 The <classname>Zend_Service_WindowsAzure_SessionHandler</classname> session handler
-                should be registered before a call to <functionname>session_start()</functionname>
+                should be registered before a call to <modulename>session_start()</modulename>
                 is made!
             </para>
         </warning>
-    </sect2>
-</sect1>
+    </sect3>
+</sect2>

+ 20 - 0
documentation/manual/en/tutorials/autoloading-conclusion.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Zend Framework encourages the use of autoloading, and even initializes it by default in
+        <classname>Zend_Application</classname>. Hopefully this tutorial provides you with the
+        information you need to use <classname>Zend_Loader_Autoloader</classname> to its best
+        advantage, as well as extend its capabilities by attaching custom autoloaders or resource
+        autoloaders.
+    </para>
+
+    <para>
+        For more information on its usage, read the <link
+            linkend="zend.loader.autoloader">Zend_Loader_Autoloader</link> and <link
+            linkend="zend.loader.autoloader-resources">Zend_Loader_Autoloader_Resource</link> manual
+        pages.
+    </para>
+</sect1>

+ 101 - 0
documentation/manual/en/tutorials/autoloading-design.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.design">
+    <title>Goals and Design</title>
+
+    <sect2 id="learning.autoloading.design.naming">
+        <title>Class Naming Conventions</title>
+
+        <para>
+            To understand autoloading in Zend Framework, first you need to understand the relationship
+            between class names and class files.
+        </para>
+
+        <para>
+            Zend Framework has borrowed an idea from <ulink url="http://pear.php.net/">PEAR</ulink>,
+            whereby class names have a 1:1 relationship with the filesystem. Simply put, the underscore
+            character ("_") is replaced by a directory separator in order to resolve the path to the
+            file, and then the suffix ".php" is added. For example, the class "Foo_Bar_Baz" would
+            correspond to "Foo/Bar/Baz.php" on the filesystem. The assumption is also that the classes
+            may be resolved via PHP's <constant>include_path</constant> setting, which allows both
+            <methodname>include</methodname> and <methodname>require</methodname> to find the filename
+            via a relative path lookup on the <constant>include_path</constant>.
+        </para>
+
+        <para>
+            Additionally, per PEAR as well as the <ulink
+                url="http://php.net/userlandnaming.tips">PHP project</ulink>, we use and recommend
+            using a vendor or project prefix for your code. What this means is that all classes you
+            write will share a common class prefix; for example, all code in Zend Framework has the
+            prefix "Zend_". This naming convention helps prevent naming collisions. Within Zend
+            Framework, we often refer to this as the "namespace" prefix; be careful not to confuse
+            it with PHP's native namespace implementation.
+        </para>
+
+        <para>
+            Zend Framework follows these simple rules internally, and our coding standards encourage
+            that you do so as well for all library code.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.autoloading.design.autoloader">
+        <title>Autoloader Conventions and Design</title>
+
+        <para>
+            Zend Framework's autoloading support, provided primarily via
+            <classname>Zend_Loader_Autoloader</classname>, has the following goals and design elements:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis role="strong">Provide namespace matching.</emphasis> If the class
+                    namespace prefix is not in a list of registered namespaces, return false
+                    immediately. This allows for more optimistic matching, as well as fallback to other
+                    autoloaders.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow the autoloader to act as a fallback
+                        autoloader.</emphasis> In the case where a team may be widely distributed, or
+                    using an undetermined set of namespace prefixes, the autoloader should still be
+                    configurable such that it will attempt to match any namespace prefix. It will be
+                    noted, however, that this practice is not recommended, as it can lead to unnecessary
+                    lookups.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow toggling error suppression.</emphasis> We feel -- and
+                    the greater PHP community does as well -- that error suppression is a bad idea. It's
+                    expensive, and it masks very real application problems. So, by default, it should be
+                    off. However, if a developer <emphasis>insists</emphasis> that it be on, we allow
+                    toggling it on.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow specifying custom callbacks for
+                        autoloading.</emphasis> Some developers don't want to use
+                    <methodname>Zend_Loader::loadClass()</methodname> for autoloading, but still want to
+                    make use of ZF's mechanisms. <classname>Zend_Loader_Autoloader</classname> allows
+                    specyfing an alternate callback for autoloading.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow manipulation of the SPL autoload callback
+                        chain.</emphasis> The purpose of this is to allow specifying additional
+                    autoloaders -- for instance, resource loaders for classes that don't have a 1:1
+                    mapping to the filesystem -- to be registered before or after the primary ZF
+                    autoloader.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+</sect1>

+ 32 - 0
documentation/manual/en/tutorials/autoloading-intro.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.intro">
+    <title>Introduction</title>
+
+    <para>
+        Autoloading is a mechanism that eliminates the need to manually require dependencies within
+        your PHP code. Per <ulink url="http://php.net/autoload">the PHP autoload manual</ulink>,
+        once an autoloader has been defined, it "is automatically called in case you are trying to use
+        a class/interface which hasn't been defined yet."
+    </para>
+
+    <para>
+        Using autoloading, you do not need to worry about <emphasis>where</emphasis> a class exists
+        in your project. With well-defined autoloaders, you do not need to worry about where a class
+        file is relative to the current class file; you simply use the class, and the autoloader
+        will perform the file lookup.
+    </para>
+
+    <para>
+        Additionally, autoloading, because it defers loading to the last possible moment and ensures
+        that a match only has to occur once, can be a huge performance boost -- particularly if you
+        take the time to strip out <methodname>require_once()</methodname> calls before you move
+        to deployment.
+    </para>
+
+    <para>
+        Zend Framework encourages the use of autoloading, and provides several tools to provide
+        autoloading of both library code as well as application code. This tutorial covers these
+        tools, as well as how to use them effectively.
+    </para>
+</sect1>

+ 106 - 0
documentation/manual/en/tutorials/autoloading-resources.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.resources">
+    <title>Resource Autoloading</title>
+
+    <para>
+        Often, when developing an application, it's either difficult to package classes in the 1:1
+        classname:filename standard Zend Framework recommends, or it's advantageous for purposes of
+        packaging not to do so. However, this means you class files will not be found by the
+        autoloader.
+    </para>
+
+    <para>
+        If you read through <link linkend="learning.autoloading.design">the design goals</link> for
+        the autoloader, the last point in that section indicated that the solution should cover this
+        situation. Zend Framework does so with
+        <classname>Zend_Loader_Autoloader_Resource</classname>.
+    </para>
+
+    <para>
+        A resource is just a name that corresponds to a component namespace (which
+        is appended to the autoloader's namespace) and a path (which is relative to
+        the autoloader's base path). In action, you'd do something like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader = new Zend_Application_Module_Autoloader(array(
+    'namespace' => 'Blog',
+    'basePath'  => APPLICATION_PATH . '/modules/blog',
+));
+]]></programlisting>
+
+    <para>
+        Once you have the loader in place, you then need to inform it of the various resource types
+        it's aware of. These resource types are simply subtree/prefix pairs.
+    </para>
+
+    <para>
+        As an example, consider the following tree:
+    </para>
+
+    <programlisting language="text"><![CDATA[
+path/to/some/resources/
+|-- forms/
+|   `-- Guestbook.php        // Foo_Form_Guestbook
+|-- models/
+|   |-- DbTable/
+|   |   `-- Guestbook.php    // Foo_Model_DbTable_Guestbook
+|   |-- Guestbook.php        // Foo_Model_Guestbook
+|   `-- GuestbookMapper.php  // Foo_Model_GuestbookMapper
+]]></programlisting>
+
+    <para>
+        Our first step is creating the resource loader:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader = new Zend_Loader_Autoloader_Resource(array(
+    'basePath'  => 'path/to/some/resources/',
+    'namespace' => 'Foo',
+));
+]]></programlisting>
+
+    <para>
+        Next, we need to define some resource types.
+        <methodname>Zend_Loader_Autoloader_Resourse::addResourceType()</methodname> has three
+        arguments: the "type" of resource (an arbitrary string), the path under the base path in
+        which the resource type may be found, and the component prefix to use for the resource type.
+        In the above tree, we have three resource types: form (in the subdirectory "forms", with a
+        component prefix of "Form"), model (in the subdirectory "models", with a component prefix of
+        "Model"), and dbtable (in the subdirectory "models/DbTable, with a component prefix of
+        "Model_DbTable"). We'd define them as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader->addResourceType('form', 'forms', 'Form')
+       ->addResourceType('model', 'models', 'Model')
+       ->addResourceType('dbtable', 'models/DbTable', 'Model_DbTable');
+]]></programlisting>
+
+    <para>
+        Once defined, we can simply use these classes:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$form      = new Foo_Form_Guestbook();
+$guestbook = new Foo_Model_Guestbook();
+]]></programlisting>
+
+    <note>
+        <title>Module Resource Autoloading</title>
+
+        <para>
+            Zend Framework's MVC layer encourages the use of "modules", which are self-contained
+            applications within your site. Modules typically have a number of resource types by
+            default, and Zend Framework even <link linkend="project-structure.filesystem">recommends
+                a standard directory layout for modules</link>. Resource autoloaders are therefore
+            quite useful in this paradigm -- so useful that they are enabled by default when you
+            create a bootstrap class for your module that extends
+            <classname>Zend_Application_Module_Bootstrap</classname>. For more information, read
+            the <link
+                linkend="zend.loader.autoloader-resource.module">Zend_Loader_Autoloader_Module
+                documentation</link>.
+        </para>
+    </note>
+</sect1>

+ 158 - 0
documentation/manual/en/tutorials/autoloading-usage.xml

@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.usage">
+    <title>Basic Autoloader Usage</title>
+
+    <para>
+        Now that we have an understanding of what autoloading is and the goals and design of Zend
+        Framework's autoloading solution, let's look at how to use
+        <classname>Zend_Loader_Autoloader</classname>.
+    </para>
+
+    <para>
+        In the simplest case, you would simply require the class, and then instantiate it. Since
+        <classname>Zend_Loader_Autoloader</classname> is a singleton (due to the fact that the SPL
+        autoloader is a single resource), we use <methodname>getInstance()</methodname> to retrieve
+        an instance.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/Autoloader.php';
+Zend_Loader_Autoloader::getInstance();
+]]></programlisting>
+
+    <para>
+        By default, this will allow loading any classes with the class namespace prefixes of "Zend_"
+        or "ZendX_", as long as they are on your <constant>include_path</constant>.
+    </para>
+
+    <para>
+        What happens if you have other namespace prefixes you wish to use? The best, and simplest,
+        way is to call the <methodname>registerNamespace()</methodname> method on the instance. You
+        can pass a single namespace prefix, or an array of them:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/Autoloader.php';
+$loader = Zend_Loader_Autoloader::getInstance();
+$loader->registerNamespace('Foo_');
+$loader->registerNamespace(array('Foo_', 'Bar_'));
+]]></programlisting>
+
+    <para>
+        Alternately, you can tell <classname>Zend_Loader_Autoloader</classname> to act as a
+        "fallback" autoloader. This means that it will try to resolve any class regardless of
+        namespace prefix.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader->setFallbackAutoloader(true);
+]]></programlisting>
+
+    <warning>
+        <title>Do not use as a fallback autoloader</title>
+
+        <para>
+            While it's tempting to use <classname>Zend_Loader_Autoloader</classname> as a fallback
+            autoloader, we do not recommend the practice.
+        </para>
+
+        <para>
+            Internally, <classname>Zend_Loader_Autoloader</classname> uses
+            <methodname>Zend_Loader::loadClass()</methodname> to load classes. That method uses
+            <methodname>include()</methodname> to attempt to load the given class file.
+            <methodname>include()</methodname> will return a boolean <constant>false</constant>
+            if not successful -- but also issues a PHP warning. This latter fact can lead to some
+            issues:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    If <constant>display_errors</constant> is enabled, the warning will be included
+                    in output.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    Depending on the <constant>error_reporting</constant> level you have chosen, it
+                    could also clutter your logs.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            You can suppress the error messages (the <classname>Zend_Loader_Autoloader</classname>
+            documentation details this), but note that the suppression is only relevant when
+            <constant>display_errors</constant> is enabled; the error log will always display the
+            messages. For these reasons, we recommend always configuring the namespace prefixes the
+            autoloader should be aware of
+        </para>
+    </warning>
+
+    <note>
+        <title>Namespace Prefixes vs PHP Namespaces</title>
+
+        <para>
+            At the time this is written, PHP 5.3 has been released. With that version, PHP now has
+            official namespace support.
+        </para>
+
+        <para>
+            However, Zend Framework predates PHP 5.3, and thus namespaces. Within Zend Framework,
+            when we refer to "namespaces", we are referring to a practice whereby classes are
+            prefixed with a vender "namespace". As an example, all Zend Framework class names are
+            prefixed with "Zend_" -- that is our vendor "namespace".
+        </para>
+
+        <para>
+            Zend Framework plans to offer native PHP namespace support to the autoloader in future
+            revisions, and its own library will utilize namespaces starting with version 2.0.0.
+        </para>
+    </note>
+
+    <para>
+        If you have a custom autoloader you wish to use with Zend Framework -- perhaps an autoloader
+        from a third-party library you are also using -- you can manage it with
+        <classname>Zend_Loader_Autoloader</classname>'s <methodname>pushAutoloader()</methodname>
+        and <methodname>unshiftAutoloader()</methodname> methods. These methods will append or
+        prepend, respectively, autoloaders to a chain that is called prior to executing ZF's
+        internal autoloading mechanism. This approach offers the following benefits:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Each method takes an optional second argument, a class namespace prefix. This can be
+                used to indicate that the given autoloader should only be used when looking up
+                classes with that given class prefix. If the class being resolved does not have that
+                prefix, the autoloader will be skipped -- which can lead to performance
+                improvements.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                If you need to manipulate <methodname>spl_autoload</methodname>'s registry, any
+                autoloaders that are callbacks pointing to instance methods can pose issues, as
+                <methodname>spl_autoload_functions()</methodname> does not return the exact same
+                callbacks. <classname>Zend_Loader_Autoloader</classname> has no such limitation.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Autoloaders managed this way may be any valid PHP callback.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// Append function 'my_autoloader' to the stack, 
+// to manage classes with the prefix 'My_':
+$loader->pushAutoloader('my_autoloader', 'My_');
+
+// Prepend static method Foo_Loader::autoload() to the stack, 
+// to manage classes with the prefix 'Foo_':
+$loader->unshiftAutoloader(array('Foo_Loader', 'autoload'), 'Foo_');
+]]></programlisting>
+</sect1>

+ 384 - 0
documentation/manual/en/tutorials/form-decorators-composite.xml

@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.composite">
+    <title>Creating and Rendering Composite Elements</title>
+
+    <para>
+        In <link linkend="learning.form.decorators.individual">the last section</link>, we had an
+        example showing a "date of birth element":
+    </para>
+
+
+    <programlisting language="php"><![CDATA[
+<div class="element">
+    <?php echo $form->dateOfBirth->renderLabel() ?>
+    <?php echo $this->formText('dateOfBirth[day]', '', array(
+        'size' => 2, 'maxlength' => 2)) ?>
+    /
+    <?php echo $this->formText('dateOfBirth[month]', '', array(
+        'size' => 2, 'maxlength' => 2)) ?>
+    /
+    <?php echo $this->formText('dateOfBirth[year]', '', array(
+        'size' => 4, 'maxlength' => 4)) ?>
+</div>
+]]></programlisting>
+
+    <para>
+        How might you represent this element as a <classname>Zend_Form_Element</classname>?
+        How might you write a decorator to render it?
+    </para>
+
+    <sect2 id="learning.form.decorators.composite.element">
+        <title>The Element</title>
+
+        <para>
+            The questions about how the element would work include:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    How would you set and retrieve the value?
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    How would you validate the value?
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    Regardless, how would you then allow for discrete form inputs for the three
+                    segments (day, month, year)?
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            The first two questions center around the form element itself: how would
+            <methodname>setValue()</methodname> and <methodname>getValue()</methodname> work?
+            There's actually another question implied by the question about the decorator: how would
+            you retrieve the discrete date segments from the element and/or set them?
+        </para>
+
+        <para>
+            The solution is to override the <methodname>setValue()</methodname> method of your
+            element to provide some custom logic. In this particular case, our element should have
+            three discrete behaviors:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    If an integer timestamp is provided, it should be used to determine and store
+                    the day, month, and year.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    If a textual string is provided, it should be cast to a timestamp, and then that
+                    value used to determine and store the day, month, and year.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    If an array containing keys for date, month, and year is provided, those values
+                    should be stored.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            Internally, the day, month, and year will be stored discretely. When the value of the
+            element is retrieved, it will be done so in a normalized string format. We'll override
+            <methodname>getValue()</methodname> as well to assemble the discrete date segments into
+            a final string.
+        </para>
+
+        <para>
+            Here's what the class would look like:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    protected $_dateFormat = '%year%-%month%-%day%';
+    protected $_day;
+    protected $_month;
+    protected $_year;
+
+    public function setDay($value)
+    {
+        $this->_day = (int) $value;
+        return $this;
+    }
+
+    public function getDay()
+    {
+        return $this->_day;
+    }
+
+    public function setMonth($value)
+    {
+        $this->_month = (int) $value;
+        return $this;
+    }
+
+    public function getMonth()
+    {
+        return $this->_month;
+    }
+
+    public function setYear($value)
+    {
+        $this->_year = (int) $value;
+        return $this;
+    }
+
+    public function getYear()
+    {
+        return $this->_year;
+    }
+
+    public function setValue($value)
+    {
+        if (is_int($value)) {
+            $this->setDay(date('d', $value))
+                 ->setMonth(date('m', $value))
+                 ->setYear(date('Y', $value));
+        } elseif (is_string($value)) {
+            $date = strtotime($value);
+            $this->setDay(date('d', $date))
+                 ->setMonth(date('m', $date))
+                 ->setYear(date('Y', $date));
+        } elseif (is_array($value)
+                  && (isset($value['day']) 
+                      && isset($value['month']) 
+                      && isset($value['year'])
+                  )
+        ) {
+            $this->setDay($value['day'])
+                 ->setMonth($value['month'])
+                 ->setYear($value['year']);
+        } else {
+            throw new Exception('Invalid date value provided');
+        }
+
+        return $this;
+    }
+
+    public function getValue()
+    {
+        return str_replace(
+            array('%year%', '%month%', '%day%'),
+            array($this->getYear(), $this->getMonth(), $this->getDay()),
+            $this->_dateFormat
+        );
+    }
+}
+]]></programlisting>
+
+        <para>
+            This class gives some nice flexibility -- we can set default values from our database, and
+            be certain that the value will be stored and represented correctly.  Additionally, we can
+            allow for the value to be set from an array passed via form input. Finally, we have discrete
+            accessors for each date segment, which we can now use in a decorator to create a composite
+            element.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.composite.decorator">
+        <title>The Decorator</title>
+
+        <para>
+            Revisiting the example from the last section, let's assume that we want users to input
+            each of the year, month, and day separately. PHP fortunately allows us to use array
+            notation when creating elements, so it's still possible to capture these three entities
+            into a single value -- and we've now created a <classname>Zend_Form</classname> element
+            that can handle such an array value.
+        </para>
+
+        <para>
+            The decorator is relatively simple: it will grab the day, month, and year from the
+            element, and pass each to a discrete view helper to render individual form inputs; these
+            will then be aggregated to form the final markup.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract
+{
+    public function render($content)
+    {
+        $element = $this->getElement();
+        if (!$element instanceof My_Form_Element_Date) {
+            // only want to render Date elements
+            return $content;
+        }
+
+        $view = $element->getView();
+        if (!$view instanceof Zend_View_Interface) {
+            // using view helpers, so do nothing if no view present
+            return $content;
+        }
+
+        $day   = $element->getDay();
+        $month = $element->getMonth();
+        $year  = $element->getYear();
+        $name  = $element->getFullyQualifiedName();
+
+        $params = array(
+            'size'      => 2,
+            'maxlength' => 2,
+        );
+        $yearParams = array(
+            'size'      => 4,
+            'maxlength' => 4,
+        );
+
+        $markup = $view->formText($name . '[day]', $day, $params)
+                . ' / ' . $view->formText($name . '[month]', $month, $params)
+                . ' / ' . $view->formText($name . '[year]', $year, $yearParams);
+
+        switch ($this->getPlacement()) {
+            case self::PREPEND:
+                return $markup . $this->getSeparator() . $content;
+            case self::APPEND:
+            default:
+                return $content . $this->getSeparator() . $markup;
+        }
+    }
+}
+]]></programlisting>
+
+    <para>
+        We now have to do a minor tweak to our form element, and tell it that we want to use the
+        above decorator as a default. That takes two steps. First, we need to inform the element of
+        the decorator path. We can do that in the constructor:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    // ...
+
+    public function __construct($spec, $options = null)
+    {
+        $this->addPrefixPath(
+            'My_Form_Decorator', 
+            'My/Form/Decorator', 
+            'decorator'
+        );
+        parent::__construct($spec, $options);
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        Note that this is being done in the constructor and not in <methodname>init()</methodname>.
+        This is for two reasons. First, it allows extending the element later to add logic in
+        <methodname>init</methodname> without needing to worry about calling
+        <methodname>parent::init()</methodname>. Second, it allows passing additional plugin paths
+        via configuration or within an <methodname>init</methodname> method that will then allow
+        overriding the default <classname>Date</classname> decorator with my own replacement.
+    </para>
+
+    <para>
+        Next, we need to override the <methodname>loadDefaultDecorators()</methodname> method to use
+        our new <classname>Date</classname> decorator:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    // ...
+
+    public function loadDefaultDecorators()
+    {
+        if ($this->loadDefaultDecoratorsIsDisabled()) {
+            return;
+        }
+
+        $decorators = $this->getDecorators();
+        if (empty($decorators)) {
+            $this->addDecorator('Date')
+                 ->addDecorator('Errors')
+                 ->addDecorator('Description', array(
+                     'tag'   => 'p', 
+                     'class' => 'description'
+                 ))
+                 ->addDecorator('HtmlTag', array(
+                     'tag' => 'dd',
+                     'id'  => $this->getName() . '-element'
+                 ))
+                 ->addDecorator('Label', array('tag' => 'dt'));
+        }
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        What does the final output look like? Let's consider the following element:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$d = new My_Form_Element_Date('dateOfBirth');
+$d->setLabel('Date of Birth: ')
+  ->setView(new Zend_View());
+
+// These are equivalent:
+$d->setValue('20 April 2009');
+$d->setValue(array('year' => '2009', 'month' => '04', 'day' => '20'));
+]]></programlisting>
+
+    <para>
+        If you then echo this element, you get the following markup (with some slight whitespace
+        modifications for readability):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<dt id="dateOfBirth-label"><label for="dateOfBirth" class="optional">
+    Date of Birth:
+</label></dt>
+<dd id="dateOfBirth-element">
+    <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day" 
+        value="20" size="2" maxlength="2"> / 
+    <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
+        value="4" size="2" maxlength="2"> / 
+    <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
+        value="2009" size="4" maxlength="4">
+</dd>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.composite.conclusion">
+        <title>Conclusion</title>
+
+        <para>
+            We now have an element that can render multiple related form input fields, and then
+            handle the aggregated fields as a single entity -- the <varname>dateOfBirth</varname>
+            element will be passed as an array to the element, and the element will then, as we
+            noted earlier, create the appropriate date segments and return a value we can use for
+            most backends.
+        </para>
+
+        <para>
+            Additionally, we can use different decorators with the element. If we wanted to use a
+            <ulink url="http://dojotoolkit.org/">Dojo</ulink> <classname>DateTextBox</classname>
+            dijit decorator -- which accepts and returns string values -- we can, with no
+            modifications to the element itself.
+        </para>
+
+        <para>
+            In the end, you get a uniform element API you can use to describe an element
+            representing a composite value.
+        </para>
+    </sect2>
+</sect1>

+ 12 - 0
documentation/manual/en/tutorials/form-decorators-conclusion.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Form decorators are a system that takes some time to learn. At first, they will likely
+        feel cumbersome and overly complex. Hopefully the various topics covered in this chapter
+        will help you to understand both how they work, as well as strategies for using them
+        effectively in your forms.
+    </para>
+</sect1>

+ 283 - 0
documentation/manual/en/tutorials/form-decorators-individual.xml

@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.individual">
+    <title>Rendering Individual Decorators</title>
+
+    <para>
+        In the <link linkend="learning.form.decorators.layering">previous section</link>, we
+        looked at how you can combine decorators to create complex output. We noted that while you
+        have a ton of flexibility with this approach, it also adds some complexity and overhead. In
+        this section, we will examine how to render decorators individually in order to create
+        custom markup for forms and/or individual elements. 
+    </para>
+
+    <para>
+        Once you have registered your decorators, you can later retrieve them by name from the
+        element. Let's review the previous example:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput'
+        array('SimpleLabel', array('placement' => 'append')),
+    ),
+));
+]]></programlisting>
+
+    <para>
+        If we wanted to pull and render just the <classname>SimpleInput</classname> decorator, we
+        can do so using the <methodname>getDecorator()</methodname> method:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$decorator = $element->getDecorator('SimpleInput');
+echo $decorator->render('');
+]]></programlisting>
+
+    <para>
+        This is pretty easy, but it can be made even easier; let's do it in a single line:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+echo $element->getDecorator('SimpleInput')->render('');
+]]></programlisting>
+
+    <para>
+        Not too bad, but still a little complex. To make this easier, a shorthand notation was
+        introduced into <classname>Zend_Form</classname> in 1.7: you can render any registered
+        decorator by calling a method of the format <methodname>renderDecoratorName()</methodname>.
+        This will effectively perform what you see above, but makes the <varname>$content</varname>
+        argument optional and simplifies the usage:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+echo $element->renderSimpleInput();
+]]></programlisting>
+
+    <para>
+        This is a neat trick, but how and why would you use it?
+    </para>
+
+    <para>
+        Many developers and designers have very precise markup needs for their forms. They would
+        rather have full control over the output than rely on a more automated solution which may or
+        may not conform to their design. In other cases, the form layout may require a lot of
+        specialized markup -- grouping arbitrary elements, making some invisible unless a particular
+        link is selected, etc.
+    </para>
+
+    <para>
+        Let's utilize the ability to render individual decorators to create some specialized markup.
+    </para>
+
+    <para>
+        First, let's define a form. Our form will capture a user's demographic details. The markup
+        will be highly customized, and in some cases use view helpers directly instead of form
+        elements in order to achieve its goals.  Here is the basic form definition:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_UserDemographics extends Zend_Form
+{
+    public function init()
+    {
+        // Add a path for my own decorators
+        $this->addElementPrefixPaths(array(
+            'decorator' => array('My_Decorator' => 'My/Decorator'),
+        ));
+
+        $this->addElement('text', 'firstName', array(
+            'label' => 'First name: ',
+        ));
+        $this->addElement('text', 'lastName', array(
+            'label' => 'Last name: ',
+        ));
+        $this->addElement('text', 'title', array(
+            'label' => 'Title: ',
+        ));
+        $this->addElement('text', 'dateOfBirth', array(
+            'label' => 'Date of Birth (DD/MM/YYYY): ',
+        ));
+        $this->addElement('text', 'email', array(
+            'label' => 'Your email address: ',
+        ));
+        $this->addElement('password', 'password', array(
+            'label' => 'Password: ',
+        ));
+        $this->addElement('password', 'passwordConfirmation', array(
+            'label' => 'Confirm Password: ',
+        ));
+    }
+}
+]]></programlisting>
+
+    <note>
+        <para>
+            We're not defining any validators or filters at this time, as they are not relevant to
+            the discussion of decoration. In a real-world scenario, you should define them.
+        </para>
+    </note>
+
+    <para>
+        With that out of the way, let's consider how we might want to display this form. One common
+        idiom with first/last names is to display them on a single line; when a title is provided,
+        that is often on the same line as well.  Dates, when not using a JavaScript date chooser,
+        will often be separated into three fields displayed side by side.
+    </para>
+
+    <para>
+        Let's use the ability to render an element's decorators one by one to accomplish this.
+        First, let's note that no explicit decorators were defined for the given elements. As a
+        refresher, the default decorators for (most) elements are:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>ViewHelper</classname>: utilize a view helper to render a form input
+            </para>
+        </listitem>
+        
+        <listitem>
+            <para>
+                <classname>Errors</classname>: utilize the <classname>FormErrors</classname> view
+                helper to render validation errors
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Description</classname>: utilize the <classname>FormNote</classname> view
+                helper to render the element description (if any)
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>HtmlTag</classname>: wrap the above three items in a
+                <code>&lt;dd&gt;</code> tag
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Label</classname>: render the element label using the
+                <classname>FormLabel</classname> view helper (and wrap it in a
+                <code>&lt;dt&gt;</code> tag)
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Also, as a refresher, you can access any element of a form as if it were a class property;
+        simply reference the element by the name you assigned it.
+    </para>
+
+    <para>
+        Our view script might then look like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<?php 
+$form = $this->form; 
+// Remove <dt> from label generation
+foreach ($form->getElements() as $element) {
+    $element->getDecorator('label')->setOption('tag', null);
+}
+?>
+<form method="<?php echo $form->getMethod() ?>" action="<?php echo
+    $form->getAction()?>">
+    <div class="element">
+        <?php echo $form->title->renderLabel() 
+              . $form->title->renderViewHelper() ?>
+        <?php echo $form->firstName->renderLabel() 
+              . $form->firstName->renderViewHelper() ?>
+        <?php echo $form->lastName->renderLabel() 
+              . $form->lastName->renderViewHelper() ?>
+    </div>
+    <div class="element">
+        <?php echo $form->dateOfBirth->renderLabel() ?>
+        <?php echo $this->formText('dateOfBirth['day']', '', array(
+            'size' => 2, 'maxlength' => 2)) ?>
+        /
+        <?php echo $this->formText('dateOfBirth['month']', '', array(
+            'size' => 2, 'maxlength' => 2)) ?>
+        /
+        <?php echo $this->formText('dateOfBirth['year']', '', array(
+            'size' => 4, 'maxlength' => 4)) ?>
+    </div>
+    <div class="element">
+        <?php echo $form->password->renderLabel() 
+              . $form->password->renderViewHelper() ?>
+    </div>
+    <div class="element">
+        <?php echo $form->passwordConfirmation->renderLabel() 
+              . $form->passwordConfirmation->renderViewHelper() ?>
+    </div>
+    <?php echo $this->formSubmit('submit', 'Save') ?>
+</form>
+]]></programlisting>
+
+    <para>
+        If you use the above view script, you'll get approximately the following HTML (approximate,
+        as the HTML below is formatted):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<form method="post" action="">
+    <div class="element">
+        <label for="title" tag="" class="optional">Title:</label>
+        <input type="text" name="title" id="title" value=""/>
+
+        <label for="firstName" tag="" class="optional">First name:</label>
+        <input type="text" name="firstName" id="firstName" value=""/>
+        
+        <label for="lastName" tag="" class="optional">Last name:</label>
+        <input type="text" name="lastName" id="lastName" value=""/>
+    </div>
+
+    <div class="element">
+        <label for="dateOfBirth" tag="" class="optional">Date of Birth
+            (DD/MM/YYYY):</label>
+        <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day" 
+            value="" size="2" maxlength="2"/>
+        /
+        <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
+            value="" size="2" maxlength="2"/>
+        /
+        <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
+            value="" size="4" maxlength="4"/>
+    </div>
+
+    <div class="element">
+        <label for="password" tag="" class="optional">Password:</label>
+        <input type="password" name="password" id="password" value=""/>
+    </div>
+
+    <div class="element">
+        <label for="passwordConfirmation" tag="" class="" id="submit" 
+            value="Save"/>
+</form>
+]]></programlisting>
+
+    <para>
+        It may not be truly pretty, but with some CSS, it could be made to look exactly how you
+        might want to see it. The main point, however, is that this form was generated using almost
+        entirely custom markup, while still leveraging decorators for the most common markup (and to
+        ensure things like escaping with htmlentities and value injection occur).
+    </para>
+
+    <para>
+        By this point in the tutorial, you should be getting fairly comfortable with the markup
+        possibilities using <classname>Zend_Form</classname>'s decorators. In the next section, we'll
+        revisit the date element from above, and demonstrate how to create a custom element
+        and decorator for composite elements.
+    </para>
+</sect1>

+ 22 - 0
documentation/manual/en/tutorials/form-decorators-intro.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.intro">
+    <title>Introduction</title>
+
+    <para>
+        <link linkend="zend.form">Zend_Form</link> utilizes the <emphasis>decorator</emphasis>
+        pattern in order to render elements and forms. Unlike the classic <ulink
+            url="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</ulink>, in
+        which you pass an object to a wrapping class, decorators in <classname>Zend_Form</classname>
+        implement a <ulink url="http://en.wikipedia.org/wiki/Strategy_pattern">strategy
+            pattern</ulink>, and utilize the metadata contained in an element or form in order to
+        create a representation of it
+    </para>
+
+    <para>
+        Don't let the terminology scare you away, however; at heart, decorators in
+        <classname>Zend_Form</classname> are not terribly difficult, and the mini-tutorials that
+        follow should help you along the way. They will guide you through the basics of decoration,
+        all the way to creating decorators for composite elements.
+    </para>
+</sect1>

+ 362 - 0
documentation/manual/en/tutorials/form-decorators-layering.xml

@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.layering">
+    <title>Layering Decorators</title>
+
+    <para>
+        If you were following closely in <link linkend="learning.form.decorators.simplest">the
+            previous section</link>, you may have noticed that a decorator's
+        <methodname>render()</methodname> method takes a single argument,
+        <varname>$content</varname>. This is expected to be a string.
+        <methodname>render()</methodname> will then take this string and decide to either replace
+        it, append to it, or prepend it. This allows you to have a chain of decorators -- which
+        allows you to create decorators that render only a subset of the element's metadata, and
+        then layer these decorators to build the full markup for the element. 
+    </para>
+
+    <para>
+        Let's look at how this works in practice.
+    </para>
+
+    <para>
+        For most form element types, the following decorators are used:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>ViewHelper</classname> (render the form input using one of the standard
+                form view helpers).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Errors</classname> (render validation errors via an unordered list).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Description</classname> (render any description attached to the element;
+                often used for tooltips).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>HtmlTag</classname> (wrap all of the above in a <code>&lt;dd&gt;</code>
+                tag.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Label</classname> (render the label preceding the above, wrapped in a
+                <code>&lt;dt&gt;</code> tag.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        You'll notice that each of these decorators does just one thing, and operates on one
+        specific piece of metadata stored in the form element: the <classname>Errors</classname>
+        decorator pulls validation errors and renders them; the <classname>Label</classname>
+        decorator pulls just the label and renders it. This allows the individual decorators to be
+        very succinct, repeatable, and, more importantly, testable.
+    </para>
+
+    <para>
+        It's also where that <varname>$content</varname> argument comes into play: each decorator's
+        <methodname>render()</methodname> method is designed to accept content, and then either
+        replace it (usually by wrapping it), prepend to it, or append to it.
+    </para>
+
+    <para>
+        So, it's best to think of the process of decoration as one of building an onion from the
+        inside out.
+    </para>
+
+    <para>
+        To simplify the process, we'll take a look at the example from <link
+            linkend="learning.form.decorators.simplest">the previous section</link>. Recall:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>'
+                       . '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $label   = htmlentities($element->getLabel());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $label, $id, $name, $value);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Let's now remove the label functionality, and build a separate decorator for that.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $name, $value);
+        return $markup;
+    }
+}
+
+class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $id      = htmlentities($element->getId());
+        $label   = htmlentities($element->getLabel());
+
+        $markup = sprint($this->_format, $id, $label);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now, this may look all well and good, but here's the problem: as written currently, the last
+        decorator to run wins, and overwrites everything.  You'll end up with just the input, or
+        just the label, depending on which you register last.
+    </para>
+
+    <para>
+        To overcome this, simply concatenate the passed in <varname>$content</varname> with the
+        markup somehow:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+return $content . $markup;
+]]></programlisting>
+
+    <para>
+        The problem with the above approach comes when you want to programmatically choose whether
+        the original content should precede or append the new markup. Fortunately, there's a
+        standard mechanism for this already; <classname>Zend_Form_Decorator_Abstract</classname> has
+        a concept of placement and defines some constants for matching it. Additionally, it allows
+        specifying a separator to place between the two. Let's make use of those:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $name, $value);
+
+        $placement = $this->getPlacement();
+        $separator = $this->getSeparator();
+        switch ($placement) {
+            case self::PREPEND:
+                return $markup . $separator . $content;
+            case self::APPEND:
+            default:
+                return $content . $separator . $markup;
+        }
+    }
+}
+
+class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $id      = htmlentities($element->getId());
+        $label   = htmlentities($element->getLabel());
+
+        $markup = sprint($this->_format, $id, $label);
+
+        $placement = $this->getPlacement();
+        $separator = $this->getSeparator();
+        switch ($placement) {
+            case self::APPEND:
+                return $markup . $separator . $content;
+            case self::PREPEND:
+            default:
+                return $content . $separator . $markup;
+        }
+    }
+}
+]]></programlisting>
+
+    <para>
+        Notice in the above that I'm switching the default case for each; the assumption will be
+        that labels prepend content, and input appends.
+    </para>
+
+    <para>
+        Now, let's create a form element that uses these:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput',
+        'SimpleLabel',
+    ),
+));
+]]></programlisting>
+
+    <para>
+        How will this work? When we call <methodname>render()</methodname>, the element will iterate
+        through the various attached decorators, calling <methodname>render()</methodname> on each.
+        It will pass an empty string to the very first, and then whatever content is created will be
+        passed to the next, and so on:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Initial content is an empty string: ''.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                '' is passed to the <classname>SimpleInput</classname> decorator, which then
+                generates a form input that it appends to the empty string: <code>&lt;input
+                    id="bar-foo" name="bar[foo]" type="text" value="test"/&gt;</code>.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                The input is then passed as content to the <classname>SimpleLabel</classname>
+                decorator, which generates a label and prepends it to the original content; the
+                default separator is a <constant>PHP_EOL</constant> character, giving us this:
+                <code>&lt;label for="bar-foo"&gt;\n&lt;input id="bar-foo" name="bar[foo]"
+                    type="text" value="test"/&gt;</code>.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        But wait a second! What if you wanted the label to come after the input for some reason?
+        Remember that "placement" flag? You can pass it as an option to the decorator. The easiest
+        way to do this is to pass an array of options with the decorator during element creation:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput'
+        array('SimpleLabel', array('placement' => 'append')),
+    ),
+));
+]]></programlisting>
+
+    <para>
+        Notice that when passing options, you must wrap the decorator within an array; this hints to
+        the constructor that options are available. The decorator name is the first element of the
+        array, and options are passed in an array to the second element of the array.
+    </para>
+
+    <para>
+        The above results in the markup <code>&lt;input id="bar-foo" name="bar[foo]" type="text"
+            value="test"/&gt;\n&lt;label for="bar-foo"&gt;</code>.
+    </para>
+
+    <para>
+        Using this technique, you can have decorators that target specific metadata of the element
+        or form and create only the markup relevant to that metadata; by using mulitiple decorators,
+        you can then build up the complete element markup. Our onion is the result.
+    </para>
+
+    <para>
+        There are pros and cons to this approach. First, the cons:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                More complex to implement. You have to pay careful attention to the decorators you
+                use and what placement you utilize in order to build up the markup in the correct
+                sequence.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                More resource intensive. More decorators means more objects; multiply this by the
+                number of elements you have in a form, and you may end up with some serious resource
+                usage. Caching can help here.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        The advantages are compelling, though:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Reusable decorators. You can create truly re-usable decorators with this technique,
+                as you don't have to worry about the complete markup, but only markup for one or a
+                few pieces of element/form metadata.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Ultimate flexibility. You can theoretically generate any markup combination you want
+                from a small number of decorators.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        While the above examples are the intended usage of decorators within
+        <classname>Zend_Form</classname>, it's often hard to wrap your head around how the
+        decorators interact with one another to build the final markup. For this reason, some
+        flexibility was added in the 1.7 series to make rendering individual decorators possible --
+        which gives some Rails-like simplicity to rendering forms. We'll look at that in the next
+        section.
+    </para>
+</sect1>

+ 244 - 0
documentation/manual/en/tutorials/form-decorators-simplest.xml

@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.simplest">
+    <title>Decorator Basics</title>
+
+    <sect2 id="learning.form.decorators.simplest.decorator-overview">
+        <title>Overview of the Decorator Pattern</title>
+
+        <para>
+            To begin, we'll cover some background on the <ulink
+                url="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator design
+                pattern</ulink>.  One common technique is to define a common interface that both
+            your originating object and decorator will implement; your decorator than accepts the
+            originating object as a dependency, and will either proxy to it or override its methods.
+            Let's put that into code to make it more easily understood:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+interface Window
+{
+    public function isOpen();
+    public function open();
+    public function close();
+}
+
+class StandardWindow implements Window
+{
+    protected $_open = false;
+
+    public function isOpen()
+    {
+        return $this->_open;
+    }
+
+    public function open()
+    {
+        if (!$this->_open) {
+            $this->_open = true;
+        }
+    }
+
+    public function close()
+    {
+        if ($this->_open) {
+            $this->_open = false;
+        }
+    }
+}
+
+class LockedWindow implements Window
+{
+    protected $_window;
+
+    public function __construct(Window $window)
+    {
+        $this->_window = $window;
+        $this->_window->close();
+    }
+
+    public function isOpen()
+    {
+        return false;
+    }
+
+    public function open()
+    {
+        throw new Exception('Cannot open locked windows');
+    }
+
+    public function close()
+    {
+        $this->_window->close();
+    }
+}
+]]></programlisting>
+
+        <para>
+            You then create an object of type <classname>StandardWindow</classname>, pass it to the
+            constructor of <classname>LockedWindow</classname>, and your window instance now has
+            different behavior. The beauty is that you don't have to implement any sort of "locking"
+            functionality on your standard window class -- the decorator takes care of that for you.
+            In the meantime, you can pass your locked window around as if it were just another
+            window.
+        </para>
+
+        <para>
+            One particular place where the decorator pattern is useful is for creating textual
+            representations of objects. As an example, you might have a "Person" object that, by
+            itself, has no textual representation. By using the Decorator pattern, you can create an
+            object that will act as if it were a Person, but also provide the ability to render that
+            Person textually.
+        </para>
+
+        <para>
+            In this particular example, we're going to use <ulink
+                url="http://en.wikipedia.org/wiki/Duck_typing">duck typing</ulink> instead of an
+            explicit interface. This allows our implementation to be a bit more flexible, while
+            still allowing the decorator object to act exactly as if it were a Person object.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Person
+{
+    public function setFirstName($name) {}
+    public function getFirstName() {}
+    public function setLastName($name) {}
+    public function getLastName() {}
+    public function setTitle($title) {}
+    public function getTitle() {}
+}
+
+class TextPerson
+{
+    protected $_person;
+
+    public function __construct(Person $person)
+    {
+        $this->_person = $person;
+    }
+
+    public function __call($method, $args)
+    {
+        if (!method_exists($this->_person, $method)) {
+            throw new Exception('Invalid method called on HtmlPerson: ' 
+                .  $method);
+        }
+        return call_user_func_array(array($this->_person, $method), $args);
+    }
+
+    public function __toString()
+    {
+        return $this->_person->getTitle() . ' '
+            . $this->_person->getFirstName() . ' '
+            . $this->_person->getLastName();
+    }
+}
+]]></programlisting>
+
+        <para>
+            In this example, you pass your <classname>Person</classname> instance to the
+            <classname>TextPerson</classname> constructor. By using method overloading, you are able
+            to continue to call all the methods of <classname>Person</classname> -- to set the first
+            name, last name, or title -- but you also now gain a string representation via the
+            <methodname>__toString()</methodname> method.
+        </para>
+
+        <para>
+            This latter example is getting close to how <classname>Zend_Form</classname> decorators
+            work. The key difference is that instead of a decorator wrapping the element, the
+            element has one or more decorators attached to it that it then injects itself into in
+            order to render.  The decorator then can access the element's methods and properties in
+            order to create a representation of the element -- or a subset of it.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.simplest.first-decorator">
+        <title>Creating Your First Decorator</title>
+
+        <para>
+            <classname>Zend_Form</classname> decorators all implement a common interface,
+            <interfacename>Zend_Form_Decorator_Interface</interfacename>. That interface provides
+            the ability to set decorator-specific options, register and retrieve the element, and
+            render. A base decorator, <classname>Zend_Form_Decorator_Abstract</classname>, provides
+            most of the functionality you will ever need, with the exception of the rendering logic.
+        </para>
+
+        <para>
+            Let's consider a situation where we simply want to render an element as a standard form
+            text input with a label. We won't worry about error handling or whether or not the
+            element should be wrapped within other tags for now -- just the basics. Such a decorator
+            might look like this:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>'
+                       . '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $label   = htmlentities($element->getLabel());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $name, $label, $id, $name, $value);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+        <para>
+            Let's create an element that uses this decorator:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$decorator = new My_Decorator_SimpleInput();
+$element   = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'decorators' => array($decorator),
+));
+]]></programlisting>
+
+        <para>
+            Rendering this element results in the following markup:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<label for="bar[foo]">Foo</label>
+<input id="bar-foo" name="bar[foo]" type="text" value="test"/>
+]]></programlisting>
+
+        <para>
+            You could also put this class in your library somewhere, inform your element of that
+            path, and refer to the decorator as simply "SimpleInput" as well:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array('SimpleInput'),
+));
+]]></programlisting>
+
+        <para>
+            This gives you the benefit of re-use in other projects, and also opens the door for
+            providing alternate implementations of that decorator later.
+        </para>
+
+        <para>
+            In the next section, we'll look at how to combine decorators in order to create
+            composite output.
+        </para>
+    </sect2>
+</sect1>

+ 20 - 0
documentation/manual/en/tutorials/layout-conclusions.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.conclusions">
+    <title>Zend_Layout: Conclusions</title>
+
+    <para>
+        <classname>Zend_Layout</classname> is a very simple wrapper around
+        <classname>Zend_View</classname> which provides you immediately with the benefits of a Two
+        Step View, giving you the flexibility to create a site-wide design into which you may inject
+        your application content.
+    </para>
+
+    <para>
+        If you're looking closely at the examples, however, you may come away with the idea that the
+        functionality is relatively limited: how do you alter the page title, inject an optional
+        script tag, or even create an optional sidebar? These questions are addressed with the
+        concept of a "Composite View" -- and are the subject of the next chapter in the tutorial,
+        which covers view "placeholders."
+    </para>
+</sect1>

+ 54 - 0
documentation/manual/en/tutorials/layout-intro.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.intro">
+    <title>Introduction</title>
+
+    <para>
+        When building a website using the Zend Framework <acronym>MVC</acronym> layers, your view
+        scripts will typically be just snippets of <acronym>HTML</acronym> pertinent to the
+        requested action. For instance, if you had the action "/user/list", you might create a view
+        script that iterates through the users and presents an unordered list:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<h2>Users</h2>
+<ul>
+    <?php if (!count($this->users)): ?>
+    <li>No users found</li>
+    <?php else: ?>
+    <?php foreach ($this->users as $user): ?>
+    <li>
+        <?php echo $this->escape($user->fullname) ?> 
+        (<?php echo $this->escape($user->email) ?>)
+    </li>
+    <?php endforeach ?>
+    <?php endif ?>
+</ul>
+]]></programlisting>
+
+    <para>
+        Since this is just a snippet of <acronym>HTML</acronym>, it's not a valid page; it's missing
+        a <acronym>Doctype</acronym> declaration, and the opening <acronym>HTML</acronym> and
+        <acronym>body</acronym> tags. So, the question is, where will these be created?
+    </para>
+
+    <para>
+        In early versions of Zend Framework, developers often created "header" and "footer" view
+        scripts that had these artifacts, and then in each view script they would render them. While
+        this methodology worked, it also made it difficult to refactor later, or to build composite
+        content by calling multiple actions.
+    </para>
+
+    <para>
+        The <ulink url="http://martinfowler.com/eaaCatalog/twoStepView.html">Two Step View</ulink>
+        design pattern answers many of the issues presented. In this pattern, the "application" view
+        is created first, and then injected into the "page" view, which is then presented to the
+        client. The page view can be thought of as your site-wide template or layout, and would have
+        common elements used across various pages.
+    </para>
+
+    <para>
+        Within Zend Framework, <classname>Zend_Layout</classname> implements the Two Step View
+        pattern.
+    </para>
+</sect1>

+ 242 - 0
documentation/manual/en/tutorials/layout-usage.xml

@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.usage">
+    <title>Using Zend_Layout</title>
+
+    <para>
+        Basic usage of <classname>Zend_Layout</classname> is fairly trivial. Assuming you're using
+        <classname>Zend_Application</classname> already, you can simply provide some configuration
+        options and create a layout view script.
+    </para>
+
+    <sect2 id="learning.layout.usage.configuration">
+        <title>Layout Configuration</title>
+
+        <para>
+            The recommended location of layouts is in a "layouts/scripts/" subdirectory of your
+            application:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+application
+|-- Bootstrap.php
+|-- configs
+|   `-- application.ini
+|-- controllers
+|-- layouts
+|   `-- scripts
+|       |-- layout.phtml
+]]></programlisting>
+
+        <para>
+            To initialize <classname>Zend_Layout</classname>, add the following to your
+            configuration file ("application/configs/application.ini"):
+        </para>
+
+        <programlisting language="dosini"><![CDATA[
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+resources.layout.layout = "layout"
+]]></programlisting>
+
+        <para>
+            The first line indicates where to look for layout scripts; the second line gives the
+            name of the layout to use, minus the view script extension (which is assumed to be
+            ".phtml" by default).
+        </para>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.layout-script">
+        <title>Create a Layout Script</title>
+
+        <para>
+            Now that you have your configuration in place, you need to create your layout script.
+            First, make sure that you've created the "application/layouts/scripts" directory; then,
+            open an editor, and create the markup for your layout. Layout scripts are simply view
+            scripts, with some slight differences.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<html>
+<head>
+    <title>My Site</title>
+</head>
+<body>
+    <?php echo $this->layout()->content ?>
+</body>
+</html>
+]]></programlisting>
+
+        <para>
+            In the example above, you'll note the call to a <methodname>layout()</methodname> view
+            helper. When you register the <classname>Zend_Layout</classname> resource, you also gain
+            access to both an action and view helper that allow you access to the
+            <classname>Zend_Layout</classname> instance; you can then call operations on the layout
+            object. In this case, we're retrieving a named variable, <varname>content</varname>, and
+            echoing it. By default, the <varname>content</varname> variable is populated for you
+            from the application view script rendered. Otherwise, anything you'd normally do in a
+            view script is perfectly valid -- call any helpers or view methods you desire.
+        </para>
+
+        <para>
+            At this point, you have a working layout script, and your application is informed of its
+            location and knows to render it.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.access">
+        <title>Accessing the Layout Object</title>
+
+        <para>
+            On occasion, you may need direct access to the layout object. There are three ways you
+            can do this:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis>Within view scripts:</emphasis> use the
+                    <methodname>layout()</methodname> view helper, which returns the
+                    <classname>Zend_Layout</classname> instance registered with the front controller
+                    plugin.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+<?php $layout = $this->layout(); ?>
+]]></programlisting>
+
+                <para>
+                    Since it returns the layout instance, you can also simply call methods on it,
+                    rather than assigning it to a variable.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Within action controllers:</emphasis> use the
+                    <methodname>layout()</methodname> action helper, which acts just like the view
+                    helper.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Calling helper as a method of the helper broker:
+$layout = $this->_helper->layout();
+
+// Or, more verbosely:
+$helper = $this->_helper->getHelper('Layout');
+$layout = $helper->getLayoutInstance();
+]]></programlisting>
+
+                <para>
+                    As with the view helper, since the action helper returns the layout instance,
+                    you can also simply call methods on it, rather than assigning it to a variable.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Elsewhere: </emphasis> use the static method
+                    <methodname>getMvcInstance()</methodname>. This will return the layout instance
+                    registered by the bootstrap resource.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout = Zend_Layout::getMvcInstance();
+]]></programlisting>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Via the bootstrap: </emphasis> retrieve the layout resource, which
+                    will be the <classname>Zend_Layout</classname> instance.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout = $bootstrap->getResource('Layout');
+]]></programlisting>
+
+                <para>
+                    Anywhere you have access to the bootstrap object, this method is preferred over
+                    using the static <methodname>getMvcInstance()</methodname> method.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.other-operations">
+        <title>Other Operations</title>
+
+        <para>
+            In most cases, the above configuration and layout script (with modifications) will get
+            you what you need. However, some other functionality exists you will likely use sooner
+            or later. In all of the following examples, you may use one of the <link
+                linkend="learning.layout.usage.access">methods listed above</link> for retrieving
+            the layout object.
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis>Setting layout variables</emphasis>.
+                    <classname>Zend_Layout</classname> keeps its own registry of layout-specific
+                    view variables that you can access; the <varname>content</varname> key noted in
+                    the initial layout script sample is one such example. You can assign and
+                    retrieve these using normal property access, or via the
+                    <methodname>assign()</methodname> method.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Setting content:
+$layout->somekey = "foo"
+
+// Echoing that same content:
+echo $layout->somekey; // 'foo'
+
+// Using the assign() method:
+$layout->assign('someotherkey', 'bar');
+
+// Access to assign()'d variables remains the same:
+echo $layout->someotherkey; // 'bar'
+]]></programlisting>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>disableLayout()</methodname>. Occasionally, you may want to disable
+                    layouts; for example, when answering an Ajax request, or providing a RESTful
+                    representation of a resource. In these cases, you can call the
+                    <methodname>disableLayout()</methodname> method on your layout object.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout->disableLayout();
+]]></programlisting>
+
+                <para>
+                    The opposite of this method is, of course,
+                    <methodname>enableLayout()</methodname>, which can be called at any time to
+                    re-enable layouts for the requested action.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Selecting an alternate layout</emphasis>: If you have multiple
+                    layouts for your site or application, you can select the layout to use at any
+                    time by simply calling the <methodname>setLayout()</methodname> method. Call it
+                    by specifying the name of the layout script without the file suffix.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Use the layout script "alternate.phtml":
+$layout->setLayout('alternate');
+]]></programlisting>
+
+                <para>
+                    The layout script should reside in the <varname>layoutPath</varname> directory
+                    specified in your configuration. <classname>Zend_Layout</classname> will then
+                    use this new layout when rendering.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+</sect1>

+ 27 - 0
documentation/manual/en/tutorials/lucene-index-opening.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.index-opening">
+    <title>Index Opening and Creation</title>
+
+    <para>
+        All index operations (e.g., creating a new index, adding a document to the index, deleting a
+        document, searching through the index) need an index object. One can be obtained using one
+        of the following two methods.
+    </para>
+
+    <example id="learning.lucene.index-opening.creation">
+        <title>Lucene Index Creation</title>
+        
+        <programlisting language="php"><![CDATA[
+$index = Zend_Search_Lucene::create($indexPath);
+]]></programlisting>
+    </example>
+    
+    <example id="learning.lucene.index-opening.creation">
+        <title>Lucene Index Opening</title>
+        
+        <programlisting language="php"><![CDATA[
+$index = Zend_Search_Lucene::open($indexPath);
+]]></programlisting>
+    </example>
+</sect1>

+ 116 - 0
documentation/manual/en/tutorials/lucene-index-structure.xml

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.index-structure">
+    <title>Lucene Index Structure</title>
+
+    <para>
+        In order to fully utilize <classname>Zend_Search_Lucene</classname>'s capabilities with
+        maximum performance, you need to understand it's internal index structure.
+    </para>
+    
+    <para>
+        An <emphasis>index</emphasis> is stored as a set of files within a single directory.
+    </para>
+
+    <para>
+        An <emphasis>index</emphasis> consists of any number of independent
+        <emphasis>segments</emphasis> which store information about a subset of indexed documents.
+        Each <emphasis>segment</emphasis> has its own <emphasis>terms dictionary</emphasis>, terms
+        dictionary index, and document storage (stored field values) <footnote><para>Starting with
+                Lucene 2.3, document storage files can be shared between segments; however,
+                <classname>Zend_Search_Lucene</classname> doesn't use this
+                capability</para></footnote>. All segment data is stored in
+        <filename>_xxxxx.cfs</filename> files, where <varname>xxxxx</varname> is a segment name.
+    </para>
+
+    <para>
+        Once an index segment file is created, it can't be updated.  New documents are added to new
+        segments.  Deleted documents are only marked as deleted in an optional
+        <filename>&lt;segmentname&gt;.del</filename> file. 
+    </para>
+
+    <para>
+        Document updating is performed as separate delete and add operations, even though it's done
+        using an <methodname>update()</methodname> API call<footnote><para>This call is provided
+                only by Java Lucene now, but it's planned to extend the
+                <classname>Zend_Search_Lucene</classname> API with similar
+                functionality</para></footnote>.  This simplifies adding new documents, and allows
+        updating concurrently with search operations.
+    </para>
+
+    <para>
+        On the other hand, using several segments (one document per segment as a borderline case) 
+        increases search time:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                retrieving a term from a dictionary is performed for each segment;
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                the terms dictionary index is pre-loaded for each segment (this process takes the
+                most search time for simple queries, and it also requires additional memory).
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        If the terms dictionary reaches a saturation point, then search through one segment is 
+        <emphasis>N</emphasis> times faster than search through <emphasis>N</emphasis> segments 
+        in most cases.
+    </para>
+
+    <para>
+        <emphasis>Index optimization</emphasis> merges two or more segments into a single new one. A
+        new segment is added to the index segments list, and old segments are excluded.
+    </para>
+
+    <para>
+        Segment list updates are performed as an atomic operation. This gives the ability of
+        concurrently adding new documents, performing index optimization, and searching through the
+        index.
+    </para>
+
+    <para>
+        Index auto-optimization is performed after each new segment generation. It merges sets of
+        the smallest segments into larger segments, and larger segments into even larger segments,
+        if we have enough segments to merge.
+    </para>
+
+    <para>
+        Index auto-optimization is controlled by three options:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <varname>MaxBufferedDocs</varname> (the minimal number of documents required before
+                the buffered in-memory documents are written into a new segment);
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <varname>MaxMergeDocs</varname> (the largest number of documents ever merged by
+                an optimization operation); and
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <varname>MergeFactor</varname> (which determines how often segment indices are
+                merged by auto-optimization operations).
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        If we add one document per script execution, then <emphasis>MaxBufferedDocs</emphasis> is
+        actually not used (only one new segment with only one document is created at the end of
+        script execution, at which time the auto-optimization process starts).
+    </para>
+</sect1>

+ 94 - 0
documentation/manual/en/tutorials/lucene-indexing.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.indexing">
+    <title>Indexing</title>
+
+    <para>
+        Indexing is performed by adding a new document to an existing or new index:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$index->addDocument($doc);
+]]></programlisting>
+
+    <para>
+        There are two ways to create document object. The first is to do it manually.
+    </para>
+
+    <example id="learning.lucene.indexing.doc-creation">
+        <title>Manual Document Construction</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = new Zend_Search_Lucene_Document();
+$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
+$doc->addField(Zend_Search_Lucene_Field::Text('title', $docTitle));
+$doc->addField(Zend_Search_Lucene_Field::unStored('contents', $docBody));
+$doc->addField(Zend_Search_Lucene_Field::binary('avatar', $avatarData));
+]]></programlisting>
+    </example>
+    
+    <para>
+        The second method is to load it from <acronym>HTML</acronym> or Microsoft Office 2007 files:
+    </para>
+
+    <example id="learning.lucene.indexing.doc-loading">
+        <title>Document loading</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
+$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($path);
+$doc = Zend_Search_Lucene_Document_Pptx::loadPptFile($path);
+$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($path);
+]]></programlisting>
+    </example>
+
+    <para>
+        If a document is loaded from one of the supported formats, it still can be extended manually
+        with new user defined fields.
+    </para>
+
+    <sect2 id="learning.lucene.indexing.policy">
+        <title>Indexing Policy</title>
+
+        <para>
+            You should define indexing policy within your application architectural design.
+        </para>
+
+        <para>
+            You may need an on-demand indexing configuration (something like OLTP system). In such
+            systems, you usually add one document per user request. As such, the
+            <emphasis>MaxBufferedDocs</emphasis> option will not affect the system. On the other
+            hand, <emphasis>MaxMergeDocs</emphasis> is really helpful as it allows you to limit
+            maximum script execution time. <emphasis>MergeFactor</emphasis> should be set to a value
+            that keeps balance between the average indexing time (it's also affected by average
+            auto-optimization time) and search performance (index optimization level is dependent on
+            the number of segments).
+        </para>
+
+        <para>
+            If you will be primarily performing batch index updates, your configuration should use a
+            <emphasis>MaxBufferedDocs</emphasis> option set to the maximum value supported by the
+            available amount of memory.  <emphasis>MaxMergeDocs</emphasis> and
+            <emphasis>MergeFactor</emphasis> have to be set to values reducing auto-optimization
+            involvement as much as possible <footnote><para>An additional limit is the maximum file
+                    handlers supported by the operation system for concurrent open
+                    operations</para></footnote>. Full index optimization should be applied after
+            indexing.
+        </para>
+
+        <example id="learning.lucene.indexing.optimization">
+            <title>Index optimization</title>
+
+            <programlisting language="php"><![CDATA[
+$index->optimize();
+]]></programlisting>
+        </example>
+
+        <para>
+            In some configurations, it's more effective to serialize index updates by organizing
+            update requests into a queue and processing several update requests in a single script
+            execution. This reduces index opening overhead, and allows utilizing index document
+            buffering.
+        </para>
+    </sect2>
+</sect1>

+ 102 - 0
documentation/manual/en/tutorials/lucene-intro.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.intro">
+    <title>Zend_Search_Lucene Introduction</title>
+
+    <para>
+        The <classname>Zend_Search_Lucene</classname> component is intended to provide a
+        ready-for-use full-text search solution.  It doesn't require any PHP
+        extensions<footnote><para>Though some <acronym>UTF-8</acronym> processing functionality
+                requires the <classname>mbstring</classname> extension to be turned
+                on</para></footnote> or additional software to be installed, and can be used
+        immediately after Zend Framework installation.
+    </para>
+
+    <para>
+        <classname>Zend_Search_Lucene</classname> is a pure PHP port of the popular open source
+        full-text search engine known as Apache Lucene.  See <ulink
+            url="http://lucene.apache.org">http://lucene.apache.org/</ulink> for the details.
+    </para>
+
+    <para>
+        Information must be indexed to be available for searching.
+        <classname>Zend_Search_Lucene</classname> and Java Lucene use a document concept known as an
+        "atomic indexing item."
+    </para>
+
+    <para>
+        Each document is a set of fields: &lt;name, value&gt; pairs where name and value are
+        <acronym>UTF-8</acronym> strings<footnote><para>Binary strings are also allowed to be used
+                as field values</para></footnote>. Any subset of the document fields may be marked
+        as "indexed" to include field data in the text indexing process.
+    </para>
+
+    <para>
+        Field values may or may not be tokenized while indexing. If a field is not tokenized, then
+        the field value is stored as one term; otherwise, the current analyzer is used for
+        tokenization.
+    </para>
+
+    <para>
+        Several analyzers are provided within the <classname>Zend_Search_Lucene</classname> package.
+        The default analyzer works with <acronym>ASCII</acronym> text (since the
+        <acronym>UTF-8</acronym> analyzer needs the <classname>mbstring</classname> extension to be
+        turned on). It is case insensitive, and it skips numbers. Use other analyzers or create your
+        own analyzer if you need to change this behavior.
+    </para>
+
+    <note>
+        <title>Using analyzers during indexing and searching</title>
+
+        <para>
+            Important note! Search queries are also tokenized using the "current analyzer", so the
+            same analyzer must be set as the default during both the indexing and searching process.
+            This will guarantee that source and searched text will be transformed into terms in the
+            same way.
+        </para>
+    </note>
+
+    <para>
+        Field values are optionally stored within an index. This allows the original field data to
+        be retrieved from the index while searching. This is the only way to associate search
+        results with the original data (internal document IDs may be changed after index
+        optimization or auto-optimization).
+    </para>
+
+    <para>
+        The thing that should be remembered is that a Lucene index is not a database. It doesn't
+        provide index backup mechanisms except backup of the file system directory. It doesn't
+        provide transactional mechanisms though concurrent index update as well as concurrent update
+        and read are supported. It doesn't compare with databases in data retrieving speed.
+    </para>
+
+    <para>
+        So it's good idea:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <emphasis>Not</emphasis> to use Lucene index as a storage since it may dramatically
+                decrease search hit retrieving performance. Store only unique document identifiers
+                (doc paths, URLs, database unique IDs) and associated data within an index. E.g.
+                title, annotation, category, language info, avatar. (Note: a field may be included
+                in indexing, but not stored, or stored, but not indexed).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                To write functionality that can rebuild an index completely if it's corrupted for
+                any reason.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Individual documents in the index may have completely different sets of fields. The same
+        fields in different documents don't need to have the same attributes. E.g. a field may be
+        indexed for one document and skipped from indexing for another. The same applies for
+        storing, tokenizing, or treating field value as a binary string.
+    </para>
+</sect1>

+ 48 - 0
documentation/manual/en/tutorials/lucene-pagination.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.pagination">
+    <title>Search result pagination</title>
+
+    <para>
+        As <link linkend="learning.lucene.searching.identifiers">mentioned above</link>, search
+        result hit objects use lazy loading for stored document fields. When any stored field is
+        accessed, the complete document is loaded.
+    </para>
+        
+    <para>
+        Do not retrieve all documents if you actually need to work only with some portion of them.
+        Go through the search results and store document IDs (and optionally the score) somewhere to
+        retrive documents from the index during the next script execution.
+    </para>
+        
+    <example id="learning.lucene.pagination.example">
+        <title>Search result pagination example</title>
+
+        <programlisting language="php"><![CDATA[
+$cacheId = md5($query);
+
+if (!$resultSet = $cache->load($cacheId)) {
+    $hits = $index->find($query);
+    $resultSet = array();
+    foreach ($hits as $hit) {
+        $resultSetEntry          = array();
+        $resultSetEntry['id']    = $hit->id;
+        $resultSetEntry['score'] = $hit->score;
+
+        $resultSet[] = $resultSetEntry;
+    }
+
+    $cache->save($resultSet, $cacheId);
+}
+
+$publishedResultSet = array();
+for ($resultId = $startId; $resultId < $endId; $resultId++) {
+    $publishedResultSet[$resultId] = array(
+        'id'    => $resultSet[$resultId]['id'], 
+        'score' => $resultSet[$resultId]['score'],
+        'doc'   => $index->getDocument($resultSet[$resultId]['id']),
+    );
+}
+]]></programlisting>
+    </example>
+</sect1>

+ 228 - 0
documentation/manual/en/tutorials/lucene-queries.xml

@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.queries">
+    <title>Supported queries</title>
+
+    <para>
+        <classname>Zend_Search_Lucene</classname> and Java Lucene support a powerful query language.
+        It allows searching for individual terms, phrases, ranges of terms; using wildcards and
+        fuzzy search; combining queries using boolean operators; and so on.
+    </para>
+        
+    <para>
+        A detailed query language description can be found in the <link
+            linkend="zend.search.lucene.query-language">
+            Zend_Search_Lucene component documentation</link>.
+    </para>
+        
+    <para>
+        What follows are examples of some common query types and strategies.
+    </para>
+
+    <example id="learning.lucene.queries.keyword">
+        <title>Querying for a single word</title>
+
+        <programlisting language="text"><![CDATA[
+hello
+]]></programlisting>
+
+        <para>
+            Searches for the word "hello" through all document fields.
+        </para>
+    </example>
+
+    <note>
+        <title>Default search field</title>
+
+        <para>
+            Important note! Java Lucene searches only through the "contents" field by default, but
+            <classname>Zend_Search_Lucene</classname> searches through <emphasis>all</emphasis>
+            fields. This behavior can be modified using the
+            <methodname>Zend_Search_Lucene::setDefaultSearchField($fieldName)</methodname> method.
+        </para>
+    </note>
+
+    <example id="learning.lucene.queries.multiple-words">
+        <title>Querying for multiple words</title>
+
+        <programlisting language="text"><![CDATA[
+hello dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words. Both words are optional; at least one of them must be present in
+            the result.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.required-words">
+        <title>Requiring words in a query</title>
+
+        <programlisting language="text"><![CDATA[
++hello dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words; "hello" is required, "dolly" is optional.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.prohibited-words">
+        <title>Prohibiting words in queried documents</title>
+
+        <programlisting language="text"><![CDATA[
++hello -dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words; "hello" is required, 'dolly' is prohibited. In other words, if
+            the document matches "hello", but contains the word "dolly", it will not be returned in
+            the set of matches.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.phrases">
+        <title>Querying for phrases</title>
+
+        <programlisting language="text"><![CDATA[
+"hello dolly"
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "hello dolly"; a document only matches if that exact string is
+            present.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields">
+        <title>Querying against specific fields</title>
+
+        <programlisting language="text"><![CDATA[
+title:"The Right Way" AND text:go
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "The Right Way" within the <varname>title</varname> field and
+            the word "go" within the <varname>text</varname> field.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields-and-document">
+        <title>Querying against specific fields as well as the entire document</title>
+
+        <programlisting language="text"><![CDATA[
+title:"The Right Way" AND  go
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "The Right Way" within the <varname>title</varname> field and
+            the word "go" word appearing in any field of the document.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields-and-document-alt">
+        <title>Querying against specific fields as well as the entire document (alternate)</title>
+        
+        <programlisting language="text"><![CDATA[
+title:Do it right
+]]></programlisting>
+
+        <para>
+            Searches for the word "Do" within the <varname>title</varname> field and the words "it"
+            and "right" words through all fields; any single one matching will result in a document
+            match.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.wildcard-question">
+        <title>Querying with the wildcard "?"</title>
+
+        <programlisting language="text"><![CDATA[
+te?t
+]]></programlisting>
+
+        <para>
+            Search for words matching the pattern "te?t", where "?" is any single character.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.wildcard-asterisk">
+        <title>Querying with the wildcard "*"</title>
+
+        <programlisting language="text"><![CDATA[
+test*
+]]></programlisting>
+
+        <para>
+            Search for words matching the pattern "test*", where "*" is any sequence of zero or more
+            characters.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.range-inclusive">
+        <title>Querying for an inclusive range of terms</title>
+
+        <programlisting language="text"><![CDATA[
+mod_date:[20020101 TO 20030101]
+]]></programlisting>
+
+        <para>
+            Search for the range of terms (inclusive).
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.range-exclusive">
+        <title>Querying for an exclusive range of terms</title>
+
+        <programlisting language="text"><![CDATA[
+title:{Aida to Carmen}
+]]></programlisting>
+
+        <para>
+            Search for the range of terms (exclusive).
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fuzzy">
+        <title>Fuzzy searches</title>
+
+        <programlisting language="text"><![CDATA[
+roam~
+]]></programlisting>
+
+        <para>
+            Fuzzy search for the word "roam".
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.boolean">
+        <title>Boolean searches</title>
+
+        <programlisting language="text"><![CDATA[
+(framework OR library) AND php
+]]></programlisting>
+
+        <para>
+            Boolean query.
+        </para>
+    </example>
+
+    <para>
+        All supported queries can be constructed through <classname>Zend_Search_Lucene</classname>'s
+        <link linkend="zend.search.lucene.query-api"> query
+            construction API</link>. Moreover, query parsing and query constructing may be
+        combined:
+    </para>
+
+    <example id="learning.lucene.queries.combining">
+        <title>Combining parsed and constructed queries</title>
+
+        <programlisting language="php"><![CDATA[
+$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
+
+$query = new Zend_Search_Lucene_Search_Query_Boolean();
+$query->addSubquery($userQuery, true  /* required */);
+$query->addSubquery($constructedQuery, true  /* required */);
+]]></programlisting>
+    </example>
+</sect1>

+ 97 - 0
documentation/manual/en/tutorials/lucene-searching.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.searching">
+    <title>Searching</title>
+
+    <para>
+        Searching is performed by using the <methodname>find()</methodname> method:
+    </para>
+
+    <example id="learning.lucene.searching.search-example">
+        <title>Searching through the index</title>
+
+        <programlisting language="php"><![CDATA[
+$hits = $index->find($query);
+
+foreach ($hits as $hit) {
+    printf(“%d %f %s\n”, $hit->id, $hit->score, $hit->title);
+}
+]]></programlisting>
+    </example>
+
+    <para>
+        This example demonstrates the usage of two special search hit properties -
+        <varname>id</varname> and <varname>score</varname>.
+    </para>
+    
+    <para>
+        <varname>id</varname> is an internal document identifier used within a Lucene index. It may
+        be used for a variety of operations, including deleting a document from the index:
+    </para>
+
+    <example id="learning.lucene.searching.delete-example">
+        <title>Deleting an Indexed Document</title>
+
+        <programlisting language="php"><![CDATA[
+$index->delete($id);
+]]></programlisting>
+    </example>
+
+    <para>
+        Or retrieving the document from the index:
+    </para>
+
+    <example id="learning.lucene.searching.retrieve-example">
+        <title>Retrieving an Indexed Document</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = $index->getDocument($id);
+]]></programlisting>
+    </example>
+
+    <note id="learning.lucene.searching.identifiers">
+        <title>Internal Document Identifiers</title>
+
+        <para>
+            Important note! Internal document identifiers may be changed by index optimization or
+            the auto-optimization process, but it's never changed within a single script's execution
+            unless the <methodname>addDocument()</methodname> (which may involve an
+            auto-optimization procedure) or <methodname>optimize()</methodname> methods are called.
+        </para>
+    </note>
+    
+    <para>
+        The <varname>score</varname> field is a hit score. Search results are ordered by score by
+        default (best results returned first).
+    </para>
+    
+    <para>
+        It's also possible to order result sets by specific field values. See the <link
+            linkend="zend.search.lucene.searching.sorting">
+            <classname>Zend_Search_Lucene</classname> documentation</link> for more details about
+        this possibility.
+    </para>
+        
+    <para>
+        The example also demonstrates an ability to access stored fields (e.g.,
+        <code>$hit-&gt;title</code>).  At the first access to any hit property other than
+        <varname>id</varname> or <varname>score</varname>, document stored fields are loaded, and
+        the corresponding field value is returned.
+    </para>
+        
+    <para>
+        This causes an ambiguity for documents having their own <varname>id</varname> or
+        <varname>score</varname> fields; as a result, it's not recommended to use these field names
+        within stored documents. Nevertheless, they still can be accessed via the
+        <methodname>getDocument()</methodname> method:
+    </para>
+
+    <example id="learning.lucene.searching.id-score-fields">
+        <title>Accessing the original document's "id" and "score" fields</title>
+
+        <programlisting language="php"><![CDATA[
+$id    = $hit->getDocument()->id;
+$score = $hit->getDocument()->score;
+]]></programlisting>
+    </example>
+</sect1>

+ 182 - 0
documentation/manual/en/tutorials/multiuser-authentication.xml

@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.authentication">
+    <title>Authenticating Users in ZF</title>
+
+    <sect2 id="learning.multiuser.authentication.intro">
+        <title>Introduction to Authentication</title>
+        
+        <para>
+            Once a web application has been able to distinguish one user from another by establishing a 
+            session, web applications typically want to validate the identity of a user.  The process 
+            of validating a consumer as being authentic is "authentication."  Authentication is made up 
+            of two distinctive parts: an identity and a set of credentials.  It takes some variation of 
+            both presented to the application for processing so that it may authenticate a user.    
+        </para>
+        
+        <para>
+            While the most common pattern of authentication revolves around usernames and passwords,
+            it should be stated that this is not always the case.  Identities are not limited to 
+            usernames.  In fact, any public identifier can be used: an assigned number, social security 
+            number, or residence address.  Likewise, credentials are not limited to passwords.  
+            Credentials can come in the form of protected private information: fingerprint, eye retinal 
+            scan, passphrase, or any other obscure personal information.  
+        </para>
+        
+    </sect2>
+
+    <sect2 id="learning.multiuser.authentication.basic-usage">
+        <title>Basic Usage of Zend_Auth</title>
+        
+        <para>
+            In the following example, we will be using Zend_Auth to complete what is probably the most
+            prolific form of authentication: username and password from a database table.  This
+            example assumes that you have already setup your application using Zend_Application, and
+            that inside that application you have configured a database connection.
+        </para>
+        
+        <para>
+            The job of the Zend_Auth class is twofold.  First, it should be able to accept an
+            authentication adapter to use to authenticate a user.  Secondly, after a successful
+            authentication of a user, it should persist throughout each and every request that might
+            need to know if the current user has indeed been authenticated.  To persist this data,
+            Zend_Auth consumes Zend_Session_Namespace, but you will generally never need to interact
+            with this session object.
+        </para>
+        
+        <para>
+            Lets assume we have the following database table setup:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+CREATE TABLE users (
+    id INTEGER  NOT NULL PRIMARY KEY, 
+    username VARCHAR(50) UNIQUE NOT NULL, 
+    password VARCHAR(32) NULL, 
+    password_salt VARCHAR(32) NULL,
+    real_name VARCHAR(150) NULL
+)
+]]></programlisting>
+
+        <para>
+            The above demonstrates a user table that includes a username, password, and also a 
+            password salt column.  This salt column is used as part of a technique called salting that 
+            would improve the security of your database of information against brute force attacks 
+            targeting the algorithm of your password hashing. 
+            <a href="http://en.wikipedia.org/wiki/Salting_%28cryptography%29">More information</a>
+            on salting.
+        </para>
+
+        <para>
+            For this implementation, we must first make a simple form that we can utilized as the 
+            "login form".  We will use Zend_Form to accomplish this.
+        </para>
+
+
+<programlisting language="php"><![CDATA[
+<?php
+// located at application/forms/Auth/Login.php
+
+class Default_Form_Auth_Login extends Zend_Form
+{
+    public function init()
+    {
+        $this->setMethod('post');
+
+        $this->addElement(
+            'text', 'username', array(
+                'label' => 'Username:',
+                'required' => true,
+                'filters'    => array('StringTrim'),
+            ));
+
+        $this->addElement('password', 'password', array(
+            'label' => 'Password:',
+            'required' => true,
+            ));
+
+        $this->addElement('submit', 'submit', array(
+            'ignore'   => true,
+            'label'    => 'Login',
+            ));
+
+    }
+}
+]]></programlisting>
+
+        <para>
+            With the above form, we can now go about creating our login action for
+            our authentication controller.  This controller will be called "AuthController", and
+            will be located at application/controllers/AuthController.php.  It will have a single
+            method called "loginAction" which will serve as the self-posting action.  In other words,
+            regardless of the url was POSTed to or GETed to, this method will handle the logic.
+        </para>
+        
+        <para>
+            The following code will demonstrate how to construct the proper adapter, integrate it
+            with the form:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class AuthController extends Zend_Controller_Action
+{
+
+    public function loginAction()
+    {
+        $db = $this->_getParam('db');
+        
+        $loginForm = new Default_Form_Auth_Login($_POST);
+        
+        if ($loginForm->isValid()) {
+        
+            $adapter = new Zend_Auth_Adapter_DbTable(
+                $db,
+                'users',
+                'username',
+                'password',
+                'MD5(CONCAT(?, password_salt))'
+                );
+            
+            $adapter->setIdentity($loginForm->getValue('username'));
+            $adapter->setCredential($loginForm->getValue('password'));
+            
+            $result = $auth->authenticate($adapter);
+            
+            if ($result->isValid()) {
+                $this->_helper->FlashMessenger('Successful Login');
+                $this->redirect('/');
+                return;
+            }
+            
+        }
+
+        $this->view->loginForm = $loginForm;
+
+    }
+
+}
+]]></programlisting>
+
+        <para>
+            The corresponding view script is quite simple for this action.  It will set the current
+            url since this form is self processing, and it will display the form.  This view script is
+            located at application/views/scripts/auth/login.phtml:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php 
+
+$this->form->setAction($this->url());
+echo $this->form;
+]]></programlisting>
+        
+        <para>
+            There you have it.  With these basics you can expand the general concepts to include 
+            more complex authentication scenarios.  For more information on other Zend_Auth adapters,
+            have a look in <link linkend="zend.auth">the reference guide</link>.
+        </para>
+        
+    </sect2>
+
+</sect1>
+

+ 226 - 0
documentation/manual/en/tutorials/multiuser-authorization.xml

@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.authentication">
+    <title>Building an Authorization System in ZF</title>
+
+    <sect2 id="learning.multiuser.authorization.intro">
+        <title>Introduction to Authorization</title>
+
+        <para>
+            After a user has been identified as being authentic, an application can go about its 
+            business of providing some useful and desirable resources to a consumer.  In many cases, 
+            applications might contain different resource types, with some resources having stricter 
+            rules regarding access.  This process of determining who has access to which resources is 
+            the process of "authorization".  Authorization in its simplest form is the composition of 
+            these elements:
+        </para>
+        
+        <itemizedlist>
+            <listitem>
+                <para>
+                    the identity whom wishes to be granted access
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    the resource the identity is asking permission to consume
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    and optionally, what the identity is privileged to do with the resource
+                </para>
+            </listitem>
+        </itemizedlist>
+        
+        <para>
+            In ZF, the Zend_Acl component handles the task of building a tree of roles, resources and
+            privileges to manage and query authorization requests against.
+        </para>
+        
+    </sect2>
+    
+    <sect2 id="learning.multiuser.authorization.basic-usage">
+        <title>Basic Usage of Zend_Acl</title>
+
+<!-- explain the interaction with a User object, how -->
+        
+        <para>
+            When using Zend_Acl, any models can serve as roles or resources by simply implementing
+            the proper interface.  To be used in a role capacity, the class must implement the 
+            Zend_Acl_Role_Interface, which requires only getRoleId().  To be used in a resource
+            capacity, a class must implement the Zend_Acl_Resource_Interface which similarly requires
+            the class implement the getResourceId() method.
+        </para>
+
+        <para>
+            Demonstrated below is a simple user model.  This model can take part in our ACL system
+            simply by implementing the Zend_Acl_Role_Interface.  The method getRoleId() will return
+            the id "guest" when an ID is not known, or it will return the role ID that was assigned
+            to this actual user object.  This value can effectively come from anywhere, a static definition
+            or perhaps dynamically from the users database role itself.
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+class Default_Model_User implements Zend_Acl_Role_Interface
+{
+    protected $_aclRoleId = null;
+    
+    public function getRoleId()
+    {
+        if ($this->_aclRoleId == null) {
+            return 'guest';
+        }
+        
+        return $this->_aclRoleId;
+    }
+}
+]]></programlisting>
+
+        <para>
+            While the concept of a user as a role is pretty straight forward, your application might
+            choose to have any other models in your system as a potential "resource" to be consumed in
+            this ACL system.  For simplicity, we'll use the example of a blog post.  Since the type of
+            the resource is tied to the type of the object, this class will only return 'blogPost' as
+            the resource ID in this system.  Naturally, this value can be dynamic if your system requires
+            it to be so.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Default_Model_BlogPost implements Zend_Acl_Resource_Interface
+{
+    public function getResourceId()
+    {
+        return 'blogPost';
+    }
+}
+]]></programlisting>
+
+        <para>
+            Now that we have at least a role and a resource, we can go about defining the rules of the
+            ACL system.  These rules will be consulted when the system receives a query about what is 
+            possible given a certain role, resources, and optionally a privilege.
+        </para>
+        
+        <para>
+            Lets assume the following rules:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+$acl = new Zend_Acl();
+
+// setup the various roles in our system
+$acl->addRole('guest');
+$acl->addRole('owner', 'guest');  // owner inherits all of the rules of guest
+
+// add the resources
+$acl->addResource('blogPost');
+
+// add privileges to roles and resource combinations
+$acl->allow('guest', 'blogPost', 'view');
+$acl->allow('owner', 'blogPost', 'post');
+$acl->allow('owner', 'blogPost', 'publish');
+]]></programlisting>
+
+        <para>
+            The above rules are quite simple: a guest role and an owner role exist; as does a blogPost
+            type resource.  Guests are allowed to view blog posts, and owners are allowed to post and publish
+            blog posts.  To query this system one might do any of the following:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+$guestUser = new Default_Model_User(); // assume the user model is of type guest resource 
+$ownerUser = new Default_Model_Owner('OwnersUsername');
+
+$post = new Default_Model_BlogPost();
+            
+$acl->isAllowed($guestUser, $post, 'view'); // true
+$acl->isAllowed($ownerUser, $post, 'view'); // true
+$acl->isAllowed($guestUser, $post, 'post'); // false
+$acl->isAllowed($ownerUser, $post, 'post'); // true
+]]></programlisting>
+        
+        <para>
+            As you can see, the above rules exercise whether owners and guests can view posts, which
+            they can, or post new posts, which owners can and guests cannot.  But as you might expect
+            this type of system might not be as dynamic as we wish it to be.  What if we want to ensure
+            a specific owner actual owns a very specific blog post before allowing him to publish it?
+            In other words, we want to ensure that only post owners have the ability to publish their
+            own posts.  
+        </para>
+        
+        <para>
+            This is where assertions come in.  Assertions are methods that will be called out to
+            when the static rule checking is simply not enough.  When registering an assertion object
+            this object will be consulted to determine, typically dynamically, if some roles has access
+            to some resource, with some optional privlidge that can only be answered by the logic within
+            the assertion.  For this example, we'll use the following assertion:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class OwnerCanPublishBlogPostAssertion implements Zend_Acl_Assert_Interface
+{
+    /**
+     * This assertion should receive the actual User and BlogPost objects.
+     *
+     * @param Zend_Acl $acl
+     * @param Zend_Acl_Role_Interface $user
+     * @param Zend_Acl_Resource_Interface $blogPost
+     * @param $privilege
+     * @return bool
+     */
+    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $user = null, Zend_Acl_Resource_Interface $blogPost = null, $privilege = null)
+    {
+    	if (!$user instanceof Default_Model_User) {
+            throw new Exception(__CLASS__ . '::' . __METHOD__ . ' expects the role to be an instance of User');
+        }
+
+        if (!$blogPost instanceof Default_Model_BlogPost) {
+            throw new Exception(__CLASS__ . '::' . __METHOD__ . ' expects the resource to be an instance of BlogPost');
+        }
+
+        // if role is publisher, he can always modify a post
+        if ($user->getRoleId() == 'publisher') {
+        	return true;
+        }
+
+        // check to ensure that everyone else is only modifying their own post
+        if ($user->id != null && $blogPost->ownerUserId == $user->id) {
+        	return true;
+        } else {
+        	return false;
+        }
+    }
+}
+
+]]></programlisting>
+        
+        <para>
+            To hook this into our ACL system, we would do the following:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+// replace this:
+//   $acl->allow('owner', 'blogPost', 'publish');
+// with this:
+$acl->allow('owner', 'blogPost', 'publish', new OwnerCanPublishBlogPostAssertion());
+
+// lets also add the role of a "publisher" who has access to everything
+$acl->allow('publisher', 'blogPost', 'publish');
+
+]]></programlisting>
+
+        <para>
+            Now, anytime the ACL is consulted about whether or not an owner can publish a specific
+            blog post, this assertion will be run.  This assertion will ensure that unless the role
+            type is 'publisher' the owner role must be logically tied to the blog post in question.
+            In this example, we check to see that the ownerUserId property of the blog post matches
+            the id of the owner passed in.
+        </para>
+
+        
+    </sect2>
+    
+</sect1>

+ 71 - 0
documentation/manual/en/tutorials/multiuser-intro.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.intro">
+    <title>Building Multi-User Applications With ZF</title>
+    
+    <sect2 id="learning.multiuser.intro.zf">
+        <title>Zend Framework</title>
+
+        <para>
+            When the original "web" was created, it was designed to be a publishing platform for
+            predominantly static content.  As demand for content on the web grew, as did the number of
+            consumers on the internet for web content, the demand for using the web as an application
+            platform also grew.  Since the web is inherently good at delivering a simultaneous
+            experience to many consumers from a single location, it makes it an ideal environment for
+            building dynamically driven, multi-user, and more commonly today, social systems.
+        </para>
+        
+        <para>
+            HTTP is the protocol of the web: a stateless, typically short lived, request/response
+            protocol.  This protocol was designed this way because the original intent of the web was
+            to serve or publish static content.  It is this very design that has made the web as
+            immensely successful as it is.  It is also exactly this design that brings new concerns to
+            developers who wish to use the web as an application platform.
+        </para>
+
+        <para>
+            These concerns and responsibilities can effectively be summed up by three questions:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    How do you distinguish one application consumer from another?
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    How do you identify a consumer as authentic?
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    How do you control what a consumer has access to?
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <note>
+            <title>Consumer Vs. User</title>
+            
+            <para>
+                Notice we use the term "consumer" instead of person.  Increasingly, web applications
+                are becoming service driven.  This means that not only are real people ("users") with
+                real web browsers consuming and using your application, but also other web applications
+                through machine service technologies such as ReST, SOAP, and XML-RPC.  In this respect,
+                people, as well as other consuming applications, should all be treated in same with
+                regard to the concerns outlined above.
+            </para>
+            
+        </note>
+        
+        <para>
+            In the following chapters, we'll take a look at these common problems relating to 
+            authentication and authorization in detail.  We will discover how 3 main components: 
+            Zend_Session, Zend_Auth, and Zend_Acl; provide an out-of-the-box solution as well as the 
+            extension points each have that will cater to a more customized solution.
+        </para>
+    </sect2>
+</sect1>

+ 133 - 0
documentation/manual/en/tutorials/multiuser-sessions.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.sessions">
+    <title>Managing User Sessions In ZF</title>
+
+    <sect2 id="learning.multiuser.sessions.intro">
+        <title>Introduction to Sessions</title>
+        
+        <para>
+            The success of the web is deeply rooted in the protocol that drives the web: HTTP.  HTTP
+            over TCP is by its very nature stateless, which means that inherently the web is also 
+            stateless.  While this very aspect is one of the dominating factors for why the web has
+            become such a popular medium, it also causes an interesting problem for developers that
+            want to use the web as an application platform.
+        </para>
+        
+        <para>
+            The act of interacting with a web application is typically defined by the sum 
+            of all requests sent to a web server.  Since there can be many consumers being served 
+            simultaneously, the application must decide which requests belong to which consumer.  These 
+            requests are typically known as a "session".
+        </para>
+        
+        <para>
+            In PHP, the session problem is solved by the session extension which utilizes some state 
+            tracking, typically cookies, and some form of local storage which is exposed via the 
+            $_SESSION superglobal.  In Zend Framework, the component Zend_Session adds value to the php 
+            session extension making it easier to use and depend on inside object-oriented 
+            applications.
+        </para>
+
+    </sect2>
+    
+    <sect2 id="learning.multiuser.sessions.basic-usage">
+        <title>Basic Usage of Zend_Session</title>
+        
+        <para>
+            The Zend_Session component is both a session manager as well as an API for
+            storing data into a session object for long-term persistence.  The Zend_Session API is
+            for managing the options and behavior of a session, like options, starting and stopping
+            a session, whereas Zend_Session_Namespace is the actual object used to store data.
+        </para>
+        
+        <para>
+            While its generally good practice to start a session inside a bootstrap process, this
+            is generally not necessary as all sessions will be automatically started upon the first
+            creation of a Zend_Session_Namespace object.
+        </para>
+        
+        <para>
+            Zend_Application is capable of configuring Zend_Session for you as part of the
+            Zend_Application_Resource system.  To use this, assuming your project uses
+            Zend_Application to bootstrap, you would add the following code to your 
+            application.ini file:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+resources.session.save_path = APPLICATION_PATH "/../data/session"
+resources.session.use_only_cookies = true
+resources.session.remember_me_seconds = 864000
+]]></programlisting>
+        
+        <para>
+            As you can see, the options passed in are the same options that you'd expect to find
+            in the ext/session extension in PHP.  Those options setup the path to the session
+            files where data will be stored within the project.  Since ini files can additionally
+            use constants, the above will use the APPLICATION_PATH constant and relatively point
+            to a data session directory.
+        </para>
+        
+        <para>
+            Most Zend Framework components that use sessions need nothing more to use Zend_Session.
+            At this point, you an either use a component that consumes Zend_Session, or start 
+            storing your own data inside a session with Zend_Session_Namespace.
+        </para>
+        
+        <para>
+            Zend_Session_Namespace is a simple class that proxies data via an easy to use API
+            into the Zend_Session managed $_SESSION superglobal.  The reason it is called
+            Zend_Session_Namespace is that it effectively namespaces the data inside $_SESSION, thus
+            allowing multiple components and objects to safely store and retrieve data.  In the
+            following code, we'll explore how to build a simple session incrementing counter, starting
+            at 1000 and resetting itself after 1999.
+        </para>
+            
+        <programlisting language="php"><![CDATA[
+$mysession = Zend_Session_Namespace('mysession');
+
+if (!isset($mysession->counter)) {
+    $mysession->counter = 1000;
+} else {
+    $mysession->counter++;
+}
+
+if ($mysession->counter > 1999) {
+    unset($mysession->counter);
+}
+]]></programlisting>
+            
+        <para>
+            As you can see above, the session namespace object uses the magic __get, __set,
+            __isset, and __unset to allow you to seemlessly and fluently interact with the session.
+            The information stored in the above example is stored at $_SESSION['mysession']['counter'].
+        </para>
+
+    </sect2>
+    <sect2 id="learning.multiuser.sessions.basic-usage">
+        <title>Advanced Usage of Zend_Session</title>
+
+        <para>
+            Addionally, if you wanted to use the DbTable
+            save handler for Zend_Session, you'd add the following code to your application.ini:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
+resources.session.saveHandler.options.name = "session"
+resources.session.saveHandler.options.primary.session_id = "session_id"
+resources.session.saveHandler.options.primary.save_path = "save_path"
+resources.session.saveHandler.options.primary.name = "name"
+resources.session.saveHandler.options.primaryAssignment.sessionId = "sessionId"
+resources.session.saveHandler.options.primaryAssignment.sessionSavePath = "sessionSavePath"
+resources.session.saveHandler.options.primaryAssignment.sessionName = "sessionName"
+resources.session.saveHandler.options.modifiedColumn = "modified"
+resources.session.saveHandler.options.dataColumn = "session_data"
+resources.session.saveHandler.options.lifetimeColumn = "lifetime"
+]]></programlisting>
+        
+        
+    </sect2>
+        
+
+</sect1>

+ 106 - 0
documentation/manual/en/tutorials/paginator-control.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.control">
+    <title>Pagination Control and ScrollingStyles</title>
+    
+    <para>
+        Rendering the items for a page on the screen has been a good start. In the code 
+        snippets in previous section we have also seen the <methodname>setCurrentPageNumber()</methodname>
+        method to set the active page number. The next step is to navigate through your pages.
+        To do this, Paginator provides you with two important tools: the ability to render the Paginator
+        with help of a View Partial, and support for so-called ScrollingStyles.
+    </para>
+    
+    <para>
+        The View Partial is a small view script that renders the Pagination controls, such as buttons to go 
+        to the next or previous page. Which pagination controls are rendered depends on the contents of
+        the view partial. Working with the view partial requires that you have set up Zend_View.
+        To get started with the pagination control, create a new view script somewhere in
+        your view scripts path. You can name it anything you want, but we'll call it "controls.phtml" in this text.
+        The reference manual contains various examples of what might go in the view script. Here is one example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<?php if ($this->pageCount): ?>
+<!-- First page link -->
+<?php if (isset($this->previous)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->first)); ?>">
+    First
+  </a> |
+<?php else: ?>
+  <span class="disabled">First</span> |
+<?php endif; ?>
+
+<!-- Previous page link -->
+<?php if (isset($this->previous)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
+    &lt; Previous
+  </a> |
+<?php else: ?>
+  <span class="disabled">&lt; Previous</span> |
+<?php endif; ?>
+
+<!-- Next page link -->
+<?php if (isset($this->next)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->next)); ?>">
+    Next &gt;
+  </a> |
+<?php else: ?>
+  <span class="disabled">Next &gt;</span> |
+<?php endif; ?>
+
+<!-- Last page link -->
+<?php if (isset($this->next)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->last)); ?>">
+    Last
+  </a>
+<?php else: ?>
+  <span class="disabled">Last</span>
+<?php endif; ?>
+
+</div>
+<?php endif; ?>
+]]></programlisting>
+    
+    <para>
+        The next step is to tell Zend_Paginator which view partial can be used to render
+        the navigation controls. Put the following line in your application's bootstrap file.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+Zend_View_Helper_PaginationControl::setDefaultViewPartial('controls.phtml');
+]]></programlisting>
+    
+    <para>
+        The last step is probably the easiest. Make sure you have assigned your Paginator object
+        to the a script (NOT the 'controls.phtml' script!). The only thing left to do is echo the Paginator in the view script.
+        This will automatically render the Paginator using the PaginationControl view helper.
+        In this next example, the Paginator object has been assigned to the 'paginator' view variable.
+        Don't worry if you don't fully get how it all works yet. The next section will feature
+        a complete example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<?php echo $this->paginator; ?>
+]]></programlisting>
+    
+    <para>
+        <classname>Zend_Paginator</classname>, together with the 'controls.phtml' view script you wrote,
+        makes sure your Paginator navigation is rendered properly. In order to decide
+        which page numbers need to be displayed on screen, Paginator uses so-called ScrollingStyles.
+        The default style is called "Sliding", which is similar to the way Yahoo's search result
+        navigation works. To mimick Google's ScrollingStyle, use the Elastic style.
+        You can set a default ScrollingStyle with the static <methodname>setDefaultScrollingStyle()</methodname> method,
+        or you can specify a ScrollingStyle dynamically when rendering the Paginator in your view script. This
+        requires manual invocation of the view helper in your view script.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// $this->paginator is a Paginator object
+<?php echo $this->paginationControl($this->paginator, 'Elastic', 'controls.phtml'); ?>
+]]></programlisting>
+    
+    <para>
+        For a list of all available ScrollingStyles, see the reference manual.
+    </para>
+</sect1>

+ 37 - 0
documentation/manual/en/tutorials/paginator-intro.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.intro">
+    <title>Introduction</title>
+    
+    <para>
+        Let's say you're creating a blogging application that will be home to your vast
+        collection of blog posts. There is a good chance that you do not want all of
+        your blog posts to appear on one single page when someone visits your blog.
+        An obvious solution would be to only display a small number of blog posts
+        on the screen at a time, and allow the user to browse through the different pages,
+        much like your favorite search engine shows you the result of your search query.
+        <classname>Zend_Paginator</classname> is designed to help you achieve the goal of dividing collections
+        of data in smaller, more manageable sets more easily, with more consistency,
+        and with less duplicate code.
+    </para>
+    
+    <para>
+        <classname>Zend_Paginator</classname> uses Adapters to support various data sources and ScrollingStyles
+        to support various methods of showing the user which pages are available.
+        In later sections of this text we will have a closer look at what these things
+        are and how they can help you to make the most out of <classname>Zend_Paginator</classname>.
+    </para>
+    
+    <para>
+        Before going in-depth, we will have a look at some simple examples first.
+        After these simple examples, we will see how <classname>Zend_Paginator</classname> supports the most
+        common use-case; paginating database results.
+    </para>
+    
+    <para>
+        This introduction has given you a quick overview of <classname>Zend_Paginator</classname>. To get
+        started and to have a look at some code snippets, let's have a look at some
+        simple examples.
+    </para>
+    
+</sect1>

+ 124 - 0
documentation/manual/en/tutorials/paginator-simple.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.simple">
+    <title>Simple Examples</title>
+    
+    <para>
+        In this first example we won't do anything spectacular, but hopefully it will
+        give you a good idea of what Zend_Paginator is designed to do.
+        Let's say we have an array called $data with the numbers 1 to 100 in it, which
+        we want to divide over a number of pages. We can use the static <methodname>factory()</methodname>
+        method in the <classname>Zend_Paginator</classname> class to get a <classname>Zend_Paginator</classname>
+        object with our array in it.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+]]></programlisting>
+    
+    <para>
+        We're already almost done! The $paginator variable now contains a reference to the
+        Paginator object. By default it is setup to display 10 items per page.
+        To display the items for the currently active page, all you need to do is iterate
+        over the Paginator object with a foreach loop. The currently active page defaults
+        to the first page if it's not explicitly specified. We will see how you can select
+        a specific page later on. The snippet below will display an unordered list containing the
+        numbers 1 to 10, which are the numbers on the first page.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+
+?><ul><?php
+
+// Render each item for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        Now let's try and render the items on the second page. You can use the
+        <methodname>setCurrentPageNumber()</methodname> method to select which page you want to view.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+
+// Select the second page
+$paginator->setCurrentPageNumber(2);
+
+?><ul><?php
+
+// Render each item for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        As expected, this little snippet will render an unordered list with the numbers
+        11 to 20 in it.
+    </para>
+    
+    <para>
+        These simple examples demonstrate a small portion of what can be achieved with
+        <classname>Zend_Paginator</classname>. However, a real application rarely reads its data from a plain
+        array, so the next section is dedicated to showing you how you can use Paginator
+        to paginate the results of a database query. Before reading on, make sure you're familiar with
+        the way <classname>Zend_Db_Select</classname> works!
+    </para>
+    
+    <para>
+        In the database examples we will look at a table with blog posts called 'posts'.
+        The 'posts' table has four columns: id, title, body, date_created.
+        Let's dive right in and have a look at a simple example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create a select query. $db is a Zend_Db_Adapter object, which we assume
+// already exists in your script.
+$select = $db->select()->from('posts')->order('date_created DESC');
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($select);
+
+// Select the second page
+$paginator->setCurrentPageNumber(2);
+
+?><ul><?php
+
+// Render each the title of each post for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item->title . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        As you can see, this example is not that different from the previous one.
+        The only difference is that you pass a <classname>Zend_Db_Select</classname> object to the 
+        Paginator's <methodname>factory()</methodname> method, rather than an array.
+        For more details on how the database adapter makes sure that your query
+        is being executed efficiently, see the Zend_Paginator chapter in the reference manual
+        on the DbSelect and DbTableSelect adapters.
+    </para>
+    
+</sect1>

+ 74 - 0
documentation/manual/en/tutorials/paginator-together.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.together">
+    <title>Putting it all Together</title>
+    
+    <para>
+        You have seen how to create a Paginator object, how to render the items on the current page, and
+        how to render a navigation element to browse through your pages. In this section you will
+        see how Paginator fits in with the rest of your MVC application.
+    </para>
+    
+    <para>
+        In the following examples we will ignore the best practice implementation of using a Service Layer to keep
+        the example simple and easier to understand. Once you get familiar with using Service Layers, it should be easy to see
+        how Paginator can fit in with the best practice approach.
+    </para>
+    
+    <para>
+        Lets start with the controller. The sample application is simple, and we'll just put everything
+        in the IndexController and the IndexAction. Again, this is for demonstration purposes only. A real application
+        should not use controllers in this manner.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+class IndexController extends Zend_Controller_Action
+{
+    public function indexAction()
+    {
+        // Setup pagination control view script. See the pagation control tutorial page
+        // for more information about this view script.
+        Zend_View_Helper_PaginationControl::setDefaultViewPartial('controls.phtml');
+        
+        // Fetch an already instantiated database connection from the registry
+        $db = Zend_Registry::get('db');
+        
+        // Create a select object which fetches blog posts, sorted decending by date of creation
+        $select = $db->select()->from('posts')->sort('date_created DESC');
+        
+        // Create a Paginator for the blog posts query
+        $paginator = Zend_Paginator::factory($select);
+        
+        // Read the current page number from the request. Default to 1 if no explicit page number is provided.
+        $paginator->setCurrentPageNumber($this->_getParam('page', 1));
+        
+        // Assign the Paginator object to the view
+        $this->view->paginator = $paginator;
+    }
+}
+]]></programlisting>
+    
+    <para>
+        The following view script is the index.phtml view script for the IndexController's indexAction.
+        The view script can be kept simple. We're assuming the use of the default ScrollingStyle.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<ul>
+<?php
+// Render each the title of each post for the current page in a list-item
+foreach ($this->paginator as $item) {
+    echo '<li>' . $item->title . '</li>';
+}
+?>
+</ul>
+<?php echo $this->paginator; ?>
+]]></programlisting>
+    
+    <para>
+        Now navigate to your project's index and see Paginator in action. What we have discussed in this
+        tutorial is just the tip of the iceberg. The reference manual and API documentation can tell
+        you more about what you can do with Zend_Paginator.
+    </para>
+    
+</sect1>

+ 61 - 0
documentation/manual/en/tutorials/plugins-conclusion.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Understanding the concept of prefix paths and overriding existing plugins will help you with
+        your understanding of many components within the framework. Plugins are used in a variety of
+        places: 
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>Zend_Application</classname>: resources.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Controller_Action</classname>: action helpers.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Feed_Reader</classname>: plugins.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Form</classname>: elements, filters, validators, and decorators.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_View</classname>: view helpers.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        And several more places, besides. Learn the concepts early so you can leverage this
+        important extension point in Zend Framework.
+    </para>
+
+    <note>
+        <title>Caveat</title>
+
+        <para>
+            We'll note here that <classname>Zend_Controller_Front</classname> has a plugin system -
+            but it does not adhere to any of the guidelines offerred in this tutorial. The plugins
+            registered with the front controller must be instantiated directly and registered
+            individually with it. The reason for this is that this system predates any other plugin
+            system in the framework, and changes to it must be carefully weighed to ensure existing
+            plugins written by developers continue to work with it.
+        </para>
+    </note>
+</sect1>

+ 54 - 0
documentation/manual/en/tutorials/plugins-intro.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.intro">
+    <title>Introduction</title>
+
+    <para>
+        Zend Framework makes heavy use of plugin architectures. Plugins allow for easy extensibility
+        and customization of the framework while keeping your code separate from the Zend Framework
+        code.
+    </para>
+
+    <para>
+        Typically, plugins in Zend Framework work as follows:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Plugins are classes. The actual class definition will vary based on the component --
+                you may need to extend an abstract class or implement an interface, but the fact
+                remains that the plugin is itself a class.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Related plugins will share a common class prefix. For instance, if you have created
+                a number of view helpers, they might all share the class prefix "Foo_View_Helper_". 
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Everything after the common prefix will be considered the <emphasis>plugin
+                    name</emphasis> or <emphasis>short name</emphasis> (versus the "long name",
+                which is the full classname). For example, if the plugin prefix is
+                "Foo_View_Helper_", and the class name is "Foo_View_Helper_Bar", the plugin name
+                will be simply "Bar".
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Plugin names are typically case sensitive. The one caveat is that the initial letter
+                can often be either lower or uppercase; in our previous example, both "bar" and
+                "Bar" would refer to the same plugin.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Now let's turn to using plugins.
+    </para>
+</sect1>

+ 151 - 0
documentation/manual/en/tutorials/plugins-usage.xml

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.usage">
+    <title>Using Plugins</title>
+
+    <para>
+        Components that make use of plugins typically use
+        <classname>Zend_Loader_PluginLoader</classname> to do their work. This class has you
+        register plugins by specifying one or more "prefix paths". The component will then call the
+        PluginLoader's <methodname>load()</methodname> method, passing the plugin's short name to
+        it. The PluginLoader will then query each prefix path to see if a class matching that short
+        name exists. Prefix paths are searched in LIFO (last in, first out) order, so it will match
+        those prefix paths registered last first -- allowing you to override existing plugins.
+    </para>
+
+    <para>
+        Some examples will make all of this more clear.
+    </para>
+
+    <example id="learning.plugins.usage.basic">
+        <title>Basic Plugin Example: Adding a single prefix path</title>
+
+        <para>
+            In this example, we will assume some validators have been written and placed in the
+            directory <filename>foo/plugins/validators/</filename>, and that all these classes share
+            the class prefix "Foo_Validate_"; these two bits of information form our "prefix path".
+            Furthermore, let's assume we have two validators, one named "Even" (ensuring a number to
+            be validated is even), and another named "Dozens" (ensuring the number is a multiple of
+            12). The tree might look like this:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+foo/
+|-- plugins/
+|   |-- validators/
+|   |   |-- Even.php
+|   |   |-- Dozens.php
+]]></programlisting>
+
+        <para>
+            Now, we'll inform a <classname>Zend_Form_Element</classname> instance of this prefix
+            path. <classname>Zend_Form_Element</classname>'s
+            <methodname>addPrefixPath()</methodname> method expects a third argument that indicates
+            the type of plugin for which the path is being registered; in this case, it's a
+            "validate" plugin.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element->addPrefixPath('Foo_Validate', 'foo/plugins/validators/', 'validate');
+]]></programlisting>
+
+        <para>
+            Now we can simply tell the element the short name of the validators we want to use. In
+            the following example, we're using a mix of standard validators ("NotEmpty", "Int") and
+            custom validators ("Even", "Dozens"):
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element->addValidator('NotEmpty')
+        ->addValidator('Int')
+        ->addValidator('Even')
+        ->addValidator('Dozens');
+]]></programlisting>
+
+        <para>
+            When the element needs to validate, it will then request the plugin class from the
+            PluginLoader. The first two validators will resolve to
+            <classname>Zend_Validate_NotEmpty</classname> and
+            <classname>Zend_Validate_Int</classname>, respectively; the next two will resolve to
+            <classname>Foo_Validate_Even</classname> and <classname>Foo_Validate_Dozens</classname>,
+            respectively.
+        </para>
+    </example>
+
+    <note>
+        <title>What happens if a plugin is not found?</title>
+
+        <para>
+            What happens if a plugin is requested, but the PluginLoader is unable to find a class
+            matching it? For instance, in the above example, if we registered the plugin "Bar" with
+            the element, what would happen?
+        </para>
+
+        <para>
+            The plugin loader will look through each prefix path, checking to see if a file matching
+            the plugin name is found on that path. If the file is not found, it then moves on to the
+            next prefix path.
+        </para>
+
+        <para>
+            Once the stack of prefix paths has been exhausted, if no matching file has been found,
+            it will throw a <exceptionname>Zend_Loader_PluginLoader_Exception</exceptionname>.
+        </para>
+    </note>
+
+    <example id="learning.plugins.usage.override">
+        <title>Intermediate Plugin Usage: Overriding existing plugins</title>
+
+        <para>
+            One strength of the PluginLoader is that its use of a LIFO stack allows you to override
+            existing plugins by creating your own versions locally with a different prefix path, and
+            registering that prefix path later in the stack.
+        </para>
+
+        <para>
+            For example, let's consider <classname>Zend_View_Helper_FormButton</classname> (view
+            helpers are one form of plugin). This view helper accepts three arguments, an element
+            name (also used as the element's DOM identifier), a value (used as the button label),
+            and an optional array of attributes.  The helper then generates HTML markup for a form
+            input element.
+        </para>
+
+        <para>
+            Let's say you want the helper to instead generate a true HTML
+            <constant>button</constant> element; don't want the helper to generate a DOM identifier,
+            but instead use the value for a CSS class selector; and that you have no interest in
+            handling arbitrary attributes. You could accomplish this in a couple of ways. In both
+            cases, you'd create your own view helper class that implements the behavior you want;
+            the difference is in how you would name and invoke them.
+        </para>
+
+        <para>
+            Our first example will be to name the element with a unique name:
+            <classname>Foo_View_Helper_CssButton</classname>, which implies the plugin name
+            "CssButton". While this certainly is a viable approach, it poses several issues: if
+            you've already used the Button view helper in your code, you now have to refactor;
+            alternately, if another developer starts writing code for your application, they may
+            inadvertently use the Button view helper instead of your new view helper.
+        </para>
+
+        <para>
+            So, the better example is to use the plugin name "Button", giving us the class name
+            <classname>Foo_View_Helper_Button</classname>. We then register the prefix path with the
+            view:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// Zend_View::addHelperPath() utilizes the PluginLoader; however, it inverts 
+// the arguments, as it provides a default value of "Zend_View_Helper" for the
+// plugin prefix.
+//
+// The below assumes your class is in the directory 'foo/view/helpers/'.
+$view->addHelperPath('foo/view/helpers', 'Foo_View_Helper');
+]]></programlisting>
+
+        <para>
+            Once done, anywhere you now use the "Button" helper will delegate to your custom
+            <classname>Foo_View_Helper_Button</classname> class!
+        </para>
+    </example>
+</sect1>

+ 15 - 0
documentation/manual/en/tutorials/quickstart-conclusion.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.conclusion">
+    <title>Congratulations!</title>
+
+    <para>
+        You have now built a very simple application using some of the most commonly used Zend
+        Framework components. Zend Framework makes many components available to you which address
+        most common requirements in web applications, including web services, search, PDF reading
+        and writing, authentication, authorization, and much more. The <link
+            linkend="reference">Reference Guide</link> is a great place to find out more about the
+        components you've used in this QuickStart as well as other components. We hope you find Zend
+        Framework useful and - more importantly - fun!
+    </para>
+</sect1>

+ 171 - 0
documentation/manual/en/tutorials/quickstart-create-form.xml

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-form">
+    <title>Create A Form</title>
+
+    <para>
+        For our guestbook to be useful, we need a form for submitting new entries.
+    </para>
+
+    <para>
+        Our first order of business is to create the actual form class. First, create the directory
+        <filename>application/forms/</filename>. This directory will contain form classes for the
+        application. Next, we'll create a form class in
+        <filename>application/forms/Guestbook.php</filename>:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/forms/Guestbook.php
+
+class Default_Form_Guestbook extends Zend_Form
+{
+    public function init()
+    {
+        // Set the method for the display form to POST
+        $this->setMethod('post');
+
+        // Add an email element
+        $this->addElement('text', 'email', array(
+            'label'      => 'Your email address:',
+            'required'   => true,
+            'filters'    => array('StringTrim'),
+            'validators' => array(
+                'EmailAddress',
+            )
+        ));
+
+        // Add the comment element
+        $this->addElement('textarea', 'comment', array(
+            'label'      => 'Please Comment:',
+            'required'   => true,
+            'validators' => array(
+                array('validator' => 'StringLength', 'options' => array(0, 20))
+                )
+        ));
+
+        // Add a captcha
+        $this->addElement('captcha', 'captcha', array(
+            'label'      => 'Please enter the 5 letters displayed below:',
+            'required'   => true,
+            'captcha'    => array(
+                'captcha' => 'Figlet', 
+                'wordLen' => 5, 
+                'timeout' => 300
+            )
+        ));
+
+        // Add the submit button
+        $this->addElement('submit', 'submit', array(
+            'ignore'   => true,
+            'label'    => 'Sign Guestbook',
+        ));
+
+        // And finally add some CSRF protection
+        $this->addElement('hash', 'csrf', array(
+            'ignore' => true,
+        ));
+    }
+}
+]]></programlisting>
+
+    <para>
+        The above form defines five elements: an email address field, a comment field, a CAPTCHA for
+        preventing spam submissions, a submit button, and a CSRF protection token.
+    </para>
+
+    <para>
+        Next, we will add a <methodname>signAction()</methodname> to our
+        <classname>GuestbookController</classname> which will process the form upon submission. To
+        create the action and related view script, execute the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+# Unix-like systems:
+% zf.sh create action sign guestbook
+
+# DOS/Windows:
+C:> zf.bat create action sign guestbook
+]]></programlisting>
+
+    <para>
+        This will create a <methodname>signAction()</methodname> method in our controller, as well
+        as the appropriate view script.
+    </para>
+
+    <para>
+        Let's add some logic into our guestbook controller's sign action. We need to first check if
+        we're getting a POST or a GET request; in the latter case, we'll simply display the form.
+        However, if we get a POST request, we'll want to validate the posted data against our form,
+        and, if valid, create a new entry and save it. The logic might look like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/controllers/GuestbookController.php
+
+class GuestbookController extends Zend_Controller_Action
+{
+    // snipping indexAction()...
+
+    public function signAction()
+    {
+        $request = $this->getRequest();
+        $form    = new Default_Form_Guestbook();
+
+        if ($this->getRequest()->isPost()) {
+            if ($form->isValid($request->getPost())) {
+                $model = new Default_Model_Guestbook($form->getValues());
+                $model->save();
+                return $this->_helper->redirector('index');
+            }
+        }
+        
+        $this->view->form = $form;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Of course, we also need to edit the view script; edit <filename>application/views/scripts/guestbook/sign.phtml</filename> to read:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/guestbook/sign.phtml -->
+
+Please use the form below to sign our guestbook!
+
+<?php 
+$this->form->setAction($this->url());
+echo $this->form;
+]]></programlisting>
+
+    <note>
+        <title>Better Looking Forms</title>
+
+        <para>
+            No one will be waxing poetic about the beauty of this form anytime soon. No matter -
+            form appearance is fully customizable! See the <link
+                linkend="zend.form.decorators">decorators section in the reference guide</link>
+            for details.
+        </para>
+
+        <para>
+            Additionally, you may be interested in <ulink
+                url="http://weierophinney.net/matthew/plugin/tag/decorators">this series of posts on
+                decorators</ulink>.
+        </para>
+    </note>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now browse to "http://localhost/guestbook/sign". You should see the following in your
+            browser:
+        </para>
+
+        <para>
+            <inlinegraphic width="421" scale="100" align="center" valign="middle"
+                fileref="figures/learning.quickstart.create-form.png" format="PNG" />
+        </para>
+    </note>
+</sect1>

+ 213 - 0
documentation/manual/en/tutorials/quickstart-create-layout.xml

@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-layout">
+    <title>Create A Layout</title>
+
+    <para>
+        You may have noticed that the view scripts in the previous sections were HTML fragments- not
+        complete pages. This is by design; we want our actions to return content only related to the
+        action itself, not the application as a whole.
+    </para>
+
+    <para>
+        Now we must compose that generated content into a full HTML page. We'd also like to have a
+        consistent look and feel for the application. We will use a global site layout to accomplish
+        both of these tasks.
+    </para>
+
+    <para>
+        There are two design patterns that Zend Framework uses to implement layouts: <ulink
+            url="http://martinfowler.com/eaaCatalog/twoStepView.html">Two Step View</ulink> and
+        <ulink
+            url="http://java.sun.com/blueprints/corej2eepatterns/Patterns/CompositeView.html">Composite
+            View</ulink>. <emphasis>Two Step View</emphasis> is usually associated with the <ulink
+            url="http://www.martinfowler.com/eaaCatalog/transformView.html">Transform View</ulink>
+        pattern; the basic idea is that your application view creates a representation that is then
+        injected into the master view for final transformation. The <emphasis>Composite
+            View</emphasis> pattern deals with a view made of one or more atomic, application views.
+    </para>
+
+    <para>
+        In Zend Framework, <link linkend="zend.layout">Zend_Layout</link> combines the ideas behind
+        these patterns. Instead of each action view script needing to include site-wide artifacts,
+        they can simply focus on their own responsibilities.
+    </para>
+
+    <para>
+        Occasionally, however, you may need application-specific information in your site-wide view
+        script. Fortunately, Zend Framework provides a variety of view
+        <emphasis>placeholders</emphasis> to allow you to provide such information from your action
+        view scripts.
+    </para>
+
+    <para>
+        To get started using Zend_Layout, first we need to inform our bootstrap to use the
+        <classname>Layout</classname> resource. This can be done by adding the following line to
+        your <filename>application/configs/application.ini</filename> file, within the
+        <constant>production</constant> section:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add to [production] section:
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+]]></programlisting>
+
+    <para>
+        The final INI file should look as follows:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+; PHP settings we want to initialize
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+includePaths.library = APPLICATION_PATH "/../library"
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+]]></programlisting>
+
+    <para>
+        This directive tells your application to look for layout view scripts in
+        <filename>application/layouts/scripts</filename>. Create those directories now.
+    </para>
+
+    <para>
+        We also want to ensure we have an XHTML DocType declaration for our application. To enable
+        this, we need to add a resource to our bootstrap.
+    </para>
+
+    <para>
+        The simplest way to add a bootstrap resource is to simply create a protected method
+        beginning with the phrase <methodname>_init</methodname>. In this case, we want to
+        initialize the doctype, so we'll create an <methodname>_initDoctype()</methodname> method
+        within our bootstrap class:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDoctype()
+    {
+    }
+}
+]]></programlisting>
+
+    <para>
+        Within that method, we need to hint to the view to use the appropriate doctype. But where
+        will the view object come from? The easy solution is to initialize the
+        <classname>View</classname> resource; once we have, we can pull the view object from the
+        bootstrap and use it.
+    </para>
+
+    <para>
+        To initialize the view resource, add the following line to your
+        <filename>application/configs/application.ini</filename> file, in the section marked
+        <constant>production</constant>:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add to [production] section:
+resources.view[] =
+]]></programlisting>
+
+    <para>
+        This tells us to initialize the view with no options (the '[]' indicates that the "view" key
+        is an array, and we pass nothing to it).
+    </para>
+
+    <para>
+        Now that we have a view, let's flesh out our <methodname>_initDoctype()</methodname> method.
+        In it, we will first ensure the <classname>View</classname> resource has run, fetch the view
+        object, and then configure it:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDoctype()
+    {
+        $this->bootstrap('view');
+        $view = $this->getResource('view');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now that we've initialized <classname>Zend_Layout</classname> and set the Doctype, let's
+        create our site-wide layout:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/layouts/scripts/layout.phtml
+
+echo $this->doctype() ?>
+<html xmlns="http://www.w3.org/1999/xhtml"> 
+<head>  
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+  <title>Zend Framework Quickstart Application</title>
+  <?php echo $this->headLink()->appendStylesheet('/css/global.css') ?>
+</head> 
+<body>
+<div id="header" style="background-color: #EEEEEE; height: 30px;">
+    <div id="header-logo" style="float: left">
+        <b>ZF Quickstart Application</b>
+    </div>
+    <div id="header-navigation" style="float: right">
+        <a href="<?php echo $this->url(
+            array('controller'=>'guestbook'), 
+            'default', 
+            true) ?>">Guestbook</a>
+    </div>
+</div>
+
+<?php echo $this->layout()->content ?>
+
+</body>
+</html>
+]]></programlisting>
+
+    <para>
+        We grab our application content using the <methodname>layout()</methodname> view helper, and
+        accessing the "content" key. You may render to other response segments if you wish to, but
+        in most cases, this is all that's necessary.
+    </para>
+
+    <para>
+        Note also the use of the <methodname>headLink()</methodname> placeholder. This is an easy
+        way to generate the HTML for &lt;link&gt; elements, as well as to keep track of them
+        throughout your application. If you need to add additional CSS sheets to support a single
+        action, you can do so, and be assured it will be present in the final rendered page.
+    </para>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now go to "http://localhost" and check out the source. You should see your XHTML header,
+            head, title, and body sections.
+        </para>
+    </note>
+</sect1>

+ 760 - 0
documentation/manual/en/tutorials/quickstart-create-model.xml

@@ -0,0 +1,760 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-model">
+    <title>Create a Model and Database Table</title>
+
+    <para>
+        Before we get started, let's consider something: where will these classes live, and how will
+        we find them? The default project we created instantiates an autoloader. We can attach other
+        autoloaders to it so that it knows where to find different classes. Typically, we want our
+        various MVC classes grouped under the same tree -- in this case,
+        <filename>application/</filename> -- and most often using a common prefix.
+    </para>
+
+    <para>
+        <classname>Zend_Controller_Front</classname> has a notion of "modules", which are individual
+        mini-applications.  Modules mimic the directory structure that the <command>zf</command>
+        tool sets up under <filename>application/</filename>, and all classes inside them are
+        assumed to begin with a common prefix, the module name.  <filename>application/</filename>
+        is itself a module -- the "default" module. As such, let's setup autoloading for resources
+        within this directory, giving them a prefix of "Default". We can do this by creating another
+        bootstrap resource.
+    </para>
+
+    <para>
+        <classname>Zend_Application_Module_Autoloader</classname> provides the functionality needed
+        to map the various resources under a module to the appropriate directories, and provides a
+        standard naming mechanism as well. In our bootstrap resource, we'll instantiate this, and be
+        done. The method looks like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+// Add this method to the Bootstrap class:
+
+    protected function _initAutoload()
+    {
+        $autoloader = new Zend_Application_Module_Autoloader(array(
+            'namespace' => 'Default_',
+            'basePath'  => dirname(__FILE__),
+        ));
+        return $autoloader;
+    }
+]]></programlisting>
+
+    <para>
+        The final bootstrap class will look as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initAutoload()
+    {
+        $autoloader = new Zend_Application_Module_Autoloader(array(
+            'namespace' => 'Default',
+            'basePath'  => dirname(__FILE__),
+        ));
+        return $autoloader;
+    }
+
+    protected function _initDoctype()
+    {
+        $this->bootstrap('view');
+        $view = $this->getResource('view');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now, let's consider what makes up a guestbook. Typically, they are simply a list of entries
+        with a <emphasis>comment</emphasis>, <emphasis>timestamp</emphasis>, and, often,
+        <emphasis>email address</emphasis>. Assuming we store them in a database, we may also want a
+        <emphasis>unique identifier</emphasis> for each entry. We'll likely want to be able to save
+        an entry, fetch individual entries, and retrieve all entries. As such, a simple guestbook
+        model API might look something like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/Guestbook.php
+
+class Default_Model_Guestbook
+{
+    protected $_comment;
+    protected $_created;
+    protected $_email;
+    protected $_id;
+
+    public function __set($name, $value);
+    public function __get($name);
+
+    public function setComment($text);
+    public function getComment();
+
+    public function setEmail($email);
+    public function getEmail();
+
+    public function setCreated($ts);
+    public function getCreated();
+
+    public function setId($id);
+    public function getId();
+
+    public function save();
+    public function find($id);
+    public function fetchAll();
+}
+]]></programlisting>
+
+    <para>
+        <methodname>__get()</methodname> and <methodname>__set()</methodname> will provide a
+        convenience mechanism for us to access the individual entry properties, and proxy to the
+        other getters and setters. They also will help ensure that only properties we whitelist will
+        be available in the object.
+    </para>
+
+    <para>
+        <methodname>find()</methodname> and <methodname>fetchAll()</methodname> provide the ability
+        to fetch a single entry or all entries.
+    </para>
+
+    <para>
+        Now from here, we can start thinking about setting up our database.
+    </para>
+
+    <para>
+        First we need to initialize our <classname>Db</classname> resource. As with the
+        <classname>Layout</classname> and <classname>View</classname> resource, we can provide
+        configuration for the <classname>Db</classname> resource. In your
+        <filename>application/configs/application.ini</filename> file, add the following lines in
+        the appropriate sections.
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add these lines to the appropriate sections:
+[production]
+resources.db.adapter       = "PDO_SQLITE"
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
+
+[testing : production]
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
+
+[development : production]
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
+]]></programlisting>
+    
+    <para>
+        Your final configuration file should look like the following:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+resources.view[] = 
+resources.db.adapter = "PDO_SQLITE"
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
+]]></programlisting>
+
+    <para>
+        Note that the database(s) will be stored in <filename>data/db/</filename>. Create those
+        directories, and make them world-writeable. On unix-like systems, you can do that as
+        follows:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+% mkdir -p data/db; chmod -R a+rwX data
+]]></programlisting>
+
+    <para>
+        On Windows, you will need to create the directories in Explorer and set the permissions to
+        allow anyone to write to the directory.
+    </para>
+
+    <para>
+        At this point we have a connection to a database; in our case, its a connection to a Sqlite
+        database located inside our <filename>application/data/</filename> directory. So, let's
+        design a simple table that will hold our guestbook entries.
+    </para>
+
+    <programlisting language="sql"><![CDATA[
+-- scripts/schema.sqlite.sql
+--
+-- You will need load your database schema with this SQL.
+
+CREATE TABLE guestbook (
+    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+    email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
+    comment TEXT NULL,
+    created DATETIME NOT NULL
+);
+
+CREATE INDEX "id" ON "guestbook" ("id");
+]]></programlisting>
+
+    <para>
+        And, so that we can have some working data out of the box, lets create a few rows of
+        information to make our application interesting.
+    </para>
+
+    <programlisting language="sql"><![CDATA[
+-- scripts/data.sqlite.sql
+--
+-- You can begin populating the database with the following SQL statements.
+
+INSERT INTO guestbook (email, comment, created) VALUES 
+    ('ralph.schindler@zend.com', 
+    'Hello! Hope you enjoy this sample zf application!', 
+    DATETIME('NOW'));
+INSERT INTO guestbook (email, comment, created) VALUES 
+    ('foo@bar.com', 
+    'Baz baz baz, baz baz Baz baz baz - baz baz baz.', 
+    DATETIME('NOW'));
+]]></programlisting>
+
+    <para>
+        Now that we have both the schema and some data defined. Lets get a script together that we
+        can now execute to build this database. Naturally, this is not needed in production, but
+        this script will help developers build out the database requirements locally so they can
+        have the fully working application. Create the script as
+        <filename>scripts/load.sqlite.php</filename> with the following contents:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// scripts/load.sqlite.php
+
+/**
+ * Script for creating and loading database
+ */
+
+// Initialize the application path and autoloading
+defined('APPLICATION_PATH')
+    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
+set_include_path(implode(PATH_SEPARATOR, array(
+    APPLICATION_PATH . '/../library',
+    get_include_path(),
+)));
+require_once 'Zend/Loader/Autoloader.php';
+Zend_Loader_Autoloader::getInstance();
+
+// Define some CLI options
+$getopt = new Zend_Console_Getopt(array(
+    'withdata|w' => 'Load database with sample data',
+    'env|e-s'    => 'Application environment for which to create database (defaults to development)',
+    'help|h'     => 'Help -- usage message',
+));
+try {
+    $getopt->parse();
+} catch (Zend_Console_Getopt_Exception $e) {
+    // Bad options passed: report usage
+    echo $e->getUsageMessage();
+    return false;
+}
+
+// If help requested, report usage message
+if ($getopt->getOption('h')) {
+    echo $getopt->getUsageMessage();
+    return true;
+}
+
+// Initialize values based on presence or absence of CLI options
+$withData = $getopt->getOption('w');
+$env      = $getopt->getOption('e');
+defined('APPLICATION_ENV')
+    || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
+
+// Initialize Zend_Application
+$application = new Zend_Application(
+    APPLICATION_ENV,
+    APPLICATION_PATH . '/configs/application.ini'
+);
+
+// Initialize and retrieve DB resource
+$bootstrap = $application->getBootstrap();
+$bootstrap->bootstrap('db');
+$dbAdapter = $bootstrap->getResource('db');
+
+// let the user know whats going on (we are actually creating a 
+// database here)
+if ('testing' != APPLICATION_ENV) {
+    echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
+    for ($x = 5; $x > 0; $x--) {
+        echo $x . "\r"; sleep(1);
+    }
+}
+
+// Check to see if we have a database file already
+$options = $bootstrap->getOption('resources');
+$dbFile  = $options['db']['params']['dbname'];
+if (file_exists($dbFile)) {
+    unlink($dbFile);
+}
+
+// this block executes the actual statements that were loaded from 
+// the schema file.
+try {
+    $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
+    // use the connection directly to load sql in batches
+    $dbAdapter->getConnection()->exec($schemaSql);
+    chmod($dbFile, 0666);
+
+    if ('testing' != APPLICATION_ENV) {
+        echo PHP_EOL;
+        echo 'Database Created';
+        echo PHP_EOL;
+    }
+    
+    if ($withData) {
+        $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
+        // use the connection directly to load sql in batches
+        $dbAdapter->getConnection()->exec($dataSql);
+        if ('testing' != APPLICATION_ENV) {
+            echo 'Data Loaded.';
+            echo PHP_EOL;
+        }
+    }
+    
+} catch (Exception $e) {
+    echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
+    echo $e->getMessage() . PHP_EOL;
+    return false;
+}
+
+// generally speaking, this script will be run from the command line
+return true;
+]]></programlisting>
+
+    <para>
+        Now, let's execute this script. From a terminal or the DOS command line, do the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+% php scripts/load.sqlite.php --withdata
+]]></programlisting>
+
+    <para>
+        You should see output like the following:
+    </para>
+
+    <programlisting language="text"><![CDATA[
+path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
+Writing Database Guestbook in (control-c to cancel): 
+1
+Database Created
+Data Loaded.
+]]></programlisting>
+
+    <para>
+        Now we have a fully working database and table for our guestbook application. Our next few
+        steps are to build out our application code. This includes building a data source (in our
+        case, we will use <classname>Zend_Db_Table</classname>), and a data mapper to connect that
+        data source to our domain model. Finally we'll also create the controller that will interact
+        with this model to both display existing entries and process new entries.
+    </para>
+
+    <para>
+        We'll use a <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
+            Gateway</ulink> to connect to our data source; <classname>Zend_Db_Table</classname>
+        provides this functionality. To get started, lets create a
+        <classname>Zend_Db_Table</classname>-based table class. First, create the directory
+        <filename>application/models/DbTable/</filename>. Then create and edit a file
+        <filename>Guestbook.php</filename> within it, and add the following contents:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/DbTable/Guestbook.php
+
+/**
+ * This is the DbTable class for the guestbook table.
+ */
+class Default_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
+{
+    /** Table name */
+    protected $_name    = 'guestbook';
+}
+]]></programlisting>
+
+    <para>
+        Note the class prefix: <classname>Default_Model_DbTable</classname>. The class prefix
+        "Default" from our autoloader is the first segment, and then we have the component,
+        "Model_DbTable"; the latter is mapped to the <filename>models/DbTable/</filename> directory
+        of the module.
+    </para>
+
+    <para>
+        All that is truly necessary when extending <classname>Zend_Db_Table</classname> is to
+        provide a table name and optionally the primary key (if it is not "id").
+    </para>
+
+    <para>
+        Now let's create a <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data
+            Mapper</ulink>. A <emphasis>Data Mapper</emphasis> maps a domain object to the database.
+        In our case, it will map our model, <classname>Default_Model_Guestbook</classname>, to our
+        data source, <classname>Default_Model_DbTable_Guestbook</classname>. A typical API for a
+        data mapper is as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/GuestbookMapper.php
+
+class Default_Model_GuestbookMapper
+{
+    public function save($model);
+    public function find($id, $model);
+    public function fetchAll();
+}
+]]></programlisting>
+
+    <para>
+        In addition to these methods, we'll add methods for setting and retrieving the Table Data
+        Gateway. The final class, located in
+        <filename>application/models/GuestbookMapper.php</filename>, looks like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/GuestbookMapper.php
+
+class Default_Model_GuestbookMapper
+{
+    protected $_dbTable;
+
+    public function setDbTable($dbTable)
+    {
+        if (is_string($dbTable)) {
+            $dbTable = new $dbTable();
+        }
+        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
+            throw new Exception('Invalid table data gateway provided');
+        }
+        $this->_dbTable = $dbTable;
+        return $this;
+    }
+
+    public function getDbTable()
+    {
+        if (null === $this->_dbTable) {
+            $this->setDbTable('Default_Model_DbTable_Guestbook');
+        }
+        return $this->_dbTable;
+    }
+
+    public function save(Default_Model_Guestbook $guestbook)
+    {
+        $data = array(
+            'email'   => $guestbook->getEmail(),
+            'comment' => $guestbook->getComment(),
+            'created' => date('Y-m-d H:i:s'),
+        );
+
+        if (null === ($id = $guestbook->getId())) {
+            unset($data['id']);
+            $this->getDbTable()->insert($data);
+        } else {
+            $this->getDbTable()->update($data, array('id = ?' => $id));
+        }
+    }
+
+    public function find($id, Default_Model_Guestbook $guestbook)
+    {
+        $result = $this->getDbTable()->find($id);
+        if (0 == count($result)) {
+            return;
+        }
+        $row = $result->current();
+        $guestbook->setId($row->id)
+                  ->setEmail($row->email)
+                  ->setComment($row->comment)
+                  ->setCreated($row->created);
+    }
+
+    public function fetchAll()
+    {
+        $resultSet = $this->getDbTable()->fetchAll();
+        $entries   = array();
+        foreach ($resultSet as $row) {
+            $entry = new Default_Model_Guestbook();
+            $entry->setId($row->id)
+                  ->setEmail($row->email)
+                  ->setComment($row->comment)
+                  ->setCreated($row->created)
+                  ->setMapper($this);
+            $entries[] = $entry;
+        }
+        return $entries;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now it's time to update our model class slightly, to accomodate the data mapper. Just like
+        the data mapper contains a reference to the data source, the model contains a reference to
+        the data mapper. Additionally, we'll make it easy to populate the model by passing an array
+        of data either to the constructor or a <methodname>setOptions()</methodname> method. The
+        final model class, located in <filename>application/models/Guestbook.php</filename>, looks
+        like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/Guestbook.php
+
+class Default_Model_Guestbook
+{
+    protected $_comment;
+    protected $_created;
+    protected $_email;
+    protected $_id;
+    protected $_mapper;
+
+    public function __construct(array $options = null)
+    {
+        if (is_array($options)) {
+            $this->setOptions($options);
+        }
+    }
+
+    public function __set($name, $value)
+    {
+        $method = 'set' . $name;
+        if (('mapper' == $name) || !method_exists($this, $method)) {
+            throw new Exception('Invalid guestbook property');
+        }
+        $this->$method($value);
+    }
+
+    public function __get($name)
+    {
+        $method = 'get' . $name;
+        if (('mapper' == $name) || !method_exists($this, $method)) {
+            throw new Exception('Invalid guestbook property');
+        }
+        return $this->$method();
+    }
+
+    public function setOptions(array $options)
+    {
+        $methods = get_class_methods($this);
+        foreach ($options as $key => $value) {
+            $method = 'set' . ucfirst($key);
+            if (in_array($method, $methods)) {
+                $this->$method($value);
+            }
+        }
+        return $this;
+    }
+
+    public function setComment($text)
+    {
+        $this->_comment = (string) $text;
+        return $this;
+    }
+
+    public function getComment()
+    {
+        return $this->_comment;
+    }
+
+    public function setEmail($email)
+    {
+        $this->_email = (string) $email;
+        return $this;
+    }
+
+    public function getEmail()
+    {
+        return $this->_email;
+    }
+
+    public function setCreated($ts)
+    {
+        $this->_created = $ts;
+        return $this;
+    }
+
+    public function getCreated()
+    {
+        return $this->_created;
+    }
+
+    public function setId($id)
+    {
+        $this->_id = (int) $id;
+        return $this;
+    }
+
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    public function setMapper($mapper)
+    {
+        $this->_mapper = $mapper;
+        return $this;
+    }
+
+    public function getMapper()
+    {
+        if (null === $this->_mapper) {
+            $this->setMapper(new Default_Model_GuestbookMapper());
+        }
+        return $this->_mapper;
+    }
+
+    public function save()
+    {
+        $this->getMapper()->save($this);
+    }
+
+    public function find($id)
+    {
+        $this->getMapper()->find($id, $this);
+        return $this;
+    }
+
+    public function fetchAll()
+    {
+        return $this->getMapper()->fetchAll();
+    }
+}
+]]></programlisting>
+
+    <para>
+        Lastly, to connect these elements all together, lets create a guestbook controller that will
+        both list the entries that are currently inside the database.
+    </para>
+
+    <para>
+        To create a new controller, open a terminal or DOS console, navigate to your project
+        directory, and enter the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+# Unix-like systems:
+% zf.sh create controller guestbook
+
+# DOS/Windows:
+C:> zf.bat create controller guestbook
+]]></programlisting>
+
+    <para>
+        This will create a new controller, <classname>GuestbookController</classname>, in
+        <filename>application/controllers/GuestbookController.php</filename>, with a single action
+        method, <methodname>indexAction()</methodname>.  It will also create a view script directory
+        for the controller, <filename>application/views/scripts/guestbook/</filename>, with a view
+        script for the index action.
+    </para>
+
+    <para>
+        We'll use the "index" action as a landing page to view all guestbook entries.
+    </para>
+
+    <para>
+        Now, let's flesh out the basic application logic. On a hit to
+        <methodname>indexAction()</methodname>, we'll display all guestbook entries. This would look
+        like the following:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/controllers/GuestbookController.php
+
+class GuestbookController extends Zend_Controller_Action 
+{
+    public function indexAction()
+    {
+        $guestbook = new Default_Model_Guestbook();
+        $this->view->entries = $guestbook->fetchAll();
+    }
+}
+]]></programlisting>
+
+    <para>
+        And, of course, we need a view script to go along with that. Edit
+        <filename>application/views/scripts/guestbook/index.phtml</filename> to read as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/guestbook/index.phtml -->
+
+<p><a href="<?php echo $this->url(
+    array(
+        'controller' => 'guestbook',
+        'action'     => 'sign'
+    ), 
+    'default', 
+    true) ?>">Sign Our Guestbook</a></p>
+
+Guestbook Entries: <br />
+<dl>
+    <?php foreach ($this->entries as $entry): ?>
+    <dt><?php echo $this->escape($entry->email) ?></dt>
+    <dd><?php echo $this->escape($entry->comment) ?></dd>
+    <?php endforeach ?>
+</dl>
+]]></programlisting>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now browse to "http://localhost/guestbook". You should see the following in your
+            browser:
+        </para>
+
+        <para>
+            <inlinegraphic width="525" scale="100" align="center" valign="middle"
+                fileref="figures/learning.quickstart.create-model.png" format="PNG" />
+        </para>
+    </note>
+
+    <note>
+        <title>Using the data loader script</title>
+
+        <para>
+            The data loader script introduced in this section
+            (<filename>scripts/load.sqlite.php</filename>) can be used to create the database for
+            each environment you have defined, as well as to load it with sample data. Internally,
+            it utilizes <classname>Zend_Console_Getopt</classname>, which allows it to provide a
+            number of command line switches. If you pass the "-h" or "--help" switch, it will give
+            you the available options:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+Usage: load.sqlite.php [ options ]
+--withdata|-w         Load database with sample data
+--env|-e [  ]         Application environment for which to create database 
+                      (defaults to development)
+--help|-h             Help -- usage message)]]
+]]></programlisting>
+
+        <para>
+            The "-e" switch allows you to specify the value to use for the constant
+            <constant>APPLICATION_ENV</constant> -- which in turn allows you to create a SQLite
+            database for each environment you define.  Be sure to run the script for the environment
+            you choose for your application when deploying.
+        </para>
+    </note>
+</sect1>

+ 429 - 0
documentation/manual/en/tutorials/quickstart-create-project.xml

@@ -0,0 +1,429 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-project">
+    <title>Create Your Project</title>
+
+    <para>
+        In order to create your project, you must first download and extract Zend Framework.
+    </para>
+
+    <sect2 id="learning.quickstart.create-project.install-zf">
+        <title>Install Zend Framework</title>
+
+        <para>
+            The easiest way to get Zend Framework along with a complete PHP stack is by installing
+            <ulink url="http://www.zend.com/en/products/server-ce/downloads">Zend Server</ulink>.
+            Zend Server has native installers for Mac OSX, Windows, Fedora Core, and Ubuntu, as well
+            as a universal installation package compatible with most Linux distributions.
+        </para>
+
+        <para> 
+            After you have installed Zend Server, the Framework files may be found
+            under <filename>/Applications/ZendServer/share/ZendFramework</filename> on Mac
+            OSX, <filename>C:\Program Files\Zend\ZendServer\share\ZendFramework</filename> on
+            Windows, and <filename>/usr/local/zend/share/ZendFramework</filename> on Linux.
+            The <constant>include_path</constant> will already be configured to include
+            Zend Framework.
+        </para>
+
+        <para>
+            Alternately, you can <ulink url="http://framework.zend.com/download/latest">Download the
+            latest version of Zend Framework</ulink> and extract the contents; make a note of where
+            you have done so.
+        </para>
+
+        <para>
+            Optionally, you can add the path to the <filename>library/</filename> subdirectory of
+            the archive to your <filename>php.ini</filename>'s <constant>include_path</constant>
+            setting.
+        </para>
+
+        <para>
+            That's it! Zend Framework is now installed and ready to use.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.create-project">
+        <title>Create Your Project</title>
+
+        <note>
+            <title>zf Command Line Tool</title>
+
+            <para>
+                In your Zend Framework installation is a <filename>bin/</filename> subdirectory,
+                containing the scripts <filename>zf.sh</filename> and <filename>zf.bat</filename>
+                for Unix-based and Windows-based users, respectively. Make a note of the absolute
+                path to this script.
+            </para>
+
+            <para>
+                Wherever you see references to <filename>zf.sh</filename> or
+                <filename>zf.bat</filename>, please substitute the absolute path to the script. On
+                Unix-like systems, you may want to use your shell's alias functionality:
+                <command>alias zf.sh=path/to/ZendFramework/bin/zf.sh</command>.
+            </para>
+
+            <para>
+                If you have problems setting up the <command>zf</command> command-line tool, please
+                refer to <link linkend="zend.tool.framework.clitool.setup-general">the
+                    manual</link>.
+            </para>
+        </note>
+
+        <para>
+            Open a terminal (in Windows, <command>Start -> Run</command>, and then use
+            <command>cmd</command>). Navigate to a directory where you would like to start a
+            project. Then, use the path to the appropriate script, and execute one of the following:
+        </para>
+
+        <programlisting language="shell"><![CDATA[
+# Unix:
+% zf.sh create project quickstart
+
+# DOS/Windows:
+C:> zf.bat create project quickstart
+]]></programlisting>
+
+        <para>
+            Running this command will create your basic site structure, including your initial
+            controllers and views. The tree looks like the following:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+quickstart
+|-- application
+|   |-- Bootstrap.php
+|   |-- configs
+|   |   `-- application.ini
+|   |-- controllers
+|   |   |-- ErrorController.php
+|   |   `-- IndexController.php
+|   |-- models
+|   `-- views
+|       |-- helpers
+|       `-- scripts
+|           |-- error
+|           |   `-- error.phtml
+|           `-- index
+|               `-- index.phtml
+|-- library
+|-- public
+|   `-- index.php
+`-- tests
+    |-- application
+    |   `-- bootstrap.php
+    |-- library
+    |   `-- bootstrap.php
+    `-- phpunit.xml
+]]></programlisting>
+
+        <para>
+            At this point, if you haven't added Zend Framework to your
+            <constant>include_path</constant>, we recommend either copying or symlinking it into
+            your <filename>library/</filename> directory. In either case, you'll want to either
+            recursively copy or symlink the <filename>library/Zend/</filename> directory of your
+            Zend Framework installation into the <filename>library/</filename> directory of your
+            project. On unix-like systems, that would look like one of the following:
+        </para>
+
+        <programlisting language="shell"><![CDATA[
+# Symlink:
+% cd library; ln -s path/to/ZendFramework/library/Zend .
+
+# Copy:
+% cd library; cp -r path/to/ZendFramework/library/Zend .
+]]></programlisting>
+
+        <para>
+            On Windows systems, it may be easiest to do this from the Explorer.
+        </para>
+
+        <para>
+            Now that the project is created, the main artifacts to begin understanding are the
+            bootstrap, configuration, action controllers, and views.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.bootstrap">
+        <title>The Bootstrap</title>
+
+        <para>
+            Your <classname>Bootstrap</classname> class defines what resources and components to
+            initialize. By default, Zend Framework's <link linkend="zend.controller.front">Front
+                Controller</link> is initialized, and it uses the
+            <filename>application/controllers/</filename> as the default directory in which to look
+            for action controllers (more on that later). The class looks like the following:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+}
+]]></programlisting>
+
+        <para>
+            As you can see, not much is necessary to begin with.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.configuration">
+        <title>Configuration</title>
+
+        <para>
+            While Zend Framework is itself configurationless, you often need to configure your
+            application. The default configuration is placed in
+            <filename>application/configs/application.ini</filename>, and contains some basic
+            directives for setting your PHP environment (for instance, turning error reporting on
+            and off), indicating the path to your bootstrap class (as well as its class name), and
+            the path to your action controllers. It looks as follows:
+        </para>
+
+        <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+includePaths.library = APPLICATION_PATH "/../library"
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+]]></programlisting>
+
+        <para>
+            Several things about this file should be noted. First, when using INI-style
+            configuration, you can reference constants directly and expand them;
+            <constant>APPLICATION_PATH</constant> is actually a constant. Additionally note that
+            there are several sections defined: production, staging, testing, and development. The
+            latter three inherit settings from the "production" environment. This is a useful way to
+            organize configuration to ensure that appropriate settings are available in each stage
+            of application development.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.action-controllers">
+        <title>Action Controllers</title>
+
+        <para>
+            Your application's <emphasis>action controllers</emphasis> contain your application
+            workflow, and do the work of mapping your requests to the appropriate models and views.
+        </para>
+
+        <para>
+            An action controller should have one or more methods ending in "Action"; these methods
+            may then be requested via the web. By default, Zend Framework URLs follow the schema
+            <constant>/controller/action</constant>, where "controller" maps to the action
+            controller name (minus the "Controller" suffix) and "action" maps to an action method
+            (minus the "Action" suffix).
+        </para>
+
+        <para>
+            Typically, you always need an <classname>IndexController</classname>, which is a
+            fallback controller and which also serves the home page of the site, and an
+            <classname>ErrorController</classname>, which is used to indicate things such as HTTP
+            404 errors (controller or action not found) and HTTP 500 errors (application errors).
+        </para>
+
+        <para>
+            The default <classname>IndexController</classname> is as follows:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/controllers/IndexController.php
+
+class IndexController extends Zend_Controller_Action
+{
+
+    public function init()
+    {
+        /* Initialize action controller here */
+    }
+
+    public function indexAction()
+    {
+        // action body
+    }
+}
+]]></programlisting>
+
+        <para>
+            And the default <classname>ErrorController</classname> is as follows:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/controllers/ErrorController.php
+
+class ErrorController extends Zend_Controller_Action
+{
+
+    public function errorAction()
+    {
+        $errors = $this->_getParam('error_handler');
+        
+        switch ($errors->type) { 
+            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
+            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
+        
+                // 404 error -- controller or action not found
+                $this->getResponse()->setHttpResponseCode(404);
+                $this->view->message = 'Page not found';
+                break;
+            default:
+                // application error 
+                $this->getResponse()->setHttpResponseCode(500);
+                $this->view->message = 'Application error';
+                break;
+        }
+        
+        $this->view->exception = $errors->exception;
+        $this->view->request   = $errors->request;
+    }
+}
+]]></programlisting>
+
+        <para>
+            You'll note that (1) the <classname>IndexController</classname> contains no real code,
+            and (2) the <classname>ErrorController</classname> makes reference to a "view" property.
+            That leads nicely into our next subject.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.views">
+        <title>Views</title>
+
+        <para>
+            Views in Zend Framework are written in plain old PHP. View scripts are placed in
+            <filename>application/views/scripts/</filename>, where they are further categorized
+            using the controller names. In our case, we have an
+            <classname>IndexController</classname> and an <classname>ErrorController</classname>,
+            and thus we have corresponding <filename>index/</filename> and
+            <filename>error/</filename> subdirectories within our view scripts directory. Within
+            these subdirectories, you will then find and create view scripts that correspond to each
+            controller action exposed; in the default case, we thus have the view scripts
+            <filename>index/index.phtml</filename> and <filename>error/error.phtml</filename>.
+        </para>
+
+        <para>
+            View scripts may contain any markup you want, and use the <code>&lt;?php</code> opening
+            tag and <code>?&gt;</code> closing tag to insert PHP directives.
+        </para>
+
+        <para>
+            The following is what we install by default for the
+            <filename>index/index.phtml</filename> view script:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/index/index.phtml -->
+<style>
+    
+    a:link,
+    a:visited
+    {
+        color: #0398CA;
+    }
+
+    span#zf-name
+    {
+        color: #91BE3F;
+    }
+
+    div#welcome
+    {
+        color: #FFFFFF;
+        background-image: url(http://framework.zend.com/images/bkg_header.jpg);
+        width:  600px;
+        height: 400px;
+        border: 2px solid #444444;
+        overflow: hidden;
+        text-align: center;
+    }
+    
+    div#more-information
+    {
+        background-image: url(http://framework.zend.com/images/bkg_body-bottom.gif);
+        height: 100%;
+    }
+
+</style>
+<div id="welcome">
+    <h1>Welcome to the <span id="zf-name">Zend Framework!</span><h1 />
+    <h3>This is your project's main page<h3 />
+    <div id="more-information">
+        <p>
+            <img src="http://framework.zend.com/images/PoweredBy_ZF_4LightBG.png" />
+        </p>
+
+        <p>
+            Helpful Links: <br />
+            <a href="http://framework.zend.com/">Zend Framework Website</a> |
+            <a href="http://framework.zend.com/manual/en/">Zend Framework 
+                Manual</a>
+        </p>
+    </div>
+</div>
+]]></programlisting>
+
+        <para>
+            The <filename>error/error.phtml</filename> view script is slightly more interesting as
+            it uses some PHP conditionals:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/error/error.phtml -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"; 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd> 
+<html xmlns="http://www.w3.org/1999/xhtml"> 
+<head>  
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+  <title>Zend Framework Default Application</title> 
+</head> 
+<body> 
+  <h1>An error occurred</h1> 
+  <h2><?php echo $this->message ?></h2> 
+
+  <?php if ('development' == $this->env): ?> 
+  
+  <h3>Exception information:</h3> 
+  <p> 
+      <b>Message:</b> <?php echo $this->exception->getMessage() ?> 
+  </p> 
+
+  <h3>Stack trace:</h3> 
+  <pre><?php echo $this->exception->getTraceAsString() ?> 
+  </pre> 
+
+  <h3>Request Parameters:</h3> 
+  <pre><?php echo var_export($this->request->getParams(), 1) ?> 
+  </pre> 
+  <?php endif ?>
+  
+</body> 
+</html>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.checkpoint">
+        <title>Checkpoint</title>
+
+        <para>
+            At this point, you should be able to fire up your initial Zend Framework application.
+            Create a virtual host in your web server, and point its document root to your
+            application's <filename>public/</filename> subdirectory. Make sure your host's name is
+            in your DNS or hosts file, and then point your browser to it. You should be able to see
+            a welcome page at this point.
+        </para>
+    </sect2>
+</sect1>

+ 114 - 0
documentation/manual/en/tutorials/quickstart-intro-mvc.xml

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.intro">
+    <title>ZF &amp; MVC Introduction</title>
+
+    <sect2 id="learning.quickstart.intro.zf">
+        <title>Zend Framework</title>
+
+        <para>
+            Zend Framework is an open source, object oriented web application framework for PHP 5.
+            ZF is often called a 'component library', because it has many loosely coupled components
+            that you can use more or less independently. But Zend Framework also provides an
+            advanced Model-View-Controller (MVC) implementation that can be used to establish a
+            basic structure for your ZF applications. A full list of Zend Framework components along
+            with short descriptions may be found in the <ulink
+                url="http://framework.zend.com/about/components">components overview</ulink>. This
+            QuickStart will introduce you to some of ZF's most commonly used components, including
+            <classname>Zend_Controller</classname>, <classname>Zend_Layout</classname>,
+            <classname>Zend_Config</classname>, <classname>Zend_Db</classname>,
+            <classname>Zend_Db_Table</classname>, <classname>Zend_Registry</classname>, along
+            with a few view helpers.
+        </para>
+
+        <para>
+            Using these components, we will build a simple database-driven guest book application
+            within minutes. The complete source code for this application is available in the
+            following archives:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <ulink
+                        url="http://framework.zend.com/demos/ZendFrameworkQuickstart.zip">zip</ulink>
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <ulink
+                        url="http://framework.zend.com/demos/ZendFrameworkQuickstart.tar.gz">tar.gz</ulink>
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+
+    <sect2 id="learning.quickstart.intro.mvc">
+        <title>Model-View-Controller</title>
+
+        <para>
+            So what exactly is this MVC pattern everyone keeps talking about, and why should you
+            care? MVC is much more than just a three-letter acronym (TLA) that you can whip out
+            anytime you want to sound smart; it has become something of a standard in the design of
+            modern web applications. And for good reason. Most web application code falls under one
+            of the following three categories: presentation, business logic, and data access. The
+            MVC pattern models this separation of concerns well. The end result is that your
+            presentation code can be consolidated in one part of your application with your business
+            logic in another and your data access code in yet another. Many developers have found
+            this well-defined separation indispensable for keeping their code organized, especially
+            when more than one developer is working on the same application.
+        </para>
+
+        <note>
+            <title>More Information</title>
+
+            <para>
+                Let's break down the pattern and take a look at the individual pieces:
+            </para>
+
+            <para>
+                <inlinegraphic width="321" scale="100" align="center" valign="middle"
+                    fileref="figures/learning.quickstart.intro.mvc.png" format="PNG" />
+            </para>
+
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis role="strong">Model</emphasis> - This is the part of your
+                        application that defines its basic functionality behind a set of
+                        abstractions. Data access routines and some business logic can be defined in
+                        the model.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        <emphasis role="strong">View</emphasis> - Views define exactly what is
+                        presented to the user. Usually controllers pass data to each view to render
+                        in some format. Views will often collect data from the user, as well. This
+                        is where you're likely to find HTML markup in your MVC applications.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        <emphasis role="strong">Controller</emphasis> - Controllers bind the whole
+                        pattern together. They manipulate models, decide which view to display based
+                        on the user's request and other factors, pass along the data that each view
+                        will need, or hand off control to another controller entirely. Most MVC
+                        experts recommend <ulink
+                            url="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">keeping
+                        controllers as skinny as possible</ulink>.
+                    </para>
+                </listitem>
+            </itemizedlist>
+
+            <para>
+                Of course there is <ulink url="http://ootips.org/mvc-pattern.html">more to be
+                    said</ulink> about this critical pattern, but this should give you enough
+                background to understand the guestbook application we'll be building.
+            </para>
+        </note>
+    </sect2>
+</sect1>

+ 254 - 0
documentation/manual/en/tutorials/view-placeholders-basics.xml

@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.basics">
+    <title>Basic Placeholder Usage</title>
+
+    <para>
+        Zend Framework defines a generic <methodname>placeholder()</methodname> view helper that you
+        may use for as many custom placeholders you need. It also provides a variety of specific
+        placeholder implementations for often-needed functionality, such as specifying the
+        <acronym>DocType</acronym> declaration, document title, and more.
+    </para>
+
+    <para>
+        All placeholders operate in roughly the same way. They are containers, and thus allow you to
+        operate on them as collections. With them you can:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <emphasis>Append</emphasis> or <emphasis>prepend</emphasis> items to the collection.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Replace</emphasis> the entire collection with a single value.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>prepend output</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>append output</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>separate items</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Capture content</emphasis> into the collection.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Render</emphasis> the aggregated content.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Typically, you will call the helper with no arguments, which will return a container on
+        which you may operate. You will then either echo this container to render it, or call
+        methods on it to configure or populate it. If the container is empty, rendering it will
+        simply return an empty string; otherwise, the content will be aggregated according to the
+        rules by which you configure it.
+    </para>
+
+    <para>
+        As an example, let's create a sidebar that consists of a number of "blocks" of content.
+        You'll likely know up-front the structure of each block; let's assume for this example that
+        it might look like this:
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<div class="sidebar">
+    <div class="block">
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur aliquet
+            odio ac consectetur. Nulla quis eleifend tortor. Pellentesque varius, odio quis bibendum
+            consequat, diam lectus porttitor quam, et aliquet mauris orci eu augue.
+        </p>
+    </div>
+    <div class="block">
+        <ul>
+            <li><a href="/some/target">Link</a></li>
+            <li><a href="/some/target">Link</a></li>
+        </ul>
+    </div>
+</div>        
+]]></programlisting>
+
+    <para>
+        The content will vary based on the controller and action, but the structure will be the
+        same. Let's first setup the sidebar in a resource method of our bootstrap:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initSidebar()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+
+        $view->placeholder('sidebar')
+             // "prefix" -> markup to emit once, before all items in collection
+             ->setPrefix("<div class=\"sidebar\">\n    <div class=\"block\">\n")
+             // "separator" -> markup to emit between items in a collection
+             ->setSeparator("</div>\n    <div class=\"block\">\n")
+             // "postfix" -> markup to emit once, after all items in a collection
+             ->setPostfix("</div>\n</div>");
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        The above defines a placeholder, "sidebar", that has no items. It configures the basic
+        markup structure of that placeholder, however, per our requirements.
+    </para>
+
+    <para>
+        Now, let's assume for the "user" controller that for all actions we'll want a block at the
+        top containing some information. We could accomplish this in two ways: (a) we could add the
+        content to the placeholder directly in the controller's
+        <methodname>preDispatch()</methodname> method, or (b) we could render a view script from
+        within the <methodname>preDispatch()</methodname> method. We'll use (b), as it follows a
+        more proper separation of concerns (leaving view-related logic and functionality within a
+        view script).
+    </para>
+
+    <para>
+        We'll name the view script "user/_sidebar.phtml", and populate it as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<?php $this->placeholder('sidebar')->captureStart() ?>
+<h4>User Administration</h4>
+<ul>
+    <li><a href="<?php $this->url(array('action' => 'list')) ?>">
+        List</a></li>
+    <li><a href="<?php $this->url(array('action' => 'create')) ?>">
+        Create</a></a></li>
+</ul>
+<?php $this->placeholder('sidebar')->captureEnd() ?>
+]]></programlisting>
+
+    <para>
+        The above example makes use of the content capturing feature of placeholders. By default,
+        content is appended as a new item in the container, allowing us to aggregate content. This
+        example makes use of view helpers and static HTML in order to generate markup, and the
+        content is then captured and appended into the placeholder itself.
+    </para>
+
+    <para>
+        To invoke the above view script, we would write the following in our
+        <methodname>preDispatch()</methodname> method:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class UserController extends Zend_Controller_Action
+{
+    // ...
+
+    public function preDispatch()
+    {
+        // ...
+
+        $this->view->render('user/_sidebar.phtml');
+
+        // ...
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        Note that we're not capturing the rendered value; there's no need, as the entierty of that
+        view is being captured into a placeholder.
+    </para>
+
+    <para>
+        Now, let's assume our "view" action in that same controller needs to present some
+        information. Within the "user/view.phtml" view script, we might have the following snippet
+        of content:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$this->placeholder('sidebar')
+     ->append('<p>User: ' . $this->escape($this->username) .  '</p>');
+]]></programlisting>
+
+    <para>
+        This example makes use of the <methodname>append()</methodname> method, and passes it some
+        simple markup to aggregate.
+    </para>
+
+    <para>
+        Finally, let's modify our layout view script, and have it render the placeholder.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<html>
+<head>
+    <title>My Site</title>
+</head>
+<body>
+    <div class="content">
+        <?php echo $this->layout()->content ?>
+    </div>
+    <?php echo $this->placeholder('sidebar') ?>
+</body>
+</html>
+]]></programlisting>
+
+    <para>
+        For controllers and actions that do not populate the "sidebar" placeholder, no content will
+        be rendered; for those that do, however, echoing the placeholder will render the content
+        according to the rules we created in our bootstrap, and the content we aggregated throughout
+        the application. In the case of the "/user/view" action, and assuming a username of
+        "matthew", we would get content for the sidebar as follows (formatted for readability):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<div class="sidebar">
+    <div class="block">
+        <h4>User Administration</h4>
+        <ul>
+            <li><a href="/user/list">List</a></li>
+            <li><a href="/user/create">Create</a></a></li>
+        </ul>
+    </div>
+    <div class="block">
+        <p>User: matthew</p>
+    </div>
+</div>        
+]]></programlisting>
+
+    <para>
+        There are a large number of things you can do by combining placeholders and layout scripts;
+        experiment with them, and read the <link
+            linkend="zend.view.helpers.initial.placeholder">relevant manual sections</link> for more
+        information.
+    </para>
+</sect1>

+ 16 - 0
documentation/manual/en/tutorials/view-placeholders-conclusion.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.conclusion">
+    <title>View Placeholders: Conclusion</title>
+
+    <para>
+        View placeholders are a simple and powerful method for creating rich layouts for your
+        application. You can use a variety of standard placeholders, such as those discussed
+        (<methodname>doctype()</methodname>, <methodname>headTitle()</methodname>,
+        <methodname>headLink()</methodname>, and <methodname>headScript()</methodname>), or use
+        the generic <methodname>placeholder()</methodname> helper to aggregate content and render it
+        in custom ways. Experiment with their exposed functionality, and visit the appropriate
+        sections in the reference guide to find out about the additional features they offer -- and
+        how you may leverage those features to create rich content for your readers.
+    </para>
+</sect1>

+ 41 - 0
documentation/manual/en/tutorials/view-placeholders-intro.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.intro">
+    <title>Introduction</title>
+
+    <para>
+        In <link linkend="learning.layout">the previous chapter</link>, we looked at primarily the
+        Two Step View pattern, which allows you to embed individual application views within a
+        sitewide layout. At the end of that chapter, however, we discussed some limitations:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                How do you alter the page title?
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                How would you inject conditional scripts or stylesheets into the sitewide layout?
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                How would you create and render an optional sidebar? What if there was some content
+                that was unconditional, and other content that was conditional for the sidebar?
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        These questions are addressed in the <ulink
+            url="http://java.sun.com/blueprints/corej2eepatterns/Patterns/CompositeView.html">Composite
+        View</ulink> design pattern. One approach to that pattern is to provide "hints" or content
+        to the sitewide layout. In Zend Framework, this is achieved through specialized view helpers
+        called "placeholders." Placeholders allow you to aggregate content, and then render that
+        aggregate content elsewhere.
+    </para>
+</sect1>

+ 460 - 0
documentation/manual/en/tutorials/view-placeholders-standard.xml

@@ -0,0 +1,460 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.standard">
+    <title>Standard Placeholders</title>
+
+    <para>
+        In the <link linkend="learning.view.placeholders.basics">previous section</link>, we learned
+        about the <methodname>placeholder()</methodname> view helper, and how it can be used to
+        aggregate custom content. In this section, we'll look at some of the concrete placeholders
+        shipped with Zend Framework, and how you can use them to your advantage when creating
+        complex composite layouts.
+    </para>
+
+    <para>
+        Most of the shipped placeholders are for generating content for the
+        <code>&lt;head&gt;</code> section of your layout content -- an area you typically cannot
+        manipulate directly via your application view scripts, but one you may want to influence. As
+        examples: you may want your title to contain certain content on every page, but specific
+        content based on the controller and/or action; you may want to specify
+        <acronym>CSS</acronym> files to load based on what section of the application you're in; you
+        may need specific JavaScript scripts loaded at different times; or you may want to set the
+        <acronym>DocType</acronym> declaration.
+    </para>
+
+    <para>
+        Zend Framework ships with placeholder implementations for each of these situations, and
+        several more.
+    </para>
+
+    <sect2 id="learning.view.placeholders.standard.doctype">
+        <title>Setting the DocType</title>
+
+        <para>
+            <acronym>DocType</acronym> declarations are troublesome to memorize, and often essential
+            to include in your document to ensure the browser properly renders your content. The
+            <methodname>doctype()</methodname> view helper allows you to use simple string mnemonics
+            to specify the desired <acronym>DocType</acronym>; additionally, other helpers will
+            query the <methodname>doctype()</methodname> helper to ensure the output generated
+            conforms with the requested <acronym>DocType</acronym>.
+        </para>
+
+        <para>
+            As an example, if you want to use the <acronym>XHTML1</acronym> Strict
+            <acronym>DTD</acronym>, you can simply specify:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$this->doctype('XHTML1_STRICT');
+]]></programlisting>
+
+        <para>
+            Among the other available mnemonics, you'll find these common types:
+        </para>
+
+        <variablelist>
+            <varlistentry>
+                <term>XHTML1_STRICT</term>
+
+                <listitem>
+                    <para>
+                        <acronym>XHTML</acronym> 1.0 Strict
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>XHTML1_TRANSITIONAL</term>
+
+                <listitem>
+                    <para>
+                        <acronym>XHTML</acronym> 1.0 Transitional
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML4_STRICT</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 4.01 Strict
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML4_Loose</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 4.01 Loose
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML5</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 5
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+
+        <para>
+            You can assign the type and render the declaration in a single call:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+echo $this->doctype('XHTML1_STRICT');
+]]></programlisting>
+
+        <para>
+            However, the better approach is to assign the type in your bootstrap, and then render it
+            in your layout. Try adding the following to your bootstrap class:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDocType()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+        <para>
+            Then, in your layout script, simply <functionname>echo</functionname> the helper at the
+            top of the file:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will ensure that your DocType-aware view helpers render the appropriate markup, 
+            ensure that the type is set well before the layout is rendered, and provide a single
+            location to change the DocType.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-title">
+        <title>Specifying the Page Title</title>
+
+        <para>
+            Often, a site will include the site or business name as  part of the page title, and
+            then add additional information based on the location within the site. As an example,
+            the zend.com website includes the string "Zend.com" on all pages, and the prepends
+            information based on the page: "Zend Server - Zend.com". Within Zend Framework, the
+            <methodname>headTitle()</methodname> view helper can help simplify this task.
+        </para>
+
+        <para>
+            At its simplest, the <methodname>headTitle()</methodname> helper allows you to aggregate
+            content for the <code>&lt;title&gt;</code> tag; when you echo it, it then assembles it
+            based on the order in which segments are added. You can control the order using
+            <methodname>prepend()</methodname> and <methodname>append()</methodname>, and provide a
+            separator to use between segments using the <methodname>setSeparator()</methodname>
+            method.
+        </para>
+
+        <para>
+            Typically, you should specify any segments common to all pages in your bootstrap,
+            similar to how we define the doctype. In this case, we'll define a
+            <methodname>_initPlaceholders()</methodname> method for operating on all the various
+            placeholders, and specify an initial title as well as a separator.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Within a view script, we might want to add another segment:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headTitle()->append('Some Page'); // place after other segments ?>
+<?php $this->headTitle()->prepend('Some Page'); // place before ?>
+]]></programlisting>
+
+        <para>
+            In our layout, we will simply echo the <methodname>headTitle()</methodname> helper:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<!-- If append() was used: -->
+<title>My Site :: Some Page</title>
+
+<!-- If prepend() was used: -->
+<title>Some Page :: My Site</title>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-link">
+        <title>Specifying Stylesheets with HeadLink</title>
+
+        <para>
+            Good CSS developers will often create a general stylesheet for sitewide styles, and
+            individual stylesheets for specific sections or pages of the website, and load these
+            latter conditionally so as to decrease the amount of data needing to be transferred on
+            each request. The <methodname>headLink()</methodname> placeholder makes such conditional
+            aggregation of stylesheets trivial within your application.
+        </para>
+
+        <para>
+            To accomplish this, <methodname>headLink()</methodname> defines a number of "virtual"
+            methods (via overloading) to make the process trivial. The ones we will be concerned
+            with are <methodname>appendStylesheet()</methodname> and
+            <methodname>prependStylesheet()</methodname>. Each takes up to four arguments,
+            <varname>$href</varname> (the relative path to the stylesheet),
+            <varname>$media</varname> (the MIME type, which defaults to "text/css"),
+            <varname>$conditionalStylesheet</varname> (which can be used to specify a "condition"
+            under which the stylesheet will be evaluated), and <varname>$extras</varname> (an
+            associative array of key/value pairs, commonly used to specify a key for "media"). In
+            most cases, you will only need to specify the first argument, the relative path to the
+            stylesheet.
+        </para>
+
+        <para>
+            In our example, we'll assume that all pages need to load the stylesheet located in
+            "/styles/site.css" (relative to the document root); we'll specify this in our
+            <methodname>_initPlaceholders()</methodname> bootstrap method.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+
+        // Set the initial stylesheet:
+        $view->headLink()->prependStylesheet('/styles/site.css');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Later, in a controller or action-specific view script, we can add more stylesheets:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headLink()->appendStylesheet('/styles/user-list.css') ?>
+]]></programlisting>
+
+        <para>
+            Within our layout view script, once again, we simply echo the placeholder:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <?php echo $this->headLink() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<link rel="stylesheet" type="text/css" href="/styles/site.css" />
+<link rel="stylesheet" type="text/css" href="/styles/user-list.css" />
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-script">
+        <title>Aggregating Scripts Using HeadScript</title>
+
+        <para>
+            Another common tactic to prevent long page load times is to only load JavaScript when
+            necessary. That said, you may need several layers of scripts: perhaps one for
+            progressively enhancing menus on the site, and another for page-specific content. In
+            these situations, the <methodname>headScript()</methodname> helper presents a solution.
+        </para>
+
+        <para>
+            Similar to the <methodname>headLink()</methodname> helper,
+            <methodname>headScript()</methodname> provides the ability to append or prepend scripts
+            to the collection, and then echo the entire set. It provides the flexibility to specify
+            either script files themselves to load, or explicit JavaScript. You also have the option
+            of capturing JavaScript via
+            <methodname>captureStart()</methodname>/<methodname>captureEnd()</methodname>, which
+            allows you to simply inline the JavaScript instead of requiring an additional call to
+            your server.
+        </para>
+
+        <para>
+            Also like <methodname>headLink()</methodname>, <methodname>headScript</methodname>
+            provides "virtual" methods via overloading as a convenience when specifying items to
+            aggregate; common methods include <methodname>prependFile()</methodname>,
+            <methodname>appendFile()</methodname>, <methodname>prependScript()</methodname>, and
+            <methodname>appendScript()</methodname>. The first two allow you to specify files that
+            will be referenced in a <code>&lt;script&gt;</code> tag's <varname>src</varname>
+            attribute; the latter two will take the content provided and render it as literal
+            JavaScript within a <code>&lt;script&gt;</code> tag.
+        </para>
+
+        <para>
+            In this example, we'll specify that a script, "/js/site.js" needs to be loaded on every
+            page; we'll update our <methodname>_initPlaceholders()</methodname> bootstrap method to
+            do this.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+
+        // Set the initial stylesheet:
+        $view->headLink()->prependStylesheet('/styles/site.css');
+
+        // Set the initial JS to load:
+        $view->headScript()->prependFile('/js/site.js');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Within a view script, we might then add an extra script file to source, or capture some
+            JavaScript to include in our document.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headScript()->appendFile('/js/user-list.js') ?>
+<?php $this->headScript()->captureStart() ?>
+site = {
+    baseUrl: "<?php echo $this->baseUrl() ?>"
+};
+<?php $this->headScript()->captureEnd() ?>
+]]></programlisting>
+
+        <para>
+            Within our layout script, we then simply echo the placeholder, just as we have all the
+            others:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <?php echo $this->headLink() ?> 
+    <?php echo $this->headScript() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<script type="text/javascript" src="/js/site.js"></script>
+<script type="text/javascript" src="/js/user-list.js"></script>
+<script type="text/javascript">
+site = {
+    baseUrl: "<?php echo $this->baseUrl() ?>"
+};
+</script>
+]]></programlisting>
+
+        <note>
+            <title>InlineScript Variant</title>
+
+            <para>
+                Many browsers will often block display of a page until all scripts and stylesheets
+                referenced in the <code>&lt;head&gt;</code> section have loaded. If you have a
+                number of such directives, this can impact how soon somebody can start actually
+                viewing the page.
+            </para>
+
+            <para>
+                One way around this is to emit your <code>&lt;script&gt;</code> tags just prior to
+                closing the <code>&lt;body&gt;</code> of your document. (This is a practice
+                specifically recommend by the <ulink
+                    url="http://developer.yahoo.com/yslow/">Y! Slow project</ulink>.)
+            </para>
+
+            <para>
+                Zend Framework supports this in two different ways:
+            </para>
+
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        You can render your <methodname>headScript()</methodname> tag whereever you
+                        like in your layout script; just because the title references "head" does
+                        not mean it needs to be rendered in that location.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        Alternately, you may use the <methodname>inlineScript()</methodname> helper,
+                        which is simply a variant on <methodname>headScript()</methodname>, and
+                        retains the same behavior, but uses a separate registry.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </note>
+    </sect2>
+</sect1>