diff --git a/3rd_party/php/propel/CHANGELOG b/3rd_party/php/propel/CHANGELOG
new file mode 100644
index 000000000..fe4ff5fde
--- /dev/null
+++ b/3rd_party/php/propel/CHANGELOG
@@ -0,0 +1,107 @@
+= Changelog Of The Propel 1.5 Branch =
+
+== 2010-06-17: Version 1.5.2 ==
+
+ * [1810] Changed default table type keyword to ENGINE for MySQL (closes #969)
+ * [1809] Added a way to read virtual columns starting with a lowercase character (closes #993)
+ * [1808] Added connection object to the FK getter (closes #1018)
+ * [1807] Fixed namespace issue with `soft_delete` behavior (closes #1015)
+ * [1806] Fixed issue with instance pooling and soft_delete behavior (closes #1016)
+ * [1805] Added namespace declaration to model class interface (closes #1014)
+ * [1804] Improved generated class code when using namespaces (refs #683)
+ * [1803] Documented namespace.autoPackage build property (refs #1005)
+ * [1802] Added support for package autosetting based on namespace attribute (refs #1005)
+ * [1801] Fixed related instance pooling clear in case of an emulated on delete cascade / set null (refs #1012)
+ * [1800] Fixed onDelete cascade and setnull for self-referencing foreign keys (closes #1012)
+ * [1799] Fixed `ModelCriteria::find()` throws `Exception` instead of `PropelException`
+ * [1798] Fixed hard-to-debug unit test exception message
+ * [1797] Fixed cascade deletion emulation when `Criteria` is modified by `doSelect()` (closes #1008)
+ * [1796] Added `ModelCriteria::findOneOrCreate()` (closes #1009)
+ * [1795] Fixed `delete()` called in iterator breaks on demand formatter (closes #1006)
+ * [1794] Fixed double iteration on Propel collection (closes #1004) (patch from jeremyp)
+ * [1793] Documented namespaces (refs #683)
+ * [1792] Added support for namespaces in many-to-many relationships (refs #683)
+ * [1791] Added support for namespaces in single table inheritance (refs #683)
+ * [1790] Added mention of a common error code in runtime settings documentation
+ * [1789] Documented the simple templating system (refs #1002)
+ * [1788] Allowed namespace tests to run alongside normal tests (refs #683)
+ * [1787] Fixed `PropelObjectCollection::toArray()` when the collection is empty (closes #1001)
+ * [1786] Fixed runtime doc typo
+ * [1785] Refactored the `aggregate_column` behavior to take advantage of the buildtime simple templating engine (refs #1002, #995)
+ * [1784] Added simple templating engine for behaviors (refs #1002)
+ * [1783] Added a !HowTo on writing behaviors (should have been published in the blog, but Posterous is having troubles with code samples)
+ * [1782] Improved namespace support in generated `TableMap` classes (refs #683)
+ * [1781] Introducing Model Namespaces (PHP 5.3 only) (WIP) (refs #683)
+ * [1780] Fixed generated `filterByXXX()` for string columns when using custom comparison
+ * [1779] Added `aggregate_column` behavior (refs #995)
+ * [1778] Refactored `ForeignKey` class in generator
+ * [1777] [doc] Fixed typo in CRUD chapter
+ * [1776] Fixed generated relation names for tables with symmetrical foreign keys (closes #968)
+ * [1775] Fixed generated relation names for tables with more than one self-referencing foreign key (closes #972)
+ * [1774] Fixed copy of foreign keys with hardcoded refPhpName in concrete inheritance behavior (closes #988)
+ * [1773] Changing runtime autoload strategy (closes #974):
+ * Using absolute path in core autoloading
+ * introducing `PropelAutoloader` for models
+ * removing the need for include path change in installation docs
+ * [1772] Added failed SQL query to `BasePeer` exception messages (closes #979)
+ * [1771] Documented the schema autosuggest feature in supported IDEs
+ * [1770] Expanded the schema XSD annotations for easier schema autocompletion
+ * [1769] showcasing link to XSD file in schema to allow autocompletion on NetBeans
+ * [1768] Fixed typos in `ModelCriteria` doc (closes #978) (patch from Frosty)
+ * [1767] Fixed typo in install doc (closes #576)
+ * [1766] Fixed schema DTD does not validate schemas without behaviors (closes #973)
+ * [1765] Added the ability to comment the generated SQL query from a Criteria (closes #970)
+ * [1764] Fixed limitation in schema size when transformation or external schema is included (closes #971)
+ * [1763] Fixed limitation in schema size when no transformation nor external schema is included (closes #971)
+
+== 2010-05-10: Version 1.5.1 ==
+
+ * [1759] Moved ModelWith runtime class to formatter directory
+ * [1758] Fixed warning with new StringReader
+ * [1757] Reduced console logging when building an up-to-date schema
+ * [1756] Parsing schemas as strings instead of files (closes #967)
+ * [1755] Reverting r1548 to allow inclusion of external schemas (refs #967)
+ * [1754] Documented custom defaultJoin type (refs #870) (closes #936)
+ * [1749] fix Criteria::addCond() example and php-doc (closes #964)
+ * [1748] fix Join::addCondition() php-doc (closes #963)
+ * [1747] Add getJoin() method to ModelCriteria (closes #961)
+ * [1745] Fixed auto_add_pk behavior when using separate schemas (closes #956)
+ * [1743] Refactored ModelCriteria::count() to allow query cache on counts
+ * [1742] Fixed propel-gen executable on windows (closes #942)
+ * [1741] disabled query cloning by default, you can enable it on a per query basis using keepQuery() (refs #953)
+ * [1740] Fixed shallow Criteria cloning (refs #953)
+ * [1739] Fixed overriding primary key in a new object (closes #960)
+ * [1738] Fixed generated joinXXX() methods used in secondary Criteria (closes #958)
+ * [1737] Fixed array hydration (refs #954, #959)
+ * [1736] Added unit test to demonstrate Array Hydration regression (refs #959)
+ * [1735] Fixed typo in MySQL DDL builder (closes #957)
+ * [1734] fixed doc typos (patch from Frosty) (closes #955)
+ * [1733] Refactored hydration schema
+ * removed circular dependency between Criteria and Formatter (refs #891)
+ * formatters now copy the necessary hydration data from the ModelCriteria
+ * Should improve memory handling in large resultsets
+ * removed PropelFormatter::setCriteria() and checkCriteria (refs #892)
+ * [1732] refactored on demand hydration (refs #954), removed ModelJoin storage in ModelWith
+ * [1731] Refactored Joined Array hydration (refs #954)
+ * [1730] Changed Propel::enableInstancePooling() return value
+ * [1729] Added a exception to explicit the limits of one-to-many joined hydration
+ * [1728] Refactored joined object hydration
+ * Now deals correctly with any join chain (refs #954)
+ * Faster for large resultsets and long join chains
+ * [1727] refactored BasePeer::doDelete() to handle table aliases and perform better (closes #949)
+ * [1726] Small Criteria optimization
+ * [1725] Fixed ModelCriteria::delete() fails when using true table alias (closes #949)
+ * [1724] Allowed Merging of Criteria objects to combien conditions with an OR (closes #951)
+ * [1723] Added the ability to reindex a collection (closes #851)
+ * [1722] Gave a public way to remove an alias (useful when merging Criterias)
+ * [1721] Added ModelCriteria::postUpdate() and ModelCriteria::postDelete() hooks (closes #945)
+ * [1720] Fixed issue with instance pooling and composite fkeys in peer classes (closes #924)
+ * [1719] Fixed E_STRICT warning in concrete table inheritance (closes #910)
+ * [1718] Added unit tests for PropelObjectCollection::toKeyValue(), and made it just a little smarter (closes #943)
+ * [1717] Fixed typo in Relationships doc closes #941)
+ * [1716] Fixed reverse task and validators (closes #928)
+ * [1715] Fixed phpDoc for Criteria::getCriterion() (closes #929)
+ * [1714] Fixed regression in orderBy() used in conjunction with ignoreCase (closes #946)
+ * [1712] Fixed concrete table inheritance with more than one level (closes #922)
+ * [1711] Fixed filterByXXX() when passed an explicit Criteria::EQUAL (closes #944)
+ * [1710] Fixed references to 1.4 pages in documentaton (closes #937)
\ No newline at end of file
diff --git a/3rd_party/php/propel/INSTALL b/3rd_party/php/propel/INSTALL
new file mode 100644
index 000000000..cb7effd16
--- /dev/null
+++ b/3rd_party/php/propel/INSTALL
@@ -0,0 +1,4 @@
+I N S T A L L I N G P R O P E L
+==================================
+
+See docs/guide/01-Installation.txt for detailed installation instructions.
\ No newline at end of file
diff --git a/3rd_party/php/propel/LICENSE b/3rd_party/php/propel/LICENSE
new file mode 100644
index 000000000..1b4925a27
--- /dev/null
+++ b/3rd_party/php/propel/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2005-2010 Hans Lellelid, David Zuelke, Francois Zaninotto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/3rd_party/php/propel/WHATS_NEW b/3rd_party/php/propel/WHATS_NEW
new file mode 100644
index 000000000..9120f63ae
--- /dev/null
+++ b/3rd_party/php/propel/WHATS_NEW
@@ -0,0 +1,807 @@
+= What's new in Propel 1.5? =
+
+[[PageOutline]]
+
+First and foremost, don't be frightened by the long list of new features that follows. Propel 1.5 is completely backwards compatible with Propel 1.4 and 1.3, so there is no hidden cost to benefit from these features. If you didn't do it already, upgrade the propel libraries, rebuild your model, and you're done - your application can now use the Propel 1.5 features.
+
+== New Query API ==
+
+This is the killer feature of Propel 1.5. It will transform the painful task of writing Criteria queries into a fun moment.
+
+=== Model Queries ===
+
+Along Model and Peer classes, Propel 1.5 now generates one Query class for each table. These Query classes inherit from Criteria, but have additional abilities since the Propel generator has a deep knowledge of your schema. That means that Propel 1.5 advises that you use ModelQueries instead of raw Criteria.
+
+Model queries have smart filter methods for each column, and termination methods on their own. That means that instead of writing:
+
+{{{
+#!php
+add(BookPeer::TITLE, 'War And Peace');
+$book = BookPeer::doSelectOne($c);
+}}}
+
+You can write:
+
+{{{
+#!php
+filterByTitle('War And Peace');
+$book = $q->findOne();
+}}}
+
+In addition, each Model Query class benefits from a factory method called `create()`, which returns a new instance of the query class. And the filter methods return the current query object. So it's even easier to write the previous query as follows:
+
+{{{
+#!php
+filterByTitle('War And Peace');
+ ->findOne();
+}}}
+
+The termination methods are `find()`, `findOne()`, `count()`, `paginate()`, `update()`, and `delete()`. They all accept a connection object as last parameter.
+
+Remember that a Model Query IS a Criteria. So your Propel 1.4 code snippets still work:
+
+{{{
+#!php
+addJoin(BookPeer::AUTHOR_ID, AuthorPeer::ID);
+ ->add(AuthorPeer::LAST_NAME, 'Tolstoi')
+ ->addAscendingOrderByColumn(BookPeer::TITLE)
+ ->findOne();
+}}}
+
+But you will soon see that it's faster to use the generated methods of the Model Query classes:
+
+{{{
+#!php
+useAuthorQuery();
+ ->filterByLastName('Tolstoi')
+ ->endUse()
+ ->orderByTitle()
+ ->findOne();
+}}}
+
+That's right, you can embed a query into another; Propel guesses the join to apply from the foreign key declared in your schema.
+
+That makes it very easy to package your own custom model logic into reusable query methods. After a while, your code can easily look like the following:
+
+{{{
+#!php
+filterByPublisher($publisher)
+ ->cheap()
+ ->recent()
+ ->useAuthorQuery();
+ ->stillAlive()
+ ->famous()
+ ->endUse()
+ ->orderByTitle()
+ ->find();
+}}}
+
+The Model Queries can understand `findByXXX()` method calls, where `'XXX'` is the phpName of a column of the model. That answers one of the most common customization need:
+
+{{{
+#!php
+findOneByTitle('War And Peace');
+}}}
+
+Eventually, these Query classes will replace the Peer classes; you should place all the code necessary to request or alter Model object in these classes. The Criteria/Peer way of doing queries still work exactly the same as in previous Propel versions, so your existing applications won't suffer from this update.
+
+'''Tip''': Incidentally, if you use an IDE with code completion, you will see that writing a query has never been so easy.
+
+=== Collections And On-Demand Hydration ===
+
+The `find()` method of generated Model Query objects returns a `PropelCollection` object. You can use this object just like an array of model objects, iterate over it using `foreach`, access the objects by key, etc.
+
+{{{
+#!php
+limit(5)
+ ->find(); // $books is a PropelCollection object
+foreach ($books as $book) {
+ echo $book->getTitle();
+}
+}}}
+
+Propel also returns a `PropelCollection` object instead of an array when you use a getter for a one-to-many relationship:
+
+{{{
+#!php
+getBooks(); // $books is a PropelCollection object
+}}}
+
+If your code relies on list of objects being arrays, you will need to update it a little. The `PropelCollection` object provides a method for most common array operations:
+
+{{{
+Array | Collection object
+------------------------ | -----------------------------------------
+foreach($books as $book) | foreach($books as $book)
+count($books) | count($books) or $books->count()
+$books[]= $book | $books[]= $book or $books->append($book)
+$books[0] | $books[0] or $books->getFirst()
+$books[123] | $books[123] or $books->offsetGet(123)
+unset($books[1]) | unset($books[1]) or $books->remove(1)
+empty($books) | $books->isEmpty()
+in_array($book, $books) | $books->contains($book)
+array_pop($books) | $books->pop()
+etc.
+}}}
+
+'''Warning''': `empty($books)` always returns false when using a collection, even on a non-empty one. This is a PHP limitation. Prefer `$books->isEmpty()`, or `count($books)>0`.
+
+'''Tip''': If you can't afford updating your code to support collections instead of arrays, you can still ask Propel to generate 1.4-compatible model objects by overriding the `propel.builder.object.class` setting in your `build.properties`, as follows:
+
+{{{
+#!ini
+propel.builder.object.class = builder.om.PHP5ObjectNoCollectionBuilder
+}}}
+
+The `PropelCollection` class offers even more methods that you will soon use a lot:
+
+{{{
+#!php
+getArrayCopy() // get the array inside the collection
+$books->toArray() // turn all objects to associative arrays
+$books->getPrimaryKeys() // get an array of the primary keys of all the objects in the collection
+$books->getModel() // return the model of the collection, e.g. 'Book'
+}}}
+
+Another advantage of using a collection instead of an array is that Propel can hydrate model objects on demand. Using this feature, you'll never fall short of memory again. Available through the `setFormatter()` method of Model Queries, on-demand hydration is very easy to trigger:
+
+{{{
+#!php
+limit(50000)
+ ->setFormatter(ModelCriteria::FORMAT_ON_DEMAND) // just add this line
+ ->find();
+foreach ($books as $book) {
+ echo $book->getTitle();
+}
+}}}
+
+In this example, Propel will hydrate the `Book` objects row by row, after the `foreach` call, and reuse the memory between each iteration. The consequence is that the above code won't use more memory when the query returns 50,000 results than when it returns 5.
+
+`ModelCriteria::FORMAT_ON_DEMAND` is one of the many formatters provided by the new Query objects. You can also get a collection of associative arrays instead of objects, if you don't need any of the logic stored in your model object, by using `ModelCriteria::FORMAT_ARRAY`.
+
+The [wiki:Users/Documentation/1.5/ModelCriteria documentation] describes each formatter, and how to use it.
+
+=== Model Criteria ===
+
+Generated Model Queries inherit from `ModelCriteria`, which extends your good old `Criteria`, and adds a few useful features. Basically, a `ModelCriteria` is a `Criteria` linked to a Model; by using the information stored in the generated TableMaps at runtime, `ModelCriteria` offers powerful methods to simplify the process of writing a query.
+
+For instance, `ModelCriteria::where()` provides similar functionality to `Criteria::add()`, except that its [http://www.php.net/manual/en/pdostatement.bindparam.php PDO-like syntax] removes the burden of Criteria constants for comparators.
+
+{{{
+#!php
+where('Book.Title LIKE ?', 'War And P%')
+ ->findOne();
+}}}
+
+Propel analyzes the clause passed as first argument of `where()` to determine which escaping to use for the value passed as second argument. In the above example, the `Book::TITLE` column is declared as `VARCHAR` in the schema, so Propel will bind the title as a string.
+
+The `where()` method can also accept more complex clauses. You just need to explicit every column name as `'ModelClassName.ColumnPhpName'`, as follows:
+
+{{{
+#!php
+where('UPPER(Book.Title) LIKE ?', 'WAR AND P%')
+ ->where('(Book.Price * 100) <= ?', 1500)
+ ->findOne();
+}}}
+
+Another great addition of `ModelCriteria` is the `join()` method, which just needs the name of a related model to build a JOIN clause:
+
+{{{
+#!php
+join('Book.Author')
+ ->where('CONCAT(Author.FirstName, " ", Author.LastName) = ?', 'Leo Tolstoi')
+ ->find();
+}}}
+
+`ModelCriteria` has a built-in support for table aliases, which allows to setup a query using two joins on the same table, which was not possible with the `Criteria` object:
+
+{{{
+#!php
+join('b.Author a') // use 'a' as an alias for 'Author' in the query
+ ->where('CONCAT(a.FirstName, " ", a.LastName) = ?', 'Leo Tolstoi')
+ ->find();
+}}}
+
+This syntax probably looks familiar, because it is very close to SQL. So you probably won't need long to figure out how to write a complex query with it. The documentation offers [wiki:Users/Documentation/1.5/ModelCriteria an entire chapter] dedicated to the new `ModelCriteria` class. Make sure you read it to see the power of this new query API.
+
+=== Criteria Enhancements ===
+
+Generated queries and ModelQueries are not the only ones to have received a lot of attention in Propel 1.5. The Criteria object itself sees a few improvements, that will ease the writing of queries with complex logic.
+
+`Criteria::addOr()` operates the way you always expected it to. For instance, in Propel 1.4, `addOr()` resulted in a SQL `AND` if called on a column with no other condition:
+
+{{{
+#!php
+add(BookPeer::TITLE, '%Leo%', Criteria::LIKE);
+$c->addOr(BookPeer::TITLE, '%Tolstoi%', Criteria::LIKE);
+// translates in SQL as
+// WHERE (book.TITLE LIKE '%Leo%' OR book.TITLE LIKE '%Tolstoi%')
+
+// addOr() used to fail on a column with no existing condition
+$c = new Criteria();
+$c->add(BookPeer::TITLE, '%Leo%', Criteria::LIKE);
+$c->addOr(BookPeer::ISBN, '1234', Criteria::EQUAL);
+// translates in SQL as
+// WHERE book.TITLE LIKE '%Leo%' AND book.ISBN = '1234'
+}}}
+
+This is fixed in Propel 1.5. This means that you don't need to call upon the `Criterion` object for a simple OR clause:
+
+{{{
+#!php
+add(BookPeer::TITLE, '%Leo%', Criteria::LIKE);
+$c->addOr(BookPeer::ISBN, '1234', Criteria::EQUAL);
+// translates in SQL as
+// WHERE (book.TITLE LIKE '%Leo%' OR book.ISBN = '1234')
+
+// and it's much faster to write than
+$c = new Criteria();
+$c1 = $c->getNewCriterion(BookPeer::TITLE, '%Leo%', Criteria::LIKE);
+$c2 = $c->getNewCriterion(BookPeer::ISBN, '1234', Criteria::EQUAL);
+$c1->addOr($c2);
+$c->add($c1);
+}}}
+
+`add()` and `addOr()` only allow simple logical operations on a single condition. For more complex logic, Propel 1.4 forced you to use Criterions again. This is no longer the case in Propel 1.5, which provides a new `Criteria::combine()` method. It expects an array of named conditions to be combined, and an operator. Use `Criteria::addCond()` to create a condition, instead of the usual `add()`:
+
+{{{
+#!php
+addCond('cond1', BookPeer::TITLE, 'Foo', Criteria::EQUAL); // creates a condition named 'cond1'
+$c->addCond('cond2', BookPeer::TITLE, 'Bar', Criteria::EQUAL); // creates a condition named 'cond2'
+$c->combine(array('cond1', 'cond2'), Criteria::LOGICAL_OR); // combine 'cond1' and 'cond2' with a logical OR
+// translates in SQL as
+// WHERE (book.TITLE = 'Foo' OR book.TITLE = 'Bar');
+}}}
+
+`combine()` accepts more than two conditions at a time:
+{{{
+#!php
+addCond('cond1', BookPeer::TITLE, 'Foo', Criteria::EQUAL);
+$c->addCond('cond2', BookPeer::TITLE, 'Bar', Criteria::EQUAL);
+$c->addCond('cond3', BookPeer::TITLE, 'FooBar', Criteria::EQUAL);
+$c->combine(array('cond1', 'cond2', 'cond3'), Criteria::LOGICAL_OR);
+// translates in SQL as
+// WHERE ((book.TITLE = 'Foo' OR book.TITLE = 'Bar') OR book.TITLE = 'FooBar');
+}}}
+
+`combine()` itself can return a named condition to be combined later. So it allows for any level of logical complexity:
+
+{{{
+#!php
+addCond('cond1', BookPeer::TITLE, 'Foo', Criteria::EQUAL);
+$c->addCond('cond2', BookPeer::TITLE, 'Bar', Criteria::EQUAL);
+$c->combine(array('cond1', 'cond2'), Criteria::LOGICAL_OR, 'cond12');
+
+$c->addCond('cond3', BookPeer::ISBN, '1234', Criteria::EQUAL);
+$c->addCond('cond4', BookPeer::ISBN, '4567', Criteria::EQUAL);
+$c->combine(array('cond3', 'cond4'), Criteria::LOGICAL_OR, 'cond34');
+
+$c->combine(array('cond12', 'cond34'), Criteria::LOGICAL_AND);
+
+// WHERE (book.TITLE = 'Foo' OR book.TITLE = 'Bar')
+// AND (book.ISBN = '1234' OR book.ISBN = '4567');
+}}}
+
+The new `combine()` method makes it much easier to handle logically complex criterions. The good news is that if your application code already uses the old Criterion way, it will continue to work with Propel 1.5 as all these changes are backwards compatible.
+
+Of course, since Model Queries extend Criteria, this new feature is available for all your queries, with a slightly different syntax, in order to support column phpNames:
+
+{{{
+#!php
+condition('cond1', 'Book.Title = ?', 'Foo')
+ ->condition('cond2', 'Book.Title = ?', 'Bar')
+ ->combine(array('cond1', 'cond2'), Criteria::LOGICAL_OR, 'cond12')
+ ->condition('cond3', 'Book.ISBN = ?', '1234')
+ ->condition('cond4', 'Book.ISBN = ?', '4567')
+ ->combine(array('cond3', 'cond4'), Criteria::LOGICAL_OR, 'cond34')
+ ->combine(array('cond12', 'cond34'), Criteria::LOGICAL_AND)
+ ->find();
+// WHERE (book.TITLE = 'Foo' OR book.TITLE = 'Bar')
+// AND (book.ISBN = '1234' OR book.ISBN = '4567');
+}}}
+
+== Many-to-Many Relationships ==
+
+At last, Propel generates the necessary methods to retrieve related objects in a many-to-many relationship. Since this feature is often needed, many developers already wrote these methods themselves. To avoid method collision, the generation of many-to-many getters is therefore optional.
+
+All you have to do is to add the `isCrossRef` attribute to the cross reference table, and rebuild your model. For instance, if a `User` has many `Groups`, and the `Group` has many `Users`, the many-to-many relationship is materialized by a `user_group` cross reference table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Then, both end of the relationship see the other end through a one-to-many relationship. That means that you can deal with related objects just like you normally do, without ever creating instances of the cross reference object:
+
+{{{
+#!php
+setName('John Doe');
+$group = new Group();
+$group->setName('Anonymous');
+// relate $user and $group
+$user->addGroup($group);
+// save the $user object, the $group object, and a new instance of the UserGroup class
+$user->save();
+
+// retrieve objects as if they shared a one-to-many relationship
+$groups = $user->getGroups();
+
+// the model query also features a smart filter method for the relation
+$groups = GroupPeer::create()
+ ->filterByUser($user)
+ ->find();
+}}}
+
+The syntax should be no surprise, since it's the same as the one for one-to-many relationships. Find more details about many-to-many relationships in the [wiki:Users/Documentation/1.5/Relationships relationships documentation].
+
+== New Behaviors ==
+
+The new behavior system, introduced in Propel 1.4, starts to unleash its true power with this release. Three new behaviors implement the most common customizations of object models: `nested_sets`, `sluggable`, and `sortable`.
+
+=== Nested Set Behavior ===
+
+Using the `treeMode` attribute in a schema, you could turn a Propel model into a hierarchical data store starting with Propel 1.3. This method is now deprecated in favor of a new `nested_set` behavior, that does eactly the same thing, but in a more extensible and effective way.
+
+The main difference between the two implementations is performance. On the first levels of a large tree, the Propel 1.3 implementation of Nested sets used to consume a very large amount of memory and CPU to retrieve the siblings or the children of a given node. This is no longer true with the new behavior.
+
+This performance boost comes at a small price: you must add a new "level" column to your nested set models, and let the behavior update this column for the whole tree.
+
+For instance, if you used nested sets to keep a list of categories, the schema used to look like:
+
+{{{
+#!xml
+
+
+
+
+
+
+}}}
+
+The upgrade path is then pretty straightforward:
+
+1 - Update the schema, by removing the `treeMode` and `nestedSet` attributes and adding the `nested_set` behavior and the `tree_level` column:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+2 - Rebuild the model
+
+3 - Change the parent class of your model classes (object and peer) that used the nested set `treeMode`:
+
+{{{
+#!php
+
+
+
+
+
+
+
+
+}}}
+
+Now, every time you save a new `Post` object, Propel will compose its slug according to the pattern defined in the behavior parameter and save it in an additional `slug` column:
+
+{{{
+#!php
+setTitle('How Is Life On Earth?');
+$post1->setContent('Lorem Ipsum...');
+$post1->save();
+echo $post1->getSlug(); // '/posts/how-is-life-on-earth'
+}}}
+
+Propel replaces every name enclosed between brackets in the slug pattern by the related column value. It also cleans up the string to make it URL-compatible, and ensures that it is unique.
+
+If you use this slug in URLs, you will need to retrieve a `Post` object based on it. This is just a one-liner:
+
+{{{
+#!php
+findOneBySlug('/posts/how-is-life-on-earth');
+}}}
+
+There are many ways to customize the `sluggable` behavior to match the needs of your applications. Check the new [wiki:Users/Documentation/1.5/Behaviors/sluggable sluggable behavior documentation] for more details.
+
+=== Concrete Table Inheritance Behavior ===
+
+Propel has offered [wiki:Users/Documentation/1.5/Inheritance#SingleTableInheritance Single Table Inheritance] for a long time. But for complex table inheritance needs, it is necessary to provide [http://martinfowler.com/eaaCatalog/concreteTableInheritance.html Concrete Table Inheritance]. Starting with Propel 1.5, this inheritance implementation is supported through the new `concrete_inheritance` behavior.
+
+In the following example, the `article` and `video` tables use this behavior to inherit the columns and foreign keys of their parent table, `content`:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+The behavior copies the columns of the parent table to the child tables. That means that the generated `Article` and `Video` models have a `Title` property and a `Category` relationship:
+
+{{{
+#!php
+setName('Movie');
+$cat->save();
+// create a new Article
+$art = new Article();
+$art->setTitle('Avatar Makes Best Opening Weekend in the History');
+$art->setCategory($cat);
+$art->setContent('With $232.2 million worldwide total, Avatar had one of the best-opening weekends in the history of cinema.');
+$art->save();
+// create a new Video
+$vid = new Video();
+$vid->setTitle('Avatar Trailer');
+$vid->setCategory($cat);
+$vid->setResourceLink('http://www.avatarmovie.com/index.html')
+$vid->save();
+}}}
+
+If Propel stopped there, the `concrete_inheritance` behavior would only provide a shorcut to avoid repeating tags in the schema. But wait, there is more: the `Article` and `Video` classes actually extend the `Content` class:
+
+{{{
+#!php
+getCategory()->getName();
+ }
+}
+echo $art->getCategoryName(); // 'Movie'
+echo $vid->getCategoryName(); // 'Movie'
+}}}
+
+And the true power of Propel's Concrete Table Inheritance is that every time you save an `Article` or a `Video` object, Propel saves a copy of the `title` and `category_id` columns in a `Content` object. Consequently, retrieving objects regardless of their child type becomes very easy:
+
+{{{
+#!php
+find();
+foreach ($conts as $content) {
+ echo $content->getTitle() . "(". $content->getCategoryName() ")/n";
+}
+// Avatar Makes Best Opening Weekend in the History (Movie)
+// Avatar Trailer (Movie)
+}}}
+
+The resulting relational model is denormalized - in other terms, data is copied across tables - but the behavior takes care of everything for you. That allows for very effective read queries on complex inheritance structures.
+
+Check out the brand new [wiki:Users/Documentation/1.5/Inheritance#ConcreteTableInheritance Inheritance Documentation] for more details on using and customizing this behavior.
+
+=== Sortable Behavior ===
+
+Have you ever enhanced a Propel Model to give it the ability to move up or down in an ordered list? The `sortable` behavior, new in Propel 1.5, offers exactly that... and even more.
+
+As usual for behaviors, activate `sortable` in your `schema.yml`:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Then rebuild your model, and you're done. You have just created an ordered task list for users:
+
+{{{
+#!php
+setTitle('Wash the dishes');
+$t1->setUser($paul);
+$t1->save();
+echo $t1->getRank(); // 1
+$t2 = new Task();
+$t2->setTitle('Do the laundry');
+$t2->setUser($paul);
+$t2->save();
+echo $t2->getRank(); // 2
+$t3 = new Task();
+$t3->setTitle('Rest a little');
+$t3->setUser($john);
+$t3->save()
+echo $t3->getRank(); // 1, because John has his own task list
+
+// retrieve the tasks
+$allPaulsTasks = TaskPeer::retrieveList($scope = $paul->getId());
+$allJohnsTasks = TaskPeer::retrieveList($scope = $john->getId());
+$t1 = TaskPeer::retrieveByRank($rank = 1, $scope = $paul->getId());
+$t2 = $t1->getNext();
+$t2->moveUp();
+echo $t2->getRank(); // 1
+echo $t1->getRank(); // 2
+}}}
+
+This new behavior is fully unit tested and very customizable. Check out all you can do with `sortable` in the [wiki:Users/Documentation/1.5/Behaviors/sortable sortable behavior documentation].
+
+=== Timestampable Behavior ===
+
+This behavior is not new, since it was introduced in Propel 1.4. However, with the introduction of model queries, it gains specific query methods that will ease your work when retrieving objects based on their update date:
+
+{{{
+#!php
+recentlyUpdated() // adds a minimum value for the update date
+ ->lastUpdatedFirst() // orders the results by descending update date
+ ->find();
+}}}
+
+== Better `toArray()` ==
+
+When you call `toArray()` on a model object, you can now ask for the related objects:
+
+{{{
+#!php
+toArray($keyType = BasePeer::TYPE_COLNAME, $includeLazyLoadColumns = true, $includeForeignObjects = true);
+print_r($bookArray);
+ => array(
+ 'Id' => 123,
+ 'Title' => 'War And Peace',
+ 'ISBN' => '3245234535',
+ 'AuthorId' => 456,
+ 'PublisherId' => 567
+ 'Author' => array(
+ 'Id' => 456,
+ 'FirstName' => 'Leo',
+ 'LastName' => 'Tolstoi'
+ ),
+ 'Publisher' => array(
+ 'Id' => 567,
+ 'Name' => 'Penguin'
+ )
+ )
+}}}
+
+Only the related objects that were already hydrated appear in the result, so `toArray()` never issues additional queries. Together with the ability to return arrays instead of objects when using `PropelQuery`, this addition will help to debug and optimize model code.
+
+== Better Oracle Support ==
+
+The Oracle adapter for the generator, the reverse engineering, and the runtime components have been greatly improved. This should provide an easier integration of Propel with an Oracle database.
+
+== Code Cleanup ==
+
+=== Directory Structure Changes ===
+
+The organization of the Propel runtime and generator code has been reworked, in order to make navigation across Propel classes easier for developers. End users should see no difference, apart if your `build.properties` references alternate builder classes in the Propel code. In that case, you will need to update your `build.properties` with the new paths. For instance, a reference to:
+
+{{{
+#!ini
+propel.builder.peer.class = propel.engine.builder.om.php5.PHP5PeerBuilder
+}}}
+
+Must be changed to:
+
+{{{
+#!ini
+propel.builder.peer.class = builder.om.PHP5PeerBuilder
+}}}
+
+Browse the Propel generator directory structure to find the classes you need.
+
+=== DebugPDO Refactoring ===
+
+To allow custom connection handlers, the debug code that was written in the `DebugPDO` class has been moved to `PropelPDO`. The change is completely backwards compatible, but makes it easier to connect to a database without using PDO.
+
+During the change, the [wiki:Users/Documentation/1.5/07-Logging documentation about Propel logging and debugging features] was rewritten and should now be clearer.
+
+== propel-gen Script Modifications ==
+
+The `propel-gen` script no longer requires a path to the project directory if you call it from a project directory. That means that calling `propel-gen` with a single argument defaults to expecting a task name:
+
+{{{
+> cd /path/to/my/project
+> propel-gen reverse
+}}}
+
+By default, the `propel-gen` command called without a task name defaults to the `main` task (and builds the model, the SQL, and the generation).
+
+Note: The behavior of the `propel-gen` script when called with one parameter differs from what it used to be in Propel 1.4, where the script expected a path in every situation. So the following syntax won't work anymore:
+
+{{{
+> propel-gen /path/to/my/project
+}}}
+
+Instead, use either:
+
+{{{
+> cd /path/to/my/project
+> propel-gen
+}}}
+
+or:
+
+{{{
+> propel-gen /path/to/my/project main
+}}}
+
+== License Change ==
+
+Propel is more open-source than ever. To allow for an easier distribution, the open-source license of the Propel library changes from LGPL3 to MIT. This [http://en.wikipedia.org/wiki/MIT_License MIT License] is also known as the X11 License.
+
+This change removes a usage restriction enforced by the LGPL3: you no longer need to release any modifications to the core Propel source code under a LGPL compatible license.
+
+Of course, you still have the right to use, copy, modify, merge, publish, distribute, sublicense, and/or sell Propel. In other terms, you can do whatever you want with the Propel code, without worrying about the license, as long as you leave the LICENSE file within.
+
+== Miscellaneous ==
+
+ * Generated model classes now offer a `fromArray()` and a `toArray()` method by default. This feature existed before, but was disabled by default in the `build.properties`. The `addGenericAccessors` and `addGenericMutators` settings are therefore enabled by default in Propel 1.5.
+ * You can now prefix all the table names of a database schema by setting the `tablePrefix` attribute of the `` tag.
+ * The `addIncludes` build property introduced in Propel 1.4 is now set to `false` by default. That means that the runtime autoloading takes care of loading all classes at runtime, including generated Base classes.
+ * A bugfix in the name generator for related object getter in tables with two foreign keys related to the same table may have introduced problems in applications relying on old (wrong) names. Check your generated base model classes for the `getXXXrelatedByYYY()` and modify the application code relying on it if it exists. A good rule of thumb to avoid problems in such case is to name your relations by using the `phpName` and `refPhpName` attributes in the `` element in the schema.
+ * XSL transformation of your schemas is no longer enabled by default. Turn the `propel.schema.transform` setting to `true` in your `build.properties` to enable it again. This change removes the requirement on the libxslt extention for Propel.
+ * `ModelObject::addSelectColumns()` now accepts an additional parameter to allow the use of table aliases
+ * Added `ModelObject::clear()` to reinitialize a model object
+ * Added `ModelObject::isPrimaryKeyNull()` method to check of an object was hydrated with no values (in case of a left join)
+ * Added `Criteria::addSelectModifier($modifier)` to add more than one select modifier (e.g. 'SQL_CALC_FOUND_ROWS', 'HIGH_PRIORITY', etc.)
+ * Added `PeerClass::addGetPrimaryKeyFromRow()` to retrieve the Primary key from a result row
+ * Added a new set of constants in the generated Peer class to list column names without the table name (this is `BasePeer::TYPE_RAW_COLNAME`)
+ * Removed references to Creole in the code (Propel uses PDO instead of Creole since version 1.3)
\ No newline at end of file
diff --git a/3rd_party/php/propel/contrib/dbd2propel/dbd2propel.xsl b/3rd_party/php/propel/contrib/dbd2propel/dbd2propel.xsl
new file mode 100644
index 000000000..852af4ace
--- /dev/null
+++ b/3rd_party/php/propel/contrib/dbd2propel/dbd2propel.xsl
@@ -0,0 +1,381 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TIMESTAMP
+ LONGVARCHAR
+ BOOLEAN
+ BLOB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ restrict
+ cascade
+ setnull
+ restrict
+
+
+
+
diff --git a/3rd_party/php/propel/contrib/dbd2propel/transform.php b/3rd_party/php/propel/contrib/dbd2propel/transform.php
new file mode 100644
index 000000000..129f19f85
--- /dev/null
+++ b/3rd_party/php/propel/contrib/dbd2propel/transform.php
@@ -0,0 +1,21 @@
+load('model.xml');
+
+// load the transformation stylesheet
+$xsl = new DOMDocument;
+$xsl->load('dbd2propel.xsl');
+
+$proc = new XSLTProcessor();
+// attach the xsl rules
+$proc->importStyleSheet($xsl);
+
+$schema_xml = $proc->transformToXML($xml);
+
+file_put_contents('schema.xml', $schema_xml);
diff --git a/3rd_party/php/propel/contrib/pat/patForms.php b/3rd_party/php/propel/contrib/pat/patForms.php
new file mode 100644
index 000000000..a1c66a135
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms.php
@@ -0,0 +1,2784 @@
+
+ * @author gERD Schaufelberger
+ * @author Stephan Schmidt
+ * @copyright 2003-2004 PHP Application Tools
+ * @license LGPL
+ * @link http://www.php-tools.net
+ */
+
+/**
+ * set the include path
+ */
+if ( !defined( 'PATFORMS_INCLUDE_PATH' ) ) {
+ define( 'PATFORMS_INCLUDE_PATH', dirname( __FILE__ ). '/patForms' );
+}
+
+/**
+ * needs helper methods of patForms_Element
+ */
+include_once PATFORMS_INCLUDE_PATH . "/Element.php";
+
+/**
+ * error definition: renderer base class file (renderers/_base.php) could not
+ * be found.
+ *
+ * @see patForms::_createModule()
+ */
+define( "PATFORMS_ERROR_NO_MODULE_BASE_FILE", 1001 );
+
+/**
+ * error definition: the specified renderer could not be found.
+ *
+ * @see patForms::_createModule()
+ */
+define( "PATFORMS_ERROR_MODULE_NOT_FOUND", 1002 );
+
+/**
+ * error definition: the element added via the {@link patForms::addElement()}
+ * is not an object. Use the {@link patForms::createElement()} method to
+ * create an element object.
+ *
+ * @see patForms::addElement()
+ * @see patForms::createElement()
+ */
+define( "PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT", 1003 );
+
+/**
+ * error definition: generic unexpected error.
+ */
+define( "PATFORMS_ERROR_UNEXPECTED_ERROR", 1004 );
+
+/**
+ * element does not exist
+ */
+define( "PATFORMS_ERROR_ELEMENT_NOT_FOUND", 1012 );
+
+/**
+ * renderer object has not been set - if you want to render the form, you have to
+ * set a renderer object via the {@link patForms::setRenderer()} method. To create
+ * a renderer, use the {@link patForms::createRenderer()} method.
+ *
+ * @see patForms::setRenderer()
+ * @see patForms::createRenderer()
+ */
+define( "PATFORMS_ERROR_NO_RENDERER_SET", 1013 );
+
+/**
+ * invalid renderer
+ *
+ * @see createRenderer()
+ */
+define( "PATFORMS_ERROR_INVALID_RENDERER", 1014 );
+
+/**
+ * invalid method
+ *
+ * @see setMethod()
+ */
+define( "PATFORMS_ERROR_INVALID_METHOD", 1015 );
+
+/**
+ * Given parameter is not a boolean value
+ */
+define( "PATFORMS_ERROR_PARAMETER_NO_BOOL", 1016 );
+
+/**
+ * Given Static property does not exist
+ */
+define( "PATFORMS_ERROR_NO_STATIC_PROPERTY", 1017 );
+
+/**
+ * Unknown event
+ */
+define( "PATFORMS_ERROR_UNKNOWN_EVENT", 1018 );
+
+/**
+ * Invalid event handler
+ */
+define( "PATFORMS_ERROR_INVALID_HANDLER", 1019 );
+
+/**
+ * Event exists
+ */
+define( 'PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED', 1020 );
+
+/**
+ * Invalid storage container
+ */
+define( 'PATFORMS_ERROR_INVALID_STORAGE', 1021 );
+
+define( 'PATFORMS_NOTICE_ARRAY_EXPECTED', 1022 );
+
+define( 'PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED', 1023 );
+
+define( 'PATFORMS_NOTICE_INVALID_OPTION', 1024 );
+
+define( 'PATFORMS_ERROR_ATTRIBUTE_REQUIRED', 1025 );
+
+define( 'PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT', 1026 );
+
+define( 'PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE', 1027 );
+
+
+/**
+ * errors apply on translating errors matching current locale settings
+ */
+define( 'PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED', 1028 );
+define( 'PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED', 1029 );
+
+/**
+ * apply the rule before the built-in validation
+ */
+define( 'PATFORMS_RULE_BEFORE_VALIDATION', 1 );
+
+/**
+ * apply the rule after the built-in validation
+ */
+define( 'PATFORMS_RULE_AFTER_VALIDATION', 2 );
+
+/**
+ * apply the rule before AND after the built-in validation
+ */
+define( 'PATFORMS_RULE_BOTH', 3 );
+
+/**
+ * attach the observer to the elements
+ */
+define( 'PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS', 1 );
+
+/**
+ * attach the observer to the form
+ */
+define( 'PATFORMS_OBSERVER_ATTACH_TO_FORM', 2 );
+
+/**
+ * attach the observer to the form and the elements
+ */
+define( 'PATFORMS_OBSERVER_ATTACH_TO_BOTH', 3 );
+
+/**
+ * group values should stay nested
+ */
+define('PATFORMS_VALUES_NESTED', 0);
+
+/**
+ * group values should be flattened
+ */
+define('PATFORMS_VALUES_FLATTENED', 1);
+
+/**
+ * group values should be prefixed
+ */
+define('PATFORMS_VALUES_PREFIXED', 2);
+
+/**
+ * Static patForms properties - used to emulate pre-PHP5 static properties.
+ *
+ * @see setStaticProperty()
+ * @see getStaticProperty()
+ */
+$GLOBALS['_patForms'] = array(
+ 'format' => 'html',
+ 'locale' => 'C',
+ 'customLocales' => array(),
+ 'autoFinalize' => true,
+ 'defaultAttributes' => array(),
+);
+
+/**
+ * patForms form manager class - serialize form elements into any given output format
+ * using element classes, and build the output via renderer classes.
+ *
+ * @package patForms
+ * @author Sebastian Mordziol
+ * @author gERD Schaufelberger
+ * @author Stephan Schmidt
+ * @copyright 2003-2004 PHP Application Tools
+ * @license LGPL
+ * @link http://www.php-tools.net
+ * @version 0.9.0alpha
+ * @todo check the clientside functionality, as that can lead to broken pages
+ */
+class patForms
+{
+ /**
+ * javascript that will displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalJavascript = array();
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceJavascript = array();
+
+ /**
+ * stores the mode for the form. It defaults to 'default', and is only overwritten if
+ * set specifically. It is passed on to any elements you create.
+ *
+ * @access private
+ * @see setMode()
+ */
+ var $mode = 'default';
+
+ /**
+ * XML entities
+ *
+ * @access private
+ * @see toXML()
+ * @todo This is redundant to the Element's xmlEntities property - find a way to keep this in one place
+ */
+ var $xmlEntities = array(
+ "<" => "<",
+ ">" => ">",
+ "&" => "&",
+ "'" => "'",
+ '"' => """
+ );
+
+ /**
+ * stores the format for the element. It defaults to 'html', and is only overwritten if
+ * set specifically. It is passed on to any elements you create.
+ *
+ * @access private
+ * @see setFormat()
+ */
+ var $format = 'html';
+
+ /**
+ * stores the flag telling the form whether it has been submitted - this is passed on to any
+ * elements you create.
+ *
+ * @access private
+ * @see setSubmitted()
+ */
+ var $submitted = false;
+
+ /**
+ * stores the element objects of this form.
+ * @access private
+ * @see addElement()
+ */
+ var $elements = array();
+
+ /**
+ * stores the current element count for this form, used to generate the ids for each element
+ * @access private
+ * @see getElementId()
+ */
+ var $elementCounter = 0;
+
+ /**
+ * stores a renderer
+ * @access private
+ * @see setRenderer(), renderForm()
+ */
+ var $renderer = null;
+
+ /**
+ * stores the locale to use when adding validation errors for the whole form.
+ *
+ * @access private
+ * @var string $locale
+ * @see setLocale()
+ */
+ var $locale = 'C';
+
+ /**
+ * stores custom locale
+ *
+ * @access private
+ * @var array
+ * @see setLocale()
+ */
+ var $customLocales = array();
+
+ /**
+ * stores the element name
+ * @access private
+ * @see getElementName()
+ */
+ var $elementName = 'Form';
+
+ /**
+ * flag to indicate, whether form should be validated automatically
+ * by renderForm()
+ *
+ * @access private
+ * @var string
+ * @see setAutoValidate(), renderForm()
+ */
+ var $autoValidate = false;
+
+ /**
+ * name of the variable that indicates, whether the form has
+ * been submitted.
+ *
+ * @access private
+ * @var string
+ * @see setAutoValidate()
+ */
+ var $submitVar = null;
+
+ /**
+ * event handlers
+ *
+ * @access private
+ * @var array
+ * @see registerEventHandler()
+ * @see registerEvent()
+ */
+ var $_eventHandler = array();
+
+ /**
+ * events that can be triggered
+ *
+ * @access private
+ * @var array
+ * @see registerEventHandler()
+ * @see triggerEvent()
+ * @see registerEvent()
+ */
+ var $_validEvents = array( 'onInit', 'onValidate', 'onSubmit', 'onError', 'onSuccess' );
+
+ /**
+ * Stores whether the current form has been validated
+ *
+ * @access private
+ */
+ var $validated = false;
+
+ /**
+ * Stores whether the current form is valid or not (after the
+ * validation process)
+ *
+ * @access private
+ */
+ var $valid = null;
+
+ /**
+ * Stores the names of all static properties that patForms will use as defaults
+ * for the properties with the same name on startup.
+ *
+ * @access private
+ */
+ var $staticProperties = array(
+ 'format' => 'setFormat',
+ 'autoFinalize' => 'setAutoFinalize',
+ 'locale' => 'setLocale',
+ );
+
+ /**
+ * Stores the flag for the autoFinalize feature
+ *
+ * @access private
+ */
+ var $autoFinalize = true;
+
+ /**
+ * custom validation rules
+ *
+ * @access private
+ * @var array
+ */
+ var $_rules = array();
+
+ /**
+ * define error codes an messages for the form
+ *
+ * Will be set by validation rules that have been
+ * added to the form.
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ */
+ var $validatorErrorCodes = array();
+
+ /**
+ * stores any validation errors that can occurr during the
+ * form's validation process.
+ *
+ * @access private
+ * @var array $validationErrors
+ */
+ var $validationErrors = array();
+
+ /**
+ * next error offset for rules
+ * @access private
+ * @var integer
+ */
+ var $nextErrorOffset = 1000;
+
+ /**
+ * Attributes of the form - needed to generate the form tag
+ *
+ * @access private
+ * @var array $attributes
+ * @see setAttribute()
+ */
+ var $attributes = array();
+
+ /**
+ * Attribute definition for the form - defines which attribute the form
+ * itself supports.
+ *
+ * @access public
+ */
+ var $attributeDefinition = array(
+
+ 'id' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'name' => array(
+ 'required' => true,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'method' => array(
+ 'required' => true,
+ 'format' => 'string',
+ 'default' => 'post',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'action' => array(
+ 'required' => true,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'accept' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'accept-charset' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'enctype' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'onreset' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'onsubmit' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+
+ 'target' => array(
+ 'required' => false,
+ 'format' => 'string',
+ 'outputFormats' => array( 'html' ),
+ ),
+ );
+
+ /**
+ * Stores all available patForms options - these are inherited by all elements
+ * and their dependencies, like rules.
+ *
+ * Short option overview:
+ *
+ * - scripts: enable client script integration
+ *
+ * @access public
+ */
+ var $options = array(
+
+ 'scripts' => array(
+ 'enabled' => true,
+ 'params' => array(),
+ ),
+
+ );
+
+ /**
+ * observers of the form
+ *
+ * @access private
+ * @var array
+ */
+ var $observers = array();
+
+ /**
+ * Sets the default attributes that will be inherited by any elements you add to the form.
+ *
+ * Note: You have to call this method statically before creating a new form if you use
+ * patForm's automatic element creation feature via the {@link createForm()} method, as the
+ * default attributes cannot be set after an element has been created.
+ *
+ * @static
+ * @access public
+ * @param array $attributes The list of attributes to set with key => value pairs.
+ */
+ function setDefaultAttributes( $attributes )
+ {
+ patForms::setStaticProperty( 'defaultAttributes', $attributes );
+ }
+
+ /**
+ * sets the locale (language) to use for the validation error messages of all elements
+ * in the form.
+ *
+ * @access public
+ * @param string language code
+ * @param string optional language file
+ * @return bool True on success
+ */
+ function setLocale( $locale, $languageFile = null )
+ {
+ if (!is_null($languageFile)) {
+ $languageData = patForms::parseLocaleFile($languageFile);
+
+ $customLocales = patForms::getStaticProperty('customLocales');
+ $customLocales[$locale] = $languageData;
+ patForms::setStaticProperty('customLocales', $customLocales);
+ }
+
+ if ( isset( $this ) && is_a( $this, 'patForms' ) ) {
+ $this->locale = $locale;
+
+ if ( !empty( $this->elements ) ) {
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ ) {
+ $this->elements[$i]->setLocale( $locale );
+ }
+ }
+ } else {
+ patForms::setStaticProperty('locale', $locale);
+ }
+
+ return true;
+ }
+
+ /**
+ * checks, whether a locale is a custom locale
+ *
+ * @static
+ * @access public
+ * @param string locale name
+ * @return boolean
+ */
+ function isCustomLocale($locale)
+ {
+ $customLocales = patForms::getStaticProperty('customLocales');
+ if (isset($customLocales[$locale])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * get the custom locale for an element or a rule
+ *
+ * @static
+ * @access public
+ * @param string locale
+ * @param string key
+ * @return array
+ */
+ function getCustomLocale($locale, $key)
+ {
+ $customLocales = patForms::getStaticProperty('customLocales');
+ if (!isset($customLocales[$locale])) {
+ return false;
+ }
+ if (!isset($customLocales[$locale][$key])) {
+ return false;
+ }
+ return $customLocales[$locale][$key];
+ }
+
+ /**
+ * parses a locale file
+ *
+ * @access private
+ * @param string filename
+ * @return array locale information
+ * @todo add some file checks
+ */
+ function parseLocaleFile($filename)
+ {
+ return parse_ini_file($filename, true);
+ }
+
+ /**
+ * sets the format of the element - this will be passed on to any elements you create. If you
+ * have already added some elements when you call this method, it will be passed on to them too.
+ *
+ * @access public
+ * @param string $format The name of the format you have implemented in your element(s).
+ * @return bool $result True on success
+ * @see setMode()
+ * @see format
+ * @see serialize()
+ */
+ function setFormat( $format )
+ {
+ if ( isset( $this ) && is_a( $this, 'patForms' ) )
+ {
+ $this->format = strtolower( $format );
+
+ if ( !empty( $this->elements ) )
+ {
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->setFormat( $format );
+ }
+ }
+ }
+ else
+ {
+ patForms::setStaticProperty( 'format', $format );
+ }
+
+ return true;
+ }
+
+ /**
+ * sets the mode of the form - If you have already added some elements when you call this
+ * method, it will be passed on to them too.
+ *
+ * @access public
+ * @param string $mode The mode to set the form to: default|readonly or any other mode you have implemented in your element class(es). Default is 'default'.
+ * @see setMode()
+ * @see mode
+ * @see serialize()
+ */
+ function setMode( $mode )
+ {
+ $this->mode = strtolower( $mode );
+
+ if ( !empty( $this->elements ) )
+ {
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->setMode( $mode );
+ }
+ }
+ }
+
+ /**
+ * sets the current submitted state of the form. Set this to true if you want the form
+ * to pick up its submitted data. It will pass on this information to all elements that
+ * have been added so far, and new ones inherit it too.
+ *
+ * @access public
+ * @param bool $state True if it has been submitted, false otherwise (default).
+ * @see isSubmitted()
+ * @see submitted
+ */
+ function setSubmitted( $state )
+ {
+ if ( $state == true )
+ {
+ $eventState = $this->triggerEvent( 'Submit' );
+ if ( $eventState === false )
+ return false;
+ }
+
+ $this->submitted = $state;
+
+ if ( !empty( $this->elements ) )
+ {
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->setSubmitted( $state );
+ }
+ }
+
+ return $state;
+ }
+
+ /**
+ * sets the method for the request
+ *
+ * @access public
+ * @param string $method GET or POST
+ * @see method
+ * @uses setAttribute()
+ */
+ function setMethod( $method )
+ {
+ $method = strtolower( $method );
+
+ if ( $method != 'get' && $method != 'post' )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_INVALID_METHOD,
+ 'Unknown method "'.$method.'". Currently only GET and POST are supported as patForms methods.'
+ );
+ }
+ $this->setAttribute( 'method', $method );
+ return true;
+ }
+
+ /**
+ * sets the action for the form
+ *
+ * This is a only a wrapper for setAttribute()
+ *
+ * @access public
+ * @param string $action
+ * @see setAttribute()
+ */
+ function setAction( $action )
+ {
+ return $this->setAttribute( 'action', $action );
+ }
+
+ /**
+ * Sets the AutoFinalize mode for the form. The AutoFinalize mode will tell patForms to
+ * finalize all elements after the form has been validated successfully.
+ *
+ * @access public
+ * @param boolean $mode Whether to activate the AutoFinalize mode (true) or not (false).
+ * @return boolean $success True if okay, a patError object otherwise.
+ * @see finalizeForm()
+ */
+ function setAutoFinalize( $mode )
+ {
+ if ( !is_bool( $mode ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_PARAMETER_NO_BOOL,
+ 'The setAutoFinalize() method requires a boolean value ( true or false ) as parameter.'
+ );
+ }
+
+ if ( isset( $this ) && is_a( $this, 'patForms' ) )
+ {
+ $this->autoFinalize = $mode;
+ }
+ else
+ {
+ patForms::setStaticProperty( 'autoFinalize', $mode );
+ }
+
+ return true;
+ }
+
+ /**
+ * Wrapper method that adds a filter to all elements
+ * of the form at once instead of having to do it for
+ * each element.
+ *
+ * @access public
+ * @param object &$filter The filter object to apply
+ * @see patForms_Element::applyFilter()
+ * @todo add error management and docs once the element's applyFilter method has too
+ */
+ function applyFilter( &$filter )
+ {
+ if ( empty( $this->elements ) )
+ return true;
+
+ $cnt = count( $this->elements );
+
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->applyFilter( $filter );
+ }
+ }
+
+ /**
+ * creates a new patForms object and returns it; this method is made to be called statically
+ * to be able to create a new patForms object from anywhere.
+ *
+ * @access public
+ * @param array $formDefinition Optional form definition for elements that will be added to the form
+ * @param array $attributes The attributes to set for the form itself
+ * @return object patForms $form The new patForms object.
+ * @todo it should be possible to pass Rule definitions, so they can be loaded and added automatically.
+ */
+ function &createForm( $formDefinition = null, $attributes = null )
+ {
+ $form = &new patForms();
+
+ if ( $attributes != null )
+ {
+ $form->setAttributes( $attributes );
+ }
+
+ if ( $formDefinition === null )
+ return $form;
+
+ foreach ( $formDefinition as $name => $element )
+ {
+ if ( !isset( $element["filters"] ) )
+ {
+ $element["filters"] = null;
+ }
+ if ( !isset( $element["children"] ) )
+ {
+ $element["children"] = null;
+ }
+
+ $el = &$form->createElement( $name, $element["type"], $element["attributes"], $element["filters"], $element["children"] );
+
+ if ( isset( $element["renderer"] ) ) {
+ $el->setRenderer( $element["renderer"] );
+ }
+
+ $result = $form->addElement( $el );
+ if (patErrorManager::isError( $result )) {
+ return $result;
+ }
+ }
+ return $form;
+ }
+
+ /**
+ * add a custom validation rule
+ *
+ * @access public
+ * @param object patForms_Rule validation rule
+ * @param integer time to apply rule (before or after built-in validation)
+ * @param boolean apply the rule, even if the form is invalid
+ * @param boolean should form get revalidated (not implemented yet)
+ * @return boolean currently always true
+ */
+ function addRule( &$rule, $time = PATFORMS_RULE_AFTER_VALIDATION, $invalid = false, $revalidate = false )
+ {
+ $rule->prepareRule( $this );
+
+ $this->_rules[] = array(
+ 'rule' => &$rule,
+ 'time' => $time,
+ 'invalid' => $invalid,
+ 'revalidate' => $revalidate
+ );
+ }
+
+ /**
+ * patForms PHP5 constructor - processes some intitialization tasks like merging the currently
+ * set static properties with the internal properties.
+ *
+ * @access public
+ */
+ function __construct()
+ {
+ foreach ( $this->staticProperties as $staticProperty => $setMethod )
+ {
+ $propValue = patForms::getStaticProperty( $staticProperty );
+ if ( patErrorManager::isError( $propValue ) )
+ continue;
+
+ $this->$setMethod( $propValue );
+ }
+
+ // initialize patForms internal attribute collection
+ $this->loadAttributeDefaults();
+ }
+
+ /**
+ * patForms pre-PHP5 constructor - does nothing for the moment except being a wrapper
+ * for the PHP5 contructor for older PHP versions support.
+ *
+ * @access public
+ */
+ function patForms()
+ {
+ patForms::__construct();
+ }
+
+ /**
+ * sets a renderer object that will be used to render
+ * the form.
+ *
+ * @access public
+ * @param object &$renderer The renderer object
+ * @return mixed $success True on success, patError object otherwise.
+ * @see createRenderer()
+ * @see renderForm()
+ */
+ function setRenderer( &$renderer, $args = array() )
+ {
+ if ( !is_object( $renderer ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_INVALID_RENDERER,
+ 'You can only set a patForms_Renderer object with the setRenderer() method, "'.gettype( $renderer ).'" given.'
+ );
+ }
+
+ $this->renderer = &$renderer;
+
+ if ( isset( $args['includeElements'] ) && $args['includeElements'] === true )
+ {
+ // check all elements - there may be some that need
+ // renderers too, so we give them the same renderer if
+ // they don't already have one.
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( $this->elements[$i]->usesRenderer && !is_object( $this->elements[$i]->renderer ) )
+ {
+ $this->elements[$i]->setRenderer( $renderer );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * sets a storage container object that will be used to store data
+ *
+ * @access public
+ * @param object patForms_Storage
+ * @see createStorage()
+ */
+ function setStorage( &$storage )
+ {
+ if ( !is_object( $storage ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_INVALID_STORAGE,
+ 'You can only set a patForms_Storage object with the setStorage() method, "'.gettype( $storage ).'" given.'
+ );
+ }
+
+ $this->registerEventHandlerObject( $storage,
+ array(
+ 'onInit' => 'loadEntry',
+ 'onValidate' => 'validateEntry',
+ 'onSuccess' => 'storeEntry'
+ )
+ );
+ }
+
+ /**
+ * renders the form with the renderer that was set via the {@link setRenderer()}
+ * method.
+ *
+ * WARNING: This is still in alpha state!
+ *
+ * Should this method return a reference??
+ * The return value could contain large blocks of HTML or large arrays!
+ * Do we want to copy these?
+ *
+ * @access public
+ * @param mixed $args arguments that will be passed to the renderer
+ * @return mixed $form The rendered form, or false if failed.
+ */
+ function renderForm( $args = null )
+ {
+ if ( $this->renderer === null )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_NO_RENDERER_SET,
+ 'Form cannot be rendered, you have to set a renderer first via the setRenderer() method.'
+ );
+ }
+
+ // form is not submitted, or auto-validation is disabled => render it
+ if ( !$this->isSubmitted() || $this->autoValidate !== true )
+ {
+ $this->triggerEvent( 'Init' );
+ return $this->renderer->render( $this, $args );
+ }
+
+ $this->validateForm();
+
+ return $this->renderer->render( $this, $args );
+ }
+
+ /**
+ * Validates all elements of the form.
+ *
+ * @access public
+ * @param boolean Flag to indicate, whether form should be validated again, if it already has been validated.
+ * @return boolean True if all elements could be validated, false otherwise.
+ * @see finishForm()
+ */
+ function validateForm( $revalidate = false )
+ {
+ if ( $this->validated && !$revalidate )
+ return $this->valid;
+
+ $valid = true;
+
+ /**
+ * validate custom rules
+ */
+ if ( !$this->_applyRules( PATFORMS_RULE_BEFORE_VALIDATION ) )
+ {
+ $valid = false;
+ }
+
+ /**
+ * validate elements
+ */
+ if ( $valid === true )
+ {
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ if ( !$this->elements[$i]->validate() )
+ {
+ $valid = false;
+ }
+ }
+ }
+
+ if ($valid === true) {
+ $result = $this->triggerEvent('Validate');
+ if ($result === false) {
+ $valid = false;
+ }
+ }
+
+ /**
+ * validate custom rules
+ */
+ if ( !$this->_applyRules( PATFORMS_RULE_AFTER_VALIDATION, $valid ) )
+ {
+ $valid = false;
+ }
+
+ if ( $valid === true && $this->autoFinalize === true )
+ $this->finalizeForm();
+
+ $this->valid = $valid;
+
+ $this->validated = true;
+
+ if ( $valid === true )
+ {
+ $this->_announce( 'status', 'validated' );
+ $event = 'Success';
+ }
+ else
+ {
+ $this->_announce( 'status', 'error' );
+ $event = 'Error';
+ }
+
+ $this->triggerEvent( $event );
+
+ return $this->valid;
+ }
+
+ /**
+ * apply rules
+ *
+ * @access private
+ * @param integer time of validation
+ * @param boolean form is valid
+ * @return boolean rules are valid or not
+ * @todo add documentation
+ */
+ function _applyRules( $time, $isValid = true )
+ {
+ $valid = true;
+
+ $cnt = count( $this->_rules );
+ for ($i = 0; $i < $cnt; $i++) {
+
+ // wrong time
+ if (( $this->_rules[$i]['time'] & $time ) != $time) {
+ continue;
+ }
+ if (!$isValid && !$this->_rules[$i]['invalid']) {
+ continue;
+ }
+
+ $result = $this->_rules[$i]['rule']->applyRule( $this, PATFORMS_RULE_AFTER_VALIDATION );
+ if ( $result === false ) {
+ $valid = false;
+ }
+ }
+ return $valid;
+ }
+
+ /**
+ * Finalizes the form by telling each fom element to finalize - finalizing means to
+ * process any tasks that need to be done after the form has been validated, like
+ * deleting any temporary files or whatever an element needs to do at that point.
+ *
+ * @access public
+ * @return bool $success Wether all elements could be finalized
+ * @see validateForm()
+ */
+ function finalizeForm()
+ {
+ $success = true;
+
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ if ( !$this->elements[$i]->finalize() )
+ {
+ patErrorManager::raiseWarning(
+ PATFORMS_ERROR_ELEMENT_NOT_FINALIZED,
+ 'Element "'.$this->elements[$i]->elementName.'" could not be finalized. See the element error messages for more details.'
+ );
+
+ $success = false;
+ }
+ }
+
+ return $success;
+ }
+
+ /**
+ * creates a new renderer from the patForms renderer collection and returns it.
+ *
+ * @access public
+ * @param string The name of the renderer to create - have a look at the Renderer/ subfolder for a list of available renderers.
+ * @return object patForms_Renderer The renderer object, or error object
+ */
+ function &createRenderer( $name )
+ {
+ return patForms::_createModule( 'Renderer', $name );
+ }
+
+ /**
+ * creates a new storage container and returns it.
+ *
+ * @access public
+ * @param string The name of the storage to create - have a look at the Storage/ subfolder for a list of available storage containers.
+ * @return object patForms_Storage The storage container, or error object
+ */
+ function &createStorage( $name )
+ {
+ return patForms::_createModule( 'Storage', $name );
+ }
+
+ /**
+ * Creates a new filter and returns it.
+ *
+ * You may pass an array as second parameter that contains
+ * parameters for the filter. patForms will check for setter methods
+ * for all keys and set the corresponding values.
+ *
+ * This eases the creating of simple filter objects.
+ *
+ * @access public
+ * @param string The name of the filter to create - have a look at the Filter/ subfolder for a list of available filters.
+ * @param array Optional parameters for the filter, if you provide a parameter, make sure the filter implements a set[Paramname]() method.
+ * This will be automated with interceptors in the PHP5 version of patForms
+ * @return object patForms_Filter The filter, or error object
+ */
+ function &createFilter( $name, $params = null )
+ {
+ $filter = &patForms::_createModule( 'Filter', $name );
+
+ if ( !is_array( $params ) )
+ {
+ return $filter;
+ }
+
+ foreach ( $params as $param => $value )
+ {
+ $setter = 'set' . ucfirst( $param );
+ if ( method_exists( $filter, $setter ) )
+ {
+ $filter->$setter( $value );
+ }
+ }
+ return $filter;
+ }
+
+ /**
+ * creates a new rule from the patForms rule collection and returns it.
+ *
+ * If your rules are not located in patForms/Rule you have to load and
+ * instantiate them on your own.
+ *
+ * @access public
+ * @param string The name of the rule to create - have a look at the Rule/ subfolder for a list of available rules.
+ * @param string The id of the rule, needed if the rule uses client side actions.
+ * @return object patForms_Rule The rule object, or error object
+ */
+ function &createRule( $name, $id = null )
+ {
+ $rule = &patForms::_createModule( 'Rule', $name );
+ if ( $id != null )
+ {
+ $rule->setId( $id );
+ }
+ return $rule;
+ }
+
+ /**
+ * creates a new observer from the patForms observer collection and returns it.
+ *
+ * If your observers are not located in patForms/Observer you have to load and
+ * instantiate them on your own.
+ *
+ * @access public
+ * @param string The name of the observer to create - have a look at the Observer/ subfolder for a list of available observers.
+ * @return object patForms_Observer The observer object, or error object
+ */
+ function &createObserver( $name )
+ {
+ $observer = &patForms::_createModule( 'Observer', $name );
+
+ return $observer;
+ }
+
+ /**
+ * creates a new module for patForms
+ *
+ * @access private
+ * @param string $type type of the module. Possible values are 'Renderer', 'Rule'
+ * @param string $name The name of the renderer to create - have a look at the renderers/ subfolder for a list of available renderers.
+ * @return object $module The module object, or an error object
+ */
+ function &_createModule( $type, $name )
+ {
+ $baseFile = PATFORMS_INCLUDE_PATH . '/'.$type.'.php';
+ $baseClass = 'patForms_'.$type;
+
+ // if there is an underscore in the module name, we want
+ // to load the module from a subfolder, so we transform
+ // all underscores to slashes.
+ $pathName = $name;
+ if ( strstr( $pathName, '_' ) )
+ {
+ $pathName = str_replace( '_', '/', $name );
+ }
+
+ $moduleFile = PATFORMS_INCLUDE_PATH . '/'.$type.'/'.$pathName.'.php';
+ $moduleClass = 'patForms_'.$type.'_'.$name;
+
+ if ( !class_exists( $baseClass ) )
+ {
+ if ( !file_exists( $baseFile ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_NO_MODULE_BASE_FILE,
+ $type .' base file could not be found',
+ 'Tried to load base file in path "'.$baseFile.'"'
+ );
+ }
+
+ include_once $baseFile;
+ }
+
+ if ( !class_exists( $moduleClass ) )
+ {
+ if ( !file_exists( $moduleFile ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_MODULE_NOT_FOUND,
+ $type.' "'.$name.'" file "'.$moduleFile. '" could not be found.'
+ );
+ }
+
+ include_once $moduleFile;
+ }
+
+ $module = &new $moduleClass();
+
+ return $module;
+ }
+
+ /**
+ * adds an element to the form - has to be a patForms_Element object. Use the {@link createElement()}
+ * method to create a new element object. Also takes care of passing on the form's configuration
+ * including the mode, format and submitted flags to the element.
+ *
+ * @access public
+ * @param object &$element The patForms_Element object to add to this form.
+ * @return bool $success True if everything went well, false otherwise.
+ * @see patForms_Element
+ * @see createElement()
+ */
+ function addElement( &$element )
+ {
+ if ( !is_object( $element ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
+ 'The addElement() method expects an element object, "'.gettype( $element ).'" given.'
+ );
+ }
+
+ if ( patErrorManager::isError( $element ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_UNEXPECTED_ERROR,
+ 'The element you are trying to add is a patError object, and not a patForms element object.'
+ );
+ }
+
+ if ( !$element->getId() ) {
+ $element->setId( $this->getElementId() );
+ }
+ $element->setMode( $this->getMode() );
+ $element->setFormat( $this->getFormat() );
+ $element->setSubmitted( $this->isSubmitted() );
+ $element->setLocale( $this->getLocale() );
+
+ $this->elements[] =& $element;
+
+ return true;
+ }
+
+ /**
+ * replaces an element in the form
+ *
+ * @access public
+ * @param object $element The patForms_Element object to be replaced
+ * @param object &$replace The element that will replace the old element
+ * @return bool $success True if everything went well, false otherwise.
+ * @see patForms_Element
+ * @see addElement()
+ */
+ function replaceElement( $element, &$replace )
+ {
+ if ( !is_object( $replace ) ) {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
+ 'The addElement() method expects an element object, "'.gettype( $replace ).'" given.'
+ );
+ }
+
+ if ( patErrorManager::isError( $replace ) ) {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_UNEXPECTED_ERROR,
+ 'The element you are trying to add is a patError object, and not a patForms element object.'
+ );
+ }
+
+ if (is_object($element)) {
+ $element = $element->getId();
+ }
+
+ $cnt = count($this->elements);
+ for ($i = 0; $i < $cnt; $i++) {
+ if ($this->elements[$i]->getId() === $element) {
+
+ if ( !$replace->getId() ) {
+ $replace->setId( $this->getElementId() );
+ }
+ $replace->setMode( $this->getMode() );
+ $replace->setFormat( $this->getFormat() );
+ $replace->setSubmitted( $this->isSubmitted() );
+ $replace->setLocale( $this->getLocale() );
+
+ $this->elements[$i] = &$replace;
+ return true;
+ }
+
+ // the current element is a container
+ if (method_exists($this->elements[$i], 'replaceElement')) {
+ $result = $this->elements[$i]->replaceElement($element, $replace);
+ if ($result === true) {
+ return $result;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an element by its name.
+ *
+ * @access public
+ * @param string $name name of the element
+ * @return object patForms element
+ * @deprecated please use patForms::getElementByName() instead
+ */
+ function &getElement( $name )
+ {
+ return $this->getElementByName( $name );
+ }
+
+ /**
+ * Get an element by its name.
+ *
+ * @access public
+ * @param string $name name of the element
+ * @return mixed either a patForms element or an array containing patForms elements
+ * @see getElementById()
+ */
+ function &getElementByName( $name )
+ {
+ if ( $name == '__form' ) {
+ return $this;
+ }
+
+ $elements = array();
+ $cnt = count( $this->elements );
+ for ($i = 0; $i < $cnt; $i++) {
+ if ($this->elements[$i]->getName() == $name) {
+ $elements[] = &$this->elements[$i];
+ continue;
+ }
+ if (method_exists($this->elements[$i], 'getElementById')) {
+ patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
+ $result = &$this->elements[$i]->getElementByName($name);
+ patErrorManager::popExpect();
+ if (!patErrorManager::isError($result)) {
+ if (is_array($result)) {
+ $cnt2 = count( $result );
+ for ($j = 0; $j < $cnt2; $j++) {
+ $elements[] = &$result[$j];
+ }
+ } else {
+ $elements[] = &$result;
+ }
+ }
+ }
+ }
+
+ switch( count( $elements ) )
+ {
+ case 0:
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_ELEMENT_NOT_FOUND,
+ 'Element '.$name.' could not be found.'
+ );
+ break;
+ case 1:
+ return $elements[0];
+ break;
+ default:
+ return $elements;
+ break;
+ }
+ }
+
+ /**
+ * Get an element by its id.
+ *
+ * @access public
+ * @param string $id id of the element
+ * @return object patForms element
+ */
+ function &getElementById( $id )
+ {
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( $this->elements[$i]->getId() == $id ) {
+ return $this->elements[$i];
+ }
+ if (method_exists($this->elements[$i], 'getElementById')) {
+ patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
+ $result = &$this->elements[$i]->getElementById($id);
+ patErrorManager::popExpect();
+ if (!patErrorManager::isError($result)) {
+ return $result;
+ }
+ }
+ }
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_ELEMENT_NOT_FOUND,
+ 'Element '.$name.' could not be found.'
+ );
+ }
+
+ /**
+ * Get all elements of the form
+ *
+ * @access public
+ * @return array all elements of the form
+ */
+ function &getElements()
+ {
+ return $this->elements;
+ }
+
+ /**
+ * Creates a new form element and returns a reference to it.
+ *
+ * The optional $filters array has to be in the following format:
+ *
+ *
+ *
+ * @access public
+ * @param string $name The name of the element
+ * @param string $type The type of the element; for a list of possible elements, have a look at the elements/ subfolder of the patForms package.
+ * @param array $attributes Attributes for the element
+ * @param array $filters Optional filters that will be applied
+ * @return object patForms_Element $element The element object, or patError if failed.
+ */
+ function &createElement( $name, $type, $attributes, $filters = null, $children = null )
+ {
+ $element =& patForms::_createModule( 'Element', $type );
+ if ( patErrorManager::isError( $element ) )
+ {
+ return $element;
+ }
+
+ $attributes['name'] = $name;
+ if ( !isset( $attributes['id'] ) ) {
+ $attributes['id'] = $this->getElementId();
+ }
+
+ // add default attributes - do this the 'silent' way be checking whether
+ // the element supports the given attribute, as the element throws a notice
+ // if it does not support it - this is not expected from default attributes.
+ foreach ( patForms::getStaticProperty( 'defaultAttributes' ) as $attributeName => $attributeValue )
+ {
+ if ( !$element->hasAttribute( $attributeName ) )
+ {
+ continue;
+ }
+
+ $element->setAttribute( $attributeName, $attributeValue );
+ }
+
+ // set the given attributes normally
+ $success = $element->setAttributes( $attributes );
+ if ( patErrorManager::isError( $success ) )
+ {
+ return $success;
+ }
+
+ if (is_array($children)) {
+ foreach ($children as $child) {
+ $childName = $child['attributes']['name'];
+
+ $childEl = &patForms::createElement($childName, $child['type'], $child['attributes']);
+ if ( isset( $child["renderer"] ) ) {
+ $childEl->setRenderer( $child["renderer"] );
+ }
+
+ $element->addElement($childEl);
+ }
+ }
+
+ $success = $element->_init();
+ if ( patErrorManager::isError( $success ) ) {
+ return $success;
+ }
+
+ // if we don't have any filters to add, we're done
+ if ( !is_array( $filters ) )
+ {
+ return $element;
+ }
+
+ $cnt = count( $filters );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $params = isset( $filters[$i]['params'] ) ? $filters[$i]['params'] : null;
+ $filter = &patForms::createFilter( $filters[$i]['filter'], $params );
+ if ( patErrorManager::isError( $filter ) )
+ {
+ continue;
+ }
+ $element->applyFilter( $filter );
+ }
+
+ return $element;
+ }
+
+ /**
+ * retrieves the validation errors from all elements in the form. Use this if the validateForm()
+ * method returned false.
+ *
+ * @access public
+ * q
+ * @return array $errors Array containing an array with validation errors for each element in the form.
+ * @todo replace __form with the name of the form, once attributes are implemented
+ */
+ function getValidationErrors($withElements = true)
+ {
+ $found = false;
+ $errors = array();
+
+ if ( !empty( $this->validationErrors ) )
+ {
+ $errors['__form'] = $this->validationErrors;
+ $found = true;
+ }
+
+ if ($withElements === false) {
+ return $errors;
+ }
+
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ $name = $this->elements[$i]->getAttribute( 'name' );
+ if ( $name === false )
+ {
+ continue;
+ }
+
+ $elementErrors = $this->elements[$i]->getValidationErrors();
+
+ if ( empty( $elementErrors ) )
+ continue;
+
+ $errors[$name] = $elementErrors;
+ $found = true;
+ }
+
+ if ( $found )
+ return $errors;
+
+ return false;
+ }
+
+ /**
+ * retrieves the values for all elements in the form.
+ *
+ * @access public
+ * @param array desired fields
+ * @param integer Mode that should be used to return values in groups
+ * @return array The values for all elements, as elementname => elementvalue.
+ *
+ * @todo remove the ugly Group check and replace with something better
+ * @todo implement something similar for getValidation errors
+ */
+ function getValues( $fields = null, $type = PATFORMS_VALUES_NESTED )
+ {
+ $values = array();
+
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ $name = $this->elements[$i]->getAttribute( 'name' );
+ if ( $name === false ) {
+ continue;
+ }
+
+ if ( is_array( $fields ) && !in_array( $name, $fields ) ) {
+ continue;
+ }
+
+ $tmpVal = $this->elements[$i]->getValue();
+ if (!is_array($tmpVal) || $this->elements[$i]->elementName != 'Group') {
+ $values[$name] = $tmpVal;
+ continue;
+ }
+
+ switch ($type) {
+ case PATFORMS_VALUES_FLATTENED:
+ $values = array_merge($values, $tmpVal);
+ break;
+ case PATFORMS_VALUES_PREFIXED:
+ foreach ($tmpVal as $key => $val) {
+ $values[$name.'_'.$key] = $val;
+ }
+ break;
+ case PATFORMS_VALUES_NESTED:
+ default:
+ $values[$name] = $tmpVal;
+ break;
+
+ }
+ }
+ return $values;
+ }
+
+ /**
+ * sets the values for all elements in the form. Use this to fill your form with external
+ * data, like a db query. Caution: if you do this and set the form to submitted, the values
+ * will be overwritten by any values present in the $_GET or $_POST variables.
+ *
+ * @access public
+ * @param array $values The values for all elements, as elementname => elementvalue.
+ */
+ function setValues( $values, $overrideUserInput = false )
+ {
+ patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
+ foreach ($values as $elName => $value) {
+ $el = &$this->getElementByName($elName);
+ if (patErrorManager::isError($el)) {
+ continue;
+ }
+ if ($overrideUserInput === true) {
+ $el->setValue($value);
+ } else {
+ $el->setDefaultValue($value);
+ }
+ }
+ patErrorManager::popExpect();
+ return true;
+ }
+
+ /**
+ * retrieves the current mode of the form
+ *
+ * @access public
+ * @return string $mode The current form mode
+ * @see setMode()
+ * @see $mode
+ */
+ function getMode()
+ {
+ return $this->mode;
+ }
+
+ /**
+ * returns the locale that is currently set for the form.
+ *
+ * @access public
+ * @return string $locale The locale.
+ * @see setLocale()
+ * @see $locale
+ */
+ function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * retrieves the current format of the form
+ *
+ * @access public
+ * @return string $format The current form format
+ * @see setFormat()
+ * @see format
+ */
+ function getFormat()
+ {
+ return $this->format;
+ }
+
+ /**
+ * retrieves the current method of the form
+ *
+ * @access public
+ * @return string $method The request method
+ * @see setMethod()
+ */
+ function getMethod()
+ {
+ return $this->getAttribute( 'method' );
+ }
+
+ /**
+ * retrieves the current action of the form
+ *
+ * @access public
+ * @return string $action Action of the form
+ * @see setAction()
+ */
+ function getAction()
+ {
+ $action = $this->getAttribute( 'action' );
+ if ( !empty( $action ) )
+ return $action;
+ return $_SERVER['PHP_SELF'];
+ }
+
+ /**
+ * adds an atribute to the form's attribute collection. If the attribute
+ * already exists, it is overwritten.
+ *
+ * @access public
+ * @param string $attributeName The name of the attribute to add
+ * @param string $atributeValue The value of the attribute
+ */
+ function setAttribute( $attributeName, $attributeValue )
+ {
+ if ( !isset( $this->attributeDefinition[$attributeName] ) )
+ {
+ patErrorManager::raiseNotice(
+ PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED,
+ "The attribute '".$attributeName."' is not supported by the form, skipped it. [".get_class( $this )."]"
+ );
+ return true;
+ }
+
+ $this->attributes[$attributeName] = $attributeValue;
+
+ return true;
+ }
+
+ /**
+ * adds several attributes at once to the form's attribute collection.
+ * Any existing attributes will be overwritten.
+ *
+ * @access public
+ * @param array $attributes The attributes to add
+ * @see setAttribute()
+ */
+ function setAttributes( $attributes )
+ {
+ if ( !is_array( $attributes ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_NOTICE_ARRAY_EXPECTED,
+ "setAttributes: array expected"
+ );
+ }
+
+ foreach ( $attributes as $attributeName => $attributeValue )
+ {
+ $this->setAttribute( $attributeName, $attributeValue );
+ }
+
+ return true;
+ }
+
+ /**
+ * retrieves the value of a form attribute.
+ *
+ * @access public
+ * @param string $attribute The name of the attribute to retrieve
+ * @return mixed $attributeValue The value of the attribute, or false if it does not exist in the attributes collection.
+ * @see setAttribute()
+ */
+ function getAttribute( $attribute )
+ {
+ if ( !isset( $this->attributes[$attribute] ) )
+ {
+ return false;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ /**
+ * retrieves all attributes of the form, or only the specified attributes.
+ *
+ * @access public
+ * @param array $attributes Optional: The names of the attributes to retrieve. Only the attributes that exist will be returned.
+ * @return array $result The attributes
+ * @see getAttribute()
+ */
+ function getAttributes( $attributes = array() )
+ {
+ if ( empty( $attributes ) )
+ {
+ return $this->attributes;
+ }
+
+ $result = array();
+ foreach ( $attributes as $attribute )
+ {
+ if ( $attributeValue = $this->getAttribute( $attribute ) )
+ {
+ $result[$attribute] = $attributeValue;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Loads the default attribute values into the attributes collection. Done directly
+ * on startup (in the consructor).
+ *
+ * The action defaults to the path of the current script, with session
+ * ID appended automatically, if SID has been defined.
+ *
+ * @access public
+ * @return bool $success Always returns true.
+ * @see $attributeDefaults
+ */
+ function loadAttributeDefaults()
+ {
+ foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
+ {
+ if ( isset( $attributeDef['default'] ) )
+ {
+ $this->attributes[$attributeName] = $attributeDef['default'];
+ }
+
+ if ( $attributeName == 'action' )
+ {
+ $this->attributes[$attributeName] = $_SERVER['PHP_SELF'];
+ /**
+ * session has been started, append session ID
+ */
+ if ( defined( 'SID' ) )
+ $this->attributes[$attributeName] .= '?' . SID;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * retrieves the form's current submitted state.
+ *
+ * If autoValidate is used, it will check for the submitVar and
+ * set the submitted flag accordingly
+ *
+ * @access public
+ * @return bool $state True if it has been submitted, false otherwise.
+ * @see setSubmitted(), setAutoValidate()
+ * @see submitted
+ */
+ function isSubmitted()
+ {
+ if ( $this->submitted === true )
+ {
+ return true;
+ }
+
+ if ( !isset( $this->submitVar ) )
+ {
+ return false;
+ }
+
+ if ( !$this->autoValidate )
+ {
+ return false;
+ }
+
+ if ( isset( $_GET[$this->submitVar] ) || isset( $_POST[$this->submitVar] ) )
+ {
+ $this->setSubmitted( true );
+ }
+
+ return $this->submitted;
+ }
+
+ /**
+ * Creates a new patForms_Creator object
+ *
+ * @static
+ * @access public
+ * @return object $creator The creator object, or a patError object on failure
+ */
+ function createCreator( $type )
+ {
+ return patForms::_createModule( 'Creator', $type );
+ }
+
+ /**
+ * get the element name of the form
+ *
+ * @access public
+ * @return string name of the form
+ */
+ function getElementName()
+ {
+ return $this->elementName;
+ }
+
+ /**
+ * get next error offset
+ *
+ * @access public
+ * @return integer
+ */
+ function getErrorOffset( $requiredCodes = 100 )
+ {
+ $offset = $this->nextErrorOffset;
+ $this->nextErrorOffset = $this->nextErrorOffset + $requiredCodes;
+ return $offset;
+ }
+
+ /**
+ * add error codes and messages for validator method
+ *
+ * @access public
+ * @param array defintions
+ * @param integer offset for the error codes
+ */
+ function addValidatorErrorCodes( $defs, $offset = 1000 )
+ {
+ foreach ( $defs as $lang => $codes )
+ {
+ if ( !isset( $this->validatorErrorCodes[$lang] ) )
+ {
+ $this->validatorErrorCodes[$lang] = array();
+ }
+
+ foreach ( $codes as $code => $message )
+ {
+ $this->validatorErrorCodes[$lang][($offset+$code)] = $message;
+ }
+ }
+ }
+
+ /**
+ * add a validation error to the whole form
+ *
+ * This can be achieved by adding a validation rule to the form.
+ *
+ * @access public
+ * @param integer $code
+ * @param array $vars fill named placeholder with values
+ * @return boolean $result true on success
+ * @see addRule()
+ */
+ function addValidationError( $code, $vars = array() )
+ {
+ $error = false;
+ $lang = $this->locale;
+ $element = $this->getElementName();
+
+ // find error message for selected language
+ while ( true )
+ {
+ // error message matches language code
+ if ( isset( $this->validatorErrorCodes[$lang][$code] ) )
+ {
+ $error = array( "element" => $element, "code" => $code, "message" => $this->validatorErrorCodes[$lang][$code] );
+ break;
+ }
+ // no message found and no fallback-langauage available
+ else if ( $lang == "C" )
+ {
+ break;
+ }
+
+ $lang_old = $lang;
+
+ // look for other languages
+ if ( strlen( $lang ) > 5 )
+ {
+ list( $lang, $trash ) = explode( ".", $lang );
+ }
+ else if ( strlen( $lang ) > 2 )
+ {
+ list( $lang, $trash ) = explode( "_", $lang );
+ }
+ else
+ {
+ $lang = "C";
+ }
+
+ // inform developer about missing language
+ patErrorManager::raiseNotice(
+ PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED,
+ "Required Validation Error-Code for language: $lang_old not available. Now trying language: $lang",
+ "Add language definition in used element or choose other language"
+ );
+
+ }
+
+ // get default Error!
+ if ( !$error )
+ {
+ patErrorManager::raiseWarning(
+ PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED,
+ "No Error Message for this validation Error was defined",
+ "Review the error-definition for validation-errors in your element '$element'."
+ );
+ $error = array( "element" => $element, "code" => 0, "message" => "Unknown validation Error" );
+ }
+
+ // insert values to placeholders
+ if ( !empty( $vars ) )
+ {
+ foreach ( $vars as $key => $value )
+ {
+ $error["message"] = str_replace( "[". strtoupper( $key ) ."]", $value, $error["message"] );
+ }
+ }
+
+ array_push( $this->validationErrors, $error );
+ $this->valid = false;
+ return true;
+ }
+
+ /**
+ * retreives a new element id, used to give each added element a unique id for this
+ * form (id can be overwritten by setting the id attribute specifically).
+ *
+ * @access private
+ * @return int $elementId The new element id.
+ */
+ function getElementId()
+ {
+ $this->elementCounter++;
+ return 'pfo'.$this->elementCounter;
+ }
+
+ /**
+ * attach an observer
+ *
+ * @access public
+ * @param object patForms_Observer
+ * @see createObserver()
+ * @uses patForms_Element::createObserver()
+ */
+ function attachObserver( &$observer, $where = PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
+ {
+ /**
+ * attach the observer to all elements
+ */
+ if ( ( $where & PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS ) == PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
+ {
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ $this->elements[$i]->attachObserver( $observer );
+ }
+ }
+
+ /**
+ * attach the observer to the form
+ */
+ if ( ( $where & PATFORMS_OBSERVER_ATTACH_TO_FORM ) == PATFORMS_OBSERVER_ATTACH_TO_FORM )
+ {
+ $this->observers[] = &$observer;
+ }
+ return true;
+ }
+
+ /**
+ * Retrieve the content for the start of the form, including any
+ * additional content, e.g. global scripts if the scripts option
+ * is enabled.
+ *
+ * @access public
+ * @return string $formStart The form start content
+ * @todo use format to build a dynamic method
+ */
+ function serializeStart()
+ {
+ $methodName = "serializeStart".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
+
+ if ( !method_exists( $this, $methodName ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
+ "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
+ );
+ }
+
+ return $this->$methodName();
+ }
+
+ /**
+ * Serializes the form's start element for html format, in default mode.
+ *
+ * @access private
+ * @return mixed $formStart The serialized start content, or a patError object.
+ */
+ function serializeStartHtmlDefault()
+ {
+ $attributes = $this->getAttributesFor( $this->format );
+ if ( patErrorManager::isError( $attributes ) )
+ {
+ return $attributes;
+ }
+
+ $content = patForms_Element::createTag( 'form', 'opening', $attributes );
+
+ if ( $this->optionEnabled( 'scripts' ) )
+ {
+ $content .= $this->getScripts();
+ }
+
+ return $content;
+ }
+
+ /**
+ * Serializes the form's start element for html format, in readonly mode.
+ *
+ * @access private
+ * @return mixed $formStart The serialized start content, or a patError object.
+ */
+ function serializeStartHtmlReadonly()
+ {
+ $attributes = $this->getAttributesFor( $this->format );
+ if ( patErrorManager::isError( $attributes ) )
+ {
+ return $attributes;
+ }
+
+ return patForms_Element::createTag( 'form', 'opening', $attributes );
+ }
+
+ /**
+ * Retrieve the content for the end of the form.
+ *
+ * @access public
+ * @return string $formEnd The form end content
+ */
+ function serializeEnd()
+ {
+ $methodName = "serializeEnd".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
+
+ if ( !method_exists( $this, $methodName ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
+ "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
+ );
+ }
+
+ return $this->$methodName();
+ }
+
+ /**
+ * Retrieves the content for the end of the form for html format,
+ * in default mode.
+ *
+ * @access private
+ * @return string $formEnd The form end content
+ */
+ function serializeEndHtmlDefault()
+ {
+ return patForms_Element::createTag( 'form', 'closing' );
+ }
+
+ /**
+ * Retrieves the content for the end of the form for html format,
+ * in readonly mode.
+ *
+ * @access private
+ * @return string $formEnd The form end content
+ */
+ function serializeEndHtmlReadonly()
+ {
+ return $this->serializeEndHtmlDefault();
+ }
+
+ /**
+ * validates the current attribute collection according to the attributes definition
+ * and the given output format, and returns the list of valid attributes.
+ *
+ * @access private
+ * @param string $format The output format to retrieve the attributes for.
+ * @return mixed $attributes The list of attributes, or false if failed.
+ */
+ function getAttributesFor( $format )
+ {
+ $attributes = array();
+
+ foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
+ {
+ if ( !isset( $this->attributes[$attributeName] ) )
+ {
+ if ( $attributeDef["required"] )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_ATTRIBUTE_REQUIRED,
+ 'patForms needs the attribute "'.$attributeName.'" to be set.',
+ 'See the patForms attribute definition of patForms for a complete attribute reference.'
+ );
+ }
+
+ continue;
+ }
+
+ $attributeValue = $this->attributes[$attributeName];
+
+ if ( !in_array( $format, $attributeDef["outputFormats"] ) )
+ {
+ continue;
+ }
+
+ if ( isset( $attributeDef["format"] ) )
+ {
+ if ( !$this->_checkAttributeFormat( $attributeValue, $attributeDef["format"] ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT,
+ "Format '".$attributeDef["format"]."' could not be verified for patForms attribute '".$attributeName."' => '".$attributeValue."'"
+ );
+ }
+ }
+
+ $attributes[$attributeName] = $attributeValue;
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * checks the format of an attribute value according to the given format.
+ *
+ * @access private
+ * @param mixed $attributeValue The attribute value to check
+ * @param string $format The format to check the attribute value against
+ * @return bool $result True if format check succeeded, false otherwise.
+ * @see createAttributes()
+ * @todo Implement this method sometime
+ */
+ function _checkAttributeFormat( $attributeValue, $format )
+ {
+ return true;
+ }
+
+ /**
+ * Enables a patForms option.
+ *
+ * See the {@link $options} property for an exhaustive list of available options.
+ *
+ * @access public
+ * @param string $option The option to enable
+ * @param array $params Optional parameters for the option
+ * @return mixed $result True on success, patError object otherwise.
+ * @see disableOption()
+ * @see optionEnabled()
+ * @see $options
+ */
+ function enableOption( $option, $params = array() )
+ {
+ if ( !in_array( $option, array_keys( $this->options ) ) )
+ {
+ return patErrorManager::raiseNotice(
+ PATFORMS_NOTICE_INVALID_OPTION,
+ 'The option "'.$option.'" is not a valid patForms option.'
+ );
+ }
+
+ $this->options[$option]['enabled'] = true;
+ $this->options[$option]['params'] = $params;
+
+ // now update all available elements too
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->enableOption( $option, $params );
+ }
+
+ return true;
+ }
+
+ /**
+ * Disables a patForms option
+ *
+ * See the {@link $options} property for an exhaustive list of available options.
+ *
+ * @access public
+ * @param string $option The option to disable
+ * @return mixed $result True on success, patError object otherwise.
+ * @see enableOption()
+ * @see optionEnabled()
+ * @see $options
+ */
+ function disableOption( $option )
+ {
+ if ( !in_array( $option, array_keys( $this->options ) ) )
+ {
+ return patErrorManager::raiseNotice(
+ PATFORMS_NOTICE_INVALID_OPTION,
+ 'The option "'.$option.'" is not a valid patForms option.'
+ );
+ }
+
+ $this->options[$option]['enabled'] = false;
+
+ // now update all available elements too
+ $cnt = count( $this->elements );
+ for ( $i=0; $i < $cnt; $i++ )
+ {
+ $this->elements[$i]->disableOption( $option );
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether the given option is enabled.
+ *
+ * @access public
+ * @param string $option The option to check
+ * @return bool $enabled True if enabled, false otherwise.
+ * @see enableOption()
+ * @see disableOption()
+ * @see $options
+ */
+ function optionEnabled( $option )
+ {
+ if ( !isset( $this->options[$option] ) )
+ return false;
+
+ return $this->options[$option]['enabled'];
+ }
+
+ /**
+ * Set the form to auto validate
+ *
+ * If you use this method, patForms will check the _GET and _POST variables
+ * for the variable you specified. If it is set, patForms assumes, that
+ * the form has been submitted.
+ *
+ * When creating a start tag for the form, the value will be inserted automatically.
+ *
+ * @access public
+ * @param string $submitVar
+ */
+ function setAutoValidate( $submitVar )
+ {
+ $this->autoValidate = true;
+ $this->submitVar = $submitVar;
+ }
+
+ /**
+ * register a new event
+ *
+ * After registering an event, you may register one or more
+ * event handlers for this event an then trigger the event.
+ *
+ * This lets you extend the functionality of patForms.
+ *
+ * @access public
+ * @param string event name
+ * @return boolean true, if event could be registered
+ * @see registerEventHandler()
+ * @see triggerEvent()
+ */
+ function registerEvent( $name )
+ {
+ $event = 'on' . $name;
+ if ( in_array( $event, $this->_validEvents ) )
+ {
+ return patErrorManager::raiseNotice(
+ PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED,
+ 'Event "'.$event.'" already has been registered or is built-in event'
+ );
+ }
+ array_push( $this->_validEvents, $event );
+ return true;
+ }
+
+ /**
+ * Register an event handler
+ *
+ * An event handler can be any valid PHP callback. You may pass
+ * one of the following values:
+ * - string functionname to call a globally declared function
+ * - array( string classname, string methodname) to call a static method
+ * - array( object obj, string methodname) to call a method of an object
+ *
+ * When the handler is called, two parameters will be passed:
+ * - object form : a patForms object
+ * - string event : the name of the event has should be handled.
+ *
+ * An event handler should always return true. If false is returned,
+ * the event will be cancelled.
+ *
+ * Currently handlers for the following events can be registered:
+ * - onSubmit
+ * - onSuccess
+ * - onError
+ *
+ * @access public
+ * @param string event name
+ * @param mixed event handler
+ * @return boolean true, if the handler could be registered
+ * @see triggerEvent()
+ * @see $_validEvents
+ */
+ function registerEventHandler( $event, $handler )
+ {
+ if ( !in_array( $event, $this->_validEvents ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_UNKNOWN_EVENT,
+ 'Cannot register event handler for unknown event "' . $event .'".'
+ );
+ }
+
+ if ( !is_callable( $handler ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ERROR_INVALID_HANDLER,
+ 'Event handler is not callable.'
+ );
+ }
+
+ if ( !isset( $this->_eventHandler[$event] ) )
+ {
+ $this->_eventHandler[$event] = array();
+ }
+
+ $this->_eventHandler[$event][] = &$handler;
+ return true;
+ }
+
+ /**
+ * set event handler object.
+ *
+ * An event handler object is used to handle all
+ * registered events. The object has to provide methods
+ * for all events it should handle, the names of the methods
+ * have to be the same as the names of the events.
+ *
+ * @access public
+ * @param object event handler object
+ * @param array method names, used to change the names of the methods
+ * @return boolean
+ */
+ function registerEventHandlerObject( &$obj, $methods = array() )
+ {
+ if ( empty( $methods ) )
+ {
+ foreach ( $this->_validEvents as $event )
+ {
+ if ( !method_exists( $obj, $event ) )
+ continue;
+
+ $methods[$event] = $event;
+ }
+ }
+
+ foreach ( $methods as $event => $method )
+ {
+ if ( !isset( $this->_eventHandler[$event] ) )
+ {
+ $this->_eventHandler[$event] = array();
+ }
+
+ $this->_eventHandler[$event][] = array( &$obj, $method );
+ }
+
+ return true;
+ }
+
+ /**
+ * Trigger an event
+ *
+ * In most cases there's no need to call this event
+ * from outside the class. The method is declared public
+ * to allow you to trigger custom events.
+ *
+ * @access public
+ * @param string Event name. The event name must not contain 'on', as this will be
+ * prefixed automatically.
+ */
+ function triggerEvent( $event )
+ {
+ $handlerName = 'on' . $event;
+
+ if ( !isset( $this->_eventHandler[$handlerName] ) || empty( $this->_eventHandler[$handlerName] ) )
+ {
+ return true;
+ }
+
+ $cnt = count( $this->_eventHandler[$handlerName] );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $result = call_user_func( $this->_eventHandler[$handlerName][$i], $this, $event );
+ if ( $result == false )
+ {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Serializes the entire form to XML, all elements included
+ *
+ * @access public
+ * @param string $namespace Optional namespace to use for the tags
+ * @return string $xml The XML representation of the form
+ * @see patForms_Element::toXML()
+ * @todo needs patForms_Element, maybe switch to PEAR::XML_Util
+ */
+ function toXML( $namespace = null )
+ {
+ $tagName = 'Form';
+
+ // prepend Namespace
+ if ( $namespace != null )
+ {
+ $tagName = $namespace.':'.$tagName;
+ }
+
+ // get all attributes
+ $attributes = $this->getAttributes();
+
+ // create valid XML attributes
+ foreach ( $attributes as $key => $value )
+ {
+ $attributes[$key] = strtr( $value, $this->xmlEntities );
+ }
+
+ $elements = '';
+ for ( $i = 0; $i < $this->elementCounter; $i++ )
+ {
+ $elements .= $this->elements[$i]->toXML( $namespace );
+ }
+
+ return patForms_Element::createTag( $tagName, "full", $attributes, $elements );
+ }
+
+ /**
+ * Set a static property.
+ *
+ * Static properties are stored in an array in a global variable,
+ * until PHP5 is ready to use.
+ *
+ * @static
+ * @param string property name
+ * @param mixed property value
+ * @see getStaticProperty()
+ */
+ function setStaticProperty( $property, &$value )
+ {
+ $GLOBALS["_patForms"][$property] = &$value;
+ }
+
+ /**
+ * Get a static property.
+ *
+ * Static properties are stored in an array in a global variable,
+ * until PHP5 is ready to use.
+ *
+ * @static
+ * @param string property name
+ * @return mixed property value
+ * @see setStaticProperty()
+ */
+ function &getStaticProperty( $property )
+ {
+ if ( isset( $GLOBALS["_patForms"][$property] ) )
+ {
+ return $GLOBALS["_patForms"][$property];
+ }
+ return patErrorManager::raiseWarning(
+ PATFORMS_ERROR_NO_STATIC_PROPERTY,
+ 'Static property "'.$property.'" could not be retreived, it does not exist.'
+ );
+ }
+
+ /**
+ * Retrieves the form's name
+ *
+ * If no name is set, it will use 'patForms' as name.
+ *
+ * @access public
+ * @return string $name The name of the form.
+ */
+ function getName()
+ {
+ if ( isset( $this->attributes['name'] ) )
+ return $this->attributes['name'];
+ return 'patForms';
+ }
+
+ /**
+ * get the javascript for the form
+ *
+ * This is still in alpha state. It will later
+ * allow client side validation if the element
+ * provides this feature.
+ *
+ * @access public
+ * @return string javascript needed by the form
+ * @todo make this dependent on the format
+ * @todo add changeable linebreaks
+ */
+ function getScripts()
+ {
+ foreach ($this->elements as $element) {
+ $element->registerJavascripts($this);
+ }
+
+ $globalJavascript = implode ("", $this->javascripts['global']);
+ $instances = implode ("", $this->javascripts['instance']);
+
+ $script = '';
+
+ return $script;
+
+ /*
+ $globalJavascript = '';
+ $instances = '';
+
+ $displayedTypes = array();
+
+ $cnt = count( $this->elements );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ $instances .= $this->elements[$i]->getInstanceJavascript();
+
+ $type = $this->elements[$i]->getElementName();
+ if ( in_array( $type, $displayedTypes ) )
+ continue;
+
+ array_push( $displayedTypes, $type );
+ $globalJavascript .= $this->elements[$i]->getGlobalJavascript();
+ }
+
+ $cnt = count( $this->_rules );
+ for ( $i = 0; $i < $cnt; ++$i )
+ {
+ $instances .= $this->_rules[$i]['rule']->getInstanceJavascript();
+
+
+ $type = $this->_rules[$i]['rule']->getRuleName();
+ if ( in_array( $type, $displayedTypes ) )
+ continue;
+
+ array_push( $displayedTypes, $type );
+
+ $globalJavascript .= $this->_rules[$i]['rule']->getGlobalJavascript();
+ }
+
+ $script = '';
+
+ return $script;
+ */
+ }
+
+ private $javascripts = array(
+ 'global' => array(),
+ 'instance' => array()
+ );
+
+ function registerGlobalJavascript($type, $script) {
+
+ $this->javascripts['global'][$type] = $script;
+ }
+
+ function registerInstanceJavascript($script) {
+
+ $this->javascripts['instance'][] = $script;
+ }
+
+ /**
+ * anounce a change in the element to all observers
+ *
+ * @access private
+ * @param string property that changed
+ * @param mixed new value of the property
+ */
+ function _announce( $property, $value )
+ {
+ $cnt = count( $this->observers );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $this->observers[$i]->notify( $this, $property, $value );
+ }
+ return true;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Creator/Definition.php b/3rd_party/php/propel/contrib/pat/patForms/Creator/Definition.php
new file mode 100644
index 000000000..c2ecb4a96
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Creator/Definition.php
@@ -0,0 +1,42 @@
+ $definition->name
+ ));
+
+ foreach ($definition->elements as $el) {
+ $element = &$form->createElement($el['name'], $el['type'], null);
+ if (!empty($el['attributes']['datasource'])) {
+ $ds = $el['attributes']['datasource'];
+ unset($el['attributes']['datasource']);
+ $element->setDatasource(new $ds['name']($ds));
+ }
+ // patForms will choke when we try to set attributes that
+ // don't exist for an element type. So we'll have to ask.
+ foreach ($el['attributes'] as $name => $value) {
+ if ($element->hasAttribute($name)) {
+ $element->setAttribute($name, $value);
+ }
+ }
+ if (isset($el['rules'])) {
+ foreach ($el['rules'] as $rule) {
+ $element->addRule(new $rule['type']($rule));
+ }
+ }
+ $form->addElement($element);
+ }
+ if (!is_null($object)) {
+ $form->setValues($object->toArray());
+ }
+ if ($definition->autoValidate) {
+ $form->setAutoValidate($definition->autoValidate);
+ }
+
+ return $form;
+ }
+
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Creator/Propel.php b/3rd_party/php/propel/contrib/pat/patForms/Creator/Propel.php
new file mode 100644
index 000000000..61876da98
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Creator/Propel.php
@@ -0,0 +1,132 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Creator_Propel extends patForms_Creator
+{
+ private static $creoleTypeMapping = array(
+ CreoleTypes::BOOLEAN =>'Radio', // BOOLEAN = 1;
+ CreoleTypes::BIGINT =>'String', // BIGINT = 2;
+ CreoleTypes::SMALLINT =>'String', // SMALLINT = 3;
+ CreoleTypes::TINYINT =>'String', // TINYINT = 4;
+ CreoleTypes::INTEGER =>'String', // INTEGER = 5;
+ CreoleTypes::CHAR =>'String', // CHAR = 6;
+ CreoleTypes::VARCHAR =>'String', // VARCHAR = 7;
+ CreoleTypes::FLOAT =>'String', // FLOAT = 8;
+ CreoleTypes::DOUBLE =>'String', // DOUBLE = 9;
+ CreoleTypes::DATE =>'Date', // DATE = 10;
+ CreoleTypes::TIME =>'String', // TIME = 11;
+ CreoleTypes::TIMESTAMP =>'Date', // TIMESTAMP = 12;
+ CreoleTypes::VARBINARY =>'String', // VARBINARY = 13;
+ CreoleTypes::NUMERIC =>'String', // NUMERIC = 14;
+ CreoleTypes::BLOB =>'String', // BLOB = 15;
+ CreoleTypes::CLOB =>'String', // CLOB = 16;
+ CreoleTypes::TEXT =>'Text', // TEXT = 17;
+ CreoleTypes::LONGVARCHAR =>'Text', // LONGVARCHAR = 17;
+ CreoleTypes::DECIMAL =>'String', // DECIMAL = 18;
+ CreoleTypes::REAL =>'String', // REAL = 19;
+ CreoleTypes::BINARY =>'String', // BINARY = 20;
+ CreoleTypes::LONGVARBINARY =>'String', // LONGVARBINARY = 21;
+ CreoleTypes::YEAR =>'String', // YEAR = 22;
+ CreoleTypes::ARR =>'String',
+ CreoleTypes::OTHER =>'String'
+ );
+
+ /**
+ * Create a form from a propel instance
+ *
+ * @access public
+ * @param mixed $object An instance of a Propel object
+ * @param array $options Any options the creator may need
+ * @return object $form The patForms object, or a patError object on failure.
+ */
+ function &create( $object, $options = array() )
+ {
+ // Propel stuff
+ $propel_peer = $object->getPeer();
+ $propel_mapBuilder = $propel_peer->getMapBuilder(); // Not sure if we're gonna need this one
+ $propel_tablename = constant(get_class($propel_peer) . '::TABLE_NAME');
+ $propel_tableMap = $propel_mapBuilder->getDatabaseMap()->getTable($propel_tablename);
+
+ // The form
+ $form =& patForms::createForm( null, array( 'name' => 'patForms_Creator_Form' ) );
+
+ $propel_cols = $propel_tableMap->getColumns();
+ foreach ($propel_cols as $propel_colname => $propel_col) {
+
+ // phpName can be altered by editing the schema.xml,
+ // thus I think, we should lowercase()/ucfirst() this
+ $propel_colname = strtolower($propel_colname);
+ $el_displayname = ucFirst($propel_colname);
+ // this could be omitted of course, but I think, it's a
+ // convenient way to get more safe request properties
+ $el_name = $propel_tablename . '[' . $propel_colname . ']';
+
+ $el_attr = array(
+ 'edit' => 'yes',
+ 'title' => $el_displayname,
+ 'label' => $el_displayname,
+ 'name' => $el_name,
+ 'description' => $el_displayname
+ );
+
+ //// Obsolete ?
+ // Parse column info to element type info
+ //$type_info = $this->parseTypeInfoFromColumn($propel_col);
+ // Merge extra element attributes
+ //$el_attr = array_merge( $el_attr, $type_info['attributes'] );
+
+ // Is the element required ? Can we retrieve this info from the Column object ?
+ $el_attr['required'] = 'yes';
+ // Value: for now we use default to set the value. Is there a better (more correct) way to do this ?
+ $el_attr['default'] = $object->{'get'.$propel_col->getPhpName()}();
+
+ if ($propel_col->isPrimaryKey()) {
+ $el_type = 'hidden';
+ } else {
+ $el_type = self::$creoleTypeMapping[$propel_col->getCreoleType()];
+ }
+
+ $el = &$form->createElement($el_name, $el_type, null);
+ // patForms will choke when we try to set attributes
+ // that don't match the element type. So we'll ask.
+ foreach ($el_attr as $name => $value) {
+ if ($el->hasAttribute($name)) {
+ $el->setAttribute($name, $value);
+ }
+ }
+ $form->addElement($el);
+ }
+
+ return $form;
+ }
+
+ // Seems this function will become obsolete if we use the static $creoleTypeMapping
+ function parseTypeInfoFromColumn ( $column ) {
+
+ return array(
+ 'type' => 'String',
+ 'attributes' => array()
+ );
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Creator/_propel_creator_test.php b/3rd_party/php/propel/contrib/pat/patForms/Creator/_propel_creator_test.php
new file mode 100644
index 000000000..33f108ed7
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Creator/_propel_creator_test.php
@@ -0,0 +1,201 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+
+ /**
+ * Main examples prepend file, needed *only* for the examples framework!
+ */
+ //include_once 'patExampleGen/prepend.php';
+ //$exampleGen->displayHead( 'Example' );
+
+ include('include/common.php');
+
+ // EXAMPLE START ------------------------------------------------------
+
+ /**
+ * main patForms class
+ */
+ require_once ('patForms.php');
+
+ /**
+ * patErrorManager class
+ */
+ require_once ('patErrorManager.php');
+
+
+ // create the creator :)
+ $creator = &patForms::createCreator( 'Propel' );
+
+ // create the form object from the given propel Object class instance
+
+ require_once('model/general/UserProfile.php');
+ $userProfile = UserProfilePeer::retrieveByPK(1);
+ $form =& $creator->create( $userProfile );
+
+ //$wikipage = WikipagePeer::retrieveByPK('wiki');
+ //$form =& $creator->create($wikipage);
+
+ // create the needed renderer
+ $renderer =& patForms::createRenderer( "Array" );
+
+ // set the renderer
+ $form->setRenderer( $renderer );
+
+ // use auto-validation
+ $form->setAutoValidate( 'save' );
+
+ // serialize the elements
+ $elements = $form->renderForm();
+
+
+ // ERROR DISPLAY ------------------------------------------------------
+ if ( $form->isSubmitted() )
+ {
+ displayErrors( $form ); // see patExampleGen/customFunctions.php
+ }
+
+ // DISPLAY FORM ------------------------------------------------------
+ displayForm( $form, $elements ); // see patExampleGen/customFunctions.php
+
+ /**
+ * Takes a patForms object, asks it if there are any validation
+ * errors and displays them if need be.
+ *
+ * NOTE: this is just a helper method for our examples collection,
+ * so that you may concentrate on the relevant parts of the examples.
+ * It does in no way represent the way it should be done :)
+ *
+ * @access public
+ * @param object &$form The patForms object to use
+ */
+ function displayErrors( &$form )
+ {
+ // get the errors from the form object - if there are none,
+ // this returns false so it is easy to check if there are any.
+ $errors = $form->getValidationErrors();
+
+ // if there are any errors, display them.
+ if ( $errors )
+ {
+ echo '
';
+ echo '
Validation failed
';
+ echo '
';
+
+ // the errors collection is an associative array with the
+ // field names as keys, so we go through that.
+ foreach ( $errors as $elementName => $elementErrors )
+ {
+ $element =& $form->getElementByName( $elementName );
+
+ // each element can have more than one error - this
+ // is rare, but can happen so this is an indexed array
+ // with one error in each row.
+ foreach ( $elementErrors as $row => $error )
+ {
+ echo '
';
+ }
+ // no errors, tell the world everything is fine
+ else
+ {
+
+ echo '
Validation successful.
';
+ }
+ }
+
+
+ /**
+ * Displays a standard form from the examples collection when the
+ * form is rendered via the array renderer. Does not work for any
+ * other examples.
+ *
+ * NOTE: this is just a helper method for our examples collection,
+ * so that you may concentrate on the relevant parts of the examples.
+ * It does in no way represent the way it should be done :)
+ *
+ * @access public
+ * @param object &$form The current form object
+ * @param array $elements The rendered elements from the
+ * @return
+ * @see
+ */
+ function displayForm( &$form, $elements )
+ {
+ // output the opening form tag
+ echo $form->serializeStart();
+
+ echo "
\n";
+ //}
+ }
+
+ // submit button, closing form tag
+ echo '
';
+ echo $form->serializeEnd();
+
+
+ // form submitted? display all form values
+ if ( $form->isSubmitted() ) {
+ $els =& $form->getElements();
+ $cnt = count( $els );
+
+ echo '
';
+ echo '
Submitted form values
';
+ echo '
';
+ echo '
';
+
+ for ( $i = 0; $i < $cnt; $i++ ) {
+ echo '
';
+ echo '
'.$els[$i]->getAttribute('label').'
:
'.$els[$i]->getValue().'
';
+ echo '
';
+ }
+
+ echo '
';
+ echo '
';
+ echo '
';
+ }
+ }
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Datasource/Propel.php b/3rd_party/php/propel/contrib/pat/patForms/Datasource/Propel.php
new file mode 100644
index 000000000..08c059535
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Datasource/Propel.php
@@ -0,0 +1,70 @@
+peername = $conf['peername'];
+ $this->label = $conf['label'];
+ $this->value = $conf['value'];
+ }
+
+ public function getValues() {
+
+ $map = call_user_func(array($this->peername, 'getPhpNameMap'));
+
+ $c = new Criteria();
+ $c->clearSelectColumns();
+
+ foreach (array($this->label, $this->value) as $arr) {
+ foreach ($arr['members'] as $member) {
+ if (is_array($member)) {
+ foreach ($member as $member) {
+ $c->addSelectColumn(constant($this->peername . '::' . $map[$member]));
+ }
+ } else {
+ $c->addSelectColumn(constant($this->peername . '::' . $map[$member]));
+ }
+ }
+ }
+
+ if (isset($this->label['initial']) OR isset($this->value['initial'])) {
+ $label = isset($this->label['initial']) ? $this->label['initial'] : '';
+ $value = isset($this->value['initial']) ? $this->value['initial'] : '';
+ $result[] = array(
+ 'value' => $value,
+ 'label' => $label
+ );
+ }
+
+ $rs = AuthorPeer::doSelectStmt($c);
+ $rs->setFetchmode(ResultSet::FETCHMODE_ASSOC);
+ while ($rs->next()) {
+ $row = $rs->getRow();
+ foreach (array('label', 'value') as $key) {
+ $arr = $this->$key;
+ $params = array($arr['mask']);
+ foreach ($arr['members'] as $member) {
+ if (is_array($member)) {
+ foreach ($member as $member) {
+ $field_name = strtolower($map[$member]); // TODO is this always true?
+ $params[] = $row[$field_name];
+ }
+ } else {
+ $field_name = strtolower($map[$member]); // TODO is this always true?
+ $params[] = $row[$field_name];
+ }
+ }
+ $$key = call_user_func_array('sprintf', $params);
+ $tmp[$key] = $$key;
+ }
+ $result[] = $tmp;
+ }
+
+ return $result;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Definition.php b/3rd_party/php/propel/contrib/pat/patForms/Definition.php
new file mode 100644
index 000000000..d02ab5b78
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Definition.php
@@ -0,0 +1,122 @@
+
+
+class patForms_Definition {
+
+ private $data = array();
+
+ public function __construct($name, $autoValidate = '') {
+
+ $this->data['name'] = $name;
+ $this->data['mtime'] = time();
+ if ($autoValidate) {
+ $this->data['autoValidate'] = $autoValidate;
+ }
+ }
+
+ static public function create($conf) {
+ // TODO
+ }
+
+ public function __get($name) {
+
+ if (isset($this->data[$name])) {
+ return $this->data[$name];
+ }
+ }
+
+ // TODO change protocol to addElement(array $element)
+ public function addElement($name, $type, $attributes = array(), $rules = array()) {
+
+ if (is_array($type)) {
+ extract($type);
+ }
+
+ $this->data['elements'][$name]['name'] = $name;
+ $this->data['elements'][$name]['type'] = $type;
+
+ foreach ($attributes as $key => $value) {
+ $value = $this->cast($value);
+ $this->data['elements'][$name]['attributes'][$key] = $value;
+ }
+ foreach ($rules as $key => $rule) {
+ $this->data['elements'][$name]['rules'][$key] = $rule;
+ }
+ }
+
+ public function load($filename) {
+
+ $data = $this->read($filename);
+
+ foreach ($data as $key => $value) {
+ if ($key == 'elements') {
+ foreach ($value as $name => $element) {
+ $this->addElement($name, $element);
+ }
+ } else {
+ $this->data[$key] = $this->cast($value);
+ }
+ }
+ }
+
+ public function save($filename) {
+
+ $this->write($filename, $this->data);
+ }
+
+ protected function read($filename) {
+
+ $xml = file_get_contents($filename);
+ $unserializer = new XML_Unserializer();
+ $unserializer->unserialize($xml);
+ return $unserializer->getUnserializedData();
+ }
+
+ protected function write($filename, $data) {
+
+ $serializer = new XML_Serializer(array (
+ 'addDecl' => true,
+ 'encoding' => 'ISO-8859-1',
+ 'indent' => ' ',
+ 'rootName' => 'form',
+ 'defaultTagName' => 'tag'
+ ));
+ $serializer->serialize($data);
+ $xml = $serializer->getSerializedData();
+
+ $fp = fopen($filename, 'w+');
+ fputs($fp, $xml);
+ fclose($fp);
+ }
+
+ protected function cast($value) {
+
+ return $value;
+
+ // seems as if patForms_Element(s) are broken here
+ // e.g. in patForms_Element_Text::serializeHtmlDefault()
+ // at line 245 if ( $this->attributes['display'] == 'no' )
+ // will result to true if the display attribute is set
+ // to (php boolean) true
+ // so casting the 'true'/'false' and 'yes'/'no' values
+ // would break intended behaviour here
+
+ if (is_array($value) OR is_bool($value)) {
+ return $value;
+ }
+ if ($value === 'true') {
+ return true;
+ }
+ if ($value === 'false') {
+ return false;
+ }
+ if (preg_match('/^[+-]?[0-9]+$/', $value)) {
+ settype($value, 'int');
+ return $value;
+ }
+ if (preg_match('/^[+-]?[0-9]*\.[0-9]+$/', $value)) {
+ settype($value, 'double');
+ return $value;
+ }
+ return $value;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Definition/Propel.php b/3rd_party/php/propel/contrib/pat/patForms/Definition/Propel.php
new file mode 100644
index 000000000..d2ecbe0a0
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Definition/Propel.php
@@ -0,0 +1,165 @@
+
+
+class patForms_Definition_Propel extends patForms_Definition {
+
+ private static $creoleTypeMap = array(
+ CreoleTypes::BOOLEAN => 'Switch', // BOOLEAN = 1;
+ CreoleTypes::BIGINT => 'String', // BIGINT = 2;
+ CreoleTypes::SMALLINT => 'String', // SMALLINT = 3;
+ CreoleTypes::TINYINT => 'String', // TINYINT = 4;
+ CreoleTypes::INTEGER => 'String', // INTEGER = 5;
+ CreoleTypes::CHAR => 'String', // CHAR = 6;
+ CreoleTypes::VARCHAR => 'String', // VARCHAR = 7;
+ CreoleTypes::FLOAT => 'String', // FLOAT = 8;
+ CreoleTypes::DOUBLE => 'String', // DOUBLE = 9;
+ CreoleTypes::DATE => 'String', // DATE = 10;
+ CreoleTypes::TIME => 'String', // TIME = 11;
+ CreoleTypes::TIMESTAMP => 'Date', // TIMESTAMP = 12;
+ CreoleTypes::VARBINARY => 'String', // VARBINARY = 13;
+ CreoleTypes::NUMERIC => 'String', // NUMERIC = 14;
+ CreoleTypes::BLOB => 'Text', // BLOB = 15;
+ CreoleTypes::CLOB => 'Text', // CLOB = 16;
+ CreoleTypes::TEXT => 'Text', // TEXT = 17;
+ CreoleTypes::LONGVARCHAR => 'Text', // LONGVARCHAR = 17;
+ CreoleTypes::DECIMAL => 'String', // DECIMAL = 18;
+ CreoleTypes::REAL => 'String', // REAL = 19;
+ CreoleTypes::BINARY => 'String', // BINARY = 20;
+ CreoleTypes::LONGVARBINARY => 'Text', // LONGVARBINARY = 21;
+ CreoleTypes::YEAR => 'String', // YEAR = 22;
+ CreoleTypes::ARR => 'String',
+ CreoleTypes::OTHER => 'String'
+ );
+
+ private static $validatorTypeMap = array(
+ 'unique' => null,
+ 'minLength' => 'patForms_Rule_MinLength',
+ 'maxLength' => 'patForms_Rule_MaxLength',
+ 'minValue' => 'patForms_Rule_MinValue',
+ 'maxValue' => 'patForms_Rule_MaxValue',
+ 'match' => 'patForms_Rule_Match',
+ 'notMatch' => 'patForms_Rule_NotMatch',
+ 'required' => null, // will be done by the elements "required" attribute
+ 'validValues' => 'patForms_Rule_ValidValues',
+ );
+
+ /**
+ * @param array $conf an assoc array of parameters. these are:
+ * - string name => $name of the propel object class
+ * - string filename => $filename of the form definition xml file
+ */
+
+ static public function create($conf) {
+
+ extract($conf);
+
+ $autoValidate = isset($autoValidate) ? $autoValidate : 'save';
+
+ $definition = new patForms_Definition_Propel($name, $autoValidate);
+
+ if (0 AND file_exists($filename)) {
+ // load definition from xml file
+ $definition->load($filename);
+ } else {
+ // populate definition from table map and save it to xml file
+ $definition = self::populateFromTableMap($definition, $conf);
+ $definition->save($filename);
+ }
+
+ return $definition;
+ }
+
+ private function populateFromTableMap($definition, $conf) {
+
+ extract($conf);
+
+ $mapBuilder = call_user_func(array($name . 'Peer', 'getMapBuilder'));
+ $tablename = constant($name . 'Peer::TABLE_NAME');
+ $tableMap = $mapBuilder->getDatabaseMap()->getTable($tablename);
+ $cols = $tableMap->getColumns();
+
+ foreach ($cols as $col) {
+
+ $phpname = $col->getPhpName();
+ // this would need a patched version of patForms in order
+ // to retrieve request vars after having submitted the form
+ // TODO - ask patForms developers to enable this
+ // $elementName = $tablename . '[' . $phpname . ']';
+ $elementName = $phpname;
+
+ $elementType = self::$creoleTypeMap[$col->getCreoleType()];
+
+ // TODO somehow retrieve element type specific default values?
+ $elementAttributes = array(
+ 'name' => $elementName,
+ 'title' => $phpname,
+ 'label' => $phpname,
+ 'description' => $phpname,
+ 'edit' => 'yes',
+ 'display' => $col->isPrimaryKey() ? 'no' : 'yes',
+ // Is the element required?
+ // TODO Can we retrieve this info from the Column object?
+ 'required' => true,
+ );
+
+ switch ($col->getCreoleType()) {
+ case CreoleTypes::BOOLEAN: {
+ $elementAttributes['value'] = 1;
+ break;
+ }
+ case CreoleTypes::DATE: {
+ // TODO doesn't seem to work for some reason
+ // $elementAttributes['format'] = 'date';
+ // $elementAttributes['dateformat'] = 'Ymd';
+ break;
+ }
+ }
+
+ if ($col->isForeignKey()) {
+
+ $relColname = $col->getRelatedColumnName();
+ $relTablename = $col->getRelatedTableName();
+ $relColPhpname =
+ Propel::getDatabaseMap(constant($relTablename . 'Peer::DATABASE_NAME'))->
+ getTable($relTablename)->getColumn($relColname)->getPhpname();
+
+ $elementAttributes['datasource'] = array (
+ 'name' => 'patForms_Datasource_Propel',
+ 'peername' => $relTablename . 'Peer',
+ 'label' => array(
+ 'initial' => 'Please select one ...',
+ 'members' => array($relColPhpname),
+ 'mask' => '%s',
+ ),
+ 'value' => array(
+ 'members' => array($relColPhpname),
+ 'mask' => '%s',
+ ),
+ );
+ $elementType = 'Enum';
+ }
+
+ $rules = array();
+ if ($col->hasValidators()) {
+ foreach ($col->getValidators() as $validator) {
+ $name = $validator->getName();
+ $type = self::$validatorTypeMap[$name];
+ if (!is_null($type)) {
+ $rules[$name] = array (
+ 'table' => $col->getTablename(),
+ 'col' => $col->getColumnName(),
+ 'name' => $name,
+ 'type' => self::$validatorTypeMap[$name],
+ 'value' => $validator->getValue(),
+ 'class' => $validator->getClass(),
+ 'message' => $validator->getMessage(),
+ );
+ }
+ }
+ }
+
+ $definition->addElement($phpname, $elementType, $elementAttributes, $rules);
+ }
+
+ return $definition;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Element.php b/3rd_party/php/propel/contrib/pat/patForms/Element.php
new file mode 100644
index 000000000..de1a6c9ef
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Element.php
@@ -0,0 +1,1954 @@
+
+ * @author gERD Schaufelberger
+ * @author Stephan Schmidt
+ */
+
+/**
+ * error definition: the attribute that was set is not supported by this element (it is
+ * not listed in the attributeDefinition property set in the element class).
+ * @see patForms_Element::attributeDefinition
+ */
+define( "PATFORMS_ELEMENT_NOTICE_ATTRIBUTE_NOT_SUPPORTED", 1101 );
+
+/**
+ * error definition: the setAttributes() method expects an array,
+ * but given value was not.
+ * @see patForms_Element::setAttributes()
+ */
+define( "PATFORMS_ELEMENT_ERROR_ARRAY_EXPECTED", 1102 );
+
+/**
+ * error definition: the given attribute could not be set
+ */
+define( "PATFORMS_ELEMENT_ERROR_ADDING_ATTRIBUTE_FAILED", 1103 );
+
+/**
+ * error definition: the element method to serialize the element in the given mode is
+ * not implemented.
+ * @see patForms_Element::serialize()
+ */
+define( "PATFORMS_ELEMENT_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE", 1104 );
+
+/**
+ * error definition: the element returned an error
+ */
+define( "PATFORMS_ELEMENT_ERROR_ERROR_RETURNED", 1105 );
+
+/**
+ * error definition: the utility class {@link patForms_FormatChecker} could not be found, this is
+ * needed for the format validation of certain variable types.
+ * @see patForms_FormatChecker
+ * @see patForms_Element::validateFormat()
+ */
+define( "PATFORMS_ELEMENT_ERROR_FORMAT_CHECKER_NOT_FOUND", 1106 );
+
+/**
+ * error definition: the modifier that was set for the element is not an array.
+ * @see patForms_Element::_applyModifiers()
+ */
+define( "PATFORMS_ELEMENT_ERROR_MODIFIER_NOT_AN_ARRAY", 1107 );
+
+/**
+ * error definition: the method for the given modifier does not exist
+ * @see patForms_Element::_applyModifiers()
+ */
+define( "PATFORMS_ELEMENT_ERROR_METHOD_FOR_MODIFIER_NOT_FOUND", 1108 );
+
+/**
+ * error definition: the modifier returned an error, modifications could not be made.
+ * @see patForms_Element::_applyModifiers()
+ */
+define( "PATFORMS_ELEMENT_ERROR_MODIFIER_RETURNED_ERROR", 1109 );
+
+/**
+ * error definition: the given attribute is required for the specified output format.
+ * @see patForms_Element::getAttributesFor()
+ */
+define( "PATFORMS_ELEMENT_ERROR_ATTRIBUTE_REQUIRED", 1110 );
+
+/**
+ * error definition: given modifier could not be applied to specified attribute
+ * @see patForms_Element::getAttributesFor()
+ */
+define( "PATFORMS_ELEMENT_ERROR_UNABLE_TO_APPLY_MODIFIER_TO_ATTRIBUTE", 1111 );
+
+/**
+ * error definition: the given attribute is not available for output in the specified
+ * output format.
+ * @see patForms_Element::getAttributesFor()
+ */
+define( "PATFORMS_ELEMENT_ERROR_ATTRIBUTE_NOT_AVAILABLE_FOR_OUTPUT", 1112 );
+
+/**
+ * error definition: format of the attribute could not be verified
+ * @see patForms_Element::getAttributesFor()
+ */
+define( "PATFORMS_ELEMENT_ERROR_CAN_NOT_VERIFY_FORMAT", 1113 );
+
+/**
+ * error definition: the attribute collection of the element could not be validated.
+ * @see patForms_Element::toHtml()
+ */
+define( "PATFORMS_ELEMENT_ERROR_CAN_NOT_VALIDATE_ATTRIBUTE_COLLECTION", 1114 );
+
+/**
+ * error definition: validator undefined
+ */
+define( "PATFORMS_ELEMENT_ERROR_VALIDATOR_ERROR_UNDEFINED", 1115 );
+
+/**
+ * error definition: undefined locale for errors output
+ */
+define( "PATFORMS_ELEMENT_ERROR_VALIDATOR_ERROR_LOCALE_UNDEFINED", 1116 );
+
+/**
+ * error definition: the html source for the element could not be generated.
+ */
+define( "PATFORMS_ELEMENT_ERROR_NO_HTML_CONTENT", 1221 );
+
+/**
+ * error definition: not a valid renderer
+ */
+define( 'PATFORMS_ELEMENT_ERROR_INVALID_RENDERER', 1222 );
+
+/**
+ * error definition: this element does not support the use of a renderer
+ */
+define( 'PATFORMS_ELEMENT_RENDERER_NOT_SUPPORTED', 1223 );
+
+/**
+ * filter is located between patForms and browser
+ */
+define( 'PATFORMS_FILTER_TYPE_HTTP', 1 );
+
+/**
+ * filter is located between patForms and the PHP script
+ */
+define( 'PATFORMS_FILTER_TYPE_PHP', 2 );
+
+/**
+ * base patForms element class with all needed base functionality that each element
+ * should have. Extend this class to create your own elements.
+ *
+ * $Id: Element.php 1347 2009-12-03 21:06:36Z francois $
+ *
+ * @abstract
+ * @package patForms
+ * @subpackage patForms_Element
+ * @access protected
+ * @version 0.1
+ * @author Sebastian Mordziol
+ * @author gERD Schaufelberger
+ * @author Stephan Schmidt
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Element
+{
+ /**
+ * the type of the element, set this in your element class!
+ * @access protected
+ */
+ var $elementType = false;
+
+ /**
+ * javascript that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalJavascript = array();
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceJavascript = array();
+
+ /**
+ * the value of the element
+ * @access protected
+ */
+ var $value = false;
+
+ /**
+ * filters that have been applied
+ * @access private
+ */
+ var $filters = array();
+
+ /**
+ * observers that have been attached
+ *
+ * @access private
+ * @var array
+ */
+ var $observers = array();
+
+ /**
+ * The elementName for the serialized version of the element
+ *
+ * This is needed for the toXML() method and also by the patForms
+ * error management. If it is not set, the element name will be
+ * created by extracting everything after the last underscore in
+ * the classname.
+ *
+ * @access protected
+ * @see toXML()
+ */
+ var $elementName = null;
+
+ /**
+ * the attribute collection of the element
+ * @access private
+ * @see setAttribute()
+ * @see setAttributes()
+ * @see getAttribute()
+ * @see getAttributes()
+ */
+ var $attributes = array();
+
+ /**
+ * the configuration for the attributes supported by the element. Overwrite this
+ * in your element class.
+ *
+ * @abstract
+ */
+ var $attributeDefinition = array();
+
+ /**
+ * Stores the attribute defaults for the element, that will be used
+ * if the given attributes have not been set by the user.
+ *
+ * @abstract
+ * @access private
+ * @see getAttributeDefaults()
+ */
+ var $attributeDefaults = array();
+
+ /**
+ * stores the mode for the element. It defaults to 'default', and is only overwritten if
+ * set specifically.
+ *
+ * @access protected
+ * @see setMode()
+ */
+ var $mode = "default";
+
+ /**
+ * stores the format for the element. It defaults to 'html', and is only overwritten if
+ * set specifically.
+ *
+ * @access protected
+ * @see setFormat()
+ */
+ var $format = "html";
+
+ /**
+ * stores the locale to use when adding validation errors. The specified locale has
+ * to be set in the validationErrorCodes element class property, otherwise the default
+ * 'C' (as in the programming language C => english) will be used.
+ *
+ * @access private
+ * @var string $locale
+ * @see setLocale()
+ */
+ var $locale = "C";
+
+ /**
+ * stores the flag telling the element whether it has been submitted - this is used by the
+ * getValue() method to determine where to get the element's value from.
+ * @access protected
+ * @see getValue()
+ */
+ var $submitted = false;
+
+ /**
+ * stores the flag whether the element is valid
+ * @access protected
+ */
+ var $valid = true;
+
+ /**
+ * stores any validation errors that can occurr during the element's validation process.
+ *
+ * @access private
+ * @var array $validationErrors
+ */
+ var $validationErrors = array();
+
+ /**
+ * define error codes an messages for each form element
+ *
+ * @access protected
+ * @var array $validatorErrorCodes
+ */
+ var $validatorErrorCodes = array();
+
+ /**
+ * defines the starting character for the modifier placeholders that can be inserted
+ * in the attributes listed as having modifier support.
+ *
+ * @access private
+ * @var string $modifierStart
+ */
+ var $modifierStart = "[";
+
+ /**
+ * defines the starting character for the modifier placeholders that can be inserted
+ * in the attributes listed as having modifier support.
+ *
+ * @access private
+ * @var string $modifierStart
+ */
+ var $modifierEnd = "]";
+
+ /**
+ * XML entities
+ *
+ * @access protected
+ * @see toXML()
+ */
+ var $xmlEntities = array(
+ "<" => "<",
+ ">" => ">",
+ "&" => "&",
+ "'" => "'",
+ '"' => """
+ );
+ /**
+ * shortcur to the session variables
+ * If "false", no session will be used, otherwise it stores the session variables for this element
+ *
+ * @access private
+ * @var mixed $sessionVar
+ */
+ var $sessionVar = false;
+
+ /**
+ * custom validation rules
+ *
+ * @access private
+ * @var array
+ */
+ var $_rules = array();
+
+ /**
+ * next error offset for rules
+ * @access private
+ * @var integer
+ */
+ var $nextErrorOffset = 1000;
+
+ /**
+ * stores whether the element uses a renderer to serialize its content
+ * @access private
+ * @var bool
+ */
+ var $usesRenderer = false;
+
+ /**
+ * Stores the renderer object that can be set via the setRenderer method
+ * @access private
+ * @var object
+ */
+ var $renderer = false;
+
+ /**
+ * Stores all element options
+ * @access private
+ */
+ var $options = array();
+
+ /**
+ * constructor - extend this in your class if you need to do specific operations
+ * on startup. In that case, however, don't forget to call this constructor anyway
+ * so that the thing happening here don't get lost.
+ *
+ * That's easy to do... just add the following line in your constructor:
+ * parent::patForms_Element();
+ *
+ * @access public
+ * @param mixed $mode Optional: the output format, e.g. 'html'
+ */
+ function __construct( $format = false )
+ {
+ if ( $format !== false )
+ {
+ $this->format = $format;
+ }
+
+ $this->loadAttributeDefaults();
+ }
+
+ /**
+ * patForms_Element constructor for php4
+ *
+ * @access private
+ * @param integer $id
+ * @return boolean $result true on success
+ * @see __construct
+ */
+ function patForms_Element( $format = false )
+ {
+ $this->__construct( $format );
+ }
+
+ /**
+ * Add any initialization routines for your element in your element class,
+ * for everythig your element needs to do after it has been instantiated and
+ * the attribute collection has been created.
+ *
+ * @abstract
+ * @access private
+ * @return mixed $success True on success, a patError object otherwise
+ */
+ function _init()
+ {
+ // your code here
+ return true;
+ }
+
+ /**
+ * sets the format of the element - this defines which method will be called in your
+ * element class, along with the {@link mode} property.
+ *
+ * @access public
+ * @param string $format The name of the format you have implemented in your element(s). Default is 'html'
+ * @see setFormat()
+ * @see format
+ * @see serialize()
+ */
+ function setFormat( $format )
+ {
+ $this->format = strtolower( $format );
+ }
+
+ /**
+ * sets the mode of the element that defines which methods will be called in your
+ * element class, along with the {@link format} property.
+ *
+ * @access public
+ * @param string $mode The mode to set the element to: default|readonly or any other mode you have implemented in your element class(es). Default is 'default'.
+ * @see setFormat()
+ * @see mode
+ * @see serialize()
+ */
+ function setMode( $mode )
+ {
+ $this->mode = strtolower( $mode );
+ }
+
+ /**
+ * sets the locale (language) to use for the validation error messages of the form.
+ *
+ * @access public
+ * @param string $lang
+ * @return bool $result True on success
+ * @see $locale
+ */
+ function setLocale( $lang )
+ {
+ $this->locale = $lang;
+
+ // check, whether this is a custom locale
+ if (patForms::isCustomLocale($lang)) {
+ $errorMessages = patForms::getCustomLocale($lang, 'Element::' . $this->elementName);
+ if (is_array($errorMessages)) {
+ $this->validatorErrorCodes[$lang] = $errorMessages;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * sets the value of the element, which will be used to fill the element with. If none is
+ * set and the element needs a value, it will load it using the {@link resolveValue()} method.
+ *
+ * This will override user input.
+ *
+ * @access public
+ * @param mixed $value The value to set
+ * @see $value
+ * @see resolveValue()
+ * @see getValue()
+ */
+ function setValue( $value )
+ {
+ $value = $this->_applyFilters( $value, 'in', PATFORMS_FILTER_TYPE_PHP );
+ $this->value = $value;
+ }
+
+ /**
+ * sets the default value of the element, which will be used to fill the element with.
+ *
+ * @access public
+ * @param mixed $value The value to set
+ * @see $value
+ * @see resolveValue()
+ * @see setValue()
+ * @see getValue()
+ */
+ function setDefaultValue( $value )
+ {
+ $this->setAttribute('default', $value);
+ }
+
+ /**
+ * sets the current submitted state of the element. Set this to true if you want the element
+ * to pick up its submitted data.
+ *
+ * @access public
+ * @param bool $state True if it has been submitted, false otherwise (default).
+ * @see getSubmitted()
+ * @see $submitted
+ */
+ function setSubmitted( $state )
+ {
+ $this->submitted = $state;
+ }
+
+ /**
+ * sets the internal ID of the element - this is only used by the {@link patForms} class to
+ * give each element a unique ID that will be added as ID attribute to each element if the
+ * id attribute has not been defined.
+ *
+ * @access public
+ * @param string $id The id to set for the element
+ * @see getId()
+ */
+ function setId( $id )
+ {
+ $this->attributes['id'] = $id;
+ }
+
+ /**
+ * gets the internal ID of the element
+ *
+ * @access public
+ * @return string $id The id to set for the element
+ * @see setId()
+ */
+ function getId()
+ {
+ return $this->getAttribute( 'id' );
+ }
+
+ /**
+ * checks whether a given attribute is supported by this element.
+ *
+ * @access public
+ * @param string $attributeName The name of the attribute to check
+ * @return bool $hasAttribute True if it supports the attribute, false otherwise.
+ */
+ function hasAttribute( $attributeName )
+ {
+ if ( isset( $this->attributeDefinition[$attributeName] ) )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * adds an attribute to the element's attribut3 collection. If the attribute
+ * already exists, it is overwritten.
+ *
+ * @access public
+ * @param string $attributeName The name of the attribute to add
+ * @param string $attributeValue The value of the attribute
+ * @return mixed $success True on success, a patError object otherwise
+ */
+ function setAttribute( $attributeName, $attributeValue )
+ {
+ if ( !isset( $this->attributeDefinition[$attributeName] ) )
+ {
+ return patErrorManager::raiseNotice(
+ PATFORMS_ELEMENT_NOTICE_ATTRIBUTE_NOT_SUPPORTED,
+ 'Unknown attribute ['.$attributeName.']',
+ 'Ignored the attribute as the ['.$this->elementName.'] element does not support it.'
+ );
+ }
+
+ $this->attributes[$attributeName] = $attributeValue;
+
+ return true;
+ }
+
+ /**
+ * adds several attribute at once to the element's attributes collection.
+ * Any existing attributes will be overwritten.
+ *
+ * @access public
+ * @param array $attributes The attributes to add
+ * @return mixed $success True on success, false otherwise
+ */
+ function setAttributes( $attributes )
+ {
+ if ( !is_array( $attributes ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_ARRAY_EXPECTED,
+ "Not an array given (setAttributes)"
+ );
+ }
+
+ foreach ( $attributes as $attributeName => $attributeValue )
+ {
+ $this->setAttribute( $attributeName, $attributeValue );
+ }
+
+ return true;
+ }
+
+ /**
+ * sets a renderer object that will be used to render
+ * the element. Use the serialize() method to retrieve
+ * the rendered content of the element.
+ *
+ * Only enabled in elements that support renderers, like
+ * the radio element.
+ *
+ * @access public
+ * @param object &$renderer The renderer object
+ */
+ function setRenderer( &$renderer )
+ {
+ if ( !$this->usesRenderer )
+ {
+ return patErrorManager::raiseWarning(
+ PATFORMS_ELEMENT_RENDERER_NOT_SUPPORTED,
+ 'The element \''.$this->elementName.'\' does not support the use of renderers - you do not have to set a renderer for this element.'
+ );
+ }
+
+ if ( !is_object( $renderer ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_INVALID_RENDERER,
+ 'You can only set a patForms_Renderer object with the setRenderer() method, "'.gettype( $renderer ).'" given.'
+ );
+ }
+
+ $this->renderer = &$renderer;
+ }
+
+ /**
+ * retrieves the value of an attribute.
+ *
+ * @access public
+ * @param string $attribute The name of the attribute to retrieve
+ * @return mixed $attributeValue The value of the attribute, or false if it does not exist in the attributes collection.
+ * @see setAttribute()
+ */
+ function getAttribute( $attribute )
+ {
+ if ( !isset( $this->attributes[$attribute] ) )
+ {
+ return false;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ /**
+ * retrieves all attributes, or only the specified attributes.
+ *
+ * @access public
+ * @param array $attributes Optional: The names of the attributes to retrieve. Only the attributes that exist will be returned.
+ * @return array $result The attributes
+ * @see getAttribute()
+ */
+ function getAttributes( $attributes = array() )
+ {
+ if ( empty( $attributes ) )
+ {
+ return $this->attributes;
+ }
+
+ $result = array();
+ foreach ( $attributes as $attribute )
+ {
+ if ( $attributeValue = $this->getAttribute( $attribute ) )
+ {
+ $result[$attribute] = $attributeValue;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Loads the default attribute values into the attributes collection. Done directly
+ * on startup (in the consructor), so make sure you call this if your element needs
+ * this feature and you have implemented a custom constructor in your element.
+ *
+ * @access public
+ * @return bool $success Always returns true.
+ * @see $attributeDefaults
+ */
+ function loadAttributeDefaults()
+ {
+ foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
+ {
+ if ( isset( $attributeDef['default'] ) )
+ {
+ $this->attributes[$attributeName] = $attributeDef['default'];
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * retrieves the current value of the element. If none is set, will try to retrieve the
+ * value from submitted form data.
+ *
+ * @access public
+ * @param boolean Determines whether the method is used from an external script
+ * @return mixed The value, or an empty string if none found.
+ * @see setValue()
+ * @see value
+ * @see resolveValue()
+ */
+ function getValue( $external = true )
+ {
+ if ( $this->value === false )
+ {
+ $this->resolveValue();
+
+ // could not be resolved
+ if ( $this->value === false )
+ {
+ $value = '';
+ }
+ else
+ {
+ $value = $this->value;
+ }
+ }
+ else
+ {
+ $value = $this->value;
+ }
+
+ if ( $external === false )
+ {
+ return $value;
+ }
+
+ $value = $this->_applyFilters( $value, 'out', PATFORMS_FILTER_TYPE_PHP );
+
+ return $value;
+ }
+
+ /**
+ * resolves the scope the value of the element may be stored in, and returns it.
+ *
+ * @access protected
+ * @see getValue()
+ * @see value
+ * @todo parse element name, if it uses the array syntax
+ */
+ function resolveValue()
+ {
+ $varName = $this->attributes['name'];
+
+ if ( $this->submitted && isset( $_POST[$varName] ) )
+ {
+ $this->value = $_POST[$varName];
+ if ( ini_get( 'magic_quotes_gpc' ) )
+ $this->value = $this->rStripSlashes( $this->value );
+ $this->value = $this->_applyFilters( $this->value, 'in', PATFORMS_FILTER_TYPE_HTTP );
+ return true;
+ }
+
+ if ( $this->submitted && isset( $_GET[$varName] ) )
+ {
+ $this->value = $_GET[$varName];
+ if ( ini_get( 'magic_quotes_gpc' ) )
+ $this->value = $this->rStripSlashes( $this->value );
+ $this->value = $this->_applyFilters( $this->value, 'in', PATFORMS_FILTER_TYPE_HTTP );
+ return true;
+ }
+
+ if ( isset( $this->attributes['default'] ) )
+ {
+ $this->value = $this->attributes['default'];
+ $this->value = $this->_applyFilters( $this->value, 'in', PATFORMS_FILTER_TYPE_PHP );
+
+ return true;
+ }
+
+ return true;
+ }
+
+ /**
+ * recursively strip slashes
+ *
+ * This method is used to 'fix' magic_quotes_gpc.
+ *
+ * @access public
+ * @param mixed user input (get or post)
+ * @return mixed data with slashes stripped
+ */
+ function rStripSlashes( $value )
+ {
+ if ( is_scalar( $value ) )
+ return stripslashes( $value );
+ if ( is_array( $value ) )
+ {
+ foreach ( $value as $key => $val )
+ {
+ $value[$key] = $this->rStripSlashes( $val );
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * apply filters to a value
+ *
+ * @access private
+ * @param mixed value
+ * @param string direction of the filter ('in' or 'out')
+ * @param integer type of filters to apply
+ * @return mixed filtered value
+ */
+ function _applyFilters( $value, $dir = 'in', $type = PATFORMS_FILTER_TYPE_PHP )
+ {
+ if ( empty( $this->filters ) )
+ return $value;
+
+ /**
+ * apply filters!
+ */
+ $cnt = count( $this->filters );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ /**
+ * check, whether filter is located between php script and form
+ */
+ if ( $this->filters[$i]->getType() != $type )
+ {
+ continue;
+ }
+
+ $value = $this->filters[$i]->$dir( $value );
+ }
+ return $value;
+ }
+
+ /**
+ * retrieves the current mode of the element
+ *
+ * @access public
+ * @return string $mode The current element mode
+ * @see setMode()
+ * @see mode
+ */
+ function getMode()
+ {
+ return $this->mode;
+ }
+
+ /**
+ * retrieves the current format of the element
+ *
+ * @access public
+ * @return string $format The current element format
+ * @see setFormat()
+ * @see format
+ */
+ function getFormat()
+ {
+ return $this->format;
+ }
+
+ /**
+ * retrieves the element's current submitted state.
+ *
+ * @access public
+ * @return bool $state True if it has been submitted, false otherwise.
+ * @see setSubmitted()
+ * @see submitted
+ */
+ function getSubmitted()
+ {
+ return $this->submitted;
+ }
+
+ /**
+ * retrieves the name of the element
+ *
+ * @access public
+ * @return string $name name of the element
+ * @uses getAttribute()
+ */
+ function getName()
+ {
+ return $this->getAttribute( 'name' );
+ }
+
+ /**
+ * add a custom validation rule
+ *
+ * @access public
+ * @param object patForms_Rule validation rule
+ * @param integer time, when rule has to be applied, can be before or after validation.
+ * If set to null, it will use the default value as specified in the rule
+ * @return boolean currently always true
+ */
+ function addRule( &$rule, $time = null )
+ {
+ if ( is_null( $time ) )
+ {
+ $time = $rule->getTime();
+ }
+
+ $rule->prepareRule( $this );
+
+ $this->_rules[] = array(
+ 'rule' => &$rule,
+ 'time' => $time,
+ );
+ return true;
+ }
+
+ /**
+ * adds an observer to the element
+ *
+ * @access public
+ * @param object patForms_Observer observer
+ * @return boolean currently always true
+ */
+ function attachObserver( &$observer )
+ {
+ $this->observers[] = &$observer;
+ return true;
+ }
+
+ /**
+ * dispatches the serialization of the element in the format that was set to the
+ * corresponding method in the element class. These methods must be named in the
+ * folowing scheme:
+ *
+ * serialize[format][mode](), e.g. serializeHtmlDefault()
+ *
+ * @access public
+ * @return string $element The created element according to the specified mode.
+ * @see setFormat()
+ * @see setMode()
+ * @todo serialize*() methods should return a patError object instead of false!!!!
+ * Has to be changed asap!
+ */
+ function serialize()
+ {
+ $methodName = "serialize".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
+
+ if ( !method_exists( $this, $methodName ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
+ "Element method for form mode '".$this->getMode()."' (".$methodName.") is not available."
+ );
+ }
+
+ /**
+ * get the value for internal use
+ * The PHP-filters will not be applied
+ */
+ $value = $this->getValue( false );
+
+ $element = $this->$methodName( $value );
+ if ( patErrorManager::isError( $element ) )
+ {
+ return $element;
+ }
+
+ return $element;
+ }
+
+ /**
+ * Template method that applies rules and calls the elements
+ * validation method
+ *
+ * @final
+ * @access public
+ * @return bool $success True on success, false otherwise
+ */
+ function validate()
+ {
+ // apply locale, if the current locale is a custom locale
+ if (patForms::isCustomLocale($this->locale)) {
+ $cnt = count( $this->_rules );
+ for ( $i = 0; $i < $cnt; $i++ ) {
+ $this->_rules[$i]['rule']->setLocale($this->locale);
+ }
+ }
+
+ /**
+ * validate custom rules
+ */
+ if ( !$this->_applyRules( PATFORMS_RULE_BEFORE_VALIDATION ) )
+ {
+ $this->_announce( 'status', 'error' );
+ return false;
+ }
+
+ /**
+ * the the unfiltered value
+ */
+ $value = $this->getValue( false );
+
+ $valid = $this->validateElement( $value );
+ if ( $valid === false )
+ {
+ $this->_announce( 'status', 'error' );
+ return false;
+ }
+
+ /**
+ * validate custom rules
+ */
+ if ( !$this->_applyRules( PATFORMS_RULE_AFTER_VALIDATION ) )
+ {
+ $this->_announce( 'status', 'error' );
+ return false;
+ }
+
+ $this->_announce( 'status', 'validated' );
+ return true;
+ }
+
+ /**
+ * validates the given data with the element's validation routines
+ * and returns the data with any needed modifications.
+ *
+ * @abstract
+ * @access private
+ * @return bool $success True on success, false otherwise
+ */
+ function validateElement()
+ {
+ // your code here
+ return true;
+ }
+
+ /**
+ * apply rules
+ *
+ * @access private
+ * @param integer time of validation
+ * @return boolean rules are valid or not
+ * @todo add documentation
+ */
+ function _applyRules( $time )
+ {
+ $valid = true;
+
+ $cnt = count( $this->_rules );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( ( $this->_rules[$i]['time'] & $time ) != $time )
+ continue;
+
+ $result = $this->_rules[$i]['rule']->applyRule( $this, $time );
+ if ( $result === false )
+ {
+ $valid = false;
+ }
+ }
+ return $valid;
+ }
+
+ /**
+ * finalize the element.
+ *
+ * Used as a template method.
+ *
+ * @final
+ * @access protected
+ * @return bool $success True on success, false otherwise
+ * @uses finalizeElement() to call the user code
+ */
+ function finalize()
+ {
+ $value = $this->getValue( false );
+ return $this->finalizeElement( $value );
+ }
+
+ /**
+ * finalize the element
+ *
+ * Offers the possibility to process any needed operations after the element
+ * has been validated. Implement any tasks that you need to do then here - a
+ * good example is the File element, where this method enables the moving of
+ * the uploaded file to the correct location.
+ *
+ * @abstract
+ * @access private
+ * @param mixed value of the element
+ * @return bool $success True on success, false otherwise
+ */
+ function finalizeElement( $value )
+ {
+ return true;
+ }
+
+ /**
+ * Enables an element option.
+ *
+ * See the {@link patForms::$options} property for an exhaustive list of available options.
+ *
+ * @access public
+ * @param string $option The option to enable
+ * @param array $params Optional parameters for the option
+ * @see disableOption()
+ * @see $options
+ */
+ function enableOption( $option, $params = array() )
+ {
+ if ( !isset( $this->options[$option] ) )
+ $this->options[$option] = array();
+
+ $this->options[$option]['enabled'] = true;
+ $this->options[$option]['params'] = $params;
+ }
+
+ /**
+ * Disables an element option
+ *
+ * See the {@link patForms::$options} property for an exhaustive list of available options.
+ *
+ * @access public
+ * @param string $option The option to disable
+ * @see enableOption()
+ * @see $options
+ */
+ function disableOption( $option )
+ {
+ if ( !isset( $this->options[$option] ) )
+ $this->options[$option] = array();
+
+ $this->options[$option]['enabled'] = false;
+ }
+
+ /**
+ * [helper method] validates the given value according to the specified method. It first
+ * checks if there is a method to check the format in the {@link patForms_FormatChecker}
+ * class, then checks in the element class itself.
+ *
+ * @access public
+ * @param mixed $value The value to validate the format of
+ * @param string $format The format to validate the value with
+ * @return bool $isValid True if valid, false if invalid or no method exists to validate the format.
+ * @see patForms_FormatChecker
+ */
+ function validateFormat( $value, $format )
+ {
+ if ( !class_exists( "patForms_FormatChecker" ) )
+ {
+ $checkerFile = dirname( __FILE__ )."/FormatChecker.php";
+ if ( !file_exists( $checkerFile ) )
+ {
+ $this->valid = false;
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_FORMAT_CHECKER_NOT_FOUND,
+ "Type checker could not be found, aborting validation."
+ );
+ }
+
+ include_once( $checkerFile );
+ }
+
+ $format = strtolower( $format );
+
+ $methodName = "is_".$format;
+ $option = false;
+
+ if ( method_exists( $this, $methodName ) )
+ {
+ return $this->$methodName( $value );
+ }
+
+ if ( in_array( $methodName, get_class_methods( "patForms_FormatChecker" ) ) )
+ {
+ return call_user_func( array( 'patForms_FormatChecker', $methodName ), $value );
+ }
+
+ return false;
+ }
+
+ /**
+ * get next error offset
+ *
+ * @access public
+ * @return integer
+ */
+ function getErrorOffset( $requiredCodes = 100 )
+ {
+ $offset = $this->nextErrorOffset;
+ $this->nextErrorOffset = $this->nextErrorOffset + $requiredCodes;
+ return $offset;
+ }
+
+ /**
+ * add error codes and messages for validator method
+ *
+ * @access public
+ * @param array defintions
+ * @param integer offset for the error codes
+ */
+ function addValidatorErrorCodes( $defs, $offset = 1000 )
+ {
+ foreach ( $defs as $lang => $codes )
+ {
+ if ( !isset( $this->validatorErrorCodes[$lang] ) ) {
+ $this->validatorErrorCodes[$lang] = array();
+ }
+
+ foreach ( $codes as $code => $message ) {
+ $this->validatorErrorCodes[$lang][($offset+$code)] = $message;
+ }
+ }
+ }
+
+ /**
+ * getValidationErrors
+ *
+ * @access public
+ * @return array errors that occured during the validation
+ */
+ function getValidationErrors()
+ {
+ return $this->validationErrors;
+ }
+
+ /**
+ * addValidationError
+ *
+ *
+ * @access public
+ * @param integer $code
+ * @param array $vars fill named placeholder with values
+ * @return boolean $result true on success
+ */
+ function addValidationError( $code, $vars = array() )
+ {
+ $error = false;
+ $lang = $this->locale;
+ $element = $this->getElementName();
+
+ // find error message for selected language
+ while ( true )
+ {
+ // error message matches language code
+ if ( isset( $this->validatorErrorCodes[$lang][$code] ) )
+ {
+ $error = array( "element" => $element, "code" => $code, "message" => $this->validatorErrorCodes[$lang][$code] );
+ break;
+ }
+ // no message found and no fallback-langauage available
+ else if ( $lang == "C" )
+ {
+ break;
+ }
+
+ $lang_old = $lang;
+
+ // look for other languages
+ if ( strlen( $lang ) > 5 )
+ {
+ list( $lang, $trash ) = explode( ".", $lang );
+ }
+ else if ( strlen( $lang ) > 2 )
+ {
+ list( $lang, $trash ) = explode( "_", $lang );
+ }
+ else
+ {
+ $lang = "C";
+ }
+
+ // inform developer about missing language
+ patErrorManager::raiseNotice(
+ PATFORMS_ELEMENT_ERROR_VALIDATOR_ERROR_LOCALE_UNDEFINED,
+ "Required Validation Error-Code for language: $lang_old not available. Now trying language: $lang",
+ "Add language definition in used element or choose other language"
+ );
+
+ }
+
+ // get default Error!
+ if ( !$error )
+ {
+ patErrorManager::raiseWarning(
+ PATFORMS_ELEMENT_ERROR_VALIDATOR_ERROR_UNDEFINED,
+ "No Error Message for this validation Error was defined",
+ "Review the error-definition for validation-errors in your element '$element'."
+ );
+ $error = array( "element" => $element, "code" => 0, "message" => "Unknown validation Error" );
+ }
+
+ // insert values to placeholders
+ if ( !empty( $vars ) )
+ {
+ foreach ( $vars as $key => $value )
+ {
+ $error["message"] = str_replace( "[". strtoupper( $key ) ."]", $value, $error["message"] );
+ }
+ }
+
+ array_push( $this->validationErrors, $error );
+ $this->valid = false;
+ return true;
+ }
+
+ /**
+ * applies the specified modifiers to an attribute value, as set in the attribute definition.
+ *
+ * @access private
+ * @param mixed $attributeValue The value of the attribute to modify
+ * @param array $modifiers Array containing the list of modifiers and their options to apply.
+ * @return mixed $attributeValue The modified attribute value.
+ * @see createAttributes()
+ */
+ function _applyModifiers( $attributeValue, $modifiers )
+ {
+ if ( !is_array( $modifiers ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_MODIFIER_NOT_AN_ARRAY,
+ "Modifiers are not an array"
+ );
+ }
+
+ foreach ( $modifiers as $modifier => $modifierOptions )
+ {
+ // compute method name for this definition and check if it exists
+ $modifierMethod = "_modifier".ucfirst( $modifier );
+
+ if ( !method_exists( $this, $modifierMethod ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_METHOD_FOR_MODIFIER_NOT_FOUND,
+ "Method not found for modifier '" . $modifier . "' (".$modifierMethod.") in class '" . get_class( $this ) . "'"
+ );
+ }
+
+ $modifiedValue = $this->$modifierMethod( $attributeValue );
+
+ if ( $modifiedValue === false )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_MODIFIER_RETURNED_ERROR,
+ "Modifier '".$modifier."' returned an error."
+ );
+ }
+
+ $attributeValue = $modifiedValue;
+ }
+
+ return $attributeValue;
+ }
+
+ /**
+ * insertSpecials attribute value modifier
+ *
+ * you can use special placeholders to insert dynamic values into the attribute values.
+ * This method inserts the correct information for each placeholder in the given string.
+ *
+ * @access private
+ * @param string $string The string to insert the specials in
+ * @return string $string The string with all needed replacements
+ * @see _applyModifiers()
+ * @todo Maybe make this configurable
+ * @todo Add any other relevant information
+ */
+ function _modifierInsertSpecials( $modifyValue, $options = array() )
+ {
+ if ( is_array( $modifyValue ) || is_object( $modifyValue ) || is_array( $this->value ) )
+ return $modifyValue;
+
+ // go through each attribute in the attribute definition and replace the strings
+ // with the corresponding attribute values.
+ foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
+ {
+ // if attribute was not set, strip the variable by setting it to empty.
+ $attributeValue = "";
+
+ // retrieve real attribute value if it was set
+ if ( isset( $this->attributes[$attributeName] ) && is_string( $this->attributes[$attributeName] ) )
+ {
+ $attributeValue = $this->attributes[$attributeName];
+ }
+
+ $search = $this->modifierStart."ELEMENT_".strtoupper( $attributeName ).$this->modifierEnd;
+
+ // make the replacement
+ $modifyValue = str_replace( $search, $attributeValue, $modifyValue );
+ }
+
+ // the element's value is special...
+ $modifyValue = str_replace( $this->modifierStart."ELEMENT_VALUE".$this->modifierEnd, $this->value, $modifyValue );
+
+ return $modifyValue;
+ }
+
+ /**
+ * checks the format of an attribute value according to the given format.
+ *
+ * @access private
+ * @param mixed $attributeValue The attribute value to check
+ * @param string $format The format to check the attribute value against
+ * @return bool $result True if format check succeeded, false otherwise.
+ * @see createAttributes()
+ * @todo Implement this method sometime
+ */
+ function _checkAttributeFormat( $attributeValue, $format )
+ {
+ return true;
+ }
+
+ /**
+ * validates the current attribute collection according to the attributes definition
+ * and the given output format, and returns the list of valid attributes.
+ *
+ * @access private
+ * @param string $format The output format to retrieve the attributes for.
+ * @return mixed $attributes The list of attributes, or false if failed.
+ */
+ function getAttributesFor( $format )
+ {
+ $attributes = array();
+
+ foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
+ {
+ if ( !isset( $this->attributes[$attributeName] ) )
+ {
+ if ( $attributeDef["required"] )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_ATTRIBUTE_REQUIRED,
+ 'The element "'.$this->getElementName().'" needs the attribute "'.$attributeName.'" to be set.',
+ 'See the attribute definition of the element class "'.get_class( $this ).'"'
+ );
+ }
+
+ continue;
+ }
+
+ $attributeValue = $this->attributes[$attributeName];
+
+ // special case disabled attribute: skip this if it is not set to yes
+ // to avoid generating a disabled field anyway (empty HTML attribute)
+ if ( $attributeName == 'disabled' && $attributeValue != 'yes' )
+ {
+ continue;
+ }
+
+ if ( isset( $attributeDef["modifiers"] ) && !empty( $attributeDef["modifiers"] ) )
+ {
+ $modifiedValue = $this->_applyModifiers( $attributeValue, $attributeDef["modifiers"] );
+ if ( $modifiedValue === false )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_UNABLE_TO_APPLY_MODIFIER_TO_ATTRIBUTE,
+ "Could not apply modifier to attribute '".$attributeName."' (value:'".$attributeValue."')"
+ );
+ }
+
+ $attributeValue = $modifiedValue;
+
+ // store this for later use too
+ $this->attributes[$attributeName] = $attributeValue;
+ }
+
+ if ( !in_array( $format, $attributeDef["outputFormats"] ) )
+ {
+ continue;
+ }
+
+ if ( isset( $attributeDef["format"] ) )
+ {
+ if ( !$this->_checkAttributeFormat( $attributeValue, $attributeDef["format"] ) )
+ {
+ return patErrorManager::raiseError(
+ PATFORMS_ELEMENT_ERROR_CAN_NOT_VERIFY_FORMAT,
+ "Format '".$attributeDef["format"]."' could not be verified for attribute '".$attributeName."' => '".$attributeValue."'"
+ );
+ }
+ }
+
+ $attributes[$attributeName] = $attributeValue;
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * [helper method] wrapper for the {@link createTag()} method which automates the tag
+ * creation by creating the tag from the current attribute collection and element type.
+ *
+ * @access protected
+ * @return mixed $result The created tag, or false if failed.
+ * @see elementType
+ * @see attributes
+ * @see createTag()
+ */
+ function toHtml()
+ {
+ $attributes = $this->getAttributesFor( $this->getFormat() );
+ if ( patErrorManager::isError( $attributes ) )
+ {
+ return $attributes;
+ }
+
+ return $this->createTag( $this->elementType[$this->getFormat()], "full", $attributes );
+ }
+
+ /**
+ * [helper method] create a hidden field with the given value. Retrieves all other needed
+ * attributes from the attributes collection.
+ * @access public
+ */
+ function createHiddenTag( $value )
+ {
+ $attribs = array( 'type' => 'hidden',
+ 'name' => $this->attributes['name'],
+ 'value' => $value,
+ 'id' => $this->attributes['id'],
+ );
+
+ return $this->createTag( "input", "full", $attribs );
+ }
+
+ /**
+ * [helper method] creates a hidden field with the given value. Used for the
+ * display=no attribute, and is the same as the createHiddenTag() method, only
+ * that the attributes collection is initialized to ensure that any variables
+ * in the element's attributes get replaced.
+ *
+ * @access private
+ * @param mixed $value The value of the element
+ * @return string $element The serialized hidden tag
+ * @see createHiddenTag()
+ */
+ function createDisplaylessTag( $value )
+ {
+ // call this to initialize all attributes. This is needed
+ // here to make sure that if there are
+ $this->getAttributesFor( $this->getFormat() );
+
+ return $this->createHiddenTag( $value );
+ }
+
+ /**
+ * [helper method] create an element HTML source from its attribute collection and
+ * returns it.
+ *
+ * @static
+ * @access protected
+ * @param string $tagname The name of the element / tag
+ * @param string $type Optional: the type of element to generate. Valid parameters are full|opening|closing|empty. Defaults to "full".
+ * @param mixed $value The value of the element
+ * @return string $element The HTML source of the element
+ */
+ function createTag( $tagname, $type = "full", $attributes = array(), $value = false )
+ {
+ switch( $type )
+ {
+ case "closing":
+ return "$tagname>";
+ break;
+
+ case "empty":
+ case "opening":
+ $tag = "<".$tagname;
+
+ // create attribute collection
+ foreach ( $attributes as $attributeName => $attributeValue )
+ {
+ $tag = $tag . " ".$attributeName."=\"".htmlentities( (string)$attributeValue )."\"";
+ }
+
+ // empty tag?
+ if ( $type == "empty" )
+ {
+ $tag = $tag . " />";
+ return $tag;
+ }
+
+ $tag = $tag . ">";
+ return $tag;
+
+ break;
+
+ case "full":
+ if ( $value === false )
+ {
+ return patForms_Element::createTag( $tagname, "empty", $attributes );
+ }
+
+ return patForms_Element::createTag( $tagname, "opening", $attributes ).htmlentities( $value ).patForms_Element::createTag( $tagname, "closing" );
+ break;
+ }
+ }
+
+ /**
+ * create XML representation of the element
+ *
+ * This can be used when you need to store the structure
+ * of your form in flat files or create form templates that can
+ * be read by patForms_Parser at a later point.
+ *
+ * @access public
+ * @param string namespace
+ * @uses getElementName()
+ * @see patForms_Parser
+ */
+ function toXML( $namespace = null )
+ {
+ $tagName = $this->getElementName();
+
+ // prepend Namespace
+ if ( $namespace != null )
+ {
+ $tagName = $namespace.':'.$tagName;
+ }
+
+ // get all attributes
+ $attributes = $this->getAttributes();
+
+ // create valid XML attributes
+ foreach ( $attributes as $key => $value )
+ {
+ $attributes[$key] = strtr( $value, $this->xmlEntities );
+ }
+
+ $value = strtr( $this->getValue(), $this->xmlEntities );
+
+ if ( $value != false )
+ {
+ return $this->createTag( $tagName, "full", $attributes, $value );
+ }
+ return $this->createTag( $tagName, "empty", $attributes );
+ }
+
+ /**
+ * apply a filter
+ *
+ * This is still in alpha state!
+ *
+ * @access public
+ * @param object patForms_Filter
+ * @todo add error management and docs
+ * @todo allow filter to be an array containg two callbacks
+ * array( 'in' => 'myInFunc', 'out' => 'myOutFunc' ) )
+ */
+ function applyFilter( &$filter )
+ {
+ $this->filters[] = &$filter;
+ return true;
+ }
+
+ /**
+ * Get the name of the element, as stored in the elementName property.
+ *
+ * This is used when serializing an element to XML to
+ * create a now form template.
+ *
+ * This method checks for the $elementName property and if it
+ * is set to null, it will extract the element name from the class name
+ *
+ * @access public
+ * @return string tag name
+ */
+ function getElementName()
+ {
+ if ( $this->elementName != null )
+ {
+ return $this->elementName;
+ }
+
+ $class = get_class( $this );
+ $name = substr( strrchr( $class, "_" ), 1 );
+ return ucfirst( $name );
+ }
+
+ /**
+ * checks wheter sessions are used or switch session usage on or of
+ *
+ * If switch argument is missing, this function just reports if sessions
+ * will be used or not
+ *
+ * @access protected
+ * @param string $switch switch sessions on ("yes") or off ("yes")
+ * @return boolean $result true if sessions will be used, false otherwise
+ * @see setSessionValue()
+ * @see getSessionValue()
+ * @see unsetSessionValue()
+ * @todo destroy session variables if sessions won't be usead any further
+ */
+ function useSession( $switch = null )
+ {
+ // switch sessions on or off
+ if ( $switch == "yes" )
+ {
+ $this->attributes["usesession"] = "yes";
+ }
+ else if ( $switch == "no" )
+ {
+ $this->attributes["usesession"] = "no";
+ return false;
+ }
+
+ if ( isset( $this->attributes["usesession"] ) && $this->attributes["usesession"] == "yes" )
+ {
+ if ( !$this->sessionVar )
+ {
+ if ( !defined( "SID" ) )
+ {
+ session_start();
+ }
+
+ $name = $this->attributes["name"];
+ if ( !isset( $_SESSION["_patforms_element"][$name] ) )
+ {
+ $_SESSION["_patforms_element"][$name] = array();
+ }
+
+ $this->sessionVar =& $_SESSION["_patforms_element"][$name];
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * save a variable to the session
+ *
+ * @access protected
+ * @param string $name name to identify the variable
+ * @param mixed $value
+ * @return boolean $result true on success
+ * @see getSessionValue()
+ * @see unsetSessionValue()
+ */
+ function setSessionValue( $name, $value )
+ {
+ if ( !$this->useSession() )
+ {
+ return false;
+ }
+
+ $this->sessionVar[$name] = $value;
+ return true;
+ }
+
+ /**
+ * get a variable from session
+ *
+ * @access protected
+ * @param string $name name to identify the variable
+ * @return mixed $result false if no sessions are used, null if variable is not set or the value of the variable
+ * @see getSessionValue()
+ * @see unsetSessionValue()
+ */
+ function getSessionValue( $name )
+ {
+ if ( !$this->useSession() )
+ {
+ return false;
+ }
+
+ if ( isset( $this->sessionVar[$name] ) )
+ {
+ return $this->sessionVar[$name];
+ }
+ return null;
+ }
+
+ /**
+ * remove a variable from session
+ *
+ * @access protected
+ * @param string $name name to identify the variable
+ * @return mixed $result false if no sessions are used, null if variable is not set or the value of the variable
+ * @see getSessionValue()
+ * @see setSessionValue)
+ */
+ function unsetSessionValue( $name )
+ {
+ if ( !$this->useSession() )
+ {
+ return false;
+ }
+
+ $value = null;
+ if ( isset( $this->sessionVar[$name] ) )
+ {
+ $value = $this->sessionVar[$name];
+ unset( $this->sessionVar[$name] );
+ }
+ return $value;
+ }
+
+ /**
+ * get the global javascript of the element
+ *
+ * @access public
+ * @return string
+ */
+ /*
+ function getGlobalJavascript()
+ {
+ if ( !isset( $this->globalJavascript[$this->format] ) )
+ {
+ $script = '';
+ }
+ else
+ {
+ $script = $this->globalJavascript[$this->format];
+ }
+
+ $cnt = count( $this->_rules );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $tmp = $this->_rules[$i]['rule']->getGlobalJavascript();
+ if ( $tmp === false )
+ continue;
+ $script .= $tmp;
+ }
+
+ return $script;
+ }
+ */
+
+ /**
+ * get the instance javascript of the element
+ *
+ * @access public
+ * @return string javascript for this instance
+ */
+ /*
+ function getInstanceJavascript()
+ {
+ if ( !isset( $this->instanceJavascript[$this->format] ) )
+ {
+ $script = '';
+ }
+ else
+ {
+ $script = $this->instanceJavascript[$this->format];
+
+ $script = str_replace( '[ELEMENT::NAME]', $this->getName(), $script );
+ $script = str_replace( '[ELEMENT::ID]', $this->getId(), $script );
+ }
+
+ $cnt = count( $this->_rules );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $tmp = $this->_rules[$i]['rule']->getInstanceJavascript();
+ if ( $tmp === false )
+ continue;
+ $script .= $tmp;
+ }
+
+ return $script;
+ }
+ */
+
+ function registerJavascripts(&$form) {
+
+ if ($script = $this->getGlobalJavascript()) {
+ $form->registerGlobalJavascript($this->elementName, $script);
+ }
+
+ if ($script = $this->getInstanceJavascript()) {
+ $form->registerInstanceJavascript($script);
+ }
+
+ foreach ($this->_rules as $rule) {
+ $rule['rule']->registerJavascripts($form);
+ }
+ }
+
+ function getGlobalJavascript() {
+
+ if (isset($this->globalJavascript[$this->format])) {
+ return $this->globalJavascript[$this->format];
+ }
+ }
+
+ function getInstanceJavascript() {
+
+ if (isset($this->instanceJavascript[$this->format])) {
+ $script = $this->instanceJavascript[$this->format];
+ $script = str_replace('[ELEMENT::NAME]', $this->getName(), $script);
+ $script = str_replace('[ELEMENT::ID]', $this->getId(), $script);
+ return $script;
+ }
+ }
+
+ /**
+ * retrieves the element's current submitted state.
+ *
+ * @access public
+ * @return bool $state True if it has been submitted, false otherwise.
+ * @see submitted
+ */
+ function isSubmitted()
+ {
+ if ( $this->submitted === true ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * returns the locale that is currently set for the form.
+ *
+ * @access public
+ * @return string $locale The locale.
+ * @see setLocale()
+ * @see $locale
+ */
+ function getLocale()
+ {
+ return $this->locale;
+ }
+
+ /**
+ * anounce a change in the element to all observers
+ *
+ * @access private
+ * @param string property that changed
+ * @param mixed new value of the property
+ */
+ function _announce( $property, $value )
+ {
+ $cnt = count( $this->observers );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $this->observers[$i]->notify( $this, $property, $value );
+ }
+ return true;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule.php b/3rd_party/php/propel/contrib/pat/patForms/Rule.php
new file mode 100644
index 000000000..911c0965e
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule.php
@@ -0,0 +1,340 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ * @todo implement javascript helper methods (set a javascript property plus an
+ * array of keys that will be replaced by the properties of the rule)
+ */
+class patForms_Rule
+{
+ /**
+ * time when the rule should be applied
+ *
+ * Possible values are:
+ * -PATFORMS_RULE_BEFORE_VALIDATION
+ * -PATFORMS_RULE_AFTER_VALIDATION
+ * -PATFORMS_RULE_BOTH
+ *
+ * @access private
+ * @var integer
+ */
+ var $_time = PATFORMS_RULE_AFTER_VALIDATION;
+
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalScript = array();
+
+ /**
+ * script that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array();
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array();
+
+ /**
+ * store the container of the rule
+ *
+ * @access private
+ * @var object
+ */
+ var $container;
+
+ /**
+ * define error codes an messages for each form element
+ *
+ * @abstract
+ * @access private
+ * @var array
+ */
+ var $validatorErrorCodes = array();
+
+ /**
+ * error code offset for the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $errorOffset;
+
+ /**
+ * format of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $format = 'html';
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = '';
+
+ /**
+ * Get the time when the rule should be applied.
+ *
+ * This has to be defined in the _time property of the rule.
+ *
+ * @access public
+ * @return integer
+ */
+ function getTime()
+ {
+ return $this->_time;
+ }
+
+ /**
+ * create a new rule object
+ *
+ * @access public
+ * @param string id
+ */
+ function patForms_Rule( $id = null )
+ {
+ if ( $id === null )
+ {
+ $id = uniqid( '' );
+ }
+
+ $this->_id = $id;
+ }
+
+ /**
+ * set the id for the rule
+ *
+ * @access public
+ * @param string id
+ */
+ function setId( $id )
+ {
+ $this->_id = $id;
+ }
+
+ /**
+ * set the locale, this is needed to update the rule
+ * translations, that have been passed to the container
+ * element
+ *
+ * @access public
+ * @param string new locale
+ * @return boolean
+ */
+ function setLocale( $locale )
+ {
+ // rules do not store locale information
+ if (!patForms::isCustomLocale($locale)) {
+ return true;
+ }
+
+ $errorMessages = patForms::getCustomLocale($locale, 'Rule::' . $this->getRuleName());
+
+ if (is_array($errorMessages)) {
+ $this->validatorErrorCodes[$locale] = $errorMessages;
+ }
+
+ $this->container->addValidatorErrorCodes( $this->validatorErrorCodes, $this->errorOffset );
+
+ return true;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * This method is used to initialize the rule.
+ * By default it adds it validatorErrorCodes
+ * to the container and stores a reference to the
+ * container.
+ *
+ * You may extend it in your custom rules, but should always be calling
+ * this method using:
+ *
+ *
+ * patForms_Rule::prepareRule( $container );
+ *
+ *
+ * @access public
+ * @param object Either a patForms or patForms_Element object
+ */
+ function prepareRule( &$container )
+ {
+ $this->format = $container->getFormat();
+
+ $this->container = &$container;
+ $this->errorOffset = $container->getErrorOffset();
+
+ $container->addValidatorErrorCodes( $this->validatorErrorCodes, $this->errorOffset );
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @abstract
+ * @access public
+ * @param object Either a patForms or patForms_Element object
+ * @return boolean true, if rule has been applied succesfully, false otherwise
+ */
+ function applyRule( &$container, $type = PATFORMS_RULE_BEFORE_VALIDATION )
+ {
+ // your code
+ }
+
+ /**
+ * addValidationError
+ *
+ * @access private
+ * @param integer $code
+ * @param array $vars fill named placeholder with values
+ * @return boolean $result true on success
+ */
+ function addValidationError( $code, $vars = array() )
+ {
+ $code= $this->errorOffset + $code;
+ return $this->container->addValidationError( $code, $vars );
+ }
+
+ /**
+ * get the name of the rule
+ *
+ * By default just return the classname, this is sufficient.
+ *
+ * @access public
+ * @return string
+ */
+ function getRuleName()
+ {
+ if (!empty($this->ruleName)) {
+ return $this->ruleName;
+ }
+ return get_class( $this );
+ }
+
+ /**
+ * get the global javascript of the rule
+ *
+ * @access public
+ * @return string
+ * @todo Rules need to know the output format
+ */
+ /*
+ function getGlobalJavascript()
+ {
+ if ( isset( $this->globalScript['html'] ) )
+ {
+ return $this->globalScript['html'];
+ }
+ return '';
+ }
+ */
+
+ /**
+ * get the instance javascript of the rule
+ *
+ * @access public
+ * @return string
+ */
+ /*
+ function getInstanceJavascript()
+ {
+ if ( !isset( $this->instanceScript[$this->format] ) )
+ {
+ return false;
+ }
+ // get the script for the current format
+ $script = $this->instanceScript[$this->format];
+
+ // always replace the id
+ $script = str_replace( '[RULE::ID]', $this->_id, $script );
+ if ( method_exists( $this->container, 'getId' ) )
+ {
+ $script = str_replace( '[CONTAINER::ID]', $this->container->getId(), $script );
+ }
+ if ( method_exists( $this->container, 'getName' ) )
+ {
+ $script = str_replace( '[CONTAINER::NAME]', $this->container->getName(), $script );
+ }
+
+ foreach ( $this->scriptPlaceholders as $placeholder => $property )
+ {
+ if ( isset( $this->$property ) )
+ $script = str_replace( '['.$placeholder.']', $this->$property, $script );
+ else
+ $script = str_replace( '['.$placeholder.']', '', $script );
+ }
+ return $script;
+ }
+ */
+
+ function registerJavascripts(&$form) {
+
+ if ($script = $this->getGlobalJavascript()) {
+ $form->registerGlobalJavascript($this->getRuleName(), $script);
+ }
+
+ if ($script = $this->getInstanceJavascript()) {
+ $form->registerInstanceJavascript($script);
+ }
+ }
+
+ function getGlobalJavascript() {
+
+ if (isset($this->globalScript[$this->format])) {
+ return $this->globalScript[$this->format];
+ }
+ }
+
+ function getInstanceJavascript(){
+
+ if (isset($this->instanceScript[$this->format])) {
+ $script = $this->instanceScript[$this->format];
+ $script = str_replace('[RULE::ID]', $this->_id, $script);
+ if (method_exists($this->container, 'getId')) {
+ $script = str_replace('[CONTAINER::ID]', $this->container->getId(), $script);
+ }
+ if (method_exists($this->container, 'getName')) {
+ $script = str_replace('[CONTAINER::NAME]', $this->container->getName(), $script);
+ }
+ foreach ($this->scriptPlaceholders as $placeholder => $property) {
+ if (isset($this->$property)) {
+ $script = str_replace('['.$placeholder.']', $this->$property, $script);
+ } else {
+ $script = str_replace('['.$placeholder.']', '', $script);
+ }
+ }
+ return $script;
+ }
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/Match.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/Match.php
new file mode 100644
index 000000000..3d47185f2
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/Match.php
@@ -0,0 +1,163 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_Match extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::Match */
+
+function pFRC_Match(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_Match.prototype.validate = function() {
+ value = this.field.getValue();
+ if (!value.match(this.pattern)) {
+ alert('This is an invalid value.');
+ }
+}
+
+pFRC_Match.prototype.setValue = function(pattern) {
+ this.pattern = pattern;
+}
+
+/* END: patForms::Rule::Match */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_Match('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'Match';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "This is an invalid value.",
+ ),
+ "de" => array(
+ 1 => "Dies ist ein ungültiger Wert.",
+ ),
+ "fr" => array(
+ 1 => "This is an invalid value.",
+ )
+ );
+
+ /**
+ * the regEx pattern
+ * @access private
+ * @var string
+ */
+ var $_pattern;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ private $value = 10;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->_pattern = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (preg_match($this->_pattern, $element->getValue()) != 0){
+ return true;
+ }
+
+ $this->addValidationError(1);
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setValue(%s);\n", $this->_id, $this->_pattern);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxLength.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxLength.php
new file mode 100644
index 000000000..d28c2f758
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxLength.php
@@ -0,0 +1,161 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_MaxLength extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::MaxLength */
+
+function pFRC_MaxLength(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_MaxLength.prototype.validate = function() {
+ value = this.field.getValue();
+ if (value.length > this.value) {
+ alert('Please enter a value that is max. ' + this.value + ' characters long.');
+ }
+}
+
+pFRC_MaxLength.prototype.setValue = function(value) {
+ this.value = value;
+}
+
+/* END: patForms::Rule::MaxLength */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_MaxLength('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'MaxLength';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "Please enter a value that is max. [VALUE] characters long.",
+ ),
+ "de" => array(
+ 1 => "Bitte geben Sie einen max. [VALUE] Zeichen langen Wert ein.",
+ ),
+ "fr" => array(
+ 1 => "Please enter a value that is max. [VALUE] characters long.",
+ )
+ );
+
+ /**
+ * possible values
+ * @access private
+ * @var array
+ */
+ var $_values;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ private $value = 10;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->value = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (strlen($element->getValue()) <= $this->value) {
+ return true;
+ }
+
+ $this->addValidationError(1, array('value' => $this->value));
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setValue(%s);\n", $this->_id, $this->value);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxValue.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxValue.php
new file mode 100644
index 000000000..6b25b8c84
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/MaxValue.php
@@ -0,0 +1,163 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_MaxValue extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::MaxValue */
+
+function pFRC_MaxValue(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_MaxValue.prototype.validate = function() {
+ value = this.field.getValue();
+ if (parseInt(value) != value) {
+ alert('Please enter a number that is less or equal to ' + this.value);
+ }
+ if (parseInt(value) > this.value) {
+ alert('Please enter a number that is less or equal to ' + this.value);
+ }
+}
+
+pFRC_MaxValue.prototype.setMaxValue = function(value) {
+ this.value = value;
+}
+
+/* END: patForms::Rule::MaxValue */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_MaxValue('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'MaxValue';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "Please enter a number that is less or equal to [VALUE].",
+ ),
+ "de" => array(
+ 1 => "Bitte geben Sie eine Zahl kleiner oder gleich [VALUE] ein.",
+ ),
+ "fr" => array(
+ 1 => "Please enter a number that is less or equal to [VALUE].",
+ )
+ );
+
+ /**
+ * the regEx pattern
+ * @access private
+ * @var string
+ */
+ var $_value;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->_value = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (intval($element->getValue()) <= intval($this->_value)){
+ return true;
+ }
+
+ $this->addValidationError(1, array('value' => $this->_value));
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setMaxValue(%s);\n", $this->_id, $this->_value);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/MinLength.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/MinLength.php
new file mode 100644
index 000000000..427600e49
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/MinLength.php
@@ -0,0 +1,161 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_MinLength extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::MinLength */
+
+function pFRC_MinLength(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_MinLength.prototype.validate = function() {
+ value = this.field.getValue();
+ if (value.length < this.value) {
+ alert('Please enter a value that is at least ' + this.value + ' characters long.');
+ }
+}
+
+pFRC_MinLength.prototype.setValue = function(value) {
+ this.value = value;
+}
+
+/* END: patForms::Rule::MinLength */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_MinLength('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'MinLength';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "Please enter a value that is at least [VALUE] characters long.",
+ ),
+ "de" => array(
+ 1 => "Bitte geben Sie einen mindestens [VALUE] Zeichen langen Wert ein.",
+ ),
+ "fr" => array(
+ 1 => "Please enter a value that is at least [VALUE] characters long.",
+ )
+ );
+
+ /**
+ * possible values
+ * @access private
+ * @var array
+ */
+ var $_values;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ private $value = 10;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->value = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (strlen($element->getValue()) >= $this->value) {
+ return true;
+ }
+
+ $this->addValidationError(1, array('value' => $this->value));
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setValue(%s);\n", $this->_id, $this->value);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/MinValue.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/MinValue.php
new file mode 100644
index 000000000..f31b107a4
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/MinValue.php
@@ -0,0 +1,165 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_MinValue extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::MinValue */
+
+function pFRC_MinValue(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_MinValue.prototype.validate = function() {
+ value = this.field.getValue();
+ if (parseInt(value) != value) {
+ alert('Please enter a number that is greater or equal to ' + this.value);
+ }
+ if (parseInt(value) < this.value) {
+ alert('Please enter a number that is greater or equal to ' + this.value);
+ }
+}
+
+pFRC_MinValue.prototype.setMinValue = function(value) {
+ this.value = value;
+}
+
+/* END: patForms::Rule::MinValue */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_MinValue('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'MinValue';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "Please enter a number that is greater or equal to [VALUE].",
+ ),
+ "de" => array(
+ 1 => "Bitte geben Sie eine Zahl größer oder gleich [VALUE] ein.",
+ ),
+ "fr" => array(
+ 1 => "Please enter a number that is greater or equal to [VALUE].",
+ )
+ );
+
+ /**
+ * the regEx pattern
+ * @access private
+ * @var string
+ */
+ var $_value;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ private $value = 10;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->_value = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (intval($element->getValue()) >= intval($this->_value)){
+ return true;
+ }
+
+ $this->addValidationError(1, array('value' => $this->_value));
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setMinValue(%s);\n", $this->_id, $this->_value);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/NotMatch.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/NotMatch.php
new file mode 100644
index 000000000..c0ff7f365
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/NotMatch.php
@@ -0,0 +1,163 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_NotMatch extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::NotMatch */
+
+function pFRC_NotMatch(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_NotMatch.prototype.validate = function() {
+ value = this.field.getValue();
+ if (value.match(this.pattern)) {
+ alert('This is an invalid value.');
+ }
+}
+
+pFRC_NotMatch.prototype.setValue = function(pattern) {
+ this.pattern = pattern;
+}
+
+/* END: patForms::Rule::NotMatch */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_NotMatch('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'NotMatch';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "This is an invalid value.",
+ ),
+ "de" => array(
+ 1 => "Dies ist ein ungültiger Wert.",
+ ),
+ "fr" => array(
+ 1 => "This is an invalid value.",
+ )
+ );
+
+ /**
+ * the regEx pattern
+ * @access private
+ * @var string
+ */
+ var $_pattern;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ private $value = 10;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->_pattern = $value;
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (preg_match($this->_pattern, $element->getValue()) == 0){
+ return true;
+ }
+
+ $this->addValidationError(1);
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ $script = sprintf("pfr_%s.setValue(%s);\n", $this->_id, $this->_pattern);
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Rule/ValidValues.php b/3rd_party/php/propel/contrib/pat/patForms/Rule/ValidValues.php
new file mode 100644
index 000000000..290ace0ed
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Rule/ValidValues.php
@@ -0,0 +1,182 @@
+
+ * @license LGPL, see license.txt for details
+ * @link http://www.php-tools.net
+ */
+class patForms_Rule_ValidValues extends patForms_Rule
+{
+ /**
+ * script that will be displayed only once
+ *
+ * @access private
+ * @var array
+ */
+ var $globalScript = array(
+ 'html' => "/* patForms::Rule::ValidValues */
+
+Array.prototype.inArray = function(value) {
+ var i;
+ for (i=0; i < this.length; i++) {
+ if (this[i] === value) {
+ return true;
+ }
+ }
+ return false;
+};
+
+function pFRC_ValidValue(field) {
+ this.field = eval('pfe_' + field);
+}
+
+pFRC_ValidValue.prototype.validate = function() {
+ value = this.field.getValue();
+ for (var i = 0; i < this.values.length; i++) {
+ if (this.values[i] === value) {
+ return true;
+ }
+ }
+ var msg = 'Please enter one of the following values: ';
+ for (var i = 0; i < this.values.length; i++) {
+ msg = msg + this.values[i];
+ if (i < this.values.length - 1) {
+ msg = msg + ', ';
+ }
+ }
+ alert(msg);
+}
+
+pFRC_ValidValue.prototype.setValues = function(values) {
+ this.values = values;
+}
+
+/* END: patForms::Rule::ValidValue */
+"
+ );
+
+ /**
+ * javascript that will be displayed once per instance
+ *
+ * @access private
+ * @var array
+ */
+ var $instanceScript = array(
+ 'html' => "var pfr_[RULE::ID] = new pFRC_ValidValue('[CONTAINER::NAME]');\n"
+ );
+
+ /**
+ * properties that have to be replaced in the instance script.
+ *
+ * @access private
+ * @var array
+ */
+ var $scriptPlaceholders = array(
+ 'RULE::SOURCE' => '_source',
+ );
+
+ /**
+ * name of the rule
+ *
+ * @abstract
+ * @access private
+ */
+ var $ruleName = 'ValidValue';
+
+ /**
+ * define error codes and messages for the rule
+ *
+ * @access private
+ * @var array $validatorErrorCodes
+ * @todo translate error messages
+ */
+ var $validatorErrorCodes = array(
+ "C" => array(
+ 1 => "Please enter one of the following values: [VALUES].",
+ ),
+ "de" => array(
+ 1 => "Bitte geben Sie einen der folgenden Werte ein: [VALUES].",
+ ),
+ "fr" => array(
+ 1 => "Please enter one of the following values: [VALUES].",
+ )
+ );
+
+ /**
+ * possible values
+ * @access private
+ * @var array
+ */
+ var $_values;
+
+ /**
+ * field id that is used
+ * @access private
+ * @var string
+ */
+ var $_field;
+
+ public function __construct($params) {
+
+ parent::__construct();
+
+ extract($params);
+ $this->_values = explode('|', $value);
+ }
+
+ /**
+ * prepare the rule
+ *
+ * @access public
+ * @param object patForms
+ */
+ function prepareRule(&$container) {
+
+ patForms_Rule::prepareRule($container);
+
+ $onChange = $container->getAttribute('onchange');
+ $newHandler = sprintf('pfr_%s.validate();', $this->_id);
+ $container->setAttribute('onchange', $newHandler . $onChange);
+
+ return true;
+ }
+
+ /**
+ * method called by patForms or any patForms_Element to validate the
+ * element or the form.
+ *
+ * @access public
+ * @param object patForms form object
+ */
+ function applyRule(&$element, $type = PATFORMS_RULE_AFTER_VALIDATION) {
+
+ if (in_array($element->getValue(), $this->_values)) {
+ return true;
+ }
+
+ $this->addValidationError(1, array('values' => implode(', ', $this->_values)));
+ return false;
+ }
+
+ /**
+ *
+ *
+ * @access public
+ */
+ function registerJavascripts(&$form) {
+
+ parent::registerJavascripts($form);
+
+ foreach ($this->_values as $value) {
+ $values[] = "'$value'";
+ }
+ $script = sprintf("pfr_%s.setValues(new Array(%s));\n", $this->_id, implode(', ', $values));
+ $form->registerInstanceJavascript($script);
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/Storage/Propel.php b/3rd_party/php/propel/contrib/pat/patForms/Storage/Propel.php
new file mode 100644
index 000000000..f90c81dbb
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/Storage/Propel.php
@@ -0,0 +1,146 @@
+peer = new $peername();
+ $this->peername = $peername;
+
+ $parts = explode('.', explode('.', $this->peer->getOMClass()));
+ $this->classname = array_pop($parts);
+
+ $this->setPrimaryField('Id');
+ }
+
+ private function getCriteria($values) {
+
+ $object = new $this->classname();
+ //$object->populateFromArray($values); //TODO use a workaround until we'll get phpNamed keys in populateFromArray()
+ $object = $this->populateObjectFromArray($object, $values);
+ return $object->buildPkeyCriteria();
+ }
+
+ /**
+ * get an entry
+ *
+ * This tries to find an entry in the storage container
+ * that matches the current data that has been set in the
+ * form and populates the form with the data of this
+ * entry
+ *
+ * @access public
+ * @param object patForms patForms object that should be stored
+ * @return boolean true on success
+ */
+ public function loadEntry(&$form) {
+
+ if (!$object = $this->_entryExists($form->getValues())) {
+ // entry does not exists (why return an array here??)
+ return array();
+ }
+
+ $form->setValues($object->toArray());
+ return true;
+ }
+
+ public function validateEntry(&$form) {
+
+ if (!$object = $this->_entryExists($form->getValues())) {
+ $object = new $this->classname();
+ }
+ //$object->populateFromArray($form->getValues()); //TODO use a workaround until we'll get phpNamed keys in populateFromArray()
+ $object = $this->populateObjectFromArray($object, $form->getValues());
+ $result = $object->validate();
+
+ if ($result !== true) {
+ $mapBuilder = $this->peer->getMapBuilder();
+ $dbMap = $mapBuilder->getDatabaseMap();
+ foreach ($result as $colname => $error) {
+ list($tablename, $colname) = explode('.', $colname);
+ $column = $dbMap->getTable($tablename)->getColumn($colname);
+ $element = $form->getElement($column->getPhpName());
+ $element->addValidatorErrorCodes(array(
+ 'C' => array(
+ 1 => $error->getMessage() . ' (occured in Storage)',
+ ),
+ ), 1000);
+ $element->addValidationError(1001);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * adds an entry to the storage
+ *
+ * @param object patForms patForms object that should be stored
+ * @return boolean true on success
+ */
+ public function _addEntry(&$form) {
+
+ $object = new $this->classname();
+ //$object->populateFromArray($form->getValues()); //TODO use a workaround until we'll get phpNamed keys in populateFromArray()
+ $object = $this->populateObjectFromArray($object, $form->getValues());
+ $object->save();
+ return true;
+ }
+
+ /**
+ * updates an entry in the storage
+ *
+ * @param object patForms patForms object that should be stored
+ * @return boolean true on success
+ */
+ public function _updateEntry(&$form, $primary) {
+
+ $object = $this->_entryExists($form->getValues());
+ //$object->populateFromArray($form->getValues()); //TODO use a workaround until we'll get phpNamed keys in populateFromArray()
+ $object = $this->populateObjectFromArray($object, $form->getValues());
+ $object->save();
+ return true;
+ }
+
+ /**
+ * check, whether an entry exists
+ *
+ * @access private
+ * @param array
+ */
+ public function _entryExists($values) {
+
+ // This method gets called multiple times, e.g. when an existing
+ // object gets updated. We'll therefor cache results locally using
+ // a criteria string representation as hash.
+
+ static $objects;
+ $criteria = $this->getCriteria($values);
+ $hash = $criteria->toString();
+
+ if (isset($objects[$hash])) {
+ return $objects[$hash];
+ }
+
+ $objects[$hash] = $this->peer->doSelectOne($criteria);
+
+ if (empty($objects[$hash])) {
+ return false;
+ }
+ return $objects[$hash];
+ }
+
+ // this method is just a workaround
+
+ private function populateObjectFromArray($object, $values) {
+
+ foreach (array_keys($object->toArray()) as $key) {
+ if (array_key_exists($key, $values)) {
+ $object->{'set' . $key}($values[$key]);
+ }
+ }
+ return $object;
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pat/patForms/res/form.dynamic.tpl b/3rd_party/php/propel/contrib/pat/patForms/res/form.dynamic.tpl
new file mode 100644
index 000000000..3935ea6c1
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/res/form.dynamic.tpl
@@ -0,0 +1,94 @@
+
+
+
+
+
+ {TITLE}
+
+
+
+
+
+
+ {START}
+
+
+
Validation failed
+
Sorry, your input could not be saved for the following reasons:
+
+
+
{FIELD}: {MESSAGE}
+
+
+
+
+
+
+ {ELEMENT}
+
+
+
+
+
{ELEMENT}
+
{DESCRIPTION}
+
+
+
+
+
+
+ {END}
+
+
+
+
+
\ No newline at end of file
diff --git a/3rd_party/php/propel/contrib/pat/patForms/res/mysql-dump.bookstore.sql b/3rd_party/php/propel/contrib/pat/patForms/res/mysql-dump.bookstore.sql
new file mode 100644
index 000000000..48c0ceeb1
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms/res/mysql-dump.bookstore.sql
@@ -0,0 +1,10 @@
+
+# This first example is tested with a Bookstore project on MySql
+# (default setting Sqlite has not been tested)
+#
+# Additionally, you'll need some data in your tables. In case you
+# don't have - here's a mini-dump to get the example running.
+
+INSERT INTO `author` VALUES (1, 'Martin', 'Heidegger');
+INSERT INTO `book` VALUES (1, 'Sein und Zeit', '3484701226', NULL, NULL);
+INSERT INTO `publisher` VALUES (1, 'Max Niemeyer Verlag');
\ No newline at end of file
diff --git a/3rd_party/php/propel/contrib/pat/patForms_Storage_Propel_test.php b/3rd_party/php/propel/contrib/pat/patForms_Storage_Propel_test.php
new file mode 100644
index 000000000..04d3bdff1
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patForms_Storage_Propel_test.php
@@ -0,0 +1,97 @@
+
+
+function __autoload($classname) {
+ $filename = str_replace ('_', '/', $classname) . '.php';
+ require_once $filename;
+}
+
+/**
+ * Required packages:
+ *
+ * - Propel Bookstore project (tested only with mysql, not sqlite)
+ * - patForms (http://www.php-tools.net/site.php?file=patForms)
+ * - patTemplate (http://www.php-tools.net/site.php?file=patTemplate)
+ * - Xml_Serializer (http://pear.php.net/package/XML_Serializer)
+ *
+ * Installation:
+ *
+ * In theorie, it should work to
+ *
+ * - download the files from svn/propel/contrib/pat
+ * - save them anywhere in your servers docroot
+ * - tweak the following settings and
+ * - run this file
+ */
+
+// change these according to your setup
+
+$pathToBookstore = 'f:/test/propel'; // omit bookstore/ here
+$pathToPear = 'f:/pear';
+$pathToPat = 'f:/pear/pat';
+
+$path = PATH_SEPARATOR . $pathToBookstore . PATH_SEPARATOR . $pathToPat;
+set_include_path(get_include_path() . $path);
+
+// change these according to your propel settings
+$classname = 'book';
+$path = './patForms/res';
+$propelConfFilename = 'conf/bookstore-conf.php';
+
+// uncomment this to edit an existing record
+$pk = array('Id' => 2);
+
+
+/**
+ * the rest should work out of the box if you don't have any unusal
+ * types in your database schema.xml (strings, int etc. should work)
+ */
+
+require_once 'bookstore/' . $classname . '.php';
+Propel::init($propelConfFilename);
+
+// create a form definition
+
+$definition = patForms_Definition_Propel::create(array(
+ 'name' => $classname,
+ 'filename' => $path . '/form.' . $classname . '.xml',
+));
+
+// create a storage
+
+$storage = patForms::createStorage('Propel');
+$storage->setStorageLocation($classname . 'peer');
+
+// create a form
+
+$form = &patForms::createCreator('Definition')->create($definition);
+$form->setRenderer(patForms::createRenderer('Array'));
+$form->setStorage($storage);
+if (isset($pk)) {
+ $form->setValues($pk);
+}
+
+// render it to a patTemplate (could be done by other template engines)
+
+$tpl = new patTemplate();
+$tpl->setRoot($path);
+$tpl->readTemplatesFromInput('form.dynamic.tpl');
+
+$tpl->addVar('page', 'title', 'Bookstore party');
+$tpl->addVar('form', 'start', $form->serializeStart());
+$tpl->addVar('form', 'end', $form->serializeEnd());
+$tpl->addRows('elements', $form->renderForm());
+
+// this should be possible to be done in a more elegant way
+if ($errors = $form->getValidationErrors()) {
+ foreach ($errors as $field => $error) {
+ $tpl->addVar('error', 'field', $field);
+ foreach ($error as $line) {
+ $tpl->addVar('error', 'message', $line['message']);
+ $tpl->addVar('error', 'code', $line['code']);
+ $tpl->parseTemplate('error', 'a');
+ }
+ }
+ $tpl->setAttribute('errors', 'visibility', 'visible');
+}
+
+$tpl->displayParsedTemplate();
diff --git a/3rd_party/php/propel/contrib/pat/patTemplate.php b/3rd_party/php/propel/contrib/pat/patTemplate.php
new file mode 100644
index 000000000..01347f63a
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pat/patTemplate.php
@@ -0,0 +1,2378 @@
+
+ * @license LGPL
+ * @link http://www.php-tools.net
+ */
+
+/**
+ * template already exists
+ */
+define( 'PATTEMPLATE_ERROR_TEMPLATE_EXISTS', 5010 );
+
+/**
+ * template does not exist
+ */
+define ( 'PATTEMPLATE_WARNING_NO_TEMPLATE', 5011 );
+
+/**
+ * unknown type
+ */
+define ( 'PATTEMPLATE_WARNING_UNKNOWN_TYPE', 5012 );
+
+/**
+ * base class for module could not be found
+ */
+define( 'PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND', 5050 );
+
+/**
+ * module could not be found
+ */
+define( 'PATTEMPLATE_ERROR_MODULE_NOT_FOUND', 5051 );
+
+/**
+ * array expected
+ */
+define( 'PATTEMPLATE_ERROR_EXPECTED_ARRAY', 5052 );
+
+/**
+ * No input
+ */
+define( 'PATTEMPLATE_ERROR_NO_INPUT', 6000 );
+
+/**
+ * patTemplate
+ *
+ * powerful templating engine
+ *
+ * @version 3.0.0
+ * @package patTemplate
+ * @author Stephan Schmidt
+ * @license LGPL
+ * @link http://www.php-tools.net
+ */
+class patTemplate
+{
+ /**
+ * standard system vars that identify pat tools
+ * @var array
+ */
+ var $_systemVars = array(
+ 'appName' => 'patTemplate',
+ 'appVersion' => '3.0.0',
+ 'author' => array(
+ 'Stephan Schmidt '
+ )
+ );
+
+ /**
+ * default attributes for new templates
+ * @access private
+ * @var array
+ */
+ var $_defaultAttributes = array(
+ 'type' => 'standard',
+ 'visibility' => 'visible',
+ 'loop' => 1,
+ 'unusedvars' => 'strip',
+ 'whitespace' => 'keep',
+ 'autoclear' => 'off',
+ 'autoload' => 'on'
+ );
+
+ /**
+ * options for patTemplate
+ *
+ * Currently the following options are implemented:
+ * - maintainBc defines, whether patTemplate should be backwards compatible.
+ * This means, that you may use 'default' and 'empty' for subtemplates.
+ *
+ * @access private
+ * @var array
+ */
+ var $_options = array(
+ 'startTag' => '{',
+ 'endTag' => '}',
+ 'root' => '.',
+ 'namespace' => 'patTemplate',
+ 'maintainBc' => true
+ );
+
+ /**
+ * start tag
+ *
+ * @access private
+ * @var string
+ */
+ var $_startTag = '{';
+
+ /**
+ * end tag
+ *
+ * @access private
+ * @var string
+ */
+ var $_endTag = '}';
+
+ /**
+ * loaded modules
+ *
+ * Modules are:
+ * - Readers
+ * - Caches
+ * - Variable modifiers
+ * - Filters
+ *
+ * @access private
+ * @var array
+ */
+ var $_modules = array();
+
+ /**
+ * directories, where modules can be stored
+ * @access private
+ * @var array
+ */
+ var $_moduleDirs = array();
+
+ /**
+ * stores all template names
+ * @access private
+ * @var array
+ */
+ var $_templateList = array();
+
+ /**
+ * stores all template data
+ * @access private
+ * @var array
+ */
+ var $_templates = array();
+
+ /**
+ * stores all global variables
+ * @access private
+ * @var array
+ */
+ var $_globals = array();
+
+ /**
+ * stores all local variables
+ * @access private
+ * @var array
+ */
+ var $_vars = array();
+
+ /**
+ * stores the name of the first template that has been
+ * found
+ *
+ * @access private
+ * @var string
+ */
+ var $_root;
+
+ /**
+ * output filters that should be used
+ *
+ * @access private
+ * @var array
+ */
+ var $_outputFilters = array();
+
+ /**
+ * input filters that should be used
+ *
+ * @access private
+ * @var array
+ */
+ var $_inputFilters = array();
+
+ /**
+ * template cache, that should be used
+ *
+ * @access private
+ * @var array
+ */
+ var $_tmplCache = null;
+
+ /**
+ * Create a new patTemplate instance.
+ *
+ * The constructor accepts the type of the templates as sole parameter.
+ * You may choose one of:
+ * - html (default)
+ * - tex
+ *
+ * The type influences the tags you are using in your templates.
+ *
+ * @access public
+ * @param string type (either html or tex)
+ */
+ function patTemplate( $type = 'html' )
+ {
+ if ( !defined( 'PATTEMPLATE_INCLUDE_PATH' ) )
+ define( 'PATTEMPLATE_INCLUDE_PATH', dirname( __FILE__ ) . '/patTemplate' );
+
+ $this->setType( $type );
+ }
+
+ /**
+ * sets an option
+ *
+ * Currently, the following options are supported
+ * - maintainBc (true|false)
+ * - namespace (string)
+ *
+ * @access public
+ * @param string option to set
+ * @param string value of the option
+ */
+ function setOption( $option, $value )
+ {
+ $this->_options[$option] = $value;
+ }
+
+ /**
+ * gets an option
+ *
+ * @access public
+ * @param string option to get
+ * @return mixed value of the option
+ */
+ function getOption( $option )
+ {
+ if ( !isset( $this->_options[$option] ) )
+ return null;
+ return $this->_options[$option];
+ }
+
+ /**
+ * sets name of directory where templates are stored
+ *
+ * @access public
+ * @param string dir where templates are stored
+ * @deprecated please use patTemplate::setRoot() instead
+ */
+ function setBasedir( $basedir )
+ {
+ $this->_options['root'] = $basedir;
+ }
+
+ /**
+ * sets root base for the template
+ *
+ * The parameter depends on the reader you are using.
+ *
+ * @access public
+ * @param string root base of the templates
+ */
+ function setRoot( $root )
+ {
+ $this->_options['root'] = $root;
+ }
+
+ /**
+ * gets name of root base for the templates
+ *
+ * @access public
+ * @return mixed root base
+ */
+ function getRoot()
+ {
+ return $this->_options['root'];
+ }
+
+ /**
+ * sets namespace of patTemplate tags
+ *
+ * @access public
+ * @param string namespace
+ */
+ function setNamespace( $ns )
+ {
+ $this->_options['namespace'] = $ns;
+ }
+
+ /**
+ * gets namespace of patTemplate tags
+ *
+ * @access public
+ * @return string namespace
+ */
+ function getNamespace()
+ {
+ return $this->_options['namespace'];
+ }
+
+ /**
+ * set default attribute
+ *
+ * @access public
+ * @param string attribute name
+ * @param mixed attribute value
+ */
+ function setDefaultAttribute( $name, $value )
+ {
+ $this->_defaultAttributes[$name] = $value;
+ }
+
+ /**
+ * set default attributes
+ *
+ * @access public
+ * @param array attributes
+ */
+ function setDefaultAttributes( $attributes )
+ {
+ $this->_defaultAttributes = array_merge( $this->_defaultAttributes, $attributes );
+ }
+
+ /**
+ * get default attributes
+ *
+ * @access public
+ * @return return default attributes
+ */
+ function getDefaultAttributes()
+ {
+ return $this->_defaultAttributes;
+ }
+
+ /**
+ * set the type for the templates
+ *
+ * @access public
+ * @param string type (html or tex)
+ * @return boolean true on success
+ */
+ function setType( $type )
+ {
+ switch( strtolower( $type ) )
+ {
+ case "tex":
+ $this->setTags( '<{', '}>' );
+ break;
+ case "html":
+ $this->setTags( '{', '}' );
+ break;
+ default:
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_UNKNOWN_TYPE,
+ "Unknown type '$type'. Please use 'html' or 'tex'."
+ );
+ }
+ return true;
+ }
+
+ /**
+ * set the start and end tag for variables
+ *
+ * @access public
+ * @param string start tag
+ * @param string end tag
+ * @return boolean true on success
+ */
+ function setTags( $startTag, $endTag )
+ {
+ $this->_options['startTag'] = $startTag;
+ $this->_options['endTag'] = $endTag;
+
+ $this->_startTag = $startTag;
+ $this->_endTag = $endTag;
+ return true;
+ }
+
+ /**
+ * get start tag for variables
+ *
+ * @access public
+ * @return string start tag
+ */
+ function getStartTag()
+ {
+ return $this->_options['startTag'];
+ }
+
+ /**
+ * get end tag for variables
+ *
+ * @access public
+ * @return string end tag
+ */
+ function getEndTag()
+ {
+ return $this->_options['endTag'];
+ }
+
+ /**
+ * add a directory where patTemplate should search for
+ * modules.
+ *
+ * You may either pass a string or an array of directories.
+ *
+ * patTemplate will be searching for a module in the same
+ * order you added them. If the module cannot be found in
+ * the custom folders, it will look in
+ * patTemplate/$moduleType.
+ *
+ * @access public
+ * @param string module type
+ * @param string|array directory or directories to search.
+ */
+ function addModuleDir( $moduleType, $dir )
+ {
+ if ( !isset( $this->_moduleDirs[$moduleType] ) )
+ $this->_moduleDirs[$moduleType] = array();
+ if ( is_array( $dir ) )
+ $this->_moduleDirs[$moduleType] = array_merge( $this->_moduleDirs[$moduleType], $dir );
+ else
+ array_push( $this->_moduleDirs[$moduleType], $dir );
+ }
+
+ /**
+ * Sets an attribute of a template
+ *
+ * supported attributes: visibilty, loop, parse, unusedvars
+ *
+ * @param string $template name of the template
+ * @param string $attribute name of the attribute
+ * @param mixed $value value of the attribute
+ * @access public
+ * @see setAttributes(),getAttribute(), clearAttribute()
+ */
+ function setAttribute( $template, $attribute, $value )
+ {
+ $template = strtolower( $template );
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+
+ $attribute = strtolower( $attribute );
+ $this->_templates[$template]['attributes'][$attribute] = $value;
+ return true;
+ }
+
+ /**
+ * Sets several attribute of a template
+ *
+ * $attributes has to be a assotiative arrays containing attribute/value pairs
+ * supported attributes: visibilty, loop, parse, unusedvars
+ *
+ * @param string $template name of the template
+ * @param array $attributes attribute/value pairs
+ * @access public
+ * @see setAttribute(), getAttribute(), clearAttribute()
+ */
+ function setAttributes( $template, $attributes )
+ {
+ if ( !is_array( $attributes ) )
+ {
+ return patErrorManager::raiseError( PATTEMPLATE_ERROR_EXPECTED_ARRAY, 'patTemplate::setAttributes: Expected array as second parameter, '.gettype( $attributes ).' given' );
+ }
+
+ $template = strtolower( $template );
+ $attributes = array_change_key_case( $attributes );
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+
+ $this->_templates[$template]['attributes'] = array_merge( $this->_templates[$template]['attributes'], $attributes );
+ return true;
+ }
+
+ /**
+ * Get all attributes of a template
+ *
+ * @param string name of the template
+ * @return array attributes
+ * @access public
+ */
+ function getAttributes( $template )
+ {
+ $template = strtolower( $template );
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+ return $this->_templates[$template]['attributes'];
+ }
+
+ /**
+ * Gets an attribute of a template
+ *
+ * supported attributes: visibilty, loop, parse, unusedvars
+ *
+ * @param string $template name of the template
+ * @param string $attribute name of the attribute
+ * @return mixed value of the attribute
+ * @access public
+ * @see setAttribute(), setAttributes(), clearAttribute()
+ */
+ function getAttribute( $template, $attribute )
+ {
+ $template = strtolower( $template );
+ $attribute = strtolower( $attribute );
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+ return $this->_templates[$template]['attributes'][$attribute];
+ }
+
+ /**
+ * Clears an attribute of a template
+ *
+ * supported attributes: visibilty, loop, parse, unusedvars
+ *
+ * @param string $template name of the template
+ * @param string $attribute name of the attribute
+ * @access public
+ * @see setAttribute(), setAttributes(), getAttribute()
+ */
+ function clearAttribute( $template, $attribute )
+ {
+ $template = strtolower( $template );
+ $attribute = strtolower( $attribute );
+
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+ $this->_templates[$template]['attributes'][$attribute] = '';;
+ return true;
+ }
+
+ /**
+ * Prepare a template
+ *
+ * This can be used if you want to add variables to
+ * a template, that has not been loaded yet.
+ *
+ * @access public
+ * @param string template name
+ */
+ function prepareTemplate( $name )
+ {
+ $name = strtolower( $name );
+ if ( !isset( $this->_vars[$name] ) )
+ {
+ $this->_vars[$name] = array(
+ 'scalar' => array(),
+ 'rows' => array()
+ );
+ }
+ }
+
+ /**
+ * add a variable to a template
+ *
+ * A variable may also be an indexed array, but _not_
+ * an associative array!
+ *
+ * @access public
+ * @param string $template name of the template
+ * @param string $varname name of the variable
+ * @param mixed $value value of the variable
+ */
+ function addVar( $template, $varname, $value )
+ {
+ $template = strtolower( $template );
+ $varname = strtoupper( $varname );
+
+ if ( !is_array( $value ) )
+ {
+ $this->_vars[$template]['scalar'][$varname] = $value;
+ return true;
+ }
+
+ $cnt = count( $value );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( !isset( $this->_vars[$template]['rows'][$i] ) )
+ $this->_vars[$template]['rows'][$i] = array();
+
+ $this->_vars[$template]['rows'][$i][$varname] = $value[$i];
+ }
+
+ return true;
+ }
+
+ /**
+ * get the value of a variable
+ *
+ * @access public
+ * @param string name of the template
+ * @param string name of the variable
+ * @return string value of the variable, null if the variable is not set
+ */
+ function getVar( $template, $varname )
+ {
+ $template = strtolower( $template );
+ $varname = strtoupper( $varname );
+
+ if ( isset( $this->_vars[$template]['scalar'][$varname] ) )
+ return $this->_vars[$template]['scalar'][$varname];
+
+ $value = array();
+
+ $cnt = count( $this->_vars[$template]['rows'] );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( !isset( $this->_vars[$template]['rows'][$i][$varname] ) )
+ continue;
+ array_push( $value, $this->_vars[$template]['rows'][$i][$varname] );
+ }
+ if ( !empty( $value ) )
+ return $value;
+ return null;
+ }
+
+ /**
+ * Adds several variables to a template
+ *
+ * Each Template can have an unlimited amount of its own variables
+ * $variables has to be an assotiative array containing variable/value pairs
+ *
+ * @param string $template name of the template
+ * @param array $variables assotiative array of the variables
+ * @param string $prefix prefix for all variable names
+ * @access public
+ * @see addVar(), addRows(), addGlobalVar(), addGlobalVars()
+ */
+ function addVars( $template, $variables, $prefix = '' )
+ {
+ $template = strtolower( $template );
+ $prefix = strtoupper( $prefix );
+ $variables = array_change_key_case( $variables, CASE_UPPER );
+
+ foreach ( $variables as $varname => $value )
+ {
+ $varname = $prefix.$varname;
+
+ if ( !is_array( $value ) ) {
+ if (!is_scalar($value)) {
+ continue;
+ }
+ $this->_vars[$template]['scalar'][$varname] = $value;
+ continue;
+ }
+
+ $cnt = count( $value );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( !isset( $this->_vars[$template]['rows'][$i] ) )
+ $this->_vars[$template]['rows'][$i] = array();
+
+ $this->_vars[$template]['rows'][$i][$varname] = $value[$i];
+ }
+ }
+ }
+
+ /**
+ * Adds several rows of variables to a template
+ *
+ * Each Template can have an unlimited amount of its own variables
+ * Can be used to add a database result as variables to a template
+ *
+ * @param string $template name of the template
+ * @param array $rows array containing assotiative arrays with variable/value pairs
+ * @param string $prefix prefix for all variable names
+ * @access public
+ * @see addVar(), addVars(), addGlobalVar(), addGlobalVars()
+ */
+ function addRows( $template, $rows, $prefix = '' )
+ {
+ $template = strtolower( $template );
+ $prefix = strtoupper( $prefix );
+
+ $cnt = count( $rows );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ if ( !isset( $this->_vars[$template]['rows'][$i] ) )
+ $this->_vars[$template]['rows'][$i] = array();
+
+ $rows[$i] = array_change_key_case( $rows[$i], CASE_UPPER );
+
+ foreach ( $rows[$i] as $varname => $value )
+ {
+ $this->_vars[$template]['rows'][$i][$prefix.$varname] = $value;
+ }
+ }
+ }
+
+ /**
+ * Adds an object to a template
+ *
+ * All properties of the object will be available as template variables.
+ *
+ * @param string name of the template
+ * @param object|array object or array of objects
+ * @param string prefix for all variable names
+ * @access public
+ * @see addVar(), addRows(), addGlobalVar(), addGlobalVars()
+ */
+ function addObject( $template, $object, $prefix = '' )
+ {
+ if ( is_array( $object ) )
+ {
+ $rows = array();
+ foreach ( $object as $o )
+ array_push( $rows, get_object_vars( $o ) );
+
+ $this->addRows( $template, $rows, $prefix );
+ return true;
+ }
+ elseif ( is_object( $object ) )
+ {
+ $this->addVars( $template, get_object_vars( $object ), $prefix );
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds a global variable
+ *
+ * Global variables are valid in all templates of this object.
+ * A global variable has to be scalar, it will be converted to a string.
+ *
+ * @access public
+ * @param string $varname name of the global variable
+ * @param string $value value of the variable
+ * @return boolean true on success
+ * @see addGlobalVars(), addVar(), addVars(), addRows()
+ */
+ function addGlobalVar( $varname, $value )
+ {
+ $this->_globals[strtoupper( $varname )] = ( string )$value;
+ return true;
+ }
+
+ /**
+ * Adds several global variables
+ *
+ * Global variables are valid in all templates of this object.
+ *
+ * $variables is an associative array, containing name/value pairs of the variables.
+ *
+ * @access public
+ * @param array $variables array containing the variables
+ * @param string $prefix prefix for variable names
+ * @return boolean true on success
+ * @see addGlobalVar(), addVar(), addVars(), addRows()
+ */
+ function addGlobalVars( $variables, $prefix = '' )
+ {
+ $variables = array_change_key_case( $variables, CASE_UPPER );
+ $prefix = strtoupper( $prefix );
+ foreach ( $variables as $varname => $value )
+ {
+ $this->_globals[$prefix.$varname] = ( string )$value;
+ }
+ return true;
+ }
+
+ /**
+ * get all global variables
+ *
+ * @access public
+ * @return array global variables
+ */
+ function getGlobalVars()
+ {
+ return $this->_globals;
+ }
+
+ /**
+ * checks wether a template exists
+ *
+ * @access public
+ * @param string name of the template
+ * @return boolean true, if the template exists, false otherwise
+ */
+ function exists( $name )
+ {
+ return in_array( strtolower( $name ), $this->_templateList );
+ }
+
+ /**
+ * enable a template cache
+ *
+ * A template cache will improve performace, as the templates
+ * do not have to be read on each request.
+ *
+ * @access public
+ * @param string name of the template cache
+ * @param array parameters for the template cache
+ * @return boolean true on success, patError otherwise
+ */
+ function useTemplateCache( $cache, $params = array() )
+ {
+ if ( !is_object( $cache ) )
+ {
+ $cache = &$this->loadModule( 'TemplateCache', $cache, $params );
+ }
+ if ( patErrorManager::isError( $cache ) )
+ return $cache;
+
+ $this->_tmplCache = &$cache;
+ return true;
+ }
+
+ /**
+ * enable an output filter
+ *
+ * Output filters are used to modify the template
+ * result before it is sent to the browser.
+ *
+ * They are applied, when displayParsedTemplate() is called.
+ *
+ * @access public
+ * @param string name of the output filter
+ * @param array parameters for the output filter
+ * @return boolean true on success, patError otherwise
+ */
+ function applyOutputFilter( $filter, $params = array() )
+ {
+ if ( !is_object( $filter ) )
+ {
+ $filter = &$this->loadModule( 'OutputFilter', $filter, $params );
+ }
+ if ( patErrorManager::isError( $filter ) )
+ return $filter;
+
+ $this->_outputFilters[] = &$filter;
+ return true;
+ }
+
+ /**
+ * enable an input filter
+ *
+ * input filters are used to modify the template
+ * stream before it is split into smaller templates-
+ *
+ * @access public
+ * @param string name of the input filter
+ * @param array parameters for the input filter
+ * @return boolean true on success, patError otherwise
+ */
+ function applyInputFilter( $filter, $params = array() )
+ {
+ if ( !is_object( $filter ) )
+ {
+ $filter = &$this->loadModule( 'InputFilter', $filter, $params );
+ }
+ if ( patErrorManager::isError( $filter ) )
+ return $filter;
+
+ $this->_inputFilters[] = &$filter;
+ return true;
+ }
+
+ /**
+ * open a file and parse for patTemplate tags
+ *
+ * @access public
+ * @param name of the file
+ * @return true, if the template could be parsed
+ * @deprecated Use patTemplate::readTemplatesFromInput() instead, as the method name is misleading
+ * @see readTemplatesFromInput()
+ */
+ function readTemplatesFromFile( $filename )
+ {
+ return $this->readTemplatesFromInput( $filename, 'File' );
+ }
+
+ /**
+ * open any input and parse for patTemplate tags
+ *
+ * @access public
+ * @param string name of the input (filename, shm segment, etc.)
+ * @param string driver that is used as reader, you may also pass a Reader object
+ * @param array additional options that will only be used for this template
+ * @param string name of the template that should be used as a container, should not be used by public
+ * calls.
+ * @return boolean true, if the template could be parsed, false otherwise
+ */
+ function readTemplatesFromInput( $input, $reader = 'File', $options = null, $parseInto = null )
+ {
+ if ($input === '') {
+ return patErrorManager::raiseError(PATTEMPLATE_ERROR_NO_INPUT, 'No input to read has been passed.');
+ }
+
+ if ( is_array( $options ) )
+ $options = array_merge( $this->_options, $options );
+ else
+ $options = $this->_options;
+
+ if ( !is_null( $parseInto ) )
+ $parseInto = strtolower( $parseInto );
+
+ $templates = false;
+ if ( $this->_tmplCache !== null )
+ {
+ /**
+ * get the unique cache key
+ */
+ $key = $this->_tmplCache->getKey( $input, $options );
+
+ $templates = $this->_loadTemplatesFromCache( $input, $reader, $options, $key );
+
+ /**
+ * check for error returned from cache
+ */
+ if ( patErrorManager::isError( $templates ) )
+ return $templates;
+ }
+
+ /**
+ * templates have not been loaded from cache
+ */
+ if ( $templates === false )
+ {
+ if ( !is_object( $reader ) )
+ {
+ $reader = &$this->loadModule( 'Reader', $reader );
+ if ( patErrorManager::isError( $reader ) )
+ return $reader;
+ }
+ $reader->setOptions( $options );
+
+ /**
+ * set the root attributes
+ */
+ if ( !is_null( $parseInto ) )
+ {
+ $attributes = $this->getAttributes( $parseInto );
+ if ( !patErrorManager::isError( $attributes ) )
+ {
+ $reader->setRootAttributes( $attributes );
+ }
+ }
+
+ $templates = $reader->readTemplates( $input );
+
+ /**
+ * check for error returned from reader
+ */
+ if ( patErrorManager::isError( $templates ) )
+ return $templates;
+
+ /**
+ * store the
+ */
+ if ( $this->_tmplCache !== null )
+ {
+ $this->_tmplCache->write( $key, $templates );
+ }
+ }
+
+ /**
+ * traverse all templates
+ */
+ foreach ( $templates as $name => $spec )
+ {
+ /**
+ * root template
+ */
+ if ( $name == '__ptroot' )
+ {
+ if ( $parseInto === false )
+ {
+ continue;
+ }
+ if ( !in_array( $parseInto, $this->_templateList ) )
+ continue;
+
+ $spec['loaded'] = true;
+ $spec['attributes'] = $this->_templates[$parseInto]['attributes'];
+ $name = $parseInto;
+ }
+ else
+ {
+ /**
+ * store the name
+ */
+ array_push( $this->_templateList, $name );
+ }
+
+ /**
+ * if this is the first template that has been loaded
+ * set it as the root template
+ */
+ if ( $this->_root === null && is_null( $parseInto ) && isset( $spec['isRoot'] ) && $spec['isRoot'] == true )
+ {
+ $this->_root = $name;
+ }
+
+ /**
+ * set some default values
+ */
+ $spec['iteration'] = 0;
+ $spec['lastMode'] = 'w';
+ $spec['result'] = '';
+ $spec['modifyVars'] = array();
+ $spec['copyVars'] = array();
+ $spec['defaultVars'] = array();
+
+ /**
+ * store the template
+ */
+ $this->_templates[$name] = $spec;
+
+ $this->prepareTemplate( $name );
+
+ /**
+ * store the default values of the variables
+ */
+ foreach ( $spec['varspecs'] as $varname => $varspec )
+ {
+ if ( isset( $varspec['modifier'] ) )
+ {
+ $this->_templates[$name]['modifyVars'][$varname] = $varspec['modifier'];
+ }
+
+ if ( isset( $varspec['copyfrom'] ) )
+ {
+ $this->_templates[$name]['copyVars'][$varname] = $varspec['copyfrom'];
+ }
+
+ if ( !isset( $varspec['default'] ) )
+ continue;
+
+ $this->_templates[$name]['defaultVars'][$varname] = $varspec['default'];
+
+ if ( !is_null( $this->getVar( $name, $varname ) ) )
+ continue;
+
+ $this->addVar( $name, $varname, $varspec['default'] );
+ }
+
+ unset($this->_templates[$name]['varspecs']);
+
+ /**
+ * autoload the template
+ *
+ * Some error management is needed here...
+ */
+ if ( isset( $this->_templates[$name]['attributes']['src'] ) && $this->_templates[$name]['attributes']['autoload'] == 'on' )
+ {
+ if ( $this->_templates[$name]['loaded'] !== true )
+ {
+ if ( $this->_templates[$name]['attributes']['parse'] == 'on' )
+ {
+ $this->readTemplatesFromInput( $this->_templates[$name]['attributes']['src'], $this->_templates[$name]['attributes']['reader'], $options, $name );
+ }
+ else
+ {
+ $this->loadTemplateFromInput( $this->_templates[$name]['attributes']['src'], $this->_templates[$name]['attributes']['reader'], null, $name );
+ }
+ $this->_templates[$name]['loaded'] = true;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * load from template cache
+ *
+ * @access private
+ * @param string name of the input (filename, shm segment, etc.)
+ * @param string driver that is used as reader, you may also pass a Reader object
+ * @param array options for the reader
+ * @param string cache key
+ * @return array|boolean either an array containing the templates, or false
+ */
+ function _loadTemplatesFromCache( $input, &$reader, $options, $key )
+ {
+ if ( is_object( $reader ) )
+ $statName = $reader->getName();
+ else
+ $statName = $reader;
+
+ $stat = &$this->loadModule( 'Stat', $statName );
+ $stat->setOptions( $options );
+
+ /**
+ * get modification time
+ */
+ $modTime = $stat->getModificationTime( $input );
+ $templates = $this->_tmplCache->load( $key, $modTime );
+
+ return $templates;
+ }
+
+ /**
+ * open any input and load content into template
+ *
+ * @access public
+ * @param string name of the input (filename, shm segment, etc.)
+ * @param string driver that is used as reader
+ * @param string name of the template that should be used as a container,
+ * @return boolean true, if the template could be parsed, false otherwise
+ */
+ function loadTemplateFromInput( $input, $reader = 'File', $options = null, $parseInto = false )
+ {
+ if ( is_array( $options ) )
+ $options = array_merge( $this->_options, $options );
+ else
+ $options = $this->_options;
+
+ if ( !is_null( $parseInto ) )
+ $parseInto = strtolower( $parseInto );
+
+ $reader = &$this->loadModule( 'Reader', $reader );
+ if ( patErrorManager::isError( $reader ) )
+ {
+ return $reader;
+ }
+ $reader->setOptions($options);
+
+ $result = $reader->loadTemplate( $input );
+
+ if ( patErrorManager::isError( $result ) )
+ {
+ return $result;
+ }
+
+ $this->_templates[$parseInto]['content'] .= $result;
+ $this->_templates[$parseInto]['loaded'] = true;
+ return true;
+ }
+
+ /**
+ * load a template that had autoload="off"
+ *
+ * This is needed, if you change the source of a template and want to
+ * load it, after changing the attribute.
+ *
+ * @access public
+ * @param string template name
+ * @return boolean true, if template could be loaded
+ */
+ function loadTemplate( $template )
+ {
+ $template = strtolower( $template );
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+
+ if ( $this->_templates[$template]['loaded'] === true )
+ return true;
+
+ if ( $this->_templates[$template]['attributes']['parse'] == 'on' )
+ {
+ return $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
+ }
+ else
+ {
+ return $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
+ }
+ }
+
+ /**
+ * loads a patTemplate module
+ *
+ * Modules are located in the patTemplate folder and include:
+ * - Readers
+ * - Caches
+ * - Variable Modifiers
+ * - Filters
+ * - Functions
+ * - Stats
+ *
+ * @access public
+ * @param string moduleType (Reader|TemplateCache|Modifier|OutputFilter|InputFilter)
+ * @param string moduleName
+ * @param array parameters for the module
+ * @return object
+ */
+ function &loadModule( $moduleType, $moduleName, $params = array() )
+ {
+ if ( !isset( $this->_modules[$moduleType] ) )
+ $this->_modules[$moduleType] = array();
+
+ $sig = md5( $moduleName . serialize( $params ) );
+
+ if ( isset( $this->_modules[$moduleType][$sig] ) )
+ return $this->_modules[$moduleType][$sig];
+
+ if ( !class_exists( 'patTemplate_Module' ) )
+ {
+ $file = sprintf( "%s/Module.php", $this->getIncludePath() );
+ if ( !@include_once $file )
+ return patErrorManager::raiseError( PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND, 'Could not load module base class.' );
+ }
+
+ $baseClass = 'patTemplate_' . $moduleType;
+ if ( !class_exists( $baseClass ) )
+ {
+ $baseFile = sprintf( "%s/%s.php", $this->getIncludePath(), $moduleType );
+ if ( !@include_once $baseFile )
+ return patErrorManager::raiseError( PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND, "Could not load base class for $moduleType ($baseFile)." );
+ }
+
+ $moduleClass = 'patTemplate_' . $moduleType . '_' .$moduleName;
+ if ( !class_exists( $moduleClass ) )
+ {
+ if ( isset( $this->_moduleDirs[$moduleType] ) )
+ $dirs = $this->_moduleDirs[$moduleType];
+ else
+ $dirs = array();
+ array_push( $dirs, $this->getIncludePath() .'/'. $moduleType );
+
+ foreach ( $dirs as $dir )
+ {
+ $moduleFile = sprintf( "%s/%s.php", $dir, str_replace( '_', '/', $moduleName ) );
+ if ( @include_once $moduleFile )
+ break;
+ return patErrorManager::raiseError( PATTEMPLATE_ERROR_MODULE_NOT_FOUND, "Could not load module $moduleClass ($moduleFile)." );
+ }
+ }
+
+ if ( !class_exists( $moduleClass ) )
+ {
+ return patErrorManager::raiseError( PATTEMPLATE_ERROR_MODULE_NOT_FOUND, "Module file $moduleFile does not contain class $moduleClass." );
+ }
+
+ $this->_modules[$moduleType][$sig] = &new $moduleClass;
+ if ( method_exists( $this->_modules[$moduleType][$sig], 'setTemplateReference' ) )
+ {
+ $this->_modules[$moduleType][$sig]->setTemplateReference( $this );
+ }
+
+ $this->_modules[$moduleType][$sig]->setParams( $params );
+
+ return $this->_modules[$moduleType][$sig];
+ }
+
+ /**
+ * checks whether a module exists.
+ *
+ * Modules are located in the patTemplate folder and include:
+ * - Readers
+ * - Caches
+ * - Variable Modifiers
+ * - Filters
+ * - Functions
+ * - Stats
+ *
+ * @access public
+ * @param string moduleType (Reader|TemplateCache|Modifier|OutputFilter|InputFilter)
+ * @param string moduleName
+ * @return boolean
+ */
+ function moduleExists( $moduleType, $moduleName )
+ {
+ if ( isset( $this->_moduleDirs[$moduleType] ) )
+ $dirs = $this->_moduleDirs[$moduleType];
+ else
+ $dirs = array();
+ array_push( $dirs, $this->getIncludePath() .'/'. $moduleType );
+
+ foreach ( $dirs as $dir )
+ {
+ $moduleFile = sprintf( "%s/%s.php", $dir, str_replace( '_', '/', $moduleName ) );
+ if ( !file_exists( $moduleFile ) )
+ continue;
+ if ( !is_readable( $moduleFile ) )
+ continue;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * parses a template
+ *
+ * Parses a template and stores the parsed content.
+ * mode can be "w" for write (delete already parsed content) or "a" for append (appends the
+ * new parsed content to the already parsed content)
+ *
+ * @access public
+ * @param string name of the template
+ * @param string mode for the parsing
+ */
+ function parseTemplate( $template, $mode = 'w' )
+ {
+ $template = strtolower( $template );
+
+ if ( !isset( $this->_templates[$template] ) )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$template' does not exist."
+ );
+ }
+
+ /**
+ * template is not visible
+ */
+ if ( $this->_templates[$template]['attributes']['visibility'] == 'hidden' )
+ {
+ $this->_templates[$template]['result'] = '';
+ $this->_templates[$template]['parsed'] = true;
+ return true;
+ }
+
+ /**
+ * check, if the template has been loaded
+ * and load it if necessary.
+ */
+ if ( $this->_templates[$template]['loaded'] !== true )
+ {
+ if ( $this->_templates[$template]['attributes']['parse'] == 'on' )
+ {
+ $result = $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
+ }
+ else
+ {
+ $result = $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
+ }
+ if ( patErrorManager::isError( $result ) )
+ {
+ return $result;
+ }
+ }
+
+ /**
+ * check for autoclear
+ */
+ if (
+ isset( $this->_templates[$template]['attributes']['autoclear'] ) &&
+ $this->_templates[$template]['attributes']['autoclear'] == 'yes' &&
+ $mode === 'w' &&
+ $this->_templates[$template]['lastMode'] != 'a'
+ )
+ {
+ $this->_templates[$template]['parsed'] = false;
+ }
+
+ /**
+ * template has been parsed and mode is not 'append'
+ */
+ if ( $this->_templates[$template]['parsed'] === true && $mode === 'w' )
+ {
+ return true;
+ }
+
+ $this->_templates[$template]['lastMode'] = $mode;
+
+ $this->_initTemplate( $template );
+
+ if ( !isset( $this->_vars[$template]['rows'] ) )
+ $this->_vars[$template]['rows'] = array();
+ $loop = count( $this->_vars[$template]['rows'] );
+
+ /**
+ * loop at least one times
+ */
+ if ( $loop < 1 )
+ $loop = 1;
+
+ if ( isset( $this->_templates[$template]['attributes']['maxloop'] ) )
+ {
+ $loop = ceil( $loop / $this->_templates[$template]['attributes']['maxloop'] ) * $this->_templates[$template]['attributes']['maxloop'];
+ }
+
+ $this->_templates[$template]['loop'] = max( $this->_templates[$template]['attributes']['loop'], $loop );
+
+ $start = 0;
+ if ( isset( $this->_templates[$template]['attributes']['limit'] ) )
+ {
+ $p = strpos( $this->_templates[$template]['attributes']['limit'], ',' );
+ if ( $p === false )
+ {
+ $this->_templates[$template]['loop'] = min( $this->_templates[$template]['loop'], $this->_templates[$template]['attributes']['limit'] );
+ $start = 0;
+ }
+ else
+ {
+ $start = substr( $this->_templates[$template]['attributes']['limit'], 0, $p );
+ $end = substr( $this->_templates[$template]['attributes']['limit'], $p+1 )+$start;
+
+ $this->_templates[$template]['loop'] = min( $this->_templates[$template]['loop'], $end );
+ }
+ }
+
+ /**
+ * template should be cleared before parsing
+ */
+ if ( $mode == 'w' )
+ {
+ $this->_templates[$template]['result'] = '';
+ $this->_templates[$template]['iteration'] = $start;
+ }
+
+ $loopCount = 0;
+ for ( $i = $start; $i < $this->_templates[$template]['loop']; $i++ )
+ {
+ $finished = false;
+
+ unset( $this->_templates[$template]['vars'] );
+
+ /**
+ * fetch the variables
+ */
+ $this->_fetchVariables( $template );
+
+ /**
+ * fetch the template
+ */
+ $result = $this->_fetchTemplate( $template );
+
+ if ( $result === false )
+ {
+ $this->_templates[$template]['iteration']++;
+ continue;
+ }
+
+ /**
+ * parse
+ */
+ $this->_parseVariables( $template );
+ $this->_parseDependencies( $template );
+
+ /**
+ * store result
+ */
+ $this->_templates[$template]['result'] .= $this->_templates[$template]['work'];
+
+ $this->_templates[$template]['iteration']++;
+
+ ++$loopCount;
+
+ /**
+ * check for maximum loops
+ */
+ if ( isset( $this->_templates[$template]['attributes']['maxloop'] ) )
+ {
+ if ( $loopCount == $this->_templates[$template]['attributes']['maxloop'] && $i < ( $loop-1 ) )
+ {
+ $loopCount = 0;
+ $finished = true;
+ $this->_templates[$template]['parsed'] = true;
+ $this->parseTemplate( $this->_templates[$template]['attributes']['parent'], 'a' );
+ $this->_templates[$template]['parsed'] = false;
+ $this->_templates[$template]['result'] = '';
+ }
+ }
+ }
+
+ if ( !$finished && isset( $this->_templates[$template]['attributes']['maxloop'] ) )
+ {
+ $this->_templates[$template]['parsed'] = true;
+ $this->parseTemplate( $this->_templates[$template]['attributes']['parent'], 'a', false );
+ $this->_templates[$template]['parsed'] = false;
+ $this->_templates[$template]['result'] = '';
+ $this->_templates[$this->_templates[$template]['attributes']['parent']]['work'] = '';
+ }
+
+ $this->_parseGlobals($template);
+
+ $this->_handleUnusedVars( $template );
+
+ $this->_templates[$template]['parsed'] = true;
+
+ if ( isset( $this->_templates[$template]['attributes']['autoclear'] ) && $this->_templates[$template]['attributes']['autoclear'] == 'yes' )
+ {
+ $this->_vars[$template] = array(
+ 'scalar' => array(),
+ 'rows' => array()
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Initialize a template
+ *
+ * This method checks the variable specifications and
+ * copys variables from other templates.
+ *
+ * @access private
+ * @param string name of the template
+ * @return boolean true on success
+ */
+ function _initTemplate( $template )
+ {
+ foreach ( $this->_templates[$template]['copyVars'] as $dest => $src )
+ {
+ /**
+ * copy from the same template
+ */
+ if ( !is_array( $src ) )
+ {
+ $srcTemplate = $template;
+ $srcVar = $src;
+ }
+ else
+ {
+ $srcTemplate = $src[0];
+ $srcVar = $src[1];
+ }
+
+ $copied = false;
+
+ /**
+ * copy from another template
+ */
+ if ( isset( $this->_vars[$srcTemplate] ) )
+ {
+ if ( isset( $this->_vars[$srcTemplate]['scalar'][$srcVar] ) )
+ {
+ $this->_vars[$template]['scalar'][$dest] = $this->_vars[$srcTemplate]['scalar'][$srcVar];
+ continue;
+ }
+
+ $rows = count( $this->_vars[$srcTemplate]['rows'] );
+
+ for ( $i = 0; $i < $rows; $i++ )
+ {
+ if ( !isset( $this->_vars[$srcTemplate]['rows'][$i][$srcVar] ) )
+ continue;
+ if ( !isset( $this->_vars[$template]['rows'][$i] ) )
+ $this->_vars[$template]['rows'][$i] = array();
+ $this->_vars[$template]['rows'][$i][$dest] = $this->_vars[$srcTemplate]['rows'][$i][$srcVar];
+ $copied = true;
+ }
+ }
+ if ( !$copied && isset( $this->_globals[$srcVar] ))
+ {
+ $this->_vars[$template]['scalar'][$dest] = $this->_globals[$srcVar];
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * parse all variables in a template
+ *
+ * @access private
+ * @param string
+ */
+ function _parseVariables( $template )
+ {
+ /**
+ * modify variables before parsing
+ */
+ $this->_applyModifers($template, $this->_templates[$template]['vars']);
+
+ foreach ( $this->_templates[$template]['vars'] as $key => $value )
+ {
+ if ( is_array( $value ) )
+ {
+ if ( count( $this->_templates[$template]['currentDependencies'] ) == 1 )
+ {
+ $child = $this->_templates[$template]['currentDependencies'][0];
+ }
+ else
+ {
+ if ( isset( $this->_templates[$template]['attributes']['child'] ) )
+ $child = $this->_templates[$template]['attributes']['child'];
+ else
+ continue;
+ }
+
+ $this->setAttribute( $child, 'autoclear', 'yes' );
+ $this->addVar( $child, $key, $value );
+ continue;
+ }
+
+ $var = $this->_startTag.$key.$this->_endTag;
+ $this->_templates[$template]['work'] = @str_replace( $var, $value, $this->_templates[$template]['work'] );
+ }
+ return true;
+ }
+
+ /**
+ * parse global variables in the template
+ *
+ * @access private
+ * @param string name of the template
+ * @return boolean
+ */
+ function _parseGlobals($template)
+ {
+ $globalVars = $this->_globals;
+ $this->_applyModifers($template, $globalVars);
+
+ foreach ( $globalVars as $key => $value )
+ {
+ if ( is_array( $value ) )
+ {
+ continue;
+ }
+
+ $var = $this->_startTag.$key.$this->_endTag;
+ $this->_templates[$template]['result'] = str_replace( $var, $value, $this->_templates[$template]['result'] );
+ }
+ return true;
+ }
+
+ /**
+ * apply variable modifiers
+ *
+ * The variables will be passed by reference.
+ *
+ * @access private
+ * @param string name of the template (use modifiers from this template)
+ * @param array variables to which the modifiers should be applied
+ * @return boolean
+ */
+ function _applyModifers($template, &$vars)
+ {
+ foreach ( $this->_templates[$template]['modifyVars'] as $varname => $modifier )
+ {
+ if ( !isset( $vars[$varname] ) )
+ continue;
+
+ if ( ( $modifier['type'] === 'php' || $modifier['type'] === 'auto' ) && is_callable( $modifier['mod'] ) )
+ {
+ $vars[$varname] = call_user_func( $modifier['mod'], $vars[$varname] );
+ continue;
+ }
+
+ if ( $modifier['type'] === 'php' )
+ continue;
+
+ $mod = &$this->loadModule( 'Modifier', ucfirst( $modifier['mod'] ) );
+ $vars[$varname] = $mod->modify( $vars[$varname], $modifier['params'] );
+ }
+ return true;
+ }
+
+ /**
+ * parse all dependencies in a template
+ *
+ * @access private
+ * @param string
+ */
+ function _parseDependencies( $template )
+ {
+ $countDep = count( $this->_templates[$template]['currentDependencies'] );
+ for ( $i = 0; $i < $countDep; $i++ )
+ {
+ $depTemplate = $this->_templates[$template]['currentDependencies'][$i];
+ $this->parseTemplate( $depTemplate );
+ $var = $this->_startTag.'TMPL:'.strtoupper( $depTemplate) .$this->_endTag;
+ $this->_templates[$template]['work'] = str_replace( $var, $this->_templates[$depTemplate]['result'], $this->_templates[$template]['work'] );
+ }
+ return true;
+ }
+
+ /**
+ * fetch plain template
+ *
+ * The template content will be stored in the template
+ * configuration so it can be used by other
+ * methods.
+ *
+ * @access private
+ * @param string template name
+ * @return boolean
+ */
+ function _fetchTemplate( $template )
+ {
+ switch( $this->_templates[$template]['attributes']['type'] )
+ {
+ /**
+ * condition template
+ */
+ case 'condition':
+ $value = $this->_getConditionValue( $template, $this->_templates[$template]['attributes']['conditionvar'] );
+ if ( $value === false )
+ {
+ $this->_templates[$template]['work'] = '';
+ $this->_templates[$template]['currentDependencies'] = array();
+ }
+ else
+ {
+ $this->_templates[$template]['work'] = $this->_templates[$template]['subtemplates'][$value]['data'];
+ $this->_templates[$template]['currentDependencies'] = $this->_templates[$template]['subtemplates'][$value]['dependencies'];
+ }
+ break;
+
+ /**
+ * condition template
+ */
+ case 'simplecondition':
+ foreach ( $this->_templates[$template]['attributes']['requiredvars'] as $var )
+ {
+ if ( $var[0] !== $template )
+ $this->_fetchVariables($var[0]);
+
+ if ( isset( $this->_templates[$var[0]]['vars'][$var[1]] ) && strlen( $this->_templates[$var[0]]['vars'][$var[1]] ) > 0 )
+ continue;
+ if (isset($this->_templates[$template]['attributes']['useglobals']))
+ {
+ if (isset($this->_globals[$var[1]]) && strlen($this->_globals[$var[1]]) > 1)
+ continue;
+ }
+
+ $this->_templates[$template]['work'] = '';
+ $this->_templates[$template]['currentDependencies'] = array();
+ break 2;
+ }
+ $this->_templates[$template]['work'] = $this->_templates[$template]['content'];
+ $this->_templates[$template]['currentDependencies'] = $this->_templates[$template]['dependencies'];
+ break;
+
+ /**
+ * modulo template
+ */
+ case 'modulo':
+ // check for empty template
+ if ($this->_hasVariables($template)) {
+ $value = ( $this->_templates[$template]['iteration'] + 1 ) % $this->_templates[$template]['attributes']['modulo'];
+ } else {
+ $value = '__empty';
+ }
+
+ $value = $this->_getConditionValue( $template, $value, false );
+ if ( $value === false )
+ {
+ $this->_templates[$template]['work'] = '';
+ $this->_templates[$template]['currentDependencies'] = array();
+ }
+ else
+ {
+ $this->_templates[$template]['work'] = $this->_templates[$template]['subtemplates'][$value]['data'];
+ $this->_templates[$template]['currentDependencies'] = $this->_templates[$template]['subtemplates'][$value]['dependencies'];
+ }
+ break;
+
+ /**
+ * standard template
+ */
+ default:
+ $this->_templates[$template]['work'] = $this->_templates[$template]['content'];
+ $this->_templates[$template]['currentDependencies'] = $this->_templates[$template]['dependencies'];
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * check, whether a template contains variables
+ *
+ * @access private
+ * @param string template name
+ * @return boolean
+ */
+ function _hasVariables($template)
+ {
+ if (!empty($this->_vars[$template]['scalar'])) {
+ return true;
+ }
+ if (isset($this->_vars[$template]['rows'][$this->_templates[$template]['iteration']])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * fetch the value of a condition variable
+ *
+ * _fetchVariables() has to be called before this
+ * method is being called.
+ *
+ * @access private
+ * @param string template name
+ * @param string condition value
+ * @param boolean flag that indicates whether value is the name of the variable that should be resolved
+ *
+ * @todo split this method into smaller check methods that will be called according to
+ * a priority list
+ */
+ function _getConditionValue( $template, $value, $isVar = true )
+ {
+ if ( $isVar === true )
+ {
+ if ( isset( $this->_templates[$template]['attributes']['conditiontmpl'] ) )
+ {
+ $_template = $this->_templates[$template]['attributes']['conditiontmpl'];
+ $this->_fetchVariables( $_template );
+ }
+ else
+ {
+ $_template = $template;
+ }
+
+ /**
+ * get the value from the template variables
+ */
+ if ( !isset( $this->_templates[$_template]['vars'][$value] ) || strlen( $this->_templates[$_template]['vars'][$value] ) === 0 )
+ {
+ if ( $this->_templates[$template]['attributes']['useglobals'] == 'yes' || $this->_templates[$template]['attributes']['useglobals'] == 'useglobals' )
+ {
+ if ( isset( $this->_globals[$value] ) && strlen( $this->_globals[$value] ) > 0 )
+ {
+ $value = $this->_globals[$value];
+ }
+ else
+ {
+ $value = '__empty';
+ }
+ }
+ else
+ {
+ $value = '__empty';
+ }
+ }
+ else
+ {
+ $value = $this->_templates[$_template]['vars'][$value];
+ }
+ }
+ else
+ {
+ $_template = $template;
+ }
+
+ /**
+ * is __first?
+ */
+ if ( $this->_templates[$_template]['iteration'] == 0 )
+ {
+ if ( isset( $this->_templates[$template]['subtemplates']['__first'] ) )
+ {
+ return '__first';
+ }
+ }
+
+ /**
+ * is __last?
+ */
+ $max = $this->_templates[$_template]['loop'] - 1;
+ if ( $this->_templates[$_template]['iteration'] == $max )
+ {
+ if ( isset( $this->_templates[$template]['subtemplates']['__last'] ) )
+ {
+ return '__last';
+ }
+ }
+
+ /**
+ * found an exact match
+ */
+ if ( isset( $this->_templates[$template]['subtemplates'][$value] ) )
+ {
+ return $value;
+ }
+
+ /**
+ * is __default?
+ */
+ if ( isset( $this->_templates[$template]['subtemplates']['__default'] ) )
+ {
+ return '__default';
+ }
+
+ return false;
+ }
+
+ /**
+ * fetch variables for a template
+ *
+ * The variables will be stored in the template
+ * configuration so they can be used by other
+ * methods.
+ *
+ * @access private
+ * @param string template name
+ * @return boolean
+ */
+ function _fetchVariables( $template )
+ {
+ /**
+ * variables already have been fetched
+ */
+ if ( isset( $this->_templates[$template]['vars'] ) )
+ {
+ return true;
+ }
+
+ $iteration = $this->_templates[$template]['iteration'];
+
+ if ( isset( $this->_templates[$template]['attributes']['varscope'] ) )
+ {
+ $scopeTemplate = $this->_templates[$template]['attributes']['varscope'];
+ if ($this->exists($scopeTemplate)) {
+ $this->_fetchVariables( $scopeTemplate );
+ $vars = $this->_templates[$scopeTemplate]['vars'];
+ } else {
+ patErrorManager::raiseWarning(PATTEMPLATE_WARNING_NO_TEMPLATE, 'Template \''.$scopeTemplate.'\' does not exist, referenced in varscope attribute of template \''.$template.'\'');
+ $vars = array();
+ }
+ }
+ else
+ {
+ $vars = array();
+ }
+
+ /**
+ * get the scalar variables
+ */
+ if ( isset( $this->_vars[$template] ) && isset( $this->_vars[$template]['scalar'] ) )
+ {
+ $vars = array_merge( $vars, $this->_vars[$template]['scalar'] );
+ }
+
+ /**
+ * get the row variables
+ */
+ if ( isset( $this->_vars[$template]['rows'][$iteration] ) )
+ {
+ $vars = array_merge( $vars, $this->_vars[$template]['rows'][$iteration] );
+ }
+
+ /**
+ * add some system variables
+ */
+ $currentRow = $iteration + 1;
+ $vars['PAT_ROW_VAR'] = $currentRow;
+
+ if ( $this->_templates[$template]['attributes']['type'] == 'modulo' )
+ {
+ $vars['PAT_MODULO_REP'] = ceil( $currentRow / $this->_templates[$template]['attributes']['modulo'] );
+ $vars['PAT_MODULO'] = ( $this->_templates[$template]['iteration'] + 1 ) % $this->_templates[$template]['attributes']['modulo'];
+ }
+
+ if ( $this->_templates[$template]['attributes']['addsystemvars'] !== false )
+ {
+ $vars['PATTEMPLATE_VERSION'] = $this->_systemVars['appVersion'];
+ $vars['PAT_LOOPS'] = $this->_templates[$template]['loop'];
+
+ switch ($this->_templates[$template]['attributes']['addsystemvars'])
+ {
+ case 'boolean':
+ $trueValue = 'true';
+ $falseValue = 'false';
+ break;
+ case 'integer':
+ $trueValue = '1';
+ $falseValue = '0';
+ break;
+ default:
+ $trueValue = $this->_templates[$template]['attributes']['addsystemvars'];
+ $falseValue = '';
+ break;
+ }
+
+ $vars['PAT_IS_ODD'] = ( $currentRow % 2 == 1 ) ? $trueValue : $falseValue;
+ $vars['PAT_IS_EVEN'] = ( $currentRow % 2 == 0 ) ? $trueValue : $falseValue;
+ $vars['PAT_IS_FIRST'] = ( $currentRow == 1 ) ? $trueValue : $falseValue;
+ $vars['PAT_IS_LAST'] = ( $currentRow == $this->_templates[$template]['loop'] ) ? $trueValue : $falseValue;
+ }
+
+ $this->_templates[$template]['vars'] = $vars;
+ return true;
+ }
+
+ /**
+ * handle all unused variables in a template
+ *
+ * This is influenced by the 'unusedvars' attribute of the
+ * template
+ *
+ * @access private
+ * @param string
+ */
+ function _handleUnusedVars( $template )
+ {
+ $regexp = '/('.$this->_startTag.'[^a-z]+'.$this->_endTag.')/U';
+
+ switch( $this->_templates[$template]['attributes']['unusedvars'] )
+ {
+ case 'comment':
+ $this->_templates[$template]['result'] = preg_replace( $regexp, '', $this->_templates[$template]['result'] );
+ break;
+ case 'strip':
+ $this->_templates[$template]['result'] = preg_replace( $regexp, '', $this->_templates[$template]['result'] );
+ break;
+ case 'nbsp':
+ $this->_templates[$template]['result'] = preg_replace( $regexp, ' ', $this->_templates[$template]['result'] );
+ break;
+ case 'ignore':
+ break;
+ default:
+ $this->_templates[$template]['result'] = preg_replace( $regexp, $this->_templates[$template]['attributes']['unusedvars'], $this->_templates[$template]['result'] );
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * returns a parsed Template
+ *
+ * If the template already has been parsed, it just returns the parsed template.
+ * If the template has not been loaded, it will be loaded.
+ *
+ * @access public
+ * @param string name of the template
+ * @return string Content of the parsed template
+ * @see displayParsedTemplate()
+ */
+ function getParsedTemplate( $name = null )
+ {
+ if ( is_null( $name ) )
+ $name = $this->_root;
+
+ $name = strtolower( $name );
+
+ $result = $this->parseTemplate( $name );
+
+ if ( patErrorManager::isError( $result ) )
+ return $result;
+
+ return $this->_templates[$name]['result'];
+ }
+
+ /**
+ * displays a parsed Template
+ *
+ * If the template has not been loaded, it will be loaded.
+ *
+ * @see getParsedTemplate()
+ * @param string name of the template
+ * @return boolean true on success
+ * @access public
+ */
+ function displayParsedTemplate( $name = null )
+ {
+ $result = $this->getParsedTemplate( $name );
+
+ /**
+ * error happened
+ */
+ if ( patErrorManager::isError( $result ) )
+ return $result;
+
+ $cnt = count( $this->_outputFilters );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $result = $this->_outputFilters[$i]->apply( $result );
+ }
+
+ echo $result;
+ return true;
+ }
+
+ /**
+ * parse a template and push the result into a variable of any other
+ * template
+ *
+ * If the template already has been parsed, it will just be pushed into the variable.
+ * If the template has not been loaded, it will be loaded.
+ *
+ * @access public
+ * @param string name of the template
+ * @return string Content of the parsed template
+ * @param boolean if set to true, the value will be appended to the value already stored.
+ * @see getParsedTemplate()
+ * @see addVar()
+ */
+ function parseIntoVar( $srcTmpl, $destTmpl, $var, $append = false )
+ {
+ $srcTmpl = strtolower( $srcTmpl );
+ $destTmpl = strtolower( $destTmpl );
+ $var = strtoupper($var);
+
+ $result = $this->parseTemplate( $srcTmpl );
+
+ if ( patErrorManager::isError( $result ) )
+ return $result;
+
+ if ( $append !== true || !isset( $this->_vars[$destTmpl]['scalar'][$var] ) )
+ $this->_vars[$destTmpl]['scalar'][$var] = '';
+
+ $this->_vars[$destTmpl]['scalar'][$var] .= $this->_templates[$srcTmpl]['result'];
+
+ return true;
+ }
+
+ /**
+ * clears a parsed Template
+ *
+ * Parsed Content, variables and the loop attribute are cleared
+ *
+ * If you will not be using this template anymore, then you should
+ * call freeTemplate()
+ *
+ * @access public
+ * @param string name of the template
+ * @param boolean set this to true to clear all child templates, too
+ * @see clearAllTemplates()
+ * @see freeTemplate()
+ */
+ function clearTemplate( $name, $recursive = false )
+ {
+ $name = strtolower( $name );
+ $this->_templates[$name]['parsed'] = false;
+ $this->_templates[$name]['work'] = '';
+ $this->_templates[$name]['iteration'] = 0;
+ $this->_templates[$name]['result'] = '';
+ $this->_vars[$name] = array(
+ 'scalar' => array(),
+ 'rows' => array()
+ );
+ if (!empty($this->_templates[$name]['defaultVars'])) {
+ foreach ($this->_templates[$name]['defaultVars'] as $varname => $value) {
+ $this->addVar($name, $varname, $value);
+ }
+ }
+
+ /**
+ * clear child templates as well
+ */
+ if ( $recursive === true )
+ {
+ $deps = $this->_getDependencies( $name );
+ foreach ( $deps as $dep )
+ {
+ $this->clearTemplate( $dep, true );
+ }
+ }
+ return true;
+ }
+
+ /**
+ * clears all templates
+ *
+ * @access public
+ * @uses clearTemplate()
+ */
+ function clearAllTemplates()
+ {
+ $templates = array_keys( $this->_templates );
+ $cnt = count( $templates );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $this->clearTemplate( $templates[$i] );
+ }
+ return true;
+ }
+
+ /**
+ * frees a template
+ *
+ * All memory consumed by the template
+ * will be freed.
+ *
+ * @access public
+ * @param string name of the template
+ * @param boolean clear dependencies of the template
+ * @see freeAllTemplates()
+ */
+ function freeTemplate( $name, $recursive = false )
+ {
+ $name = strtolower( $name );
+ $key = array_search( $name, $this->_templateList );
+ if ( $key === false )
+ {
+ return patErrorManager::raiseWarning(
+ PATTEMPLATE_WARNING_NO_TEMPLATE,
+ "Template '$name' does not exist."
+ );
+ }
+
+ unset( $this->_templateList[$key] );
+ $this->_templateList = array_values( $this->_templateList );
+
+ /**
+ * free child templates as well
+ */
+ if ( $recursive === true )
+ {
+ $deps = $this->_getDependencies( $name );
+ foreach ( $deps as $dep )
+ {
+ $this->freeTemplate( $dep, true );
+ }
+ }
+
+ unset( $this->_templates[$name] );
+ unset( $this->_vars[$name] );
+
+ return true;
+ }
+
+ /**
+ * frees all templates
+ *
+ * All memory consumed by the templates
+ * will be freed.
+ *
+ * @access public
+ * @see freeTemplate()
+ */
+ function freeAllTemplates()
+ {
+ $this->_templates = array();
+ $this->_vars = array();
+ }
+
+ /**
+ * get _all_ dependencies of a template,
+ * regardless of the subtemplates
+ *
+ * @access private
+ * @param string template name
+ * @return array list of all subtemplates
+ */
+ function _getDependencies( $template )
+ {
+ $deps = array();
+ if ( isset( $this->_templates[$template]['dependencies'] ) )
+ $deps = $this->_templates[$template]['dependencies'];
+
+ if ( isset( $this->_templates[$template]['subtemplates'] ) )
+ {
+ foreach ( $this->_templates[$template]['subtemplates'] as $sub )
+ {
+ if ( isset( $sub['dependencies'] ) )
+ $deps = array_merge( $deps, $sub['dependencies'] );
+ }
+ }
+ $deps = array_unique( $deps );
+ return $deps;
+ }
+
+ /**
+ * Displays useful information about all or named templates
+ *
+ * This method breaks BC, as it now awaits an array instead of
+ * unlimited parameters.
+ *
+ * @param mixed array of templates that should be dumped, or null if you
+ * want all templates to be dumped
+ * @param string dumper
+ * @access public
+ */
+ function dump( $restrict = null, $dumper = 'Html' )
+ {
+ if ( is_string( $restrict ) )
+ $restrict = array( $restrict );
+
+ $dumper = &$this->loadModule( 'Dump', $dumper );
+
+ if ( patErrorManager::isError( $dumper ) )
+ {
+ return $dumper;
+ }
+
+ if ( is_null( $restrict ) )
+ {
+ $templates = $this->_templates;
+ $vars = $this->_vars;
+ }
+ else
+ {
+ $restrict = array_map( 'strtolower', $restrict );
+
+ $templates = array();
+ $vars = array();
+
+ foreach ( $this->_templates as $name => $spec )
+ {
+ if ( !in_array( $name, $restrict ) )
+ continue;
+ $templates[$name] = $spec;
+ $vars[$name] = $this->_vars[$name];
+ }
+ }
+
+ $dumper->displayHeader();
+ $dumper->dumpGlobals( $this->_globals );
+ $dumper->dumpTemplates( $templates, $vars );
+ $dumper->displayFooter();
+
+ return true;
+ }
+
+ /**
+ * get the include path
+ *
+ * @access public
+ */
+ function getIncludePath()
+ {
+ return PATTEMPLATE_INCLUDE_PATH;
+ }
+
+ /**
+ * apply input filters that have been set
+ *
+ * This is being called by the readers.
+ *
+ * @access public
+ * @param string template
+ * @return string filtered templeta
+ */
+ function applyInputFilters( $template )
+ {
+ $cnt = count( $this->_inputFilters );
+ for ( $i = 0; $i < $cnt; $i++ )
+ {
+ $template = $this->_inputFilters[$i]->apply( $template );
+ }
+ return $template;
+ }
+
+ /**
+ * Convert the template to its string representation.
+ *
+ * This method allows you to just echo the patTemplate
+ * object in order to display the template.
+ *
+ * Requires PHP5
+ *
+ *
+ * $tmpl = new patTemplate();
+ * $tmpl->readTemplatesFromFile( 'myfile.tmpl' );
+ * echo $tmpl;
+ *
+ *
+ * @access private
+ * @return string
+ */
+ function __toString()
+ {
+ return $this->getParsedTemplate();
+ }
+}
diff --git a/3rd_party/php/propel/contrib/pear/HTML_QuickForm_Propel/Propel.php b/3rd_party/php/propel/contrib/pear/HTML_QuickForm_Propel/Propel.php
new file mode 100644
index 000000000..6a51e84fa
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pear/HTML_QuickForm_Propel/Propel.php
@@ -0,0 +1,909 @@
+.
+ */
+
+require_once 'HTML/QuickForm.php';
+
+define('HTML_QUICKFORM_PROPEL_NO_COLUMNS', 2);
+define('HTML_QUICKFORM_PROPEL_ALL_COLUMNS', 3);
+define('HTML_QUICKFORM_PROPEL_COLUMN_MADE_VISIBLE', 4);
+define('HTML_QUICKFORM_PROPEL_COLUMN_MADE_HIDDEN', 5);
+
+/* On large foreign table resultsets propel choked */
+# ini_set('memory_limit', '50M');
+
+/**
+ *
+ * NOTE: HTML_QuickForm_Propel extends HTML_QuickForm, so all QuickForm functionality is available.
+ *
+ * A fictive example:
+ *
+ * $className = 'book';
+ * $id = '7'; // existing item
+ * $id = null; // when no id is passed, it's assumed we are creating a new item
+ *
+ * $quickForm = new HTML_QuickForm_Propel($className, $id); // optionally pass it to the constructor
+ * $quickForm->setAction('/Bookstore/Form');
+ * $quickForm->addElement('header', '', 'HTML_QuickForm_Propel');
+ * $quickForm->setId($id);
+ * $quickForm->setClassName($className);
+ * $quickForm->setTarget('_self');
+ * $quickForm->setMethod('post');
+ *
+ * // use this to override the default behaviour for
+ * // foreign table select boxes, UserPeer::UNAME will be shown in the select list.
+ * // It defaults to the first string column in the foreign table.
+ *
+ * $quickForm->joinColumn(bookPeer::PUBLISHER_ID,UserPeer::UNAME);
+ *
+ * // default to all columns shown
+ * $quickForm->setColumnMode(HTML_QUICKFORM_PROPEL_ALL_COLUMNS);
+ * $quickForm->hideColumn('PASS');
+ *
+ * // or default to no columns shown
+ * $quickForm->setColumnMode(HTML_QUICKFORM_PROPEL_NO_COLUMNS);
+ * $quickForm->showColumn('NAME'); // column name without table prefix.
+ * $quickForm->showColumn('UNAME');
+ * $quickForm->showColumn('USER_INFO');
+ * $quickForm->showColumn('EMAIL');
+ * $quickForm->showColumn('FEMAIL');
+ * $quickForm->showColumn('URL');
+ * $quickForm->showColumn('PASS');
+ *
+ * // generate the form
+ * $quickForm->build();
+ *
+ * // after the form is build, it's possible to modify the generated elements
+ * $quickForm->getElement('NAME')->setSize(10); // manually tune this element
+ *
+ * if ($quickForm->validate()) {
+ * $quickForm->freeze();
+ *
+ * // save the object we have editted
+ * $quickForm->save();
+ * } else {
+ * $quickForm->toHtml(); // or any other QuickForm render option
+ * }
+ *
+ * TODO: map all creoleTypes to usefull formelements
+ *
+ * @author Rob Halff
+ * some improvements by Zoltan Nagy (sunshine@freemail.hu)
+ * @version $Rev: 1347 $
+ * @copyright Copyright (c) 2005 Rob Halff: LGPL - See LICENCE
+ * @package propel.contrib
+ */
+
+class HTML_QuickForm_Propel extends HTML_QuickForm {
+
+ /**
+ * ID of the Propel Object.
+ * @var integer
+ * @access private
+ */
+ private $id;
+
+ /**
+ * Contains column visibility information.
+ * @var array
+ * @access private
+ */
+ private $columnVisibility = array();
+
+ /**
+ * Contains titles of columns.
+ * @var array
+ * @access private
+ */
+ private $columnTitle = array();
+
+ /**
+ * The Column visibility mode either.
+ * Possible values:
+ *
+ * HTML_QUICKFORM_PROPEL_ALL_COLUMNS
+ * HTML_QUICKFORM_PROPEL_NO_COLUMNS
+ *
+ * @var integer
+ * @access private
+ */
+ private $columnMode;
+
+ /**
+ * String containing the peerName.
+ * @var string
+ * @access private
+ */
+ private $peerName;
+
+ /**
+ * String containing the className of the propel Object.
+ * @var string
+ * @access private
+ */
+ private $className;
+
+ /**
+ * The Column objects.
+ *
+ * @var array
+ * @access private
+ */
+ private $cols;
+
+ /**
+ * The Object being operated on.
+ * @var object
+ * @access private
+ */
+ private $obj;
+
+ /**
+ * Seperator value.
+ *
+ * In case the option list will be build by multiple values
+ * This is the value these fields will be seperated with
+ *
+ * @var string
+ * @access private
+ */
+ private $seperator = ' ';
+
+ /**
+ *
+ * Not used yet.
+ *
+ * @var array
+ * @access private
+ */
+ private $joinMap = array();
+
+ /**
+ * The default QuickForm rule type to use.
+ * Either server or client
+ *
+ * @var string
+ * @access private
+ */
+ private $defaultRuleType = 'server';
+
+
+ /**
+ * This is used in the QuickForm DateElement
+ * @var string
+ * @access private
+ */
+ private $lang = 'en';
+
+ /**
+ * Rulemapping should cover all available propel rules
+ *
+ * @var array
+ * @access private
+ */
+ private $ruleMapping = array(
+ 'mask'=>'regex',
+ 'maxLength'=>'maxlength',
+ 'minLength'=>'minlength',
+ 'maxValue'=>'maxvalue',
+ 'minValue'=>'minvalue',
+ 'required'=>'required',
+ 'validValues'=>'validvalues',
+ 'unique'=>'unique'
+ );
+
+ /**
+ *
+ * CreoleType to QuickForm element mapping
+ *
+ * @var array
+ * @access private
+ */
+ private $typeMapping = array(
+ CreoleTypes::BOOLEAN =>'radio',
+ CreoleTypes::BIGINT =>'text',
+ CreoleTypes::SMALLINT =>'text',
+ CreoleTypes::TINYINT =>'text',
+ CreoleTypes::INTEGER =>'text',
+ CreoleTypes::NUMERIC =>'text',
+ CreoleTypes::DECIMAL =>'text',
+ CreoleTypes::REAL =>'text',
+ CreoleTypes::FLOAT =>'text',
+ CreoleTypes::DOUBLE =>'text',
+ CreoleTypes::CHAR =>'text',
+ CreoleTypes::VARCHAR =>'text',
+ CreoleTypes::LONGVARCHAR=>'textarea',
+ CreoleTypes::TEXT =>'textarea',
+ CreoleTypes::TIME =>'text',
+ CreoleTypes::TIMESTAMP =>'date',
+ CreoleTypes::DATE =>'date',
+ CreoleTypes::YEAR =>'text',
+ CreoleTypes::VARBINARY =>'text',
+ CreoleTypes::BLOB =>'text',
+ CreoleTypes::CLOB =>'text',
+ CreoleTypes::BINARY =>'text',
+ CreoleTypes::LONGVARBINARY=>'text',
+ CreoleTypes::ARR =>'text',
+ CreoleTypes::OTHER =>'text'
+ );
+
+ /**
+ *
+ * The Constructor
+ *
+ * Classname and id are specific to HTML_QuickForm_Propel
+ *
+ * The other parameters are needed to construct the parent QuickForm Class.
+ *
+ * @param string className
+ * @param string id
+ * @param string formName
+ * @param string method
+ * @param string action
+ * @param string target
+ * @param array attributes
+ * @param boolean trackSubmit
+ *
+ */
+ public function __construct($className = null, $id = null, $formName='HTML_QuickForm_Propel', $method='post', $action='', $target='_self', $attributes=null, $trackSubmit = false)
+ {
+ $this->setClassName($className);
+ $this->setPeerName($className.'Peer'); // Is this always true ?
+ $this->setId($id);
+ parent::HTML_QuickForm($formName, $method, $action, $target, $attributes, $trackSubmit);
+
+ // set the default column policy
+ $this->setColumnMode(HTML_QUICKFORM_PROPEL_ALL_COLUMNS);
+ }
+
+ /**
+ *
+ * NOT IMPLEMENTED
+ *
+ * Allows for creating complex forms.
+ * Note that limit 1 will always be added to the criteria.
+ * Because we can only edit one record/row at a time.
+ *
+ * However we will be able to join tables in complex ways.
+ *
+ * @param Criteria
+ *
+ */
+ public function setCriteria(Criteria $c)
+ {
+ $c->setLimit(1);
+ $this->criteria = $c;
+ }
+
+ /**
+ *
+ * Set the action of this form
+ *
+ * @param string action
+ *
+ */
+ public function setAction($action)
+ {
+ $attributes = array('action'=>$action);
+ $this->updateAttributes($attributes);
+ }
+
+ /**
+ *
+ * Set method of this form, e.g. post or get
+ *
+ * @param string method
+ *
+ */
+ public function setMethod($method)
+ {
+ $attributes = array('method'=>$method);
+ $this->updateAttributes($attributes);
+ }
+
+ /**
+ *
+ * Set the target of this form
+ *
+ * @param string target
+ *
+ */
+ public function setTarget($target)
+ {
+ $attributes = array('target'=>$target);
+ $this->updateAttributes($attributes);
+ }
+
+ /**
+ *
+ * Set the id of the object we need to get
+ *
+ * @param string id
+ *
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ *
+ * Set the class
+ *
+ * @param string className
+ *
+ */
+ public function setClassName($className)
+ {
+ $this->className = $className;
+ }
+
+ /**
+ *
+ * Get the class name
+ *
+ * @return string className
+ *
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ private function setPeerName($peerName)
+ {
+ $this->peerName = $peerName;
+ }
+
+ /**
+ *
+ * Get the peer name
+ *
+ * @return string peerName
+ *
+ */
+ public function getPeerName()
+ {
+ return $this->peerName;
+ }
+
+ /**
+ * Build the form
+ *
+ * @return void
+ *
+ */
+ public function build()
+ {
+
+ if (!class_exists($this->peerName)) {
+ // some autoloading, dunno if it should belong here.
+ // or in the users autoload function
+ //$dbMap = call_user_func(array($this->peerName, 'getMapBuilder'));
+ // TODO: implement this.
+ }
+
+ if (empty($this->id)) {
+ // create empty instance of this class
+ $this->obj = new $this->className();
+ } else {
+ // find the object.
+ if (!$this->obj = call_user_func(array($this->peerName, 'retrieveByPK'), $this->id)) {
+ // for help me god.. what to do ?
+ throw new PropelException("HTML_QuickForm_Propel::build(): $this->peerName::retrieveByPK($this->id) failed.");
+ }
+
+ }
+
+ // note: getTableMap is protected by default.
+ // so do this handstand to get the tableMap
+ $mapBuilder = call_user_func(array($this->peerName, 'getMapBuilder'));
+
+ // Note: $dbMap is re-used below to determine foreign table
+ $dbMap = $mapBuilder->getDatabaseMap();
+
+ // Get the column names for this table.
+ $this->cols = $dbMap->getTable(constant("$this->peerName::TABLE_NAME"))->getColumns();
+
+ // Do it the HTML_QuickForm way and use defaultValues
+ // instead of setValue() on every element.
+ // we have to first loop through the columns
+ foreach ($this->cols as $colName=>$col) {
+
+
+ $elementType = $this->typeMapping[$col->getCreoleType()];
+
+ if ($elementType == 'date') {
+ // null returns timestamp
+ $value = $this->obj->{'get'.$col->getPhpName()}(null);
+ } else {
+
+ $value = $this->obj->{'get'.$col->getPhpName()}();
+
+ }
+
+ $defaultValues[$colName] = $value;
+ }
+
+ $this->setDefaults($defaultValues);
+
+ foreach ($this->cols as $colName=>$col) {
+
+ if ($this->isColumnHidden($colName)) {
+ continue;
+ }
+
+ if ($col->isPrimaryKey()) {
+ // create a hidden field with primary key
+ if (!$this->checkColumn($colName, HTML_QUICKFORM_PROPEL_COLUMN_MADE_VISIBLE)) {
+ $this->addElement('hidden', $col->getColumnName(), $col->getColumnName());
+ continue;
+ }
+ }
+
+ // element's title
+ $colTitle = $this->getColumnTitle($colName);
+
+ if ($col->isForeignKey()) {
+
+ // TODO: check if user created an optional method for populating the select form
+ // this populating of the select box is just some way to show joined items in a form.
+ // it could also be checkboxes, radio buttons or whatever kind of widget.
+
+ $relatedTable = $dbMap->getTable($col->getRelatedTableName());
+ $relatedCols = $relatedTable->getColumns();
+ $relatedPeer = $relatedTable->getPhpName().'Peer';
+
+ if (is_callable($relatedPeer, 'doSelect')) {
+
+
+ // TODO: allow set this criteria.
+ $c = new Criteria;
+
+ $relatedList = call_user_func(array($relatedPeer,'doSelect'), $c);
+
+ $colConstant = constant($this->peerName.'::'.$colName);
+ //$relatedColConstant = constant($relatedPeer.'::'.$relatedCol->getColumnName());
+ $relatedGetter = 'getPrimaryKey';
+
+
+ // TODO: this needs to be array based, to support multiple foreign columns
+ if (isset($this->joinMap[$colConstant])) {
+ // translate to getter
+ $relatedColConstant = $this->joinMap[$colConstant];
+ if (!$relatedTable->containsColumn($relatedColConstant)) {
+ // throw exception, there is no such column
+ throw new PropelException('HTML_QuickForm_Propel::build(): there is no column named '.$relatedTable->normalizeColName($relatedConstant).'in '.$relatedTable->getTableName().' while trying to build the select list');
+ }
+ $nameColumn = $relatedTable->getColumn($relatedColConstant);
+ $relatedGetter = 'get'.$nameColumn->getPhpName();
+ } else {
+ //auto detection
+ // determine the first string column
+ foreach ($relatedCols as $relatedCol) {
+ if ($relatedCol->getType() == 'string') {
+ $relatedGetter = 'get'.$relatedCol->getPhpName();
+ break;
+ }
+ }
+ }
+
+
+ $selectList = array();
+
+ // TODO: not hardcoded here.
+ $selectList[null] = _('Please selection an option');
+
+ foreach ($relatedList as $relObj) {
+ $key = $relObj->getPrimaryKey();
+
+ if (false OR $yesWannaUseEntireObjectsToUseItInTemplate) {
+ // TODO: IMPLEMENT THIS.
+ $selectList[$key] = $relObj;
+ } elseif (false OR $forceSomeKindOfColumnToBeDisplayed) {
+ // TODO: IMPLEMENT THIS.
+ } else {
+ $selectList[$key] = $relObj->{$relatedGetter}();
+ }
+ }
+
+ if (count($selectList) > 1) { // value of 1 depends on select message being set.
+ $select =& $this->addElement('select', $colName, $colTitle, $selectList);
+ } else {
+ // what to do if no records exists in the foreign table ?
+ $this->addElement('static', $colName, $colTitle, _('No Records'));
+ }
+
+ }
+
+ // do some recursion ?
+
+ } else {
+
+ //TODO: the mapping is not so generic anymore (to many exceptions)
+ $elementType = $this->typeMapping[$col->getCreoleType()];
+
+ if ($col->getCreoleType() == CreoleTypes::BOOLEAN) {
+ // TODO: describe how to override these options.
+ $radio = array();
+ $radio[] = HTML_QuickForm::createElement('radio', null, null, 'Yes', true);
+ $radio[] = HTML_QuickForm::createElement('radio', null, null, 'No', false);
+ $el = $this->addGroup($radio, $colName, $colName);
+
+
+ } else {
+
+ $el = $this->addElement(
+ $elementType,
+ $colName,
+ $colTitle);
+
+ if ($elementType == 'text') {
+ $el->setMaxLength($col->getSize());
+ }
+
+ if ($col->getCreoleType() == CreoleTypes::TIMESTAMP) {
+
+ /* Option Info:
+ var $_options = array(
+ 'language' => 'en',
+ 'format' => 'dMY',
+ 'minYear' => 2001,
+ 'maxYear' => 2010,
+ 'addEmptyOption' => false,
+ 'emptyOptionValue' => '',
+ 'emptyOptionText' => ' ',
+ 'optionIncrement' => array('i' => 1, 's' => 1)
+ );
+ */
+
+ // hmm, don't like it but there seems to be no public method
+ // to set an option afterwards
+ $el->_options['language'] = $this->lang;
+ // TODO: is the format always the same in propel ?
+ $el->_options['format'] = 'Y-F-d H:i:s';
+
+ }
+
+ }
+ // add an html id to the element
+ $this->addElementId($el, $colName);
+
+ //$el->setValue($value);
+
+ // required rule for NOT NULL columns
+ if ($col->isNotNull()) {
+ // TODO: What error message should we use?
+ $this->addRule($colName,
+ $this->getColumnTitle($colName) . ' is required',
+ 'required');
+ }
+
+ if ($col->hasValidators()) {
+
+ foreach ($col->getValidators() as $validatorMap) {
+
+ $this->addRule($colName,
+ $validatorMap->getMessage(),
+ $this->ruleMapping[$validatorMap->getName()],
+ $validatorMap->getValue(),
+ $this->defaultRuleType
+ );
+
+ }
+ }
+
+ }
+ }
+
+ // should HTML_QuickForm_Propel add this ?
+ $this->addElement('submit', 'submit', 'Submit');
+
+ // do this for the developer, can't think of any case where this is unwanted.
+ $this->applyFilter('__ALL__', 'trim');
+
+ }
+
+ /**
+ *
+ * Use it to change the locale used for the Date element
+ *
+ * @param string locale
+ * @return void
+ *
+ */
+ public function setLang($lang)
+ {
+ $this->lang = $lang;
+ }
+
+ /**
+ *
+ * Save the form.
+ *
+ * @return void
+ *
+ */
+ public function save()
+ {
+ $this->copyToObj();
+ $this->obj->save();
+ }
+
+ /**
+ *
+ * Copy form values to Obj.
+ *
+ * @return void
+ *
+ */
+ public function copyToObj()
+ {
+ // TODO: check what process does, if we leave out anything important.
+
+ if (!isset($this->cols)) {
+ // throw some error, form cannot be saved before it is build.
+ throw new PropelException('HTML_QuickForm_Propel::save(): form cannot be saved before it is build.');
+ }
+
+ foreach ($this->cols as $colName=>$col) {
+
+ // Has the form got this element?
+ if ($this->isColumnHidden($colName))
+ {
+ continue;
+ }
+
+ $value = $this->getElementValue($colName);
+ if ($value instanceof HTML_QuickForm_Error)
+ {
+ // TODO: What should we do if an error has occured?
+ continue;
+ }
+ $elementType = $this->typeMapping[$col->getCreoleType()];
+
+ // quickform doesn't seem to give back a timestamp, so calculate the date manually.
+ if ($elementType == 'date') {
+
+ $date = array(
+ 'D' => null,
+ 'l' => null,
+ 'd' => null,
+ 'M' => null,
+ 'm' => null,
+ 'F' => null,
+ 'Y' => null,
+ 'y' => null,
+ 'h' => null,
+ 'H' => null,
+ 'i' => null,
+ 's' => null,
+ 'a' => null,
+ 'A' => null
+ );
+
+ foreach ($value as $key=>$val) {
+ $date[$key] = $val[0];
+
+ }
+
+ $value = mktime($date['h'], $date['m'], $date['s'], $date['M'], $date['d'], $date['Y']);
+ }
+
+ $this->obj->{'set'.$col->getPhpName()}($value);
+ }
+ }
+
+ /**
+ *
+ * Get the object we are operating on.
+ *
+ * @return object a propel object
+ *
+ */
+ public function getObj()
+ {
+ return $this->obj;
+ }
+
+ /**
+ * What to do if a delete button is added
+ * and the user clicks on it, after the object has been delete in save()
+ */
+ public function onDelete()
+ {
+
+
+ }
+
+ public function createDeleteButton()
+ {
+ return $this->addElement('submit', 'delete', 'Delete');
+ }
+
+ public function isColumnHidden($column)
+ {
+ if ($this->checkColumn($column, HTML_QUICKFORM_PROPEL_COLUMN_MADE_HIDDEN) && $this->columnMode == HTML_QUICKFORM_PROPEL_ALL_COLUMNS) {
+ return true;
+ }
+
+ if (!$this->checkColumn($column, HTML_QUICKFORM_PROPEL_COLUMN_MADE_VISIBLE) && $this->columnMode == HTML_QUICKFORM_PROPEL_NO_COLUMNS) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function checkColumn($column, $state)
+ {
+ if (isset($this->columnVisibility[$column])) {
+ return ($this->columnVisibility[$column] == $state);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ *
+ * Sets the default visibility mode
+ *
+ * This must be either:
+ * HTML_QUICKFORM_PROPEL_NO_COLUMNS or
+ * HTML_QUICKFORM_PROPEL_ALL_COLUMNS
+ *
+ * @param string $column column name
+ * @return void
+ *
+ */
+ public function setColumnMode($mode)
+ {
+
+ if ($mode != HTML_QUICKFORM_PROPEL_NO_COLUMNS && $mode != HTML_QUICKFORM_PROPEL_ALL_COLUMNS) {
+
+ throw new PropelException('HTML_QuickForm_Propel::setColumnMode(): invalid mode passed.');
+
+ }
+
+ $this->columnMode = $mode;
+ }
+
+ /**
+ *
+ * Tell HTML_QuickForm_Propel it should hide this column
+ * It is now passed like ID instead of somePeer::ID
+ * The latter is better, but the array_keys of the columns are in ID format and not somePeer::ID
+ *
+ * @param string $column column name
+ * @return void
+ *
+ */
+
+ public function hideColumn($column)
+ {
+ $this->columnVisibility[$column] = HTML_QUICKFORM_PROPEL_COLUMN_MADE_HIDDEN;
+ }
+
+ /**
+ *
+ * Tell HTML_QuickForm_Propel it should show this column
+ *
+ * It is now passed like ID instead of somePeer::ID
+ * The latter is better, but the array_keys of the columns are in ID format and not somePeer::ID
+ *
+ * @param string $column column name
+ * @param string $title Title for the column, not required
+ * @return void
+ */
+ public function showColumn($column, $title = NULL)
+ {
+ $this->columnVisibility[$column] = HTML_QUICKFORM_PROPEL_COLUMN_MADE_VISIBLE;
+ if ($title !== NULL)
+ {
+ $this->setColumnTitle($column, $title);
+ }
+ }
+
+ /**
+ *
+ * assign a title to the column
+ *
+ * @param string $column
+ * @param string $title
+ * @return void
+ */
+ public function setColumnTitle($column, $title)
+ {
+ $this->columnTitles[$column] = $title;
+ }
+
+ /**
+ *
+ * returns column's title
+ *
+ * @param string $column
+ * @return void
+ */
+ public function getColumnTitle($column)
+ {
+ // TODO: check if $column exists
+ return (array_key_exists($column, $this->columnTitles))
+ ? $this->columnTitles[$column]
+ : $column;
+ }
+
+ /**
+ *
+ * Try to automatically join all relatedTables.
+ * NOT IMPLEMENTED
+ *
+ * @param boolean $bool
+ * @return void
+ */
+ public function autoJoin($bool)
+ {
+ $this->autoJoin = $bool;
+ }
+
+ /**
+ * Override this if you don't like the (strtolower) default
+ *
+ * @param HTML_QuickForm_Element $el
+ * @param string $colName
+ * @return void
+ */
+ protected function addElementId($el, $colName)
+ {
+ $el->updateAttributes(array('id'=>strtolower($colName)));
+ }
+
+ /**
+ *
+ * Set the default rule typef
+ * @param string $type
+ * @return void
+ *
+ */
+ public function setDefaultRuleType($type)
+ {
+ $this->defaultRuleType = $type;
+ }
+
+ /**
+ *
+ * UNFINISHED
+ *
+ * Probably it would be nice to be able to add this to the schema xml
+ *
+ * TODO: further implement multiple columns for the select list
+ *
+ * @var colName constant
+ * @var foreignColName mixed (constant/array of columnName constants)
+ * @var $seperator string Only used if foreignColName is an array
+ */
+
+ public function joinColumn($colName, $foreignColName, $seperator = null)
+ {
+ if (isset($seperator)) {
+ $this->seperator = $seperator;
+ }
+ $this->joinMap[$colName] = $foreignColName;
+ }
+
+}
diff --git a/3rd_party/php/propel/contrib/pear/Structures_DataGrid_Propel/Propel.php b/3rd_party/php/propel/contrib/pear/Structures_DataGrid_Propel/Propel.php
new file mode 100644
index 000000000..fcbf8afee
--- /dev/null
+++ b/3rd_party/php/propel/contrib/pear/Structures_DataGrid_Propel/Propel.php
@@ -0,0 +1,352 @@
+
+/*
+ * $Id: Propel.php 1347 2009-12-03 21:06:36Z francois $
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * .
+ */
+
+require_once 'Structures/DataGrid.php';
+
+define('STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS', 2);
+define('STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS', 3);
+define('STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_VISIBLE', 4);
+define('STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_HIDDEN', 5);
+
+/**
+ *
+ * NOTE: Structures_DataGrid_Propel extends Structures_DataGrid, so all Datagrid functionality is available.
+ *
+ * A fictive example:
+ * // Propel and Propel project classes must be in the include_path
+ *
+ * // Propel Class name : Report
+ * $dg =& new Structures_DataGrid_Propel('Report');
+ *
+ * // limit to 10 rows
+ * $c = new Criteria();
+ * $c->setLimit(10);
+ * $dg->setCriteria($c);
+ *
+ * // choose what columns must be displayed
+ * $dg->setColumnMode(STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS);
+ * $dg->showColumn('ACTIVE');
+ * $dg->showColumn('TITLE');
+ * $dg->showColumn('ID');
+ *
+ * // generate the datagrid
+ * $dg->build();
+ *
+ * // add two columns to edit the row and checkbox for further operations
+ * $dg->addColumn(new Structures_DataGrid_Column('', null, null, array('width' => '4%'), null, 'printEditLink()'));
+ * $dg->addColumn(new Structures_DataGrid_Column('', null, null, array('width' => '1%'), null, 'printCheckbox()'));
+ *
+ * // Display the datagrid
+ * $dg->render();
+ *
+ * @author Marc
+ * @version $Rev: 1347 $
+ * @copyright Copyright (c) 2005 Marc: LGPL - See LICENCE
+ * @package propel.contrib
+ */
+
+class Structures_DataGrid_Propel extends Structures_DataGrid {
+
+ /**
+ * Contains column visibility information.
+ * @var array
+ * @access private
+ */
+ private $columnVisibility = array();
+
+ /**
+ * The Column visibility mode.
+ * Possible values:
+ *
+ * STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS
+ * STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS
+ *
+ * @var integer
+ * @access private
+ */
+ private $columnMode;
+
+ /**
+ * String containing the peerName.
+ * @var string
+ * @access private
+ */
+ private $peerName;
+
+ /**
+ * String containing the className of the propel Object.
+ * @var string
+ * @access private
+ */
+ private $className;
+
+ /**
+ * Criteria of the Select query.
+ * @var criteria
+ * @access private
+ */
+ private $criteria;
+
+ /**
+ * List of primary keys
+ * @var array
+ * @access public
+ */
+ public $primaryKeys;
+
+ /**
+ *
+ * The Constructor
+ *
+ * Classname is specific to Structures_Datagrid_Propel
+ *
+ * The other parameters are needed to construct the parent Structures_DataGrid Class.
+ *
+ * @param string className
+ * @param string limit
+ * @param string render
+ *
+ */
+ public function __construct($className = null, $limit = null, $render = DATAGRID_RENDER_HTML_TABLE)
+ {
+
+ include_once $className.'.php';
+ include_once $className.'Peer'.'.php';
+
+ $this->setClassName($className);
+ $this->setPeerName($className.'Peer'); // Is this always true ?
+ parent::Structures_DataGrid($limit,null,$render);
+
+ // set the default column policy
+ $this->setColumnMode(STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS);
+ $this->criteria = new Criteria();
+ }
+
+ /**
+ *
+ * Set the criteria for select query
+ *
+ * @param Criteria c
+ *
+ */
+ public function setCriteria(Criteria $c)
+ {
+ $this->criteria = $c;
+ }
+
+ /**
+ *
+ * Set the class
+ *
+ * @param string className
+ *
+ */
+ public function setClassName($className)
+ {
+ $this->className = $className;
+ }
+
+ /**
+ *
+ * Get the class name
+ *
+ * @return string className
+ *
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ private function setPeerName($peerName)
+ {
+ $this->peerName = $peerName;
+ }
+
+ /**
+ *
+ * Get the peer name
+ *
+ * @return string peerName
+ *
+ */
+ public function getPeerName()
+ {
+ return $this->peerName;
+ }
+
+ /**
+ *
+ * Get the visibility of a column
+ *
+ * @return boolean true if column is set to hidden
+ *
+ */
+ public function isColumnHidden($column)
+ {
+ if ($this->checkColumn($column, STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_HIDDEN) && $this->columnMode == STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS) {
+ return true;
+ }
+
+ if (!$this->checkColumn($column, STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_VISIBLE) && $this->columnMode == STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * Check the state of a column
+ *
+ * @return boolean true if column is set to state
+ *
+ */
+ private function checkColumn($column, $state)
+ {
+ if (isset($this->columnVisibility[$column])) {
+ return ($this->columnVisibility[$column] == $state);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ *
+ * Sets the default visibility mode
+ *
+ * This must be either:
+ * STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS or
+ * STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS
+ *
+ * @param string $column column name
+ * @return void
+ *
+ */
+ public function setColumnMode($mode)
+ {
+
+ if ($mode != STRUCTURES_DATAGRID_PROPEL_NO_COLUMNS && $mode != STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS) {
+ throw new PropelException('STRUCTURES_DATAGRID_PROPEL::setColumnMode(): invalid mode passed.');
+ }
+
+ $this->columnMode = $mode;
+ }
+
+ /**
+ *
+ * Tell Structures_Datagrid_Propel it should hide this column
+ * It is now passed like ID instead of somePeer::ID
+ * The latter is better, but the array_keys of the columns are
+ * in ID format and not somePeer::ID
+ *
+ * @param string $column column name
+ * @return void
+ *
+ */
+ public function hideColumn($column)
+ {
+ $this->columnVisibility[$column] = STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_HIDDEN;
+ }
+
+ /**
+ *
+ * Tell Structures_Datagrid_Propel it should show this column
+ *
+ * It is now passed like ID instead of somePeer::ID
+ * The latter is better, but the array_keys of the columns are in ID format and not somePeer::ID
+ *
+ * @param string $column column name
+ * @return void
+ */
+ public function showColumn($column)
+ {
+ $this->columnVisibility[$column] = STRUCTURES_DATAGRID_PROPEL_COLUMN_MADE_VISIBLE;
+ }
+
+ /**
+ *
+ * Build the datagrid
+ *
+ * @return void
+ */
+ public function build()
+ {
+ $mapBuilder = call_user_func(array($this->getPeerName(), 'getMapBuilder'));
+ $dbMap = $mapBuilder->getDatabaseMap();
+ $cols = $dbMap->getTable(constant($this->getPeerName()."::TABLE_NAME"))->getColumns();
+ $stmt = call_user_func(array( $this->getPeerName(), 'doSelectStmt'), $this->criteria);
+
+ $dataset = array();
+ $columns = array();
+ $this->primaryKeys = array();
+ $class = $this->getClassName();
+ while ($row = $stmt->fetch(PDO::FETCH_NUM)) { // use Creole ResultSet methods to iterate over resultset
+ $obj = new $class();
+ $obj->hydrate($row);
+
+ $row = array();
+ foreach ($cols as $tmp_id => $col)
+ {
+ // save the PK in an array
+ if ($col->isPrimaryKey()) {
+ $this->primaryKeys[$col->getColumnName()] = $col->getColumnName();
+ }
+
+ $value = $obj->{'get'.$col->getPhpName()}(null);
+ // save the row value
+
+ $row[$col->getColumnName()] = $value;
+ // save the list of propel header column name
+ $columns[$col->getColumnName()] = $col->getColumnName();
+
+ }
+ // add the row to dataset
+ $dataset[] = $row;
+ }
+
+ $this->bind($dataset);
+
+ if ($this->columnMode == STRUCTURES_DATAGRID_PROPEL_ALL_COLUMNS) {
+ foreach ($columns as $tmp_id => $column) {
+
+ if (!$this->isColumnHidden($column)) {
+
+ $this->addColumn(new Structures_DataGrid_Column($column, $column, $column, null));
+ }
+ }
+ } else {
+
+ foreach ($this->columnVisibility as $column => $visibility) {
+
+ if (!$this->isColumnHidden($column)) {
+ $this->addColumn(new Structures_DataGrid_Column($column, $column, $column, null));
+ }
+ }
+ }
+
+ $this->renderer->setTableHeaderAttributes(array('class' => 'title'));
+ $this->renderer->setTableAttribute('class', 'list');
+ $this->renderer->sortIconASC = '?';
+ $this->renderer->sortIconDESC = '?';
+ }
+
+}
diff --git a/3rd_party/php/propel/docs/behavior/aggregate_column.txt b/3rd_party/php/propel/docs/behavior/aggregate_column.txt
new file mode 100644
index 000000000..e071425a7
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/aggregate_column.txt
@@ -0,0 +1,130 @@
+= Aggregate Column Behavior =
+
+The `aggregate_column` behavior keeps a column updated using an aggregate function executed on a related table.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `aggregate_column` behavior to a table. You must provide parameters for the aggregate column `name`, the foreign table name, and the aggegate `expression`. For instance, to add an aggregate column keeping the comment count in a `post` table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Rebuild your model, and insert the table creation sql again. The model now has an additional `nb_comments` column, of type `integer` by default. And each time an record from the foreign table is added, modified, or removed, the aggregate column is updated:
+
+{{{
+#!php
+setTitle('How Is Life On Earth?');
+$post->save();
+echo $post->getNbComments(); // 0
+$comment1 = new Comment();
+$comment1->setPost($post);
+$comment1->save();
+echo $post->getNbComments(); // 1
+$comment2 = new Comment();
+$comment2->setPost($post);
+$comment2->save();
+echo $post->getNbComments(); // 2
+$comment2->delete();
+echo $post->getNbComments(); // 1
+}}}
+
+The aggregate column is also kept up to date when related records get modified through a Query object:
+
+{{{
+#!php
+filterByPost($post)
+ ->delete():
+echo $post->getNbComments(); // 0
+}}}
+
+== Customizing The Aggregate Calculation ==
+
+Any aggregate function can be used on any of the foreign columns. For instance, you can use the `aggregate_column` behavior to keep the latest update date of the related comments, or the total votes on the comments. You can even keep several aggregate columns in a single table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+The behavior adds a `computeXXX()` method to the `Post` class to compute the value of the aggregate function. This method, called each time records are modified in the related `comment` table, is the translation of the behavior settings into a SQL query:
+
+{{{
+#!php
+prepare('SELECT COUNT(id) FROM `comment` WHERE comment.POST_ID = :p1');
+ $stmt->bindValue(':p1', $this->getId());
+ $stmt->execute();
+ return $stmt->fetchColumn();
+}
+}}}
+
+You can override this method in the model class to customize the aggregate column calculation.
+
+== Customizing The Aggregate Column ==
+
+By default, the behavior adds one columns to the model. If this column is already described in the schema, the behavior detects it and doesn't add it a second time. This can be useful if you need to use a custom `type` or `phpName` for the aggregate column:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+}}}
diff --git a/3rd_party/php/propel/docs/behavior/alternative_coding_standards.txt b/3rd_party/php/propel/docs/behavior/alternative_coding_standards.txt
new file mode 100644
index 000000000..bc7462357
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/alternative_coding_standards.txt
@@ -0,0 +1,89 @@
+= Alternative Coding Standards Behavior =
+
+The `alternative_coding_standards` behavior changes the coding standards of the model classes generated by Propel to match your own coding style.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `alternative_coding_standards` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, and you're ready to go. The code of the model classes now uses an alternative set of coding standards:
+
+{{{
+#!php
+title !== $v)
+ {
+ $this->title = $v;
+ $this->modifiedColumns[] = BookPeer::TITLE;
+ }
+
+ return $this;
+ }
+
+// instead of
+
+ /**
+ * Set the value of [title] column.
+ *
+ * @param string $v new value
+ * @return Table4 The current object (for fluent API support)
+ */
+ public function setTitle($v)
+ {
+ if ($v !== null) {
+ $v = (string) $v;
+ }
+
+ if ($this->title !== $v) {
+ $this->title = $v;
+ $this->modifiedColumns[] = BookPeer::TITLE;
+ }
+
+ return $this;
+ } // setTitle()
+}}}
+
+The behavior replaces tabulations by whitespace (2 spaces by default), places opening brackets on newlines, removes closing brackets comments, and can even strip every comments in the generated classes if you wish.
+
+== Parameters ==
+
+Each of the new coding style rules has corresponding parameter in the behavior description. Here is the default configuration:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+You can change these settings to better match your own coding styles.
diff --git a/3rd_party/php/propel/docs/behavior/auto_add_pk.txt b/3rd_party/php/propel/docs/behavior/auto_add_pk.txt
new file mode 100644
index 000000000..214ddf020
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/auto_add_pk.txt
@@ -0,0 +1,73 @@
+= AutoAddPk Behavior =
+
+The `auto_add_pk` behavior adds a primary key columns to the tables that don't have one. Using this behavior allows you to omit the declaration of primary keys in your tables.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `auto_add_pk` behavior to a table:
+{{{
+#!xml
+
+
+
+
+}}}
+
+Rebuild your model, and insert the table creation sql. You will notice that the `book` table has two columns and not just one. The behavior added an `id` column, of type integer and autoincremented. This column can be used as any other column:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+echo $b->getId(); // 1
+}}}
+
+This behavior is more powerful if you add it to the database instead of a table. That way, it will alter all tables not defining a primary key column - and leave the others unchanged.
+
+{{{
+#!xml
+
+
+
+
+
+
+}}}
+
+You can even enable it for all your database by adding it to the default behaviors in your `build.properties` file:
+
+{{{
+#!ini
+propel.behavior.default = auto_add_pk
+}}}
+
+== Parameters ==
+
+By default, the behavior adds a column named `id` to the table if the table has no primary key. You can customize all the attributes of the added column by setting corresponding parameters in the behavior definition:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+}}}
+
+Once you regenerate your model, the column is now named differently:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->setIdentifier(1);
+$b->save();
+echo $b->getIdentifier(); // 1
+}}}
diff --git a/3rd_party/php/propel/docs/behavior/nested_set.txt b/3rd_party/php/propel/docs/behavior/nested_set.txt
new file mode 100644
index 000000000..ad0f45077
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/nested_set.txt
@@ -0,0 +1,370 @@
+= NestedSet Behavior =
+
+The `nested_set` behavior allows a model to become a tree structure, and provides numerous methods to traverse the tree in an efficient way.
+
+Many applications need to store hierarchical data in the model. For instance, a forum stores a tree of messages for each discussion. A CMS sees sections and subsections as a navigation tree. In a business organization chart, each person is a leaf of the organization tree. [http://en.wikipedia.org/wiki/Nested_set_model Nested sets] are the best way to store such hierachical data in a relational database and manipulate it. The name "nested sets" describes the algorithm used to store the position of a model in the tree ; it is also known as "modified preorder tree traversal".
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `nested_set` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has the ability to be inserted into a tree structure, as follows:
+
+{{{
+#!php
+setTitle('Home');
+$s1->makeRoot(); // make this node the root of the tree
+$s1->save();
+$s2 = new Section();
+$s2->setTitle('World');
+$s2->insertAsFirstChildOf($s1); // insert the node in the tree
+$s2->save();
+$s3 = new Section();
+$s3->setTitle('Europe');
+$s3->insertAsFirstChildOf($s2); // insert the node in the tree
+$s3->save()
+$s4 = new Section();
+$s4->setTitle('Business');
+$s4->insertAsNextSiblingOf($s2); // insert the node in the tree
+$s4->save();
+/* The sections are now stored in the database as a tree:
+ $s1:Home
+ | \
+$s2:World $s4:Business
+ |
+$s3:Europe
+*/
+}}}
+
+You can continue to insert new nodes as children or siblings of existing nodes, using any of the `insertAsFirstChildOf()`, `insertAsLastChildOf()`, `insertAsPrevSiblingOf()`, and `insertAsNextSiblingOf()` methods.
+
+Once you have built a tree, you can traverse it using any of the numerous methods the `nested_set` behavior adds to the query and model objects. For instance:
+
+{{{
+#!php
+findRoot(); // $s1
+$worldNode = $rootNode->getFirstChild(); // $s2
+$businessNode = $worldNode->getNextSibling(); // $s4
+$firstLevelSections = $rootNode->getChildren(); // array($s2, $s4)
+$allSections = $rootNode->getDescendants(); // array($s2, $s3, $s4)
+// you can also chain the methods
+$europeNode = $rootNode->getLastChild()->getPrevSibling()->getFirstChild(); // $s3
+$path = $europeNode->getAncestors(); // array($s1, $s2)
+}}}
+
+The nodes returned by these methods are regular Propel model objects, with access to the properties and related models. The `nested_set` behavior also adds inspection methods to nodes:
+
+{{{
+#!php
+isRoot(); // false
+echo $s2->isLeaf(); // false
+echo $s2->getLevel(); // 1
+echo $s2->hasChildren(); // true
+echo $s2->countChildren(); // 1
+echo $s2->hasSiblings(); // true
+}}}
+
+Each of the traversal and inspection methods result in a single database query, whatever the position of the node in the tree. This is because the information about the node position in the tree is stored in three columns of the model, named `tree_left`, `tree_left`, and `tree_level`. The value given to these columns is determined by the nested set algorithm, and it makes read queries much more effective than trees using a simple `parent_id` foreign key.
+
+== Manipulating Nodes ==
+
+You can move a node - and its subtree - across the tree using any of the `moveToFirstChildOf()`, `moveToLastChildOf()`, `moveToPrevSiblingOf()`, and `moveToLastSiblingOf()` methods. These operations are immediate and don't require that you save the model afterwards:
+
+{{{
+#!php
+moveToFirstChildOf($s4);
+/* The tree is modified as follows:
+$s1:Home
+ |
+$s4:Business
+ |
+$s2:World
+ |
+$s3:Europe
+*/
+// now move the "Europe" section directly under root, after "Business"
+$s2->moveToFirstChildOf($s4);
+/* The tree is modified as follows:
+ $s1:Home
+ | \
+$s4:Business $s3:Europe
+ |
+$s2:World
+*/
+}}}
+
+You can delete the descendants of a node using `deleteDescendants()`:
+
+{{{
+#!php
+deleteDescendants($s4);
+/* The tree is modified as follows:
+ $s1:Home
+ | \
+$s4:Business $s3:Europe
+*/
+}}}
+
+If you `delete()` a node, all its descendants are deleted in cascade. To avoid accidental deletion of an entire tree, calling `delete()` on a root node throws an exception. Use the `delete()` Query method instead to delete an entire tree.
+
+== Filtering Results ==
+
+The `nested_set` behavior adds numerous methods to the generated Query object. You can use these methods to build more complex queries. For instance, to get all the children of the root node ordered by title, build a Query as follows:
+
+{{{
+#!php
+childrenOf($rootNode)
+ ->orderByTitle()
+ ->find();
+}}}
+
+Alternatively, if you already have an existing query method, you can pass it to the model object's methods to filter the results:
+
+{{{
+#!php
+orderByTitle();
+$children = $rootNode->getChildren($orderQuery);
+}}}
+
+== Multiple Trees ==
+
+When you need to store several trees for a single model - for instance, several threads of posts in a forum - use a ''scope'' for each tree. This requires that you enable scope tree support in the behavior definition by setting the `use_scope` parameter to `true`:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Now, after rebuilding your model, you can have as many trees as required:
+
+{{{
+#!php
+findPk(123);
+$firstPost = PostQuery::create()->findRoot($thread->getId()); // first message of the discussion
+$discussion = PostQuery::create()->findTree(thread->getId()); // all messages of the discussion
+PostQuery::create()->inTree($thread->getId())->delete(); // delete an entire discussion
+$firstPostOfEveryDiscussion = PostQuery::create()->findRoots();
+}}}
+
+== Using a RecursiveIterator ==
+
+An alternative way to browse a tree structure extensively is to use a [http://php.net/RecursiveIterator RecursiveIterator]. The `nested_set` behavior provides an easy way to retrieve such an iterator from a node, and to parse the entire branch in a single iteration.
+
+For instance, to display an entire tree structure, you can use the following code:
+
+{{{
+#!php
+findRoot();
+foreach ($root->getIterator() as $node) {
+ echo str_repeat(' ', $node->getLevel()) . $node->getTitle() . "\n";
+}
+}}}
+
+The iterator parses the tree in a recursive way by retrieving the children of every node. This can be quite effective on very large trees, since the iterator hydrates only a few objects at a time.
+
+Beware, though, that the iterator executes many queries to parse a tree. On smaller trees, prefer the `getBranch()` method to execute only one query, and hydrate all records at once:
+
+{{{
+#!php
+findRoot();
+foreach ($root->getBranch() as $node) {
+ echo str_repeat(' ', $node->getLevel()) . $node->getTitle() . "\n";
+}
+}}}
+
+== Parameters ==
+
+By default, the behavior adds three columns to the model - four if you use the scope feature. You can use custom names for the nested sets columns. The following schema illustrates a complete customization of the behavior:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Whatever name you give to your columns, the `nested_sets` behavior always adds the following proxy methods, which are mapped to the correct column:
+
+{{{
+#!php
+getLeftValue(); // returns $post->lft
+$post->setLeftValue($left);
+$post->getRightValue(); // returns $post->rgt
+$post->setRightValue($right);
+$post->getLevel(); // returns $post->lvl
+$post->setLevel($level);
+$post->getScopeValue(); // returns $post->thread_id
+$post->setScopeValue($scope);
+}}}
+
+If your application used the old nested sets builder from Propel 1.4, you can enable the `method_proxies` parameter so that the behavior generates method proxies for the methods that used a different name (e.g. `createRoot()` for `makeRoot()`, `retrieveFirstChild()` for `getFirstChild()`, etc.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+}}}
+
+== Complete API ==
+
+Here is a list of the methods added by the behavior to the model objects:
+
+{{{
+#!php
+` tag to add the `query_cache` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+After you rebuild your model, all the queries on this object can now be cached. To trigger the query cache on a particular query, just give it a query key using the `setQueryKey()` method. The key is a unique identifier that you can choose, later used for cache lookups:
+
+{{{
+#!php
+setQueryKey('search book by title')
+ ->filterByTitle($title)
+ ->findOne();
+}}}
+
+The first time Propel executes the termination method, it computes the SQL translation of the Query object and stores it into a cache backend (APC by default). Next time you run the same query, it executes faster, even with different parameters:
+
+{{{
+#!php
+setQueryKey('search book by title')
+ ->filterByTitle($title)
+ ->findOne();
+}}}
+
+'''Tip''': The more complex the query, the greater the boost you get from the query cache behavior.
+
+== Parameters ==
+
+You can change the cache backend and the cache lifetime (in seconds) by setting the `backend` and `lifetime` parameters:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+}}}
+
+To implement a custom cache backend, just override the generated `cacheContains()`, `cacheFetch()` and `cacheStore()` methods in the Query object. For instance, to implement query cache using Zend_Cache and memcached, try the following:
+
+{{{
+#!php
+getCacheBackend()->test($key);
+ }
+
+ public function cacheFetch($key)
+ {
+ return $this->getCacheBackend()->load($key);
+ }
+
+ public function cacheStore($key, $value)
+ {
+ return $this->getCacheBackend()->save($key, $value);
+ }
+
+ protected function getCacheBackend()
+ {
+ if (self::$cacheBackend === null) {
+ $frontendOptions = array(
+ 'lifetime' => 7200,
+ 'automatic_serialization' => true
+ );
+ $backendOptions = array(
+ 'servers' => array(
+ array(
+ 'host' => 'localhost',
+ 'port' => 11211,
+ 'persistent' => true
+ )
+ )
+ );
+ self::$cacheBackend = Zend_Cache::factory('Core', 'Memcached', $frontendOptions, $backendOptions);
+ }
+
+ return self::$cacheBackend;
+ }
+}
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/behavior/sluggable.txt b/3rd_party/php/propel/docs/behavior/sluggable.txt
new file mode 100755
index 000000000..beb454a16
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/sluggable.txt
@@ -0,0 +1,135 @@
+= Sluggable Behavior =
+
+The `sluggable` behavior allows a model to offer a human readable identifier that can be used for search engine friendly URLs.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `sluggable` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has an additional getter for its slug, which is automatically set before the object is saved:
+
+{{{
+#!php
+setTitle('Hello, World!');
+$p1->save();
+echo $p1->getSlug(); // 'hello-world'
+}}}
+
+By default, the behavior uses the string representation of the object to build the slug. In the example above, the `title` column is defined as `primaryString`, so the slug uses this column as a base string. The string is then cleaned up in order to allow it to appear in a URL. In the process, blanks and special characters are replaced by a dash, and the string is lowercased.
+
+'''Tip''': The slug is unique by design. That means that if you create a new object and that the behavior calculates a slug that already exists, the string is modified to be unique:
+
+{{{
+#!php
+setTitle('Hello, World!');
+$p2->save();
+echo $p2->getSlug(); // 'hello-world-1'
+}}}
+
+The generated model query offers a `findOneBySlug()` method to easily retrieve a model object based on its slug:
+
+{{{
+#!php
+findOneBySlug('hello-world');
+}}}
+
+== Parameters ==
+
+By default, the behavior adds one columns to the model. If this column is already described in the schema, the behavior detects it and doesn't add it a second time. The behavior parameters allow you to use custom patterns for the slug composition. The following schema illustrates a complete customization of the behavior:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Whatever `slug_column` name you choose, the `sluggable` behavior always adds the following proxy methods, which are mapped to the correct column:
+
+{{{
+#!php
+getSlug(); // returns $post->url
+$post->setSlug($slug); // $post->url = $slug
+}}}
+
+The `slug_pattern` parameter is the rule used to build the raw slug based on the object properties. Any substring enclosed between brackets '{}' is turned into a getter, so the `Post` class generates slugs as follows:
+
+{{{
+#!php
+getTitle();
+}
+}}}
+
+Incidentally, that means that you can use names that don't match a real column phpName, as long as your model provides a getter for it.
+
+The `replace_pattern` parameter is a regular expression that shows all the characters that will end up replaced by the `replacement` parameter. In the above example, special characters like '!' or ':' are replaced by '-', but not letters, digits, nor '/'.
+
+The `separator` parameter is the character that separates the slug from the incremental index added in case of non-unicity. Set as '/', it makes `Post` objects sharing the same title have the following slugs:
+
+{{{
+'posts/hello-world'
+'posts/hello-world/1'
+'posts/hello-world/2'
+...
+}}}
+
+A `permanent` slug is not automatically updated when the fields that constitute it change. This is useful when the slug serves as a permalink, that should work even when the model object properties change. Note that you can still manually change the slug in a model using the `permanent` setting by calling `setSlug()`;
+
+== Further Customization ==
+
+The slug is generated by the object when it is saved, via the `createSlug()` method. This method does several operations on a simple string:
+
+{{{
+#!php
+createRawSlug();
+ // truncate the slug to accomodate the size of the slug column
+ $slug = $this->limitSlugSize($slug);
+ // add an incremental index to make sure the slug is unique
+ $slug = $this->makeSlugUnique($slug);
+
+ return $slug;
+}
+
+protected function createRawSlug()
+{
+ // here comes the string composition code, generated according to `slug_pattern`
+ $slug = 'posts/' . $this->cleanupSlugPart($this->getTitle());
+ // cleanupSlugPart() cleans up the slug part
+ // based on the `replace_pattern` and `replacement` parameters
+
+ return $slug;
+}
+}}}
+
+You can override any of these methods in your model class, in order to implement a custom slug logic.
diff --git a/3rd_party/php/propel/docs/behavior/soft_delete.txt b/3rd_party/php/propel/docs/behavior/soft_delete.txt
new file mode 100644
index 000000000..0105edbf1
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/soft_delete.txt
@@ -0,0 +1,116 @@
+= SoftDelete Behavior =
+
+The `soft_delete` behavior overrides the deletion methods of a model object to make them 'hide' the deleted rows but keep them in the database. Deleted objects still don't show up on select queries, but they can be retrieved or undeleted when necessary.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `soft_delete` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has one new column, `deleted_at`, that stores the deletion date. Select queries don't return the deleted objects:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+$b->delete();
+echo $b->isDeleted(); // false
+echo $b->getDeletedAt(); // 2009-10-02 18:14:23
+$books = BookQuery::create()->find(); // empty collection
+}}}
+
+Behind the curtain, the behavior adds a condition to every SELECT query to return only records where the `deleted_at` column is null. That's why the deleted objects don't appear anymore upon selection.
+
+You can turn off the query alteration globally by calling the static method `disableSoftDelete()` on the related Query object:
+
+{{{
+#!php
+find();
+$book = $books[0];
+echo $book->getTitle(); // 'War And Peace'
+}}}
+
+Note that `find()` and other selection methods automatically re-enable the `soft_delete` filter. You can also enable it manually by calling the `enableSoftDelete()` method on Peer objects.
+
+If you want to recover a deleted object, use the `unDelete()` method:
+
+{{{
+#!php
+unDelete();
+$books = BookQuery::create()->find();
+$book = $books[0];
+echo $book->getTitle(); // 'War And Peace'
+}}}
+
+If you want to force the real deletion of an object, call the `forceDelete()` method:
+
+{{{
+#!php
+forceDelete();
+echo $book->isDeleted(); // true
+$books = BookQuery::create()->find(); // empty collection
+}}}
+
+The query methods `delete()` and `deleteAll()` also perform a soft deletion, unless you disable the behavior on the peer class:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+
+BookQuery::create()->delete($b);
+$books = BookQuery::create()->find(); // empty collection
+// the rows look deleted, but they are still there
+BookQuery::disableSoftDelete();
+$books = BookQuery::create()->find();
+$book = $books[0];
+echo $book->getTitle(); // 'War And Peace'
+
+// To perform a true deletion, disable the softDelete feature
+BookQuery::disableSoftDelete();
+BookQuery::create()->delete();
+// Alternatively, use forceDelete()
+BookQuery::create()->forceDelete();
+}}}
+
+== Parameters ==
+
+You can change the name of the column added by the behavior by setting the `deleted_column` parameter:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+}}}
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+$b->delete();
+echo $b->getMyDeletionDate(); // 2009-10-02 18:14:23
+$books = BookQuery::create()->find(); // empty collection
+}}}
diff --git a/3rd_party/php/propel/docs/behavior/sortable.txt b/3rd_party/php/propel/docs/behavior/sortable.txt
new file mode 100644
index 000000000..b45c12843
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/sortable.txt
@@ -0,0 +1,285 @@
+= Sortable Behavior =
+
+The `sortable` behavior allows a model to become an ordered list, and provides numerous methods to traverse this list in an efficient way.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `sortable` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has the ability to be inserted into an ordered list, as follows:
+
+{{{
+#!php
+setTitle('Wash the dishes');
+$t1->save();
+echo $t1->getRank(); // 1, the first rank to be given (not 0)
+$t2 = new Task();
+$t2->setTitle('Do the laundry');
+$t2->save();
+echo $t2->getRank(); // 2
+$t3 = new Task();
+$t3->setTitle('Rest a little');
+$t3->save()
+echo $t3->getRank(); // 3
+}}}
+
+As long as you save new objects, Propel gives them the first available rank in the list.
+
+Once you have built an ordered list, you can traverse it using any of the methods added by the `sortable` behavior. For instance:
+
+{{{
+#!php
+findOneByRank(1); // $t1
+$secondTask = $firstTask->getNext(); // $t2
+$lastTask = $secondTask->getNext(); // $t3
+$secondTask = $lastTask->getPrevious(); // $t2
+
+$allTasks = TaskQuery::create()->findList();
+// => collection($t1, $t2, $t3)
+$allTasksInReverseOrder = TaskQuery::create()->orderByRank('desc')->find();
+// => collection($t3, $t2, $t2)
+}}}
+
+The results returned by these methods are regular Propel model objects, with access to the properties and related models. The `sortable` behavior also adds inspection methods to objects:
+
+{{{
+#!php
+isFirst(); // false
+echo $t2->isLast(); // false
+echo $t2->getRank(); // 2
+}}}
+
+== Manipulating Objects In A List ==
+
+You can move an object in the list using any of the `moveUp()`, `moveDown()`, `moveToTop()`, `moveToBottom()`, `moveToRank()`, and `swapWith()` methods. These operations are immediate and don't require that you save the model afterwards:
+
+{{{
+#!php
+moveToTop();
+// The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
+$t2->moveToBottom();
+// The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
+$t2->moveUp();
+// The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
+$t2->swapWith($t1);
+// The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
+$t2->moveToRank(3);
+// The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
+$t2->moveToRank(2);
+}}}
+
+By default, new objects are added at the bottom of the list. But you can also insert them at a specific position, using any of the `insertAtTop(), `insertAtBottom()`, and `insertAtRank()` methods. Note that the `insertAtXXX` methods don't save the object:
+
+{{{
+#!php
+setTitle('Clean windows');
+$t4->insertAtRank(2);
+$t4->save();
+// The list is now 1 - Wash the dishes, 2 - Clean Windows, 3 - Do the laundry, 4 - Rest a little
+}}}
+
+Whenever you `delete()` an object, the ranks are rearranged to fill the gap:
+
+{{{
+#!php
+delete();
+// The list is now 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
+}}}
+
+'''Tip''': You can remove an object from the list without necessarily deleting it by calling `removeFromList()`. Don't forget to `save()` it afterwards so that the other objects in the lists are rearranged to fill the gap.
+
+== Multiple Lists ==
+
+When you need to store several lists for a single model - for instance, one task list for each user - use a ''scope'' for each list. This requires that you enable scope support in the behavior definition by setting the `use_scope` parameter to `true`:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Now, after rebuilding your model, you can have as many lists as required:
+
+{{{
+#!php
+setTitle('Wash the dishes');
+$t1->setUser($paul);
+$t1->save();
+echo $t1->getRank(); // 1
+$t2 = new Task();
+$t2->setTitle('Do the laundry');
+$t2->setUser($paul);
+$t2->save();
+echo $t2->getRank(); // 2
+$t3 = new Task();
+$t3->setTitle('Rest a little');
+$t3->setUser($john);
+$t3->save()
+echo $t3->getRank(); // 1, because John has his own task list
+}}}
+
+The generated methods now accept a `$scope` parameter to restrict the query to a given scope:
+
+{{{
+#!php
+findOneByRank($rank = 1, $scope = $paul->getId()); // $t1
+$lastPaulTask = $firstTask->getNext(); // $t2
+$firstJohnTask = TaskPeer::create()->findOneByRank($rank = 1, $scope = $john->getId()); // $t1
+}}}
+
+Models using the sortable behavior with scope benefit from one additional Query methods named `inList()`:
+
+{{{
+#!php
+inList($scope = $paul->getId())->find();
+}}}
+
+== Parameters ==
+
+By default, the behavior adds one columns to the model - two if you use the scope feature. If these columns are already described in the schema, the behavior detects it and doesn't add them a second time. The behavior parameters allow you to use custom names for the sortable columns. The following schema illustrates a complete customization of the behavior:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Whatever name you give to your columns, the `sortable` behavior always adds the following proxy methods, which are mapped to the correct column:
+
+{{{
+#!php
+getRank(); // returns $task->my_rank_column
+$task->setRank($rank);
+$task->getScopeValue(); // returns $task->user_id
+$task->setScopeValue($scope);
+}}}
+
+The same happens for the generated Query object:
+
+{{{
+#!php
+filterByRank(); // proxies to filterByMyRankColumn()
+$query = TaskQuery::create()->orderByRank(); // proxies to orderByMyRankColumn()
+$tasks = TaskQuery::create()->findOneByRank(); // proxies to findOneByMyRankColumn()
+}}}
+
+'''Tip''': The behavior adds columns but no index. Depending on your table structure, you might want to add a column index by hand to speed up queries on sorted lists.
+
+== Complete API ==
+
+Here is a list of the methods added by the behavior to the model objects:
+
+{{{
+#!php
+ $rank associative array
+// only for behavior with use_scope
+array inList($scope)
+}}}
+
+The behavior also adds a few methods to the Peer classes:
+
+{{{
+#!php
+ $rank associative array
+// only for behavior with use_scope
+array retrieveList($scope)
+int countList($scope)
+int deleteList($scope)
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/behavior/timestampable.txt b/3rd_party/php/propel/docs/behavior/timestampable.txt
new file mode 100644
index 000000000..146b694e9
--- /dev/null
+++ b/3rd_party/php/propel/docs/behavior/timestampable.txt
@@ -0,0 +1,92 @@
+= Timestampable Behavior =
+
+The `timestampable` behavior allows you to keep track of the date of creation and last update of your model objects.
+
+== Basic Usage ==
+
+In the `schema.xml`, use the `` tag to add the `timestampable` behavior to a table:
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has two new columns, `created_at` and `updated_at`, that store a timestamp automatically updated on save:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+echo $b->getCreatedAt(); // 2009-10-02 18:14:23
+echo $b->getUpdatedAt(); // 2009-10-02 18:14:23
+$b->setTitle('Anna Karenina');
+$b->save();
+echo $b->getCreatedAt(); // 2009-10-02 18:14:23
+echo $b->getUpdatedAt(); // 2009-10-02 18:14:25
+}}}
+
+The object query also has specific methods to retrieve recent objects and order them according to their update date:
+
+{{{
+#!php
+recentlyUpdated() // adds a minimum value for the update date
+ ->lastUpdatedFirst() // orders the results by descending update date
+ ->find();
+}}}
+
+You can use any of the following methods in the object query:
+
+{{{
+#!php
+
+
+
+
+
+
+
+
+
+
+}}}
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+echo $b->getMyCreateDate(); // 2009-10-02 18:14:23
+echo $b->getMyUpdateDate(); // 2009-10-02 18:14:23
+$b->setTitle('Anna Karenina');
+$b->save();
+echo $b->getMyCreateDate(); // 2009-10-02 18:14:23
+echo $b->getMyUpdateDate(); // 2009-10-02 18:14:25
+}}}
diff --git a/3rd_party/php/propel/docs/build.xml b/3rd_party/php/propel/docs/build.xml
new file mode 100755
index 000000000..cc876c4b4
--- /dev/null
+++ b/3rd_party/php/propel/docs/build.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3rd_party/php/propel/docs/cookbook/Add-Custom-SQL.txt b/3rd_party/php/propel/docs/cookbook/Add-Custom-SQL.txt
new file mode 100644
index 000000000..fd672f7ea
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Add-Custom-SQL.txt
@@ -0,0 +1,34 @@
+= Adding Additional SQL Files =
+
+In many cases you may wish to have the ''insert-sql'' task perform additional SQL operations (e.g. add views, stored procedures, triggers, sample data, etc.). Rather than have to run additional SQL statements yourself every time you re-build your object model, you can have the Propel generator do this for you.
+
+== 1. Create the SQL DDL files ==
+
+Create any additional SQL files that you want executed against the database (after the base ''schema.sql'' file is applied).
+
+For example, if we wanted to add a default value to a column that was unsupported in the schema (e.g. where value is a SQL function):
+
+{{{
+#!sql
+-- (for postgres)
+ALTER TABLE my_table ALTER COLUMN my_column SET DEFAULT CURRENT_TIMESTAMP;
+}}}
+
+Now we save that as '''my_column-default.sql''' in the same directory as the generated '''schema.sql''' file (usually in projectdir/build/sql/).
+
+== 2. Tell Propel Generator about the new file ==
+
+In that same directory (where your '''schema.sql''' is located), there is a '''sqldb.map''' file which contains a mapping of SQL DDL files to the database that they should be executed against. After running the propel generator, you will probably have a single entry in that file that looks like:
+
+{{{
+schema.sql=your-db-name
+}}}
+
+We want to simply add the new file we created to this file (future builds will preserve anything you add to this file). When we're done, the file will look like this:
+
+{{{
+schema.sql=your-db-name
+my_column-default.sql=your-db-name
+}}}
+
+Now when you execute the ''insert-sql'' Propel generator target, the '''my_column-default.sql''' file will be executed against the ''your-db-name'' database.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Copying-Objects.txt b/3rd_party/php/propel/docs/cookbook/Copying-Objects.txt
new file mode 100644
index 000000000..5a67eaa4f
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Copying-Objects.txt
@@ -0,0 +1,47 @@
+= Copying Persisted Objects =
+
+Propel provides the {{{copy()}}} method to perform copies of mapped row in the database. Note that Propel does '''not''' override the {{{__clone()}}} method; this allows you to create local duplicates of objects that map to the same persisted database row (should you need to do this).
+
+The {{{copy()}}} method by default performs shallow copies, meaning that any foreign key references will remain the same.
+
+{{{
+#!php
+setFirstName("Aldous");
+$a->setLastName("Huxley");
+
+$p = new Publisher();
+$p->setName("Harper");
+
+$b = new Book();
+$b->setTitle("Brave New World");
+$b->setPublisher($p);
+$b->setAuthor($a);
+
+$b->save(); // so that auto-increment IDs are created
+
+$bcopy = $b->copy();
+var_export($bcopy->getId() == $b->getId()); // FALSE
+var_export($bcopy->getAuthorId() == $b->getAuthorId()); // TRUE
+var_export($bcopy->getAuthor() === $b->getAuthor()); // TRUE
+?>
+}}}
+
+== Deep Copies ==
+
+By calling {{{copy()}}} with a {{{TRUE}}} parameter, Propel will create a deep copy of the object; this means that any related objects will also be copied.
+
+To continue with example from above:
+
+{{{
+#!php
+copy(true);
+var_export($bcopy->getId() == $b->getId()); // FALSE
+var_export($bcopy->getAuthorId() == $b->getAuthorId()); // FALSE
+var_export($bcopy->getAuthor() === $b->getAuthor()); // FALSE
+?>
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Customizing-Build.txt b/3rd_party/php/propel/docs/cookbook/Customizing-Build.txt
new file mode 100644
index 000000000..acfe99406
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Customizing-Build.txt
@@ -0,0 +1,153 @@
+= Customizing Build =
+It is possible to customize the Propel build process by overriding values in your propel __build.properties__ file. For maximum flexibility, you can even create your own Phing __build.xml__ file.
+
+== Customizing the build.properties ==
+The easiest way to customize your Propel build is to simply specify build properties in your project's __build.properties__ file.
+
+=== Understanding Phing build properties ===
+''Properties'' are essentially variables. These variables can be specified on the commandline or in ''properties files''.
+
+For example, here's how a property might be specified on the commandline:
+{{{
+$> phing -Dpropertyname=value
+}}}
+
+More typically, properties are stored in files and loaded by Phing. For those not familiar with Java properties files, these files look like PHP INI files; the main difference is that values in properties files can be references to other properties (a feature that will probably exist in in INI files in PHP 5.1).
+
+'''Importantly:''' properties, once loaded, are not overridden by properties with the same name unless explicitly told to do so. In the Propel build process, the order of precedence for property values is as follows:
+ 1. Commandline properties
+ 1. Project __build.properties__
+ 1. Top-level __build.properties__
+ 1. Top-level __default.properties__
+
+This means, for example, that values specified in the project's __build.properties__ files will override those in the top-level __build.properties__ and __default.properties__ files.
+
+=== Changing values ===
+To get an idea of what you can modify in Propel, simply look through the __build.properties__ and __default.properties__ files.
+
+''Note, however, that some of the current values exist for legacy reasons and will be cleaned up in Propel 1.1.''
+
+==== New build output directories ====
+This can easily be customized on a project-by-project basis. For example, here is a __build.properties__ file for the ''bookstore ''project that puts the generated classes in __/var/www/bookstore/classes__ and puts the generated SQL in __/var/www/bookstore/db/sql__:
+{{{
+propel.project = bookstore
+propel.database = sqlite
+propel.database.url = sqlite://localhost/./test/bookstore.db
+propel.targetPackage = bookstore
+
+# directories
+prope.output.dir = /var/www/bookstore
+propel.php.dir = ${propel.output.dir}/classes
+propel.phpconf.dir = ${propel.output.dir}/conf
+propel.sql.dir = ${propel.output.dir}/db/sql
+}}}
+
+The ''targetPackage'' property is also used in determining the path of the generated classes. In the example above, the __Book.php__ class will be located at __/var/www/bookstore/classes/bookstore/Book.php__. You can change this __bookstore__ subdir by altering the ''targetPackage'' property:
+{{{
+propel.targetPackage = propelom
+}}}
+
+Now the class will be located at __/var/www/bookstore/classes/propelom/Book.php__
+
+''Note that you can override the targetPackage property by specifying a package="" attribute in the tag or even the
tag of the schema.xml.''
+
+== Creating a custom build.xml file ==
+
+If you want to make more major changes to the way the build script works, you can setup your own Phing build script. This actually is not a very scary task, and once you've managed to create a Phing build script, you'll probably want to create build targets for other aspects of your project (e.g. running batch unit tests is now supported in Phing 2.1-CVS).
+
+To start with, I suggest taking a look at the __build-propel.xml__ script (the build.xml script is just a wrapper script). Note, however, that the __build-propel.xml__ script does a lot & has a lot of complexity that is designed to make it easy to configure using properties (so, don't be scared).
+
+Without going into too much detail about how Phing works, the important thing is that Phing build scripts XML and they are grouped into ''targets'' which are kinda like functions. The actual work of the scripts is performed by ''tasks'', which are PHP5 classes that extend the base Phing ''Task'' class and implement its abstract methods. Propel provides some Phing tasks that work with templates to create the object model.
+
+=== Step 1: register the needed tasks ===
+
+The Propel tasks must be registered so that Phing can find them. This is done using the '''' tag. You can see this near the top of the __build-propel.xml__ file.
+
+For example, here is how we register the ''propel-om'' task, which is the task that creates the PHP classes for your object model:
+{{{
+
+}}}
+
+Simple enough. Phing will now associate the '''' tag with the ''PropelOMTask'' class, which it expects to find at __propel/phing/PropelOMTask.php__ (on your ''include_path''). If Propel generator classes are not on your ''include_path'', you can specify that path in your '''' tag:
+{{{
+
+}}}
+
+Or, for maximum re-usability, you can create a '''' object, and then reference it (this is the way __build-propel.xml__ does it):
+{{{
+
+
+
+
+
+}}}
+
+=== Step 2: invoking the new task ===
+
+Now that the '''' task has been registered with Phing, it can be invoked in your build file.
+{{{
+
+
+
+}}}
+
+In the example above, it's worth pointing out that the '''' task can actually transform multiple __schema.xml__ files, which is why there is a '''' sub-element. Phing ''filesets'' are beyond the scope of this HOWTO, but hopefully the above example is obvious enough.
+
+=== Step 3: putting it together into a build.xml file ===
+
+Now that we've seen the essential elements of our custom build file, it's time to look at how to assemble them into a working whole:
+{{{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+If that build script was named __build.xml__ then it could be executed by simply running ''phing'' in the directory where it is located:
+{{{
+$> phing om
+}}}
+
+Actually, specifying the ''om'' target is not necessary since it is the default.
+
+Refer to the __build-propel.xml__ file for examples of how to use the other Propel Phing tasks -- e.g. '''' for generating the DDL SQL, '''' for inserting the SQL, etc.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Existing-Database.txt b/3rd_party/php/propel/docs/cookbook/Existing-Database.txt
new file mode 100644
index 000000000..3505c0f42
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Existing-Database.txt
@@ -0,0 +1,95 @@
+= Working With Existing Databases =
+
+The following topics are targeted for developers who already have a working database solution in place, but would like to use Propel to work with the data. For this case, Propel provides a number of command-line utilities helping with migrations of data and data structures.
+
+== Working with Database Structures ==
+
+Propel uses an abstract XML schema file to represent databases (the [wiki:Documentation/1.5/Schema schema]). Propel builds the SQL specific to a database based on this schema. Propel also provides a way to reverse-engineer the generic schema file based on database metadata.
+
+=== Creating an XML Schema from a DB Structure ===
+
+To generate a schema file, create a new directory for your project & specify the connection information in your `build.properties` file for that project. For example, to create a new project, `legacyapp`, follow these steps:
+
+ 1. Create the `legacyapp` project directory anywhere on your filesystem:
+{{{
+> mkdir legacyapp
+> cd legacyapp
+}}}
+ 1. Create a `build.properties` file in `legacyapp/` directory with the DB connection parameters for your existing database, e.g.:
+{{{
+propel.project = legacyapp
+
+# The Propel driver to use for generating SQL, etc.
+propel.database = mysql
+
+# This must be a PDO DSN
+propel.database.url = mysql:dbname=legacyapp
+propel.database.user = root
+# propel.database.password =
+}}}
+ 1. Run the `reverse` task to generate the `schema.xml`:
+{{{
+> propel-gen reverse
+}}}
+ 1. Pay attention to any errors/warnings issued by Phing during the task execution and then examine the generated `schema.xml` file to make any corrections needed.
+ 1. '''You're done! ''' Now you have a `schema.xml` file in the `legacyapp/` project directory. You can now run the default Propel build to generate all the classes.
+
+The generated `schema.xml` file should be used as a guide, not a final answer. There are some datatypes that Propel may not be familiar with; also some datatypes are simply not supported by Propel (e.g. arrays in PostgreSQL). Unfamiliar datatypes will be reported as warnings and substituted with a default VARCHAR datatype.
+
+Tip: The reverse engineering classes may not be able to provide the same level of detail for all databases. In particular, metadata information for SQLite is often very basic since SQLite is a typeless database.
+
+=== Migrating Structure to a New RDBMS ===
+
+Because Propel has both the ability to create XML schema files based on existing database structures and to create RDBMS-specific DDL SQL from the XML schema file, you can use Propel to convert one database into another.
+
+To do this you would simply:
+ 1. Follow the steps above to create the `schema.xml` file from existing db.
+ 1. Then you would change the target database type and specify connection URL for new database in the project's `build.properties` file:
+{{{
+propel.database = pgsql
+propel.database.url = pgsql://unix+localhost/newlegacyapp
+}}}
+ 1. And then run the `sql` task to generate the new DDL:
+{{{
+> propel-gen sql
+}}}
+ 1. And (optionally) the `insert-sql` task to create the new database:
+{{{
+> propel-gen insert-sql
+}}}
+
+== Working with Database Data ==
+
+Propel also provides several tasks to facilitate data import/export. The most important of these are `datadump` and `datasql`. The first dumps data to XML and the second converts the XML data dump to a ready-to-insert SQL file.
+
+Tip: Both of these tasks require that you already have generated the `schema.xml` for your database.
+
+=== Dumping Data to XML ===
+
+Once you have created (or reverse-engineered) your `schema.xml` file, you can run the `datadump` task to dump data from the database into a `data.xml` file.
+
+{{{
+> propel-gen datadump
+}}}
+
+The task transfers database records to XML using a simple format, where each row is an element, and each column is an attribute. So for instance, the XML representation of a row in a `publisher` table:
+
+||'''publisher_id'''||'''name'''||
+||1||William Morrow||
+
+... is rendered in the `data.xml` as follows:
+{{{
+
+ ...
+
+ ...
+
+}}}
+
+=== Creating SQL from XML ===
+
+To create the SQL files from the XML, run the `datasql` task:
+{{{
+> propel-gen datasql
+}}}
+The generated SQL is placed in the `build/sql/` directory and will be inserted when you run the `insert-sql` task.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/LOB-Columns.txt b/3rd_party/php/propel/docs/cookbook/LOB-Columns.txt
new file mode 100644
index 000000000..d4b776d0b
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/LOB-Columns.txt
@@ -0,0 +1,73 @@
+= Working with LOB Columns =
+
+Propel uses PHP streams internally for storing ''Binary'' Locator Objects (BLOBs). This choice was made because PDO itself uses streams as a convention when returning LOB columns in a resultset and when binding values to prepared statements. Unfortunately, not all PDO drivers support this (see, for example, http://bugs.php.net/bug.php?id=40913); in those cases, Propel creates a {{{php://temp}}} stream to hold the LOB contents and thus provide a consistent API.
+
+Note that CLOB (''Character'' Locator Objects) are treated as strings in Propel, as there is no convention for them to be treated as streams by PDO.
+
+== Getting BLOB Values ==
+
+BLOB values will be returned as PHP stream resources from the accessor methods. Alternatively, if the value is NULL in the database, then the accessors will return the PHP value NULL.
+
+{{{
+#!php
+getCoverImage();
+if ($fp !== null) {
+ echo stream_get_contents($fp);
+}
+}}}
+
+== Setting BLOB Values ==
+
+When setting a BLOB column, you can either pass in a stream or the blob contents.
+
+=== Setting using a stream ===
+{{{
+#!php
+setCoverImage($fp);
+}}}
+
+=== Setting using file contents ===
+{{{
+#!php
+setCoverImage(file_get_contents("/path/to/file.ext"));
+}}}
+
+Regardless of which setting method you choose, the BLOB will always be represented internally as a stream resource -- ''and subsequent calls to the accessor methods will return a stream.''
+
+For example:
+{{{
+#!php
+setCoverImage(file_get_contents("/path/to/file.ext"));
+
+$fp = $media->getCoverImage();
+print gettype($fp); // "resource"
+}}}
+
+=== Setting BLOB columns and isModified() ===
+
+Note that because a stream contents may be externally modified, ''mutator methods for BLOB columns will always set the '''isModified()''' to report true'' -- even if the stream has the same identity as the stream that was returned.
+
+For example:
+{{{
+#!php
+getCoverImage();
+$media->setCoverImage($fp);
+
+var_export($media->isModified()); // TRUE
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Master-Slave.txt b/3rd_party/php/propel/docs/cookbook/Master-Slave.txt
new file mode 100644
index 000000000..2e28f4903
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Master-Slave.txt
@@ -0,0 +1,94 @@
+= Replication =
+
+Propel can be used in a master-slave replication environment. These environments are set up to improve the performance of web applications by dispatching the database-load to multiple database-servers. While a single master database is responsible for all write-queries, multiple slave-databases handle the read-queries. The slaves are synchronised with the master by a fast binary log (depending on the database).
+
+== Configuring Propel for Replication ==
+
+ * Set up a replication environment (see the Databases section below)
+ * Use the latest Propel-Version from SVN
+ * add a slaves-section to your {{{runtime-conf.xml}}} file
+ * verify the correct setup by checking the masters log file (should not contain "select ..." statements)
+
+You can configure Propel to support replication by adding a element with nested element(s) to your {{{runtime-conf.xml}}}.
+
+The section is at the same level as the master and contains multiple nested elements with the same information as the top-level (master) . It is recommended that they are numbered. The follwing example shows a slaves section with a several slave connections configured where "localhost" is the master and "slave-server1" and "slave-server2" are the slave-database connections.
+
+{{{
+#!xml
+
+
+
+ propel-bookstore
+ console
+ 7
+
+
+
+
+ sqlite
+
+ mysql:host=localhost;dbname=bookstore
+ testuser
+ password
+
+
+
+ mysql:host=slave-server1; dbname=bookstore
+ testuser
+ password
+
+
+ mysql:host=slave-server2; dbname=bookstore
+ testuser
+ password
+
+
+
+
+
+
+}}}
+
+== Implementation ==
+
+The replication functionality is implemented in the Propel connection configuration and initialization code and in the generated Peer and Object classes.
+
+=== Propel::getConnection() ===
+
+When requesting a connection from Propel ('''Propel::getConnection()'''), you can either specify that you want a READ connection (slave) or WRITE connection (master). Methods that are designed to perform READ operations, like the '''doSelect*()''' methods of your generated Peer classes, will always request a READ connection like so:
+{{{
+#!php
+query('SELECT * FROM my');
+/* ... */
+}}}
+
+=== Propel::setForceMasterConnection() ===
+
+You can force Propel to always return a WRITE (master) connection from '''Propel::getConnection()''' by calling '''Propel::setForceMasterConnection(true);'''. This can be useful if you must be sure that you are getting the most up-to-date data (i.e. if there is some latency possible between master and slaves).
+
+== Databases ==
+
+=== MySql ===
+
+http://dev.mysql.com/doc/refman/5.0/en/replication-howto.html
+
+== References ==
+
+ * Henderson Carl (2006): Building Scalable Web Sites. The Flickr Way. O'Reilly. ISBN-596-10235-6.
+
diff --git a/3rd_party/php/propel/docs/cookbook/Multi-Component.txt b/3rd_party/php/propel/docs/cookbook/Multi-Component.txt
new file mode 100644
index 000000000..1fba7e2af
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Multi-Component.txt
@@ -0,0 +1,297 @@
+= Multi-Component Data Model =
+
+Propel comes along with packaging capabilities that allow you to more easily integrate Propel into a packaged or modularized application.
+
+== Muliple Schemas ==
+
+You can use as many `schema.xml` files as you want. Schema files have to be named `(*.)schema.xml`, so names like `schema.xml`, `package1.schema.xml`, `core.package1.schema.xml` are all acceptable. These files ''have'' to be located in your project directory.
+
+Each schema file has to contain a `` element with a `name` attribute. This name references the connection settings to be used for this database (and configured in the `runtime-conf.xml`), so separated schemas can share a common database name.
+
+Whenever you call a propel build taks, Propel will consider all these schema files and build the classes (or the SQL) for all the tables.
+
+== Understanding Packages ==
+
+In Propel, a ''package'' represents a group of models. This is a convenient way to organize your code in a modularized way, since classes and SQL files of a given package are be grouped together and separated from the other packages. By carefully choosing the package of each model, applications end up in smaller, independent modules that are easier to manage.
+
+=== Package Cascade ===
+
+The package is defined in a configuration cascade. You can set it up for the whole project, for all the tables of a schema, or for a single table.
+
+For the whole project, the main package is set in the `build.properties`:
+
+{{{
+#!ini
+propel.targetPackage = my_project
+}}}
+
+By default, all the tables of all the schemas in the project use this package. However, you can override the package for a given `` by setting its `package` attribute:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+In this example, thanks to the `package` attribute, the tables are grouped into the following packages:
+
+ * `my_project.author` package: `author` table
+ * `my_project.book` package: `book` and `review` tables
+
+'''Warning''': If you separate tables related by a foreign key into separate packages (like `book` and `author` in this example), you must enable the `packageObjectModel` build property to let Propel consider other packages for relations.
+
+You can also override the `package` attribute at the `
` element level.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+This ends up in the following package:
+
+ * `my_project.author` package: `author` table
+ * `my_project.book` package: `book` table
+ * `my_project.review` package: `review` table
+
+Notice that tables can end up in separated packages even though they belong to the same schema file.
+
+'''Tip''': You can use dots in a package name to add more package levels.
+
+=== Packages and Generated Model Files ===
+
+The `package` attribute of a table translates to the directory in which Propel generates the Model classes for this table.
+
+For instance, if no `package` attribute is defined at the database of table level, Propel places all classes according to the `propel.targetPackage` from the `build.properties`:
+
+{{{
+build/
+ classes/
+ my_project/
+ om/
+ map/
+ Author.php
+ AuthorPeer.php
+ AuthorQuery.php
+ Book.php
+ BookPeer.php
+ BookQuery.php
+ Review.php
+ ReviewPeer.php
+ ReviewQuery.php
+}}}
+
+You can further tweak the location where Propel puts the created files by changing the `propel.output.dir` build property. By default this property is set to:
+
+{{{
+#!ini
+propel.output.dir = ${propel.project.dir}/build
+}}}
+
+You can change it to use any other directory as your build directory.
+
+If you set up packages for `` elements, Propel splits up the generated model classes into subdirectories named after the package attribute:
+
+{{{
+build/
+ classes/
+ my_project/
+ author/
+ om/
+ map/
+ Author.php
+ AuthorPeer.php
+ AuthorQuery.php
+ book/
+ om/
+ map/
+ Book.php
+ BookPeer.php
+ BookQuery.php
+ Review.php
+ ReviewPeer.php
+ ReviewQuery.php
+}}}
+
+And of course, if you specialize the `package` attribute per table, you can have one table use its own package:
+
+{{{
+build/
+ classes/
+ my_project/
+ author/
+ om/
+ map/
+ Author.php
+ AuthorPeer.php
+ AuthorQuery.php
+ book/
+ om/
+ map/
+ Book.php
+ BookPeer.php
+ BookQuery.php
+ review/
+ om/
+ map/
+ Review.php
+ ReviewPeer.php
+ ReviewQuery.php
+}}}
+
+=== Packages And SQL Files ===
+
+Propel also considers packages for SQL generation. In practice, Propel generates one SQL file per package. Each file contains the CREATE TABLE SQL statements necessary to create all the tables of a given package.
+
+So by default, all the tables end up in a single SQL file:
+
+{{{
+build/
+ sql/
+ schema.sql
+}}}
+
+If you specialize the `package` for each `` element, Propel uses it for SQL files:
+
+{{{
+build/
+ sql/
+ author.schema.sql // contains CREATE TABLE author
+ book.schema.sql // contains CREATE TABLE book and CREATE TABLE review
+}}}
+
+And, as you probably expect it, a package overridden at the table level also acocunts for an independent SQL file:
+
+{{{
+build/
+ sql/
+ author.schema.sql // contains CREATE TABLE author
+ book.schema.sql // contains CREATE TABLE book
+ review.schema.sql // contains CREATE TABLE review
+}}}
+
+== Understanding The packageObjectModel Build Property ==
+
+The `propel.packageObjectModel` build property enables the "packaged" build process. This modifies the build tasks behavior by joining `` elements of the same name - but keeping their packages separate. That allows to split a large schema into several files, regardless of foreign key dependencies, since Propel will join all schemas using the same database name.
+
+To switch this on, simply add the following line to the `build.properties` file in your project directory:
+{{{
+propel.packageObjectModel = true
+}}}
+
+== The Bookstore Packaged Example ==
+
+In the bookstore-packaged example you'll find the following schema files:
+
+ * author.schema.xml
+ * book.schema.xml
+ * club.schema.xml
+ * media.schema.xml
+ * publisher.schema.xml
+ * review.schema.xml
+ * log.schema.xml
+
+Each schema file has to contain a `` tag that has its `package` attribute set to the package name where ''all'' of the tables in this schema file/database belong to.
+
+For example, in the bookstore-packaged example the `author.schema.xml` contains the following `` tag:
+
+{{{
+
+}}}
+
+That means, that the Author OM classes will be created in a subdirectory `core/author/` of the build output directory.
+
+You can have more than one schema file that belong to one package. For example, in the the bookstore-packaged example both the `book.schema.xml` and `media.schema.xml` belong to the same package "core.book". The generated OM classes for these schemas will therefore end up in the same `core/book/` subdirectory.
+
+=== The OM build ===
+
+To run the packaged bookstore example build simply go to the `propel/test/fixtures/bookstore-packages/` directory and type:
+
+{{{
+../../../generator/bin/propel-gen om
+}}}
+
+This should run without any complaints. When you have a look at the projects/bookstore-packaged/build/classes directory, the following directory tree should have been created:
+{{{
+addon/
+ club/
+ BookClubList.php
+ BookClubListPeer.php
+ BookListRel.php
+ BookListRelPeer.php
+core/
+ author/
+ Author.php
+ AuthorPeer.php
+ book/
+ Book.php
+ BookPeer.php
+
+ Media.php
+ MediaPeer.php
+ publisher/
+ Publisher.php
+ PublisherPeer.php
+ review/
+ Review.php
+ ReviewPeer.php
+util/
+ log/
+ BookstoreLog.php
+ BookstoreLogPeer.php
+}}}
+
+(The additional subdirectories map/ and om/ in each of these directories have been omitted for clarity.)
+
+== The SQL build ==
+
+From the same schema files, run the SQL generation by calling:
+
+{{{
+../../../generator/bin/propel-gen sql
+}}}
+
+Then, have a look at the `build/sql/` directory: you will see that for each package (that is specified as a package attribute in the schema file database tags), one sql file has been created:
+
+ * addon.club.schema.sql
+ * core.author.schema.sql
+ * core.book.schema.sql
+ * core.publisher.schema.sql
+ * core.review.schema.sql
+ * util.log.schema.sql
+
+These files contain the CREATE TABLE SQL statements necessary for each package.
+
+When you now run the insert-sql task by typing:
+{{{
+../../../generator/bin/propel-gen insert-sql
+}}}
+these SQL statements will be executed on a SQLite database located in the Propel/generator/test/ directory.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Namespaces.txt b/3rd_party/php/propel/docs/cookbook/Namespaces.txt
new file mode 100644
index 000000000..705d5ed68
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Namespaces.txt
@@ -0,0 +1,133 @@
+= How to Use PHP 5.3 Namespaces =
+
+The generated model classes can use a namespace. It eases the management of large database models, and makes the Propel model classes integrate with PHP 5.3 applications in a clean way.
+
+== Namespace Declaration And Inheritance ==
+
+To define a namespace for a model class, you just need to specify it in a `namespace` attribute of the `
` element for a single table, or in the `` element to set the same namespace to all the tables.
+
+Here is an example schema using namespaces:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+The `` element defines a `namespace` attribute. The `book` and `author` tables inherit their namespace from the database, therefore the generated classes for these tables will be `\Bookstore\Book` and `\Bookstore\Author`.
+
+The `publisher` table defines a `namespace` attribute on ots own, which ''extends'' the database namespace. That means that the generated class will be `\Bookstore\Book\Publisher`.
+
+As for the `user` table, it defines an absolute namespace (starting with a backslash), which ''overrides'' the database namespace. The generated class for the `user` table will be `Admin\User`.
+
+'''Tip''': You can use subnamespaces (i.e. namespaces containing backslashes) in the `namespace` attribute.
+
+== Using Namespaced Models ==
+
+Namespaced models benefit from the Propel runtime autoloading just like the other model classes. You just need to alias them, or to use their fully qualified name.
+
+{{{
+#!php
+setAuthor($author);
+$book->save();
+}}}
+
+The namespace is used for the ActiveRecord class, but also for the Query and Peer classes. Just remember that when you use relation names ina query, the namespace should not appear:
+
+{{{
+#!php
+useBookQuery()
+ ->filterByPrice(array('max' => 10))
+ ->endUse()
+ ->findOne();
+}}}
+
+Related tables can have different namespaces, it doesn't interfere with the functionality provided by the object model:
+
+{{{
+#!php
+findOne();
+echo get_class($book->getPublisher());
+// \Bookstore\Book\Publisher
+}}}
+
+'''Tip''': Using namespaces make generated model code incompatible with versions of PHP less than 5.3. Beware that you will not be able to use your model classes in an older PHP application.
+
+== Using Namespaces As A Directory Structure ==
+
+In a schema, you can define a `package` attribute on a `` or a `
` tag to generate model classes in a subdirectory (see [wiki:Documentation/1.5/Multi-Component]). If you use namespaces to autoload your classes based on a SplClassAutoloader (see http://groups.google.com/group/php-standards), then you may find yourself repeating the `namespace` data in the `package` attribute:
+
+{{{
+#!xml
+
+}}}
+
+To avoid such repetitions, just set the `propel.namespace.autoPackage` setting to `true` in your `build.properties`:
+
+{{{
+#!ini
+propel.namespace.autoPackage = true
+}}}
+
+Now Propel will automatically create a `package` attribute, and therefore distribute model classes in subdirectories, based on the `namespace` attribute, and you can omit the manual `package` attribute in the schema:
+
+{{{
+#!xml
+
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/cookbook/Nested-Set.txt b/3rd_party/php/propel/docs/cookbook/Nested-Set.txt
new file mode 100644
index 000000000..8cf7fed7e
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Nested-Set.txt
@@ -0,0 +1,183 @@
+= !NestedSet support in Propel =
+
+'''Warning''': Since Propel 1.5, the support for nested sets was moved to the `nested_set` behavior. The method described here is deprecated.'''
+
+== Description ==
+
+With !NestedSet implementation, trees are stored using different approach in databases: [http://www.sitepoint.com/article/hierarchical-data-database]
+
+Nested Set implementation requires three dedicated fields in table structure
+
+ * left
+ * right
+
+Plus an optional fields for multi nested set support
+ * scope
+
+''NB: fields name are free and must be defined in schema.xml''
+
+To enable !NestedSet support in table, schema.xml must define some specific attributes:
+'''treeMode''' which must take the value '''!NestedSet'''
+
+{{{
+#!xml
+
+}}}
+
+Then, left and right field must be defined that way '''nestedSetLeftKey''' as a boolean value as '''nestedSetRightKey'''
+
+{{{
+#!xml
+
+
+}}}
+
+For multi nestedset support, an other column must be defined with boolean attribute '''treeScopeKey''' set to true
+{{{
+#!xml
+
+}}}
+
+And then, let's the propel generator automagically create all the needed model and stub classes.
+
+== !NestedSet usage in Propel ==
+
+''ex:''
+'''schema.xml''' extract
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+=== !NestedSet insertion ===
+{{{
+#!php
+setText('Google');
+$root->setLink('http://www.google.com');
+
+$root->makeRoot();
+$root->save();
+
+$menu = new Menu();
+$menu->setText('Google Mail');
+$menu->setLink('http://mail.google.com');
+$menu->insertAsLastChildOf($root);
+$menu->save();
+
+$child = new Menu();
+$child->setText('Google Maps');
+$child->setLink('http://maps.google.com');
+$child->insertAsLastChildOf($root);
+$child->save();
+
+$sibling = new Menu();
+$sibling->setText('Yahoo!');
+$sibling->setLink('http://www.yahoo.com');
+$sibling->insertAsNextSiblingOf($root);
+$sibling->save();
+
+$child = new Menu();
+$child->setText('Yahoo! Mail');
+$child->setLink('http://mail.yahoo.com');
+$child->insertAsLastChildOf($sibling);
+$child->save();
+}}}
+
+=== Multi !NestedSet insertion ===
+{{{
+#!php
+setText('Google');
+$root->setLink('http://www.google.com');
+
+$root->makeRoot();
+$root->setScopeIdValue(1); // Tree 1
+$root->save();
+
+$menu = new Menu();
+$menu->setText('Google Mail');
+$menu->setLink('http://mail.google.com');
+$menu->insertAsLastChildOf($root);
+$menu->save();
+
+// Create secund root node
+$root2 = new Menu();
+$root2->setText('Yahoo!');
+$root2->setLink('http://www.yahoo.com');
+
+$root2->makeRoot();
+$root2->setScopeIdValue(2); // Tree 2
+$root2->save();
+
+$menu = new Menu();
+$menu->setText('Yahoo! Mail');
+$menu->setLink('http://mail.yahoo.com');
+$menu->insertAsLastChildOf($root2);
+$menu->save();
+}}}
+
+=== Tree retrieval ===
+{{{
+#!php
+getDepth());
+ }
+
+ function endChildren() {
+ echo str_repeat("\t", $this->getDepth() - 1);
+ }
+}
+
+$menu = MenuPeer::retrieveTree($scopeId);
+$it = new myMenuOutput($menu);
+foreach($it as $m) {
+ echo $m->getText(), '[', $m->getLeftValue(), '-', $m->getRightValue(), "]\n";
+}
+}}}
+=== Tree traversal ===
+
+!NestetSet implementation use the [http://somabo.de/talks/200504_php_quebec_spl_for_the_masses.pdf SPL RecursiveIterator] as suggested by soenke
+
+== !NestedSet known broken behaviour ==
+
+=== Issue description ===
+For every changes applied on the tree, several entries in the database can be involved. So all already loaded nodes have to be refreshed with their new left/right values.
+
+=== InstancePool enabled ===
+In order to refresh all loaded nodes, an automatic internal call is made after each tree change to retrieve all instance in InstancePool and update them.
+And it works fine.
+
+=== InstancePool disabled ===
+When InstancePool is disabled, their is no way to retrieve references to all already loaded node and get them updated.
+So in most case, all loaded nodes are not updated and it leads to an inconsistency state.
+So, workaround is to do an explicit reload for any node you use after tree change.
+
+
diff --git a/3rd_party/php/propel/docs/cookbook/Runtime-Introspection.txt b/3rd_party/php/propel/docs/cookbook/Runtime-Introspection.txt
new file mode 100644
index 000000000..dce769231
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Runtime-Introspection.txt
@@ -0,0 +1,164 @@
+= Model Introspection At Runtime =
+
+In addition to the object and peer classes used to do C.R.U.D. operations, Propel generates an object mapping for your tables to allow runtime introspection.
+
+The intospection objects are instances of the map classes. Propel maps databases, tables, columns, validators, and relations into objects that you can easily use.
+
+== Retrieving a TableMap ==
+
+The starting point for runtime introspection is usually a table map. This objects stores every possible property of a table, as defined in the `schema.xml`, but accessible at runtime.
+
+To retrieve a table map for a table, use the `getTableMap()` static method of the related peer class. For instance, to retrieve the table map for the `book` table, just call:
+
+{{{
+#!php
+getName(); // 'table'
+echo $bookTable->getPhpName(); // 'Table'
+echo $bookTable->getPackage(); // 'bookstore'
+echo $bookTable->isUseIdGenerator(); // true
+}}}
+
+Tip: A TableMap object also references the `DatabaseMap` that contains it. From the database map, you can also retrieve other table maps using the table name or the table phpName:
+{{{
+#!php
+getDatabaseMap();
+$authorTable = $dbMap->getTable('author');
+$authorTable = $dbMap->getTablebyPhpName('Author');
+}}}
+
+To introspect the columns of a table, use any of the `getColumns()`, `getPrimaryKeys()`, and `getForeignKeys()` `TableMap` methods. They all return an array of `ColumnMap` objects.
+
+{{{
+#!php
+getColumns();
+foreach ($bookColumns as $column) {
+ echo $column->getName();
+}
+}}}
+
+Alternatively, if you know a column name, you can retrieve the corresponding ColumnMap directly using the of `getColumn($name)` method.
+
+{{{
+#!php
+getColumn('title');
+}}}
+
+The `DatabaseMap` object offers a shortcut to every `ColumnMap` object if you know the fully qualified column name:
+{{{
+#!php
+getColumn('book.TITLE');
+}}}
+
+== ColumnMaps ==
+
+A `ColumnMap` instance offers a lot of information about a table column. Check the following examples:
+
+{{{
+#!php
+getTableName(); // 'book'
+$bookTitleColumn->getTablePhpName(); // 'Book'
+$bookTitleColumn->getType(); // 'VARCHAR'
+$bookTitleColumn->getSize(); // 255
+$bookTitleColumn->getDefaultValue(); // null
+$bookTitleColumn->isLob(); // false
+$bookTitleColumn->isTemporal(); // false
+$bookTitleColumn->isEpochTemporal(); // false
+$bookTitleColumn->isNumeric(); // false
+$bookTitleColumn->isText(); // true
+$bookTitleColumn->isPrimaryKey(); // false
+$bookTitleColumn->isForeignKey(); // false
+$bookTitleColumn->hasValidators(); // false
+}}}
+
+`ColumnMap` objects also keep a reference to their parent `TableMap` object:
+
+{{{
+#!php
+getTable();
+}}}
+
+Foreign key columns give access to more information, including the related table and column:
+
+{{{
+#!php
+getColumn('publisher_id');
+echo $bookPublisherIdColumn->isForeignKey(); // true
+echo $bookPublisherIdColumn->getRelatedName(); // 'publisher.ID'
+echo $bookPublisherIdColumn->getRelatedTableName(); // 'publisher'
+echo $bookPublisherIdColumn->getRelatedColumnName(); // 'ID'
+$publisherTable = $bookPublisherIdColumn->getRelatedTable();
+$publisherRelation = $bookPublisherIdColumn->getRelation();
+}}}
+
+== RelationMaps ==
+
+To get an insight on all the relationships of a table, including the ones relying on a foreign key located in another table, you must use the `RelationMap` objects related to a table.
+
+If you know its name, you can retrieve a `RelationMap` object using `TableMap::getRelation($relationName)`. Note that the relation name is the phpName of the related table, unless the foreign key defines a phpName in the schema. For instance, the name of the `RelationMap` object related to the `book.PUBLISHER_ID` column is 'Publisher'.
+
+{{{
+#!php
+getRelation('Publisher');
+}}}
+
+alternatively, you can access a `RelationMap` from a foreign key column using `ColumnMap::getRelation()`, as follows:
+
+{{{
+#!php
+getColumn('publisher_id')->getRelation();
+}}}
+
+Once you have a `RelationMap` instance, inspect its properties using any of the following methods:
+
+{{{
+#!php
+getType(); // RelationMap::MANY_TO_ONE
+echo $publisherRelation->getOnDelete(); // 'SET NULL'
+$bookTable = $publisherRelation->getLocalTable();
+$publisherTable = $publisherRelation->getForeignTable();
+print_r($publisherRelation->getColumnMappings());
+ // array('book.PUBLISHER_ID' => 'publisher.ID')
+print_r(publisherRelation->getLocalColumns());
+ // array($bookPublisherIdColumn)
+print_r(publisherRelation->getForeignColumns());
+ // array($publisherBookIdColumn)
+}}}
+
+This also works for relationships referencing the current table:
+
+{{{
+#!php
+getRelation('Review');
+echo $reviewRelation->getType(); // RelationMap::ONE_TO_MANY
+echo $reviewRelation->getOnDelete(); // 'CASCADE'
+$reviewTable = $reviewRelation->getLocalTable();
+$bookTable = $reviewRelation->getForeignTable();
+print_r($reviewRelation->getColumnMappings());
+ // array('review.BOOK_ID' => 'book.ID')
+}}}
+
+To retrieve all the relations of a table, call `TableMap::getRelations()`. You can then iterate over an array of `RelationMap` objects.
+
+Tip: RelationMap objects are lazy-loaded, which means that the `TableMap` will not instanciate any relation object until you call `getRelations()`. This allows the `TableMap` to remain lightweight for when you don't use relationship introspection.
diff --git a/3rd_party/php/propel/docs/cookbook/Writing-Behavior.txt b/3rd_party/php/propel/docs/cookbook/Writing-Behavior.txt
new file mode 100755
index 000000000..33ceebc63
--- /dev/null
+++ b/3rd_party/php/propel/docs/cookbook/Writing-Behavior.txt
@@ -0,0 +1,424 @@
+= How to Write A Behavior =
+
+Behaviors are a good way to reuse code across models without requiring inheritance (a.k.a. horizontal reuse). This step-by-step tutorial explains how to port model code to a behavior, focusing on a simple example.
+
+In the tutorial "[http://propel.posterous.com/getting-to-know-propel-15-keeping-an-aggregat Keeping an Aggregate Column up-to-date]", posted in the [http://propel.posterous.com/ Propel blog], the `TotalNbVotes` property of a `PollQuestion` object was updated each time a related `PollAnswer` object was saved, edited, or deleted. This "aggregate column" behavior was implemented by hand using hooks in the model classes. To make it truly reusable, the custom model code needs to be refactored and moved to a Behavior class.
+
+== Boostrapping A Behavior ==
+
+A behavior is a class that can alter the generated classes for a table of your model. It must only extend the [browser:branches/1.5/generator/lib/model/Behavior.php `Behavior`] class and implement special "hook" methods. Here is the class skeleton to start with for the `aggregate_column` behavior:
+
+{{{
+#!php
+ null,
+ );
+}
+}}}
+
+Save this class in a file called `AggregateColumnBehavior.php`, and set the path for the class file in the project `build.properties` (just replace directory separators with dots). Remember that the `build.properties` paths are relative to the include path:
+
+{{{
+#!ini
+propel.behavior.aggregate_column.class = path.to.AggregateColumnBehavior
+}}}
+
+Test the behavior by adding it to a table of your model, for instance to a `poll_question` table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+}}}
+
+Rebuild your model, and check the generated `PollQuestionTableMap` class under the `map` subdirectory of your build class directory. This class carries the structure metadata for the `PollQuestion` ActiveRecord class at runtime. The class should feature a `getBehaviors()` method as follows, proving that the behavior was correctly applied:
+
+{{{
+#!php
+ array('name' => 'total_nb_votes', ),
+ );
+ } // getBehaviors()
+}
+}}}
+
+== Adding A Column ==
+
+The behavior works, but it still does nothing at all. Let's make it useful by allowing it to add a column. In the `AggregateColumnBehavior` class, just implement the `modifyTable()` method with the following code:
+
+{{{
+#!php
+getTable();
+ if (!$columnName = $this->getParameter('name')) {
+ throw new InvalidArgumentException(sprintf(
+ 'You must define a \'name\' parameter for the \'aggregate_column\' behavior in the \'%s\' table',
+ $table->getName()
+ ));
+ }
+ // add the aggregate column if not present
+ if(!$table->containsColumn($columnName)) {
+ $table->addColumn(array(
+ 'name' => $columnName,
+ 'type' => 'INTEGER',
+ ));
+ }
+ }
+}
+}}}
+
+This method shows that a behavior class has access to the `` defined for it in the `schema.xml` through the `getParameter()` command. Behaviors can also always access the `Table` object attached to them, by calling `getTable()`. A `Table` can check if a column exists and add a new one easily. The `Table` class is one of the numerous generator classes that serve to describe the object model at buildtime, together with `Column`, `ForeignKey`, `Index`, and a lot more classes. You can find all the buildtime model classes under the [browser:branches/1.5/generator/lib/model generator/lib/model] directory.
+
+'''Tip''': Don't mix up the ''runtime'' database model (`DatabaseMap`, `TableMap`, `ColumnMap`, `ValidatorMap`, `RelationMap`) with the ''buildtime'' database model (`Database`, `Table`, `Column`, `Validator`, etc.). The buildtime model is very detailed, in order to ease the work of the builders that write the ActiveRecord and Query classes. On the other hand, the runtime model is optimized for speed, and carries minimal information to allow correct hydration and binding at runtime. Behaviors use the buildtime object model, because they are run at buildtime, so they have access to the most powerful model.
+
+Now rebuild the model and the SQL, and sure enough, the new column is there. `BasePollQuestion` offers a `getTotalNbVotes()` and a `setTotalNbVotes()` method, and the table creation SQL now includes the additional `total_nb_votes` column:
+
+{{{
+#!sql
+DROP TABLE IF EXISTS poll_question;
+CREATE TABLE poll_question
+(
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ title VARCHAR(100),
+ total_nb_votes INTEGER,
+ PRIMARY KEY (id)
+)Type=InnoDB;
+}}}
+
+'''Tip''': The behavior only adds the column if it's not present (`!$table->containsColumn($columnName)`). So if a user needs to customize the column type, or any other attribute, he can include a `` tag in the table with the same name as defined in the behavior, and the `modifyTable()` will then skip the column addition.
+
+== Adding A Method To The ActiveRecord Class ==
+
+In the previous post, a method of the ActiveRecord class was in charge of updating the `total_nb_votes` column. A behavior can easily add such methods by implementing the `objectMethods()` method:
+
+{{{
+#!php
+addUpdateAggregateColumn();
+ return $script;
+ }
+
+ protected function addUpdateAggregateColumn()
+ {
+ $sql = sprintf('SELECT %s FROM %s WHERE %s = ?',
+ $this->getParameter('expression'),
+ $this->getParameter('foreign_table'),
+ $this->getParameter('foreign_column')
+ );
+ $table = $this->getTable();
+ $aggregateColumn = $table->getColumn($this->getParameter('name'));
+ $columnPhpName = $aggregateColumn->getPhpName();
+ $localColumn = $table->getColumn($this->getParameter('local_column'));
+ return "
+/**
+ * Updates the aggregate column {$aggregateColumn->getName()}
+ *
+ * @param PropelPDO \$con A connection object
+ */
+public function update{$columnPhpName}(PropelPDO \$con)
+{
+ \$sql = '{$sql}';
+ \$stmt = \$con->prepare(\$sql);
+ \$stmt->execute(array(\$this->get{$localColumn->getPhpName()}()));
+ \$this->set{$columnPhpName}(\$stmt->fetchColumn());
+ \$this->save(\$con);
+}
+";
+ }
+}
+}}}
+
+The ActiveRecord class builder expects a string in return to the call to `Behavior::objectMethods()`, and appends this string to the generated code of the ActiveRecord class. Don't bother about indentation: builder classes know how to properly indent a string returned by a behavior. A good rule of thumb is to create one behavior method for each added method, to provide better readability.
+
+Of course, the schema must be modified to supply the necessary parameters to the behavior:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Now if you rebuild the model, you will see the new `updateTotalNbVotes()` method in the generated `BasePollQuestion` class:
+
+{{{
+#!php
+prepare($sql);
+ $stmt->execute(array($this->getId()));
+ $this->setTotalNbVotes($stmt->fetchColumn());
+ $this->save($con);
+ }
+}
+}}}
+
+Behaviors offer similar hook methods to allow the addition of methods to the query classes (`queryMethods()`) and to the peer classes (`peerMethods()`). And if you need to add attributes, just implement one of the `objectAttributes()`, `queryAttributes()`, or `peerAttributes()` methods.
+
+== Using a Template For Generated Code ==
+
+The behavior's `addUpdateAggregateColumn()` method is somehow hard to read, because of the large string containing the PHP code canvas for the added method. Propel behaviors can take advantage of Propel's simple templating system to use an external file as template for the code to insert.
+
+Let's refactor the `addUpdateAggregateColumn()` method to take advantage of this feature:
+
+{{{
+#!php
+getParameter('expression'),
+ $this->getParameter('foreign_table'),
+ $this->getParameter('foreign_column')
+ );
+ $table = $this->getTable();
+ $aggregateColumn = $table->getColumn($this->getParameter('name'));
+ return $this->renderTemplate('objectUpdateAggregate', array(
+ 'aggregateColumn' => $aggregateColumn,
+ 'columnPhpName' => $aggregateColumn->getPhpName(),
+ 'localColumn' => $table->getColumn($this->getParameter('local_column')),
+ 'sql' => $sql,
+ ));
+ }
+}
+}}}
+
+The method no longer returns a string created by hand, but a ''rendered template''. Propel templates are simple PHP files executed in a sandbox - they have only access to the variables declared as second argument of the `renderTemplate()` call.
+
+Now create a `templates/` directory in the same directory as the `AggregateColumnBehavior` class file, and add in a `objectUpdateAggregate.php` file with the following code:
+
+{{{
+#!php
+/**
+ * Updates the aggregate column getName() ?>
+ *
+ * @param PropelPDO $con A connection object
+ */
+public function update(PropelPDO $con)
+{
+ $sql = '';
+ $stmt = $con->prepare($sql);
+ $stmt->execute(array($this->getgetPhpName() ?>()));
+ $this->set($stmt->fetchColumn());
+ $this->save($con);
+}
+}}}
+
+No need to escape dollar signs anymore: this syntax allows for a cleaner separation, and is very convenient for large behaviors.
+
+== Adding Another Behavior From A Behavior ==
+
+This is where it's getting tricky. In the [http://propel.posterous.com/getting-to-know-propel-15-keeping-an-aggregat blog post] describing the column aggregation technique, the calls to the `updateTotalNbVotes()` method come from the `postSave()` and `postDelete()` hooks of the `PollAnswer` class. But the current behavior is applied to the `poll_question` table, how can it modify the code of a class based on another table?
+
+The short answer is: it can't. To modify the classes built for the `poll_answer` table, a behavior must be registered on the `poll_answer` table. But a behavior is just like a column or a foreign key: it has an object counterpart in the buildtime database model. So the trick here is to modify the `AggregateColumnBehavior::modifyTable()` method to ''add a new behavior'' to the foreign table. This second behavior will be in charge of implementing the `postSave()` and `postDelete()` hooks of the `PollAnswer` class.
+
+{{{
+#!php
+getDatabase()->getTable($this->getParameter('foreign_table'));
+ if (!$foreignTable->hasBehavior('concrete_inheritance_parent')) {
+ require_once 'AggregateColumnRelationBehavior.php';
+ $relationBehavior = new AggregateColumnRelationBehavior();
+ $relationBehavior->setName('aggregate_column_relation');
+ $relationBehavior->addParameter(array(
+ 'name' => 'foreign_table',
+ 'value' => $table->getName()
+ ));
+ $relationBehavior->addParameter(array(
+ 'name' => 'foreign_column',
+ 'value' => $this->getParameter('name')
+ ));
+ $foreignTable->addBehavior($relationBehavior);
+ }
+ }
+}
+}}}
+
+In practice, everything now happens as if the `poll_answer` had its own behavior:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Adding a behavior to a `Table` instance, as well as adding a `Parameter` to a `Behavior` instance, is quite straightforward. And since the second behavior class file is required in the `modifyTable()` method, there is no need to add a path for it in the `build.properties`.
+
+== Adding Code For Model Hooks ==
+
+The new `AggregateColumnRelationBehavior` is yet to write. It must implement a call to `PollQuestion::updateTotalNbVotes()` in the `postSave()` and `postDelete()` hooks.
+
+Adding code to hooks from a behavior is just like adding methods: add a method with the right hook name returning a code string, and the code will get appended at the right place. Unsurprisingly, the behavior hook methods for `postSave()` and `postDelete()` are called `postSave()` and `postDelete()`:
+
+{{{
+#!php
+ null,
+ 'foreignColumn' => null,
+ );
+
+ public function postSave()
+ {
+ $table = $this->getTable();
+ $foreignTable = $table->getDatabase()->getTable($this->getParameter('foreign_table'));
+ $foreignColumn = $foreignTable->getColumn($this->getParameter('foreign_column'));
+ $foreignColumnPhpName = $foreignColumn->getPhpName();
+ return "\$this->updateRelated{$foreignColumnPhpName}(\$con)";
+ }
+
+ public function postDelete()
+ {
+ return $this->postSave();
+ }
+
+ public function objectMethods()
+ {
+ $script = '';
+ $script .= $this->addUpdateRelatedAggregateColumn();
+ return $script;
+ }
+
+ protected function addUpdateRelatedAggregateColumn()
+ {
+ $table = $this->getTable();
+ $foreignTable = $table->getDatabase()->getTable($this->getParameter('foreign_table'));
+ $foreignTablePhpName = foreignTable->getPhpName();
+ $foreignColumn = $foreignTable->getColumn($this->getParameter('foreign_column'));
+ $foreignColumnPhpName = $foreignColumn->getPhpName();
+ return "
+/**
+ * Updates an aggregate column in the foreign {$foreignTable->getName()} table
+ *
+ * @param PropelPDO \$con A connection object
+ */
+protected function updateRelated{$foreignColumnPhpName}(PropelPDO \$con)
+{
+ if (\$parent{$foreignTablePhpName} = \$this->get{$foreignTablePhpName}()) {
+ \$parent{$foreignTablePhpName}->update{$foreignColumnPhpName}(\$con);
+ }
+}
+";
+ }
+}
+}}}
+
+The `postSave()` and `postDelete()` behavior hooks will not add code to the ActiveRecord `postSave()` and `postDelete()` methods - to allow users to further implement these methods - but instead it adds code directly to the `save()` and `delete()` methods, inside a transaction. Check the generated `BasePollAnswer` class for the added code in these methods:
+
+{{{
+#!php
+updateRelatedTotalNbVotes($con);
+}}}
+
+You will also see the new `updateRelatedTotalNbVotes()` method added by `AggregateColumnBehavior::objectMethods()`:
+
+{{{
+#!php
+getPollQuestion()) {
+ $parentPollQuestion->updateTotalNbVotes($con);
+ }
+}
+}}}
+
+== What's Left ==
+
+These are the basics of behavior writing: implement one of the methods documented in the [wiki:Documentation/1.5/Behaviors#WritingaBehavior behaviors chapter] of the Propel guide, and return strings containing the code to be added to the ActiveRecord, Query, and Peer classes. In addition to the behavior code, you should always write unit tests - all the behaviors bundled with Propel have full unit test coverage. And to make your behavior usable by others, documentation is highly recommended. Once again, Propel core behaviors are fully documented, to let users understand the behavior usage without having to peek into the code.
+
+As for the `AggregateColumnBehavior`, the job is not finished. The [http://propel.posterous.com/getting-to-know-propel-15-keeping-an-aggregat blog post] emphasized the need for hooks in the Query class, and these are not yet implemented in the above code. Besides, the post kept quiet about one use case that left the aggregate column not up to date (when a question is detached from a poll without deleting it). Lastly, the parameters required for this behavior are currently a bit verbose, especially concerning the need to define the foreign table and the foreign key - this could be simplified thanks to the knowledge of the object model that behaviors have.
+
+All this is left to the reader as an exercise. Fortunately, the final behavior is part of the Propel core behaviors, so the [browser:branches/1.5/generator/lib/behavior/aggregate_column code], [browser:branches/1.5/test/testsuite/generator/behavior/aggregate_column unit tests], and [wiki:Documentation/1.5/Behaviors/aggregate_column documentation] are all ready to help you to further understand the power of Propel's behavior system.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/guide/01-Installation.txt b/3rd_party/php/propel/docs/guide/01-Installation.txt
new file mode 100644
index 000000000..f5ca02442
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/01-Installation.txt
@@ -0,0 +1,164 @@
+= Installing Propel =
+
+[[PageOutline]]
+
+Propel is available as a [http://pear.php.net/manual/en/installation.getting.php PEAR] package, as a "traditional" tgz or zip package, and as a checkout from a Subversion repository. Whatever installation method you may choose, getting Propel to work is pretty straightforward.
+
+== Prerequisites ==
+
+Propel requirements are very light, allowing it to run on most PHP platforms:
+
+ * [http://www.php.net/ PHP 5.2.4] or newer, with the DOM (libxml2) module enabled
+ * A supported database (MySQL, MS SQL Server, PostgreSQL, SQLite, Oracle)
+
+'''Tip''': Propel uses the PDO and SPL components, which are bundled and enabled by default in PHP5.
+
+== Propel Components ==
+
+The Propel library is made of two components: a '''generator''', and a '''runtime library'''. These components are not co-dependent, and can be installed independently from each other.
+
+The generator is needed to build the object model, but is not required for running applications that use Propel.
+
+The runtime classes provide the shared functionality that is used by the Propel-generated object model classes. These are necessary to run applications that use Propel to access the database.
+
+Usually, both the generator and the runtime components are installed on development environments, while the actual test or production servers need only the runtime components installed. For your first contact with Propel, just install both.
+
+== Installing Propel ==
+
+=== Installing Propel From PEAR ===
+
+In order to install the Propel packages, you must add the `pear.propelorm.org` channel to your PEAR environment. Once the channel is discovered, you can install the generator package, or the runtime package, or both. Use the '-a' option to let PEAR download and install dependencies.
+
+{{{
+#!sh
+> pear channel-discover pear.propelorm.org
+> pear install -a propel/propel_generator
+> pear install -a propel/propel_runtime
+}}}
+
+Propel is now installed, and you can test it by following the instructions of the '''Testing Propel Installation''' section at the end of this page.
+
+Tip: If you want to install non-stable versions of Propel, change your `preferred_state` PEAR environment variable before installoing the Propel packages. Valid states include 'stable', 'beta', 'alpha', and 'devel':
+
+{{{
+#!sh
+> pear config-set preferred_state beta
+}}}
+
+=== Dependencies for Tarball and Subversion Versions ===
+
+The Propel generator uses [http://phing.info/ Phing 2.3.3] to manage command line tasks; both the generator and the runtime classes use [http://pear.php.net/package/Log/ PEAR Log] to log events.
+
+If you choose to install Propel via PEAR, these components will be automatically installed as dependencies. If you choose to install Propel from a tarball or a Subversion checkout, you'll have to install them manually:
+
+{{{
+#!sh
+> pear channel-discover pear.phing.info
+> pear install phing/phing
+> pear install Log
+}}}
+
+Refer to their respective websites for alternative installation strategies for Phing and Log.
+
+=== Installing Propel From Subversion ===
+
+Installing from SVN trunk ensures that you have the most up-to-date source code.
+
+{{{
+#!sh
+> svn checkout http://svn.propelorm.org/branches/1.5 /usr/local/propel
+}}}
+
+This will export both the generator and runtime components to your local `propel` directory. In addition, you'll also get Propel documentation and unit tests - that's why this method is the preferred installation method for Propel contributors.
+
+Once this is done, you'll need to setup your PHP environment to use this library - see the '''Setting Up PHP for Propel''' section below.
+
+Note: `branches/1.5` is currently more uptodate code than `trunk`; trunk is what will become 2.0, however it has had very little work done to it in a long time.
+
+=== Installing Propel From a Tarball ===
+
+Download a tarball of each of the Propel components from the Propel website, and uncompress them into the location that best suits your need. For instance, in Linux:
+
+{{{
+#!sh
+> cd /usr/local
+> mkdir propel
+> cd propel
+> wget http://pear.propelorm.org/get/propel_generator-1.5.0.tgz
+> tar zxvf propel_generator-1.5.0.tgz
+> wget http://pear.propelorm.org/get/propel_runtime-1.5.0.tgz
+> tar zxvf propel_runtime-1.5.0.tgz
+}}}
+
+Once this is done, you'll need to setup your PHP environment to use this library.
+
+== Setting Up PHP for the Propel Generator ==
+
+The following instructions are only required if you installed Propel from a tarball, or from Subversion.
+
+The Propel generator component bundles a `propel-gen` sh script (and a `propel-gen.bat` script for Windows). This script simplifies the commandline invocation of the Propel generator by hiding any references to Phing.
+
+You can call it directly from the command line:
+
+{{{
+#!sh
+> /usr/local/propel/generator/bin/propel-gen
+}}}
+
+In order to allow an easier execution the script, you can also:
+
+ * add the propel generator's `bin/` directory to your PATH,
+ * or copy the `propel-gen` script to a location on your PAH,
+ * or (on Linux systems) create a symlink. For example:
+
+{{{
+#!sh
+> cd /usr/local/bin
+> ln -s /usr/local/propel/generator/bin/propel-gen propel-gen
+}}}
+
+== Testing Propel Installation ==
+
+You can test that the '''Propel generator''' component is properly installed by calling the `propel-gen` script from the CLI:
+
+{{{
+#!sh
+> propel-gen
+}}}
+
+The script should output a few lines before displaying a 'BUILD FAILED' message, which is normal - you haven't defined a database model yet.
+
+You can test that the '''Propel runtime''' component is properly installed by requiring the `Propel.php` script, as follows:
+
+{{{
+#!php
+` tag:
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+The `name` attribute defines the name of the connection that Propel uses for the tables in this schema. It is not necessarily the name of the actual database. In fact, Propel uses a second file to link a connection name with real connection settings (like databae name, user and password). This `runtime-conf.xml` file will be explained later in this chapter.
+
+The `defaultIdMethod` attribute indicates that the tables in this schema use the database's "native" auto-increment/sequence features to handle id columns that are set to auto-increment.
+
+'''Tip''': You can define several schemas for a single project. Just make sure that each of the schema filenames end with `schema.xml`.
+
+=== Tables And Columns ===
+
+Within the `` tag, Propel expects one `
` tag for each table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+This time, the `name` attributes are the real table names. The `phpName` is the name that Propel will use for the generated PHP class. By default, Propel uses a CamelCase version of the table name as its phpName - that means that you could omit the `phpName` attribute in the example above.
+
+Within each set of `
` tags, define the columns that belong to that table:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Each column has a `name` (the one used by the database), and an optional `phpName` attribute. Once again, the Propel default behavior is to use a CamelCase version of the `name` as `phpName` when not specified.
+
+Each column also requires a `type`. The XML schema is database agnostic, so the column types and attributes are probably not exactly the same as the one you use in your own database. But Propel knows how to map the schema types with SQL types for many database vendors. Existing Propel column types are boolean, tinyint, smallint, integer, bigint, double, float, real, decimal, char, varchar, longvarchar, date, time, timestamp, blob, and clob. Some column types use a `size` (like `varchar` and `int`), some have unlimited size (`longvarchar`, `clob`, `blob`).
+
+As for the other column attributes, `required`, `primaryKey`, and `autoIncrement`, they mean exactly what their names suppose.
+
+'''Tip''': Propel supports namespaces (for PHP > 5.3). If you specify a `namespace` attribute in a `
` element, the generated PHP classes for this table will use this namespace.
+
+=== Foreign Keys ===
+
+A table can have several `` tags, describing foreign keys to foreign tables. Each `` tag consists of one or more mappings between a local column and a foreign column.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+A foreign key represents a relationship. Just like a table or a column, a relationship has a `phpName`. By default, Propel uses the `phpName` of the foreign table as the `phpName` of the relation. The `refPhpName` defines the name of the relation as seen from the foreign table.
+
+There are many more attributes and elements available to describe a datamodel. Propel's documentation provides a complete [wiki:Documentation/1.5/Schema reference of the schema syntax], together with a [source:branches/1.5/generator/resources/dtd/database.dtd DTD] and a [source:branches/1.5/generator/resources/xsd/database.xsd XSD] schema for its validation.
+
+== Building The Model ==
+
+=== Setting Up Build Configuration ===
+
+The build process is highly customizable. Whether you need the generated classes to inherit one of your classes rather than Propel's base classes, or to enable/disable some methods in the generated classes, pretty much every customization is possible. Of course, Propel provides sensible defaults, so that you actually need to define only two settings for the build process to start: the RDBMS you are going to use, and a name for your project.
+
+Propel expects the build configuration to be stored in a file called `build.properties`, and stored at the same level as the `schema.xml`. Here is an example for a MySQL database:
+
+{{{
+#!ini
+# Database driver
+propel.database = mysql
+
+# Project name
+propel.project = bookstore
+}}}
+
+Use your own database vendor driver, chosen among pgsql, mysql, sqlite, mssql, and oracle.
+
+You can learn more about the available build settings and their possible values in the [wiki:Documentation/1.5/BuildConfiguration build configuration reference].
+
+=== Using the `propel-gen` Script To Build The Model ===
+
+The Propel generator uses the `propel-gen` script, as seen in the previous chapter. This executable expects a command name as its argument.
+
+Open a terminal and browse to the `bookstore/` directory, where you saved the two previous files (`schema.xml`, and `build.properties`). Then use the `propel-gen` script to call the "Object Model generator" command using its shortcut - "om":
+
+{{{
+> cd /path/to/bookstore
+> propel-gen om
+}}}
+
+You should normally see a some colored lines appear in the terminal, logging all the class generation, and ending with "BUILD FINISHED". If not, look for red lines in the log and follow the directions in the error messages.
+
+=== Generated Object Model ===
+
+The "om" command added a new directory in the `bookstore/` project, called `build/`. The generated model classes are located under the `classes/bookstore/` subdirectory:
+
+{{{
+> cd /path/to/bookstore
+> cd build/classes/bookstore/
+> ls
+ om/
+ map/
+ Author.php
+ AuthorPeer.php
+ AuthorQuery.php
+ Book.php
+ BookPeer.php
+ BookQuery.php
+ Publisher.php
+ PublisherPeer.php
+ PublisherQuery.php
+}}}
+
+For every table in the database, Propel creates 3 PHP classes:
+
+ * a ''model'' class (e.g. `Book`), which represents a row in the database;
+ * a ''peer'' class (e.g. `BookPeer`), offering static constants and methods mostly for compatibility with previous Propel versions;
+ * a ''query'' class (e.g. `BookQuery`), used to operate on a table to retrieve and update rows
+
+Propel uses the `phpName` attribute of each table as the base for the PHP class names.
+
+All these classes are empty, but they inherit from `Base` classes that you will find under the `om/` directory:
+
+{{{
+#!php
+ cd /path/to/bookstore
+> propel-gen sql
+}}}
+
+The generated SQL definition can be found in the `build/sql/schema.sql` file. The code is optimized for the database driver defined in the `build.properties`.
+
+=== Using The SQL File ===
+
+Create the database and setup the access permissions using your favorite database client. For instance, to create the `my_db_name` database with MySQL, type:
+
+{{{
+> mysqladmin -u root -p create my_db_name
+}}}
+
+Now you can use the generated code directly:
+
+{{{
+> mysql -u root -p my_db_name < build/sql/schema.sql
+}}}
+
+'''Tip''': The `schema.sql` file will DROP any existing table before creating them, which will effectively erase your database.
+
+Depending on which RDBMS you are using, it may be normal to see some errors (e.g. "unable to DROP...") when you first run this command. This is because some databases have no way of checking to see whether a database object exists before attempting to DROP it (MySQL is a notable exception). It is safe to disregard these errors, and you can always run the script a second time to make sure that the errors are no longer present.
+
+=== Inserting SQL With `propel-gen` ===
+
+As an alternative to using the generated sql code directly, you can ask Propel to insert it directly into your database. Start by defining the database connection settings in the `build.properties`, as follows:
+
+{{{
+# Connection parameters
+propel.database.url = mysql:host=localhost;dbname=my_db_name
+propel.database.user = my_db_user
+propel.database.password = my_db_password
+
+# Other examples:
+# propel.database.url = sqlite:/path/to/bookstore.db
+# propel.database.url = pgsql:host=localhost dbname=my_db_name user=my_db_user password=my_db_password
+}}}
+
+The `propel.database.url` setting should be a PDO DSN (see the [http://www.php.net/pdo PDO documentation] for more information about vendor-specific DSN). The `user` and `password` are only necessary for the `mysql` and `oracle` drivers.
+
+Then use the `propel-gen` script with the "insert-sql" command to connect to the database and inject the generated SQL code:
+
+{{{
+> cd /path/to/bookstore
+> propel-gen insert-sql
+}}}
+
+== Runtime Connection Settings ==
+
+The database and PHP classes are now ready to be used. But they don't know yet how to communicate with each other at runtime. You must add a configuration file so that the generated object model classes and the shared Propel runtime classes can connect to the database, and log the Propel activity.
+
+=== Writing The XML Runtime Configuration ===
+
+Create a file called `runtime-conf.xml` at the root of the `bookstore` project, using the following content:
+{{{
+#!xml
+
+
+
+
+
+
+ mysql
+
+ mysql:host=localhost;dbname=my_db_name
+ my_db_user
+ my_db_password
+
+
+
+
+
+}}}
+
+Notice how the `id` attribute of the `` tag matches the connection name defined in the `` tag of the `schema.xml`. This is how Propel maps a database description to a connection.
+
+Replace the `` and the `` settings wit hthe ones of your database.
+
+See the [wiki:Documentation/1.5/RuntimeConfiguration runtime configuration reference] for a more detailed explanation of this file.
+
+'''Tip''': If you uncomment the `` section, Propel will attempt to instantiate the `Log` class (from the [http://pear.php.net/package/Log/ PEAR Log] package) with the specified parameters and use that to log queries. Propel's statement logging happens at the DEBUG level (7); errors and warnings are logged at the appropriate (non-debug) level.
+
+=== Building the Runtime Configuration ===
+
+For performance reasons, Propel prefers to use a PHP version of the connection settings rather than the XML file you just defined. So you must use the `propel-gen` script one last time to build the PHP version of the `runtime-conf.xml` configuration:
+
+{{{
+> cd /path/to/bookstore
+> propel-gen convert-conf
+}}}
+
+The resulting file can be found under `build/conf/bookstore-conf.php`, where "bookstore" is the name of the project you defined in `build.properties`.
+
+'''Tip''': As you saw, a Propel project setup requires that you call three commands with the `propel-gen` script: `om`, `sql`, and `convert-conf`. This is so usual that if you call the `propel-gen` script with no parameter, it will execute these three commands in a row:
+
+{{{
+> cd /path/to/bookstore
+> propel-gen
+}}}
+
+== Setting Up Propel ==
+
+This is the final step: initialize Propel in your PHP script. You may wish to do this step in an init or setup script that is included at the beginning of your PHP scripts.
+
+Here is a sample initialization file:
+
+{{{
+#!php
+setFirstName('Jane');
+$author->setLastName('austen');
+$author->save();
+}}}
+
+The column names used in the `setXXX()` methods correspond to the `phpName` attribute of the `` tag in your schema, or to a CamelCase version of the column name if the `phpName` is not set.
+
+In the background, the call to `save()` results in the following SQL being executed on the database:
+{{{
+#!sql
+INSERT INTO author (first_name, last_name) VALUES ('Jane', 'Austen');
+}}}
+
+== Reading Object Properties ==
+
+Propel maps the columns of a table into properties of the generated objects. For each property, you can use a generated getter to access it.
+
+{{{
+#!php
+getId(); // 1
+echo $author->getFirstName(); // 'Jane'
+echo $author->getLastName(); // 'austen'
+}}}
+
+The `id` column was set automatically by the database, since the `schema.xml` defines it as an `autoIncrement` column. The value is very easy to retrieve once the object is saved: just call the getter on the column phpName.
+
+These calls don't issue a database query, since the `Author` object is already loaded in memory.
+
+== Retrieving Rows ==
+
+Retrieving objects from the database, also referred to as ''hydrating'' objects, is essentially the process of executing a SELECT query against the database and populating a new instance of the appropriate object with the contents of each returned row.
+
+In Propel, you use the generated Query objects to select existing rows from the database.
+
+=== Retrieving by Primary Key ===
+
+The simplest way to retrieve a row from the database, is to use the generated `findPK()` method. It simply expects the value of the primary key of the row to be retrieved.
+
+{{{
+#!php
+findPK(1);
+// now $firstBook is an Author object, or NULL if no match was found.
+}}}
+
+This issues a simple SELECT SQL query. For instance, for MySQL:
+
+{{{
+#!sql
+SELECT author.id, author.first_name, author.last_name
+FROM `author`
+WHERE author.id = 1
+LIMIT 1;
+}}}
+
+When the primary key consists of more than one column, `findPK()` accepts multiple parameters, one for each primary key column.
+
+'''Tip''': Every generated Query objects offers a factory method called `create()`. This methods creates a new instance of the query, and allows you to write queries in a single line:
+
+{{{
+#!php
+findPK(1);
+}}}
+
+You can also select multiple objects based on their primary keys, by calling the generated `findPKs()` method. It takes an array of primary keys as a parameter:
+
+{{{
+#!php
+findPKs(array(1,2,3,4,5,6,7));
+// $selectedAuthors is a collection of Author objects
+}}}
+
+=== Querying the Database ===
+
+To retrieve rows other than by the primary key, use the Query's `find()` method.
+
+An empty Query object carries no condition, and returns all the rows of the table
+{{{
+#!php
+find();
+// $authors contains a collection of Author objects
+// one object for every row of the author table
+foreach($authors as $author) {
+ echo $author->getFirstName();
+}
+}}}
+
+To add a simple condition on a given column, use one of the generated `filterByXXX()` methods of the Query object, where `XXX` is a column phpName. Since `filterByXXX()` methods return the current query object, you can continue to add conditions or end the query with the result of the method call. For instance, to filter by first name:
+
+{{{
+#!php
+filterByFirstName('Jane')
+ ->find();
+}}}
+
+When you pass a value to a `filterByXXX()` method, Propel uses the column type to escape this value in PDO. This protects you from SQL injection risks.
+
+You can also easily limit and order the results on a query. Once again, the Query methods return the current Query object, so you can easily chain them:
+
+{{{
+#!php
+orderByLastName()
+ ->limit(10)
+ ->find();
+}}}
+
+`find()` always returns a collection of objects, even if there is only one result. If you know that you need a single result, use `findOne()` instead of `find()`. It will add the limit and return a single object instead of an array:
+
+{{{
+#!php
+filterByFirstName('Jane')
+ ->findOne();
+}}}
+
+'''Tip''': Propel provides magic methods for this simple use case. So you can write the above query as:
+
+{{{
+#!php
+findOneByFirstName('Jane');
+}}}
+
+The Propel Query API is very powerful. The next chapter will teach you to use it to add conditions on related objects. If you can't wait, jump to the [wiki:Documentation/1.5/ModelCriteria Query API reference].
+
+=== Using Custom SQL ===
+
+The `Query` class provides a relatively simple approach to constructing a query. Its database neutrality and logical simplicity make it a good choice for expressing many common queries. However, for a very complex query, it may prove more effective (and less painful) to simply use a custom SQL query to hydrate your Propel objects.
+
+As Propel uses PDO to query the underlying database, you can always write custom queries using the PDO syntax. For instance, if you have to use a sub-select:
+
+{{{
+#!php
+prepare($sql);
+$stmt->execute(array(':name' => 'Tolstoy');
+}}}
+
+With only a little bit more work, you can also populate `Book` objects from the resulting statement. Create a new `PropelObjectCollection` for the `Book` model, and call the `format()` method using the statement:
+
+{{{
+#!php
+setModelName('Book');
+$books = $coll->format($stmt);
+// $books contains a collection of Book objects
+}}}
+
+There are a few important things to remember when using custom SQL to populate Propel:
+ * The resultset columns must be numerically indexed
+ * The resultset must contain all columns in the object
+ * The resultset must have columns ''in the same order'' as they are defined in the `schema.xml` file
+
+== Updating Objects ==
+
+Updating database rows basically involves retrieving objects, modifying the contents, and then saving them. In practice, for Propel, this is a combination of what you've already seen in the previous sections:
+
+{{{
+#!php
+findOneByFirstName('Jane');
+$author->setLastName('Austen');
+$author->save();
+}}}
+
+Alternatively, you can update several rows based on a Query using the query object's `update()` method:
+
+{{{
+#!php
+filterByFirstName('Jane')
+ ->update(array('LastName' => 'Austen'));
+}}}
+
+This last method is better for updating several rows at once, or if you didn't retrieve the objects before.
+
+== Deleting Objects ==
+
+Deleting objects works the same as updating them. You can either delete an existing object:
+
+{{{
+#!php
+findOneByFirstName('Jane');
+$author->delete();
+}}}
+
+Or use the `delete()` method in the query:
+
+{{{
+#!php
+filterByFirstName('Jane')
+ ->delete();
+}}}
+
+'''Tip''': A deleted object still lives in the PHP code. It is marked as deleted and cannot be saved anymore, but you can still read its properties:
+
+{{{
+#!php
+isDeleted(); // true
+echo $author->getFirstName(); // 'Jane'
+}}}
+
+== Termination Methods ==
+
+The Query methods that don't return the current query object are called "Termination Methods". You've alread seen come of them: `find()`, `findOne()`, `update()`, `delete()`. There are two more termination methods that you should know about:
+
+{{{
+#!php
+count();
+// You could also count the number of results from a find(), but that would be less effective,
+// since it implies hydrating objects just to count them
+
+// paginate() returns a paginated list of results
+$authorPager = AuthorQuery::create()->paginate($page = 1, $maxPerPage = 10);
+// This method will compute an offset and a limit
+// based on the number of the page and the max number of results per page.
+// The result is a PropelModelPager object, over which you can iterate:
+foreach ($authorPager as $author) {
+ echo $author->getFirstName();
+}
+// a pager object gives more information
+echo $pager->getNbResults(); // total number of results if not paginated
+echo $pager->haveToPaginate(); // return true if the total number of results exceeds the maximum per page
+echo $pager->getFirstIndex(); // index of the first result in the page
+echo $pager->getLastIndex(); // index of the last result in the page
+$links = $pager->getLinks(5); // array of page numbers around the current page; useful to display pagination controls
+}}}
+
+== Collections And On-Demand Hydration ==
+
+The `find()` method of generated Model Query objects returns a `PropelCollection` object. You can use this object just like an array of model objects, iterate over it using `foreach`, access the objects by key, etc.
+
+{{{
+#!php
+limit(5)
+ ->find();
+foreach ($authors as $author) {
+ echo $authors->getFirstName();
+}
+}}}
+
+The advantage of using a collection instead of an array is that Propel can hydrate model objects on demand. Using this feature, you'll never fall short of memory when retrieving a large number of results. Available through the `setFormatter()` method of Model Queries, on-demand hydration is very easy to trigger:
+
+{{{
+#!php
+limit(50000)
+ ->setFormatter(ModelCriteria::FORMAT_ON_DEMAND) // just add this line
+ ->find();
+foreach ($authors as $author) {
+ echo $author->getFirstName();
+}
+}}}
+
+In this example, Propel will hydrate the `Author` objects row by row, after the `foreach` call, and reuse the memory between each iteration. The consequence is that the above code won't use more memory when the query returns 50,000 results than when it returns 5.
+
+`ModelCriteria::FORMAT_ON_DEMAND` is one of the many formatters provided by the Query objects. You can also get a collection of associative arrays instead of objects, if you don't need any of the logic stored in your model object, by using `ModelCriteria::FORMAT_ARRAY`.
+
+The [wiki:Documentation/1.5/ModelCriteria Query API reference] describes each formatter, and how to use it.
+
+== Propel Instance Pool ==
+
+Propel keeps a list of the objects that you already retrieved in memory to avoid calling the same request twice in a PHP script. This list is called the instance pool, and is automatically populated from your past requests:
+
+{{{
+#!php
+findPk(1);
+// Issues a SELECT query
+...
+// second call
+$author2 = AuthorQuery::create()->findPk(1);
+// Skips the SQL query and returns the existing $author1 object
+}}}
diff --git a/3rd_party/php/propel/docs/guide/04-Relationships.txt b/3rd_party/php/propel/docs/guide/04-Relationships.txt
new file mode 100644
index 000000000..6e5de3c68
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/04-Relationships.txt
@@ -0,0 +1,386 @@
+= Basic Relationships =
+
+[[PageOutline]]
+
+The definition of foreign keys in your schema allows Propel to add smart methods to the generated model and query objects. In practice, these generated methods mean that you will never actually have to deal with primary and foreign keys yourself. It makes the task of dealing with relations extremely straightforward.
+
+== Inserting A Related Row ==
+
+Propel creates setters for related objects that simplify the foreign key handling. You don't actually have to define a foreign key value. Instead, just set a related object, as follows:
+
+{{{
+#!php
+setFirstName("Leo");
+$author->setLastName("Tolstoy");
+$author->save();
+
+$book = new Book();
+$book->setTitle("War & Peace");
+// associate the $author object with the current $book
+$book->setAuthor($author);
+$book->save();
+}}}
+
+Propel generates the `setAuthor()` method based on the `phpName` attribute of the `` element in the schema. When the attribute is not set, Propel uses the `phpName` of the related table instead.
+
+Internally, the call to `Book::setAuthor($author)` translates into `Book::setAuthorId($author->getId())`. But you don't actually have to save a Propel object before associating it to another. In fact, Propel automatically "cascades" INSERT statements when a new object has other related objects added to it.
+
+For one-to-many relationships - meaning, from the other side of a many-to-one relationship - the process is a little different. In the previous example, one `Book` has one `Author`, but one `Author` has many `Books`. From the `Author` point of view, a one-to-many relationships relates it to `Book`. So Propel doesn't generate an `Author::setBook()`, but rather an `Author::addBook()`:
+
+{{{
+#!php
+setTitle("War & Peace");
+// associate the $author object with the current $book
+$book->save();
+
+$author = new Author();
+$author->setFirstName("Leo");
+$author->setLastName("Tolstoy");
+$author->addBook($book);
+$author->save();
+}}}
+
+The result is the same in the database - the `author_id` column of the `book` row is correctly set to the `id` of the `author` row.
+
+== Save Cascade ==
+
+As a matter of fact, you don't need to `save()` an object before relating it. Propel knows which objects are related to each other, and is capable of saving all the unsaved objects if they are related to each other.
+
+The following example shows how to create new `Author` and `Publisher` objects, which are then added to a new `Book` object; all 3 objects are saved when the `Book::save()` method is eventually invoked.
+
+{{{
+#!php
+setFirstName("Leo");
+$author->setLastName("Tolstoy");
+// no need to save the author yet
+
+$publisher = new Publisher();
+$publisher->setName("Viking Press");
+// no need to the publisher yet
+
+$book = new Book();
+$book->setTitle("War & Peace");
+$book->setIsbn("0140444173");
+$book->setPublisher($publisher);
+$book->setAuthor($author);
+$book->save(); // saves all 3 objects!
+}}}
+
+In practice, Propel '''cascades''' the `save()` action to the related objects.
+
+== Reading Related Object Properties ==
+
+Just like the related object setters, Propel generates a getter for every relation:
+
+{{{
+#!php
+findPk(1);
+$author = $book->getAuthor();
+echo $author->getFirstName(); // 'Leo'
+}}}
+
+Since a relationship can also be seen from the other end, Propel allows the foreign table to retrieve the related objects as well:
+
+{{{
+#!php
+findPk(1);
+$books = $author->getBooks();
+foreach ($books as $book) {
+ echo $book->getTitle();
+}
+}}}
+
+Notice that Propel generated a `getBooks()` method returning an array of `Book` objects, rather than a `getBook()` method. This is because the definition of a foreign key defines a many-to-one relationship, seen from the other end as a one-to-many relationship.
+
+'''Tip''': Propel also generates a `countBooks()` methods to get the number of related objects without hydrating all the `Book` objects. for performance reasons, you should prefer this method to `count($author->getBooks())`.
+
+Getters for one-to-many relationship accept an optional query object. This allows you to hydrate related objects, or retrieve only a subset of the related objects, or to reorder the list of results:
+
+{{{
+#!php
+orderByTitle()
+ ->joinWith('Book.Publisher');
+$books = $author->getBooks($query);
+}}}
+
+== Using Relationships In A Query ==
+
+=== Finding Records Related To Another One ===
+
+If you need to find objects related to a model object that you already have, you can take advantage of the generated `filterByXXX()` methods in the query objects, where `XXX` is a relation name:
+
+{{{
+#!php
+findPk(1);
+$books = BookQuery::create()
+ ->filterByAuthor($author)
+ ->orderByTitle()
+ ->find();
+}}}
+
+You don't need to specify that the `author_id` column of the `Book` object should match the `id` column of the `Author` object. Since you already defined the foreign key mapping in your schema, Propel knows enough to figure it out.
+
+=== Embedding Queries ===
+
+In SQL queries, relationships often translate to a JOIN statement. Propel abstracts this relational logic in the query objects, by allowing you to ''embed'' a related query into another.
+
+In practice, Propel generates one `useXXXQuery()` method for every relation in the Query objects. So the `BookQuery` class offers a `useAuthorQuery()` and a `usePublisherQuery()` method. These methods return a new Query instance of the related query class, that you can eventually merge into the main query by calling `endUse()`.
+
+To illustrate this, let's see how to write the following SQL query with the Propel Query API:
+
+{{{
+#!sql
+SELECT book.*
+FROM book INNER JOIN author ON book.AUTHOR_ID = author.ID
+WHERE book.ISBN = '0140444173' AND author.FIRST_NAME = 'Leo'
+ORDER BY book.TITLE ASC
+LIMIT 10;
+}}}
+
+That would simply give:
+
+{{{
+#!php
+filterByISBN('0140444173')
+ ->useAuthorQuery() // returns a new AuthorQuery instance
+ ->filterByFirstName('Leo') // this is an AuthorQuery method
+ ->endUse() // merges the Authorquery in the main Bookquery and returns the BookQuery
+ ->orderByTitle()
+ ->limit(10)
+ ->find();
+}}}
+
+Propel knows the columns to use in the `ON` clause from the definition of foreign keys in the schema. The ability to use methods of a related Query object allows you to keep your model logic where it belongs.
+
+Of course, you can embed several queries to issue a query of any complexity level:
+
+{{{
+#!php
+useBookQuery()
+ ->usePublisherQuery()
+ ->filterByName('Viking Press')
+ ->endUse()
+ ->endUse()
+ ->find();
+}}}
+
+You can see how the indentation of the method calls provide a clear explanation of the embedding logic. That's why it is a good practice to format your Propel queries with a single method call per line, and to add indentation every time a `useXXXQuery()` method is used.
+
+== Many-to-Many Relationships ==
+
+Databases typically use a cross-reference table, or junction table, to materialize the relationship. For instance, if the `user` and `group` tables are related by a many-to-many relationship, this happens through the rows of a `user_group` table. To inform Propel about the many-to-many relationship, set the `isCrossRef` attribute of the cross reference table to true:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Once you rebuild your model, the relationship is seen as a one-to-many relationship from both the `User` and the `Group` models. That means that you can deal with adding and reading relationships the same way as you usually do:
+
+{{{
+#!php
+setName('John Doe');
+$group = new Group();
+$group->setName('Anonymous');
+// relate $user and $group
+$user->addGroup($group);
+// save the $user object, the $group object, and a new instance of the UserGroup class
+$user->save();
+}}}
+
+The same happens for reading related objects ; Both ends see the relationship as a one-to-many relationship:
+
+{{{
+#!php
+getUsers();
+$nbUsers = $group->countUsers();
+$groups = $user->getGroups();
+$nbGroups = $user->countGroups();
+}}}
+
+Just like regular related object getters, these generated methods accept an optional query object, to further filter the results.
+
+To facilitate queries, Propel also adds new methods to the `UserQuery` and `GroupQuery` classes:
+
+{{{
+#!php
+filterByGroup($group)
+ ->find();
+$groups = GroupQuery::create()
+ ->filterByUser($user)
+ ->find();
+}}}
+
+== One-to-One Relationships ==
+
+Propel supports the special case of one-to-one relationships. These relationships are defined when the primary key is also a foreign key. For example :
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Because the primary key of the `bookstore_employee_account` is also a foreign key to the `bookstore_employee` table, Propel interprets this as a one-to-one relationship and will generate singular methods for both sides of the relationship (`BookstoreEmployee::getBookstoreEmployeeAccount()`, and `BookstoreEmployeeAccount::getBookstoreEmployee()`).
+
+== On-Update and On-Delete Triggers =
+
+Propel also supports the ''ON UPDATE'' and ''ON DELETE'' aspect of foreign keys. These properties can be specified in the `` tag using the `onUpdate` and `onDelete` attributes. Propel supports values of `CASCADE`, `SETNULL`, and `RESTRICT` for these attributes. For databases that have native foreign key support, these trigger events will be specified at the datbase level when the foreign keys are created. For databases that do not support foreign keys, this functionality will be emulated by Propel.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+}}}
+
+In the example above, the `review` rows will be automatically removed if the related `book` row is deleted.
+
+== Minimizing Queries ==
+
+Even if you use a foreign query, Propel will issue new queries when you fetch related objects:
+
+{{{
+#!php
+useAuthorQuery()
+ ->filterByFirstName('Leo')
+ ->endUse()
+ ->findOne();
+$author = $book->getAuthor(); // Needs another database query
+}}}
+
+Propel allows you to retrieve the main object together with related objects in a single query. You just the `with()` method to specify which objects the main object should be hydrated with.
+
+{{{
+#!php
+useAuthorQuery()
+ ->filterByFirstName('Leo')
+ ->endUse()
+ ->with('Author')
+ ->findOne();
+$author = $book->getAuthor(); // Same result, with no supplementary query
+}}}
+
+Since the call to `with()` adds the columns of the related object to the SELECT part of the query, and uses these columns to populate the related object, that means that a query using `with()` is slower and consumes more memory. So use it only when you actually need the related objects afterwards.
+
+If you don't want to add a filter on a related object but still need to hydrate it, calling `useXXXQuery()`, `endUse()`, and then `with()` can be a little cumbersome. For this case, Propel provides a proxy method called `joinWith()`. It expects a string made of the initial query name and the foreign query name. For instance:
+
+{{{
+#!php
+joinWith('Book.Author')
+ ->findOne();
+$author = $book->getAuthor(); // Same result, with no supplementary query
+}}}
+
+`with()` and `joinWith()` are not limited to immediate relationships. As a matter of fact, just like you can nest `use()` calls, you can call `with()` several times to populate a chain of objects:
+
+{{{
+#!php
+joinWith('Review.Book')
+ ->joinWith('Book.Author')
+ ->joinWith('Book.Publisher')
+ ->findOne();
+$book = $review->getBook() // No additional query needed
+$author = $book->getAuthor(); // No additional query needed
+$publisher = $book->getPublisher(); // No additional query needed
+}}}
+
+So `with()` is very useful to minimize the number of database queries. As soon as you see that the number of queries necessary to perform an action is proportional to the number of results, adding a `with()` call is the trick to get down to a more reasonnable query count.
+
+'''Tip''': `with()` also works for left joins on one-to-many relationships, but you musn't use a `limit()` in the query in this case. This is because Propel has no way to determine the actual number of rows of the main object in such a case.
+
+{{{
+#!php
+leftJoinWith('Author.Book')
+ ->find();
+// this does not work
+$authors = AuthorQuery::create()
+ ->leftJoinWith('Author.Book')
+ ->limit(5)
+ ->find();
+}}}
+
+However, it is quite easy to achieve hydration of related objects with only one additional query:
+
+{{{
+ #!php
+find();
+$authors->populateRelation('Book');
+// now you can iterate over each author's book without further queries
+foreach ($authors as $author) {
+ foreach ($authors->getBooks() as $book) { // no database query, the author already has a Books collection
+ // do stuff with $book and $author
+ }
+}
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/guide/05-Validators.txt b/3rd_party/php/propel/docs/guide/05-Validators.txt
new file mode 100644
index 000000000..352ea6e1d
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/05-Validators.txt
@@ -0,0 +1,253 @@
+= Validators =
+
+[[PageOutline]]
+
+Validators help you to validate an input before perstisting it to the database. In Propel, validators are rules describing what type of data a column accepts. Validators are referenced in the `schema.xml` file, using `` tags.
+
+Validators are applied at the PHP level, they are not created as constraints on the database itself. That means that if you also use another language to work with the database, the validator rules will not be enforced.
+You can also apply multiple rule entries per validator entry in the schema.xml file.
+
+== Overview ==
+
+In the following example, the `username` column is defined to have a minimum length of 4 characters:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+}}}
+
+Every column rule is represented by a `` tag. A `` is a set of `` tags bound to a column.
+
+At runtime, you can validate an instance of the model by calling the `validate()` method:
+
+{{{
+#!php
+setUsername("foo"); // only 3 in length, which is too short...
+if ($objUser->validate()) {
+ // no validation errors, so the data can be persisted
+ $user->save();
+} else {
+ // Something went wrong.
+ // Use the validationFailures to check what
+ $failures = $objUser->getValidationFailures();
+ foreach($failures as $failure) {
+ echo $objValidationFailure->getMessage() . " \n";
+ }
+}
+}}}
+
+`validate()` returns a boolean. If the validation failed, you can access the array `ValidationFailed` objects by way of the `getValidationFailures()` method. Each `ValidationFailed` instance gives access to the column, the messagen and the validator that caused the failure.
+
+== Core Validators ==
+
+Propel bundles a set of validatorts that should help you deal with the most common cases.
+
+=== !MatchValidator ===
+
+The `MatchValidator` is used to run a regular expression of choice against the column. Note that this is a `preg`, not `ereg` (check [http://www.php.net/preg_match the preg_match documentation] for more information about regexps).
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !NotMatchValidator ===
+
+Opposite of `MatchValidator, this validator returns false if the regex returns true
+
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+=== !MaxLengthValidator ===
+
+When you want to limit the size of the string to be inserted in a column, use the `MaxLengthValidator`. Internally, it uses `strlen()` to get the length of the string. For instance, some database completely ignore the lentgh of `LONGVARCHAR` columns; you can enforce it using a validator:
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+'''Tip''': If you have specified the `size` attribute in the `` tag, you don't have to specify the `value` attribute in the validator rule again, as this is done automatically.
+
+=== !MinLengthValidator ===
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !MaxValueValidator ===
+
+To limit the value of an integer column, use the `MaxValueValidator`. Note that this validator uses a non-strict comparison ('less than or equal'):
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !MinValueValidator ===
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+'''Tip''': You can run multiple validators against a single column.
+
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+=== !RequiredValidator ===
+
+This validtor checks the same rule as a `required=true` on the column at the database level. However it will not give you a clean error to work with.
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !UniqueValidator ===
+
+To check whether the value already exists in the table, use the `UniqueValidator`:
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !ValidValuesValidator ===
+
+This rule restricts the valid values to a list delimited by a pipe ('|').
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+=== !TypeValidator ===
+
+Restrict values to a certain PHP type using the `TypeValidator`:
+
+{{{
+#!xml
+
+
+
+
+}}}
+
+== Adding A Custom Validator ==
+
+You can easily add a custom validator. A validator is a class extending `BasicValidator` providing a public `isValid()` method. For instance:
+
+{{{
+#!php
+` tag. So `$map->getValue()` returns the `value` attribute.
+
+'''Tip''': Make sure that `isValid()` returns a boolean, so really true or false. Propel is very strict about this. Returning a mixed value just won't do.
+
+To enable the new validator on a column, add a corresponding `` in your schema and use 'class' as the rule `name`.
+
+{{{
+#!xml
+
+
+
+}}}
+
+The `class` attribute of the `` tag should contain a path to the validator class accessible from the include_path, where the directory separator is replaced by a dot.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/guide/06-Transactions.txt b/3rd_party/php/propel/docs/guide/06-Transactions.txt
new file mode 100644
index 000000000..55a9240cc
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/06-Transactions.txt
@@ -0,0 +1,291 @@
+= Transactions =
+
+[[PageOutline]]
+
+Database transactions are the key to assure the data integrity and the performance of database queries. Propel uses transactions internally, and provides a simple API to use them in your own code.
+
+'''Tip''': If the [http://en.wikipedia.org/wiki/ACID ACID] acronym doesn't ring a bell, you should probably learn some [http://en.wikipedia.org/wiki/Database_transaction fundamentals about database transactions] before reading further.
+
+== Wrapping Queries Inside a Transaction ==
+
+Propel uses PDO as database abstraction layer, and therefore uses [http://www.php.net/manual/en/pdo.transactions.php PDO's built-in support for database transactions]. The syntax is the same, as you can see in the classical "money transfer" example:
+
+{{{
+#!php
+beginTransaction();
+
+ try {
+ // remove the amount from $fromAccount
+ $fromAccount->setValue($fromAccount->getValue() - $amount);
+ $fromAccount->save($con);
+ // add the amount to $toAccount
+ $toAccount->setValue($toAccount->getValue() + $amount);
+ $toAccount->save($con);
+
+ $con->commit();
+ } catch (Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+}
+}}}
+
+The transaction statements are `beginTransaction()`, `commit()` and `rollback()`, which are methods of the PDO connection object. Transaction methods are typically used inside a `try/catch` block. The exception is rethrown after rolling back the transaction: That ensures that the user knows that something wrong happenned.
+
+In this example, if something wrong happens while saving either one of the two accounts, an `Exception` is thrown, and the whole operation is rolled back. That means that the transfer is cancelled, with an insurance that the money hasn't vanished (that's the A in ACID, which stands for "Atomicity"). If both account modifications work as expected, the whole transaction is committed, meaning that the data changes enclosed in the transaction are persisted in the database.
+
+Tip: In order to build a transaction, you need a connection object. The connection object for a Propel model is always available through `Propel::getConnection([ModelName]Peer::DATABASE_NAME)`.
+
+== Denormalization And Transactions ==
+
+Another example of the use of transactions is for [http://en.wikipedia.org/wiki/Denormalization denormalized schemas].
+
+For instance, suppose that you have an `Author` model with a one to many relationship to a `Book` model. every time you need to display the number of books written by an author, you call `countBooks()` on the author object, which issues a new query to the database:
+
+{{{
+#!php
+
+
+
getName() ?> (countBooks() ?> books)
+
+
+}}}
+
+If you have a large number of authors and books, this simple code snippet can be a real performance blow to your application. The usual way to optimize it is to ''denormalize'' your schema by storing the number of books by each author in a new `nb_books` column, in the `author` table.
+
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+You must update this new column every time you save or delete a `Book` object; this will make write queries a little slower, but read queries much faster. Fortunately, Propel model objects support pre- and post- hooks for the `save()` and `delete()` methods, so this is quite easy to implement:
+
+{{{
+#!php
+updateNbBooks($con);
+ }
+
+ public function postDelete(PropelPDO $con)
+ {
+ $this->updateNbBooks($con);
+ }
+
+ public function updateNbBooks(PropelPDO $con)
+ {
+ $author = $this->getAuthor();
+ $nbBooks = $author->countBooks($con);
+ $author->setNbBooks($nbBooks);
+ $author->save($con);
+ }
+}
+}}}
+
+The `BaseBook::save()` method wraps the actual database INSERT/UPDATE query inside a transaction, together with any other query registered in a pre- or post- save hook. That means that when you save a book, the `postSave()` code is executed in the same transaction as the actual `$book->save()` method. Everything happens as is the code was the following:
+
+{{{
+#!php
+beginTransaction();
+
+ try {
+ // insert/update query for the current object
+ $this->doSave($con);
+
+ // postSave hook
+ $author = $this->getAuthor();
+ $nbBooks = $author->countBooks($con);
+ $author->setNbBooks($nbBooks);
+ $author->save($con);
+
+ $con->commit();
+ } catch (Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+ }
+}
+}}}
+
+In this example, the `nb_books` column of the `author` table will always we synchronized with the number of books. If anything happens during the transaction, the saving of the book is rolled back, as well as the `nb_books` column update. The transaction serves to preserve data consistency in a denormalized schema ("Consistency" stands for the C in ACID).
+
+'''Tip''': Check the [wiki:Documentation/1.5/Behaviors behaviors documentation] for details about the pre- and post- hooks in Propel model objects.
+
+== Nested Transactions ==
+
+Some RDBMS offer the ability to nest transactions, to allow partial rollback of a set of transactions. PDO does not provide this ability at the PHP level; nevertheless, Propel emulates nested transactions for all supported database engines:
+
+{{{
+#!php
+beginTransaction();
+ try {
+ $c = new Criteria();
+ $c->add(BookPeer::PRICE, null, Criteria::ISNULL);
+ BookPeer::doDelete($c, $con);
+ $con->commit();
+ } catch (Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+}
+
+function deleteAuthorsWithNoEmail(PropelPDO $con)
+{
+ $con->beginTransaction();
+ try {
+ $c = new Criteria();
+ $c->add(AuthorPeer::EMAIL, null, Criteria::ISNULL);
+ AuthorPeer::doDelete($c, $con);
+ $con->commit();
+ } catch (Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+}
+
+function cleanup(PropelPDO $con)
+{
+ $con->beginTransaction();
+ try {
+ deleteBooksWithNoPrice($con);
+ deleteAuthorsWithNoEmail($con);
+ $con->commit();
+ } catch (Exception $e) {
+ $con->rollback();
+ throw $e;
+ }
+}
+}}}
+
+All three functions alter data in a transaction, ensuring data integrity for each. In addition, the `cleanup()` function actually executes two nested transactions inside one main transaction.
+
+Propel deals with this case by seeing only the outermost transaction, and ignoring the `beginTransaction()`, `commit()` and `rollback()` statements of nested transactions. If nothing wrong happens, then the last `commit()` call (after both `deleteBooksWithNoPrice()` and `deleteAuthorsWithNoEmail()` end) triggers the actual database commit. However, if an exception is thrown in either one of these nested transactions, it is escalated to the main `catch` statement in `cleanup()` so that the entire transaction (starting at the main `beginTransaction()`) is rolled back.
+
+So you can use transactions everywhere it's necessary in your code, without worrying about nesting them. Propel will always commit or rollback everything altogether, whether the RDBMS supports nested transactions or not.
+
+'''Tip''': This allows you to wrap all your application code inside one big transaction for a better integrity.
+
+== Using Transactions To Boost Performance ==
+
+A database transaction has a cost in terms of performance. In fact, for simple data manipulation, the cost of the transaction is more important than the cost of the query itself. Take the following example:
+
+{{{
+#!php
+setTitle($i . ': A Space Odyssey');
+ $book->save($con);
+}
+}}}
+
+As explained earlier, Propel wraps every save operation inside a transaction. In terms of execution time, this is very expensive. Here is how the above code would translate to MySQL in an InnodDB table:
+
+{{{
+#!sql
+BEGIN;
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'0: A Space Odyssey');
+COMMIT;
+BEGIN;
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'1: A Space Odyssey');
+COMMIT;
+BEGIN;
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'2: A Space Odyssey');
+COMMIT;
+...
+}}}
+
+You can take advantage of Propel's nested transaction capabilities to encapsulate the whole loop inside one single transaction. This will reduce the execution time drastically:
+
+{{{
+#!php
+beginTransaction();
+for ($i=0; $i<2002; $i++)
+{
+ $book = new Book();
+ $book->setTitle($i . ': A Space Odyssey');
+ $book->save($con);
+}
+$con->commit();
+}}}
+
+The transactions inside each `save()` will become nested, and therefore not translated into actual database transactions. Only the outmost transaction will become a database transaction. So this will translate to MySQL as:
+
+{{{
+#!sql
+BEGIN;
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'0: A Space Odyssey');
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'1: A Space Odyssey');
+INSERT INTO book (`ID`,`TITLE`) VALUES (NULL,'2: A Space Odyssey');
+...
+COMMIT;
+}}}
+
+In practice, encapsulating a large amount of simple queries inside a single transaction significantly improves performance.
+
+Tip: Until the final `commit()` is called, most database engines lock updated rows, or even tables, to prevent any query outside the transaction from seeing the partially committed data (this is how transactions preserve Isolation, which is the I in ACID). That means that large transactions will queue every other queries for potentially a long time. Consequently, use large transactions only when concurrency is not a requirement.
+
+== Why Is The Connection Always Passed As Parameter? ==
+
+All the code examples in this chapter show the connection object passed a a parameter to Propel methods that trigger a database query:
+
+{{{
+#!php
+setValue($fromAccount->getValue() - $amount);
+$fromAccount->save($con);
+}}}
+
+The same code works without explicitely passing the connection object, because Propel knows how to get the right connection from a Model:
+
+{{{
+#!php
+setValue($fromAccount->getValue() - $amount);
+$fromAccount->save();
+}}}
+
+However, it's a good practice to pass the connection explicitely, and for three reasons:
+
+ * Propel doesn't need to look for a connection object, and this results in a tiny boost in performance.
+ * You can use a specific connection, which is required in distributed (master/slave) environments, in order to distinguish read and write operations.
+ * Most importantly, transactions are tied to a single connection. You can't enclose two queries using different connections in a single transaction. So it's very useful to identify the connection you want to use for every query, as Propel will throw an exception if you use the wrong connection.
+
+== Limitations ==
+
+ * Currently there is no support for row locking (e.g. `SELECT blah FOR UPDATE`).
+ * You must rethrow the exception caught in the `catch` statement of nested transactions, otherwise there is a risk that the global rollback doesn't occur.
+ * True nested transactions, with partial rollback, are only possible in MSSQL, and can be emulated in other RDBMS through savepoints. This feature may be added to Propel in the future, but for the moment, only the outermost PHP transaction triggers a database transaction.
+ * If you rollback a partially executed transaction and ignore the exception thrown, there are good chances that some of your objects are out of sync with the database. The good practice is to always let a transaction exception escalate until it stops the script execution.
+
diff --git a/3rd_party/php/propel/docs/guide/07-Behaviors.txt b/3rd_party/php/propel/docs/guide/07-Behaviors.txt
new file mode 100644
index 000000000..fe0025ec1
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/07-Behaviors.txt
@@ -0,0 +1,280 @@
+= Behaviors =
+
+[[PageOutline]]
+
+Behaviors are a great way to package model extensions for reusability. They are the powerful, versatile, fast, and help you organize your code in a better way.
+
+== Pre and Post Hooks For `save()` And `delete()` Methods ==
+
+The `save()` and `delete()` methods of your generated objects are easy to override. In fact, Propel looks for one of the following methods in your objects and executes them when needed:
+
+{{{
+#!php
+
+ ...
+
+
+}}}
+
+Then, you can force the update of the `created_at` column before every insertion as follows:
+
+{{{
+#!php
+setCreatedAt(time());
+ return true;
+ }
+}
+}}}
+
+Whenever you call `save()` on a new object, Propel now executes the `preInsert()` method on this objects and therefore update the `created_at` column:
+
+{{{
+#!php
+setTitle('War And Peace');
+$b->save();
+echo $b->getCreatedAt(); // 2009-10-02 18:14:23
+}}}
+
+If you implement `preInsert()`, `preUpdate()`, `preSave()` or `preDelete()`, these methods must return a boolean value. This determines whether the action (save or delete) may proceed.
+
+'''Tip''': Since this feature adds a small overhead to write operations, you can deactivate it completely in your build properties by setting `propel.addHooks` to `false`.
+
+{{{
+#!ini
+# -------------------
+# TEMPLATE VARIABLES
+# -------------------
+propel.addHooks = false
+}}}
+
+== Introducing Behaviors ==
+
+When several of your custom model classes end up with similar methods added, it is time to refactor the common code.
+
+For example, you may want to add the same ability you gave to `Book` to all the other objects in your model. Let's call this the "Timestampable behavior", because then all of your rows have a timestamp marking their creation. In order to achieve this behavior, you have to repeat the same operations on every table. First, add a `created_at` column to the other tables:
+
+{{{
+#!xml
+
+ ...
+
+
+
+ ...
+
+
+}}}
+
+Then, add a `preInsert()` hook to the object stub classes:
+
+{{{
+#!php
+setCreatedAt(time());
+ }
+}
+
+class Author extends BaseAuthor
+{
+ public function preInsert()
+ {
+ $this->setCreatedAt(time());
+ }
+}
+}}}
+
+Even if the code of this example is very simple, the repetition of code is already too much. Just imagine a more complex behavior, and you will understand that using the copy-and-paste technique soon leads to a maintenance nightmare.
+
+Propel offers three ways to achieve the refactoring of the common behavior. The first one is to use a custom builder during the build process. This can work if all of your models share one single behavior. The second way is to use table inheritance. The inherited methods then offer limited capabilities. And the third way is to use Propel behaviors. This is the right way to refactor common model logic.
+
+Behaviors are special objects that use events called during the build process to enhance the generated model classes. Behaviors can add attributes and methods to both the Peer and model classes, they can modify the course of some of the generated methods, and they can even modify the structure of a database by adding columns or tables.
+
+For instance, Propel bundles a behavior called `timestampable`, which does exatcly the same thing as described above. But instead of adding columns and methods by hand, all you have to do is to declare it in a `` tag in your `schema.xml`, as follows:
+
+{{{
+#!xml
+
+ ...
+
+
+
+ ...
+
+
+}}}
+
+Then rebuild your model, and there you go: two columns, `created_at` and `updated_at`, were automatically added to both the `book` and `author` tables. Besides, the generated `BaseBook` and `BaseAuthor` classes already contain the code necessary to auto-set the current time on creation and on insertion.
+
+== Bundled Behaviors ==
+
+Propel currently bundles several behaviors. Check the behavior documentation for details on usage:
+
+ * [wiki:Documentation/1.5/Behaviors/aggregate_column aggregate_column]
+ * [wiki:Documentation/1.5/Behaviors/alternative_coding_standards alternative_coding_standards]
+ * [wiki:Documentation/1.5/Behaviors/auto_add_pk auto_add_pk]
+ * [wiki:Documentation/1.5/Behaviors/timestampable timestampable]
+ * [wiki:Documentation/1.5/Behaviors/sluggable sluggable]
+ * [wiki:Documentation/1.5/Behaviors/soft_delete soft_delete]
+ * [wiki:Documentation/1.5/Behaviors/sortable sortable]
+ * [wiki:Documentation/1.5/Behaviors/nested_set nested_set]
+ * [wiki:Documentation/1.5/Behaviors/query_cache query_cache]
+ * And [wiki:Documentation/1.5/Inheritance#ConcreteTableInheritance concrete_inheritance], documented in the Inheritance Chapter even if it's a behavior
+
+Behaviors bundled with Propel require no further installation and work out of the box.
+
+== Customizing Behaviors ==
+
+Behaviors often offer some parameters to tweak their effect. For instance, the `timestampable` behavior allows you to customize the names of the columns added to store the creation date and the update date. The behavior customization occurs in the `schema.xml`, inside `` tags nested in the `` tag. So let's set the behavior to use `created_on` instead of `created_at` for the creation date column name (and same for the update date column):
+
+{{{
+#!xml
+
+ ...
+
+
+
+
+
+}}}
+
+If the columns already exist in your schema, a behavior is smart enough not to add them one more time.
+
+{{{
+#!xml
+
+ ...
+
+
+
+
+
+
+
+}}}
+
+== Using Third-Party Behaviors ==
+
+As a Propel behavior can be packaged into a single class, behaviors are quite easy to reuse and distribute across several projects. All you need to do is to copy the behavior file into your project, and declare it in `build.properties`, as follows:
+
+{{{
+#!ini
+# ----------------------------------
+# B E H A V I O R S E T T I N G S
+# ----------------------------------
+
+propel.behavior.timestampable.class = propel.engine.behavior.timestampable.TimestampableBehavior
+# Add your custom behavior pathes here
+propel.behavior.formidable.class = path.to.FormidableBehavior
+}}}
+
+Propel will then find the `FormidableBehavior` class whenever you use the `formidable` behavior in your schema:
+
+{{{
+#!xml
+
+ ...
+
+
+
+}}}
+
+'''Tip''': If you use autoloading during the build process, and if the behavior classes benefit from the autoloading, then you don't even need to declare the path to the behavior class.
+
+== Applying a Behavior To All Tables ==
+
+You can add a `` tag directly under the `` tag. That way, the behavior will be applied to all the tables of the database.
+
+{{{
+#!xml
+
+
+
+ ...
+
+
+ ...
+
+
+}}}
+
+In this example, both the `book` and `author` table benefit from the `timestampable` behavior, and therefore automatically update their `created_at` and `updated_at` columns upon saving.
+
+Going one step further, you can even apply a behavior to all the databases of your project, provided the behavior doesn't need parameters - or can use default parameters. To add a behavior to all databases, simply declare it in the project's `build.properties` under the `propel.behavior.default` key, as follows:
+
+{{{
+#!ini
+propel.behavior.default = soft_delete, timestampable
+}}}
+
+== Writing a Behavior ==
+
+Behaviors can modify their table, and even add another table, by implementing the `modifyTable` method. In this method, use `$this->getTable()` to retrieve the table buildtime model and manipulate it.
+
+Behaviors can add code to the generated model object by implementing one of the following methods:
+
+{{{
+objectAttributes() // add attributes to the object
+objectMethods() // add methods to the object
+preInsert() // add code to be executed before insertion of a new object
+postInsert() // add code to be executed after insertion of a new object
+preUpdate() // add code to be executed before update of an existing object
+postUpdate() // add code to be executed after update of an existing object
+preSave() // add code to be executed before saving an object (new or existing)
+postSave() // add code to be executed after saving an object (new or existing)
+preDelete() // add code to be executed before deleting an object
+postDelete() // add code to be executed after deleting an object
+objectCall() // add code to be executed inside the object's __call()
+objectFilter(&$script) // do whatever you want with the generated code, passed as reference
+}}}
+
+Behaviors can also add code to the generated query objects by implementing one of the following methods:
+
+{{{
+queryAttributes() // add attributes to the query class
+queryMethods() // add methods to the query class
+preSelectQuery() // add code to be executed before selection of a existing objects
+preUpdateQuery() // add code to be executed before update of a existing objects
+postUpdateQuery() // add code to be executed after update of a existing objects
+preDeleteQuery() // add code to be executed before deletion of a existing objects
+postDeleteQuery() // add code to be executed after deletion of a existing objects
+queryFilter(&$script) // do whatever you want with the generated code, passed as reference
+}}}
+
+Behaviors can also add code to the generated peer objects by implementing one of the following methods:
+
+{{{
+staticAttributes() // add static attributes to the peer class
+staticMethods() // add static methods to the peer class
+preSelect() // adds code before every select query
+peerFilter(&$script) // do whatever you want with the generated code, passed as reference
+}}}
+
+Check the behaviors bundled with Propel to see how to implement your own behavior.
diff --git a/3rd_party/php/propel/docs/guide/08-Logging.txt b/3rd_party/php/propel/docs/guide/08-Logging.txt
new file mode 100644
index 000000000..80445f17d
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/08-Logging.txt
@@ -0,0 +1,448 @@
+= Logging And Debugging =
+
+[[PageOutline]]
+
+Propel provides tools to monitor and debug your model. Whether you need to check the SQL code of slow queries, or to look for error messages previously thrown, Propel is your best friend for finding and fixing problems.
+
+== Propel Logs ==
+
+Propel uses the logging facility configured in `runtime-conf.xml` to record errors, warnings, and debug information.
+
+By default Propel will attempt to use the Log framework that is distributed with PEAR. If you are not familiar with it, check its [http://www.indelible.org/php/Log/guide.html online documentation]. It is also easy to configure Propel to use your own logging framework -- or none at all.
+
+=== Logger Configuration ===
+
+The Propel log handler is configured in the `` section of your project's `runtime-conf.xml` file. Here is the accepted format for this section with the default values that Propel uses:
+
+{{{
+#!xml
+
+
+
+ file
+ ./propel.log
+ propel
+ 7
+
+
+
+ ...
+
+
+}}}
+
+Using these parameters, Propel creates a ''file'' Log handler in the background, and keeps it for later use:
+
+{{{
+#!php
+` nested elements may vary, depending on which log handler you are using. Refer to the [http://www.indelible.org/php/Log/guide.html#standard-log-handlers PEAR::Log] documentation for more details on log handlers configuration and options.
+
+Note that the `` tag needs to correspond to the integer represented by one of the `PEAR_LOG_*` constants:
+
+||'''Constant'''||'''Value'''||'''Description'''
+||PEAR_LOG_EMERG||0||System is unusable||
+||PEAR_LOG_ALERT||1||Immediate action required||
+||PEAR_LOG_CRIT||2||Critical conditions||
+||PEAR_LOG_ERR||3||Error conditions||
+||PEAR_LOG_WARNING||4||Warning conditions||
+||PEAR_LOG_NOTICE||5||Normal but significant||
+||PEAR_LOG_INFO||6||Informational||
+||PEAR_LOG_DEBUG||7||Debug-level messages||
+
+=== Logging Messages ===
+
+Use the static `Propel::log()` method to log a message using the configured log handler:
+
+{{{
+#!php
+setName('foo');
+Propel::log('uh-oh, something went wrong with ' . $myObj->getName(), Propel::LOG_ERROR);
+}}}
+
+You can log your own messages from the generated model objects by using their `log()` method, inherited from `BaseObject`:
+
+{{{
+#!php
+log('uh-oh, something went wrong', Propel::LOG_ERROR);
+}}}
+
+The log messages will show up in the log handler defined in `runtime-conf.xml` (`propel.log` file by default) as follows:
+
+{{{
+Oct 04 00:00:18 [error] uh-oh, something went wrong with foo
+Oct 04 00:00:18 [error] MyObj: uh-oh, something went wrong
+}}}
+
+Tip: All serious errors coming from the Propel core do not only issue a log message, they are also thrown as `PropelException`.
+
+=== Using An Alternative PEAR Log Handler ===
+
+In many cases you may wish to integrate Propel's logging facility with the rest of your web application. In `runtime-conf.xml`, you can customize a different PEAR logger. Here are a few examples:
+
+'''Example 1:''' Using 'display' container (for output to HTML)
+{{{
+#!xml
+
+ display
+ 6
+
+}}}
+
+'''Example 2:''' Using 'syslog' container
+{{{
+#!xml
+
+ syslog
+ 8
+ propel
+ 6
+
+}}}
+
+=== Using A Custom Logger ===
+
+If you omit the `` section of your `runtime-conf.xml`, then Propel will not setup ''any'' logging for you. In this case, you can set a custom logging facility and pass it to Propel at runtime.
+
+Here's an example of how you could configure your own logger and then set Propel to use it:
+
+{{{
+#!php
+log($m, Propel::LOG_EMERG);
+ }
+ public function alert($m)
+ {
+ $this->log($m, Propel::LOG_ALERT);
+ }
+ public function crit($m)
+ {
+ $this->log($m, Propel::LOG_CRIT);
+ }
+ public function err($m)
+ {
+ $this->log($m, Propel::LOG_ERR);
+ }
+ public function warning($m)
+ {
+ $this->log($m, Propel::LOG_WARNING);
+ }
+ public function notice($m)
+ {
+ $this->log($m, Propel::LOG_NOTICE);
+ }
+ public function info($m)
+ {
+ $this->log($m, Propel::LOG_INFO);
+ }
+ public function debug($m)
+ {
+ $this->log($m, Propel::LOG_DEBUG);
+ }
+
+ public function log($message, $priority)
+ {
+ $color = $this->priorityToColor($priority);
+ echo '
$message
';
+ }
+
+ private function priorityToColor($priority)
+ {
+ switch($priority) {
+ case Propel::LOG_EMERG:
+ case Propel::LOG_ALERT:
+ case Propel::LOG_CRIT:
+ case Propel::LOG_ERR:
+ return 'red';
+ break;
+ case Propel::LOG_WARNING:
+ return 'orange';
+ break;
+ case Propel::LOG_NOTICE:
+ return 'green';
+ break;
+ case Propel::LOG_INFO:
+ return 'blue';
+ break;
+ case Propel::LOG_DEBUG:
+ return 'grey';
+ break;
+ }
+ }
+}
+}}}
+
+Tip: There is also a bundled `MojaviLogAdapter` class which allows you to use a Mojavi logger with Propel.
+
+== Debugging Database Activity ==
+
+By default, Propel uses `PropelPDO` for database connections. This class, which extends PHP's `PDO`, offers a debug mode to keep track of all the database activity, including all the executed queries.
+
+=== Enabling The Debug Mode ===
+
+The debug mode is disabled by default, but you can enable it at runtime as follows:
+
+{{{
+#!php
+useDebug(true);
+}}}
+
+You can also disable the debug mode at runtime, by calling `PropelPDO::useDebug(false)`. Using this method, you can choose to enable the debug mode for only one particular query, or for all queries.
+
+Alternatively, you can ask Propel to always enable the debug mode for a particular connection by using the `DebugPDO` class instead of the default `PropelPDO` class. This is accomplished in the `runtime-conf.xml` file, in the `` tag of a given datasource connection (see the [wiki:Documentation/1.5/RuntimeConfiguration runtime configuration reference] for more details).
+
+{{{
+#!xml
+
+
+
+
+
+ sqlite
+
+
+ DebugPDO
+}}}
+
+'''Tip''': You can use your own connection class there, but make sure that it extends `PropelPDO` and not only `PDO`. Propel requires certain fixes to PDO API that are provided by `PropelPDO`.
+
+=== Counting Queries ===
+
+In debug mode, `PropelPDO` keeps track of the number of queries that are executed. Use `PropelPDO::getQueryCount()` to retrieve this number:
+
+{{{
+#!php
+getQueryCount(); // 1
+}}}
+
+Tip: You cannot use persistent connections if you want the query count to work. Actually, the debug mode in general requires that you don't use persistent connections in order for it to correctly log bound values and count executed statements.
+
+=== Retrieving The Latest Executed Query ===
+
+For debugging purposes, you may need the SQL code of the latest executed query. It is available at runtime in debug mode using `PropelPDO::getLastExecutedQuery()`, as follows:
+
+{{{
+#!php
+getLastExecutedQuery(); // 'SELECT * FROM my_obj';
+}}}
+
+Tip: You can also get a decent SQL representation of the criteria being used in a SELECT query by using the `Criteria->toString()` method.
+
+Propel also keeps track of the queries executed directly on the connection object, and displays the bound values correctly.
+
+{{{
+#!php
+prepare('SELECT * FROM my_obj WHERE name = :p1');
+$stmt->bindValue(':p1', 'foo');
+$stmt->execute();
+echo $con->getLastExecutedQuery(); // 'SELECT * FROM my_obj where name = "foo"';
+}}}
+
+'''Tip''': The debug mode is intended for development use only. Do not use it in production environment, it logs too much information for a production server, and adds a small overhead to the database queries.
+
+== Full Query Logging ==
+
+The combination of the debug mode and a logging facility provides a powerful debugging tool named ''full query logging''. If you have properly configured a log handler, enabling the debug mode (or using `DebugPDO`) automatically logs the executed queries into Propel's default log file:
+
+{{{
+Oct 04 00:00:18 propel-bookstore [debug] INSERT INTO publisher (`ID`,`NAME`) VALUES (NULL,'William Morrow')
+Oct 04 00:00:18 propel-bookstore [debug] INSERT INTO author (`ID`,`FIRST_NAME`,`LAST_NAME`) VALUES (NULL,'J.K.','Rowling')
+Oct 04 00:00:18 propel-bookstore [debug] INSERT INTO book (`ID`,`TITLE`,`ISBN`,`PRICE`,`PUBLISHER_ID`,`AUTHOR_ID`) VALUES (NULL,'Harry Potter and the Order of the Phoenix','043935806X',10.99,53,58)
+Oct 04 00:00:18 propel-bookstore [debug] INSERT INTO review (`ID`,`REVIEWED_BY`,`REVIEW_DATE`,`RECOMMENDED`,`BOOK_ID`) VALUES (NULL,'Washington Post','2009-10-04',1,52)
+...
+Oct 04 00:00:18 propel-bookstore [debug] SELECT bookstore_employee_account.EMPLOYEE_ID, bookstore_employee_account.LOGIN FROM `bookstore_employee_account` WHERE bookstore_employee_account.EMPLOYEE_ID=25
+}}}
+
+By default, Propel logs all SQL queries, together with the date of the query and the name of the connection.
+
+=== Setting The Data To Log ===
+
+The full query logging feature can be configured either in the `runtime-conf.xml` configuration file, or using the runtime configuration API.
+
+In `runtime-conf.xml`, tweak the feature by adding a `` tag under ``:
+
+{{{
+#!xml
+
+
+
+ ...
+
+
+
+ ...
+
+
+
+
+
+ true
+
+
+
+ true
+
+
+
+
+
+
+}}}
+
+To accomplish the same configuration as above at runtime, change the settings in your main include file, after `Propel::init()`, as follows:
+
+{{{
+#!php
+setParameter('debugpdo.logging.details.method.enabled', true);
+$config->setParameter('debugpdo.logging.details.time.enabled', true);
+$config->setParameter('debugpdo.logging.details.mem.enabled', true);
+}}}
+
+Let's see a few of the provided parameters.
+
+=== Logging More Connection Messages ===
+
+`PropelPDO` can log queries, but also connection events (open and close), and transaction events (begin, commit and rollback). Since Propel can emulate nested transactions, you may need to know when an actual `COMMIT` or `ROLLBACK` is issued.
+
+To extend which methods of `PropelPDO` do log messages in debug mode, customize the `'debugpdo.logging.methods'` parameter, as follows:
+
+{{{
+#!php
+setParameter('debugpdo.logging.methods', $allMethods);
+}}}
+
+By default, only the messages coming from `PropelPDO::exec`, `PropelPDO::query`, and `DebugPDOStatement::execute` are logged.
+
+=== Logging Execution Time And Memory ===
+
+In debug mode, Propel counts the time and memory necessary for each database query. This very valuable data can be added to the log messages on demand, by adding the following configuration:
+
+{{{
+#!php
+setParameter('debugpdo.logging.details.time.enabled', true);
+$config->setParameter('debugpdo.logging.details.mem.enabled', true);
+}}}
+
+Enabling the options shown above, you get log output along the lines of:
+
+{{{
+Feb 23 16:41:04 Propel [debug] time: 0.000 sec | mem: 1.4 MB | SET NAMES 'utf8'
+Feb 23 16:41:04 Propel [debug] time: 0.002 sec | mem: 1.6 MB | SELECT COUNT(tags.NAME) FROM tags WHERE tags.IMAGEID = 12
+Feb 23 16:41:04 Propel [debug] time: 0.012 sec | mem: 2.4 MB | SELECT tags.NAME, image.FILENAME FROM tags LEFT JOIN image ON tags.IMAGEID = image.ID WHERE image.ID = 12
+}}}
+
+The order in which the logging details are enabled is significant, since it determines the order in which they will appear in the log file.
+
+=== Complete List Of Logging Options ===
+
+The following settings can be customized at runtime or in the configuration file:
+
+||'''Parameter'''||'''Default'''||'''Meaning'''||
+||`debugpdo.logging.enabled`||`true`||Should any logging take place||
+||`debugpdo.logging.innerglue`||`": "`||String to use for combining the title of a detail and its value||
+||`debugpdo.logging.outerglue`||`" | "`||String to use for combining details together on a log line||
+||`debugpdo.logging.realmemoryusage`||`false`||Parameter to [http://www.php.net/manual/en/function.memory-get-usage.php memory_get_usage()] and [http://www.php.net/manual/en/function.memory-get-peak-usage.php memory_get_peak_usage()] calls||
+||`debugpdo.logging.methods`||[http://propel.propelorm.org/browser/branches/1.5/runtime/classes/propel/util/DebugPDO.php#L151 array(...)]||An array of method names `Class::method`) to be included in method call logging||
+||`debugpdo.logging.details.slow.enabled`||`false`||Enables flagging of slow method calls||
+||`debugpdo.logging.details.slow.threshold`||`0.1`||Method calls taking more seconds than this threshold are considered slow||
+||`debugpdo.logging.details.time.enabled`||`false`||Enables logging of method execution times||
+||`debugpdo.logging.details.time.precision`||`3`||Determines the precision of the execution time logging||
+||`debugpdo.logging.details.time.pad`||`10`||How much horizontal space to reserve for the execution time on a log line||
+||`debugpdo.logging.details.mem.enabled`||`false`||Enables logging of the instantaneous PHP memory consumption||
+||`debugpdo.logging.details.mem.precision`||`1`||Determines the precision of the memory consumption logging||
+||`debugpdo.logging.details.mem.pad`||`9`||How much horizontal space to reserve for the memory consumption on a log line||
+||`debugpdo.logging.details.memdelta.enabled`||`false`||Enables logging differences in memory consumption before and after the method call||
+||`debugpdo.logging.details.memdelta.precision`||`1`||Determines the precision of the memory difference logging||
+||`debugpdo.logging.details.memdelta.pad`||`10`||How much horizontal space to reserve for the memory difference on a log line||
+||`debugpdo.logging.details.mempeak.enabled`||`false`||Enables logging the peak memory consumption thus far by the currently executing PHP script||
+||`debugpdo.logging.details.mempeak.precision`||`1`||Determines the precision of the memory peak logging||
+||`debugpdo.logging.details.mempeak.pad`||`9`||How much horizontal space to reserve for the memory peak on a log line||
+||`debugpdo.logging.details.querycount.enabled`||`false`||Enables logging of the number of queries performed by the DebugPDO instance thus far||
+||`debugpdo.logging.details.querycount.pad`||`2`||How much horizontal space to reserve for the query count on a log line||
+||`debugpdo.logging.details.method.enabled`||`false`||Enables logging of the name of the method call||
+||`debugpdo.logging.details.method.pad`||`28`||How much horizontal space to reserve for the method name on a log line||
+
+=== Changing the Log Level ===
+
+By default the connection log messages are logged at the `Propel::LOG_DEBUG` level. This can be changed by calling the `setLogLevel()` method on the connection object:
+
+{{{
+#!php
+setLogLevel(Propel::LOG_INFO);
+}}}
+
+Now all queries and bind param values will be logged at the INFO level.
+
+=== Configuring a Different Full Query Logger ===
+
+By default the `PropelPDO` connection logs queries and binds param values using the `Propel::log()` static method. As explained above, this method uses the log storage configured by the `` tag in the `runtime-conf.xml` file.
+
+If you would like the queries to be logged using a different logger (e.g. to a different file, or with different ident, etc.), you can set a logger explicitly on the connection at runtime, using `Propel::setLogger()`:
+
+{{{
+#!php
+setLogger($logger);
+}
+}}}
+
+This will not affect the general Propel logging, but only the full query logging. That way you can log the Propel error and warnings in one file, and the SQL queries in another file.
diff --git a/3rd_party/php/propel/docs/guide/09-Inheritance.txt b/3rd_party/php/propel/docs/guide/09-Inheritance.txt
new file mode 100644
index 000000000..25e03b8e1
--- /dev/null
+++ b/3rd_party/php/propel/docs/guide/09-Inheritance.txt
@@ -0,0 +1,329 @@
+= Inheritance =
+
+[[PageOutline]]
+
+Developers often need one model table to extend another model table. Inheritance being an object-oriented notion, it doesn't have a true equivalent in the database world, so this is something an ORM must emulate. Propel offers two types of table inheritance: [http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html Single Table Inheritance], which is the most efficient implementations from a SQL and query performance perspective, but is limited to a small number of inherited fields ; and [http://www.martinfowler.com/eaaCatalog/concreteTableInheritance.html Concrete Table Inheritance], which provides the most features but adds a small overhead on write queries.
+
+== Single Table Inheritance ==
+
+In this implementation, one table is used for all subclasses. This has the implication that your table must have all columns needed by the main class and subclasses. Propel will create stub subclasses.
+
+Let's illustrate this idea with an example. Consider an object model with three classes, `Book`, `Essay`, and `Comic` - the first class being parent of the other two. With single table inheritance, the data of all three classes is stored in one table, named `book`.
+
+=== Schema Definition ===
+
+A table using Single Table Inheritance requires a column to identify which class should be used to represent the ''table'' row. Classically, this column is named `class_key` - but you can choose whatever name fits your taste. The column needs the `inheritance="single"` attribute to make Propel understand that it's the class key column. Note that this 'key' column must be a real column in the table.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+}}}
+
+Once you rebuild your model, Propel generated all three model classes (`Book`, `Essay`, and `Comic`) and three query classes (`BookQuery`, `EssayQuery`, and `ComicQuery`). The `Essay` and `Comic` classes extend the `Book` class, the `EssayQuery` and `ComicQuery` classes extend `BookQuery`.
+
+'''Tip''': An inherited class can extend another inherited class. That mean that you can add a `Manga` kind of book that extends `Comic` instead of `Book`.
+
+=== Using Inherited Objects ===
+
+Use inherited objects just like you use regular Propel model objects:
+
+{{{
+#!php
+setTitle('War And Peace');
+$book->save();
+$essay = new Essay();
+$essay->setTitle('On the Duty of Civil Disobedience');
+$essay->save();
+$comic = new Comic();
+$comic->setTitle('Little Nemo In Slumberland');
+$comic->save();
+}}}
+
+Inherited objects share the same properties and methods by default, but you can add your own logic to each of the generated classes.
+
+Behind the curtain, Propel sets the `class_key` column based on the model class. So the previous code stores the following rows in the database:
+
+{{{
+id | title | class_key
+---|-----------------------------------|----------
+1 | War And Peace | Book
+2 | On the Duty of Civil Disobedience | Essay
+3 | Little Nemo In Slumberland | Comic
+}}}
+
+Incidentally, that means that you can add new classes manually, even if they are not defined as `` tags in the `schema.xml`:
+
+{{{
+#!php
+setClassKey('Novel');
+ }
+}
+$novel = new Novel();
+$novel->setTitle('Harry Potter');
+$novel->save();
+}}}
+
+=== Retrieving Inherited objects ===
+
+In order to retrieve books, use the Query object of the main class, as you would usually do. Propel will hydrate children objects instead of the parent object when necessary:
+
+{{{
+#!php
+find();
+foreach ($books as $book) {
+ echo get_class($book) . ': ' . $book->getTitle() . "\n";
+}
+// Book: War And Peace
+// Essay: On the Duty of Civil Disobedience
+// Comic: Little Nemo In Slumberland
+// Novel: Harry Potter
+}}}
+
+If you want to retrieve only objects of a certain class, use the inherited query classes:
+
+{{{
+#!php
+findOne();
+echo get_class($comic) . ': ' . $comic->getTitle() . "\n";
+// Comic: Little Nemo In Slumberland
+}}}
+
+'''Tip''': You can override the base peer's `getOMClass()` to return the classname to use based on more complex logic (or query).
+
+=== Abstract Entities ===
+
+If you wish to enforce using subclasses of an entity, you may declare a table "abstract" in your XML data model:
+
+{{{
+#!xml
+
+ ...
+}}}
+
+That way users will only be able to instanciate `Essay` or `Comic` books, but not `Book`.
+
+== Concrete Table Inheritance ==
+
+Concrete Table Inheritance uses one table for each class in the hierarchy. Each table contains columns for the class and all its ancestors, so any fields in a superclass are duplicated across the tables of the subclasses.
+
+Propel implements Concrete Table Inheritance through a behavior.
+
+=== Schema Definition ===
+
+Once again, this is easier to understand through an example. In a Content Management System, content types are often organized in a hierarchy, each subclass adding more fields to the superclass. So let's consider the following schema, where the `article` and `video` tables use the same fields as the main `content` tables, plus additional fields:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+Since the columns of the main table are copied to the child tables, this schema is a simple implementation of Concrete Table Inheritance. This is something that you can write by hand, but the repetition makes it tedious. Instead, you should let the `concrete_inheritance` behavior do it for you:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+'''Tip''': The `concrete_inheritance` behavior copies columns, foreign keys, indices and validators.
+
+=== Using Inherited Model Classes ===
+
+For each of the tables in the schema above, Propel generates a Model class:
+
+{{{
+#!php
+setName('Movie');
+$cat->save();
+// create a new Article
+$art = new Article();
+$art->setTitle('Avatar Makes Best Opening Weekend in the History');
+$art->setCategory($cat);
+$art->setContent('With $232.2 million worldwide total, Avatar had one of the best-opening weekends in the history of cinema.');
+$art->save();
+// create a new Video
+$vid = new Video();
+$vid->setTitle('Avatar Trailer');
+$vid->setCategory($cat);
+$vid->setResourceLink('http://www.avatarmovie.com/index.html')
+$vid->save();
+}}}
+
+And since the `concrete_inheritance` behavior tag defines a parent table, the `Article` and `Video` classes extend the `Content` class (same for the generated Query classes):
+
+{{{
+#!php
+getCategory()->getName();
+ }
+}
+echo $art->getCategoryName(); // 'Movie'
+echo $vid->getCategoryName(); // 'Movie'
+
+// methods of the parent query are accessible to the child query
+class ContentQuery extends BaseContentQuery
+{
+ public function filterByCategoryName($name)
+ {
+ return $this
+ ->useCategoryQuery()
+ ->filterByName($name)
+ ->endUse();
+ }
+}
+$articles = ArticleQuery::create()
+ ->filterByCategoryName('Movie')
+ ->find();
+}}}
+
+That makes of Concrete Table Inheritance a powerful way to organize your model logic and to avoid repetition, both in the schema and in the model code.
+
+=== Data Replication ===
+
+By default, every time you save an `Article` or a `Video` object, Propel saves a copy of the `title` and `category_id` columns in a `Content` object. Consequently, retrieving objects regardless of their child type becomes very easy:
+
+{{{
+#!php
+find();
+foreach ($conts as $content) {
+ echo $content->getTitle() . "(". $content->getCategoryName() ")/n";
+}
+// Avatar Makes Best Opening Weekend in the History (Movie)
+// Avatar Trailer (Movie)
+}}}
+
+Propel also creates a one-to-one relationship between a object and its parent copy. That's why the schema definition above doesn't define any primary key for the `article` and `video` tables: the `concrete_inheritance` behavior creates the `id` primary key which is also a foreign key to the parent `id` column. So once you have a parent object, getting the child object is just one method call away:
+
+{{{
+#!php
+getContent();
+ }
+}
+class Movie extends BaseMovie
+{
+ public function getPreview()
+ {
+ return $this->getResourceLink();
+ }
+}
+$conts = ContentQuery::create()->find();
+foreach ($conts as $content) {
+ echo $content->getTitle() . "(". $content->getCategoryName() ")/n"
+ if ($content->hasChildObject()) {
+ echo ' ' . $content->getChildObject()->getPreview(), "\n";
+}
+// Avatar Makes Best Opening Weekend in the History (Movie)
+// With $232.2 million worldwide total, Avatar had one of the best-opening
+// weekends in the history of cinema.
+// Avatar Trailer (Movie)
+// http://www.avatarmovie.com/index.html
+}}}
+
+The `hasChildObject()` and `getChildObject()` methods are automatically added by the behavior to the parent class. Behind the curtain, the saved `content` row has an additional `descendant_column` field allowing it to use the right model for the job.
+
+'''Tip''' You can disable the data replication by setting the `copy_data_to_parent` parameter to "false". In that case, the `concrete_inheritance` behavior simply modifies the table at buildtime and does nothing at runtime. Also, with `copy_data_to_parent` disabled, any primary key copied from the parent table is not turned into a foreign key:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+// results in
+
+
+
+
+
+
+
+
+
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/reference/Buildtime-Configuration.txt b/3rd_party/php/propel/docs/reference/Buildtime-Configuration.txt
new file mode 100644
index 000000000..36fbe642b
--- /dev/null
+++ b/3rd_party/php/propel/docs/reference/Buildtime-Configuration.txt
@@ -0,0 +1,312 @@
+= Build Properties Reference =
+
+[[PageOutline]]
+
+Here is a list of properties that can be set to affect how Propel builds database files. For a complete list, see the {{{default.properties}}} file that is bundled with your version of Propel generator (this will be in PEAR's data directory if you are using a PEAR-installed version of Propel).
+
+First, some conventions:
+
+ * Text surrounded by a '''/''' is text that you would provide and is not defined in the language. (i.e. a table name is a good example of this.)
+ * Items where you have an alternative choice have a '''|''' character between them (i.e. true|false)
+ * Alternative choices may be delimited by '''{''' and '''}''' to indicate that this is the default option, if not overridden elsewhere.
+
+== Where to Specify Properties ==
+
+=== In the Project build.properties File ===
+
+The most natural place to specify properties for a file are in the project's {{{build.properties}}} file. This file is expected to be found in the project directory.
+
+=== In a global build.properties file ===
+
+You can also create a {{{global build.properties}}} file in the same directory as Propel's {{{default.properties}}} file. For users who have installed Propel using PEAR, this will be in PEAR data directory structure.
+
+=== On the Command Line ===
+
+You can also specify properties on the commandline when you invoke Propel:
+
+{{{
+$ propel-gen /path/to/project -Dpropel.someOtherProperty=value
+}}}
+
+''Note that there is '''no space''' between the -D and the property name.''
+
+== The Properties ==
+
+=== General Build Settings ===
+
+{{{
+propel.project = Your-Project-Name
+}}}
+
+The name of your project. This affects names of generated files, etc.
+
+{{{
+propel.targetPackage = {propel.project}
+}}}
+
+The package to use for the generated classes. This affects the value of the @package phpdoc tag, and it also affects the directory that the classes are placed in. By default this will be the same as the project. Note that the target package (and thus the target directory for generated classes) can be overridden in each `` and `
` element in the XML schema.
+
+{{{
+propel.packageObjectModel = true|{false}
+}}}
+
+Whether to join schemas using the same database name into a single schema. This allows splitting schemas in packages, and referencing tables in another schema (but in the same database) in a foreign key. Beware that database behaviors will also be joined when this parameter is set to true.
+
+{{{
+propel.schema.validate = {true}|false
+}}}
+
+Whether to validate the schema using the XSD file. The default XSD file is located under `generator/resources/xsd/database.xsd`, and you can use a custom XSD file by changing the `propel.schema.xsd.file` property.
+
+{{{
+propel.schema.transform = true|{false}
+}}}
+
+Whether to transform the schema using the XSL file. This was used in previous Propel versions to clean up the schema, but tended to hide problems in the schema. It is disabled by default since Propel 1.5. The default XSL file is located under `generator/resources/xsd/database.xsl`, and you can use a custom XSL file by changing the `propel.schema.xsl.file` property.
+
+=== Database Settings ===
+
+{{{
+propel.database = pgsql|mysql|sqlite|mssql|oracle
+}}}
+
+The Propel platform that will be used to determine how to build the SQL DDL, etc.
+
+
+{{{
+propel.database.url = /PDO database connection string/
+propel.database.user =
+propel.database.password =
+}}}
+
+Propel will use this information as the default to connect to your database. Note that for PDO some drivers (e.g. mysql, oracle) require that you specify the username and password separately from the DSN, which is why they are available as options.
+
+
+{{{
+propel.database.buildUrl = /PDO database connection string, defaults to use ${propel.database.url}/
+}}}
+
+This property is used by Propel to connect to a database to reverse engineer or data dump. The default is to use the database connection defined by the ''propel.database.url'' property.
+
+
+{{{
+propel.database.createUrl = /PDO database connection string, defaults to use ${propel.database.url}/
+}}}
+
+This property is used by Propel for creating a database. Of course, Propel is unable to create many databases because they do not provide a SQL method for creation; therefore, it is usually recommended that you actually create your database by hand.
+
+{{{
+propel.database.schema = /schema-name/
+}}}
+
+Where supported by the RDBMS, you can specify a schema name for Propel to use.
+
+{{{
+propel.database.encoding =
+}}}
+The encoding to use for the database. This can affect things such as transforming charsets when exporting to XML, etc.
+
+{{{
+propel.tablePrefix = {empty}|string
+}}}
+
+Add a prefix to all the table names in the database. This does not affect the tables phpName. This setting can be overridden on a per-database basis in the schema.
+
+=== Reverse-Engineering Settings ===
+
+{{{
+propel.samePhpName = true|{false}
+}}}
+Whether to specify PHP names that are the same as the column names.
+
+{{{
+propel.addVendorInfo = true|{false}
+}}}
+Whether to add the vendor info. This is currently only used for MySQL, but does provide additional information (such as full-text indexes) which can affect the generation of the DDL from the schema.
+
+{{{
+propel.addValidators = {none}|maxvalue|type|required|unique|all
+}}}
+Which Propel validators to add to the generated schema (based on the db constraints).
+
+=== Customizing Generated Object Model ===
+
+{{{
+propel.addGenericAccessors = true|{false}
+propel.addGenericMutators = true|{false}
+}}}
+Whether to add generic getter/setter methods -- e.g. '''getByName()''', '''setByName()'''.
+
+{{{
+propel.addTimeStamp = true|{false}
+}}}
+Whether to add a timestamp to the phpdoc header of generated OM classes.
+
+{{{
+propel.addValidateMethod = {true}|false
+}}}
+Whether to add `validate()` method to your classes. Set to false if you don't use Propel validation.
+
+{{{
+propel.addIncludes = {true}|false
+}}}
+Whether to add `require` statements on the generated stub classes. Set to false if you autoload every classe at runtime.
+
+{{{
+propel.addHooks = {true}|false
+}}}
+Whether to support pre- and post- hooks on `save()` and `delete()` methods. Set to false if you never use these hooks for a small speed boost.
+
+{{{
+propel.basePrefix = {Base}|/YourPrefix/
+}}}
+The prefix to use for the base (super) classes that are generated.
+
+{{{
+propel.classPrefix = {empty}|string;
+}}}
+Some sort of "namespacing": All Propel classes with get the Prefix "My_ORM_Prefix_" just like "My_ORM_Prefix_BookPeer".
+
+{{{
+propel.disableIdentifierQuoting = true|{false}
+}}}
+Identifier quoting is only implemented at the DDL layer at this point. Since this may result in undesired behavior (especially in Postgres), it can be disabled by setting this property to true.
+
+{{{
+propel.useLeftJoinsInDoJoinMethods = {true}|false
+}}}
+Set whether the '''doSelectJoin*()''' methods use LEFT JOIN or INNER JOIN (see ticket:491 and ticket:588 to understand more about why this might be important).
+
+=== MySQL-specific Settings ===
+
+{{{
+propel.mysqlTableType = /DefaultTableEngine/
+}}}
+Default table engine - defaults to MyISAM. You can override this setting if you wish to default to another engine for all tables (for instance InnoDB, or HEAP). This setting can also be overridden on a per-table basis using the `` element in the schema (see [wiki:Documentation/1.5/Schema#AddingVendorInfo]).
+
+{{{
+propel.mysqlTableEngineKeyword = /EngineKeyword/
+}}}
+Keyword used to specify the table engine in the CREATE SQL statement. Defaults to 'ENGINE', users of MYSQL < 5 should use 'TYPE' instead.
+
+=== Date/Time Settings ===
+
+{{{
+propel.useDateTimeClass = true|{false}
+}}}
+This is how you enable full use of the new DateTime class in Propel. Setting this to true means that getter methods for date/time/timestamp columns will return a DateTime object ''when the default format is empty''. Note that the current default of ''false'' is only for backwards compatibility; in the future ''true'' will be the only option here.
+
+{{{
+propel.dateTimeClass = {DateTime}|string
+}}}
+Specify a custom DateTime subclass that you wish to have Propel use for temporal values.
+
+{{{
+propel.defaultTimeStampFormat = {Y-m-d H:i:s}|string
+propel.defaultTimeFormat = {%X}|string
+propel.defaultDateFormat = {%x}|string
+}}}
+These are the default formats that will be used when fetching values from temporal columns in Propel. You can always specify these when calling the methods directly, but for methods like getByName() it is nice to change the defaults.
+
+To have these methods return DateTime objects instead, you should set these to empty values, for example:
+{{{
+propel.defaultTimeStampFormat =
+}}}
+
+=== Directories ===
+
+{{{
+propel.project.dir = default-depends-on-installation-type
+}}}
+
+''This is not necessarily a property you can change.'' The project directory is the directory where you project files (build.properties, schema.xml, runtime-conf.xml, etc.) are located. For example, if you use the {{{propel-gen}}} script, this value will get overridden to the path you pass to {{{propel-gen}}}.
+
+{{{
+propel.output.dir = ${propel.project.dir}/build
+}}}
+The default top-level directory for output of classes, sql, config, etc.
+
+{{{
+propel.schema.dir = ${propel.project.dir}
+}}}
+The directory where Propel expects to find your schema.xml file.
+
+{{{
+propel.conf.dir = ${propel.project.dir}
+}}}
+The directory where Propel expects to find your {{{runtime-conf.xml}}} file.
+
+{{{
+propel.php.dir = ${propel.output.dir}/classes
+}}}
+The directory where Propel will create generated object model classes.
+
+{{{
+propel.phpconf.dir = ${propel.output.dir}/conf
+}}}
+The directory where Propel will place the php-ified version of your {{{runtime-conf.xml}}}.
+
+{{{
+propel.sql.dir = ${propel.output.dir}/sql
+}}}
+The directory where Propel will place generated DDL (or data insert statements, etc.)
+
+
+=== Overriding Builder Classes ===
+
+{{{
+# Object Model builders
+propel.builder.peer.class = propel.engine.builder.om.php5.PHP5ComplexPeerBuilder
+propel.builder.object.class = propel.engine.builder.om.php5.PHP5ComplexObjectBuilder
+propel.builder.objectstub.class = propel.engine.builder.om.php5.PHP5ExtensionObjectBuilder
+propel.builder.peerstub.class = propel.engine.builder.om.php5.PHP5ExtensionPeerBuilder
+
+propel.builder.objectmultiextend.class = propel.engine.builder.om.php5.PHP5MultiExtendObjectBuilder
+
+propel.builder.tablemap.class = propel.engine.builder.om.php5.PHP5TableMapBuilder
+
+propel.builder.interface.class = propel.engine.builder.om.php5.PHP5InterfaceBuilder
+
+propel.builder.node.class = propel.engine.builder.om.php5.PHP5NodeBuilder
+propel.builder.nodepeer.class = propel.engine.builder.om.php5.PHP5NodePeerBuilder
+propel.builder.nodestub.class = propel.engine.builder.om.php5.PHP5ExtensionNodeBuilder
+propel.builder.nodepeerstub.class = propel.engine.builder.om.php5.PHP5ExtensionNodePeerBuilder
+
+propel.builder.nestedset.class = propel.engine.builder.om.php5.PHP5NestedSetBuilder
+propel.builder.nestedsetpeer.class = propel.engine.builder.om.php5.PHP5NestedSetPeerBuilder
+
+# SQL builders
+
+propel.builder.ddl.class = propel.engine.builder.sql.${propel.database}.${propel.database}DDLBuilder
+propel.builder.datasql.class = propel.engine.builder.sql.${propel.database}.${propel.database}DataSQLBuilder
+
+# Platform classes
+
+propel.platform.class = propel.engine.platform.${propel.database}Platform
+
+# Pluralizer class (used to generate plural forms)
+
+propel.builder.pluralizer.class = propel.engine.builder.util.DefaultEnglishPluralizer
+}}}
+
+As you can see, you can specify your own builder and platform classes if you want to extend & override behavior in the default classes
+
+=== Adding Behaviors ===
+
+{{{
+propel.behavior.timestampable.class = propel.engine.behavior.TimestampableBehavior
+}}}
+
+Define the path to the class to be used for the `timestampable` behavior. This behavior is bundled wit hPropel, but if you want to override it, you can specify a different path.
+
+If you want to add more behaviors, write their path following the same model:
+
+{{{
+propel.behavior.my_behavior.class = my.custom.path.to.MyBehaviorClass
+}}}
+
+Behaviors are enabled on a per-table basis in the `schema.xml`. However, you can add behaviors for all your schemas, provided that you define them in the `propel.behavior.default` setting:
+
+{{{
+propel.behavior.default = soft_delete,my_behavior
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/reference/ModelCriteria.txt b/3rd_party/php/propel/docs/reference/ModelCriteria.txt
new file mode 100644
index 000000000..d5c4042d3
--- /dev/null
+++ b/3rd_party/php/propel/docs/reference/ModelCriteria.txt
@@ -0,0 +1,1025 @@
+= Propel Query Reference =
+
+[[PageOutline]]
+
+Propel's Query classes make it easy to write queries of any level of complexity in a simple and reusable way.
+
+== Overview ==
+
+Propel proposes an object-oriented API for writing database queries. That means that you don't need to write any SQL code to interact with the database. Object orientation also facilitates code reuse and readability. Here is how to query the database for records in the `book` table ordered by the `title` column and published in the last month:
+
+{{{
+#!php
+filterByPublishedAt(array('min' => time() - 30 * 24 * 60 * 60))
+ ->orderByTitle()
+ ->find();
+}}}
+
+The first thing to notice here is the fluid interface. Propel queries are made of method calls that return the current query object - `filterByPublishedAt()` and `orderByTitle()` return the current query augmented with conditions. `find()`, on the other hand, is a ''termination method'' that doesn't return the query, but its result - in this case, a collection of `Book` objects.
+
+Propel generates one `filterByXXX()` method for every column in the table. The column name used in the PHP method is not the actual column name in the database ('`published_at`'), but rather a CamelCase version of it ('`PublishedAt`'), called the column ''phpName''. Remember to always use the phpName in the PHP code ; the actual SQL name only appears in the SQL code.
+
+When a termination method like `find()` is called, Propel builds the SQL code and executes it. The previous example generates the following code when `find()` is called:
+
+{{{
+#!php
+= :p1
+ORDER BY book.TITLE ASC';
+}}}
+
+Propel uses the column name in conjunction with the schema to determine the column type. In this case, `published_at` is defined in the schema as a `TIMESTAMP`. Then, Propel ''binds'' the value to the condition using the column type. This prevents SQL injection attacks that often plague web applications. Behind the curtain, Propel uses PDO to achieve this binding:
+
+{{{
+#!php
+prepare($query);
+$stmt->bind(':p1', time() - 30 * 24 * 60 * 60, PDO::PARAM_INT);
+$res = $stmt->execute();
+}}}
+
+The final `find()` doesn't just execute the SQL query above, it also instanciates `Book` objects and populates them with the results of the query. Eventually, it returns a `PropelCollection` object with these `Book` objects inside. For the sake of clarity, you can consider this collection object as an array. In fact, you can use it as if it were a true PHP array and iterate over the result list the usual way:
+
+{{{
+#!php
+getTitle();
+}
+}}}
+
+So Propel queries are a very powerful tool to write your queries in an object-oriented fashion. They are also very natural - if you know how to write an SQL query, chances are that you will write Propel queries in minutes.
+
+== Generated Query Methods ==
+
+For each object, Propel creates a few methods in the generated query object.
+
+=== Column Filter Methods ===
+
+`filterByXXX()`, generated for each column, provides a different feature and a different functionality depending on the column type:
+
+ * For all columns, `filterByXXX()` translates to a simple SQL `WHERE` condition by default:
+
+{{{
+#!php
+filterByTitle('War And Peace')
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from `book`
+WHERE book.TITLE = :p1'; // :p1 => 'War And Peace'
+}}}
+
+ * For string columns, `filterByXXX()` translates to a SQL `WHERE ... LIKE` if the value contains wildcards:
+
+{{{
+#!php
+filterByTitle('War%')
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from `book`
+WHERE book.TITLE LIKE :p1'; // :p1 => 'War%'
+}}}
+
+ * For integer columns, `filterByXXX()` translates into a SQL `WHERE ... IN` if the value is an array:
+
+{{{
+#!php
+filterByAuthorId(array(123, 456))
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from `book`
+WHERE book.AUTHOR_ID IN (:p1, :p2)'; // :p1 => 123, :p2 => 456
+}}}
+
+ * For Boolean columns, `filterByXXX()` translates the value to a boolean using smart casting:
+
+{{{
+#!php
+filterByIsPublished('yes')
+ ->filterByIsSoldOut('no')
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from `book`
+WHERE book.IS_PUBLISHED = :p1
+ AND book.IS_SOLD_OUT = :p2'; // :p1 => true, :p2 => false
+}}}
+
+=== Relation Filter Methods ===
+
+Propel also generates a `filterByXXX()` method for every foreign key. The filter expects an object of the related class as parameter:
+
+{{{
+#!php
+findPk(123);
+$books = BookQuery::create()
+ ->filterByAuthor($author)
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from `book`
+WHERE book.AUTHOR_ID = :p1'; // :p1 => 123
+}}}
+
+Check the generated BaseQuery classes for a complete view of the generated query methods. Every generated method comes with a detailed phpDoc comment, making code completion very easy on supported IDEs.
+
+=== Embedding a Related Query ===
+
+In order to add conditions on related tables, a propel query can ''embed'' the query of the related table. The generated `useXXXQuery()` serve that purpose. For instance, here is how to query the database for books written by 'Leo Tolstoi':
+
+{{{
+#!php
+useAuthorQuery()
+ ->filterByName('Leo Tolstoi')
+ ->endUse()
+ ->find();
+}}}
+
+`useAuthorQuery()` returns a new instance of `AuthorQuery` already joined with the current `BookQuery` instance. The next method is therefore called on a different object - that's why the `filterByName()` call is further indented in the code example. Finally, `endUse()` merges the conditions applied on the `AuthorQuery` to the `BookQuery`, and returns the original `BookQuery` object.
+
+Propel knows how to join the `Book` model to the `Author` model, since you already defined a foreign key between the two tables in the `schema.xml`. Propel takes advantage of this knowledge of your model relationships to help you write faster queries and omit the most obvious data.
+
+{{{
+#!php
+ 'Leo Tolstoi'
+}}}
+
+You can customize the related table alias and the join type by passing arguments to the `useXXXQuery()` method:
+
+{{{
+#!php
+useAuthorQuery('a', 'left join')
+ ->filterByName('Leo Tolstoi')
+ ->endUse()
+ ->find();
+// example Query generated for a MySQL database
+$query = 'SELECT book.* from book
+LEFT JOIN author a ON book.AUTHOR_ID = a.ID
+WHERE a.NAME = :p1'; // :p1 => 'Leo Tolstoi'
+}}}
+
+The `useXXXQuery()` methods allow for very complex queries. You can mix them, nest them, and reopen them to add more conditions.
+
+== Inherited Methods ==
+
+The generated Query classes extend a core Propel class named `ModelCriteria`, which provides even more methods for building your queries.
+
+=== Finding An Object From Its Primary Key ===
+
+{{{
+#!php
+findPk(123);
+// Finding the books having primary keys 123 and 456
+$books = BookQuery::create()->findPks(array(123, 456));
+// Also works for objects with composite primary keys
+$bookOpinion = BookOpinionQuery::create()->findPk(array($bookId, $userId));
+}}}
+
+=== Finding Objects ===
+
+{{{
+#!php
+find();
+// Finding 3 Books
+$articles = BookQuery::create()
+ ->limit(3)
+ ->find();
+// Finding a single Book
+$article = BookQuery::create()
+ ->findOne();
+}}}
+
+=== Using Magic Query Methods ===
+
+{{{
+#!php
+findOneByTitle('War And Peace');
+// same as
+$book = BookQuery::create()
+ ->filterByTitle('War And Peace')
+ ->findOne();
+
+$books = BookQuery::create()->findByTitle('War And Peace');
+// same as
+$books = BookQuery::create()
+ ->filterByTitle('War And Peace')
+ ->find();
+
+// You can even combine several column conditions in a method name, if you separate them with 'And'
+$book = BookQuery::create()->findOneByTitleAndAuthorId('War And Peace', 123);
+// same as
+$book = BookQuery::create()
+ ->filterByTitle('War And Peace')
+ ->filterById(123)
+ ->findOne();
+}}}
+
+=== Ordering Results ===
+
+{{{
+#!php
+orderByPublishedAt()
+ ->find();
+// Finding all Books ordered by published_at desc
+$books = BookQuery::create()
+ ->orderByPublishedAt('desc')
+ ->find();
+}}}
+
+=== Specifying A Connection ===
+
+{{{
+#!php
+findOne($con);
+}}}
+
+'''Tip''': In debug mode, the connection object provides a way to check the latest executed query, by calling `$con->getLastExecutedQuery()`. See the [wiki:Documentation/1.5/07-Logging Logging documentation] for more details.
+
+=== Counting Objects ===
+
+{{{
+#!php
+count($con);
+// This is much faster than counting the results of a find()
+// since count() doesn't populate Model objects
+}}}
+
+=== Deleting Objects ===
+
+{{{
+#!php
+deleteAll($con);
+// Deleting a selection of Books
+$nbDeletedBooks = BookQuery::create()
+ ->filterByTitle('Pride And Prejudice')
+ ->delete($con);
+}}}
+
+=== Updating Objects ===
+
+{{{
+#!php
+setName('Jane Austen');
+$author1->save();
+$author2 = new Author();
+$author2->setName('Leo Tolstoy');
+$author2->save();
+
+// update() issues an UPDATE ... SET query based on an associative array column => value
+$nbUpdatedRows = AuthorQuery::create()
+ ->filterByName('Leo Tolstoy')
+ ->update(array('Name' => 'Leo Tolstoi'), $con);
+
+// update() returns the number of modified columns
+echo $nbUpdatedRows; // 1
+
+// Beware that update() updates all records found in a single row
+// And bypasses any behavior registered on the save() hooks
+// You can force a one-by-one update by setting the third parameter of update() to true
+$nbUpdatedRows = AuthorQuery::create()
+ ->filterByName('Leo Tolstoy')
+ ->update(array('Name' => 'Leo Tolstoi'), $con, true);
+// Beware that it may take a long time
+}}}
+
+=== Creating An Object Based on a Query ===
+
+You may often create a new object based on values used in conditions if a query returns no result. This happens a lot when dealing with cross-reference tables in many-to-many relationships. To avoid repeating yourself, use `findOneOrCreate()` instead of `findOne()` in such cases:
+
+{{{
+#!php
+filterByBook($book)
+ ->filterByTag('crime')
+ ->findOne();
+if (!$bookTag) {
+ $bookTag = new BookTag();
+ $bookTag->setBook($book);
+ $bookTag->setTag('crime');
+}
+// The short way
+$bookTag = BookTagQuery::create()
+ ->filterByBook($book)
+ ->filterByTag('crime')
+ ->findOneOrCreate();
+}}}
+
+=== Reusing A Query ===
+
+By default, termination methods like `findOne()`, `find()`, `count()`, `paginate()`, or `delete()` alter the original query. That means that if you need to reuse a query after a termination method, you must call the `keepQuery()` method first:
+
+{{{
+#!php
+filterByIsPublished(true);
+$book = $q->findOneByTitle('War And Peace');
+// findOneByXXX() adds a limit() to the query
+// so further reuses of the query may show side effects
+echo $q->count(); // 1
+
+// to allos query reuse, call keepQuery() before the termination method
+$q = BookQuery::create()->filterByIsPublished(true)->keepQuery();
+$book = $q->findOneByTitle('War And Peace');
+echo $q->count(); // 34
+}}}
+
+== Relational API ==
+
+For more complex queries, you can use an alternative set of methods, closer to the relational logic of SQL, to make sure that Propel issues exactly the SQL query you need.
+
+This alternative API uses methods like `where()`, `join()` and `orderBy()` that translate directly to their SQL equivalent - `WHERE`, `JOIN`, etc. Here is an example:
+
+{{{
+#!php
+join('Book.Author')
+ ->where('Author.Name = ?', 'Leo Tolstoi')
+ ->orderBy('Book.Title', 'asc')
+ ->find();
+
+}}}
+
+The names passed as parameters in these methods, like 'Book.Author', 'Author.Name', and 'Book.Title', are ''explicit column names''. These names are composed of the phpName of the model, and the phpName of the column, separated by a dot (e.g. 'Author.Name'). Manipulating object model names allows you to be detached from the actual data storage, and alter the database names without necessarily updating the PHP code. It also makes the use of table aliases much easier - more on that matter later.
+
+Propel knows how to map the explicit column names to database column names in order to translate the Propel query into an actual database query:
+
+{{{
+#!php
+where('Book.Title = ?', 'War And Peace')
+ ->find();
+// Finding all Books where title is like 'War%'
+$books = BookQuery::create()
+ ->where('Book.Title LIKE ?', 'War%')
+ ->find();
+// Finding all Books published after $date
+$books = BookQuery::create()
+ ->where('Book.PublishedAt > ?', $date)
+ ->find();
+// Finding all Books with no author
+$books = BookQuery::create()
+ ->where('Book.AuthorId IS NULL')
+ ->find();
+// Finding all books from a list of authors
+$books = BookQuery::create()
+ ->where('Book.AuthorId IN ?', array(123, 542, 563))
+ ->find();
+// You can even use SQL functions inside conditions
+$books = BookQuery::create()
+ ->where('UPPER(Book.Title) = ?', 'WAR AND PEACE')
+ ->find();
+}}}
+
+=== Combining Several Conditions ===
+
+For speed reasons, `where()` only accepts simple conditions, with a single interrogation point for the value replacement. When you need to apply more than one condition, and combine them with a logical operator, you have to call `where()` multiple times.
+
+{{{
+#!php
+where('Book.Title = ?', 'War And Peace')
+ ->where('Book.PublishedAt > ?', $date)
+ ->find();
+// For conditions chained with OR, use orWhere() instead of where()
+$books = BookQuery::create()
+ ->where('Book.Title = ?', 'War And Peace')
+ ->orWhere('Book.Title LIKE ?', 'War%')
+ ->find();
+}}}
+
+The use of `where()` and `orWhere()` doesn't allow logically complex conditions, that you would write in SQL with parenthesis. For such cases, create named conditions with `condition()`, and then combine them in an array that you can pass to `where()` instead of a single condition, as follows:
+
+{{{
+#!php
+condition('cond1', 'Book.Title = ?', 'War And Peace') // create a condition named 'cond1'
+ ->condition('cond2', 'Book.Title LIKE ?', 'War%') // create a condition named 'cond2'
+ ->where(array('cond1', 'cond2'), 'or')-> // combine 'cond1' and 'cond2' with a logical OR
+ ->find();
+ // SELECT book.* from book WHERE (book.TITLE = 'War And Peace' OR book.TITLE LIKE 'War%');
+
+// You can create a named condition from the combination of other named conditions by using `combine()`
+// That allows for any level of complexity
+$books = BookQuery::create()
+ ->condition('cond1', 'Book.Title = ?', 'War And Peace') // create a condition named 'cond1'
+ ->condition('cond2', 'Book.Title LIKE ?', 'War%') // create a condition named 'cond2'
+ ->combine(array('cond1', 'cond2'), 'or', 'cond12') // create a condition named 'cond12' from 'cond1' and 'cond2'
+ ->condition('cond3', 'Book.PublishedAt <= ?', $end) // create a condition named 'cond3'
+ ->condition('cond4', 'Book.PublishedAt >= ?', $begin) // create a condition named 'cond4'
+ ->combine(array('cond3', 'cond4'), 'and', 'cond34') // create a condition named 'cond34' from 'cond3' and 'cond4'
+ ->where(array('cond12', 'cond34'), 'and') // combine the two conditions in a where
+ ->find();
+ // SELECT book.* FROM book WHERE (
+ // (book.TITLE = 'War And Peace' OR book.TITLE LIKE 'War%')
+ // AND
+ // (book.PUBLISHED_AT <= $end AND book.PUBLISHED_AT >= $begin)
+ // );
+}}}
+
+=== Joining Tables ===
+
+{{{
+#!php
+setName('Jane Austen');
+$author1->save();
+$book1 = new Book();
+$book1->setTitle('Pride And Prejudice');
+$book1->setAuthor($author1);
+$book1->save();
+
+// Add a join statement
+// No need to tell the query which columns to use for the join, just the related Class
+// After all, the columns of the FK are already defined in the schema.
+$book = BookQuery::create()
+ ->join('Book.Author')
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+ // SELECT book.* FROM book
+ // INNER JOIN author ON book.AUTHOR_ID = author.ID
+ // WHERE author.NAME = 'Jane Austin'
+ // LIMIT 1;
+
+// The default join() call results in a SQL INNER JOIN clause
+// For LEFT JOIN or RIGHT JOIN clauses, use leftJoin() or rightJoin() instead of join()
+$book = BookQuery::create()
+ ->leftJoin('Book.Author')
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+
+// You can chain joins if you want to make more complex queries
+$review = new Review();
+$review->setBook($book1);
+$review->setRecommended(true);
+$review->save();
+
+$author = BookQuery::create()
+ ->join('Author.Book')
+ ->join('Book.Review')
+ ->where('Review.Recommended = ?', true)
+ ->findOne();
+
+// Alternatively, you can use the generated joinXXX() methods
+// Which are a bit faster than join(), but limited to the current model's relationships
+$book = BookQuery::create()
+ ->joinAuthor()
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+// The join type depends on the required attribute of the foreign key column
+// If the column is required, then the default join type is an INNER JOIN
+// Otherwise, the default join type is a LEFT JOIN
+// You can override the default join type for a given relationship
+// By setting the joinType attribute of the foreign key element in the schema.xml
+}}}
+
+=== Table Aliases ===
+
+{{{
+#!php
+where('b.Title = ?', 'Pride And Prejudice')
+ ->find();
+
+// join(), leftJoin() and rightJoin() also allow table aliases
+$author = AuthorQuery::create('a')
+ ->join('a.Book b')
+ ->join('b.Review r')
+ ->where('r.Recommended = ?', true)
+ ->findOne();
+
+// Table aliases can be used in all query methods (where, groupBy, orderBy, etc.)
+$books = BookQuery::create('b')
+ ->where('b.Title = ?', 'Pride And Prejudice')
+ ->orderBy('b.Title')
+ ->find();
+
+// Table aliases are mostly useful to join the current table,
+// or to handle multiple foreign keys on the same column
+$employee = EmployeeQuery::create('e')
+ ->innerJoin('e.Supervisor s')
+ ->where('s.Name = ?', 'John')
+ ->find();
+}}}
+
+=== Minimizing Queries ===
+
+Even if you do a join, Propel will issue new queries when you fetch related objects:
+
+{{{
+#!php
+join('Book.Author')
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+$author = $book->getAuthor(); // Needs another database query
+}}}
+
+Propel allows you to retrieve the main object together with related objects in a single query. You just have to call the `with()` method to specify which objects the main object should be hydrated with.
+
+{{{
+#!php
+join('Book.Author')
+ ->with('Author')
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+$author = $book->getAuthor(); // Same result, with no supplementary query
+}}}
+
+`with()` expects a relation name, as declared previously by `join()`. In practice, that means that `with()` and `join()` should always come one after the other. To avoid repetition, use `joinWith()` to both add a `join()` and a `with()` on a relation. So the shorter way to write the previous query is:
+
+{{{
+#!php
+joinWith('Book.Author')
+ ->where('Author.Name = ?', 'Jane Austen')
+ ->findOne();
+$author = $book->getAuthor(); // Same result, with no supplementary query
+}}}
+
+Since the call to `with()` adds the columns of the related object to the SELECT part of the query, and uses these columns to populate the related object, that means that `joinWith()` is slower and consumes more memory that `join()`. So use it only when you actually need the related objects afterwards.
+
+`with()` and `joinWith()` are not limited to immediate relationships. As a matter of fact, just like you can chain `join()` calls, you can chain `joinWith()` calls to populate a chain of objects:
+
+{{{
+#!php
+joinWith('Review.Book')
+ ->joinWith('Book.Author')
+ ->joinWith('Book.Publisher')
+ ->findOne();
+$book = $review->getBook() // No additional query needed
+$author = $book->getAuthor(); // No additional query needed
+$publisher = $book->getPublisher(); // No additional query needed
+}}}
+
+So `joinWith()` is very useful to minimize the number of database queries. As soon as you see that the number of queries necessary to perform an action is proportional to the number of results, adding `With` after `join()` calls is the trick to get down to a more reasonnable query count.
+
+=== Adding Columns ===
+
+Sometimes you don't need to hydrate a full object in addition to the main object. If you only need one additional column, the `withColumn()` method is a good alternative to `joinWith()`, and it speeds up the query:
+
+{{{
+#!php
+join('Book.Author')
+ ->withColumn('Author.Name', 'AuthorName')
+ ->findOne();
+$authorName = $book->getAuthorName();
+}}}
+
+Propel adds the 'with' column to the SELECT clause of the query, and uses the second argument of the `withColumn()` call as a column alias. This additional column is later available as a 'virtual' column, i.e. using a getter that does not correspond to a real column. You don't actually need to write the `getAuthorName()` method ; Propel uses the magic `__call()` method of the generated `Book` class to catch the call to a virtual column.
+
+`withColumn()` is also of great use to add calculated columns:
+
+{{{
+#!php
+join('Author.Book')
+ ->withColumn('COUNT(Book.Id)', 'NbBooks')
+ ->groupBy('Author.Id')
+ ->find();
+foreach ($authors as $author) {
+ echo $author->getName() . ': ' . $author->getNbBooks() . " books\n";
+}
+}}}
+
+With a single SQL query, you can have both a list of objects and an additional column for each object. That makes of `withColumn()` a great query saver.
+
+'''Tip''': You can call `withColumn()` multiple times to add more than one virtual column to the resulting objects.
+
+=== Adding A Comment ===
+
+{{{
+#!php
+setComment('Author Deletion')
+ ->filterByName('Leo Tolstoy')
+ ->delete($con);
+// The comment ends up in the generated SQL query
+// DELETE /* Author Deletion */ FROM `author` WHERE author.NAME = 'Leo Tolstoy'
+}}}
+
+=== Using Methods From Another Query Class ===
+
+After writing custom methods to query objects, developers often meet the need to use the method from another query. For instance, in order to select the authors of the most recent books, you may want to write:
+
+{{{
+#!php
+join('Author.Book')
+ ->recent()
+ ->find();
+}}}
+
+The problem is that `recent()` is a method of `BookQuery`, not of the `AuthorQuery` class that the `create()` factory returns.
+
+Does that mean that you must repeat the `BookQuery::recent()` code into a new `AuthorQuery::recentBooks()` method? That would imply repeating the same code in two classes, which is not a good practice. Instead, use the `useQuery()` and `endUse()` combination to use the methods of `BookQuery` inside `AuthorQuery`:
+
+{{{
+#!php
+join('Author.Book')
+ ->useQuery('Book')
+ ->recent()
+ ->endUse()
+ ->find();
+}}}
+
+This is excatly whath the generated `useBookQuery()` does, except that you have more control over the join type and alias when you use the relational API. Behind the scene, `useQuery('Book')` creates a `BookQuery` instance and returns it. So the `recent()` call is actually called on `BookQuery`, not on `ArticleQuery`. Upon calling `endUse()`, the `BookQuery` merges into the original `ArticleQuery` and returns it. So the final `find()` is indeed called on the `AuthorQuery` instance.
+
+You can nest queries in as many levels as you like, in order to avoid the repetition of code in your model.
+
+'''Tip''': If you define an alias for the relation in `join()`, you must pass this alias instead of the model name in `useQuery()`.
+
+{{{
+#!php
+join('a.Book b')
+ ->useQuery('b')
+ ->recent()
+ ->endUse()
+ ->find();
+}}}
+
+=== Fluid Conditions ===
+
+Thanks to the query factories and the fluid interface, developers can query the database without creating a variable for the Query object. This helps a lot to reduce the amount of code necessary to write a query, and it also makes the code more readable.
+
+But when you need to call a method on a Query object only if a certain condition is satisfied, it becomes compulsory to use a variable for the Query object:
+
+{{{
+#!php
+isEditor()) {
+ $query->where('Book.IsPublished = ?', true);
+}
+$books = $query
+ ->orderByTitle()
+ ->find();
+}}}
+
+The `ModelCriteria` class offers a neat way to keep your code to a minimum in such occasions. It provides `_if()` and `_endif()` methods allowing for inline conditions. Using thses methods, the previous query can be written as follows:
+
+{{{
+#!php
+_if(!$user->isEditor())
+ ->where('Book.IsPublished = ?', true)
+ ->_endif()
+ ->orderByTitle()
+ ->find();
+}}}
+
+The method calls enclosed between `_if($cond)` and `_endif()` will only be executed if the condition is true. To complete the list of tools available for fluid conditions, you can also use `_else()` and `_elseif($cond)`.
+
+=== More Complex Queries ===
+
+The Propel Query objects have even more methods that allow you to write queries of any level of complexity. Check the API documentation for the `ModelCriteria` class to see all methods.
+
+{{{
+#!php
+find(); // $books behaves like an array
+?>
+There are books:
+
+
+
+ getTitle() ?>
+
+
+
+
+find(); // $books is an object
+?>
+
+isEmpty()): ?>
+There are no books.
+
+There are count() ?> books:
+
+
+
+ getTitle() ?>
+
+ isLast()): ?>
+
Do you want more books?
+
+
+
+
+}}}
+
+Here is the list of methods you can call on a PropelCollection:
+
+{{{
+#!php
+setFormatter('PropelArrayFormatter')
+ ->findOne();
+print_r($book);
+ => array('Id' => 123, 'Title' => 'War And Peace', 'ISBN' => '3245234535', 'AuthorId' => 456, 'PublisherId' => 567)
+}}}
+
+Of course, the formatters take the calls to `with()` into account, so you can end up with a precise array representation of a model object:
+
+{{{
+#!php
+setFormatter('PropelArrayFormatter')
+ ->with('Book.Author')
+ ->with('Book.Publisher')
+ ->findOne();
+print_r($book);
+ => array(
+ 'Id' => 123,
+ 'Title' => 'War And Peace',
+ 'ISBN' => '3245234535',
+ 'AuthorId' => 456,
+ 'PublisherId' => 567
+ 'Author' => array(
+ 'Id' => 456,
+ 'FirstName' => 'Leo',
+ 'LastName' => 'Tolstoi'
+ ),
+ 'Publisher' => array(
+ 'Id' => 567,
+ 'Name' => 'Penguin'
+ )
+ )
+}}}
+
+Propel provides four formatters:
+ * `PropelObjectFormatter`: The default formatter, returning a model object for `findOne()`, and a `PropelObjectCollection` of model objects for `find()`
+ * `PropelOnDemandFormatter`: To save memory for large resultsets, prefer this formatter ; it hydrates rows one by one as they are iterated on, and doesn't create a new Propel Model object at each row. Note that this formatter doesn't use the Instance Pool.
+ * `PropelArrayFormatter`: The array formatter, returning an associative array for `findOne()`, and a `PropelArrayCollection` of arrays for `find()`
+ * `PropelStatementFormatter`: The 'raw' formatter, returning a `PDOStatement` in any case.
+
+You can easily write your own formatter to format the resultas the way you want. A formatter is basically a subclass of `PropelFormatter` providing a `format()` and a `formatOne()` method expecting a PDO statement.
+
+== Writing Your Own business Logic Into A Query ==
+
+=== Custom Filters ===
+
+You can add custom methods to the query objects to make your queries smarter, more reusable, and more readable. Don't forget to return the current object (`$this`) in the new methods.
+
+{{{
+#!php
+filterByPublishedAt(array('min' => time() - $nbDays * 24 * 60 * 60));
+ }
+
+ public function mostRecentFirst()
+ {
+ return $this->orderByPublishedAt('desc');
+ }
+}
+
+// You can now use your custom query and its methods together with the usual ones
+$books = BookQuery::create()
+ ->recent()
+ ->mostRecentFirst()
+ ->find();
+}}}
+
+=== Custom Hooks ===
+
+The query objects also allow you to add code to be executed before each query, by implementing one of the following methods: `preSelect()`, `preUpdate()`, and `preDelete()`. It makes the implementation of a 'soft delete' behavior very straightforward:
+
+{{{
+#!php
+filterByDeletedAt(null);
+ }
+
+ public function preDelete($con)
+ {
+ // mark the records as deleted instead of deleting them
+ return $this->update(array('DeletedAt' => time()));
+ }
+}
+}}}
+
+'''Tip''': You can create several custom queries for a given model, in order to separate the methods into logical classes.
+
+{{{
+#!php
+where($this->getModelAliasOrName() . '.PublishedAt IS NOT NULL');
+ }
+}
+// Use 'frontendBook' instead of 'Book' in the frontend to retrieve only published articles
+$q = new frontendBookQuery();
+$books = $q->find();
+}}}
+
+'''Tip''': Due to late static binding issues in PHP 5.2, you cannot use the `create()` factory on an inherited query - unless you override it yoursel in the descendant class. Alternatively, Propel offers a global query factory named `PropelQuery`:
+
+{{{
+#!php
+find();
+}}}
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/reference/Runtime-Configuration.txt b/3rd_party/php/propel/docs/reference/Runtime-Configuration.txt
new file mode 100644
index 000000000..256c0a45e
--- /dev/null
+++ b/3rd_party/php/propel/docs/reference/Runtime-Configuration.txt
@@ -0,0 +1,318 @@
+= Runtime Configuration File =
+
+[[PageOutline]]
+
+== Example {{{runtime-conf.xml}}} File ==
+
+Here is a the sample runtime configuration file.
+
+{{{
+#!xml
+
+
+
+ propel-bookstore
+ console
+ 7
+
+
+
+
+ sqlite
+
+ DebugPDO
+ mysql:host=localhost;dbname=bookstore
+ testuser
+ password
+
+
+
+
+
+
+
+ utf8
+
+ set search_path myschema, public
+ INSERT INTO BAR ('hey', 'there')
+
+
+
+
+
+ mysql:host=slave-server1; dbname=bookstore
+
+
+ mysql:host=slave-server2; dbname=bookstore
+
+
+
+
+
+
+
+
+ true
+
+
+
+ true
+ 1
+
+
+
+
+
+
+}}}
+
+== Explanation of Configuration Sections ==
+
+Below you will find an explanation of the primary elements in the configuration.
+
+=== ===
+
+If the '''''' element is present, Propel will use the specified information to instantiate a [http://pear.php.net/Log PEAR Log] logger.
+
+{{{
+#!xml
+
+
+ file
+ /path/to/logger.log
+ my-app
+ 7
+
+}}}
+
+The nested elements correspond to the configuration options for the logger (options that would otherwise be passed to '''Log::factory()''' method).
+
+||'''Element'''||'''Default'''||'''Description'''||
+||''''''||file||The logger type.||
+||''''''||./propel.log||Name of log, meaning is dependent on type specified. (For ''file'' type this is the filename).||
+||''''''||propel||The identifier tag for the log.||
+||''''''||7 (PEAR_LOG_DEBUG)||The logging level.||
+
+This log configuring API is designed to provide a simple way to get log output from Propel; however, if your application already has a logging mechanism, we recommend instead that you use your existing logger (writing a simple log adapter, if you are using an unsupported logger). See the [wiki:Documentation/1.5/07-Logging Logging documentation] for more info.
+
+=== ===
+
+This is the top-level tag for Propel datasources configuration.
+
+{{{
+#!xml
+
+
+
+}}}
+
+=== ===
+
+{{{
+#!xml
+
+
+
+
+}}}
+A specific datasource being configured.
+ * The @id must match the @name attribute from your {{{schema.xml}}}.
+
+=== ===
+
+The adapter to use for Propel. Currently supported adapters: sqlite, pgsql, mysql, oracle, mssql. Note that it is possible that your adapter could be different from your connection driver (e.g. if using ODBC to connect to MSSQL database, you would use an ODBC PDO driver, but MSSQL Propel adapter).
+
+{{{
+#!xml
+
+
+
+
+ sqlite
+}}}
+
+=== ===
+
+The PDO database connection for the specified datasource.
+
+Nested elements define the DSN, connection options, other PDO attributes, and finally some Propel-specific initialization settings.
+
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+==== ====
+
+A custom PDO class (must be a PropelPDO subclass) that you would like to use for the PDO connection.
+
+{{{
+#!xml
+
+
+
+
+
+ DebugPDO
+}}}
+
+This can be used to specify the alternative '''DebugPDO''' class bundled with Propel, or your own subclass. ''Your class must extend PropelPDO, because Propel requires the ability to nest transactions (without having exceptions being thrown by PDO).''
+
+==== ====
+
+The PDO DSN that Propel will use to connect to the database for this datasource.
+
+{{{
+#!xml
+
+
+
+
+
+ mysql:host=localhost;dbname=bookstore
+}}}
+
+See the PHP documentation for specific format:
+ * [http://www.php.net/manual/en/ref.pdo-mysql.connection.php MySQL DSN]
+ * [http://www.php.net/manual/en/ref.pdo-pgsql.connection.php PostgreSQL DSN]
+ * [http://www.php.net/manual/en/ref.pdo-sqlite.connection.php SQLite DSN]
+ * [http://www.php.net/manual/en/ref.pdo-oci.connection.php Oracle DSN]
+ * [http://www.php.net/manual/en/ref.pdo-dblib.connection.php MSSQL DSN]
+
+Note that some database (e.g. PostgreSQL) specify username and password as part of the DSN while the others specify user and password separately.
+
+==== and ====
+
+Specifies credentials for databases that specify username and password separately (e.g. MySQL, Oracle).
+
+{{{
+#!xml
+
+
+
+
+
+ mysql:host=localhost;dbname=bookstore
+ test
+ testpass
+}}}
+
+==== ====
+
+Specify any options which ''must'' be specified when the PDO connection is created. For example, the ATTR_PERSISTENT option must be specified at object creation time.
+
+See the [http://www.php.net/pdo PDO documentation] for more details.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+}}}
+
+==== ====
+
+`` are similar to ``; the difference is that options specified in `` are set after the PDO object has been created. These are set using the [http://us.php.net/PDO-setAttribute PDO->setAttribute()] method.
+
+In addition to the standard attributes that can be set on the PDO object, there are also the following Propel-specific attributes that change the behavior of the PropelPDO connection:
+
+|| '''Attribute constant''' || '''Valid Values (Default)''' || '''Description''' ||
+|| PropelPDO::PROPEL_ATTR_CACHE_PREPARES || true/false (false) || Whether to have the PropelPDO connection cache the PDOStatement prepared statements. This will improve performance if you are executing the same query multiple times by your script (within a single request / script run). ||
+
+''Note that attributes in the XML can be specified with or without the PDO:: (or PropelPDO::) constant prefix.''
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+'''Tip''': If you are using MySQL and get the following error : "SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active", you can try adding the following attribute:
+
+{{{
+
+}}}
+
+==== ====
+
+Settings are Propel-specific options used to further configure the connection -- or perform other user-defined initialization tasks.
+
+Currently supported settings are:
+ * charset
+ * queries
+
+===== charset =====
+
+Specifies the character set to use. Currently you must specify the charset in the way that is understood by your RDBMS. Also note that not all database systems support specifying charset (e.g. SQLite must be compiled with specific charset support). Specifying this option will likely result in an exception if your database doesn't support the specified charset.
+
+{{{
+#!xml
+
+
+
+
+
+
+
+ utf8
+
+}}}
+
+===== queries =====
+
+Specifies any SQL statements to run when the database connection is initialized. This can be used for any environment setup or db initialization you would like to perform. These statements will be executed with every Propel initialization (e.g. every PHP script load).
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+ set search_path myschema, public
+ INSERT INTO BAR ('hey', 'there')
+
+
+}}}
+
+=== ===
+
+{{{
+#!xml
+
+
+
+
+
+}}}
+
+The `` tag groups slave `` elements which provide support for configuring slave db servers -- when using Propel in a master-slave replication environment. See the [wiki:Documentation/1.5/Master-Slave Master-Slave documentation] for more information. The nested `` elements are configured the same way as the top-level `` element is configured.
+
+=== ===
+
+The optional `` element may be provided to pass additional logging configuration options to DebugPDO. Note that these settings have no effect unless DebugPDO has been selected in [1.5/RuntimeConfiguration#debugpdo `runtime-conf.xml`] as the PDO connection class. See the [wiki:Documentation/1.5/07-Logging Logging documentation] for more information on configuring DebugPDO.
\ No newline at end of file
diff --git a/3rd_party/php/propel/docs/reference/Schema.txt b/3rd_party/php/propel/docs/reference/Schema.txt
new file mode 100644
index 000000000..b130c03fb
--- /dev/null
+++ b/3rd_party/php/propel/docs/reference/Schema.txt
@@ -0,0 +1,398 @@
+= Database Schema =
+
+[[PageOutline]]
+
+The schema for `schema.xml` contains a small number of elements with required and optional attributes. The Propel generator contains a [source:branches/1.5/generator/resources/dtd/database.dtd DTD] that can be used to validate your `schema.xml` document. Also, when you build your SQL and OM, the Propel generator will automatically validate your `schema.xml` file using a highly-detailed [source:branches/1.5/generator/resources/xsd/database.xsd XSD].
+
+== At-a-Glance ==
+
+The hierarchical tree relationship for the elements is:
+
+{{{
+#!xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}}}
+
+**Tip**: If you use an IDE supporting autocompletion in XML documents, you can take advantage of the XSD describing the `schema.xml` syntax to suggest elements and attributes as you type. To enable it, add a `xmlns:xsi` and a `xsi:noNamespaceSchemaLocation` attribute to the leading `` tag:
+
+{{{
+#!xml
+
+}}}
+
+== Detailed Reference ==
+
+This page provides an alternate rendering of the Appendix B - Schema Reference from the user's guide.
+It spells out in specific detail, just where each attribute or element belongs.
+
+First, some conventions:
+
+ * Text surrounded by a '''/''' is text that you would provide and is not defined in the language. (i.e. a table name is a good example of this.)
+ * Optional items are surrounded by '''[''' and ''']''' characters.
+ * Items where you have an alternative choice have a '''|''' character between them (i.e. true|false)
+ * Alternative choices may be delimited by '''{''' and '''}''' to indicate that this is the default option, if not overridden elsewhere.
+ * '''...''' means repeat the previous item.
+
+=== element ===
+
+Starting with the {{{}}} element. The ''attributes'' and ''elements'' available are:
+
+{{{
+#!xml
+
+
+
+ ...
+
+}}}
+
+The `package`, `baseClass`, `basePeer`, `defaultPhpNamingMethod`, and `heavyIndexing` attributes are generally optional.
+A Database element may include an `` element, or multiple `
` elements.
+
+ * `defaultIdMethod` sets the default id method to use for auto-increment columns.
+ * `package` specifies the "package" for the generated classes. Classes are created in subdirectories according to the `package` value.
+ * `namespace` specifies the default namespace that generated model classes will use (PHP 5.3 only). This attribute can be completed or overridden at the table level.
+ * `baseClass` allows you to specify a default base class that all generated Propel objects should extend (in place of `propel.om.BaseObject`).
+ * `basePeer` instructs Propel to use a different SQL-generating `BasePeer` class (or sub-class of `BasePeer`) for all generated objects.
+ * `defaultPhpNamingMethod` the default naming method to use for tables of this database. Defaults to `underscore`, which transforms table names into CamelCase phpNames.
+ * `heavyIndexing` adds indexes for each component of the primary key (when using composite primary keys).
+ * `tablePrefix` adds a prefix to all the SQL table names.
+
+=== element ===
+
+The `` element is pretty simple. It just includes a schema file from somewhere on the file systems. The format is:
+
+{{{
+#!xml
+
+}}}
+
+===
element ===
+
+The `
` element is the most complicated of the usable elements. Its definition looks like this:
+
+{{{
+#!xml
+
+
+
+ ...
+
+ ...
+
+ ...
+
+ ...
+
+ ...
+
+}}}
+
+According to the schema, `name` is the only required attribute. Also, the `idMethod`, `package`, `namespace`, `phpNamingMethod`, `baseClass`, `basePeer`, and `heavyIndexing` attributes all default to what is specified by the `` element.
+
+==== Description of Attributes ====
+
+ * `idMethod` sets the id method to use for auto-increment columns.
+ * `phpName` specifies object model class name. By default, Propel uses a CamelCase version of the table name as phpName.
+ * `package` specifies the "package" (or subdirectory) in which model classes get generated.
+ * `namespace` specifies the namespace that the generated model classes will use (PHP 5.3 only). If the table namespace starts with a `\`, it overrides the namespace defined in the `` tag; otherwise, the actual table namespace is the concatenation of the database namespace and the table namespace.
+ * `skipSql` instructs Propel not to generate DDL SQL for the specified table. This can be used together with `readOnly` for supperting VIEWS in Propel.
+ * `abstract` Whether the generated ''stub'' class will be abstract (e.g. if you're using inheritance)
+ * `phpNamingMethod` the naming method to use. Defaults to `underscore`, which transforms the table name into a CamelCase phpName.
+ * `baseClass` allows you to specify a class that the generated Propel objects should extend ({{{in place of propel.om.BaseObject}}}).
+ * `basePeer` instructs Propel to use a different SQL-generating `BasePeer` class (or sub-class of `BasePeer`).
+ * `heavyIndexing` adds indexes for each component of the primary key (when using composite primary keys).
+ * `readOnly` suppresses the mutator/setter methods, save() and delete() methods.
+ * `treeMode` is used to indicate that this table is part of a node tree. Currently the only supported values are "!NestedSet" (see [wiki:Documentation/1.5/Behaviors/nested_set]) and "!MaterializedPath" (deprecated).
+ * `reloadOnInsert` is used to indicate that the object should be reloaded from the database when an INSERT is performed. This is useful if you have triggers (or other server-side functionality like column default expressions) that alters the database row on INSERT.
+ * `reloadOnUpdate` is used to indicate that the object should be reloaded from the database when an UPDATE is performed. This is useful if you have triggers (or other server-side functionality like column default expressions) that alters the database row on UPDATE.
+ * `allowPkInsert` can be used if you want to define the primary key of a new object being inserted. By default if idMethod is "native", Propel would throw an exception. However, in some cases this feature is useful, for example if you do some replication of data in an master-master environment. It defaults to false.
+
+=== element ===
+
+{{{
+#!xml
+
+ []
+
+}}}
+
+==== Description of Attributes ====
+
+ * {{{defaultValue}}} The default value that the object will have for this column in the PHP instance after creating a "new Object". This value is always interpreted as a string.
+ * {{{defaultExpr}}} The default value for this column as expressed in SQL. This value is used solely for the "sql" target which builds your database from the schema.xml file. The defaultExpr is the SQL expression used as the "default" for the column.
+ * {{{primaryString}}} A column defined as primary string serves as default value for a `__toString()` method in the generated Propel object.
+
+=== element ===
+
+To link a column to another table use the following syntax:
+
+{{{
+#!xml
+
+
+
+}}}
+
+==== Description of Attributes ====
+
+ * {{{defaultJoin}}} This affects the default join type used in the generated `joinXXX()` methods in the model query class. Propel uses an INNER JOIN for foreign keys attached to a required column, and a LEFT JOIN for foreign keys attached to a non-required column, but you can override this in the foreign key element.
+
+=== element ===
+
+To create an index on one or more columns, use the following syntax:
+
+{{{
+#!xml
+
+
+ ...
+
+}}}
+
+In some cases your RDBMS may require you to specify an index size.
+
+=== element ===
+
+To create a unique index on one or more columns, use the following syntax:
+
+{{{
+#!xml
+
+
+ ...
+
+}}}
+
+In some cases your RDBMS may require you to specify an index size for unique indexes.
+
+=== element ===
+
+If you are using a database that uses sequences for auto-increment columns (e.g. PostgreSQL or Oracle), you can customize the name of the sequence using the tag:
+
+{{{
+#!xml
+
+}}}
+
+== Column Types ==
+
+Here are the Propel column types with some example mappings to native database and PHP types. There are also several ways to customize the mapping between these types.
+
+=== Text Types ===
+
+||'''Propel Type'''||'''Desc'''||'''Example Default DB Type (MySQL)'''||'''Default PHP Native Type'''||
+||CHAR||Fixed-lenght character data||CHAR||string||
+||VARCHAR||Variable-lenght character data||VARCHAR||string||
+||LONGVARCHAR||Long variable-length character data||TEXT||string||
+||CLOB||Character LOB (locator object)||LONGTEXT||string||
+
+=== Numeric Types ===
+
+||'''Propel Type'''||'''Desc'''||'''Example Default DB Type (MySQL)'''||'''Default PHP Native Type'''||
+||NUMERIC||Numeric data||DECIMAL||string (PHP int is limited)||
+||DECIMAL||Decimal data||DECIMAL||string (PHP int is limited)||
+||TINYINT||Tiny integer ||TINYINT||int||
+||SMALLINT||Small integer ||SMALLINT||int||
+||INTEGER||Integer||INTEGER||int||
+||BIGINT||Large integer||BIGINT||string (PHP int is limited)||
+||REAL||Real number||REAL||double||
+||FLOAT||Floating point number||FLOAT||double||
+||DOUBLE||Floating point number||DOUBLE||double||
+
+=== Binary Types ===
+
+||'''Propel Type'''||'''Desc'''||'''Example Default DB Type (MySQL)'''||'''Default PHP Native Type'''||
+||BINARY||Fixed-length binary data||BLOB||double||
+||VARBINARY||Variable-length binary data||MEDIUMBLOB||double||
+||LONGVARBINARY||Long variable-length binary data||LONGBLOB||double||
+||BLOB||Binary LOB (locator object)||LONGBLOB||string||
+
+=== Temporal (Date/Time) Types ===
+
+
+||'''Propel Type'''||'''Desc'''||'''Example Default DB Type (MySQL)'''||'''Default PHP Native Type'''||
+||DATE||Date (e.g. YYYY-MM-DD)||DATE||DateTime object||
+||TIME||Time (e.g. HH:MM:SS)||TIME||DateTime object||
+||TIMESTAMP||Date + time (e.g. YYYY-MM-DD HH:MM:SS)||TIMESTAMP||DateTime object||
+
+==== Legacy Temporal Types ====
+
+The following Propel 1.2 types are still supported, but are no longer needed with Propel 1.3.
+
+||'''Propel Type'''||'''Desc'''||'''Example Default DB Type (MySQL)'''||'''Default PHP Native Type'''||
+||BU_DATE||Pre-/post-epoch date (e.g. 1201-03-02)||DATE||DateTime object||
+||BU_TIMESTAMP||Pre-/post-epoch Date + time (e.g. 1201-03-02 12:33:00)||TIMESTAMP||DateTime object||
+
+== Customizing Mappings ==
+
+=== Specify Column Attributes ===
+
+You can change the way that Propel maps its own types to native SQL types or to PHP types by overriding the values for a specific column.
+
+For example:
+
+(Overriding PHP type)
+{{{
+#!xml
+
+}}}
+
+(Overriding SQL type)
+{{{
+#!xml
+
+}}}
+
+=== Adding Vendor Info ===
+
+Propel supports database-specific elements in the schema (currently only for MySQL). This "vendor" parameters affect the generated SQL. To add vendor data, add a `` tag with a `type` attribute specifying the target database vendor. In the `` tag, add `` tags with a `name` and a `value` attribue. For instance:
+
+{{{
+#!xml
+
+
+
+
+
+
+}}}
+
+This will change the generated SQL table creation to look like:
+
+{{{
+#!sql
+CREATE TABLE book
+ ()
+ ENGINE = InnoDB
+ DEFAULT CHARACTER SET utf8;
+}}}
+
+Propel supports the following vendor parameters for MySQL:
+
+{{{
+Name | Example values
+-----------------|---------------
+// in
element
+Engine | MYISAM (default), BDB, HEAP, ISAM, InnoDB, MERGE, MRG_MYISAM
+Charset | utf8, latin1, etc.
+Collate | utf8_unicode_ci, latin1_german1_ci, etc.
+Checksum | 0, 1
+Pack_Keys | 0, 1, DEFAULT
+Delay_key_write | 0, 1
+// in element
+Charset | utf8, latin1, etc.
+Collate | utf8_unicode_ci, latin1_german1_ci, etc.
+// in element
+Index_type | FULLTEXT
+}}}
+
+=== Using Custom Platform ===
+
+For overriding the mapping between Propel types and native SQL types, you can create your own Platform class and override the mapping.
+
+For example:
+
+{{{
+#!php
+setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, "DECIMAL"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, "TEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, "BLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, "MEDIUMBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, "LONGBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BLOB, "LONGBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, "LONGTEXT"));
+ }
+}
+}}}
+
+You must then specify that mapping in the {{{build.properties}}} for your project:
+
+{{{
+propel.platform.class = propel.engine.platform.${propel.database}Platform
+}}}
diff --git a/3rd_party/php/propel/generator/bin/propel-gen b/3rd_party/php/propel/generator/bin/propel-gen
new file mode 100755
index 000000000..ea10819b5
--- /dev/null
+++ b/3rd_party/php/propel/generator/bin/propel-gen
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Shell wrapper for Propel generator
+# $Id$
+#
+# This script will do the following:
+# - check for PHING_COMMAND env, if found, use it.
+# - if not found assume php is on the path
+# - check for PROPEL_GEN_HOME env, if found use it
+# - if not look for it
+
+if [ -z "$PROPEL_GEN_HOME" ] ; then
+
+ # try to find Propel
+ if [ -d /opt/propel/generator ] ; then
+ PROPEL_GEN_HOME=/opt/propel/generator
+ fi
+
+ if [ -d "${HOME}/opt/propel/generator" ] ; then
+ PROPEL_GEN_HOME="${HOME}/opt/propel/generator"
+ fi
+
+ if [ -d "/usr/local/propel/generator" ] ; then
+ PROPEL_GEN_HOME="/usr/local/propel/generator"
+ fi
+
+ if [ -d "${HOME}/usr/propel/generator" ] ; then
+ PROPEL_GEN_HOME="${HOME}/usr/propel/generator"
+ fi
+
+ ## resolve links - the script name may be a link to phing's home
+ PRG="$0"
+ progname=`basename "$0"`
+ saveddir=`pwd`
+
+ # need this for relative symlinks
+ dirname_prg=`dirname "$PRG"`
+ cd "$dirname_prg"
+
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+ done
+
+ cd "$saveddir"
+
+ PROPEL_GEN_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ PROPEL_GEN_HOME=`cd "$PROPEL_GEN_HOME" && pwd`
+
+ # make it available in PHP via getenv("PROPEL_GEN_HOME")
+ export PROPEL_GEN_HOME
+
+fi
+
+if [ -z "$PHING_COMMAND" ] ; then
+ # echo "WARNING: PHING_COMMAND environment not set. (Assuming phing on PATH)"
+ export PHING_COMMAND="phing"
+fi
+
+if [ $# = 1 ] ; then
+ $PHING_COMMAND -f $PROPEL_GEN_HOME/build.xml -Dusing.propel-gen=true -Dproject.dir=$saveddir $*
+else
+ $PHING_COMMAND -f $PROPEL_GEN_HOME/build.xml -Dusing.propel-gen=true -Dproject.dir=$*
+fi
diff --git a/3rd_party/php/propel/generator/bin/propel-gen.bat b/3rd_party/php/propel/generator/bin/propel-gen.bat
new file mode 100755
index 000000000..b6607f6b1
--- /dev/null
+++ b/3rd_party/php/propel/generator/bin/propel-gen.bat
@@ -0,0 +1,32 @@
+@echo off
+
+rem *********************************************************************
+rem ** The Propel generator convenience script for Windows based systems
+rem ** $Id$
+rem *********************************************************************
+
+rem This script will do the following:
+rem - check for PHING_COMMAND env, if found, use it.
+rem - if not found detect php, if found use it, otherwise err and terminate
+rem - check for PROPEL_GEN_HOME env, if found use it
+rem - if not found error and leave
+
+if "%OS%"=="Windows_NT" @setlocal
+
+rem %~dp0 is expanded pathname of the current script under NT
+set DEFAULT_PROPEL_GEN_HOME=%~dp0..
+
+if "%PROPEL_GEN_HOME%" == "" set PROPEL_GEN_HOME=%DEFAULT_PROPEL_GEN_HOME%
+set DEFAULT_PROPEL_GEN_HOME=
+
+if "%PHING_COMMAND%" == "" set PHING_COMMAND=phing.bat
+
+set nbArgs=0
+for %%x in (%*) do Set /A nbArgs+=1
+if %nbArgs% leq 1 (
+ %PHING_COMMAND% -f "%PROPEL_GEN_HOME%\build.xml" -Dusing.propel-gen=true -Dproject.dir="%CD%" %*
+) else (
+ %PHING_COMMAND% -f "%PROPEL_GEN_HOME%\build.xml" -Dusing.propel-gen=true -Dproject.dir=%*
+)
+
+if "%OS%"=="Windows_NT" @endlocal
diff --git a/3rd_party/php/propel/generator/build-propel.xml b/3rd_party/php/propel/generator/build-propel.xml
new file mode 100644
index 000000000..036544248
--- /dev/null
+++ b/3rd_party/php/propel/generator/build-propel.xml
@@ -0,0 +1,513 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ATTENTION: It appears you are using PostgreSQL and you have identifier-quoting turned on.
+ It is suggested that you disable identifier quoting when using PostgreSQL -- especially if you
+ have case-sensitive columns in your database.
+
+ To disable identifier quoting, add the following property to your build.properties (or specify
+ it using -D on commandline):
+
+ propel.disableIdentifierQuoting=true
+
+ You can ignore this warning if you understand the issues related to case-sensitivity and Propel's
+ DDL-only implementation of identifier quoting.
+
+
+
+
+
+
+
+
+
+
+
+ ATTENTION: It appears you are using the mysqli driver.
+
+ This driver is no longer supported by Propel because Propel now uses PDO for database connections.
+ Please use mysqli driver instead.
+ Use 'mysql' instead of 'mysqli' for your propel.database property.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Output file: ${propel.runtime.phpconf.file}
+ XMLFile: ${propel.conf.dir}/${propel.runtime.conf.file}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/build.properties-sample b/3rd_party/php/propel/generator/build.properties-sample
new file mode 100644
index 000000000..e94312a21
--- /dev/null
+++ b/3rd_party/php/propel/generator/build.properties-sample
@@ -0,0 +1,192 @@
+# -------------------------------------------------------------------
+#
+# P R O P E L C O N F I G U R A T I O N F I L E
+#
+# -------------------------------------------------------------------
+# This file contains some example properties. Ideally properties
+# should be specified in the project-specific build.properties file;
+# however, this file can be used to specify non-default properties
+# that you would like to use accross all of your Propel projects.
+# -------------------------------------------------------------------
+#
+
+propel.home = .
+
+# -------------------------------------------------------------------
+#
+# P R O J E C T
+#
+# -------------------------------------------------------------------
+# This is the name of your Propel project. The name of your Propel
+# project is used (by default) to determine where the generator will
+# find needed configuration files and will place resuilting build
+# files. E.g. if your project is named 'killerapp', Propel will
+# look here for schema.xml and runtime-conf.xml files:
+#
+# projects/killerapp/
+#
+# -------------------------------------------------------------------
+
+# You can set this here, but it's preferrable to set this in a
+# project-specific build.properties file.
+#
+# propel.project = bookstore
+
+# -------------------------------------------------------------------
+#
+# T A R G E T D A T A B A S E
+#
+# -------------------------------------------------------------------
+# This is the target database, only considered when generating
+# the SQL for your Propel project. Your possible choices are:
+#
+# mssql, mysql, oracle, pgsql, sqlite
+# -------------------------------------------------------------------
+
+# You can set this here, but it's preferrable to set this in a
+# project-specific build.properties file.
+#
+# propel.database = mysql
+
+# -------------------------------------------------------------------
+#
+# O B J E C T M O D E L I N F O R M A T I O N
+#
+# -------------------------------------------------------------------
+# These settings will allow you to customize the way your
+# Peer-based object model is created.
+# -------------------------------------------------------------------
+# addGenericAccessors
+# If true, Propel adds methods to get database fields by name/position.
+#
+# addGenericMutators
+# If true, Propel adds methods to set database fields by name/position.
+#
+# addSaveMethod
+# If true, Propel adds tracking code to determine how to save objects.
+#
+# addTimeStamp
+# If true, Propel true puts time stamps in phpdoc of generated om files.
+#
+# basePrefix
+# A string to pre-pend to the file names of base data and peer objects.
+#
+# complexObjectModel
+# If true, Propel generates data objects with collection support and
+# methods to easily retreive foreign key relationships.
+#
+# targetPackage
+# Sets the PHP "package" the om files will generated to, e.g.
+# "com.company.project.om".
+#
+# targetPlatform
+# Sets whether Propel is building classes for php5 (default)
+# or php4 (experimental).
+#
+# packageObjectModel
+# Sets whether Propel is packaging ObjectModel fro several
+# [package].schema.xml files. The
+# attribute has to be set then. (warning: this is experimental!)
+#
+# -------------------------------------------------------------------
+
+# classes will be put in (and included from) this directory
+# e.g. if package is "bookstore" then om will expect include('bookstore/Book.php'); to work.
+# use dot-path notation -- e.g. my.bookstore -> my/bookstore.
+#
+propel.targetPackage = ${propel.project}
+
+propel.addGenericAccessors = false
+propel.addGenericMutators = false
+propel.addSaveMethod = true
+propel.addTimeStamp = true
+propel.basePrefix = Base
+propel.complexObjectModel = true
+propel.targetPlatform = php5
+propel.packageObjectModel = false
+
+# -------------------------------------------------------------------
+#
+# D B C O N N E C T I O N S E T T I N G S
+#
+# -------------------------------------------------------------------
+# PDO connection settings. These connection settings are used by
+# build tagets that perform database operations (e.g. 'insert-sql',
+# 'reverse').
+#
+# You can set them here, but it's preferrable to set these properties
+# in a project-specific build.properties file.
+#
+
+# If you want to use a custom driver, specify it below, otherwise
+# leave it blank or comment it out to use Creole stock driver.
+#
+# propel.database.driver = creole.drivers.sqlite.SQLiteConnection
+
+# Note that if you do not wish to specify the database (e.g. if you
+# are using multiple databses) you can use the @DB@ token which
+# will be replaced with a database at runtime.
+#
+# propel.database.url = mysql:host=$host;dbname=$database
+
+# For MySQL or Oracle, you also need to specify username & password
+# propel.database.user = [db username]
+# propel.database.password = [db password]
+
+# Use the URL below to specify a DSN to used to create the database.
+# Note that this URL should not contain the database name, as you will
+# get an error if the database does not exist.
+# (This does not apply to SQLite since the databse is automatically created
+# when the connection is made -- if it does not already exist.)
+#
+# propel.database.createUrl = mysql:host=$host;dbname=$database
+
+
+# -------------------------------------------------------------------
+#
+# D A T A B A S E TO X M L
+#
+# -------------------------------------------------------------------
+#
+# samePhpName
+# If true, the reverse task will set the phpName attribute for the
+# tables and columns to be the same as SQL name.
+#
+# addVendorInfo
+# If true, the reverse task will add vendor specific information
+# to the database schema
+#
+# addValidators
+# Bitfield like option to turn on/off addition of Validator and
+# Rule tags to the schema. Uses a boolean syntax like in php.ini.
+# Allowed tokens are:
+# none add no validators)
+# all add all validators)
+# maxlength add maxlengths for string type columns)
+# maxvalue add maxvalue for numeric columns)
+# type add notmatch validators for numeric columns)
+# required add required validators for required columns)
+# unique add unique validators for unique indexes)
+# Allowed operators are:
+# & bitwise AND
+# | bitwise OR
+# ~ bitwise NOT
+#
+# -------------------------------------------------------------------
+
+# propel.samePhpName = false
+# propel.addVendorInfo=true
+# propel.addValidators=none
+
+
+# -------------------------------------------------------------------
+#
+# D A T A B A S E B U I L D C O N F I G
+#
+# -------------------------------------------------------------------
+# Some databases provide some configuration options that can be set
+# in this script.
+#
+# === MySQL
+# propel.mysql.tableType
+# Use this property to set the table type of generated tables (e.g. InnoDB, MyISAM).
diff --git a/3rd_party/php/propel/generator/build.xml b/3rd_party/php/propel/generator/build.xml
new file mode 100644
index 000000000..5005dae11
--- /dev/null
+++ b/3rd_party/php/propel/generator/build.xml
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Project name
+
+
+
+
+ No project.dir was specified, using default path: ./projects/${project}
+
+
+
+
+
+
+
+ Processing additional properties file: ${additional.properties}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3rd_party/php/propel/generator/build.xml-local b/3rd_party/php/propel/generator/build.xml-local
new file mode 100644
index 000000000..ff57c3b2b
--- /dev/null
+++ b/3rd_party/php/propel/generator/build.xml-local
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3rd_party/php/propel/generator/default.properties b/3rd_party/php/propel/generator/default.properties
new file mode 100644
index 000000000..3d914e514
--- /dev/null
+++ b/3rd_party/php/propel/generator/default.properties
@@ -0,0 +1,267 @@
+# -------------------------------------------------------------------
+#
+# D E F A U L T P R O P E R T I E S
+#
+# -------------------------------------------------------------------
+# This file sets default properties. You can override any of these
+# by specifying your new value in the build.properties file for your
+# project or a top-level build.properties file. Either way, you
+# should not need to edit this file.
+# -------------------------------------------------------------------
+
+
+# -------------------------------------------------------------------
+#
+# B A S I C P R O P E R T I E S
+#
+# -------------------------------------------------------------------
+
+propel.version = 1.5.2
+
+propel.home = .
+
+propel.project =
+propel.database =
+propel.targetPackage = ${propel.project}
+propel.runOnlyOnSchemaChange = false
+
+# Default behavior settings
+#
+propel.targetPlatform = php5
+propel.packageObjectModel = false
+propel.useDateTimeClass = true
+propel.dateTimeClass = DateTime
+
+propel.schema.validate = true
+propel.schema.transform = false
+
+# controls what type of joins will be used in the doSelectJoin*() peer methods,
+# if set to true, LEFT JOINS will be used, INNER JOINS otherwise
+# see ticket #491, #588
+propel.useLeftJoinsInDoJoinMethods = true
+
+# -------------------------------------------------------------------
+#
+# D A T A B A S E S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+propel.database.url =
+propel.database.buildUrl = ${propel.database.url}
+propel.database.createUrl = ${propel.database.buildUrl}
+
+propel.database.driver =
+
+propel.database.schema =
+propel.database.encoding =
+propel.database.manualCreation = false
+
+# if these arent blank then when we try to connect with insert-sql to a database
+# that doesnt require them and it isnt in the build.properties it sends
+# the ${blah} for the username and password
+propel.database.user =
+propel.database.password =
+
+# -------------------------------------------------------------------
+#
+# D A T A B A S E T O X M L S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+propel.samePhpName = false
+propel.addVendorInfo = false
+propel.addValidators = none
+
+# -------------------------------------------------------------------
+#
+# T E M P L A T E V A R I A B L E S
+#
+# -------------------------------------------------------------------
+
+propel.addGenericAccessors = true
+propel.addGenericMutators = true
+propel.addSaveMethod = true
+propel.addTimeStamp = false
+propel.addValidateMethod = true
+propel.addIncludes = false
+propel.addHooks = true
+propel.basePrefix = Base
+propel.saveException = PropelException
+propel.emulateForeignKeyConstraints = false
+
+# Identifier quoting is only implemented at the DDL layer at this point.
+# Since this may result in undesired behavior (especially in Postgres),
+# it can be disabled by setting this property to true in your build.properties file.
+propel.disableIdentifierQuoting = false
+
+# These are the default formats that will be used when fetching values
+# from temporal columns in Propel. You can always specify these when
+# calling the methods directly, but for methods like getByName()
+# it is nice to change the defaults.
+
+propel.defaultTimeStampFormat = Y-m-d H:i:s
+propel.defaultTimeFormat = %X
+propel.defaultDateFormat = %x
+
+propel.namespace.om = om
+propel.namespace.map = map
+propel.namespace.autoPackage = false
+
+propel.omtar.src.base = false
+propel.omtar.src.extension = false
+propel.omtar.bin.base = false
+propel.omtar.bin.extension = false
+propel.omtar.deleteFiles = false
+
+# -------------------------------------------------------------------
+#
+# D I R E C T O R I E S
+#
+# -------------------------------------------------------------------
+
+propel.project.dir = ${propel.home}/projects/${propel.project}
+
+propel.output.dir = ${propel.project.dir}/build
+propel.schema.dir = ${propel.project.dir}
+propel.templatePath = ${propel.home}/templates
+propel.conf.dir = ${propel.project.dir}
+
+propel.doc.dir = ${propel.output.dir}/doc
+propel.php.dir = ${propel.output.dir}/classes
+propel.phpconf.dir = ${propel.output.dir}/conf
+propel.phpdoc.dir = ${propel.output.dir}/phpdoc
+propel.sql.dir = ${propel.output.dir}/sql
+propel.graph.dir = ${propel.output.dir}/graph
+propel.omtar.dir = ${propel.output.dir}
+
+# -------------------------------------------------------------------
+#
+# D E F A U L T F I L E N A M ES
+#
+# -------------------------------------------------------------------
+
+# propel.sqlfile
+
+propel.runtime.conf.file = runtime-conf.xml
+propel.runtime.phpconf.file = ${propel.project}-conf.php
+propel.runtime.phpconf-classmap.file = ${propel.project}-classmap.php
+propel.default.schema.basename = schema
+
+# Can't use because of inconsistencies in where the files
+# are named (some from build-propel.xml, but some from within templates)
+# propel.default.data.basename = ${propel.project}-data
+
+propel.schema.xsd.file = ${propel.home}/resources/xsd/database.xsd
+propel.schema.xsl.file = ${propel.home}/resources/xsl/database.xsl
+
+# -------------------------------------------------------------------
+#
+# I N C L U D E A N D E X C L U D E S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+propel.schema.sql.includes = *schema.xml
+propel.schema.sql.excludes =
+propel.schema.doc.includes = *schema.xml
+propel.schema.doc.excludes =
+propel.schema.create-db.includes = *schema.xml
+propel.schema.create-db.excludes =
+propel.schema.init-sql.includes = *schema.xml
+propel.schema.init-sql.excludes = id-table-schema.xml
+propel.schema.om.includes = *schema.xml
+propel.schema.om.excludes = id-table-schema.xml
+propel.schema.datadtd.includes = *schema.xml
+propel.schema.datadtd.excludes = id-table-schema.xml
+
+# -------------------------------------------------------------------
+#
+# M A P P E R S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+# (note: data xml files are selected based on datadbmap file)
+propel.datasql.mapper.from = *.xml
+propel.datasql.mapper.to = *.sql
+
+propel.datadump.mapper.from = *schema.xml
+propel.datadump.mapper.to = *data.xml
+
+propel.datadtd.mapper.from = *.xml
+propel.datadtd.mapper.to = *.dtd
+
+propel.sql.mapper.from = *.xml
+propel.sql.mapper.to = *.sql
+
+
+# -------------------------------------------------------------------
+#
+# B U I L D E R S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+# Object Model builders
+propel.builder.peer.class = builder.om.PHP5PeerBuilder
+propel.builder.object.class = builder.om.PHP5ObjectBuilder
+propel.builder.objectstub.class = builder.om.PHP5ExtensionObjectBuilder
+propel.builder.peerstub.class = builder.om.PHP5ExtensionPeerBuilder
+
+propel.builder.objectmultiextend.class = builder.om.PHP5MultiExtendObjectBuilder
+
+propel.builder.tablemap.class = builder.om.PHP5TableMapBuilder
+propel.builder.query.class = builder.om.QueryBuilder
+propel.builder.querystub.class = builder.om.ExtensionQueryBuilder
+propel.builder.queryinheritance.class = builder.om.QueryInheritanceBuilder
+propel.builder.queryinheritancestub.class = builder.om.ExtensionQueryInheritanceBuilder
+
+propel.builder.interface.class = builder.om.PHP5InterfaceBuilder
+
+propel.builder.node.class = builder.om.PHP5NodeBuilder
+propel.builder.nodepeer.class = builder.om.PHP5NodePeerBuilder
+propel.builder.nodestub.class = builder.om.PHP5ExtensionNodeBuilder
+propel.builder.nodepeerstub.class = builder.om.PHP5ExtensionNodePeerBuilder
+
+propel.builder.nestedset.class = builder.om.PHP5NestedSetBuilder
+propel.builder.nestedsetpeer.class = builder.om.PHP5NestedSetPeerBuilder
+
+propel.builder.pluralizer.class = builder.util.DefaultEnglishPluralizer
+
+# SQL builders
+
+propel.builder.ddl.class = builder.sql.${propel.database}.${propel.database}DDLBuilder
+propel.builder.datasql.class = builder.sql.${propel.database}.${propel.database}DataSQLBuilder
+
+# Platform classes
+
+propel.platform.class = platform.${propel.database}Platform
+
+# Schema Parser (reverse-engineering) classes
+
+propel.reverse.parser.class = reverse.${propel.database}.${propel.database}SchemaParser
+
+# -------------------------------------------------------------------
+#
+# M Y S Q L S P E C I F I C S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+# Default table type
+propel.mysqlTableType = MyISAM
+# Keyword used to specify table type. MYSQL < 5 should use TYPE instead
+propel.mysqlTableEngineKeyword = ENGINE
+
+# -------------------------------------------------------------------
+#
+# B E H A V I O R S E T T I N G S
+#
+# -------------------------------------------------------------------
+
+propel.behavior.timestampable.class = behavior.TimestampableBehavior
+propel.behavior.alternative_coding_standards.class = behavior.AlternativeCodingStandardsBehavior
+propel.behavior.soft_delete.class = behavior.SoftDeleteBehavior
+propel.behavior.auto_add_pk.class = behavior.AutoAddPkBehavior
+propel.behavior.nested_set.class = behavior.nestedset.NestedSetBehavior
+propel.behavior.sortable.class = behavior.sortable.SortableBehavior
+propel.behavior.sluggable.class = behavior.sluggable.SluggableBehavior
+propel.behavior.concrete_inheritance.class = behavior.concrete_inheritance.ConcreteInheritanceBehavior
+propel.behavior.query_cache.class = behavior.query_cache.QueryCacheBehavior
+propel.behavior.aggregate_column.class = behavior.aggregate_column.AggregateColumnBehavior
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/AlternativeCodingStandardsBehavior.php b/3rd_party/php/propel/generator/lib/behavior/AlternativeCodingStandardsBehavior.php
new file mode 100644
index 000000000..d9a9de6e2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/AlternativeCodingStandardsBehavior.php
@@ -0,0 +1,133 @@
+ 'true',
+ 'remove_closing_comments' => 'true',
+ 'use_whitespace' => 'true',
+ 'tab_size' => 2,
+ 'strip_comments' => 'false'
+ );
+
+ public function objectFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function extensionObjectFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function queryFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function extensionQueryFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function peerFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function extensionPeerFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ public function tableMapFilter(&$script)
+ {
+ return $this->filter($script);
+ }
+
+ /**
+ * Transform the coding standards of a PHP sourcecode string
+ *
+ * @param string $script A script string to be filtered, passed as reference
+ */
+ protected function filter(&$script)
+ {
+ $filter = array();
+ if($this->getParameter('brackets_newline') == 'true') {
+ $filter['#^(\t*)\}\h(else|elseif|catch)(.*)\h\{$#m'] = "$1}
+$1$2$3
+$1{";
+ $filter['#^(\t*)(\w.*)\h\{$#m'] = "$1$2
+$1{";
+ }
+ if ($this->getParameter('remove_closing_comments') == 'true') {
+ $filter['#^(\t*)} //.*$#m'] = "$1}";
+ }
+ if ($this->getParameter('use_whitespace') == 'true') {
+ $filter['#\t#'] = str_repeat(' ', $this->getParameter('tab_size'));
+ }
+
+ $script = preg_replace(array_keys($filter), array_values($filter), $script);
+
+ if ($this->getParameter('strip_comments') == 'true') {
+ $script = self::stripComments($script);
+ }
+ }
+
+ /**
+ * Remove inline and codeblock comments from a PHP code string
+ * @param string $code The input code
+ * @return string The input code, without comments
+ */
+ public static function stripComments($code)
+ {
+ $output = '';
+ $commentTokens = array(T_COMMENT, T_DOC_COMMENT);
+ foreach (token_get_all($code) as $token) {
+ if (is_array($token)) {
+ if (in_array($token[0], $commentTokens)) continue;
+ $token = $token[1];
+ }
+ $output .= $token;
+ }
+
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/AutoAddPkBehavior.php b/3rd_party/php/propel/generator/lib/behavior/AutoAddPkBehavior.php
new file mode 100644
index 000000000..10b8f4edb
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/AutoAddPkBehavior.php
@@ -0,0 +1,54 @@
+ 'id',
+ 'autoIncrement' => 'true',
+ 'type' => 'INTEGER'
+ );
+
+ /**
+ * Copy the behavior to the database tables
+ * Only for tables that have no Pk
+ */
+ public function modifyDatabase()
+ {
+ foreach ($this->getDatabase()->getTables() as $table) {
+ if(!$table->hasPrimaryKey()) {
+ $b = clone $this;
+ $table->addBehavior($b);
+ }
+ }
+ }
+
+ /**
+ * Add the primary key to the current table
+ */
+ public function modifyTable()
+ {
+ $table = $this->getTable();
+ if (!$table->hasPrimaryKey() && !$table->hasBehavior('concrete_inheritance')) {
+ $columnAttributes = array_merge(array('primaryKey' => 'true'), $this->getParameters());
+ $this->getTable()->addColumn($columnAttributes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/SoftDeleteBehavior.php b/3rd_party/php/propel/generator/lib/behavior/SoftDeleteBehavior.php
new file mode 100644
index 000000000..f87bec0bb
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/SoftDeleteBehavior.php
@@ -0,0 +1,467 @@
+ 'deleted_at',
+ );
+
+ /**
+ * Add the deleted_column to the current table
+ */
+ public function modifyTable()
+ {
+ if(!$this->getTable()->containsColumn($this->getParameter('deleted_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('deleted_column'),
+ 'type' => 'TIMESTAMP'
+ ));
+ }
+ }
+
+ protected function getColumnSetter()
+ {
+ return 'set' . $this->getColumnForParameter('deleted_column')->getPhpName();
+ }
+
+ public function objectMethods($builder)
+ {
+ $script = '';
+ $this->addObjectForceDelete($script);
+ $this->addObjectUndelete($script);
+ return $script;
+ }
+
+ public function addObjectForceDelete(&$script)
+ {
+ $script .= "
+/**
+ * Bypass the soft_delete behavior and force a hard delete of the current object
+ */
+public function forceDelete(PropelPDO \$con = null)
+{
+ {$this->getTable()->getPhpName()}Peer::disableSoftDelete();
+ \$this->delete(\$con);
+}
+";
+ }
+
+ public function addObjectUndelete(&$script)
+ {
+ $script .= "
+/**
+ * Undelete a row that was soft_deleted
+ *
+ * @return int The number of rows affected by this update and any referring fk objects' save() operations.
+ */
+public function unDelete(PropelPDO \$con = null)
+{
+ \$this->{$this->getColumnSetter()}(null);
+ return \$this->save(\$con);
+}
+";
+ }
+
+ public function preDelete($builder)
+ {
+ return <<getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled()) {
+ \$this->{$this->getColumnSetter()}(time());
+ \$this->save(\$con);
+ \$con->commit();
+ {$builder->getStubPeerBuilder()->getClassname()}::removeInstanceFromPool(\$this);
+ return;
+}
+EOT;
+ }
+
+ public function queryAttributes()
+ {
+ return "protected static \$softDelete = true;
+protected \$localSoftDelete = true;
+";
+ }
+
+ public function queryMethods($builder)
+ {
+ $this->builder = $builder;
+ $script = '';
+ $this->addQueryIncludeDeleted($script);
+ $this->addQuerySoftDelete($script);
+ $this->addQueryForceDelete($script);
+ $this->addQueryForceDeleteAll($script);
+ $this->addQueryUnDelete($script);
+ $this->addQueryEnableSoftDelete($script);
+ $this->addQueryDisableSoftDelete($script);
+ $this->addQueryIsSoftDeleteEnabled($script);
+
+ return $script;
+ }
+
+ public function addQueryIncludeDeleted(&$script)
+ {
+ $script .= "
+/**
+ * Temporarily disable the filter on deleted rows
+ * Valid only for the current query
+ *
+ * @see {$this->builder->getStubQueryBuilder()->getClassname()}::disableSoftDelete() to disable the filter for more than one query
+ *
+ * @return {$this->builder->getStubQueryBuilder()->getClassname()} The current query, for fuid interface
+ */
+public function includeDeleted()
+{
+ \$this->localSoftDelete = false;
+ return \$this;
+}
+";
+ }
+
+ public function addQuerySoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Soft delete the selected rows
+ *
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return int Number of updated rows
+ */
+public function softDelete(PropelPDO \$con = null)
+{
+ return \$this->update(array('{$this->getColumnForParameter('deleted_column')->getPhpName()}' => time()), \$con);
+}
+";
+ }
+
+ public function addQueryForceDelete(&$script)
+ {
+ $script .= "
+/**
+ * Bypass the soft_delete behavior and force a hard delete of the selected rows
+ *
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return int Number of deleted rows
+ */
+public function forceDelete(PropelPDO \$con = null)
+{
+ return {$this->builder->getPeerClassname()}::doForceDelete(\$this, \$con);
+}
+";
+ }
+
+ public function addQueryForceDeleteAll(&$script)
+ {
+ $script .= "
+/**
+ * Bypass the soft_delete behavior and force a hard delete of all the rows
+ *
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return int Number of deleted rows
+ */
+public function forceDeleteAll(PropelPDO \$con = null)
+{
+ return {$this->builder->getPeerClassname()}::doForceDeleteAll(\$con);}
+";
+ }
+
+ public function addQueryUnDelete(&$script)
+ {
+ $script .= "
+/**
+ * Undelete selected rows
+ *
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return int The number of rows affected by this update and any referring fk objects' save() operations.
+ */
+public function unDelete(PropelPDO \$con = null)
+{
+ return \$this->update(array('{$this->getColumnForParameter('deleted_column')->getPhpName()}' => null), \$con);
+}
+";
+ }
+
+ public function addQueryEnableSoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Enable the soft_delete behavior for this model
+ */
+public static function enableSoftDelete()
+{
+ self::\$softDelete = true;
+}
+";
+ }
+
+ public function addQueryDisableSoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Disable the soft_delete behavior for this model
+ */
+public static function disableSoftDelete()
+{
+ self::\$softDelete = false;
+}
+";
+ }
+
+ public function addQueryIsSoftDeleteEnabled(&$script)
+ {
+ $script .= "
+/**
+ * Check the soft_delete behavior for this model
+ *
+ * @return boolean true if the soft_delete behavior is enabled
+ */
+public static function isSoftDeleteEnabled()
+{
+ return self::\$softDelete;
+}
+";
+ }
+
+ public function preSelectQuery($builder)
+ {
+ return <<getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled() && \$this->localSoftDelete) {
+ \$this->addUsingAlias({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
+} else {
+ {$this->getTable()->getPhpName()}Peer::enableSoftDelete();
+}
+EOT;
+ }
+
+ public function preDeleteQuery($builder)
+ {
+ return <<getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled() && \$this->localSoftDelete) {
+ return \$this->softDelete(\$con);
+} else {
+ return \$this->hasWhereClause() ? \$this->forceDelete(\$con) : \$this->forceDeleteAll(\$con);
+}
+EOT;
+ }
+
+ public function staticMethods($builder)
+ {
+ $builder->declareClassFromBuilder($builder->getStubQueryBuilder());
+ $this->builder = $builder;
+ $script = '';
+ $this->addPeerEnableSoftDelete($script);
+ $this->addPeerDisableSoftDelete($script);
+ $this->addPeerIsSoftDeleteEnabled($script);
+ $this->addPeerDoSoftDelete($script);
+ $this->addPeerDoDelete2($script);
+ $this->addPeerDoSoftDeleteAll($script);
+ $this->addPeerDoDeleteAll2($script);
+
+ return $script;
+ }
+
+ public function addPeerEnableSoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Enable the soft_delete behavior for this model
+ */
+public static function enableSoftDelete()
+{
+ {$this->builder->getStubQueryBuilder()->getClassname()}::enableSoftDelete();
+ // some soft_deleted objects may be in the instance pool
+ {$this->builder->getStubPeerBuilder()->getClassname()}::clearInstancePool();
+}
+";
+ }
+
+ public function addPeerDisableSoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Disable the soft_delete behavior for this model
+ */
+public static function disableSoftDelete()
+{
+ {$this->builder->getStubQueryBuilder()->getClassname()}::disableSoftDelete();
+}
+";
+ }
+
+ public function addPeerIsSoftDeleteEnabled(&$script)
+ {
+ $script .= "
+/**
+ * Check the soft_delete behavior for this model
+ * @return boolean true if the soft_delete behavior is enabled
+ */
+public static function isSoftDeleteEnabled()
+{
+ return {$this->builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled();
+}
+";
+ }
+
+ public function addPeerDoSoftDelete(&$script)
+ {
+ $script .= "
+/**
+ * Soft delete records, given a {$this->getTable()->getPhpName()} or Criteria object OR a primary key value.
+ *
+ * @param mixed \$values Criteria or {$this->getTable()->getPhpName()} object or primary key or array of primary keys
+ * which is used to create the DELETE statement
+ * @param PropelPDO \$con the connection to use
+ * @return int The number of affected rows (if supported by underlying database driver).
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+public static function doSoftDelete(\$values, PropelPDO \$con = null)
+{
+ if (\$values instanceof Criteria) {
+ // rename for clarity
+ \$criteria = clone \$values;
+ } elseif (\$values instanceof {$this->getTable()->getPhpName()}) {
+ // create criteria based on pk values
+ \$criteria = \$values->buildPkeyCriteria();
+ } else {
+ // it must be the primary key
+ \$criteria = new Criteria(self::DATABASE_NAME);";
+ $pks = $this->getTable()->getPrimaryKey();
+ if (count($pks)>1) {
+ $i = 0;
+ foreach ($pks as $col) {
+ $script .= "
+ \$criteria->add({$col->getConstantName()}, \$values[$i], Criteria::EQUAL);";
+ $i++;
+ }
+ } else {
+ $col = $pks[0];
+ $script .= "
+ \$criteria->add({$col->getConstantName()}, (array) \$values, Criteria::IN);";
+ }
+ $script .= "
+ }
+ \$criteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, time());
+ return {$this->getTable()->getPhpName()}Peer::doUpdate(\$criteria, \$con);
+}
+";
+ }
+
+ public function addPeerDoDelete2(&$script)
+ {
+ $script .= "
+/**
+ * Delete or soft delete records, depending on {$this->getTable()->getPhpName()}Peer::\$softDelete
+ *
+ * @param mixed \$values Criteria or {$this->getTable()->getPhpName()} object or primary key or array of primary keys
+ * which is used to create the DELETE statement
+ * @param PropelPDO \$con the connection to use
+ * @return int The number of affected rows (if supported by underlying database driver).
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+public static function doDelete2(\$values, PropelPDO \$con = null)
+{
+ if ({$this->getTable()->getPhpName()}Peer::isSoftDeleteEnabled()) {
+ return {$this->getTable()->getPhpName()}Peer::doSoftDelete(\$values, \$con);
+ } else {
+ return {$this->getTable()->getPhpName()}Peer::doForceDelete(\$values, \$con);
+ }
+}";
+ }
+
+ public function addPeerDoSoftDeleteAll(&$script)
+ {
+ $script .= "
+/**
+ * Method to soft delete all rows from the {$this->getTable()->getName()} table.
+ *
+ * @param PropelPDO \$con the connection to use
+ * @return int The number of affected rows (if supported by underlying database driver).
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+public static function doSoftDeleteAll(PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->getTable()->getPhpName()}Peer::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+ \$selectCriteria = new Criteria();
+ \$selectCriteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
+ \$selectCriteria->setDbName({$this->getTable()->getPhpName()}Peer::DATABASE_NAME);
+ \$modifyCriteria = new Criteria();
+ \$modifyCriteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, time());
+ return BasePeer::doUpdate(\$selectCriteria, \$modifyCriteria, \$con);
+}
+";
+ }
+
+ public function addPeerDoDeleteAll2(&$script)
+ {
+ $script .= "
+/**
+ * Delete or soft delete all records, depending on {$this->getTable()->getPhpName()}Peer::\$softDelete
+ *
+ * @param PropelPDO \$con the connection to use
+ * @return int The number of affected rows (if supported by underlying database driver).
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+public static function doDeleteAll2(PropelPDO \$con = null)
+{
+ if ({$this->getTable()->getPhpName()}Peer::isSoftDeleteEnabled()) {
+ return {$this->getTable()->getPhpName()}Peer::doSoftDeleteAll(\$con);
+ } else {
+ return {$this->getTable()->getPhpName()}Peer::doForceDeleteAll(\$con);
+ }
+}
+";
+ }
+
+ public function preSelect($builder)
+ {
+ return <<getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled()) {
+ \$criteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
+} else {
+ {$this->getTable()->getPhpName()}Peer::enableSoftDelete();
+}
+EOT;
+ }
+
+ public function peerFilter(&$script)
+ {
+ $script = str_replace(array(
+ 'public static function doDelete(',
+ 'public static function doDelete2(',
+ 'public static function doDeleteAll(',
+ 'public static function doDeleteAll2('
+ ), array(
+ 'public static function doForceDelete(',
+ 'public static function doDelete(',
+ 'public static function doForceDeleteAll(',
+ 'public static function doDeleteAll('
+ ), $script);
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/TimestampableBehavior.php b/3rd_party/php/propel/generator/lib/behavior/TimestampableBehavior.php
new file mode 100644
index 000000000..bf73b7669
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/TimestampableBehavior.php
@@ -0,0 +1,171 @@
+ 'created_at',
+ 'update_column' => 'updated_at'
+ );
+
+ /**
+ * Add the create_column and update_columns to the current table
+ */
+ public function modifyTable()
+ {
+ if(!$this->getTable()->containsColumn($this->getParameter('create_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('create_column'),
+ 'type' => 'TIMESTAMP'
+ ));
+ }
+ if(!$this->getTable()->containsColumn($this->getParameter('update_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('update_column'),
+ 'type' => 'TIMESTAMP'
+ ));
+ }
+ }
+
+ /**
+ * Get the setter of one of the columns of the behavior
+ *
+ * @param string $column One of the behavior colums, 'create_column' or 'update_column'
+ * @return string The related setter, 'setCreatedOn' or 'setUpdatedOn'
+ */
+ protected function getColumnSetter($column)
+ {
+ return 'set' . $this->getColumnForParameter($column)->getPhpName();
+ }
+
+ /**
+ * Add code in ObjectBuilder::preUpdate
+ *
+ * @return string The code to put at the hook
+ */
+ public function preUpdate()
+ {
+ return "if (\$this->isModified() && !\$this->isColumnModified(" . $this->getColumnForParameter('update_column')->getConstantName() . ")) {
+ \$this->" . $this->getColumnSetter('update_column') . "(time());
+}";
+ }
+
+ /**
+ * Add code in ObjectBuilder::preInsert
+ *
+ * @return string The code to put at the hook
+ */
+ public function preInsert()
+ {
+ return "if (!\$this->isColumnModified(" . $this->getColumnForParameter('create_column')->getConstantName() . ")) {
+ \$this->" . $this->getColumnSetter('create_column') . "(time());
+}
+if (!\$this->isColumnModified(" . $this->getColumnForParameter('update_column')->getConstantName() . ")) {
+ \$this->" . $this->getColumnSetter('update_column') . "(time());
+}";
+ }
+
+ public function objectMethods($builder)
+ {
+ return "
+/**
+ * Mark the current object so that the update date doesn't get updated during next save
+ *
+ * @return " . $builder->getStubObjectBuilder()->getClassname() . " The current object (for fluent API support)
+ */
+public function keepUpdateDateUnchanged()
+{
+ \$this->modifiedColumns[] = " . $this->getColumnForParameter('update_column')->getConstantName() . ";
+ return \$this;
+}
+";
+ }
+
+ public function queryMethods($builder)
+ {
+ $queryClassName = $builder->getStubQueryBuilder()->getClassname();
+ $updateColumnConstant = $this->getColumnForParameter('update_column')->getConstantName();
+ $createColumnConstant = $this->getColumnForParameter('create_column')->getConstantName();
+ return "
+/**
+ * Filter by the latest updated
+ *
+ * @param int \$nbDays Maximum age of the latest update in days
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function recentlyUpdated(\$nbDays = 7)
+{
+ return \$this->addUsingAlias($updateColumnConstant, time() - \$nbDays * 24 * 60 * 60, Criteria::GREATER_EQUAL);
+}
+
+/**
+ * Filter by the latest created
+ *
+ * @param int \$nbDays Maximum age of in days
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function recentlyCreated(\$nbDays = 7)
+{
+ return \$this->addUsingAlias($createColumnConstant, time() - \$nbDays * 24 * 60 * 60, Criteria::GREATER_EQUAL);
+}
+
+/**
+ * Order by update date desc
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function lastUpdatedFirst()
+{
+ return \$this->addDescendingOrderByColumn($updateColumnConstant);
+}
+
+/**
+ * Order by update date asc
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function firstUpdatedFirst()
+{
+ return \$this->addAscendingOrderByColumn($updateColumnConstant);
+}
+
+/**
+ * Order by create date desc
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function lastCreatedFirst()
+{
+ return \$this->addDescendingOrderByColumn($createColumnConstant);
+}
+
+/**
+ * Order by create date asc
+ *
+ * @return $queryClassName The current query, for fuid interface
+ */
+public function firstCreatedFirst()
+{
+ return \$this->addAscendingOrderByColumn($createColumnConstant);
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnBehavior.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnBehavior.php
new file mode 100644
index 000000000..16a996466
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnBehavior.php
@@ -0,0 +1,122 @@
+ null,
+ 'expression' => null,
+ 'foreign_table' => null,
+ );
+
+ /**
+ * Add the aggregate key to the current table
+ */
+ public function modifyTable()
+ {
+ $table = $this->getTable();
+ if (!$columnName = $this->getParameter('name')) {
+ throw new InvalidArgumentException(sprintf('You must define a \'name\' parameter for the \'aggregate_column\' behavior in the \'%s\' table', $table->getName()));
+ }
+
+ // add the aggregate column if not present
+ if(!$this->getTable()->containsColumn($columnName)) {
+ $column = $this->getTable()->addColumn(array(
+ 'name' => $columnName,
+ 'type' => 'INTEGER',
+ ));
+ }
+
+ // add a behavior in the foreign table to autoupdate the aggregate column
+ $foreignTable = $this->getForeignTable();
+ if (!$foreignTable->hasBehavior('concrete_inheritance_parent')) {
+ $relationBehavior = new AggregateColumnRelationBehavior();
+ $relationBehavior->setName('aggregate_column_relation');
+ $foreignKey = $this->getForeignKey();
+ $relationBehavior->addParameter(array('name' => 'foreign_table', 'value' => $table->getName()));
+ $relationBehavior->addParameter(array('name' => 'update_method', 'value' => 'update' . $this->getColumn()->getPhpName()));
+ $foreignTable->addBehavior($relationBehavior);
+ }
+ }
+
+ public function objectMethods($builder)
+ {
+ if (!$foreignTableName = $this->getParameter('foreign_table')) {
+ throw new InvalidArgumentException(sprintf('You must define a \'foreign_table\' parameter for the \'aggregate_column\' behavior in the \'%s\' table', $this->getTable()->getName()));
+ }
+ $script = '';
+ $script .= $this->addObjectCompute();
+ $script .= $this->addObjectUpdate();
+
+ return $script;
+ }
+
+ protected function addObjectCompute()
+ {
+ $conditions = array();
+ $bindings = array();
+ foreach ($this->getForeignKey()->getColumnObjectsMapping() as $index => $columnReference) {
+ $conditions[] = $columnReference['local']->getFullyQualifiedName() . ' = :p' . ($index + 1);
+ $bindings[$index + 1] = $columnReference['foreign']->getPhpName();
+ }
+ $sql = sprintf('SELECT %s FROM %s WHERE %s',
+ $this->getParameter('expression'),
+ $this->getTable()->getDatabase()->getPlatform()->quoteIdentifier($this->getParameter('foreign_table')),
+ implode(' AND ', $conditions)
+ );
+
+ return $this->renderTemplate('objectCompute', array(
+ 'column' => $this->getColumn(),
+ 'sql' => $sql,
+ 'bindings' => $bindings,
+ ));
+ }
+
+ protected function addObjectUpdate()
+ {
+ return $this->renderTemplate('objectUpdate', array(
+ 'column' => $this->getColumn(),
+ ));
+ }
+
+ protected function getForeignTable()
+ {
+ return $this->getTable()->getDatabase()->getTable($this->getParameter('foreign_table'));
+ }
+
+ protected function getForeignKey()
+ {
+ $foreignTable = $this->getForeignTable();
+ // let's infer the relation from the foreign table
+ $fks = $foreignTable->getForeignKeysReferencingTable($this->getTable()->getName());
+ if (!$fks) {
+ throw new InvalidArgumentException(sprintf('You must define a foreign key to the \'%s\' table in the \'%s\' table to enable the \'aggregate_column\' behavior', $this->getTable()->getName(), $foreignTable->getName()));
+ }
+ // FIXME doesn't work when more than one fk to the same table
+ return array_shift($fks);
+ }
+
+ protected function getColumn()
+ {
+ return $this->getTable()->getColumn($this->getParameter('name'));
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnRelationBehavior.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnRelationBehavior.php
new file mode 100644
index 000000000..1fce170a1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/AggregateColumnRelationBehavior.php
@@ -0,0 +1,164 @@
+ '',
+ 'update_method' => '',
+ );
+
+ public function postSave($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ return "\$this->updateRelated{$relationName}(\$con);";
+ }
+
+ // no need for a postDelete() hook, since delete() uses Query::delete(),
+ // which already has a hook
+
+ public function objectAttributes($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ return "protected \$old{$relationName};
+";
+ }
+
+ public function objectMethods($builder)
+ {
+ return $this->addObjectUpdateRelated($builder);
+ }
+
+ protected function addObjectUpdateRelated($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ $updateMethodName = $this->getParameter('update_method');
+ return $this->renderTemplate('objectUpdateRelated', array(
+ 'relationName' => $relationName,
+ 'variableName' => self::lcfirst($relationName),
+ 'updateMethodName' => $this->getParameter('update_method'),
+ ));
+ }
+
+ public function objectFilter(&$script, $builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ $relatedClass = $this->getForeignTable()->getPhpName();
+ $search = " public function set{$relationName}({$relatedClass} \$v = null)
+ {";
+ $replace = $search . "
+ // aggregate_column_relation behavior
+ if (null !== \$this->a{$relationName} && \$v !== \$this->a{$relationName}) {
+ \$this->old{$relationName} = \$this->a{$relationName};
+ }";
+ $script = str_replace($search, $replace, $script);
+ }
+
+ public function preUpdateQuery($builder)
+ {
+ return $this->getFindRelated($builder);
+ }
+
+ public function preDeleteQuery($builder)
+ {
+ return $this->getFindRelated($builder);
+ }
+
+ protected function getFindRelated($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ return "\$this->findRelated{$relationName}s(\$con);";
+ }
+
+ public function postUpdateQuery($builder)
+ {
+ return $this->getUpdateRelated($builder);
+ }
+
+ public function postDeleteQuery($builder)
+ {
+ return $this->getUpdateRelated($builder);
+ }
+
+ protected function getUpdateRelated($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ return "\$this->updateRelated{$relationName}s(\$con);";
+ }
+
+ public function queryMethods($builder)
+ {
+ $script = '';
+ $script .= $this->addQueryFindRelated($builder);
+ $script .= $this->addQueryUpdateRelated($builder);
+
+ return $script;
+ }
+
+ protected function addQueryFindRelated($builder)
+ {
+ $foreignKey = $this->getForeignKey();
+ $relationName = $this->getRelationName($builder);
+ return $this->renderTemplate('queryFindRelated', array(
+ 'foreignTable' => $this->getForeignTable(),
+ 'relationName' => $relationName,
+ 'variableName' => self::lcfirst($relationName),
+ 'foreignQueryName' => $foreignKey->getForeignTable()->getPhpName() . 'Query',
+ 'refRelationName' => $builder->getRefFKPhpNameAffix($foreignKey),
+ ));
+ }
+
+ protected function addQueryUpdateRelated($builder)
+ {
+ $relationName = $this->getRelationName($builder);
+ return $this->renderTemplate('queryUpdateRelated', array(
+ 'relationName' => $relationName,
+ 'variableName' => self::lcfirst($relationName),
+ 'updateMethodName' => $this->getParameter('update_method'),
+ ));
+ }
+
+ protected function getForeignTable()
+ {
+ return $this->getTable()->getDatabase()->getTable($this->getParameter('foreign_table'));
+ }
+
+ protected function getForeignKey()
+ {
+ $foreignTable = $this->getForeignTable();
+ // let's infer the relation from the foreign table
+ $fks = $this->getTable()->getForeignKeysReferencingTable($foreignTable->getName());
+ // FIXME doesn't work when more than one fk to the same table
+ return array_shift($fks);
+ }
+
+ protected function getRelationName($builder)
+ {
+ return $builder->getFKPhpNameAffix($this->getForeignKey());
+ }
+
+ protected static function lcfirst($input)
+ {
+ // no lcfirst in php<5.3...
+ $input[0] = strtolower($input[0]);
+ return $input;
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectCompute.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectCompute.php
new file mode 100644
index 000000000..00e183872
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectCompute.php
@@ -0,0 +1,17 @@
+
+/**
+ * Computes the value of the aggregate column getName() ?>
+ *
+ * @param PropelPDO $con A connection object
+ *
+ * @return mixed The scalar result from the aggregate query
+ */
+public function computegetPhpName() ?>(PropelPDO $con)
+{
+ $stmt = $con->prepare('');
+ $binding): ?>
+ $stmt->bindValue(':p', $this->get());
+
+ $stmt->execute();
+ return $stmt->fetchColumn();
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdate.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdate.php
new file mode 100644
index 000000000..9236bad3e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdate.php
@@ -0,0 +1,11 @@
+
+/**
+ * Updates the aggregate column getName() ?>
+ *
+ * @param PropelPDO $con A connection object
+ */
+public function updategetPhpName() ?>(PropelPDO $con)
+{
+ $this->setgetPhpName() ?>($this->computegetPhpName() ?>($con));
+ $this->save($con);
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdateRelated.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdateRelated.php
new file mode 100644
index 000000000..d5892b0f2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/objectUpdateRelated.php
@@ -0,0 +1,16 @@
+
+/**
+ * Update the aggregate column in the related object
+ *
+ * @param PropelPDO $con A connection object
+ */
+protected function updateRelated(PropelPDO $con)
+{
+ if ($ = $this->get()) {
+ $->($con);
+ }
+ if ($this->old) {
+ $this->old->($con);
+ $this->old = null;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryFindRelated.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryFindRelated.php
new file mode 100644
index 000000000..252adf195
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryFindRelated.php
@@ -0,0 +1,20 @@
+
+/**
+ * Finds the related getPhpName() ?> objects and keep them for later
+ *
+ * @param PropelPDO $con A connection object
+ */
+protected function findRelateds($con)
+{
+ $criteria = clone $this;
+ if ($this->useAliasInSQL) {
+ $alias = $this->getModelAlias();
+ $criteria->removeAlias($alias);
+ } else {
+ $alias = '';
+ }
+ $this->s = ::create()
+ ->join($alias)
+ ->mergeWith($criteria)
+ ->find($con);
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryUpdateRelated.php b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryUpdateRelated.php
new file mode 100644
index 000000000..056264c55
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/aggregate_column/templates/queryUpdateRelated.php
@@ -0,0 +1,8 @@
+
+protected function updateRelateds($con)
+{
+ foreach ($this->s as $) {
+ $->($con);
+ }
+ $this->s = array();
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceBehavior.php b/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceBehavior.php
new file mode 100644
index 000000000..8d5011a94
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceBehavior.php
@@ -0,0 +1,235 @@
+ '',
+ 'descendant_column' => 'descendant_class',
+ 'copy_data_to_parent' => 'true'
+ );
+
+ public function modifyTable()
+ {
+ $table = $this->getTable();
+ $parentTable = $this->getParentTable();
+
+ if ($this->isCopyData()) {
+ // tell the parent table that it has a descendant
+ if (!$parentTable->hasBehavior('concrete_inheritance_parent')) {
+ $parentBehavior = new ConcreteInheritanceParentBehavior();
+ $parentBehavior->setName('concrete_inheritance_parent');
+ $parentBehavior->addParameter(array('name' => 'descendant_column', 'value' => $this->getParameter('descendant_column')));
+ $parentTable->addBehavior($parentBehavior);
+ // The parent table's behavior modifyTable() must be executed before this one
+ $parentBehavior->getTableModifier()->modifyTable();
+ $parentBehavior->setTableModified(true);
+ }
+ }
+
+ // Add the columns of the parent table
+ foreach ($parentTable->getColumns() as $column) {
+ if ($column->getName() == $this->getParameter('descendant_column')) {
+ continue;
+ }
+ if ($table->containsColumn($column->getName())) {
+ continue;
+ }
+ $copiedColumn = clone $column;
+ if ($column->isAutoIncrement() && $this->isCopyData()) {
+ $copiedColumn->setAutoIncrement(false);
+ }
+ $table->addColumn($copiedColumn);
+ if ($column->isPrimaryKey() && $this->isCopyData()) {
+ $fk = new ForeignKey();
+ $fk->setForeignTableName($column->getTable()->getName());
+ $fk->setOnDelete('CASCADE');
+ $fk->setOnUpdate(null);
+ $fk->addReference($copiedColumn, $column);
+ $fk->isParentChild = true;
+ $table->addForeignKey($fk);
+ }
+ }
+
+ // add the foreign keys of the parent table
+ foreach ($parentTable->getForeignKeys() as $fk) {
+ $copiedFk = clone $fk;
+ $copiedFk->setName('');
+ $copiedFk->setRefPhpName('');
+ $this->getTable()->addForeignKey($copiedFk);
+ }
+
+ // add the validators of the parent table
+ foreach ($parentTable->getValidators() as $validator) {
+ $copiedValidator = clone $validator;
+ $this->getTable()->addValidator($copiedValidator);
+ }
+
+ // add the indices of the parent table
+ foreach ($parentTable->getIndices() as $index) {
+ $copiedIndex = clone $index;
+ $copiedIndex->setName('');
+ $this->getTable()->addIndex($copiedIndex);
+ }
+
+ // add the unique indices of the parent table
+ foreach ($parentTable->getUnices() as $unique) {
+ $copiedUnique = clone $unique;
+ $copiedUnique->setName('');
+ $this->getTable()->addUnique($copiedUnique);
+ }
+
+ // give name to newly added foreign keys and indices
+ // (this is already done for other elements of the current table)
+ $table->doNaming();
+
+ // add the Behaviors of the parent table
+ foreach ($parentTable->getBehaviors() as $behavior) {
+ if ($behavior->getName() == 'concrete_inheritance_parent' || $behavior->getName() == 'concrete_inheritance') {
+ continue;
+ }
+ $copiedBehavior = clone $behavior;
+ $this->getTable()->addBehavior($copiedBehavior);
+ }
+
+ }
+
+ protected function getParentTable()
+ {
+ return $this->getTable()->getDatabase()->getTable($this->getParameter('extends'));
+ }
+
+ protected function isCopyData()
+ {
+ return $this->getParameter('copy_data_to_parent') == 'true';
+ }
+
+ public function parentClass($builder)
+ {
+ switch (get_class($builder)) {
+ case 'PHP5ObjectBuilder':
+ return $builder->getNewStubObjectBuilder($this->getParentTable())->getClassname();
+ break;
+ case 'QueryBuilder':
+ return $builder->getNewStubQueryBuilder($this->getParentTable())->getClassname();
+ break;
+ default:
+ return null;
+ break;
+ }
+ }
+
+ public function preSave($script)
+ {
+ if ($this->isCopyData()) {
+ return "\$parent = \$this->getSyncParent(\$con);
+\$parent->save(\$con);
+\$this->setPrimaryKey(\$parent->getPrimaryKey());
+";
+ }
+ }
+
+ public function postDelete($script)
+ {
+ if ($this->isCopyData()) {
+ return "\$this->getParentOrCreate(\$con)->delete(\$con);
+";
+ }
+ }
+
+ public function objectMethods($builder)
+ {
+ if (!$this->isCopyData()) {
+ return;
+ }
+ $this->builder = $builder;
+ $script .= '';
+ $this->addObjectGetParentOrCreate($script);
+ $this->addObjectGetSyncParent($script);
+
+ return $script;
+ }
+
+ protected function addObjectGetParentOrCreate(&$script)
+ {
+ $parentTable = $this->getParentTable();
+ $parentClass = $this->builder->getNewStubObjectBuilder($parentTable)->getClassname();
+ $script .= "
+/**
+ * Get or Create the parent " . $parentClass . " object of the current object
+ *
+ * @return " . $parentClass . " The parent object
+ */
+public function getParentOrCreate(\$con = null)
+{
+ if (\$this->isNew()) {
+ \$parent = new " . $parentClass . "();
+ \$parent->set" . $this->getParentTable()->getColumn($this->getParameter('descendant_column'))->getPhpName() . "('" . $this->builder->getStubObjectBuilder()->getClassname() . "');
+ return \$parent;
+ } else {
+ return " . $this->builder->getNewStubQueryBuilder($parentTable)->getClassname() . "::create()->findPk(\$this->getPrimaryKey(), \$con);
+ }
+}
+";
+ }
+
+ protected function addObjectGetSyncParent(&$script)
+ {
+ $parentTable = $this->getParentTable();
+ $pkeys = $parentTable->getPrimaryKey();
+ $cptype = $pkeys[0]->getPhpType();
+ $script .= "
+/**
+ * Create or Update the parent " . $parentTable->getPhpName() . " object
+ * And return its primary key
+ *
+ * @return " . $cptype . " The primary key of the parent object
+ */
+public function getSyncParent(\$con = null)
+{
+ \$parent = \$this->getParentOrCreate(\$con);";
+ foreach ($parentTable->getColumns() as $column) {
+ if ($column->isPrimaryKey() || $column->getName() == $this->getParameter('descendant_column')) {
+ continue;
+ }
+ $phpName = $column->getPhpName();
+ $script .= "
+ \$parent->set{$phpName}(\$this->get{$phpName}());";
+ }
+ foreach ($parentTable->getForeignKeys() as $fk) {
+ if (isset($fk->isParentChild) && $fk->isParentChild) {
+ continue;
+ }
+ $refPhpName = $this->builder->getFKPhpNameAffix($fk, $plural = false);
+ $script .= "
+ if (\$this->get" . $refPhpName . "() && \$this->get" . $refPhpName . "()->isNew()) {
+ \$parent->set" . $refPhpName . "(\$this->get" . $refPhpName . "());
+ }";
+ }
+ $script .= "
+
+ return \$parent;
+}
+";
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceParentBehavior.php b/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceParentBehavior.php
new file mode 100644
index 000000000..e2cdb2fc6
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/concrete_inheritance/ConcreteInheritanceParentBehavior.php
@@ -0,0 +1,89 @@
+ 'descendant_class'
+ );
+
+ public function modifyTable()
+ {
+ $table = $this->getTable();
+ if (!$table->containsColumn($this->getParameter('descendant_column'))) {
+ $table->addColumn(array(
+ 'name' => $this->getParameter('descendant_column'),
+ 'type' => 'VARCHAR',
+ 'size' => 100
+ ));
+ }
+ }
+
+ protected function getColumnGetter()
+ {
+ return 'get' . $this->getColumnForParameter('descendant_column')->getPhpName();
+ }
+
+ public function objectMethods($builder)
+ {
+ $this->builder = $builder;
+ $script .= '';
+ $this->addHasChildObject($script);
+ $this->addGetChildObject($script);
+
+ return $script;
+ }
+
+ protected function addHasChildObject(&$script)
+ {
+ $script .= "
+/**
+ * Whether or not this object is the parent of a child object
+ *
+ * @return bool
+ */
+public function hasChildObject()
+{
+ return \$this->" . $this->getColumnGetter() . "() !== null;
+}
+";
+ }
+
+ protected function addGetChildObject(&$script)
+ {
+ $script .= "
+/**
+ * Get the child object of this object
+ *
+ * @return mixed
+ */
+public function getChildObject()
+{
+ if (!\$this->hasChildObject()) {
+ return null;
+ }
+ \$childObjectClass = \$this->" . $this->getColumnGetter() . "();
+ \$childObject = PropelQuery::from(\$childObjectClass)->findPk(\$this->getPrimaryKey());
+ return \$childObject->hasChildObject() ? \$childObject->getChildObject() : \$childObject;
+}
+";
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehavior.php b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehavior.php
new file mode 100644
index 000000000..5e8996352
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehavior.php
@@ -0,0 +1,99 @@
+ 'tree_left',
+ 'right_column' => 'tree_right',
+ 'level_column' => 'tree_level',
+ 'use_scope' => 'false',
+ 'scope_column' => 'tree_scope',
+ 'method_proxies' => 'false'
+ );
+
+ protected $objectBuilderModifier, $queryBuilderModifier, $peerBuilderModifier;
+
+ /**
+ * Add the left, right and scope to the current table
+ */
+ public function modifyTable()
+ {
+ if(!$this->getTable()->containsColumn($this->getParameter('left_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('left_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ if(!$this->getTable()->containsColumn($this->getParameter('right_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('right_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ if(!$this->getTable()->containsColumn($this->getParameter('level_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('level_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ if ($this->getParameter('use_scope') == 'true' &&
+ !$this->getTable()->containsColumn($this->getParameter('scope_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('scope_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ }
+
+ public function getObjectBuilderModifier()
+ {
+ if (is_null($this->objectBuilderModifier))
+ {
+ $this->objectBuilderModifier = new NestedSetBehaviorObjectBuilderModifier($this);
+ }
+ return $this->objectBuilderModifier;
+ }
+
+ public function getQueryBuilderModifier()
+ {
+ if (is_null($this->queryBuilderModifier))
+ {
+ $this->queryBuilderModifier = new NestedSetBehaviorQueryBuilderModifier($this);
+ }
+ return $this->queryBuilderModifier;
+ }
+
+ public function getPeerBuilderModifier()
+ {
+ if (is_null($this->peerBuilderModifier))
+ {
+ $this->peerBuilderModifier = new NestedSetBehaviorPeerBuilderModifier($this);
+ }
+ return $this->peerBuilderModifier;
+ }
+
+ public function useScope()
+ {
+ return $this->getParameter('use_scope') == 'true';
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorObjectBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorObjectBuilderModifier.php
new file mode 100644
index 000000000..3b99c2c87
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorObjectBuilderModifier.php
@@ -0,0 +1,1540 @@
+
+ * @package propel.generator.behavior.nestedset
+ */
+class NestedSetBehaviorObjectBuilderModifier
+{
+ protected $behavior, $table, $builder, $objectClassname, $peerClassname;
+
+ public function __construct($behavior)
+ {
+ $this->behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumnAttribute($name)
+ {
+ return strtolower($this->behavior->getColumnForParameter($name)->getName());
+ }
+
+ protected function getColumnPhpName($name)
+ {
+ return $this->behavior->getColumnForParameter($name)->getPhpName();
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ /*
+ public function objectFilter(&$script, $builder)
+ {
+ $script = str_replace('implements Persistent', 'implements Persistent, NodeObject', $script);
+ }
+ */
+
+ public function objectAttributes($builder)
+ {
+ $objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ return "
+/**
+ * Queries to be executed in the save transaction
+ * @var array
+ */
+protected \$nestedSetQueries = array();
+
+/**
+ * Internal cache for children nodes
+ * @var null|PropelObjectCollection
+ */
+protected \$collNestedSetChildren = null;
+
+/**
+ * Internal cache for parent node
+ * @var null|$objectClassname
+ */
+protected \$aNestedSetParent = null;
+
+";
+ }
+
+ public function preSave($builder)
+ {
+ return "\$this->processNestedSetQueries(\$con);";
+ }
+
+ public function preDelete($builder)
+ {
+ $peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ return "if (\$this->isRoot()) {
+ throw new PropelException('Deletion of a root node is disabled for nested sets. Use $peerClassname::deleteTree(" . ($this->behavior->useScope() ? '$scope' : '') . ") instead to delete an entire tree');
+}
+\$this->deleteDescendants(\$con);
+";
+ }
+
+ public function postDelete($builder)
+ {
+ $peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ return "// fill up the room that was used by the node
+$peerClassname::shiftRLValues(-2, \$this->getRightValue() + 1, null" . ($this->behavior->useScope() ? ", \$this->getScopeValue()" : "") . ", \$con);
+";
+ }
+
+ public function objectClearReferences($builder)
+ {
+ return "\$this->collNestedSetChildren = null;
+\$this->aNestedSetParent = null;";
+ }
+
+ public function objectMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+
+ $this->addProcessNestedSetQueries($script);
+
+ $this->addGetLeft($script);
+ $this->addGetRight($script);
+ $this->addGetLevel($script);
+ if ($this->getParameter('use_scope') == 'true')
+ {
+ $this->addGetScope($script);
+ }
+
+ $this->addSetLeft($script);
+ $this->addSetRight($script);
+ $this->addSetLevel($script);
+ if ($this->getParameter('use_scope') == 'true')
+ {
+ $this->addSetScope($script);
+ }
+
+ $this->addMakeRoot($script);
+
+ $this->addIsInTree($script);
+ $this->addIsRoot($script);
+ $this->addIsLeaf($script);
+ $this->addIsDescendantOf($script);
+ $this->addIsAncestorOf($script);
+
+ $this->addHasParent($script);
+ $this->addSetParent($script);
+ $this->addGetParent($script);
+
+ $this->addHasPrevSibling($script);
+ $this->addGetPrevSibling($script);
+
+ $this->addHasNextSibling($script);
+ $this->addGetNextSibling($script);
+
+ $this->addNestedSetChildrenClear($script);
+ $this->addNestedSetChildrenInit($script);
+ $this->addNestedSetChildAdd($script);
+ $this->addHasChildren($script);
+ $this->addGetChildren($script);
+ $this->addCountChildren($script);
+
+ $this->addGetFirstChild($script);
+ $this->addGetLastChild($script);
+ $this->addGetSiblings($script);
+ $this->addGetDescendants($script);
+ $this->addCountDescendants($script);
+ $this->addGetBranch($script);
+ $this->addGetAncestors($script);
+
+ $this->addAddChild($script);
+ $this->addInsertAsFirstChildOf($script);
+ $this->addInsertAsLastChildOf($script);
+ $this->addInsertAsPrevSiblingOf($script);
+ $this->addInsertAsNextSiblingOf($script);
+
+ $this->addMoveToFirstChildOf($script);
+ $this->addMoveToLastChildOf($script);
+ $this->addMoveToPrevSiblingOf($script);
+ $this->addMoveToNextSiblingOf($script);
+ $this->addMoveSubtreeTo($script);
+
+ $this->addDeleteDescendants($script);
+
+ $this->addGetIterator($script);
+
+ if ($this->getParameter('method_proxies') == 'true')
+ {
+ $this->addCompatibilityProxies($script);
+ }
+
+ return $script;
+ }
+
+ protected function addProcessNestedSetQueries(&$script)
+ {
+ $script .= "
+/**
+ * Execute queries that were saved to be run inside the save transaction
+ */
+protected function processNestedSetQueries(\$con)
+{
+ foreach (\$this->nestedSetQueries as \$query) {
+ \$query['arguments'][]= \$con;
+ call_user_func_array(\$query['callable'], \$query['arguments']);
+ }
+ \$this->nestedSetQueries = array();
+}
+";
+ }
+ protected function addGetLeft(&$script)
+ {
+ $script .= "
+/**
+ * Wraps the getter for the nested set left value
+ *
+ * @return int
+ */
+public function getLeftValue()
+{
+ return \$this->{$this->getColumnAttribute('left_column')};
+}
+";
+ }
+
+ protected function addGetRight(&$script)
+ {
+ $script .= "
+/**
+ * Wraps the getter for the nested set right value
+ *
+ * @return int
+ */
+public function getRightValue()
+{
+ return \$this->{$this->getColumnAttribute('right_column')};
+}
+";
+ }
+
+ protected function addGetLevel(&$script)
+ {
+ $script .= "
+/**
+ * Wraps the getter for the nested set level
+ *
+ * @return int
+ */
+public function getLevel()
+{
+ return \$this->{$this->getColumnAttribute('level_column')};
+}
+";
+ }
+
+ protected function addGetScope(&$script)
+ {
+ $script .= "
+/**
+ * Wraps the getter for the scope value
+ *
+ * @return int or null if scope is disabled
+ */
+public function getScopeValue()
+{
+ return \$this->{$this->getColumnAttribute('scope_column')};
+}
+";
+ }
+
+ protected function addSetLeft(&$script)
+ {
+ $script .= "
+/**
+ * Set the value left column
+ *
+ * @param int \$v new value
+ * @return {$this->objectClassname} The current object (for fluent API support)
+ */
+public function setLeftValue(\$v)
+{
+ return \$this->set{$this->getColumnPhpName('left_column')}(\$v);
+}
+";
+ }
+
+ protected function addSetRight(&$script)
+ {
+ $script .= "
+/**
+ * Set the value of right column
+ *
+ * @param int \$v new value
+ * @return {$this->objectClassname} The current object (for fluent API support)
+ */
+public function setRightValue(\$v)
+{
+ return \$this->set{$this->getColumnPhpName('right_column')}(\$v);
+}
+";
+ }
+
+ protected function addSetLevel(&$script)
+ {
+ $script .= "
+/**
+ * Set the value of level column
+ *
+ * @param int \$v new value
+ * @return {$this->objectClassname} The current object (for fluent API support)
+ */
+public function setLevel(\$v)
+{
+ return \$this->set{$this->getColumnPhpName('level_column')}(\$v);
+}
+";
+ }
+
+ protected function addSetScope(&$script)
+ {
+ $script .= "
+/**
+ * Set the value of scope column
+ *
+ * @param int \$v new value
+ * @return {$this->objectClassname} The current object (for fluent API support)
+ */
+public function setScopeValue(\$v)
+{
+ return \$this->set{$this->getColumnPhpName('scope_column')}(\$v);
+}
+";
+ }
+
+ protected function addMakeRoot(&$script)
+ {
+ $script .= "
+/**
+ * Creates the supplied node as the root node.
+ *
+ * @return {$this->objectClassname} The current object (for fluent API support)
+ * @throws PropelException
+ */
+public function makeRoot()
+{
+ if (\$this->getLeftValue() || \$this->getRightValue()) {
+ throw new PropelException('Cannot turn an existing node into a root node.');
+ }
+
+ \$this->setLeftValue(1);
+ \$this->setRightValue(2);
+ \$this->setLevel(0);
+ return \$this;
+}
+";
+ }
+
+ protected function addIsInTree(&$script)
+ {
+ $script .= "
+/**
+ * Tests if onbject is a node, i.e. if it is inserted in the tree
+ *
+ * @return bool
+ */
+public function isInTree()
+{
+ return \$this->getLeftValue() > 0 && \$this->getRightValue() > \$this->getLeftValue();
+}
+";
+ }
+
+ protected function addIsRoot(&$script)
+ {
+ $script .= "
+/**
+ * Tests if node is a root
+ *
+ * @return bool
+ */
+public function isRoot()
+{
+ return \$this->isInTree() && \$this->getLeftValue() == 1;
+}
+";
+ }
+
+ protected function addIsLeaf(&$script)
+ {
+ $script .= "
+/**
+ * Tests if node is a leaf
+ *
+ * @return bool
+ */
+public function isLeaf()
+{
+ return \$this->isInTree() && (\$this->getRightValue() - \$this->getLeftValue()) == 1;
+}
+";
+ }
+
+ protected function addIsDescendantOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $script .= "
+/**
+ * Tests if node is a descendant of another node
+ *
+ * @param $objectClassname \$node Propel node object
+ * @return bool
+ */
+public function isDescendantOf(\$parent)
+{";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ if (\$this->getScopeValue() !== \$parent->getScopeValue()) {
+ throw new PropelException('Comparing two nodes of different trees');
+ }";
+ }
+ $script .= "
+ return \$this->isInTree() && \$this->getLeftValue() > \$parent->getLeftValue() && \$this->getRightValue() < \$parent->getRightValue();
+}
+";
+ }
+
+ protected function addIsAncestorOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $script .= "
+/**
+ * Tests if node is a ancestor of another node
+ *
+ * @param $objectClassname \$node Propel node object
+ * @return bool
+ */
+public function isAncestorOf(\$child)
+{
+ return \$child->isDescendantOf(\$this);
+}
+";
+ }
+
+ protected function addHasParent(&$script)
+ {
+ $script .= "
+/**
+ * Tests if object has an ancestor
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+public function hasParent(PropelPDO \$con = null)
+{
+ return \$this->getLevel() > 0;
+}
+";
+ }
+
+ protected function addSetParent(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $script .= "
+/**
+ * Sets the cache for parent node of the current object.
+ * Warning: this does not move the current object in the tree.
+ * Use moveTofirstChildOf() or moveToLastChildOf() for that purpose
+ *
+ * @param $objectClassname \$parent
+ * @return $objectClassname The current object, for fluid interface
+ */
+public function setParent(\$parent = null)
+{
+ \$this->aNestedSetParent = \$parent;
+ return \$this;
+}
+";
+ }
+
+
+ protected function addGetParent(&$script)
+ {
+ $script .= "
+/**
+ * Gets parent node for the current object if it exists
+ * The result is cached so further calls to the same method don't issue any queries
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+public function getParent(PropelPDO \$con = null)
+{
+ if (\$this->aNestedSetParent === null && \$this->hasParent()) {
+ \$this->aNestedSetParent = {$this->queryClassname}::create()
+ ->ancestorsOf(\$this)
+ ->orderByLevel(true)
+ ->findOne(\$con);
+ }
+ return \$this->aNestedSetParent;
+}
+";
+ }
+
+ protected function addHasPrevSibling(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Determines if the node has previous sibling
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+public function hasPrevSibling(PropelPDO \$con = null)
+{
+ if (!{$this->peerClassname}::isValid(\$this)) {
+ return false;
+ }
+ return $queryClassname::create()
+ ->filterBy" . $this->getColumnPhpName('right_column') . "(\$this->getLeftValue() - 1)";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree(\$this->getScopeValue())";
+ }
+ $script .= "
+ ->count(\$con) > 0;
+}
+";
+ }
+
+ protected function addGetPrevSibling(&$script)
+ {
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets previous sibling for the given node if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+public function getPrevSibling(PropelPDO \$con = null)
+{
+ return $queryClassname::create()
+ ->filterBy" . $this->getColumnPhpName('right_column') . "(\$this->getLeftValue() - 1)";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree(\$this->getScopeValue())";
+ }
+ $script .= "
+ ->findOne(\$con);
+}
+";
+ }
+
+ protected function addHasNextSibling(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Determines if the node has next sibling
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+public function hasNextSibling(PropelPDO \$con = null)
+{
+ if (!{$this->peerClassname}::isValid(\$this)) {
+ return false;
+ }
+ return $queryClassname::create()
+ ->filterBy" . $this->getColumnPhpName('left_column') . "(\$this->getRightValue() + 1)";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree(\$this->getScopeValue())";
+ }
+ $script .= "
+ ->count(\$con) > 0;
+}
+";
+ }
+
+ protected function addGetNextSibling(&$script)
+ {
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets next sibling for the given node if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+public function getNextSibling(PropelPDO \$con = null)
+{
+ return $queryClassname::create()
+ ->filterBy" . $this->getColumnPhpName('left_column') . "(\$this->getRightValue() + 1)";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree(\$this->getScopeValue())";
+ }
+ $script .= "
+ ->findOne(\$con);
+}
+";
+ }
+
+ protected function addNestedSetChildrenClear(&$script)
+ {
+ $script .= "
+/**
+ * Clears out the \$collNestedSetChildren collection
+ *
+ * This does not modify the database; however, it will remove any associated objects, causing
+ * them to be refetched by subsequent calls to accessor method.
+ *
+ * @return void
+ */
+public function clearNestedSetChildren()
+{
+ \$this->collNestedSetChildren = null;
+}
+";
+ }
+
+ protected function addNestedSetChildrenInit(&$script)
+ {
+ $script .= "
+/**
+ * Initializes the \$collNestedSetChildren collection.
+ *
+ * @return void
+ */
+public function initNestedSetChildren()
+{
+ \$this->collNestedSetChildren = new PropelObjectCollection();
+ \$this->collNestedSetChildren->setModel('" . $this->builder->getNewStubObjectBuilder($this->table)->getClassname() . "');
+}
+";
+ }
+
+ protected function addNestedSetChildAdd(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Adds an element to the internal \$collNestedSetChildren collection.
+ * Beware that this doesn't insert a node in the tree.
+ * This method is only used to facilitate children hydration.
+ *
+ * @param $objectClassname $objectName
+ *
+ * @return void
+ */
+public function addNestedSetChild($objectName)
+{
+ if (\$this->collNestedSetChildren === null) {
+ \$this->initNestedSetChildren();
+ }
+ if (!\$this->collNestedSetChildren->contains($objectName)) { // only add it if the **same** object is not already associated
+ \$this->collNestedSetChildren[]= $objectName;
+ {$objectName}->setParent(\$this);
+ }
+}
+";
+ }
+
+ protected function addHasChildren(&$script)
+ {
+ $script .= "
+/**
+ * Tests if node has children
+ *
+ * @return bool
+ */
+public function hasChildren()
+{
+ return (\$this->getRightValue() - \$this->getLeftValue()) > 1;
+}
+";
+ }
+
+ protected function addGetChildren(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets the children of the given node
+ *
+ * @param Criteria \$criteria Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getChildren(\$criteria = null, PropelPDO \$con = null)
+{
+ if(null === \$this->collNestedSetChildren || null !== \$criteria) {
+ if (\$this->isLeaf() || (\$this->isNew() && null === \$this->collNestedSetChildren)) {
+ // return empty collection
+ \$this->initNestedSetChildren();
+ } else {
+ \$collNestedSetChildren = $queryClassname::create(null, \$criteria)
+ ->childrenOf(\$this)
+ ->orderByBranch()
+ ->find(\$con);
+ if (null !== \$criteria) {
+ return \$collNestedSetChildren;
+ }
+ \$this->collNestedSetChildren = \$collNestedSetChildren;
+ }
+ }
+ return \$this->collNestedSetChildren;
+}
+";
+ }
+
+ protected function addCountChildren(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets number of children for the given node
+ *
+ * @param Criteria \$criteria Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return int Number of children
+ */
+public function countChildren(\$criteria = null, PropelPDO \$con = null)
+{
+ if(null === \$this->collNestedSetChildren || null !== \$criteria) {
+ if (\$this->isLeaf() || (\$this->isNew() && null === \$this->collNestedSetChildren)) {
+ return 0;
+ } else {
+ return $queryClassname::create(null, \$criteria)
+ ->childrenOf(\$this)
+ ->count(\$con);
+ }
+ } else {
+ return count(\$this->collNestedSetChildren);
+ }
+}
+";
+ }
+
+ protected function addGetFirstChild(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets the first child of the given node
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getFirstChild(\$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isLeaf()) {
+ return array();
+ } else {
+ return $queryClassname::create(null, \$query)
+ ->childrenOf(\$this)
+ ->orderByBranch()
+ ->findOne(\$con);
+ }
+}
+";
+ }
+
+ protected function addGetLastChild(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets the last child of the given node
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getLastChild(\$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isLeaf()) {
+ return array();
+ } else {
+ return $queryClassname::create(null, \$query)
+ ->childrenOf(\$this)
+ ->orderByBranch(true)
+ ->findOne(\$con);
+ }
+}
+";
+ }
+
+ protected function addGetSiblings(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets the siblings of the given node
+ *
+ * @param bool \$includeNode Whether to include the current node or not
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return array List of $objectClassname objects
+ */
+public function getSiblings(\$includeNode = false, \$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isRoot()) {
+ return array();
+ } else {
+ \$query = $queryClassname::create(null, \$query)
+ ->childrenOf(\$this->getParent(\$con))
+ ->orderByBranch(true);
+ if (!\$includeNode) {
+ \$query->prune(\$this);
+ }
+ return \$query->find(\$con);
+ }
+}
+";
+ }
+
+ protected function addGetDescendants(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets descendants for the given node
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getDescendants(\$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isLeaf()) {
+ return array();
+ } else {
+ return $queryClassname::create(null, \$query)
+ ->descendantsOf(\$this)
+ ->orderByBranch()
+ ->find(\$con);
+ }
+}
+";
+ }
+
+ protected function addCountDescendants(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets number of descendants for the given node
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return int Number of descendants
+ */
+public function countDescendants(\$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isLeaf()) {
+ // save one query
+ return 0;
+ } else {
+ return $queryClassname::create(null, \$query)
+ ->descendantsOf(\$this)
+ ->count(\$con);
+ }
+}
+";
+ }
+
+ protected function addGetBranch(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets descendants for the given node, plus the current node
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getBranch(\$query = null, PropelPDO \$con = null)
+{
+ return $queryClassname::create(null, \$query)
+ ->branchOf(\$this)
+ ->orderByBranch()
+ ->find(\$con);
+}
+";
+ }
+
+ protected function addGetAncestors(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $script .= "
+/**
+ * Gets ancestors for the given node, starting with the root node
+ * Use it for breadcrumb paths for instance
+ *
+ * @param Criteria \$query Criteria to filter results.
+ * @param PropelPDO \$con Connection to use.
+ * @return array List of $objectClassname objects
+ */
+public function getAncestors(\$query = null, PropelPDO \$con = null)
+{
+ if(\$this->isRoot()) {
+ // save one query
+ return array();
+ } else {
+ return $queryClassname::create(null, \$query)
+ ->ancestorsOf(\$this)
+ ->orderByBranch()
+ ->find(\$con);
+ }
+}
+";
+ }
+
+ protected function addAddChild(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Inserts the given \$child node as first child of current
+ * The modifications in the current object and the tree
+ * are not persisted until the child object is saved.
+ *
+ * @param $objectClassname \$child Propel object for child node
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function addChild($objectClassname \$child)
+{
+ if (\$this->isNew()) {
+ throw new PropelException('A $objectClassname object must not be new to accept children.');
+ }
+ \$child->insertAsFirstChildOf(\$this);
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAsFirstChildOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Inserts the current node as first child of given \$parent node
+ * The modifications in the current object and the tree
+ * are not persisted until the current object is saved.
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function insertAsFirstChildOf(\$parent)
+{
+ if (\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must not already be in the tree to be inserted. Use the moveToFirstChildOf() instead.');
+ }
+ \$left = \$parent->getLeftValue() + 1;
+ // Update node properties
+ \$this->setLeftValue(\$left);
+ \$this->setRightValue(\$left + 1);
+ \$this->setLevel(\$parent->getLevel() + 1);";
+ if ($useScope)
+ {
+ $script .= "
+ \$scope = \$parent->getScopeValue();
+ \$this->setScopeValue(\$scope);";
+ }
+ $script .= "
+ // update the children collection of the parent
+ \$parent->addNestedSetChild(\$this);
+
+ // Keep the tree modification query for the save() transaction
+ \$this->nestedSetQueries []= array(
+ 'callable' => array('$peerClassname', 'makeRoomForLeaf'),
+ 'arguments' => array(\$left" . ($useScope ? ", \$scope" : "") . ")
+ );
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAsLastChildOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Inserts the current node as last child of given \$parent node
+ * The modifications in the current object and the tree
+ * are not persisted until the current object is saved.
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function insertAsLastChildOf(\$parent)
+{
+ if (\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must not already be in the tree to be inserted. Use the moveToLastChildOf() instead.');
+ }
+ \$left = \$parent->getRightValue();
+ // Update node properties
+ \$this->setLeftValue(\$left);
+ \$this->setRightValue(\$left + 1);
+ \$this->setLevel(\$parent->getLevel() + 1);";
+ if ($useScope)
+ {
+ $script .= "
+ \$scope = \$parent->getScopeValue();
+ \$this->setScopeValue(\$scope);";
+ }
+ $script .= "
+ // update the children collection of the parent
+ \$parent->addNestedSetChild(\$this);
+
+ // Keep the tree modification query for the save() transaction
+ \$this->nestedSetQueries []= array(
+ 'callable' => array('$peerClassname', 'makeRoomForLeaf'),
+ 'arguments' => array(\$left" . ($useScope ? ", \$scope" : "") . ")
+ );
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAsPrevSiblingOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Inserts the current node as prev sibling given \$sibling node
+ * The modifications in the current object and the tree
+ * are not persisted until the current object is saved.
+ *
+ * @param $objectClassname \$sibling Propel object for parent node
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function insertAsPrevSiblingOf(\$sibling)
+{
+ if (\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must not already be in the tree to be inserted. Use the moveToPrevSiblingOf() instead.');
+ }
+ \$left = \$sibling->getLeftValue();
+ // Update node properties
+ \$this->setLeftValue(\$left);
+ \$this->setRightValue(\$left + 1);
+ \$this->setLevel(\$sibling->getLevel());";
+ if ($useScope)
+ {
+ $script .= "
+ \$scope = \$sibling->getScopeValue();
+ \$this->setScopeValue(\$scope);";
+ }
+ $script .= "
+ // Keep the tree modification query for the save() transaction
+ \$this->nestedSetQueries []= array(
+ 'callable' => array('$peerClassname', 'makeRoomForLeaf'),
+ 'arguments' => array(\$left" . ($useScope ? ", \$scope" : "") . ")
+ );
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAsNextSiblingOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Inserts the current node as next sibling given \$sibling node
+ * The modifications in the current object and the tree
+ * are not persisted until the current object is saved.
+ *
+ * @param $objectClassname \$sibling Propel object for parent node
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function insertAsNextSiblingOf(\$sibling)
+{
+ if (\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must not already be in the tree to be inserted. Use the moveToNextSiblingOf() instead.');
+ }
+ \$left = \$sibling->getRightValue() + 1;
+ // Update node properties
+ \$this->setLeftValue(\$left);
+ \$this->setRightValue(\$left + 1);
+ \$this->setLevel(\$sibling->getLevel());";
+ if ($useScope)
+ {
+ $script .= "
+ \$scope = \$sibling->getScopeValue();
+ \$this->setScopeValue(\$scope);";
+ }
+ $script .= "
+ // Keep the tree modification query for the save() transaction
+ \$this->nestedSetQueries []= array(
+ 'callable' => array('$peerClassname', 'makeRoomForLeaf'),
+ 'arguments' => array(\$left" . ($useScope ? ", \$scope" : "") . ")
+ );
+ return \$this;
+}
+";
+ }
+
+ protected function addMoveToFirstChildOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Moves current node and its subtree to be the first child of \$parent
+ * The modifications in the current object and the tree are immediate
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function moveToFirstChildOf(\$parent, PropelPDO \$con = null)
+{
+ if (!\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must be already in the tree to be moved. Use the insertAsFirstChildOf() instead.');
+ }";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ if (\$parent->getScopeValue() != \$this->getScopeValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }";
+ }
+ $script .= "
+ if (\$parent->isDescendantOf(\$this)) {
+ throw new PropelException('Cannot move a node as child of one of its subtree nodes.');
+ }
+
+ \$this->moveSubtreeTo(\$parent->getLeftValue() + 1, \$parent->getLevel() - \$this->getLevel() + 1, \$con);
+
+ return \$this;
+}
+";
+ }
+
+ protected function addMoveToLastChildOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Moves current node and its subtree to be the last child of \$parent
+ * The modifications in the current object and the tree are immediate
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function moveToLastChildOf(\$parent, PropelPDO \$con = null)
+{
+ if (!\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must be already in the tree to be moved. Use the insertAsLastChildOf() instead.');
+ }";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ if (\$parent->getScopeValue() != \$this->getScopeValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }";
+ }
+ $script .= "
+ if (\$parent->isDescendantOf(\$this)) {
+ throw new PropelException('Cannot move a node as child of one of its subtree nodes.');
+ }
+
+ \$this->moveSubtreeTo(\$parent->getRightValue(), \$parent->getLevel() - \$this->getLevel() + 1, \$con);
+
+ return \$this;
+}
+";
+ }
+
+ protected function addMoveToPrevSiblingOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Moves current node and its subtree to be the previous sibling of \$sibling
+ * The modifications in the current object and the tree are immediate
+ *
+ * @param $objectClassname \$sibling Propel object for sibling node
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function moveToPrevSiblingOf(\$sibling, PropelPDO \$con = null)
+{
+ if (!\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must be already in the tree to be moved. Use the insertAsPrevSiblingOf() instead.');
+ }
+ if (\$sibling->isRoot()) {
+ throw new PropelException('Cannot move to previous sibling of a root node.');
+ }";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ if (\$sibling->getScopeValue() != \$this->getScopeValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }";
+ }
+ $script .= "
+ if (\$sibling->isDescendantOf(\$this)) {
+ throw new PropelException('Cannot move a node as sibling of one of its subtree nodes.');
+ }
+
+ \$this->moveSubtreeTo(\$sibling->getLeftValue(), \$sibling->getLevel() - \$this->getLevel(), \$con);
+
+ return \$this;
+}
+";
+ }
+
+ protected function addMoveToNextSiblingOf(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Moves current node and its subtree to be the next sibling of \$sibling
+ * The modifications in the current object and the tree are immediate
+ *
+ * @param $objectClassname \$sibling Propel object for sibling node
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return $objectClassname The current Propel object
+ */
+public function moveToNextSiblingOf(\$sibling, PropelPDO \$con = null)
+{
+ if (!\$this->isInTree()) {
+ throw new PropelException('A $objectClassname object must be already in the tree to be moved. Use the insertAsNextSiblingOf() instead.');
+ }
+ if (\$sibling->isRoot()) {
+ throw new PropelException('Cannot move to next sibling of a root node.');
+ }";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ if (\$sibling->getScopeValue() != \$this->getScopeValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }";
+ }
+ $script .= "
+ if (\$sibling->isDescendantOf(\$this)) {
+ throw new PropelException('Cannot move a node as sibling of one of its subtree nodes.');
+ }
+
+ \$this->moveSubtreeTo(\$sibling->getRightValue() + 1, \$sibling->getLevel() - \$this->getLevel(), \$con);
+
+ return \$this;
+}
+";
+ }
+
+ protected function addMoveSubtreeTo(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Move current node and its children to location \$destLeft and updates rest of tree
+ *
+ * @param int \$destLeft Destination left value
+ * @param int \$levelDelta Delta to add to the levels
+ * @param PropelPDO \$con Connection to use.
+ */
+protected function moveSubtreeTo(\$destLeft, \$levelDelta, PropelPDO \$con = null)
+{
+ \$left = \$this->getLeftValue();
+ \$right = \$this->getRightValue();";
+ if ($useScope) {
+ $script .= "
+ \$scope = \$this->getScopeValue();";
+ }
+ $script .= "
+
+ \$treeSize = \$right - \$left +1;
+
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$con->beginTransaction();
+ try {
+ // make room next to the target for the subtree
+ $peerClassname::shiftRLValues(\$treeSize, \$destLeft, null" . ($useScope ? ", \$scope" : "") . ", \$con);
+
+ if (\$left >= \$destLeft) { // src was shifted too?
+ \$left += \$treeSize;
+ \$right += \$treeSize;
+ }
+
+ if (\$levelDelta) {
+ // update the levels of the subtree
+ $peerClassname::shiftLevel(\$levelDelta, \$left, \$right" . ($useScope ? ", \$scope" : "") . ", \$con);
+ }
+
+ // move the subtree to the target
+ $peerClassname::shiftRLValues(\$destLeft - \$left, \$left, \$right" . ($useScope ? ", \$scope" : "") . ", \$con);
+
+ // remove the empty room at the previous location of the subtree
+ $peerClassname::shiftRLValues(-\$treeSize, \$right + 1, null" . ($useScope ? ", \$scope" : "") . ", \$con);
+
+ // update all loaded nodes
+ $peerClassname::updateLoadedNodes(\$con);
+
+ \$con->commit();
+ } catch (PropelException \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addDeleteDescendants(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $peerClassname = $this->peerClassname;
+ $queryClassname = $this->queryClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Deletes all descendants for the given node
+ * Instance pooling is wiped out by this command,
+ * so existing $objectClassname instances are probably invalid (except for the current one)
+ *
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return int number of deleted nodes
+ */
+public function deleteDescendants(PropelPDO \$con = null)
+{
+ if(\$this->isLeaf()) {
+ // save one query
+ return;
+ }
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+ \$left = \$this->getLeftValue();
+ \$right = \$this->getRightValue();";
+ if ($useScope) {
+ $script .= "
+ \$scope = \$this->getScopeValue();";
+ }
+ $script .= "
+ \$con->beginTransaction();
+ try {
+ // delete descendant nodes (will empty the instance pool)
+ \$ret = $queryClassname::create()
+ ->descendantsOf(\$this)
+ ->delete(\$con);
+
+ // fill up the room that was used by descendants
+ $peerClassname::shiftRLValues(\$left - \$right + 1, \$right, null" . ($useScope ? ", \$scope" : "") . ", \$con);
+
+ // fix the right value for the current node, which is now a leaf
+ \$this->setRightValue(\$left + 1);
+
+ \$con->commit();
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+
+ return \$ret;
+}
+";
+ }
+
+ protected function addGetIterator(&$script)
+ {
+ $script .= "
+/**
+ * Returns a pre-order iterator for this node and its children.
+ *
+ * @return RecursiveIterator
+ */
+public function getIterator()
+{
+ return new NestedSetRecursiveIterator(\$this);
+}
+";
+ }
+
+ protected function addCompatibilityProxies(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $script .= "
+/**
+ * Alias for makeRoot(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see makeRoot
+ */
+public function createRoot()
+{
+ return \$this->makeRoot();
+}
+
+/**
+ * Alias for getParent(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getParent
+ */
+public function retrieveParent(PropelPDO \$con = null)
+{
+ return \$this->getParent(\$con);
+}
+
+/**
+ * Alias for setParent(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see setParent
+ */
+public function setParentNode(\$parent = null)
+{
+ return \$this->setParent(\$parent);
+}
+
+/**
+ * Alias for countDecendants(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see setParent
+ */
+public function getNumberOfDescendants(PropelPDO \$con = null)
+{
+ return \$this->countDescendants(null, \$con);
+}
+
+/**
+ * Alias for countChildren(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see setParent
+ */
+public function getNumberOfChildren(PropelPDO \$con = null)
+{
+ return \$this->countChildren(null, \$con);
+}
+
+/**
+ * Alias for getPrevSibling(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getParent
+ */
+public function retrievePrevSibling(PropelPDO \$con = null)
+{
+ return \$this->getPrevSibling(\$con);
+}
+
+/**
+ * Alias for getNextSibling(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getParent
+ */
+public function retrieveNextSibling(PropelPDO \$con = null)
+{
+ return \$this->getNextSibling(\$con);
+}
+
+/**
+ * Alias for getFirstChild(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getParent
+ */
+public function retrieveFirstChild(PropelPDO \$con = null)
+{
+ return \$this->getFirstChild(null, \$con);
+}
+
+/**
+ * Alias for getLastChild(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getParent
+ */
+public function retrieveLastChild(PropelPDO \$con = null)
+{
+ return \$this->getLastChild(null, \$con);
+}
+
+/**
+ * Alias for getAncestors(), for BC with Propel 1.4 nested sets
+ *
+ * @deprecated since 1.5
+ * @see getAncestors
+ */
+public function getPath(PropelPDO \$con = null)
+{
+ return \$this->getAncestors(null, \$con);
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorPeerBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorPeerBuilderModifier.php
new file mode 100644
index 000000000..17bbb9630
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorPeerBuilderModifier.php
@@ -0,0 +1,555 @@
+
+ * @package propel.generator.behavior.nestedset
+ */
+class NestedSetBehaviorPeerBuilderModifier
+{
+ protected $behavior, $table, $builder, $objectClassname, $peerClassname;
+
+ public function __construct($behavior)
+ {
+ $this->behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumn($name)
+ {
+ return $this->behavior->getColumnForParameter($name);
+ }
+
+ protected function getColumnAttribute($name)
+ {
+ return strtolower($this->getColumn($name)->getName());
+ }
+
+ protected function getColumnConstant($name)
+ {
+ return strtoupper($this->getColumn($name)->getName());
+ }
+
+ protected function getColumnPhpName($name)
+ {
+ return $this->getColumn($name)->getPhpName();
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ public function staticAttributes($builder)
+ {
+ $tableName = $this->table->getName();
+
+ $script = "
+/**
+ * Left column for the set
+ */
+const LEFT_COL = '" . $tableName . '.' . $this->getColumnConstant('left_column') . "';
+
+/**
+ * Right column for the set
+ */
+const RIGHT_COL = '" . $tableName . '.' . $this->getColumnConstant('right_column') . "';
+
+/**
+ * Level column for the set
+ */
+const LEVEL_COL = '" . $tableName . '.' . $this->getColumnConstant('level_column') . "';
+";
+
+ if ($this->behavior->useScope()) {
+ $script .= "
+/**
+ * Scope column for the set
+ */
+const SCOPE_COL = '" . $tableName . '.' . $this->getColumnConstant('scope_column') . "';
+";
+ }
+
+ return $script;
+ }
+
+ public function staticMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+
+ if ($this->getParameter('use_scope') == 'true')
+ {
+ $this->addRetrieveRoots($script);
+ }
+ $this->addRetrieveRoot($script);
+ $this->addRetrieveTree($script);
+ $this->addIsValid($script);
+ $this->addDeleteTree($script);
+ $this->addShiftRLValues($script);
+ $this->addShiftLevel($script);
+ $this->addUpdateLoadedNodes($script);
+ $this->addMakeRoomForLeaf($script);
+ $this->addFixLevels($script);
+
+ return $script;
+ }
+
+ protected function addRetrieveRoots(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Returns the root nodes for the tree
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return {$this->objectClassname} Propel object for root node
+ */
+public static function retrieveRoots(Criteria \$criteria = null, PropelPDO \$con = null)
+{
+ if (\$criteria === null) {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ }
+ \$criteria->add($peerClassname::LEFT_COL, 1, Criteria::EQUAL);
+
+ return $peerClassname::doSelect(\$criteria, \$con);
+}
+";
+ }
+
+ protected function addRetrieveRoot(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Returns the root node for a given scope
+ *";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which root node to return";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ * @return {$this->objectClassname} Propel object for root node
+ */
+public static function retrieveRoot(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add($peerClassname::LEFT_COL, 1, Criteria::EQUAL);";
+ if($useScope) {
+ $script .= "
+ \$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ return $peerClassname::doSelectOne(\$c, \$con);
+}
+";
+ }
+
+ protected function addRetrieveTree(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Returns the whole tree node for a given scope
+ *";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which root node to return";
+ }
+ $script .= "
+ * @param Criteria \$criteria Optional Criteria to filter the query
+ * @param PropelPDO \$con Connection to use.
+ * @return {$this->objectClassname} Propel object for root node
+ */
+public static function retrieveTree(" . ($useScope ? "\$scope = null, " : "") . "Criteria \$criteria = null, PropelPDO \$con = null)
+{
+ if (\$criteria === null) {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ }
+ \$criteria->addAscendingOrderByColumn($peerClassname::LEFT_COL);";
+ if($useScope) {
+ $script .= "
+ \$criteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ return $peerClassname::doSelect(\$criteria, \$con);
+}
+";
+ }
+
+ protected function addIsValid(&$script)
+ {
+ $objectClassname = $this->objectClassname;
+ $script .= "
+/**
+ * Tests if node is valid
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @return bool
+ */
+public static function isValid($objectClassname \$node = null)
+{
+ if (is_object(\$node) && \$node->getRightValue() > \$node->getLeftValue()) {
+ return true;
+ } else {
+ return false;
+ }
+}
+";
+ }
+
+ protected function addDeleteTree(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Delete an entire tree
+ * ";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which tree to delete";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return int The number of deleted nodes
+ */
+public static function deleteTree(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{";
+ if($useScope) {
+ $script .= "
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);
+ return $peerClassname::doDelete(\$c, \$con);";
+ } else {
+ $script .= "
+ return $peerClassname::doDeleteAll(\$con);";
+ }
+ $script .= "
+}
+";
+ }
+
+ protected function addShiftRLValues(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Adds \$delta to all L and R values that are >= \$first and <= \$last.
+ * '\$delta' can also be negative.
+ *
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param int \$first First node to be shifted
+ * @param int \$last Last node to be shifted (optional)";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to use for the shift";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function shiftRLValues(\$delta, \$first, \$last = null" . ($useScope ? ", \$scope = null" : ""). ", PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ // Shift left column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion($peerClassname::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
+ if (null !== \$last) {
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::LEFT_COL, \$last, Criteria::LESS_EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);";
+ if ($useScope) {
+ $script .= "
+ \$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add($peerClassname::LEFT_COL, array('raw' => $peerClassname::LEFT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
+
+ {$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+
+ // Shift right column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion($peerClassname::RIGHT_COL, \$first, Criteria::GREATER_EQUAL);
+ if (null !== \$last) {
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::RIGHT_COL, \$last, Criteria::LESS_EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);";
+ if ($useScope) {
+ $script .= "
+ \$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add($peerClassname::RIGHT_COL, array('raw' => $peerClassname::RIGHT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
+
+ {$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+}
+";
+ }
+
+ protected function addShiftLevel(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Adds \$delta to level for nodes having left value >= \$first and right value <= \$last.
+ * '\$delta' can also be negative.
+ *
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param int \$first First node to be shifted
+ * @param int \$last Last node to be shifted";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to use for the shift";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function shiftLevel(\$delta, \$first, \$last" . ($useScope ? ", \$scope = null" : ""). ", PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$whereCriteria->add($peerClassname::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
+ \$whereCriteria->add($peerClassname::RIGHT_COL, \$last, Criteria::LESS_EQUAL);";
+ if ($useScope) {
+ $script .= "
+ \$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add($peerClassname::LEVEL_COL, array('raw' => $peerClassname::LEVEL_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
+
+ {$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+}
+";
+ }
+
+ protected function addUpdateLoadedNodes(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Reload all already loaded nodes to sync them with updated db
+ *
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function updateLoadedNodes(PropelPDO \$con = null)
+{
+ if (Propel::isInstancePoolingEnabled()) {
+ \$keys = array();
+ foreach ($peerClassname::\$instances as \$obj) {
+ \$keys[] = \$obj->getPrimaryKey();
+ }
+
+ if (!empty(\$keys)) {
+ // We don't need to alter the object instance pool; we're just modifying these ones
+ // already in the pool.
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);";
+ if (count($this->table->getPrimaryKey()) === 1) {
+ $pkey = $this->table->getPrimaryKey();
+ $col = array_shift($pkey);
+ $script .= "
+ \$criteria->add(".$this->builder->getColumnConstant($col).", \$keys, Criteria::IN);
+";
+ } else {
+ $fields = array();
+ foreach ($this->table->getPrimaryKey() as $k => $col) {
+ $fields[] = $this->builder->getColumnConstant($col);
+ };
+ $script .= "
+
+ // Loop on each instances in pool
+ foreach (\$keys as \$values) {
+ // Create initial Criterion
+ \$cton = \$criteria->getNewCriterion(" . $fields[0] . ", \$values[0]);";
+ unset($fields[0]);
+ foreach ($fields as $k => $col) {
+ $script .= "
+
+ // Create next criterion
+ \$nextcton = \$criteria->getNewCriterion(" . $col . ", \$values[$k]);
+ // And merge it with the first
+ \$cton->addAnd(\$nextcton);";
+ }
+ $script .= "
+
+ // Add final Criterion to Criteria
+ \$criteria->addOr(\$cton);
+ }";
+ }
+
+ $script .= "
+ \$stmt = $peerClassname::doSelectStmt(\$criteria, \$con);
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$object = $peerClassname::getInstanceFromPool(\$key))) {";
+ $n = 0;
+ foreach ($this->table->getColumns() as $col) {
+ if ($col->getPhpName() == $this->getColumnPhpName('left_column')) {
+ $script .= "
+ \$object->setLeftValue(\$row[$n]);";
+ } else if ($col->getPhpName() == $this->getColumnPhpName('right_column')) {
+ $script .= "
+ \$object->setRightValue(\$row[$n]);";
+ } else if ($col->getPhpName() == $this->getColumnPhpName('level_column')) {
+ $script .= "
+ \$object->setLevel(\$row[$n]);
+ \$object->clearNestedSetChildren();";
+ }
+ $n++;
+ }
+ $script .= "
+ }
+ }
+ \$stmt->closeCursor();
+ }
+ }
+}
+";
+ }
+
+ protected function addMakeRoomForLeaf(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Update the tree to allow insertion of a leaf at the specified position
+ *
+ * @param int \$left left column value";
+ if ($useScope) {
+ $script .= "
+ * @param integer \$scope scope column value";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function makeRoomForLeaf(\$left" . ($useScope ? ", \$scope" : ""). ", PropelPDO \$con = null)
+{
+ // Update database nodes
+ $peerClassname::shiftRLValues(2, \$left, null" . ($useScope ? ", \$scope" : "") . ", \$con);
+
+ // Update all loaded nodes
+ $peerClassname::updateLoadedNodes(\$con);
+}
+";
+ }
+
+ protected function addFixLevels(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Update the tree to allow insertion of a leaf at the specified position
+ *";
+ if ($useScope) {
+ $script .= "
+ * @param integer \$scope scope column value";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function fixLevels(" . ($useScope ? "\$scope, " : ""). "PropelPDO \$con = null)
+{
+ \$c = new Criteria();";
+ if ($useScope) {
+ $script .= "
+ \$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+ \$c->addAscendingOrderByColumn($peerClassname::LEFT_COL);
+ \$stmt = $peerClassname::doSelectStmt(\$c, \$con);
+ ";
+ if (!$this->table->getChildrenColumn()) {
+ $script .= "
+ // set the class once to avoid overhead in the loop
+ \$cls = $peerClassname::getOMClass(false);";
+ }
+
+ $script .= "
+ \$level = null;
+ // iterate over the statement
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+
+ // hydrate object
+ \$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null === (\$obj = $peerClassname::getInstanceFromPool(\$key))) {";
+ if ($this->table->getChildrenColumn()) {
+ $script .= "
+ // class must be set each time from the record row
+ \$cls = $peerClassname::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+ " . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
+ \$obj->hydrate(\$row);
+ $peerClassname::addInstanceToPool(\$obj, \$key);";
+ } else {
+ $script .= "
+ " . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
+ \$obj->hydrate(\$row);
+ $peerClassname::addInstanceToPool(\$obj, \$key);";
+ }
+ $script .= "
+ }
+
+ // compute level
+ // Algorithm shamelessly stolen from sfPropelActAsNestedSetBehaviorPlugin
+ // Probably authored by Tristan Rivoallan
+ if (\$level === null) {
+ \$level = 0;
+ \$i = 0;
+ \$prev = array(\$obj->getRightValue());
+ } else {
+ while (\$obj->getRightValue() > \$prev[\$i]) {
+ \$i--;
+ }
+ \$level = ++\$i;
+ \$prev[\$i] = \$obj->getRightValue();
+ }
+
+ // update level in node if necessary
+ if (\$obj->getLevel() !== \$level) {
+ \$obj->setLevel(\$level);
+ \$obj->save(\$con);
+ }
+ }
+ \$stmt->closeCursor();
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorQueryBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorQueryBuilderModifier.php
new file mode 100644
index 000000000..31cd32d58
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/nestedset/NestedSetBehaviorQueryBuilderModifier.php
@@ -0,0 +1,357 @@
+behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumn($name)
+ {
+ return $this->behavior->getColumnForParameter($name);
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ public function queryMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+
+ // select filters
+ if ($this->behavior->useScope()) {
+ $this->addTreeRoots($script);
+ $this->addInTree($script);
+ }
+ $this->addDescendantsOf($script);
+ $this->addBranchOf($script);
+ $this->addChildrenOf($script);
+ $this->addSiblingsOf($script);
+ $this->addAncestorsOf($script);
+ $this->addRootsOf($script);
+ // select orders
+ $this->addOrderByBranch($script);
+ $this->addOrderByLevel($script);
+ // select termination methods
+ $this->addFindRoot($script);
+ $this->addFindTree($script);
+
+ return $script;
+ }
+
+ protected function addTreeRoots(&$script)
+ {
+ $script .= "
+/**
+ * Filter the query to restrict the result to root objects
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function treeRoots()
+{
+ return \$this->addUsingAlias({$this->peerClassname}::LEFT_COL, 1, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addInTree(&$script)
+ {
+ $script .= "
+/**
+ * Returns the objects in a certain tree, from the tree scope
+ *
+ * @param int \$scope Scope to determine which objects node to return
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function inTree(\$scope = null)
+{
+ return \$this->addUsingAlias({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addDescendantsOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to descendants of an object
+ *
+ * @param {$this->objectClassname} $objectName The object to use for descendant search
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function descendantsOf($objectName)
+{
+ return \$this";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree({$objectName}->getScopeValue())";
+ }
+ $script .= "
+ ->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_THAN)
+ ->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::LESS_THAN);
+}
+";
+ }
+
+ protected function addBranchOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to the branch of an object.
+ * Same as descendantsOf(), except that it includes the object passed as parameter in the result
+ *
+ * @param {$this->objectClassname} $objectName The object to use for branch search
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function branchOf($objectName)
+{
+ return \$this";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree({$objectName}->getScopeValue())";
+ }
+ $script .= "
+ ->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_EQUAL)
+ ->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::LESS_EQUAL);
+}
+";
+ }
+
+ protected function addChildrenOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to children of an object
+ *
+ * @param {$this->objectClassname} $objectName The object to use for child search
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function childrenOf($objectName)
+{
+ return \$this
+ ->descendantsOf($objectName)
+ ->addUsingAlias({$this->peerClassname}::LEVEL_COL, {$objectName}->getLevel() + 1, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addSiblingsOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to siblings of an object.
+ * The result does not include the object passed as parameter.
+ *
+ * @param {$this->objectClassname} $objectName The object to use for sibling search
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function siblingsOf($objectName, PropelPDO \$con = null)
+{
+ if ({$objectName}->isRoot()) {
+ return \$this->
+ add({$this->peerClassname}::LEVEL_COL, '1<>1', Criteria::CUSTOM);
+ } else {
+ return \$this
+ ->childrenOf({$objectName}->getParent(\$con))
+ ->prune($objectName);
+ }
+}
+";
+ }
+
+ protected function addAncestorsOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to ancestors of an object
+ *
+ * @param {$this->objectClassname} $objectName The object to use for ancestors search
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function ancestorsOf($objectName)
+{
+ return \$this";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree({$objectName}->getScopeValue())";
+ }
+ $script .= "
+ ->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_THAN)
+ ->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_THAN);
+}
+";
+ }
+
+ protected function addRootsOf(&$script)
+ {
+ $objectName = '$' . $this->table->getStudlyPhpName();
+ $script .= "
+/**
+ * Filter the query to restrict the result to roots of an object.
+ * Same as ancestorsOf(), except that it includes the object passed as parameter in the result
+ *
+ * @param {$this->objectClassname} $objectName The object to use for roots search
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function rootsOf($objectName)
+{
+ return \$this";
+ if ($this->behavior->useScope()) {
+ $script .= "
+ ->inTree({$objectName}->getScopeValue())";
+ }
+ $script .= "
+ ->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_EQUAL)
+ ->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_EQUAL);
+}
+";
+ }
+
+ protected function addOrderByBranch(&$script)
+ {
+ $script .= "
+/**
+ * Order the result by branch, i.e. natural tree order
+ *
+ * @param bool \$reverse if true, reverses the order
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function orderByBranch(\$reverse = false)
+{
+ if (\$reverse) {
+ return \$this
+ ->addDescendingOrderByColumn({$this->peerClassname}::LEFT_COL);
+ } else {
+ return \$this
+ ->addAscendingOrderByColumn({$this->peerClassname}::LEFT_COL);
+ }
+}
+";
+ }
+
+ protected function addOrderByLevel(&$script)
+ {
+ $script .= "
+/**
+ * Order the result by level, the closer to the root first
+ *
+ * @param bool \$reverse if true, reverses the order
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function orderByLevel(\$reverse = false)
+{
+ if (\$reverse) {
+ return \$this
+ ->addAscendingOrderByColumn({$this->peerClassname}::RIGHT_COL);
+ } else {
+ return \$this
+ ->addDescendingOrderByColumn({$this->peerClassname}::RIGHT_COL);
+ }
+}
+";
+ }
+
+ protected function addFindRoot(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Returns " . ($useScope ? 'a' : 'the') ." root node for the tree
+ *";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which root node to return";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return {$this->objectClassname} The tree root object
+ */
+public function findRoot(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
+{
+ return \$this
+ ->addUsingAlias({$this->peerClassname}::LEFT_COL, 1, Criteria::EQUAL)";
+ if ($useScope) {
+ $script .= "
+ ->inTree(\$scope)";
+ }
+ $script .= "
+ ->findOne(\$con);
+}
+";
+ }
+
+ protected function addFindTree(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Returns " . ($useScope ? 'a' : 'the') ." tree of objects
+ *";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which tree node to return";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return mixed the list of results, formatted by the current formatter
+ */
+public function findTree(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
+{
+ return \$this";
+ if ($useScope) {
+ $script .= "
+ ->inTree(\$scope)";
+ }
+ $script .= "
+ ->orderByBranch()
+ ->find(\$con);
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/query_cache/QueryCacheBehavior.php b/3rd_party/php/propel/generator/lib/behavior/query_cache/QueryCacheBehavior.php
new file mode 100644
index 000000000..44db968ae
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/query_cache/QueryCacheBehavior.php
@@ -0,0 +1,262 @@
+ 'apc',
+ 'lifetime' => 3600,
+ );
+
+ public function queryAttributes($builder)
+ {
+ $script = "protected \$queryKey = '';
+";
+ switch ($this->getParameter('backend')) {
+ case 'backend':
+ $script .= "protected static \$cacheBackend = array();
+ ";
+ break;
+ case 'apc':
+ break;
+ case 'custom':
+ default:
+ $script .= "protected static \$cacheBackend;
+ ";
+ break;
+ }
+
+ return $script;
+ }
+
+ public function queryMethods($builder)
+ {
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ $script = '';
+ $this->addSetQueryKey($script);
+ $this->addGetQueryKey($script);
+ $this->addCacheContains($script);
+ $this->addCacheFetch($script);
+ $this->addCacheStore($script);
+ $this->addGetSelectStatement($script);
+ $this->addGetCountStatement($script);
+
+ return $script;
+ }
+
+ protected function addSetQueryKey(&$script)
+ {
+ $script .= "
+public function setQueryKey(\$key)
+{
+ \$this->queryKey = \$key;
+ return \$this;
+}
+";
+ }
+
+ protected function addGetQueryKey(&$script)
+ {
+ $script .= "
+public function getQueryKey()
+{
+ return \$this->queryKey;
+}
+";
+ }
+
+ protected function addCacheContains(&$script)
+ {
+ $script .= "
+public function cacheContains(\$key)
+{";
+ switch ($this->getParameter('backend')) {
+ case 'apc':
+ $script .= "
+ return apc_fetch(\$key);";
+ break;
+ case 'array':
+ $script .= "
+ return isset(self::\$cacheBackend[\$key]);";
+ break;
+ case 'custom':
+ default:
+ $script .= "
+ throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
+ break;
+
+ }
+ $script .= "
+}
+";
+ }
+
+ protected function addCacheStore(&$script)
+ {
+ $script .= "
+public function cacheStore(\$key, \$value, \$lifetime = " .$this->getParameter('lifetime') . ")
+{";
+ switch ($this->getParameter('backend')) {
+ case 'apc':
+ $script .= "
+ apc_store(\$key, \$value, \$lifetime);";
+ break;
+ case 'array':
+ $script .= "
+ self::\$cacheBackend[\$key] = \$value;";
+ break;
+ case 'custom':
+ default:
+ $script .= "
+ throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
+ break;
+ }
+ $script .= "
+}
+";
+ }
+
+ protected function addCacheFetch(&$script)
+ {
+ $script .= "
+public function cacheFetch(\$key)
+{";
+ switch ($this->getParameter('backend')) {
+ case 'apc':
+ $script .= "
+ return apc_fetch(\$key);";
+ break;
+ case 'array':
+ $script .= "
+ return isset(self::\$cacheBackend[\$key]) ? self::\$cacheBackend[\$key] : null;";
+ break;
+ case 'custom':
+ default:
+ $script .= "
+ throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
+ break;
+ }
+ $script .= "
+}
+";
+ }
+
+ protected function addGetSelectStatement(&$script)
+ {
+ $script .= "
+protected function getSelectStatement(\$con = null)
+{
+ \$dbMap = Propel::getDatabaseMap(" . $this->peerClassname ."::DATABASE_NAME);
+ \$db = Propel::getDB(" . $this->peerClassname ."::DATABASE_NAME);
+ if (\$con === null) {
+ \$con = Propel::getConnection(" . $this->peerClassname ."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ if (!\$this->hasSelectClause()) {
+ \$this->addSelfSelectColumns();
+ }
+
+ \$con->beginTransaction();
+ try {
+ \$this->basePreSelect(\$con);
+ \$key = \$this->getQueryKey();
+ if (\$key && \$this->cacheContains(\$key)) {
+ \$params = \$this->getParams();
+ \$sql = \$this->cacheFetch(\$key);
+ } else {
+ \$params = array();
+ \$sql = BasePeer::createSelectSql(\$this, \$params);
+ if (\$key) {
+ \$this->cacheStore(\$key, \$sql);
+ }
+ }
+ \$stmt = \$con->prepare(\$sql);
+ BasePeer::populateStmtValues(\$stmt, \$params, \$dbMap, \$db);
+ \$stmt->execute();
+ \$con->commit();
+ } catch (PropelException \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+
+ return \$stmt;
+}
+";
+ }
+
+ protected function addGetCountStatement(&$script)
+ {
+ $script .= "
+protected function getCountStatement(\$con = null)
+{
+ \$dbMap = Propel::getDatabaseMap(\$this->getDbName());
+ \$db = Propel::getDB(\$this->getDbName());
+ if (\$con === null) {
+ \$con = Propel::getConnection(\$this->getDbName(), Propel::CONNECTION_READ);
+ }
+
+ \$con->beginTransaction();
+ try {
+ \$this->basePreSelect(\$con);
+ \$key = \$this->getQueryKey();
+ if (\$key && \$this->cacheContains(\$key)) {
+ \$params = \$this->getParams();
+ \$sql = \$this->cacheFetch(\$key);
+ } else {
+ if (!\$this->hasSelectClause() && !\$this->getPrimaryCriteria()) {
+ \$this->addSelfSelectColumns();
+ }
+ \$params = array();
+ \$needsComplexCount = \$this->getGroupByColumns()
+ || \$this->getOffset()
+ || \$this->getLimit()
+ || \$this->getHaving()
+ || in_array(Criteria::DISTINCT, \$this->getSelectModifiers());
+ if (\$needsComplexCount) {
+ if (BasePeer::needsSelectAliases(\$this)) {
+ if (\$this->getHaving()) {
+ throw new PropelException('Propel cannot create a COUNT query when using HAVING and duplicate column names in the SELECT part');
+ }
+ BasePeer::turnSelectColumnsToAliases(\$this);
+ }
+ \$selectSql = BasePeer::createSelectSql(\$this, \$params);
+ \$sql = 'SELECT COUNT(*) FROM (' . \$selectSql . ') propelmatch4cnt';
+ } else {
+ // Replace SELECT columns with COUNT(*)
+ \$this->clearSelectColumns()->addSelectColumn('COUNT(*)');
+ \$sql = BasePeer::createSelectSql(\$this, \$params);
+ }
+ if (\$key) {
+ \$this->cacheStore(\$key, \$sql);
+ }
+ }
+ \$stmt = \$con->prepare(\$sql);
+ BasePeer::populateStmtValues(\$stmt, \$params, \$dbMap, \$db);
+ \$stmt->execute();
+ \$con->commit();
+ } catch (PropelException \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+
+ return \$stmt;
+}
+";
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/sluggable/SluggableBehavior.php b/3rd_party/php/propel/generator/lib/behavior/sluggable/SluggableBehavior.php
new file mode 100644
index 000000000..d9909a335
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/sluggable/SluggableBehavior.php
@@ -0,0 +1,332 @@
+ 'slug',
+ 'slug_pattern' => '',
+ 'replace_pattern' => '/\W+/', // Tip: use '/[^\\pL\\d]+/u' instead if you're in PHP5.3
+ 'replacement' => '-',
+ 'separator' => '-',
+ 'permanent' => 'false'
+ );
+
+ /**
+ * Add the slug_column to the current table
+ */
+ public function modifyTable()
+ {
+ if(!$this->getTable()->containsColumn($this->getParameter('slug_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('slug_column'),
+ 'type' => 'VARCHAR',
+ 'size' => 255
+ ));
+ // add a unique to column
+ $unique = new Unique($this->getColumnForParameter('slug_column'));
+ $unique->setName($this->getTable()->getName() . '_slug');
+ $unique->addColumn($this->getTable()->getColumn($this->getParameter('slug_column')));
+ $this->getTable()->addUnique($unique);
+ }
+ }
+
+ /**
+ * Get the getter of the column of the behavior
+ *
+ * @return string The related getter, e.g. 'getSlug'
+ */
+ protected function getColumnGetter()
+ {
+ return 'get' . $this->getColumnForParameter('slug_column')->getPhpName();
+ }
+
+ /**
+ * Get the setter of the column of the behavior
+ *
+ * @return string The related setter, e.g. 'setSlug'
+ */
+ protected function getColumnSetter()
+ {
+ return 'set' . $this->getColumnForParameter('slug_column')->getPhpName();
+ }
+
+ /**
+ * Add code in ObjectBuilder::preSave
+ *
+ * @return string The code to put at the hook
+ */
+ public function preSave($builder)
+ {
+ $const = $builder->getColumnConstant($this->getColumnForParameter('slug_column'), $this->getTable()->getPhpName() . 'Peer');
+ $script = "
+if (\$this->isColumnModified($const) && \$this->{$this->getColumnGetter()}()) {
+ \$this->{$this->getColumnSetter()}(\$this->makeSlugUnique(\$this->{$this->getColumnGetter()}()));";
+ if ($this->getParameter('permanent') == 'true') {
+ $script .= "
+} elseif (!\$this->{$this->getColumnGetter()}()) {
+ \$this->{$this->getColumnSetter()}(\$this->createSlug());
+}";
+ } else {
+ $script .= "
+} else {
+ \$this->{$this->getColumnSetter()}(\$this->createSlug());
+}";
+ }
+
+ return $script;
+ }
+
+ public function objectMethods($builder)
+ {
+ $this->builder = $builder;
+ $script = '';
+ if ($this->getParameter('slug_column') != 'slug') {
+ $this->addSlugSetter($script);
+ $this->addSlugGetter($script);
+ }
+ $this->addCreateSlug($script);
+ $this->addCreateRawSlug($script);
+ $this->addCleanupSlugPart($script);
+ $this->addLimitSlugSize($script);
+ $this->addMakeSlugUnique($script);
+
+ return $script;
+ }
+
+ protected function addSlugSetter(&$script)
+ {
+ $script .= "
+/**
+ * Wrap the setter for slug value
+ *
+ * @param string
+ * @return " . $this->getTable()->getPhpName() . "
+ */
+public function setSlug(\$v)
+{
+ return \$this->" . $this->getColumnSetter() . "(\$v);
+}
+";
+ }
+
+ protected function addSlugGetter(&$script)
+ {
+ $script .= "
+/**
+ * Wrap the getter for slug value
+ *
+ * @return string
+ */
+public function getSlug()
+{
+ return \$this->" . $this->getColumnGetter() . "();
+}
+";
+ }
+
+ protected function addCreateSlug(&$script)
+ {
+ $script .= "
+/**
+ * Create a unique slug based on the object
+ *
+ * @return string The object slug
+ */
+protected function createSlug()
+{
+ \$slug = \$this->createRawSlug();
+ \$slug = \$this->limitSlugSize(\$slug);
+ \$slug = \$this->makeSlugUnique(\$slug);
+
+ return \$slug;
+}
+";
+ }
+
+ protected function addCreateRawSlug(&$script)
+ {
+ $pattern = $this->getParameter('slug_pattern');
+ $script .= "
+/**
+ * Create the slug from the appropriate columns
+ *
+ * @return string
+ */
+protected function createRawSlug()
+{
+ ";
+ if ($pattern) {
+ $script .= "return '" . str_replace(array('{', '}'), array('\' . $this->cleanupSlugPart($this->get', '()) . \''), $pattern). "';";
+ } else {
+ $script .= "return \$this->cleanupSlugPart(\$this->__toString());";
+ }
+ $script .= "
+}
+";
+ return $script;
+ }
+
+ public function addCleanupSlugPart(&$script)
+ {
+ $script .= "
+/**
+ * Cleanup a string to make a slug of it
+ * Removes special characters, replaces blanks with a separator, and trim it
+ *
+ * @param string \$text the text to slugify
+ * @param string \$separator the separator used by slug
+ * @return string the slugified text
+ */
+protected static function cleanupSlugPart(\$slug, \$replacement = '" . $this->getParameter('replacement') . "')
+{
+ // transliterate
+ if (function_exists('iconv')) {
+ \$slug = iconv('utf-8', 'us-ascii//TRANSLIT', \$slug);
+ }
+
+ // lowercase
+ if (function_exists('mb_strtolower')) {
+ \$slug = mb_strtolower(\$slug);
+ } else {
+ \$slug = strtolower(\$slug);
+ }
+
+ // remove accents resulting from OSX's iconv
+ \$slug = str_replace(array('\'', '`', '^'), '', \$slug);
+
+ // replace non letter or digits with separator
+ \$slug = preg_replace('" . $this->getParameter('replace_pattern') . "', \$replacement, \$slug);
+
+ // trim
+ \$slug = trim(\$slug, \$replacement);
+
+ if (empty(\$slug)) {
+ return 'n-a';
+ }
+
+ return \$slug;
+}
+";
+ }
+
+ public function addLimitSlugSize(&$script)
+ {
+ $size = $this->getColumnForParameter('slug_column')->getSize();
+ $script .= "
+
+/**
+ * Make sure the slug is short enough to accomodate the column size
+ *
+ * @param string \$slug the slug to check
+ *
+ * @return string the truncated slug
+ */
+protected static function limitSlugSize(\$slug, \$incrementReservedSpace = 3)
+{
+ // check length, as suffix could put it over maximum
+ if (strlen(\$slug) > ($size - \$incrementReservedSpace)) {
+ \$slug = substr(\$slug, 0, $size - \$incrementReservedSpace);
+ }
+ return \$slug;
+}
+";
+ }
+
+ public function addMakeSlugUnique(&$script)
+ {
+ $script .= "
+
+/**
+ * Get the slug, ensuring its uniqueness
+ *
+ * @param string \$slug the slug to check
+ * @param string \$separator the separator used by slug
+ * @return string the unique slug
+ */
+protected function makeSlugUnique(\$slug, \$separator = '" . $this->getParameter('separator') ."', \$increment = 0)
+{
+ \$slug2 = empty(\$increment) ? \$slug : \$slug . \$separator . \$increment;
+ \$slugAlreadyExists = " . $this->builder->getStubQueryBuilder()->getClassname() . "::create()
+ ->filterBySlug(\$slug2)
+ ->prune(\$this)";
+ // watch out: some of the columns may be hidden by the soft_delete behavior
+ if ($this->table->hasBehavior('soft_delete')) {
+ $script .= "
+ ->includeDeleted()";
+ }
+ $script .= "
+ ->count();
+ if (\$slugAlreadyExists) {
+ return \$this->makeSlugUnique(\$slug, \$separator, ++\$increment);
+ } else {
+ return \$slug2;
+ }
+}
+";
+ }
+
+ public function queryMethods($builder)
+ {
+ $this->builder = $builder;
+ $script = '';
+ if ($this->getParameter('slug_column') != 'slug') {
+ $this->addFilterBySlug($script);
+ }
+ $this->addFindOneBySlug($script);
+
+ return $script;
+ }
+
+ protected function addFilterBySlug(&$script)
+ {
+ $script .= "
+/**
+ * Filter the query on the slug column
+ *
+ * @param string \$slug The value to use as filter.
+ *
+ * @return " . $this->builder->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
+ */
+public function filterBySlug(\$slug)
+{
+ return \$this->addUsingAlias(" . $this->builder->getColumnConstant($this->getColumnForParameter('slug_column')) . ", \$slug, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addFindOneBySlug(&$script)
+ {
+ $script .= "
+/**
+ * Find one object based on its slug
+ *
+ * @param string \$slug The value to use as filter.
+ * @param PropelPDO \$con The optional connection object
+ *
+ * @return " . $this->builder->getStubObjectBuilder()->getClassname() . " the result, formatted by the current formatter
+ */
+public function findOneBySlug(\$slug, \$con = null)
+{
+ return \$this->filterBySlug(\$slug)->findOne(\$con);
+}
+";
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehavior.php b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehavior.php
new file mode 100644
index 000000000..3fe78cb9c
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehavior.php
@@ -0,0 +1,83 @@
+ 'sortable_rank',
+ 'use_scope' => 'false',
+ 'scope_column' => 'sortable_scope',
+ );
+
+ protected $objectBuilderModifier, $queryBuilderModifier, $peerBuilderModifier;
+
+ /**
+ * Add the rank_column to the current table
+ */
+ public function modifyTable()
+ {
+ if (!$this->getTable()->containsColumn($this->getParameter('rank_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('rank_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ if ($this->getParameter('use_scope') == 'true' &&
+ !$this->getTable()->containsColumn($this->getParameter('scope_column'))) {
+ $this->getTable()->addColumn(array(
+ 'name' => $this->getParameter('scope_column'),
+ 'type' => 'INTEGER'
+ ));
+ }
+ }
+
+ public function getObjectBuilderModifier()
+ {
+ if (is_null($this->objectBuilderModifier)) {
+ $this->objectBuilderModifier = new SortableBehaviorObjectBuilderModifier($this);
+ }
+ return $this->objectBuilderModifier;
+ }
+
+ public function getQueryBuilderModifier()
+ {
+ if (is_null($this->queryBuilderModifier)) {
+ $this->queryBuilderModifier = new SortableBehaviorQueryBuilderModifier($this);
+ }
+ return $this->queryBuilderModifier;
+ }
+
+ public function getPeerBuilderModifier()
+ {
+ if (is_null($this->peerBuilderModifier)) {
+ $this->peerBuilderModifier = new SortableBehaviorPeerBuilderModifier($this);
+ }
+ return $this->peerBuilderModifier;
+ }
+
+ public function useScope()
+ {
+ return $this->getParameter('use_scope') == 'true';
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorObjectBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorObjectBuilderModifier.php
new file mode 100644
index 000000000..a59d28b0e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorObjectBuilderModifier.php
@@ -0,0 +1,636 @@
+
+ * @package propel.generator.behavior.sortable
+ */
+class SortableBehaviorObjectBuilderModifier
+{
+ protected $behavior, $table, $builder, $objectClassname, $peerClassname;
+
+ public function __construct($behavior)
+ {
+ $this->behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumnAttribute($name)
+ {
+ return strtolower($this->behavior->getColumnForParameter($name)->getName());
+ }
+
+ protected function getColumnPhpName($name)
+ {
+ return $this->behavior->getColumnForParameter($name)->getPhpName();
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ /**
+ * Get the getter of the column of the behavior
+ *
+ * @return string The related getter, e.g. 'getRank'
+ */
+ protected function getColumnGetter($columnName = 'rank_column')
+ {
+ return 'get' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
+ }
+
+ /**
+ * Get the setter of the column of the behavior
+ *
+ * @return string The related setter, e.g. 'setRank'
+ */
+ protected function getColumnSetter($columnName = 'rank_column')
+ {
+ return 'set' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
+ }
+
+ public function preSave($builder)
+ {
+ return "\$this->processSortableQueries(\$con);";
+ }
+
+ public function preInsert($builder)
+ {
+ $useScope = $this->behavior->useScope();
+ $this->setBuilder($builder);
+ return "if (!\$this->isColumnModified({$this->peerClassname}::RANK_COL)) {
+ \$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
+}
+";
+ }
+
+ public function preDelete($builder)
+ {
+ $useScope = $this->behavior->useScope();
+ $this->setBuilder($builder);
+ return "
+{$this->peerClassname}::shiftRank(-1, \$this->{$this->getColumnGetter()}() + 1, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
+{$this->peerClassname}::clearInstancePool();
+";
+ }
+
+ public function objectAttributes($builder)
+ {
+ return "
+/**
+ * Queries to be executed in the save transaction
+ * @var array
+ */
+protected \$sortableQueries = array();
+";
+ }
+
+ public function objectMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+ if ($this->getParameter('rank_column') != 'rank') {
+ $this->addRankAccessors($script);
+ }
+ if ($this->behavior->useScope() &&
+ $this->getParameter('scope_column') != 'scope_value') {
+ $this->addScopeAccessors($script);
+ }
+ $this->addIsFirst($script);
+ $this->addIsLast($script);
+ $this->addGetNext($script);
+ $this->addGetPrevious($script);
+ $this->addInsertAtRank($script);
+ $this->addInsertAtBottom($script);
+ $this->addInsertAtTop($script);
+ $this->addMoveToRank($script);
+ $this->addSwapWith($script);
+ $this->addMoveUp($script);
+ $this->addMoveDown($script);
+ $this->addMoveToTop($script);
+ $this->addMoveToBottom($script);
+ $this->addRemoveFromList($script);
+ $this->addProcessSortableQueries($script);
+
+ return $script;
+ }
+
+ /**
+ * Get the wraps for getter/setter, if the rank column has not the default name
+ *
+ * @return string
+ */
+ protected function addRankAccessors(&$script)
+ {
+ $script .= "
+/**
+ * Wrap the getter for rank value
+ *
+ * @return int
+ */
+public function getRank()
+{
+ return \$this->{$this->getColumnAttribute('rank_column')};
+}
+
+/**
+ * Wrap the setter for rank value
+ *
+ * @param int
+ * @return {$this->objectClassname}
+ */
+public function setRank(\$v)
+{
+ return \$this->{$this->getColumnSetter()}(\$v);
+}
+";
+ }
+
+ /**
+ * Get the wraps for getter/setter, if the scope column has not the default name
+ *
+ * @return string
+ */
+ protected function addScopeAccessors(&$script)
+ {
+ $script .= "
+/**
+ * Wrap the getter for scope value
+ *
+ * @return int
+ */
+public function getScopeValue()
+{
+ return \$this->{$this->getColumnAttribute('scope_column')};
+}
+
+/**
+ * Wrap the setter for scope value
+ *
+ * @param int
+ * @return {$this->objectClassname}
+ */
+public function setScopeValue(\$v)
+{
+ return \$this->{$this->getColumnSetter('scope_column')}(\$v);
+}
+";
+ }
+
+ protected function addIsFirst(&$script)
+ {
+ $script .= "
+/**
+ * Check if the object is first in the list, i.e. if it has 1 for rank
+ *
+ * @return boolean
+ */
+public function isFirst()
+{
+ return \$this->{$this->getColumnGetter()}() == 1;
+}
+";
+ }
+
+ protected function addIsLast(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Check if the object is last in the list, i.e. if its rank is the highest rank
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return boolean
+ */
+public function isLast(PropelPDO \$con = null)
+{
+ return \$this->{$this->getColumnGetter()}() == {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
+}
+";
+ }
+
+ protected function addGetNext(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Get the next item in the list, i.e. the one for which rank is immediately higher
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname}
+ */
+public function getNext(PropelPDO \$con = null)
+{";
+ if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
+ $script .= "
+ return {$this->queryClassname}::create()
+ ->filterByRank(\$this->{$this->getColumnGetter()}() + 1)
+ ->inList(\$this->{$this->getColumnGetter('scope_column')}())
+ ->findOne(\$con);";
+ } else {
+ $script .= "
+ return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() + 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
+ }
+
+ $script .= "
+}
+";
+ }
+
+ protected function addGetPrevious(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Get the previous item in the list, i.e. the one for which rank is immediately lower
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname}
+ */
+public function getPrevious(PropelPDO \$con = null)
+{";
+ if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
+ $script .= "
+ return {$this->queryClassname}::create()
+ ->filterByRank(\$this->{$this->getColumnGetter()}() - 1)
+ ->inList(\$this->{$this->getColumnGetter('scope_column')}())
+ ->findOne(\$con);";
+ } else {
+ $script .= "
+ return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() - 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
+ }
+ $script .= "
+}
+";
+ }
+
+ protected function addInsertAtRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Insert at specified rank
+ * The modifications are not persisted until the object is saved.
+ *
+ * @param integer \$rank rank value
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ *
+ * @throws PropelException
+ */
+public function insertAtRank(\$rank, PropelPDO \$con = null)
+{";
+ if ($useScope) {
+ $script .= "
+ if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
+ throw new PropelException('The scope must be defined before inserting an object in a suite');
+ }";
+ }
+ $script .= "
+ \$maxRank = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
+ if (\$rank < 1 || \$rank > \$maxRank + 1) {
+ throw new PropelException('Invalid rank ' . \$rank);
+ }
+ // move the object in the list, at the given rank
+ \$this->{$this->getColumnSetter()}(\$rank);
+ if (\$rank != \$maxRank + 1) {
+ // Keep the list modification query for the save() transaction
+ \$this->sortableQueries []= array(
+ 'callable' => array('$peerClassname', 'shiftRank'),
+ 'arguments' => array(1, \$rank, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
+ );
+ }
+
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAtBottom(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Insert in the last rank
+ * The modifications are not persisted until the object is saved.
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ *
+ * @throws PropelException
+ */
+public function insertAtBottom(PropelPDO \$con = null)
+{";
+ if ($useScope) {
+ $script .= "
+ if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
+ throw new PropelException('The scope must be defined before inserting an object in a suite');
+ }";
+ }
+ $script .= "
+ \$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
+
+ return \$this;
+}
+";
+ }
+
+ protected function addInsertAtTop(&$script)
+ {
+ $script .= "
+/**
+ * Insert in the first rank
+ * The modifications are not persisted until the object is saved.
+ *
+ * @return {$this->objectClassname} the current object
+ */
+public function insertAtTop()
+{
+ return \$this->insertAtRank(1);
+}
+";
+ }
+
+ protected function addMoveToRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Move the object to a new rank, and shifts the rank
+ * Of the objects inbetween the old and new rank accordingly
+ *
+ * @param integer \$newRank rank value
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ *
+ * @throws PropelException
+ */
+public function moveToRank(\$newRank, PropelPDO \$con = null)
+{
+ if (\$this->isNew()) {
+ throw new PropelException('New objects cannot be moved. Please use insertAtRank() instead');
+ }
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+ }
+ if (\$newRank < 1 || \$newRank > {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con)) {
+ throw new PropelException('Invalid rank ' . \$newRank);
+ }
+
+ \$oldRank = \$this->{$this->getColumnGetter()}();
+ if (\$oldRank == \$newRank) {
+ return \$this;
+ }
+
+ \$con->beginTransaction();
+ try {
+ // shift the objects between the old and the new rank
+ \$delta = (\$oldRank < \$newRank) ? -1 : 1;
+ $peerClassname::shiftRank(\$delta, min(\$oldRank, \$newRank), max(\$oldRank, \$newRank), " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
+
+ // move the object to its new rank
+ \$this->{$this->getColumnSetter()}(\$newRank);
+ \$this->save(\$con);
+
+ \$con->commit();
+ return \$this;
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addSwapWith(&$script)
+ {
+ $script .= "
+/**
+ * Exchange the rank of the object with the one passed as argument, and saves both objects
+ *
+ * @param {$this->objectClassname} \$object
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ *
+ * @throws Exception if the database cannot execute the two updates
+ */
+public function swapWith(\$object, PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ \$con->beginTransaction();
+ try {
+ \$oldRank = \$this->{$this->getColumnGetter()}();
+ \$newRank = \$object->{$this->getColumnGetter()}();
+ \$this->{$this->getColumnSetter()}(\$newRank);
+ \$this->save(\$con);
+ \$object->{$this->getColumnSetter()}(\$oldRank);
+ \$object->save(\$con);
+ \$con->commit();
+
+ return \$this;
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addMoveUp(&$script)
+ {
+ $script .= "
+/**
+ * Move the object higher in the list, i.e. exchanges its rank with the one of the previous object
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ */
+public function moveUp(PropelPDO \$con = null)
+{
+ if (\$this->isFirst()) {
+ return \$this;
+ }
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ \$con->beginTransaction();
+ try {
+ \$prev = \$this->getPrevious(\$con);
+ \$this->swapWith(\$prev, \$con);
+ \$con->commit();
+
+ return \$this;
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addMoveDown(&$script)
+ {
+ $script .= "
+/**
+ * Move the object higher in the list, i.e. exchanges its rank with the one of the next object
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ */
+public function moveDown(PropelPDO \$con = null)
+{
+ if (\$this->isLast(\$con)) {
+ return \$this;
+ }
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ \$con->beginTransaction();
+ try {
+ \$next = \$this->getNext(\$con);
+ \$this->swapWith(\$next, \$con);
+ \$con->commit();
+
+ return \$this;
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addMoveToTop(&$script)
+ {
+ $script .= "
+/**
+ * Move the object to the top of the list
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname} the current object
+ */
+public function moveToTop(PropelPDO \$con = null)
+{
+ if (\$this->isFirst()) {
+ return \$this;
+ }
+ return \$this->moveToRank(1, \$con);
+}
+";
+ }
+
+ protected function addMoveToBottom(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Move the object to the bottom of the list
+ *
+ * @param PropelPDO \$con optional connection
+ *
+ * @return integer the old object's rank
+ */
+public function moveToBottom(PropelPDO \$con = null)
+{
+ if (\$this->isLast(\$con)) {
+ return false;
+ }
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ \$con->beginTransaction();
+ try {
+ \$bottom = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
+ \$res = \$this->moveToRank(\$bottom, \$con);
+ \$con->commit();
+
+ return \$res;
+ } catch (Exception \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addRemoveFromList(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Removes the current object from the list.
+ * The modifications are not persisted until the object is saved.
+ *
+ * @return {$this->objectClassname} the current object
+ */
+public function removeFromList()
+{
+ // Keep the list modification query for the save() transaction
+ \$this->sortableQueries []= array(
+ 'callable' => array('$peerClassname', 'shiftRank'),
+ 'arguments' => array(-1, \$this->{$this->getColumnGetter()}() + 1, null" . ($useScope ? ", \$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
+ );
+ // remove the object from the list
+ \$this->{$this->getColumnSetter('rank_column')}(null);";
+ if ($useScope) {
+ $script .= "
+ \$this->{$this->getColumnSetter('scope_column')}(null);";
+ }
+ $script .= "
+
+ return \$this;
+}
+";
+ }
+
+ protected function addProcessSortableQueries(&$script)
+ {
+ $script .= "
+/**
+ * Execute queries that were saved to be run inside the save transaction
+ */
+protected function processSortableQueries(\$con)
+{
+ foreach (\$this->sortableQueries as \$query) {
+ \$query['arguments'][]= \$con;
+ call_user_func_array(\$query['callable'], \$query['arguments']);
+ }
+ \$this->sortableQueries = array();
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorPeerBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorPeerBuilderModifier.php
new file mode 100644
index 000000000..bb2b687ff
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorPeerBuilderModifier.php
@@ -0,0 +1,367 @@
+
+ * @package propel.generator.behavior.sortable
+ */
+class SortableBehaviorPeerBuilderModifier
+{
+ protected $behavior, $table, $builder, $objectClassname, $peerClassname;
+
+ public function __construct($behavior)
+ {
+ $this->behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumnAttribute($name)
+ {
+ return strtolower($this->behavior->getColumnForParameter($name)->getName());
+ }
+
+ protected function getColumnConstant($name)
+ {
+ return strtoupper($this->behavior->getColumnForParameter($name)->getName());
+ }
+
+ protected function getColumnPhpName($name)
+ {
+ return $this->behavior->getColumnForParameter($name)->getPhpName();
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ public function staticAttributes($builder)
+ {
+ $tableName = $this->table->getName();
+ $script = "
+/**
+ * rank column
+ */
+const RANK_COL = '" . $tableName . '.' . $this->getColumnConstant('rank_column') . "';
+";
+
+ if ($this->behavior->useScope()) {
+ $script .= "
+/**
+ * Scope column for the set
+ */
+const SCOPE_COL = '" . $tableName . '.' . $this->getColumnConstant('scope_column') . "';
+";
+ }
+
+ return $script;
+ }
+
+ /**
+ * Static methods
+ *
+ * @return string
+ */
+ public function staticMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+
+ $this->addGetMaxRank($script);
+ $this->addRetrieveByRank($script);
+ $this->addReorder($script);
+ $this->addDoSelectOrderByRank($script);
+ if ($this->behavior->useScope()) {
+ $this->addRetrieveList($script);
+ $this->addCountList($script);
+ $this->addDeleteList($script);
+ }
+ $this->addShiftRank($script);
+
+ return $script;
+ }
+
+ protected function addGetMaxRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Get the highest rank
+ * ";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which suite to consider";
+ }
+ $script .= "
+ * @param PropelPDO optional connection
+ *
+ * @return integer highest position
+ */
+public static function getMaxRank(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ // shift the objects with a position lower than the one of object
+ \$c = new Criteria();
+ \$c->addSelectColumn('MAX(' . {$this->peerClassname}::RANK_COL . ')');";
+ if ($useScope) {
+ $script .= "
+ \$c->add({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+ \$stmt = {$this->peerClassname}::doSelectStmt(\$c, \$con);
+
+ return \$stmt->fetchColumn();
+}
+";
+ }
+
+ protected function addRetrieveByRank(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Get an item from the list based on its rank
+ *
+ * @param integer \$rank rank";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which suite to consider";
+ }
+ $script .= "
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname}
+ */
+public static function retrieveByRank(\$rank, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+ }
+
+ \$c = new Criteria;
+ \$c->add($peerClassname::RANK_COL, \$rank);";
+ if($useScope) {
+ $script .= "
+ \$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ return $peerClassname::doSelectOne(\$c, \$con);
+}
+";
+ }
+
+ protected function addReorder(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $columnGetter = 'get' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
+ $columnSetter = 'set' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
+ $script .= "
+/**
+ * Reorder a set of sortable objects based on a list of id/position
+ * Beware that there is no check made on the positions passed
+ * So incoherent positions will result in an incoherent list
+ *
+ * @param array \$order id => rank pairs
+ * @param PropelPDO \$con optional connection
+ *
+ * @return boolean true if the reordering took place, false if a database problem prevented it
+ */
+public static function reorder(array \$order, PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+ }
+
+ \$con->beginTransaction();
+ try {
+ \$ids = array_keys(\$order);
+ \$objects = $peerClassname::retrieveByPKs(\$ids);
+ foreach (\$objects as \$object) {
+ \$pk = \$object->getPrimaryKey();
+ if (\$object->$columnGetter() != \$order[\$pk]) {
+ \$object->$columnSetter(\$order[\$pk]);
+ \$object->save(\$con);
+ }
+ }
+ \$con->commit();
+
+ return true;
+ } catch (PropelException \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+ protected function addDoSelectOrderByRank(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Return an array of sortable objects ordered by position
+ *
+ * @param Criteria \$criteria optional criteria object
+ * @param string \$order sorting order, to be chosen between Criteria::ASC (default) and Criteria::DESC
+ * @param PropelPDO \$con optional connection
+ *
+ * @return array list of sortable objects
+ */
+public static function doSelectOrderByRank(Criteria \$criteria = null, \$order = Criteria::ASC, PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+ }
+
+ if (\$criteria === null) {
+ \$criteria = new Criteria();
+ } elseif (\$criteria instanceof Criteria) {
+ \$criteria = clone \$criteria;
+ }
+
+ \$criteria->clearOrderByColumns();
+
+ if (\$order == Criteria::ASC) {
+ \$criteria->addAscendingOrderByColumn($peerClassname::RANK_COL);
+ } else {
+ \$criteria->addDescendingOrderByColumn($peerClassname::RANK_COL);
+ }
+
+ return $peerClassname::doSelect(\$criteria, \$con);
+}
+";
+ }
+
+ protected function addRetrieveList(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Return an array of sortable objects in the given scope ordered by position
+ *
+ * @param int \$scope the scope of the list
+ * @param string \$order sorting order, to be chosen between Criteria::ASC (default) and Criteria::DESC
+ * @param PropelPDO \$con optional connection
+ *
+ * @return array list of sortable objects
+ */
+public static function retrieveList(\$scope, \$order = Criteria::ASC, PropelPDO \$con = null)
+{
+ \$c = new Criteria();
+ \$c->add($peerClassname::SCOPE_COL, \$scope);
+
+ return $peerClassname::doSelectOrderByRank(\$c, \$order, \$con);
+}
+";
+ }
+
+ protected function addCountList(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Return the number of sortable objects in the given scope
+ *
+ * @param int \$scope the scope of the list
+ * @param PropelPDO \$con optional connection
+ *
+ * @return array list of sortable objects
+ */
+public static function countList(\$scope, PropelPDO \$con = null)
+{
+ \$c = new Criteria();
+ \$c->add($peerClassname::SCOPE_COL, \$scope);
+
+ return $peerClassname::doCount(\$c, \$con);
+}
+";
+ }
+
+ protected function addDeleteList(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Deletes the sortable objects in the given scope
+ *
+ * @param int \$scope the scope of the list
+ * @param PropelPDO \$con optional connection
+ *
+ * @return int number of deleted objects
+ */
+public static function deleteList(\$scope, PropelPDO \$con = null)
+{
+ \$c = new Criteria();
+ \$c->add($peerClassname::SCOPE_COL, \$scope);
+
+ return $peerClassname::doDelete(\$c, \$con);
+}
+";
+ }
+ protected function addShiftRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Adds \$delta to all Rank values that are >= \$first and <= \$last.
+ * '\$delta' can also be negative.
+ *
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param int \$first First node to be shifted
+ * @param int \$last Last node to be shifted";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to use for the shift";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ */
+public static function shiftRank(\$delta, \$first, \$last = null, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion($peerClassname::RANK_COL, \$first, Criteria::GREATER_EQUAL);
+ if (null !== \$last) {
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::RANK_COL, \$last, Criteria::LESS_EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);";
+ if ($useScope) {
+ $script .= "
+ \$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add($peerClassname::RANK_COL, array('raw' => $peerClassname::RANK_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
+
+ {$this->builder->getPeerBuilder()->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+ $peerClassname::clearInstancePool();
+}
+";
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorQueryBuilderModifier.php b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorQueryBuilderModifier.php
new file mode 100755
index 000000000..5e469ca32
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/behavior/sortable/SortableBehaviorQueryBuilderModifier.php
@@ -0,0 +1,283 @@
+behavior = $behavior;
+ $this->table = $behavior->getTable();
+ }
+
+ protected function getParameter($key)
+ {
+ return $this->behavior->getParameter($key);
+ }
+
+ protected function getColumn($name)
+ {
+ return $this->behavior->getColumnForParameter($name);
+ }
+
+ protected function setBuilder($builder)
+ {
+ $this->builder = $builder;
+ $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
+ $this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
+ $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
+ }
+
+ public function queryMethods($builder)
+ {
+ $this->setBuilder($builder);
+ $script = '';
+
+ // select filters
+ if ($this->behavior->useScope()) {
+ $this->addInList($script);
+ }
+ if ($this->getParameter('rank_column') != 'rank') {
+ $this->addFilterByRank($script);
+ $this->addOrderByRank($script);
+ }
+
+ // select termination methods
+ if ($this->getParameter('rank_column') != 'rank') {
+ $this->addFindOneByRank($script);
+ }
+ $this->addFindList($script);
+
+ // utilities
+ $this->addGetMaxRank($script);
+ $this->addReorder($script);
+
+ return $script;
+ }
+
+ protected function addInList(&$script)
+ {
+ $script .= "
+/**
+ * Returns the objects in a certain list, from the list scope
+ *
+ * @param int \$scope Scope to determine which objects node to return
+ *
+ * @return {$this->queryClassname} The current query, for fuid interface
+ */
+public function inList(\$scope = null)
+{
+ return \$this->addUsingAlias({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addFilterByRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Filter the query based on a rank in the list
+ *
+ * @param integer \$rank rank";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which suite to consider";
+ }
+ $script .= "
+ *
+ * @return " . $this->queryClassname . " The current query, for fluid interface
+ */
+public function filterByRank(\$rank" . ($useScope ? ", \$scope = null" : "") . ")
+{
+ return \$this";
+ if ($useScope) {
+ $script .= "
+ ->inList(\$scope)";
+ }
+ $script .= "
+ ->addUsingAlias($peerClassname::RANK_COL, \$rank, Criteria::EQUAL);
+}
+";
+ }
+
+ protected function addOrderByRank(&$script)
+ {
+ $script .= "
+/**
+ * Order the query based on the rank in the list.
+ * Using the default \$order, returns the item with the lowest rank first
+ *
+ * @param string \$order either Criteria::ASC (default) or Criteria::DESC
+ *
+ * @return " . $this->queryClassname . " The current query, for fluid interface
+ */
+public function orderByRank(\$order = Criteria::ASC)
+{
+ \$order = strtoupper(\$order);
+ switch (\$order) {
+ case Criteria::ASC:
+ return \$this->addAscendingOrderByColumn(\$this->getAliasedColName(" . $this->peerClassname . "::RANK_COL));
+ break;
+ case Criteria::DESC:
+ return \$this->addDescendingOrderByColumn(\$this->getAliasedColName(" . $this->peerClassname . "::RANK_COL));
+ break;
+ default:
+ throw new PropelException('" . $this->queryClassname . "::orderBy() only accepts \"asc\" or \"desc\" as argument');
+ }
+}
+";
+ }
+
+ protected function addFindOneByRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $peerClassname = $this->peerClassname;
+ $script .= "
+/**
+ * Get an item from the list based on its rank
+ *
+ * @param integer \$rank rank";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which suite to consider";
+ }
+ $script .= "
+ * @param PropelPDO \$con optional connection
+ *
+ * @return {$this->objectClassname}
+ */
+public function findOneByRank(\$rank, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ return \$this
+ ->filterByRank(\$rank" . ($useScope ? ", \$scope" : "") . ")
+ ->findOne(\$con);
+}
+";
+ }
+
+ protected function addFindList(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Returns " . ($useScope ? 'a' : 'the') ." list of objects
+ *";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which list to return";
+ }
+ $script .= "
+ * @param PropelPDO \$con Connection to use.
+ *
+ * @return mixed the list of results, formatted by the current formatter
+ */
+public function findList(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
+{
+ return \$this";
+ if ($useScope) {
+ $script .= "
+ ->inList(\$scope)";
+ }
+ $script .= "
+ ->orderByRank()
+ ->find(\$con);
+}
+";
+ }
+
+ protected function addGetMaxRank(&$script)
+ {
+ $useScope = $this->behavior->useScope();
+ $script .= "
+/**
+ * Get the highest rank
+ * ";
+ if($useScope) {
+ $script .= "
+ * @param int \$scope Scope to determine which suite to consider";
+ }
+ $script .= "
+ * @param PropelPDO optional connection
+ *
+ * @return integer highest position
+ */
+public function getMaxRank(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
+ }
+ // shift the objects with a position lower than the one of object
+ \$this->addSelectColumn('MAX(' . {$this->peerClassname}::RANK_COL . ')');";
+ if ($useScope) {
+ $script .= "
+ \$this->add({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);";
+ }
+ $script .= "
+ \$stmt = \$this->getSelectStatement(\$con);
+
+ return \$stmt->fetchColumn();
+}
+";
+ }
+
+ protected function addReorder(&$script)
+ {
+ $peerClassname = $this->peerClassname;
+ $columnGetter = 'get' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
+ $columnSetter = 'set' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
+ $script .= "
+/**
+ * Reorder a set of sortable objects based on a list of id/position
+ * Beware that there is no check made on the positions passed
+ * So incoherent positions will result in an incoherent list
+ *
+ * @param array \$order id => rank pairs
+ * @param PropelPDO \$con optional connection
+ *
+ * @return boolean true if the reordering took place, false if a database problem prevented it
+ */
+public function reorder(array \$order, PropelPDO \$con = null)
+{
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+ }
+
+ \$con->beginTransaction();
+ try {
+ \$ids = array_keys(\$order);
+ \$objects = \$this->findPks(\$ids, \$con);
+ foreach (\$objects as \$object) {
+ \$pk = \$object->getPrimaryKey();
+ if (\$object->$columnGetter() != \$order[\$pk]) {
+ \$object->$columnSetter(\$order[\$pk]);
+ \$object->save(\$con);
+ }
+ }
+ \$con->commit();
+
+ return true;
+ } catch (PropelException \$e) {
+ \$con->rollback();
+ throw \$e;
+ }
+}
+";
+ }
+
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/builder/DataModelBuilder.php b/3rd_party/php/propel/generator/lib/builder/DataModelBuilder.php
new file mode 100644
index 000000000..605f0d5d1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/DataModelBuilder.php
@@ -0,0 +1,607 @@
+
+ * @package propel.generator.builder
+ */
+abstract class DataModelBuilder
+{
+
+ /**
+ * The current table.
+ * @var Table
+ */
+ private $table;
+
+ /**
+ * The generator config object holding build properties, etc.
+ *
+ * @var GeneratorConfig
+ */
+ private $generatorConfig;
+
+ /**
+ * An array of warning messages that can be retrieved for display (e.g. as part of phing build process).
+ * @var array string[]
+ */
+ private $warnings = array();
+
+ /**
+ * Peer builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $peerBuilder;
+
+ /**
+ * Stub Peer builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $stubPeerBuilder;
+
+ /**
+ * Object builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $objectBuilder;
+
+ /**
+ * Stub Object builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $stubObjectBuilder;
+
+ /**
+ * Query builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $queryBuilder;
+
+ /**
+ * Stub Query builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $stubQueryBuilder;
+
+ /**
+ * TableMap builder class for current table.
+ * @var DataModelBuilder
+ */
+ protected $tablemapBuilder;
+
+ /**
+ * Stub Interface builder class for current table.
+ * @var DataModelBuilder
+ */
+ private $interfaceBuilder;
+
+ /**
+ * Stub child object for current table.
+ * @var DataModelBuilder
+ */
+ private $multiExtendObjectBuilder;
+
+ /**
+ * Node object builder for current table.
+ * @var DataModelBuilder
+ */
+ private $nodeBuilder;
+
+ /**
+ * Node peer builder for current table.
+ * @var DataModelBuilder
+ */
+ private $nodePeerBuilder;
+
+ /**
+ * Stub node object builder for current table.
+ * @var DataModelBuilder
+ */
+ private $stubNodeBuilder;
+
+ /**
+ * Stub node peer builder for current table.
+ * @var DataModelBuilder
+ */
+ private $stubNodePeerBuilder;
+
+ /**
+ * NestedSet object builder for current table.
+ * @var DataModelBuilder
+ */
+ private $nestedSetBuilder;
+
+ /**
+ * NestedSet peer builder for current table.
+ * @var DataModelBuilder
+ */
+ private $nestedSetPeerBuilder;
+
+ /**
+ * The DDL builder for current table.
+ * @var DDLBuilder
+ */
+ private $ddlBuilder;
+
+ /**
+ * The Data-SQL builder for current table.
+ * @var DataSQLBuilder
+ */
+ private $dataSqlBuilder;
+
+ /**
+ * The Pluralizer class to use.
+ * @var Pluralizer
+ */
+ private $pluralizer;
+
+
+ /**
+ * Creates new instance of DataModelBuilder subclass.
+ * @param Table $table The Table which we are using to build [OM, DDL, etc.].
+ */
+ public function __construct(Table $table)
+ {
+ $this->table = $table;
+ }
+
+ /**
+ * Returns new or existing Peer builder class for this table.
+ * @return PeerBuilder
+ */
+ public function getPeerBuilder()
+ {
+ if (!isset($this->peerBuilder)) {
+ $this->peerBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'peer');
+ }
+ return $this->peerBuilder;
+ }
+
+ /**
+ * Returns new or existing Pluralizer class.
+ * @return Pluralizer
+ */
+ public function getPluralizer()
+ {
+ if (!isset($this->pluralizer)) {
+ $this->pluralizer = $this->getGeneratorConfig()->getConfiguredPluralizer();
+ }
+ return $this->pluralizer;
+ }
+
+ /**
+ * Returns new or existing stub Peer builder class for this table.
+ * @return PeerBuilder
+ */
+ public function getStubPeerBuilder()
+ {
+ if (!isset($this->stubPeerBuilder)) {
+ $this->stubPeerBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'peerstub');
+ }
+ return $this->stubPeerBuilder;
+ }
+
+ /**
+ * Returns new or existing Object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getObjectBuilder()
+ {
+ if (!isset($this->objectBuilder)) {
+ $this->objectBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'object');
+ }
+ return $this->objectBuilder;
+ }
+
+ /**
+ * Returns new or existing stub Object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getStubObjectBuilder()
+ {
+ if (!isset($this->stubObjectBuilder)) {
+ $this->stubObjectBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'objectstub');
+ }
+ return $this->stubObjectBuilder;
+ }
+
+ /**
+ * Returns new or existing Query builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getQueryBuilder()
+ {
+ if (!isset($this->queryBuilder)) {
+ $this->queryBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'query');
+ }
+ return $this->queryBuilder;
+ }
+
+ /**
+ * Returns new or existing stub Query builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getStubQueryBuilder()
+ {
+ if (!isset($this->stubQueryBuilder)) {
+ $this->stubQueryBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'querystub');
+ }
+ return $this->stubQueryBuilder;
+ }
+
+ /**
+ * Returns new or existing Object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getTableMapBuilder()
+ {
+ if (!isset($this->tablemapBuilder)) {
+ $this->tablemapBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'tablemap');
+ }
+ return $this->tablemapBuilder;
+ }
+
+ /**
+ * Returns new or existing stub Interface builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getInterfaceBuilder()
+ {
+ if (!isset($this->interfaceBuilder)) {
+ $this->interfaceBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'interface');
+ }
+ return $this->interfaceBuilder;
+ }
+
+ /**
+ * Returns new or existing stub child object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getMultiExtendObjectBuilder()
+ {
+ if (!isset($this->multiExtendObjectBuilder)) {
+ $this->multiExtendObjectBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'objectmultiextend');
+ }
+ return $this->multiExtendObjectBuilder;
+ }
+
+ /**
+ * Returns new or existing node Object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getNodeBuilder()
+ {
+ if (!isset($this->nodeBuilder)) {
+ $this->nodeBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'node');
+ }
+ return $this->nodeBuilder;
+ }
+
+ /**
+ * Returns new or existing node Peer builder class for this table.
+ * @return PeerBuilder
+ */
+ public function getNodePeerBuilder()
+ {
+ if (!isset($this->nodePeerBuilder)) {
+ $this->nodePeerBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'nodepeer');
+ }
+ return $this->nodePeerBuilder;
+ }
+
+ /**
+ * Returns new or existing stub node Object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getStubNodeBuilder()
+ {
+ if (!isset($this->stubNodeBuilder)) {
+ $this->stubNodeBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'nodestub');
+ }
+ return $this->stubNodeBuilder;
+ }
+
+ /**
+ * Returns new or existing stub node Peer builder class for this table.
+ * @return PeerBuilder
+ */
+ public function getStubNodePeerBuilder()
+ {
+ if (!isset($this->stubNodePeerBuilder)) {
+ $this->stubNodePeerBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'nodepeerstub');
+ }
+ return $this->stubNodePeerBuilder;
+ }
+
+ /**
+ * Returns new or existing nested set object builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getNestedSetBuilder()
+ {
+ if (!isset($this->nestedSetBuilder)) {
+ $this->nestedSetBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'nestedset');
+ }
+ return $this->nestedSetBuilder;
+ }
+
+ /**
+ * Returns new or existing nested set Peer builder class for this table.
+ * @return PeerBuilder
+ */
+ public function getNestedSetPeerBuilder()
+ {
+ if (!isset($this->nestedSetPeerBuilder)) {
+ $this->nestedSetPeerBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'nestedsetpeer');
+ }
+ return $this->nestedSetPeerBuilder;
+ }
+
+ /**
+ * Returns new or existing ddl builder class for this table.
+ * @return DDLBuilder
+ */
+ public function getDDLBuilder()
+ {
+ if (!isset($this->ddlBuilder)) {
+ $this->ddlBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'ddl');
+ }
+ return $this->ddlBuilder;
+ }
+
+ /**
+ * Returns new or existing data sql builder class for this table.
+ * @return DataSQLBuilder
+ */
+ public function getDataSQLBuilder()
+ {
+ if (!isset($this->dataSqlBuilder)) {
+ $this->dataSqlBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'datasql');
+ }
+ return $this->dataSqlBuilder;
+ }
+
+ /**
+ * Convenience method to return a NEW Peer class builder instance.
+ *
+ * This is used very frequently from the peer and object builders to get
+ * a peer builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return PeerBuilder
+ */
+ public function getNewPeerBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'peer');
+ }
+
+ /**
+ * Convenience method to return a NEW Peer stub class builder instance.
+ *
+ * This is used from the peer and object builders to get
+ * a peer builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return PeerBuilder
+ */
+ public function getNewStubPeerBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'peerstub');
+ }
+
+ /**
+ * Convenience method to return a NEW Object class builder instance.
+ *
+ * This is used very frequently from the peer and object builders to get
+ * an object builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return ObjectBuilder
+ */
+ public function getNewObjectBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'object');
+ }
+
+ /**
+ * Convenience method to return a NEW Object stub class builder instance.
+ *
+ * This is used from the query builders to get
+ * an object builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return ObjectBuilder
+ */
+ public function getNewStubObjectBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'objectstub');
+ }
+
+ /**
+ * Convenience method to return a NEW query class builder instance.
+ *
+ * This is used from the query builders to get
+ * a query builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return QueryBuilder
+ */
+ public function getNewQueryBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'query');
+ }
+
+ /**
+ * Convenience method to return a NEW query stub class builder instance.
+ *
+ * This is used from the query builders to get
+ * a query builder for a RELATED table.
+ *
+ * @param Table $table
+ * @return QueryBuilder
+ */
+ public function getNewStubQueryBuilder(Table $table)
+ {
+ return $this->getGeneratorConfig()->getConfiguredBuilder($table, 'querystub');
+ }
+
+ /**
+ * Returns new Query Inheritance builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getNewQueryInheritanceBuilder($child)
+ {
+ $queryInheritanceBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'queryinheritance');
+ $queryInheritanceBuilder->setChild($child);
+ return $queryInheritanceBuilder;
+ }
+
+ /**
+ * Returns new stub Query Inheritance builder class for this table.
+ * @return ObjectBuilder
+ */
+ public function getNewStubQueryInheritanceBuilder($child)
+ {
+ $stubQueryInheritanceBuilder = $this->getGeneratorConfig()->getConfiguredBuilder($this->getTable(), 'queryinheritancestub');
+ $stubQueryInheritanceBuilder->setChild($child);
+ return $stubQueryInheritanceBuilder;
+ }
+
+ /**
+ * Gets the GeneratorConfig object.
+ *
+ * @return GeneratorConfig
+ */
+ public function getGeneratorConfig()
+ {
+ return $this->generatorConfig;
+ }
+
+ /**
+ * Get a specific [name transformed] build property.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function getBuildProperty($name)
+ {
+ if ($this->getGeneratorConfig()) {
+ return $this->getGeneratorConfig()->getBuildProperty($name);
+ }
+ return null; // just to be explicit
+ }
+
+ /**
+ * Sets the GeneratorConfig object.
+ *
+ * @param GeneratorConfig $v
+ */
+ public function setGeneratorConfig(GeneratorConfig $v)
+ {
+ $this->generatorConfig = $v;
+ }
+
+ /**
+ * Sets the table for this builder.
+ * @param Table $table
+ */
+ public function setTable(Table $table)
+ {
+ $this->table = $table;
+ }
+
+ /**
+ * Returns the current Table object.
+ * @return Table
+ */
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ /**
+ * Convenience method to returns the Platform class for this table (database).
+ * @return Platform
+ */
+ public function getPlatform()
+ {
+ if ($this->getTable() && $this->getTable()->getDatabase()) {
+ return $this->getTable()->getDatabase()->getPlatform();
+ }
+ }
+
+ /**
+ * Convenience method to returns the database for current table.
+ * @return Database
+ */
+ public function getDatabase()
+ {
+ if ($this->getTable()) {
+ return $this->getTable()->getDatabase();
+ }
+ }
+
+ /**
+ * Pushes a message onto the stack of warnings.
+ * @param string $msg The warning message.
+ */
+ protected function warn($msg)
+ {
+ $this->warnings[] = $msg;
+ }
+
+ /**
+ * Gets array of warning messages.
+ * @return array string[]
+ */
+ public function getWarnings()
+ {
+ return $this->warnings;
+ }
+
+ /**
+ * Wraps call to Platform->quoteIdentifier() with a check to see whether quoting is enabled.
+ *
+ * All subclasses should call this quoteIdentifier() method rather than calling the Platform
+ * method directly. This method is used by both DataSQLBuilder and DDLBuilder, and potentially
+ * in the OM builders also, which is why it is defined in this class.
+ *
+ * @param string $text The text to quote.
+ * @return string Quoted text.
+ */
+ public function quoteIdentifier($text)
+ {
+ if (!$this->getBuildProperty('disableIdentifierQuoting')) {
+ return $this->getPlatform()->quoteIdentifier($text);
+ }
+ return $text;
+ }
+
+ /**
+ * Returns the name of the current class being built, with a possible prefix.
+ * @return string
+ * @see OMBuilder#getClassname()
+ */
+ public function prefixClassname($identifier)
+ {
+ return $this->getBuildProperty('classPrefix') . $identifier;
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/ClassTools.php b/3rd_party/php/propel/generator/lib/builder/om/ClassTools.php
new file mode 100644
index 000000000..5e3b5a5f2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/ClassTools.php
@@ -0,0 +1,120 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.builder.om
+ */
+class ClassTools
+{
+
+ /**
+ * Gets just classname, given a dot-path to class.
+ * @param string $qualifiedName
+ * @return string
+ */
+ public static function classname($qualifiedName)
+ {
+ $pos = strrpos($qualifiedName, '.');
+ if ($pos === false) {
+ return $qualifiedName; // there is no '.' in the qualifed name
+ } else {
+ return substr($qualifiedName, $pos + 1); // start just after '.'
+ }
+ }
+
+ /**
+ * Gets the path to be used in include()/require() statement.
+ *
+ * Supports multiple function signatures:
+ *
+ * (1) getFilePath($dotPathClass);
+ * (2) getFilePath($dotPathPrefix, $className);
+ * (3) getFilePath($dotPathPrefix, $className, $extension);
+ *
+ * @param string $path dot-path to class or to package prefix.
+ * @param string $classname class name
+ * @param string $extension The extension to use on the file.
+ * @return string The constructed file path.
+ */
+ public static function getFilePath($path, $classname = null, $extension = '.php')
+ {
+ $path = strtr(ltrim($path, '.'), '.', '/');
+ if ($classname !== null) {
+ if ($path !== "") { $path .= '/'; }
+ return $path . $classname . $extension;
+ } else {
+ return $path . $extension;
+ }
+ }
+
+ /**
+ * Gets the basePeer path if specified for table/db.
+ * If not, will return 'propel.util.BasePeer'
+ * @return string
+ */
+ public static function getBasePeer(Table $table) {
+ $class = $table->getBasePeer();
+ if ($class === null) {
+ $class = "propel.util.BasePeer";
+ }
+ return $class;
+ }
+
+ /**
+ * Gets the baseClass path if specified for table/db.
+ * If not, will return 'propel.om.BaseObject'
+ * @return string
+ */
+ public static function getBaseClass(Table $table) {
+ $class = $table->getBaseClass();
+ if ($class === null) {
+ $class = "propel.om.BaseObject";
+ }
+ return $class;
+ }
+
+ /**
+ * Gets the interface path if specified for table.
+ * If not, will return 'propel.om.Persistent'.
+ * @return string
+ */
+ public static function getInterface(Table $table) {
+ $interface = $table->getInterface();
+ if ($interface === null && !$table->isReadOnly()) {
+ $interface = "propel.om.Persistent";
+ }
+ return $interface;
+ }
+
+ /**
+ * Gets a list of PHP reserved words.
+ *
+ * @return array string[]
+ */
+ public static function getPhpReservedWords()
+ {
+ return array(
+ 'and', 'or', 'xor', 'exception', '__FILE__', '__LINE__',
+ 'array', 'as', 'break', 'case', 'class', 'const', 'continue',
+ 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty',
+ 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile',
+ 'eval', 'exit', 'extends', 'for', 'foreach', 'function', 'global',
+ 'if', 'include', 'include_once', 'isset', 'list', 'new', 'print', 'require',
+ 'require_once', 'return', 'static', 'switch', 'unset', 'use', 'var', 'while',
+ '__FUNCTION__', '__CLASS__', '__METHOD__', 'final', 'php_user_filter', 'interface',
+ 'implements', 'extends', 'public', 'protected', 'private', 'abstract', 'clone', 'try', 'catch',
+ 'throw', 'this', 'namespace'
+ );
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryBuilder.php
new file mode 100644
index 000000000..8472c677c
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryBuilder.php
@@ -0,0 +1,139 @@
+getTable()->getPhpName() . 'Query';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $requiredClassFilePath = $this->getQueryBuilder()->getClassFilePath();
+
+ $script .="
+require '".$requiredClassFilePath."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $this->declareClassFromBuilder($this->getQueryBuilder());
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+ $baseClassname = $this->getQueryBuilder()->getClassname();
+
+ $script .= "
+
+/**
+ * Skeleton subclass for performing query and update operations on the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends $baseClassname {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub query class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see QueryBuilder::addClassBody()
+ */
+
+ protected function addClassBody(&$script)
+ {
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('extensionQueryFilter', $script, "");
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier = null)
+ {
+ return parent::hasBehaviorModifier($hookName, 'QueryBuilderModifier');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifier($hookName, &$script, $tab = " ")
+ {
+ return $this->applyBehaviorModifierBase($hookName, 'QueryBuilderModifier', $script, $tab);
+ }
+
+ /**
+ * Checks whether any registered behavior content creator on that table exists a contentName
+ * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
+ */
+ public function getBehaviorContent($contentName)
+ {
+ return $this->getBehaviorContentBase($contentName, 'QueryBuilderModifier');
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryInheritanceBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryInheritanceBuilder.php
new file mode 100755
index 000000000..2b8e3f30e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/ExtensionQueryInheritanceBuilder.php
@@ -0,0 +1,138 @@
+getChild()->getClassName() . 'Query';
+ }
+
+ /**
+ * Set the child object that we're operating on currrently.
+ * @param $child Inheritance
+ */
+ public function setChild(Inheritance $child)
+ {
+ $this->child = $child;
+ }
+
+ /**
+ * Returns the child object we're operating on currently.
+ * @return Inheritance
+ * @throws BuildException - if child was not set.
+ */
+ public function getChild()
+ {
+ if (!$this->child) {
+ throw new BuildException("The PHP5MultiExtendObjectBuilder needs to be told which child class to build (via setChild() method) before it can build the stub class.");
+ }
+ return $this->child;
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $requiredClassFilePath = $this->getStubQueryBuilder()->getClassFilePath();
+
+ $script .="
+require '".$requiredClassFilePath."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseBuilder = $this->getNewQueryInheritanceBuilder($this->getChild());
+ $this->declareClassFromBuilder($baseBuilder);
+ $baseClassname = $baseBuilder->getClassname();
+
+ $script .= "
+
+/**
+ * Skeleton subclass for representing a query for one of the subclasses of the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class " .$this->getClassname() . " extends " . $baseClassname . " {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // MultiExtensionQueryBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/OMBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/OMBuilder.php
new file mode 100644
index 000000000..c78334708
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/OMBuilder.php
@@ -0,0 +1,526 @@
+
+ * @package propel.generator.builder.om
+ */
+abstract class OMBuilder extends DataModelBuilder
+{
+ /**
+ * Declared fully qualified classnames, to build the 'namespace' statements
+ * according to this table's namespace.
+ * @var array
+ */
+ protected $declaredClasses = array();
+
+ /**
+ * Builds the PHP source for current class and returns it as a string.
+ *
+ * This is the main entry point and defines a basic structure that classes should follow.
+ * In most cases this method will not need to be overridden by subclasses. This method
+ * does assume that the output language is PHP code, so it will need to be overridden if
+ * this is not the case.
+ *
+ * @return string The resulting PHP sourcecode.
+ */
+ public function build()
+ {
+ $this->validateModel();
+
+ $script = '';
+ if ($this->isAddIncludes()) {
+ $this->addIncludes($script);
+ }
+ $this->addClassOpen($script);
+ $this->addClassBody($script);
+ $this->addClassClose($script);
+
+ if($useStatements = $this->getUseStatements($ignoredNamespace = $this->getNamespace())) {
+ $script = $useStatements . $script;
+ }
+ if($namespaceStatement = $this->getNamespaceStatement()) {
+ $script = $namespaceStatement . $script;
+ }
+ //if($this->getTable()->getName() == 'book_club_list') die($ignoredNamespace);
+
+ return "<" . "?php
+
+" . $script;
+ }
+
+ /**
+ * Validates the current table to make sure that it won't
+ * result in generated code that will not parse.
+ *
+ * This method may emit warnings for code which may cause problems
+ * and will throw exceptions for errors that will definitely cause
+ * problems.
+ */
+ protected function validateModel()
+ {
+ // Validation is currently only implemented in the subclasses.
+ }
+
+ /**
+ * Creates a $obj = new Book(); code snippet. Can be used by frameworks, for instance, to
+ * extend this behavior, e.g. initialize the object after creating the instance or so.
+ *
+ * @return string Some code
+ */
+ public function buildObjectInstanceCreationCode($objName, $clsName)
+ {
+ return "$objName = new $clsName();";
+ }
+
+ /**
+ * Returns the qualified (prefixed) classname that is being built by the current class.
+ * This method must be implemented by child classes.
+ * @return string
+ */
+ abstract public function getUnprefixedClassname();
+
+ /**
+ * Returns the prefixed classname that is being built by the current class.
+ * @return string
+ * @see DataModelBuilder#prefixClassname()
+ */
+ public function getClassname()
+ {
+ return $this->prefixClassname($this->getUnprefixedClassname());
+ }
+
+ /**
+ * Returns the namespaced classname if there is a namespace, and the raw classname otherwise
+ * @return string
+ */
+ public function getFullyQualifiedClassname()
+ {
+ if ($namespace = $this->getNamespace()) {
+ return $namespace . '\\' . $this->getClassname();
+ } else {
+ return $this->getClassname();
+ }
+ }
+
+ /**
+ * Gets the dot-path representation of current class being built.
+ * @return string
+ */
+ public function getClasspath()
+ {
+ if ($this->getPackage()) {
+ $path = $this->getPackage() . '.' . $this->getClassname();
+ } else {
+ $path = $this->getClassname();
+ }
+ return $path;
+ }
+
+ /**
+ * Gets the full path to the file for the current class.
+ * @return string
+ */
+ public function getClassFilePath()
+ {
+ return ClassTools::getFilePath($this->getPackage(), $this->getClassname());
+ }
+
+ /**
+ * Gets package name for this table.
+ * This is overridden by child classes that have different packages.
+ * @return string
+ */
+ public function getPackage()
+ {
+ $pkg = ($this->getTable()->getPackage() ? $this->getTable()->getPackage() : $this->getDatabase()->getPackage());
+ if (!$pkg) {
+ $pkg = $this->getBuildProperty('targetPackage');
+ }
+ return $pkg;
+ }
+
+ /**
+ * Returns filesystem path for current package.
+ * @return string
+ */
+ public function getPackagePath()
+ {
+ return strtr($this->getPackage(), '.', '/');
+ }
+
+ /**
+ * Return the user-defined namespace for this table,
+ * or the database namespace otherwise.
+ *
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->getTable()->getNamespace();
+ }
+
+ public function declareClassNamespace($class, $namespace = '')
+ {
+ if (isset($this->declaredClasses[$namespace])
+ && in_array($class, $this->declaredClasses[$namespace])) {
+ return;
+ }
+ $this->declaredClasses[$namespace][] = $class;
+ }
+
+ public function declareClass($fullyQualifiedClassName)
+ {
+ $fullyQualifiedClassName = trim($fullyQualifiedClassName, '\\');
+ if (($pos = strrpos($fullyQualifiedClassName, '\\')) !== false) {
+ $this->declareClassNamespace(substr($fullyQualifiedClassName, $pos + 1), substr($fullyQualifiedClassName, 0, $pos));
+ } else {
+ // root namespace
+ $this->declareClassNamespace($fullyQualifiedClassName);
+ }
+ }
+
+ public function declareClassFromBuilder($builder)
+ {
+ $this->declareClassNamespace($builder->getClassname(), $builder->getNamespace());
+ }
+
+ public function declareClasses()
+ {
+ $args = func_get_args();
+ foreach ($args as $class) {
+ $this->declareClass($class);
+ }
+ }
+
+ public function getDeclaredClasses($namespace = null)
+ {
+ if (null !== $namespace && isset($this->declaredClasses[$namespace])) {
+ return $this->declaredClasses[$namespace];
+ } else {
+ return $this->declaredClasses;
+ }
+ }
+
+ public function getNamespaceStatement()
+ {
+ $namespace = $this->getNamespace();
+ if ($namespace != '') {
+ return sprintf("namespace %s;
+
+", $namespace);
+ }
+ }
+
+ public function getUseStatements($ignoredNamespace = null)
+ {
+ $declaredClasses = $this->declaredClasses;
+ unset($declaredClasses[$ignoredNamespace]);
+ ksort($declaredClasses);
+ foreach ($declaredClasses as $namespace => $classes) {
+ sort($classes);
+ foreach ($classes as $class) {
+ $script .= sprintf("use %s\\%s;
+", $namespace, $class);
+ }
+ }
+ return $script;
+ }
+
+ /**
+ * Shortcut method to return the [stub] peer classname for current table.
+ * This is the classname that is used whenever object or peer classes want
+ * to invoke methods of the peer classes.
+ * @return string (e.g. 'MyPeer')
+ * @see StubPeerBuilder::getClassname()
+ */
+ public function getPeerClassname() {
+ return $this->getStubPeerBuilder()->getClassname();
+ }
+
+ /**
+ * Shortcut method to return the [stub] query classname for current table.
+ * This is the classname that is used whenever object or peer classes want
+ * to invoke methods of the query classes.
+ * @return string (e.g. 'Myquery')
+ * @see StubQueryBuilder::getClassname()
+ */
+ public function getQueryClassname() {
+ return $this->getStubQueryBuilder()->getClassname();
+ }
+
+ /**
+ * Returns the object classname for current table.
+ * This is the classname that is used whenever object or peer classes want
+ * to invoke methods of the object classes.
+ * @return string (e.g. 'My')
+ * @see StubPeerBuilder::getClassname()
+ */
+ public function getObjectClassname() {
+ return $this->getStubObjectBuilder()->getClassname();
+ }
+
+ /**
+ * Get the column constant name (e.g. PeerName::COLUMN_NAME).
+ *
+ * @param Column $col The column we need a name for.
+ * @param string $classname The Peer classname to use.
+ *
+ * @return string If $classname is provided, then will return $classname::COLUMN_NAME; if not, then the peername is looked up for current table to yield $currTablePeer::COLUMN_NAME.
+ */
+ public function getColumnConstant($col, $classname = null)
+ {
+ if ($col === null) {
+ $e = new Exception("No col specified.");
+ print $e;
+ throw $e;
+ }
+ if ($classname === null) {
+ return $this->getBuildProperty('classPrefix') . $col->getConstantName();
+ }
+ // was it overridden in schema.xml ?
+ if ($col->getPeerName()) {
+ $const = strtoupper($col->getPeerName());
+ } else {
+ $const = strtoupper($col->getName());
+ }
+ return $classname.'::'.$const;
+ }
+
+ /**
+ * Gets the basePeer path if specified for table/db.
+ * If not, will return 'propel.util.BasePeer'
+ * @return string
+ */
+ public function getBasePeer(Table $table) {
+ $class = $table->getBasePeer();
+ if ($class === null) {
+ $class = "propel.util.BasePeer";
+ }
+ return $class;
+ }
+
+ /**
+ * Convenience method to get the foreign Table object for an fkey.
+ * @deprecated use ForeignKey::getForeignTable() instead
+ * @return Table
+ */
+ protected function getForeignTable(ForeignKey $fk)
+ {
+ return $this->getTable()->getDatabase()->getTable($fk->getForeignTableName());
+ }
+
+ /**
+ * Convenience method to get the default Join Type for a relation.
+ * If the key is required, an INNER JOIN will be returned, else a LEFT JOIN will be suggested,
+ * unless the schema is provided with the DefaultJoin attribute, which overrules the default Join Type
+ *
+ * @param ForeignKey $fk
+ * @return string
+ */
+ protected function getJoinType(ForeignKey $fk)
+ {
+ return $fk->getDefaultJoin() ?
+ "'".$fk->getDefaultJoin()."'" :
+ ($fk->isLocalColumnsRequired() ? 'Criteria::INNER_JOIN' : 'Criteria::LEFT_JOIN');
+ }
+
+ /**
+ * Gets the PHP method name affix to be used for fkeys for the current table (not referrers to this table).
+ *
+ * The difference between this method and the getRefFKPhpNameAffix() method is that in this method the
+ * classname in the affix is the foreign table classname.
+ *
+ * @param ForeignKey $fk The local FK that we need a name for.
+ * @param boolean $plural Whether the php name should be plural (e.g. initRelatedObjs() vs. addRelatedObj()
+ * @return string
+ */
+ public function getFKPhpNameAffix(ForeignKey $fk, $plural = false)
+ {
+ if ($fk->getPhpName()) {
+ if ($plural) {
+ return $this->getPluralizer()->getPluralForm($fk->getPhpName());
+ } else {
+ return $fk->getPhpName();
+ }
+ } else {
+ $className = $fk->getForeignTable()->getPhpName();
+ if ($plural) {
+ $className = $this->getPluralizer()->getPluralForm($className);
+ }
+ return $className . $this->getRelatedBySuffix($fk);
+ }
+ }
+
+ /**
+ * Gets the "RelatedBy*" suffix (if needed) that is attached to method and variable names.
+ *
+ * The related by suffix is based on the local columns of the foreign key. If there is more than
+ * one column in a table that points to the same foreign table, then a 'RelatedByLocalColName' suffix
+ * will be appended.
+ *
+ * @return string
+ */
+ protected static function getRelatedBySuffix(ForeignKey $fk)
+ {
+ $relCol = '';
+ foreach ($fk->getLocalForeignMapping() as $localColumnName => $foreignColumnName) {
+ $localTable = $fk->getTable();
+ $localColumn = $localTable->getColumn($localColumnName);
+ if (!$localColumn) {
+ throw new Exception("Could not fetch column: $columnName in table " . $localTable->getName());
+ }
+ if (count($localTable->getForeignKeysReferencingTable($fk->getForeignTableName())) > 1
+ || count($fk->getForeignTable()->getForeignKeysReferencingTable($fk->getTableName())) > 0
+ || $fk->getForeignTableName() == $fk->getTableName()) {
+ // self referential foreign key, or several foreign keys to the same table, or cross-reference fkey
+ $relCol .= $localColumn->getPhpName();
+ }
+ }
+
+ if ($relCol != '') {
+ $relCol = 'RelatedBy' . $relCol;
+ }
+
+ return $relCol;
+ }
+
+ /**
+ * Gets the PHP method name affix to be used for referencing foreign key methods and variable names (e.g. set????(), $coll???).
+ *
+ * The difference between this method and the getFKPhpNameAffix() method is that in this method the
+ * classname in the affix is the classname of the local fkey table.
+ *
+ * @param ForeignKey $fk The referrer FK that we need a name for.
+ * @param boolean $plural Whether the php name should be plural (e.g. initRelatedObjs() vs. addRelatedObj()
+ * @return string
+ */
+ public function getRefFKPhpNameAffix(ForeignKey $fk, $plural = false)
+ {
+ if ($fk->getRefPhpName()) {
+ if ($plural) {
+ return $this->getPluralizer()->getPluralForm($fk->getRefPhpName());
+ } else {
+ return $fk->getRefPhpName();
+ }
+ } else {
+ $className = $fk->getTable()->getPhpName();
+ if ($plural) {
+ $className = $this->getPluralizer()->getPluralForm($className);
+ }
+ return $className . $this->getRefRelatedBySuffix($fk);
+ }
+ }
+
+ protected static function getRefRelatedBySuffix(ForeignKey $fk)
+ {
+ $relCol = '';
+ foreach ($fk->getLocalForeignMapping() as $localColumnName => $foreignColumnName) {
+ $localTable = $fk->getTable();
+ $localColumn = $localTable->getColumn($localColumnName);
+ if (!$localColumn) {
+ throw new Exception("Could not fetch column: $columnName in table " . $localTable->getName());
+ }
+ $foreignKeysToForeignTable = $localTable->getForeignKeysReferencingTable($fk->getForeignTableName());
+ if ($fk->getForeignTableName() == $fk->getTableName()) {
+ // self referential foreign key
+ $relCol .= $fk->getForeignTable()->getColumn($foreignColumnName)->getPhpName();
+ if (count($foreignKeysToForeignTable) > 1) {
+ // several self-referential foreign keys
+ $relCol .= array_search($fk, $foreignKeysToForeignTable);
+ }
+ } elseif (count($foreignKeysToForeignTable) > 1 || count($fk->getForeignTable()->getForeignKeysReferencingTable($fk->getTableName())) > 0) {
+ // several foreign keys to the same table, or symmetrical foreign key in foreign table
+ $relCol .= $localColumn->getPhpName();
+ }
+ }
+
+ if ($relCol != '') {
+ $relCol = 'RelatedBy' . $relCol;
+ }
+
+ return $relCol;
+ }
+
+ /**
+ * Whether to add the include statements.
+ * This is based on the build property propel.addIncludes
+ */
+ protected function isAddIncludes()
+ {
+ return $this->getBuildProperty('addIncludes');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string $modifier The name of the modifier object providing the method in the behavior
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier)
+ {
+ $modifierGetter = 'get' . $modifier;
+ foreach ($this->getTable()->getBehaviors() as $behavior) {
+ if(method_exists($behavior->$modifierGetter(), $hookName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string $modifier The name of the modifier object providing the method in the behavior
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifierBase($hookName, $modifier, &$script, $tab = " ")
+ {
+ $modifierGetter = 'get' . $modifier;
+ foreach ($this->getTable()->getBehaviors() as $behavior) {
+ $modifier = $behavior->$modifierGetter();
+ if(method_exists($modifier, $hookName)) {
+ if (strpos($hookName, 'Filter') !== false) {
+ // filter hook: the script string will be modified by the behavior
+ $modifier->$hookName($script, $this);
+ } else {
+ // regular hook: the behavior returns a string to append to the script string
+ $script .= "\n" . $tab . '// ' . $behavior->getName() . " behavior\n";
+ $script .= preg_replace('/^/m', $tab, $modifier->$hookName($this));
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks whether any registered behavior content creator on that table exists a contentName
+ * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
+ * @param string $modifier The name of the modifier object providing the method in the behavior
+ */
+ public function getBehaviorContentBase($contentName, $modifier)
+ {
+ $modifierGetter = 'get' . $modifier;
+ foreach ($this->getTable()->getBehaviors() as $behavior) {
+ $modifier = $behavior->$modifierGetter();
+ if(method_exists($modifier, $contentName)) {
+ return $modifier->$contentName($this);
+ }
+ }
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/ObjectBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/ObjectBuilder.php
new file mode 100644
index 000000000..a8ed43bfd
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/ObjectBuilder.php
@@ -0,0 +1,186 @@
+
+ * @package propel.generator.builder.om
+ */
+abstract class ObjectBuilder extends OMBuilder
+{
+
+ /**
+ * Constructs a new PeerBuilder subclass.
+ */
+ public function __construct(Table $table) {
+ parent::__construct($table);
+ }
+
+ /**
+ * This method adds the contents of the generated class to the script.
+ *
+ * This method is abstract and should be overridden by the subclasses.
+ *
+ * Hint: Override this method in your subclass if you want to reorganize or
+ * drastically change the contents of the generated peer class.
+ *
+ * @param string &$script The script will be modified in this method.
+ */
+ abstract protected function addClassBody(&$script);
+
+ /**
+ * Adds the getter methods for the column values.
+ * This is here because it is probably generic enough to apply to templates being generated
+ * in different langauges (e.g. PHP4 and PHP5).
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addColumnAccessorMethods(&$script)
+ {
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+
+ // if they're not using the DateTime class than we will generate "compatibility" accessor method
+ if ($col->getType() === PropelTypes::DATE || $col->getType() === PropelTypes::TIME || $col->getType() === PropelTypes::TIMESTAMP) {
+ $this->addTemporalAccessor($script, $col);
+ } else {
+ $this->addDefaultAccessor($script, $col);
+ }
+
+ if ($col->isLazyLoad()) {
+ $this->addLazyLoader($script, $col);
+ }
+ }
+ }
+
+ /**
+ * Adds the mutator (setter) methods for setting column values.
+ * This is here because it is probably generic enough to apply to templates being generated
+ * in different langauges (e.g. PHP4 and PHP5).
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addColumnMutatorMethods(&$script)
+ {
+ foreach ($this->getTable()->getColumns() as $col) {
+
+ if ($col->isLobType()) {
+ $this->addLobMutator($script, $col);
+ } elseif ($col->getType() === PropelTypes::DATE || $col->getType() === PropelTypes::TIME || $col->getType() === PropelTypes::TIMESTAMP) {
+ $this->addTemporalMutator($script, $col);
+ } else {
+ $this->addDefaultMutator($script, $col);
+ }
+ }
+ }
+
+
+ /**
+ * Gets the baseClass path if specified for table/db.
+ * If not, will return 'propel.om.BaseObject'
+ * @return string
+ */
+ protected function getBaseClass() {
+ $class = $this->getTable()->getBaseClass();
+ if ($class === null) {
+ $class = "propel.om.BaseObject";
+ }
+ return $class;
+ }
+
+ /**
+ * Gets the interface path if specified for current table.
+ * If not, will return 'propel.om.Persistent'.
+ * @return string
+ */
+ protected function getInterface() {
+ $interface = $this->getTable()->getInterface();
+ if ($interface === null && !$this->getTable()->isReadOnly()) {
+ $interface = "propel.om.Persistent";
+ }
+ return $interface;
+ }
+
+ /**
+ * Whether to add the generic mutator methods (setByName(), setByPosition(), fromArray()).
+ * This is based on the build property propel.addGenericMutators, and also whether the
+ * table is read-only or an alias.
+ */
+ protected function isAddGenericMutators()
+ {
+ $table = $this->getTable();
+ return (!$table->isAlias() && $this->getBuildProperty('addGenericMutators') && !$table->isReadOnly());
+ }
+
+ /**
+ * Whether to add the generic accessor methods (getByName(), getByPosition(), toArray()).
+ * This is based on the build property propel.addGenericAccessors, and also whether the
+ * table is an alias.
+ */
+ protected function isAddGenericAccessors()
+ {
+ $table = $this->getTable();
+ return (!$table->isAlias() && $this->getBuildProperty('addGenericAccessors'));
+ }
+
+ /**
+ * Whether to add the validate() method.
+ * This is based on the build property propel.addValidateMethod
+ */
+ protected function isAddValidateMethod()
+ {
+ return $this->getBuildProperty('addValidateMethod');
+ }
+
+ protected function hasDefaultValues()
+ {
+ foreach ($this->getTable()->getColumns() as $col) {
+ if($col->getDefaultValue() !== null) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier = null)
+ {
+ return parent::hasBehaviorModifier($hookName, 'ObjectBuilderModifier');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifier($hookName, &$script, $tab = " ")
+ {
+ return $this->applyBehaviorModifierBase($hookName, 'ObjectBuilderModifier', $script, $tab);
+ }
+
+ /**
+ * Checks whether any registered behavior content creator on that table exists a contentName
+ * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
+ */
+ public function getBehaviorContent($contentName)
+ {
+ return $this->getBehaviorContentBase($contentName, 'ObjectBuilderModifier');
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodeBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodeBuilder.php
new file mode 100644
index 000000000..8b1bdae65
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodeBuilder.php
@@ -0,0 +1,111 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5ExtensionNodeBuilder extends ObjectBuilder
+{
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getTable()->getPhpName() . 'Node';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $script .= "
+require '".$this->getNodeBuilder()->getClassFilePath()."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseClassname = $this->getNodeBuilder()->getClassname();
+
+ $script .= "
+
+/**
+ * Skeleton subclass for representing a node from the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends $baseClassname {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ // there is no class body
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // PHP5ExtensionObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodePeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodePeerBuilder.php
new file mode 100644
index 000000000..8f5f16cf4
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionNodePeerBuilder.php
@@ -0,0 +1,112 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5ExtensionNodePeerBuilder extends PeerBuilder
+{
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getStubNodeBuilder()->getClassname() . 'Peer';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $script .="
+require '".$this->getNodePeerBuilder()->getClassFilePath()."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseClassname = $this->getNodePeerBuilder()->getClassname();
+
+ $script .= "
+
+/**
+ * Skeleton subclass for performing query and update operations on nodes of the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends $baseClassname {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub peer class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+
+ protected function addClassBody(&$script)
+ {
+ // there is no class body
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // PHP5ExtensionPeerBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionObjectBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionObjectBuilder.php
new file mode 100644
index 000000000..b88e1df0d
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionObjectBuilder.php
@@ -0,0 +1,133 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5ExtensionObjectBuilder extends ObjectBuilder
+{
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getTable()->getPhpName();
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ switch($this->getTable()->treeMode()) {
+ case 'NestedSet':
+ $requiredClassFilePath = $this->getNestedSetBuilder()->getClassFilePath();
+ break;
+
+ case 'MaterializedPath':
+ case 'AdjacencyList':
+ default:
+ $requiredClassFilePath = $this->getObjectBuilder()->getClassFilePath();
+ break;
+ }
+
+ $script .="
+require '".$requiredClassFilePath."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $this->declareClassFromBuilder($this->getObjectBuilder());
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ switch($table->treeMode()) {
+ case 'NestedSet':
+ $baseClassname = $this->getNestedSetBuilder()->getClassname();
+ break;
+
+ case 'MaterializedPath':
+ case "AdjacencyList":
+ default:
+ $baseClassname = $this->getObjectBuilder()->getClassname();
+ break;
+ }
+
+ $script .= "
+
+/**
+ * Skeleton subclass for representing a row from the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+".($table->isAbstract() ? "abstract " : "")."class ".$this->getClassname()." extends $baseClassname {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('extensionObjectFilter', $script, "");
+ }
+
+} // PHP5ExtensionObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionPeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionPeerBuilder.php
new file mode 100644
index 000000000..ef22ae4ca
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ExtensionPeerBuilder.php
@@ -0,0 +1,136 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5ExtensionPeerBuilder extends PeerBuilder
+{
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getStubObjectBuilder()->getUnprefixedClassname() . 'Peer';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ switch($this->getTable()->treeMode()) {
+ case 'NestedSet':
+ $requiredClassFilePath = $this->getNestedSetPeerBuilder()->getClassFilePath();
+ break;
+
+ case 'MaterializedPath':
+ case 'AdjacencyList':
+ default:
+ $requiredClassFilePath = $this->getPeerBuilder()->getClassFilePath();
+ break;
+ }
+
+ $script .="
+require '".$requiredClassFilePath."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $this->declareClassFromBuilder($this->getPeerBuilder());
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ switch($table->treeMode()) {
+ case 'NestedSet':
+ $baseClassname = $this->getNestedSetPeerBuilder()->getClassname();
+ break;
+
+ case 'MaterializedPath':
+ case 'AdjacencyList':
+ default:
+ $baseClassname = $this->getPeerBuilder()->getClassname();
+ break;
+ }
+
+ $script .= "
+
+/**
+ * Skeleton subclass for performing query and update operations on the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends $baseClassname {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub peer class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+
+ protected function addClassBody(&$script)
+ {
+ // there is no class body
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('extensionPeerFilter', $script, "");
+ }
+
+
+} // PHP5ExtensionPeerBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5InterfaceBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5InterfaceBuilder.php
new file mode 100644
index 000000000..ba6f094a2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5InterfaceBuilder.php
@@ -0,0 +1,108 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5InterfaceBuilder extends ObjectBuilder
+{
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return ClassTools::classname($this->getInterface());
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseClassname = $this->getObjectBuilder()->getClassname();
+
+ $script .= "
+/**
+ * This is an interface that should be filled with the public api of the $tableName objects.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional method declarations to this interface to meet the
+ * application requirements. This interface will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+interface ".$this->getClassname()." {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ // there is no class body
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // PHP5ExtensionObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5MultiExtendObjectBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5MultiExtendObjectBuilder.php
new file mode 100644
index 000000000..5ac7eae06
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5MultiExtendObjectBuilder.php
@@ -0,0 +1,196 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5MultiExtendObjectBuilder extends ObjectBuilder
+{
+
+ /**
+ * The current child "object" we are operating on.
+ */
+ private $child;
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getChild()->getClassname();
+ }
+
+ /**
+ * Override method to return child package, if specified.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return ($this->child->getPackage() ? $this->child->getPackage() : parent::getPackage());
+ }
+
+ /**
+ * Set the child object that we're operating on currrently.
+ * @param $child Inheritance
+ */
+ public function setChild(Inheritance $child)
+ {
+ $this->child = $child;
+ }
+
+ /**
+ * Returns the child object we're operating on currently.
+ * @return Inheritance
+ * @throws BuildException - if child was not set.
+ */
+ public function getChild()
+ {
+ if (!$this->child) {
+ throw new BuildException("The PHP5MultiExtendObjectBuilder needs to be told which child class to build (via setChild() method) before it can build the stub class.");
+ }
+ return $this->child;
+ }
+
+ /**
+ * Returns classpath to parent class.
+ * @return string
+ */
+ protected function getParentClasspath()
+ {
+ if ($this->getChild()->getAncestor()) {
+ return $this->getChild()->getAncestor();
+ } else {
+ return $this->getObjectBuilder()->getClasspath();
+ }
+ }
+
+ /**
+ * Returns classname of parent class.
+ * @return string
+ */
+ protected function getParentClassname()
+ {
+ return ClassTools::classname($this->getParentClasspath());
+ }
+
+ /**
+ * Gets the file path to the parent class.
+ * @return string
+ */
+ protected function getParentClassFilePath()
+ {
+ return ClassTools::getFilePath($this->getParentClasspath());
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ if ($this->getChild()->getAncestor()) {
+ $this->declareClassFromBuilder($this->getNewStubObjectBuilder($this->getDatabase()->getTableByPhpName($this->getChild()->getAncestor())));
+ } else {
+ $this->declareClassFromBuilder($this->getObjectBuilder());
+ }
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseClassname = $this->getObjectBuilder()->getClassname();
+
+ $script .= "
+
+/**
+ * Skeleton subclass for representing a row from one of the subclasses of the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends ".$this->getParentClassname()." {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $this->declareClassFromBuilder($this->getStubPeerBuilder());
+ $child = $this->getChild();
+ $col = $child->getColumn();
+ $cfc = $col->getPhpName();
+
+ $const = "CLASSKEY_".strtoupper($child->getKey());
+
+ $script .= "
+ /**
+ * Constructs a new ".$this->getChild()->getClassname()." class, setting the ".$col->getName()." column to ".$this->getPeerClassname()."::$const.
+ */
+ public function __construct()
+ {";
+ $script .= "
+ parent::__construct();
+ \$this->set$cfc(".$this->getPeerClassname()."::CLASSKEY_".strtoupper($child->getKey()).");
+ }
+";
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // PHP5ExtensionObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetBuilder.php
new file mode 100644
index 000000000..d7e0bb8fc
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetBuilder.php
@@ -0,0 +1,1136 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5NestedSetBuilder extends ObjectBuilder
+{
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubObjectBuilder()->getUnprefixedClassname() . 'NestedSet';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $script .="
+require '".$this->getObjectBuilder()->getClassFilePath()."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $script .= "
+/**
+ * Base class that represents a row from the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * @deprecated Since Propel 1.5. Use the nested_set behavior instead of the NestedSet treeMode
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." extends ".$this->getObjectBuilder()->getClassname()." implements NodeObject {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the basic OM class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $table = $this->getTable();
+
+ $this->addAttributes($script);
+
+ $this->addGetIterator($script);
+
+ $this->addSave($script);
+ $this->addDelete($script);
+
+ $this->addMakeRoot($script);
+
+ $this->addGetLevel($script);
+ $this->addGetPath($script);
+
+ $this->addGetNumberOfChildren($script);
+ $this->addGetNumberOfDescendants($script);
+
+ $this->addGetChildren($script);
+ $this->addGetDescendants($script);
+
+ $this->addSetLevel($script);
+
+ $this->addSetChildren($script);
+ $this->addSetParentNode($script);
+ $this->addSetPrevSibling($script);
+ $this->addSetNextSibling($script);
+
+ $this->addIsRoot($script);
+ $this->addIsLeaf($script);
+ $this->addIsEqualTo($script);
+
+ $this->addHasParent($script);
+ $this->addHasChildren($script);
+ $this->addHasPrevSibling($script);
+ $this->addHasNextSibling($script);
+
+ $this->addRetrieveParent($script);
+ $this->addRetrieveFirstChild($script);
+ $this->addRetrieveLastChild($script);
+ $this->addRetrievePrevSibling($script);
+ $this->addRetrieveNextSibling($script);
+
+ $this->addInsertAsFirstChildOf($script);
+ $this->addInsertAsLastChildOf($script);
+
+ $this->addInsertAsPrevSiblingOf($script);
+ $this->addInsertAsNextSiblingOf($script);
+
+ $this->addMoveToFirstChildOf($script);
+ $this->addMoveToLastChildOf($script);
+
+ $this->addMoveToPrevSiblingOf($script);
+ $this->addMoveToNextSiblingOf($script);
+
+ $this->addInsertAsParentOf($script);
+
+ $this->addGetLeft($script);
+ $this->addGetRight($script);
+ $this->addGetScopeId($script);
+
+ $this->addSetLeft($script);
+ $this->addSetRight($script);
+ $this->addSetScopeId($script);
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+
+ /**
+ * Adds class attributes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAttributes(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $script .= "
+ /**
+ * Store level of node
+ * @var int
+ */
+ protected \$level = null;
+
+ /**
+ * Store if node has prev sibling
+ * @var bool
+ */
+ protected \$hasPrevSibling = null;
+
+ /**
+ * Store node if has prev sibling
+ * @var $objectClassName
+ */
+ protected \$prevSibling = null;
+
+ /**
+ * Store if node has next sibling
+ * @var bool
+ */
+ protected \$hasNextSibling = null;
+
+ /**
+ * Store node if has next sibling
+ * @var $objectClassName
+ */
+ protected \$nextSibling = null;
+
+ /**
+ * Store if node has parent node
+ * @var bool
+ */
+ protected \$hasParentNode = null;
+
+ /**
+ * The parent node for this node.
+ * @var $objectClassName
+ */
+ protected \$parentNode = null;
+
+ /**
+ * Store children of the node
+ * @var array
+ */
+ protected \$_children = null;
+";
+ }
+
+ protected function addGetIterator(&$script)
+ {
+ $script .= "
+ /**
+ * Returns a pre-order iterator for this node and its children.
+ *
+ * @return NodeIterator
+ */
+ public function getIterator()
+ {
+ return new NestedSetRecursiveIterator(\$this);
+ }
+";
+ }
+
+ protected function addSave(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Saves modified object data to the datastore.
+ * If object is saved without left/right values, set them as undefined (0)
+ *
+ * @param PropelPDO Connection to use.
+ * @return int The number of rows affected by this insert/update and any referring fk objects' save() operations.
+ * May be unreliable with parent/children/brother changes
+ * @throws PropelException
+ */
+ public function save(PropelPDO \$con = null)
+ {
+ \$left = \$this->getLeftValue();
+ \$right = \$this->getRightValue();
+ if (empty(\$left) || empty(\$right)) {
+ \$root = $peerClassname::retrieveRoot(\$this->getScopeIdValue(), \$con);
+ $peerClassname::insertAsLastChildOf(\$this, \$root, \$con);
+ }
+
+ return parent::save(\$con);
+ }
+";
+ }
+
+ protected function addDelete(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Removes this object and all descendants from datastore.
+ *
+ * @param PropelPDO Connection to use.
+ * @return void
+ * @throws PropelException
+ */
+ public function delete(PropelPDO \$con = null)
+ {
+ // delete node first
+ parent::delete(\$con);
+
+ // delete descendants and then shift tree
+ $peerClassname::deleteDescendants(\$this, \$con);
+ }
+";
+ }
+
+ protected function addMakeRoot(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets node properties to make it a root node.
+ *
+ * @return $objectClassName The current object (for fluent API support)
+ * @throws PropelException
+ */
+ public function makeRoot()
+ {
+ $peerClassname::createRoot(\$this);
+ return \$this;
+ }
+";
+ }
+
+ protected function addGetLevel(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets the level if set, otherwise calculates this and returns it
+ *
+ * @param PropelPDO Connection to use.
+ * @return int
+ */
+ public function getLevel(PropelPDO \$con = null)
+ {
+ if (null === \$this->level) {
+ \$this->level = $peerClassname::getLevel(\$this, \$con);
+ }
+ return \$this->level;
+ }
+";
+ }
+
+ protected function addSetLevel(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets the level of the node in the tree
+ *
+ * @param int \$v new value
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setLevel(\$level)
+ {
+ \$this->level = \$level;
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetChildren(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets the children array of the node in the tree
+ *
+ * @param array of $objectClassName \$children array of Propel node object
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setChildren(array \$children)
+ {
+ \$this->_children = \$children;
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetParentNode(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets the parentNode of the node in the tree
+ *
+ * @param $objectClassName \$parent Propel node object
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setParentNode(NodeObject \$parent = null)
+ {
+ \$this->parentNode = (true === (\$this->hasParentNode = $peerClassname::isValid(\$parent))) ? \$parent : null;
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetPrevSibling(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets the previous sibling of the node in the tree
+ *
+ * @param $objectClassName \$node Propel node object
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setPrevSibling(NodeObject \$node = null)
+ {
+ \$this->prevSibling = \$node;
+ \$this->hasPrevSibling = $peerClassname::isValid(\$node);
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetNextSibling(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Sets the next sibling of the node in the tree
+ *
+ * @param $objectClassName \$node Propel node object
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setNextSibling(NodeObject \$node = null)
+ {
+ \$this->nextSibling = \$node;
+ \$this->hasNextSibling = $peerClassname::isValid(\$node);
+ return \$this;
+ }
+";
+ }
+
+ protected function addGetPath(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Get the path to the node in the tree
+ *
+ * @param PropelPDO Connection to use.
+ * @return array
+ */
+ public function getPath(PropelPDO \$con = null)
+ {
+ return $peerClassname::getPath(\$this, \$con);
+ }
+";
+ }
+
+ protected function addGetNumberOfChildren(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets the number of children for the node (direct descendants)
+ *
+ * @param PropelPDO Connection to use.
+ * @return int
+ */
+ public function getNumberOfChildren(PropelPDO \$con = null)
+ {
+ return $peerClassname::getNumberOfChildren(\$this, \$con);
+ }
+";
+ }
+
+ protected function addGetNumberOfDescendants(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets the total number of descendants for the node
+ *
+ * @param PropelPDO Connection to use.
+ * @return int
+ */
+ public function getNumberOfDescendants(PropelPDO \$con = null)
+ {
+ return $peerClassname::getNumberOfDescendants(\$this, \$con);
+ }
+";
+ }
+
+ protected function addGetChildren(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets the children for the node
+ *
+ * @param PropelPDO Connection to use.
+ * @return array
+ */
+ public function getChildren(PropelPDO \$con = null)
+ {
+ \$this->getLevel();
+
+ if (is_array(\$this->_children)) {
+ return \$this->_children;
+ }
+
+ return $peerClassname::retrieveChildren(\$this, \$con);
+ }
+";
+ }
+
+ protected function addGetDescendants(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets the descendants for the node
+ *
+ * @param PropelPDO Connection to use.
+ * @return array
+ */
+ public function getDescendants(PropelPDO \$con = null)
+ {
+ \$this->getLevel();
+
+ return $peerClassname::retrieveDescendants(\$this, \$con);
+ }
+";
+ }
+
+ protected function addIsRoot(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Returns true if node is the root node of the tree.
+ *
+ * @return bool
+ */
+ public function isRoot()
+ {
+ return $peerClassname::isRoot(\$this);
+ }
+";
+ }
+
+ protected function addIsLeaf(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Return true if the node is a leaf node
+ *
+ * @return bool
+ */
+ public function isLeaf()
+ {
+ return $peerClassname::isLeaf(\$this);
+ }
+";
+ }
+
+ protected function addIsEqualTo(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if object is equal to \$node
+ *
+ * @param object \$node Propel object for node to compare to
+ * @return bool
+ */
+ public function isEqualTo(NodeObject \$node)
+ {
+ return $peerClassname::isEqualTo(\$this, \$node);
+ }
+";
+ }
+
+ protected function addHasParent(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if object has an ancestor
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public function hasParent(PropelPDO \$con = null)
+ {
+ if (null === \$this->hasParentNode) {
+ $peerClassname::hasParent(\$this, \$con);
+ }
+ return \$this->hasParentNode;
+ }
+";
+ }
+
+ protected function addHasChildren(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Determines if the node has children / descendants
+ *
+ * @return bool
+ */
+ public function hasChildren()
+ {
+ return $peerClassname::hasChildren(\$this);
+ }
+";
+ }
+
+ protected function addHasPrevSibling(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Determines if the node has previous sibling
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public function hasPrevSibling(PropelPDO \$con = null)
+ {
+ if (null === \$this->hasPrevSibling) {
+ $peerClassname::hasPrevSibling(\$this, \$con);
+ }
+ return \$this->hasPrevSibling;
+ }
+";
+ }
+
+ protected function addHasNextSibling(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Determines if the node has next sibling
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public function hasNextSibling(PropelPDO \$con = null)
+ {
+ if (null === \$this->hasNextSibling) {
+ $peerClassname::hasNextSibling(\$this, \$con);
+ }
+ return \$this->hasNextSibling;
+ }
+";
+ }
+
+ protected function addRetrieveParent(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets ancestor for the given node if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public function retrieveParent(PropelPDO \$con = null)
+ {
+ if (null === \$this->hasParentNode) {
+ \$this->parentNode = $peerClassname::retrieveParent(\$this, \$con);
+ \$this->hasParentNode = $peerClassname::isValid(\$this->parentNode);
+ }
+ return \$this->parentNode;
+ }
+";
+ }
+
+ protected function addRetrieveFirstChild(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets first child if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public function retrieveFirstChild(PropelPDO \$con = null)
+ {
+ if (\$this->hasChildren(\$con)) {
+ if (is_array(\$this->_children)) {
+ return \$this->_children[0];
+ }
+
+ return $peerClassname::retrieveFirstChild(\$this, \$con);
+ }
+ return false;
+ }
+";
+ }
+
+ protected function addRetrieveLastChild(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets last child if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public function retrieveLastChild(PropelPDO \$con = null)
+ {
+ if (\$this->hasChildren(\$con)) {
+ if (is_array(\$this->_children)) {
+ \$last = count(\$this->_children) - 1;
+ return \$this->_children[\$last];
+ }
+
+ return $peerClassname::retrieveLastChild(\$this, \$con);
+ }
+ return false;
+ }
+";
+ }
+
+ protected function addRetrievePrevSibling(&$script)
+ {
+ $script .= "
+ /**
+ * Gets prev sibling for the given node if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public function retrievePrevSibling(PropelPDO \$con = null)
+ {
+ if (\$this->hasPrevSibling(\$con)) {
+ return \$this->prevSibling;
+ }
+ return \$this->hasPrevSibling;
+ }
+";
+ }
+
+ protected function addRetrieveNextSibling(&$script)
+ {
+ $script .= "
+ /**
+ * Gets next sibling for the given node if it exists
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public function retrieveNextSibling(PropelPDO \$con = null)
+ {
+ if (\$this->hasNextSibling(\$con)) {
+ return \$this->nextSibling;
+ }
+ return \$this->hasNextSibling;
+ }
+";
+ }
+
+ protected function addInsertAsFirstChildOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts as first child of given destination node \$parent
+ *
+ * @param $objectClassName \$parent Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ * @throws PropelException - if this object already exists
+ */
+ public function insertAsFirstChildOf(NodeObject \$parent, PropelPDO \$con = null)
+ {
+ if (!\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must be new.\");
+ }
+ $peerClassname::insertAsFirstChildOf(\$this, \$parent, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addInsertAsLastChildOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts as last child of given destination node \$parent
+ *
+ * @param $objectClassName \$parent Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ * @throws PropelException - if this object already exists
+ */
+ public function insertAsLastChildOf(NodeObject \$parent, PropelPDO \$con = null)
+ {
+ if (!\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must be new.\");
+ }
+ $peerClassname::insertAsLastChildOf(\$this, \$parent, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addInsertAsPrevSiblingOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$node as previous sibling to given destination node \$dest
+ *
+ * @param $objectClassName \$dest Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ * @throws PropelException - if this object already exists
+ */
+ public function insertAsPrevSiblingOf(NodeObject \$dest, PropelPDO \$con = null)
+ {
+ if (!\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must be new.\");
+ }
+ $peerClassname::insertAsPrevSiblingOf(\$this, \$dest, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addInsertAsNextSiblingOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$node as next sibling to given destination node \$dest
+ *
+ * @param $objectClassName \$dest Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ * @throws PropelException - if this object already exists
+ */
+ public function insertAsNextSiblingOf(NodeObject \$dest, PropelPDO \$con = null)
+ {
+ if (!\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must be new.\");
+ }
+ $peerClassname::insertAsNextSiblingOf(\$this, \$dest, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addMoveToFirstChildOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves node to be first child of \$parent
+ *
+ * @param $objectClassName \$parent Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function moveToFirstChildOf(NodeObject \$parent, PropelPDO \$con = null)
+ {
+ if (\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must exist in tree.\");
+ }
+ $peerClassname::moveToFirstChildOf(\$parent, \$this, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addMoveToLastChildOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves node to be last child of \$parent
+ *
+ * @param $objectClassName \$parent Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function moveToLastChildOf(NodeObject \$parent, PropelPDO \$con = null)
+ {
+ if (\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must exist in tree.\");
+ }
+ $peerClassname::moveToLastChildOf(\$parent, \$this, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addMoveToPrevSiblingOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves node to be prev sibling to \$dest
+ *
+ * @param $objectClassName \$dest Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function moveToPrevSiblingOf(NodeObject \$dest, PropelPDO \$con = null)
+ {
+ if (\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must exist in tree.\");
+ }
+ $peerClassname::moveToPrevSiblingOf(\$dest, \$this, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addMoveToNextSiblingOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves node to be next sibling to \$dest
+ *
+ * @param $objectClassName \$dest Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function moveToNextSiblingOf(NodeObject \$dest, PropelPDO \$con = null)
+ {
+ if (\$this->isNew())
+ {
+ throw new PropelException(\"$objectClassName must exist in tree.\");
+ }
+ $peerClassname::moveToNextSiblingOf(\$dest, \$this, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addInsertAsParentOf(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts node as parent of given node.
+ *
+ * @param $objectClassName \$node Propel object for destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function insertAsParentOf(NodeObject \$node, PropelPDO \$con = null)
+ {
+ $peerClassname::insertAsParentOf(\$this, \$node, \$con);
+ return \$this;
+ }
+";
+ }
+
+ protected function addGetLeft(&$script)
+ {
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetLeftKey()) {
+ $left_col_getter_name = 'get'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Wraps the getter for the left value
+ *
+ * @return int
+ */
+ public function getLeftValue()
+ {
+ return \$this->$left_col_getter_name();
+ }
+";
+ }
+
+ protected function addGetRight(&$script)
+ {
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetRightKey()) {
+ $right_col_getter_name = 'get'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Wraps the getter for the right value
+ *
+ * @return int
+ */
+ public function getRightValue()
+ {
+ return \$this->$right_col_getter_name();
+ }
+";
+ }
+
+ protected function addGetScopeId(&$script)
+ {
+ $table = $this->getTable();
+
+ $scope_col_getter_name = null;
+ foreach ($table->getColumns() as $col) {
+ if ($col->isTreeScopeKey()) {
+ $scope_col_getter_name = 'get'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Wraps the getter for the scope value
+ *
+ * @return int or null if scope is disabled
+ */
+ public function getScopeIdValue()
+ {";
+ if ($scope_col_getter_name) {
+ $script .= "
+ return \$this->$scope_col_getter_name();";
+ } else {
+ $script .= "
+ return null;";
+ }
+ $script .= "
+ }
+";
+ }
+
+ protected function addSetLeft(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetLeftKey()) {
+ $left_col_setter_name = 'set'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Set the value left column
+ *
+ * @param int \$v new value
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setLeftValue(\$v)
+ {
+ \$this->$left_col_setter_name(\$v);
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetRight(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetRightKey()) {
+ $right_col_setter_name = 'set'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Set the value of right column
+ *
+ * @param int \$v new value
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setRightValue(\$v)
+ {
+ \$this->$right_col_setter_name(\$v);
+ return \$this;
+ }
+";
+ }
+
+ protected function addSetScopeId(&$script)
+ {
+ $objectClassName = $this->getStubObjectBuilder()->getClassname();
+ $table = $this->getTable();
+
+ $scope_col_setter_name = null;
+ foreach ($table->getColumns() as $col) {
+ if ($col->isTreeScopeKey()) {
+ $scope_col_setter_name = 'set'.$col->getPhpName();
+ break;
+ }
+ }
+
+ $script .= "
+ /**
+ * Set the value of scope column
+ *
+ * @param int \$v new value
+ * @return $objectClassName The current object (for fluent API support)
+ */
+ public function setScopeIdValue(\$v)
+ {";
+ if ($scope_col_setter_name) {
+ $script .= "
+ \$this->$scope_col_setter_name(\$v);";
+ }
+ $script .= "
+ return \$this;
+ }
+";
+
+ }
+
+} // PHP5NestedSetBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetPeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetPeerBuilder.php
new file mode 100644
index 000000000..ad5fa05c1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5NestedSetPeerBuilder.php
@@ -0,0 +1,1727 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5NestedSetPeerBuilder extends PeerBuilder
+{
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubObjectBuilder()->getUnprefixedClassname() . 'NestedSetPeer';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $script .="
+require '".$this->getPeerBuilder()->getClassFilePath()."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $script .= "
+/**
+ * Base static class for performing query operations on the tree contained by the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * @deprecated Since Propel 1.5. Use the nested_set behavior instead of the NestedSet treeMode
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." extends ".$this->getPeerBuilder()->getClassName()." implements NodePeer {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the basic OM class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $table = $this->getTable();
+
+ // FIXME
+ // - Probably the build needs to be customized for supporting
+ // tables that are "aliases". -- definitely a fringe usecase, though.
+
+ $this->addConstants($script);
+
+ $this->addCreateRoot($script);
+
+ $this->addRetrieveRoot($script);
+
+ $this->addInsertAsFirstChildOf($script);
+ $this->addInsertAsLastChildOf($script);
+ $this->addInsertAsPrevSiblingOf($script);
+ $this->addInsertAsNextSiblingOf($script);
+ $this->addInsertAsParentOf($script);
+
+ $this->addInsertRoot($script);
+ $this->addInsertParent($script);
+
+ $this->addDeleteRoot($script);
+ $this->addDeleteNode($script);
+
+ $this->addMoveToFirstChildOf($script);
+ $this->addMoveToLastChildOf($script);
+ $this->addMoveToPrevSiblingOf($script);
+ $this->addMoveToNextSiblingOf($script);
+
+ $this->addRetrieveFirstChild($script);
+ $this->addRetrieveLastChild($script);
+ $this->addRetrievePrevSibling($script);
+ $this->addRetrieveNextSibling($script);
+
+ $this->addRetrieveTree($script);
+ $this->addRetrieveBranch($script);
+ $this->addRetrieveChildren($script);
+ $this->addRetrieveDescendants($script);
+ $this->addRetrieveSiblings($script);
+ $this->addRetrieveParent($script);
+
+ $this->addGetLevel($script);
+ $this->addGetNumberOfChildren($script);
+ $this->addGetNumberOfDescendants($script);
+ $this->addGetPath($script);
+
+ $this->addIsValid($script);
+ $this->addIsRoot($script);
+ $this->addIsLeaf($script);
+ $this->addIsChildOf($script);
+ $this->addIsChildOfOrSiblingTo($script);
+ $this->addIsEqualTo($script);
+
+ $this->addHasParent($script);
+ $this->addHasPrevSibling($script);
+ $this->addHasNextSibling($script);
+ $this->addHasChildren($script);
+
+ $this->addDeleteDescendants($script);
+
+ $this->addGetNode($script);
+
+ $this->addHydrateDescendants($script);
+ $this->addHydrateChildren($script);
+
+ $this->addShiftRParent($script);
+ $this->addUpdateLoadedNode($script);
+ $this->addUpdateDBNode($script);
+
+ $this->addShiftRLValues($script);
+ $this->addShiftRLRange($script);
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+ protected function addConstants(&$script)
+ {
+ $table = $this->getTable();
+ $tableName = $table->getName();
+
+ $colname = array();
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetLeftKey()) {
+ $colname['left'] = $tableName . '.' . strtoupper($col->getName());
+ }
+
+ if ($col->isNestedSetRightKey()) {
+ $colname['right'] = $tableName . '.' . strtoupper($col->getName());
+ }
+
+ if ($col->isTreeScopeKey()) {
+ $colname['scope'] = $tableName . '.' . strtoupper($col->getName());
+ }
+
+ if (3 == count($colname)) {
+ break;
+ }
+ }
+
+ if(!isset($colname['left'])) {
+ throw new EngineException("One column must have nestedSetLeftKey attribute set to true for [" . $table->getName() . "] table");
+ }
+
+ if(!isset($colname['right'])) {
+ throw new EngineException("One column must have nestedSetRightKey attribute set to true for [" . $table->getName() . "] table");
+ }
+
+ $colname['scope'] = isset($colname['scope']) ? $colname['scope'] : null;
+
+ $script .= "
+ /**
+ * Left column for the set
+ */
+ const LEFT_COL = " . var_export($colname['left'], true) . ";
+
+ /**
+ * Right column for the set
+ */
+ const RIGHT_COL = " . var_export($colname['right'], true) . ";
+
+ /**
+ * Scope column for the set
+ */
+ const SCOPE_COL = " . var_export($colname['scope'], true) . ";
+";
+ }
+
+ protected function addCreateRoot(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Creates the supplied node as the root node.
+ *
+ * @param $objectClassname \$node Propel object for model
+ * @throws PropelException
+ */
+ public static function createRoot(NodeObject \$node)
+ {
+ if (\$node->getLeftValue()) {
+ throw new PropelException('Cannot turn an existing node into a root node.');
+ }
+
+ \$node->setLeftValue(1);
+ \$node->setRightValue(2);
+ }
+";
+ }
+
+ protected function addRetrieveRoot(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Returns the root node for a given scope id
+ *
+ * @param int \$scopeId Scope id to determine which root node to return
+ * @param PropelPDO \$con Connection to use.
+ * @return $objectClassname Propel object for root node
+ */
+ public static function retrieveRoot(\$scopeId = null, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+
+ \$c->add(self::LEFT_COL, 1, Criteria::EQUAL);
+
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$scopeId, Criteria::EQUAL);
+ }
+
+ return $peerClassname::doSelectOne(\$c, \$con);
+ }
+";
+ }
+
+ protected function addInsertAsFirstChildOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$child as first child of given \$parent node
+ *
+ * @param $objectClassname \$child Propel object for child node
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertAsFirstChildOf(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
+ {
+ // Update \$child node properties
+ \$child->setLeftValue(\$parent->getLeftValue() + 1);
+ \$child->setRightValue(\$parent->getLeftValue() + 2);
+ \$child->setParentNode(\$parent);
+
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$child->setScopeIdValue(\$sidv = \$parent->getScopeIdValue());
+ }
+
+ // Update database nodes
+ self::shiftRLValues(\$child->getLeftValue(), 2, \$con, \$sidv);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$parent, 2, \$con);
+ }
+";
+ }
+
+ protected function addInsertAsLastChildOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$child as last child of destination node \$parent
+ *
+ * @param $objectClassname \$child Propel object for child node
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertAsLastChildOf(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
+ {
+ // Update \$child node properties
+ \$child->setLeftValue(\$parent->getRightValue());
+ \$child->setRightValue(\$parent->getRightValue() + 1);
+ \$child->setParentNode(\$parent);
+
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$child->setScopeIdValue(\$sidv = \$parent->getScopeIdValue());
+ }
+
+ // Update database nodes
+ self::shiftRLValues(\$child->getLeftValue(), 2, \$con, \$sidv);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$parent, 2, \$con);
+ }
+";
+ }
+
+ protected function addInsertAsPrevSiblingOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$sibling as previous sibling to destination node \$node
+ *
+ * @param $objectClassname \$node Propel object for destination node
+ * @param $objectClassname \$sibling Propel object for source node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertAsPrevSiblingOf(NodeObject \$node, NodeObject \$sibling, PropelPDO \$con = null)
+ {
+ if (\$sibling->isRoot()) {
+ throw new PropelException('Root nodes cannot have siblings');
+ }
+
+ \$node->setLeftValue(\$sibling->getLeftValue());
+ \$node->setRightValue(\$sibling->getLeftValue() + 1);
+ \$node->setParentNode(\$sibling->retrieveParent());
+
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$node->setScopeIdValue(\$sidv = \$sibling->getScopeIdValue());
+ }
+
+ // Update database nodes
+ self::shiftRLValues(\$node->getLeftValue(), 2, \$con, \$sidv);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$sibling->retrieveParent(), 2, \$con);
+ }
+";
+ }
+
+ protected function addInsertAsNextSiblingOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$sibling as next sibling to destination node \$node
+ *
+ * @param $objectClassname \$node Propel object for destination node
+ * @param $objectClassname \$sibling Propel object for source node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertAsNextSiblingOf(NodeObject \$node, NodeObject \$sibling, PropelPDO \$con = null)
+ {
+ if (\$sibling->isRoot()) {
+ throw new PropelException('Root nodes cannot have siblings');
+ }
+
+ \$node->setLeftValue(\$sibling->getRightValue() + 1);
+ \$node->setRightValue(\$sibling->getRightValue() + 2);
+ \$node->setParentNode(\$sibling->retrieveParent());
+
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$node->setScopeIdValue(\$sidv = \$sibling->getScopeIdValue());
+ }
+
+ // Update database nodes
+ self::shiftRLValues(\$node->getLeftValue(), 2, \$con, \$sidv);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$sibling->retrieveParent(), 2, \$con);
+ }
+";
+ }
+
+ protected function addInsertAsParentOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$parent as parent of given node.
+ *
+ * @param $objectClassname \$parent Propel object for given parent node
+ * @param $objectClassname \$node Propel object for given destination node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertAsParentOf(NodeObject \$parent, NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$sidv = \$node->getScopeIdValue();
+ }
+
+ self::shiftRLValues(\$node->getLeftValue(), 1, \$con, \$sidv);
+ self::shiftRLValues(\$node->getRightValue() + 2, 1, \$con, \$sidv);
+
+ if (self::SCOPE_COL) {
+ \$parent->setScopeIdValue(\$sidv);
+ }
+
+ \$parent->setLeftValue(\$node->getLeftValue());
+ \$parent->setRightValue(\$node->getRightValue() + 2);
+
+ \$previous_parent = \$node->retrieveParent();
+ \$parent->setParentNode(\$previous_parent);
+ \$node->setParentNode(\$parent);
+
+ \$node->save(\$con);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$previous_parent, 2, \$con);
+ }
+";
+ }
+
+ protected function addInsertRoot(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$node as root node
+ *
+ * @param $objectClassname \$node Propel object as root node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertRoot(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$sidv = \$node->getScopeIdValue();
+ }
+
+ $peerClassname::insertAsParentOf($peerClassname::retrieveRoot(\$sidv, \$con), \$node, \$con);
+ }
+";
+ }
+
+ protected function addInsertParent(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Inserts \$parent as parent to destination node \$child
+ *
+ * @deprecated 1.3 - 2007/11/06
+ * @see insertAsParentOf()
+ * @param $objectClassname \$child Propel object to become child node
+ * @param $objectClassname \$parent Propel object as parent node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function insertParent(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
+ {
+ self::insertAsParentOf(\$parent, \$child, \$con);
+ }
+";
+ }
+
+ protected function addDeleteRoot(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Delete root node
+ *
+ * @param PropelPDO \$con Connection to use.
+ * @return boolean Deletion status
+ */
+ public static function deleteRoot(\$scopeId = null, PropelPDO \$con = null)
+ {
+ if (!self::SCOPE_COL) {
+ \$scopeId = null;
+ }
+ \$root = $peerClassname::retrieveRoot(\$scopeId, \$con);
+ if ($peerClassname::getNumberOfChildren(\$root) == 1) {
+ return $peerClassname::deleteNode(\$root, \$con);
+ } else {
+ return false;
+ }
+ }
+";
+ }
+
+ protected function addDeleteNode(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Delete \$dest node
+ *
+ * @param $objectClassname \$dest Propel object node to delete
+ * @param PropelPDO \$con Connection to use.
+ * @return boolean Deletion status
+ */
+ public static function deleteNode(NodeObject \$dest, PropelPDO \$con = null)
+ {
+ if (\$dest->getLeftValue() == 1) {
+ // deleting root implies conditions (see deleteRoot() method)
+ return $peerClassname::deleteRoot(\$con);
+ }
+
+ \$sidv = null;
+ if (self::SCOPE_COL) {
+ \$sidv = \$dest->getScopeIdValue();
+ }
+
+ self::shiftRLRange(\$dest->getLeftValue(), \$dest->getRightValue(), -1, \$con, \$sidv);
+ self::shiftRLValues(\$dest->getRightValue() + 1, -2, \$con, \$sidv);
+ return \$dest->delete(\$con);
+ }
+";
+ }
+
+ protected function addMoveToFirstChildOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves \$child to be first child of \$parent
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param $objectClassname \$child Propel object for child node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function moveToFirstChildOf(NodeObject \$parent, NodeObject \$child, PropelPDO \$con = null)
+ {
+ if (\$parent->getScopeIdValue() != \$child->getScopeIdValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }
+ \$destLeft = \$parent->getLeftValue() + 1;
+ self::updateDBNode(\$child, \$destLeft, \$con);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$parent, 2, \$con);
+ }
+";
+ }
+
+ protected function addMoveToLastChildOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves \$child to be last child of \$parent
+ *
+ * @param $objectClassname \$parent Propel object for parent node
+ * @param $objectClassname \$child Propel object for child node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function moveToLastChildOf(NodeObject \$parent, NodeObject \$child, PropelPDO \$con = null)
+ {
+ if (\$parent->getScopeIdValue() != \$child->getScopeIdValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }
+ \$destLeft = \$parent->getRightValue();
+ self::updateDBNode(\$child, \$destLeft, \$con);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$parent, 2, \$con);
+ }
+";
+ }
+
+ protected function addMoveToPrevSiblingOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves \$node to be prev sibling to \$dest
+ *
+ * @param $objectClassname \$dest Propel object for destination node
+ * @param $objectClassname \$node Propel object for source node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function moveToPrevSiblingOf(NodeObject \$dest, NodeObject \$node, PropelPDO \$con = null)
+ {
+ if (\$dest->getScopeIdValue() != \$node->getScopeIdValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }
+ \$destLeft = \$dest->getLeftValue();
+ self::updateDBNode(\$node, \$destLeft, \$con);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$dest->retrieveParent(), 2, \$con);
+ }
+";
+ }
+
+ protected function addMoveToNextSiblingOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Moves \$node to be next sibling to \$dest
+ *
+ * @param $objectClassname \$dest Propel object for destination node
+ * @param $objectClassname \$node Propel object for source node
+ * @param PropelPDO \$con Connection to use.
+ * @return void
+ */
+ public static function moveToNextSiblingOf(NodeObject \$dest, NodeObject \$node, PropelPDO \$con = null)
+ {
+ if (\$dest->getScopeIdValue() != \$node->getScopeIdValue()) {
+ throw new PropelException('Moving nodes across trees is not supported');
+ }
+ \$destLeft = \$dest->getRightValue();
+ \$destLeft = \$destLeft + 1;
+ self::updateDBNode(\$node, \$destLeft, \$con);
+
+ // Update all loaded nodes
+ self::updateLoadedNode(\$dest->retrieveParent(), 2, \$con);
+ }
+";
+ }
+
+ protected function addRetrieveFirstChild(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets first child for the given node if it exists
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public static function retrieveFirstChild(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add(self::LEFT_COL, \$node->getLeftValue() + 1, Criteria::EQUAL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+
+ return $peerClassname::doSelectOne(\$c, \$con);
+ }
+";
+ }
+
+ protected function addRetrieveLastChild(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets last child for the given node if it exists
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public static function retrieveLastChild(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add(self::RIGHT_COL, \$node->getRightValue() - 1, Criteria::EQUAL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+
+ return $peerClassname::doSelectOne(\$c, \$con);
+ }
+";
+ }
+
+ protected function addRetrievePrevSibling(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets prev sibling for the given node if it exists
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else null
+ */
+ public static function retrievePrevSibling(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add(self::RIGHT_COL, \$node->getLeftValue() - 1, Criteria::EQUAL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$prevSibling = $peerClassname::doSelectOne(\$c, \$con);
+ \$node->setPrevSibling(\$prevSibling);
+ return \$prevSibling;
+ }
+";
+ }
+
+ protected function addRetrieveNextSibling(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets next sibling for the given node if it exists
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else false
+ */
+ public static function retrieveNextSibling(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->add(self::LEFT_COL, \$node->getRightValue() + 1, Criteria::EQUAL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$nextSibling = $peerClassname::doSelectOne(\$c, \$con);
+ \$node->setNextSibling(\$nextSibling);
+ return \$nextSibling;
+ }
+";
+ }
+
+ protected function addRetrieveTree(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Retrieves the entire tree from root
+ *
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function retrieveTree(\$scopeId = null, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->addAscendingOrderByColumn(self::LEFT_COL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$scopeId, Criteria::EQUAL);
+ }
+ \$stmt = $peerClassname::doSelectStmt(\$c, \$con);
+ if (false !== (\$row = \$stmt->fetch(PDO::FETCH_NUM))) {
+ \$omClass = $peerClassname::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+
+ \$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null === (\$root = ".$peerClassname."::getInstanceFromPool(\$key))) {
+ " . $this->buildObjectInstanceCreationCode('$root', '$cls') . "
+ \$root->hydrate(\$row);
+ }
+
+ \$root->setLevel(0);
+ $peerClassname::hydrateDescendants(\$root, \$stmt);
+ $peerClassname::addInstanceToPool(\$root);
+
+ \$stmt->closeCursor();
+ return \$root;
+ }
+ return false;
+ }
+";
+ }
+
+ protected function addRetrieveBranch(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Retrieves the entire tree from parent \$node
+ *
+ * @param $objectClassname \$node Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function retrieveBranch(NodeObject \$node, PropelPDO \$con = null)
+ {
+ return $peerClassname::retrieveDescendants(\$node, \$con);
+ }
+";
+ }
+
+ protected function addRetrieveChildren(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets direct children for the node
+ *
+ * @param $objectClassname \$node Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function retrieveChildren(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->addAscendingOrderByColumn(self::LEFT_COL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$c->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::GREATER_THAN);
+ \$c->addAnd(self::RIGHT_COL, \$node->getRightValue(), Criteria::LESS_THAN);
+ \$stmt = $peerClassname::doSelectStmt(\$c, \$con);
+
+ return $peerClassname::hydrateChildren(\$node, \$stmt);
+ }
+";
+ }
+
+ protected function addRetrieveDescendants(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets all descendants for the node
+ *
+ * @param $objectClassname \$node Propel object for parent node
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function retrieveDescendants(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c->addAscendingOrderByColumn(self::LEFT_COL);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$c->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::GREATER_THAN);
+ \$c->addAnd(self::RIGHT_COL, \$node->getRightValue(), Criteria::LESS_THAN);
+ \$stmt = $peerClassname::doSelectStmt(\$c, \$con);
+
+ return $peerClassname::hydrateDescendants(\$node, \$stmt);
+ }
+";
+ }
+
+ protected function addRetrieveSiblings(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets all siblings for the node
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function retrieveSiblings(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$parent = $peerClassname::retrieveParent(\$node, \$con);
+ \$siblings = $peerClassname::retrieveChildren(\$parent, \$con);
+
+ return \$siblings;
+ }
+";
+ }
+
+ protected function addRetrieveParent(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets immediate ancestor for the given node if it exists
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return mixed Propel object if exists else null
+ */
+ public static function retrieveParent(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c1 = \$c->getNewCriterion(self::LEFT_COL, \$node->getLeftValue(), Criteria::LESS_THAN);
+ \$c2 = \$c->getNewCriterion(self::RIGHT_COL, \$node->getRightValue(), Criteria::GREATER_THAN);
+
+ \$c1->addAnd(\$c2);
+
+ \$c->add(\$c1);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$c->addAscendingOrderByColumn(self::RIGHT_COL);
+
+ \$parent = $peerClassname::doSelectOne(\$c, \$con);
+
+ \$node->setParentNode(\$parent);
+
+ return \$parent;
+ }
+";
+ }
+
+ protected function addGetLevel(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets level for the given node
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return int Level for the given node
+ */
+ public static function getLevel(NodeObject \$node, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ \$sql = \"SELECT COUNT(*) AS level FROM \" . self::TABLE_NAME . \" WHERE \" . self::LEFT_COL . \" < :left AND \" . self::RIGHT_COL . \" > :right\";
+
+ if (self::SCOPE_COL) {
+ \$sql .= ' AND ' . self::SCOPE_COL . ' = :scope';
+ }
+
+ \$stmt = \$con->prepare(\$sql);
+ \$stmt->bindValue(':left', \$node->getLeftValue(), PDO::PARAM_INT);
+ \$stmt->bindValue(':right', \$node->getRightValue(), PDO::PARAM_INT);
+ if (self::SCOPE_COL) {
+ \$stmt->bindValue(':scope', \$node->getScopeIdValue());
+ }
+ \$stmt->execute();
+ \$row = \$stmt->fetch();
+ return \$row['level'];
+ }
+";
+ }
+
+ protected function addGetNumberOfChildren(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets number of direct children for given node
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return int Level for the given node
+ */
+ public static function getNumberOfChildren(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$children = $peerClassname::retrieveChildren(\$node);
+ return count(\$children);
+ }
+";
+ }
+
+ protected function addGetNumberOfDescendants(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Gets number of descendants for given node
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @param PropelPDO \$con Connection to use.
+ * @return int Level for the given node
+ */
+ public static function getNumberOfDescendants(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$right = \$node->getRightValue();
+ \$left = \$node->getLeftValue();
+ \$num = (\$right - \$left - 1) / 2;
+ return \$num;
+ }
+";
+ }
+
+ protected function addGetPath(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Returns path to a specific node as an array, useful to create breadcrumbs
+ *
+ * @param $objectClassname \$node Propel object of node to create path to
+ * @param PropelPDO \$con Connection to use.
+ * @return array Array in order of heirarchy
+ */
+ public static function getPath(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$criteria = new Criteria();
+ if (self::SCOPE_COL) {
+ \$criteria->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$criteria->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::LESS_EQUAL);
+ \$criteria->add(self::RIGHT_COL, \$node->getRightValue(), Criteria::GREATER_EQUAL);
+ \$criteria->addAscendingOrderByColumn(self::LEFT_COL);
+
+ return self::doSelect(\$criteria, \$con);
+ }
+";
+ }
+
+ protected function addIsValid(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if node is valid
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @return bool
+ */
+ public static function isValid(NodeObject \$node = null)
+ {
+ if (is_object(\$node) && \$node->getRightValue() > \$node->getLeftValue()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+";
+ }
+
+ protected function addIsRoot(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if node is a root
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @return bool
+ */
+ public static function isRoot(NodeObject \$node)
+ {
+ return (\$node->getLeftValue()==1);
+ }
+";
+ }
+
+ protected function addIsLeaf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if node is a leaf
+ *
+ * @param $objectClassname \$node Propel object for src node
+ * @return bool
+ */
+ public static function isLeaf(NodeObject \$node)
+ {
+ return ((\$node->getRightValue()-\$node->getLeftValue())==1);
+ }
+";
+ }
+
+ protected function addIsChildOf(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$child is a child of \$parent
+ *
+ * @param $objectClassname \$child Propel object for node
+ * @param $objectClassname \$parent Propel object for node
+ * @return bool
+ */
+ public static function isChildOf(NodeObject \$child, NodeObject \$parent)
+ {
+ return ((\$child->getLeftValue()>\$parent->getLeftValue()) && (\$child->getRightValue()<\$parent->getRightValue()));
+ }
+";
+ }
+
+ protected function addIsChildOfOrSiblingTo(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node1 is a child of or equal to \$node2
+ *
+ * @deprecated 1.3 - 2007/11/09
+ * @param $objectClassname \$node1 Propel object for node
+ * @param $objectClassname \$node2 Propel object for node
+ * @return bool
+ */
+ public static function isChildOfOrSiblingTo(NodeObject \$node1, NodeObject \$node2)
+ {
+ return ((\$node1->getLeftValue()>=\$node2->getLeftValue()) and (\$node1->getRightValue()<=\$node2->getRightValue()));
+ }
+";
+ }
+
+ protected function addIsEqualTo(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node1 is equal to \$node2
+ *
+ * @param $objectClassname \$node1 Propel object for node
+ * @param $objectClassname \$node2 Propel object for node
+ * @return bool
+ */
+ public static function isEqualTo(NodeObject \$node1, NodeObject \$node2)
+ {
+ \$also = true;
+ if (self::SCOPE_COL) {
+ \$also = (\$node1->getScopeIdValue() === \$node2->getScopeIdValue());
+ }
+ return \$node1->getLeftValue() == \$node2->getLeftValue() && \$node1->getRightValue() == \$node2->getRightValue() && \$also;
+ }
+";
+ }
+
+ protected function addHasParent(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node has an ancestor
+ *
+ * @param $objectClassname \$node Propel object for node
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public static function hasParent(NodeObject \$node, PropelPDO \$con = null)
+ {
+ return $peerClassname::isValid($peerClassname::retrieveParent(\$node, \$con));
+ }
+";
+ }
+
+ protected function addHasPrevSibling(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node has prev sibling
+ *
+ * @param $objectClassname \$node Propel object for node
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public static function hasPrevSibling(NodeObject \$node, PropelPDO \$con = null)
+ {
+ return $peerClassname::isValid($peerClassname::retrievePrevSibling(\$node, \$con));
+ }
+";
+ }
+
+ protected function addHasNextSibling(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node has next sibling
+ *
+ * @param $objectClassname \$node Propel object for node
+ * @param PropelPDO \$con Connection to use.
+ * @return bool
+ */
+ public static function hasNextSibling(NodeObject \$node, PropelPDO \$con = null)
+ {
+ return $peerClassname::isValid($peerClassname::retrieveNextSibling(\$node, \$con));
+ }
+";
+ }
+
+ protected function addHasChildren(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Tests if \$node has children
+ *
+ * @param $objectClassname \$node Propel object for node
+ * @return bool
+ */
+ public static function hasChildren(NodeObject \$node)
+ {
+ return ((\$node->getRightValue()-\$node->getLeftValue())>1);
+ }
+";
+ }
+
+ protected function addDeleteDescendants(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Deletes \$node and all of its descendants
+ *
+ * @param $objectClassname \$node Propel object for source node
+ * @param PropelPDO \$con Connection to use.
+ */
+ public static function deleteDescendants(NodeObject \$node, PropelPDO \$con = null)
+ {
+ \$left = \$node->getLeftValue();
+ \$right = \$node->getRightValue();
+
+ \$c = new Criteria($peerClassname::DATABASE_NAME);
+ \$c1 = \$c->getNewCriterion(self::LEFT_COL, \$left, Criteria::GREATER_THAN);
+ \$c2 = \$c->getNewCriterion(self::RIGHT_COL, \$right, Criteria::LESS_THAN);
+
+ \$c1->addAnd(\$c2);
+
+ \$c->add(\$c1);
+ if (self::SCOPE_COL) {
+ \$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
+ }
+ \$c->addAscendingOrderByColumn(self::RIGHT_COL);
+
+ \$result = $peerClassname::doDelete(\$c, \$con);
+
+ self::shiftRLValues(\$right + 1, \$left - \$right -1, \$con, \$node->getScopeIdValue());
+
+ return \$result;
+ }
+";
+ }
+
+ protected function addGetNode(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Returns a node given its primary key or the node itself
+ *
+ * @param int/$objectClassname \$node Primary key/instance of required node
+ * @param PropelPDO \$con Connection to use.
+ * @return object Propel object for model
+ */
+ public static function getNode(\$node, PropelPDO \$con = null)
+ {
+ if (is_object(\$node)) {
+ return \$node;
+ } else {
+ \$object = $peerClassname::retrieveByPK(\$node, \$con);
+ \$rtn = is_object(\$object) ? \$object : false;
+ return \$rtn;
+ }
+ }
+";
+ }
+
+ protected function addHydrateDescendants(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Hydrate recursively the descendants of the given node
+ * @param $objectClassname \$node Propel object for src node
+ * @param PDOStatement \$stmt Executed PDOStatement
+ */
+ protected static function hydrateDescendants(NodeObject \$node, PDOStatement \$stmt)
+ {
+ \$descendants = array();
+ \$children = array();
+ \$prevSibling = null;
+";
+
+ if (!$table->getChildrenColumn()) {
+ $script .= "
+ // set the class once to avoid overhead in the loop
+ \$cls = $peerClassname::getOMClass();
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null === (\$child = ".$peerClassname."::getInstanceFromPool(\$key))) {";
+
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ // class must be set each time from the record row
+ \$cls = ".$peerClassname."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$child', '$cls') . "
+ \$child->hydrate(\$row);
+ }
+
+ \$child->setLevel(\$node->getLevel() + 1);
+ \$child->setParentNode(\$node);
+ if (!empty(\$prevSibling)) {
+ \$child->setPrevSibling(\$prevSibling);
+ \$prevSibling->setNextSibling(\$child);
+ }
+
+ \$descendants[] = \$child;
+
+ if (\$child->hasChildren()) {
+ \$descendants = array_merge(\$descendants, $peerClassname::hydrateDescendants(\$child, \$stmt));
+ } else {
+ \$child->setChildren(array());
+ }
+
+ \$children[] = \$child;
+ \$prevSibling = \$child;
+
+ $peerClassname::addInstanceToPool(\$child);
+ if (\$child->getRightValue() + 1 == \$node->getRightValue()) {
+ \$child->setNextSibling(null);
+ break;
+ }
+ }
+ \$node->setChildren(\$children);
+ return \$descendants;
+ }
+";
+ }
+
+ protected function addHydrateChildren(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Hydrate the children of the given node
+ * @param $objectClassname \$node Propel object for src node
+ * @param PDOStatement \$stmt Executed PDOStatement
+ */
+ protected static function hydrateChildren(NodeObject \$node, PDOStatement \$stmt)
+ {
+ \$children = array();
+ \$prevRight = 0;
+";
+
+ if (!$table->getChildrenColumn()) {
+ $script .= "
+ // set the class once to avoid overhead in the loop
+ \$cls = $peerClassname::getOMClass();
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null === (\$child = ".$peerClassname."::getInstanceFromPool(\$key))) {";
+
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ // class must be set each time from the record row
+ \$cls = ".$peerClassname."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$child', '$cls') . "
+ \$child->hydrate(\$row);
+ }
+
+ \$child->setLevel(\$node->getLevel() + 1);
+
+ if (\$child->getRightValue() > \$prevRight) {
+ \$children[] = \$child;
+ \$prevRight = \$child->getRightValue();
+ }
+
+ if (\$child->getRightValue() + 1 == \$node->getRightValue()) {
+ break;
+ }
+ }
+ \$node->setChildren(\$children);
+ return \$children;
+ }
+";
+ }
+
+ /**
+ * @deprecated 1.3 - 2008/03/11
+ * Won't be fixed, defect by design
+ * Never trust it
+ */
+ protected function addShiftRParent(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Adds '\$delta' to all parent R values.
+ * '\$delta' can also be negative.
+ *
+ * @deprecated 1.3 - 2008/03/11
+ * @param $objectClassname \$node Propel object for parent node
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param PropelPDO \$con Connection to use.
+ */
+ protected static function shiftRParent(NodeObject \$node, \$delta, PropelPDO \$con = null)
+ {
+ if (\$node->hasParent(\$con)) {
+ \$parent = \$node->retrieveParent();
+ self::shiftRParent(\$parent, \$delta, \$con);
+ }
+ \$node->setRightValue(\$node->getRightValue() + \$delta);
+ }
+";
+ }
+
+ protected function addUpdateLoadedNode(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * Reload all already loaded nodes to sync them with updated db
+ *
+ * @param $objectClassname \$node Propel object for parent node
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param PropelPDO \$con Connection to use.
+ */
+ protected static function updateLoadedNode(NodeObject \$node, \$delta, PropelPDO \$con = null)
+ {
+ if (Propel::isInstancePoolingEnabled()) {
+ \$keys = array();
+ foreach (self::\$instances as \$obj) {
+ \$keys[] = \$obj->getPrimaryKey();
+ }
+
+ if (!empty(\$keys)) {
+ // We don't need to alter the object instance pool; we're just modifying these ones
+ // already in the pool.
+ \$criteria = new Criteria(self::DATABASE_NAME);";
+ if (count($table->getPrimaryKey()) === 1) {
+ $pkey = $table->getPrimaryKey();
+ $col = array_shift($pkey);
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($col).", \$keys, Criteria::IN);
+";
+ } else {
+ $fields = array();
+ foreach ($table->getPrimaryKey() as $k => $col) {
+ $fields[] = $this->getColumnConstant($col);
+ };
+ $script .= "
+
+ // Loop on each instances in pool
+ foreach (\$keys as \$values) {
+ // Create initial Criterion
+ \$cton = \$criteria->getNewCriterion(" . $fields[0] . ", \$values[0]);";
+ unset($fields[0]);
+ foreach ($fields as $k => $col) {
+ $script .= "
+
+ // Create next criterion
+ \$nextcton = \$criteria->getNewCriterion(" . $col . ", \$values[$k]);
+ // And merge it with the first
+ \$cton->addAnd(\$nextcton);";
+ }
+ $script .= "
+
+ // Add final Criterion to Criteria
+ \$criteria->addOr(\$cton);
+ }";
+ }
+
+ $script .= "
+ \$stmt = $peerClassname::doSelectStmt(\$criteria, \$con);
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$object = $peerClassname::getInstanceFromPool(\$key))) {";
+ $n = 0;
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNestedSetLeftKey()) {
+ $script .= "
+ \$object->setLeftValue(\$row[$n]);";
+ } else if ($col->isNestedSetRightKey()) {
+ $script .= "
+ \$object->setRightValue(\$row[$n]);";
+ }
+ $n++;
+ }
+ $script .= "
+ }
+ }
+ \$stmt->closeCursor();
+ }
+ }
+ }
+";
+ }
+
+ protected function addUpdateDBNode(&$script)
+ {
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+ $script .= "
+ /**
+ * Move \$node and its children to location \$destLeft and updates rest of tree
+ *
+ * @param $objectClassname \$node Propel object for node to update
+ * @param int \$destLeft Destination left value
+ * @param PropelPDO \$con Connection to use.
+ */
+ protected static function updateDBNode(NodeObject \$node, \$destLeft, PropelPDO \$con = null)
+ {
+ \$left = \$node->getLeftValue();
+ \$right = \$node->getRightValue();
+
+ \$treeSize = \$right - \$left +1;
+
+ self::shiftRLValues(\$destLeft, \$treeSize, \$con, \$node->getScopeIdValue());
+
+ if (\$left >= \$destLeft) { // src was shifted too?
+ \$left += \$treeSize;
+ \$right += \$treeSize;
+ }
+
+ // now there's enough room next to target to move the subtree
+ self::shiftRLRange(\$left, \$right, \$destLeft - \$left, \$con, \$node->getScopeIdValue());
+
+ // correct values after source
+ self::shiftRLValues(\$right + 1, -\$treeSize, \$con, \$node->getScopeIdValue());
+ }
+";
+ }
+
+ protected function addShiftRLValues(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Adds '\$delta' to all L and R values that are >= '\$first'. '\$delta' can also be negative.
+ *
+ * @param int \$first First node to be shifted
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param PropelPDO \$con Connection to use.
+ */
+ protected static function shiftRLValues(\$first, \$delta, PropelPDO \$con = null, \$scopeId = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$leftUpdateCol = self::LEFT_COL;
+ \$rightUpdateCol = self::RIGHT_COL;
+
+ // Shift left column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion(
+ self::LEFT_COL,
+ \$first,
+ Criteria::GREATER_EQUAL);
+
+ if (self::SCOPE_COL) {
+ \$criterion->addAnd(
+ \$whereCriteria->getNewCriterion(
+ self::SCOPE_COL,
+ \$scopeId,
+ Criteria::EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add(
+ self::LEFT_COL,
+ array('raw' => \$leftUpdateCol . ' + ?', 'value' => \$delta),
+ Criteria::CUSTOM_EQUAL);
+
+ {$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+
+ // Shift right column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion(
+ self::RIGHT_COL,
+ \$first,
+ Criteria::GREATER_EQUAL);
+
+ if (self::SCOPE_COL) {
+ \$criterion->addAnd(
+ \$whereCriteria->getNewCriterion(
+ self::SCOPE_COL,
+ \$scopeId,
+ Criteria::EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add(
+ self::RIGHT_COL,
+ array('raw' => \$rightUpdateCol . ' + ?', 'value' => \$delta),
+ Criteria::CUSTOM_EQUAL);
+
+ {$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+ }
+";
+ }
+
+ protected function addShiftRLRange(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Adds '\$delta' to all L and R values that are >= '\$first' and <= '\$last'.
+ * '\$delta' can also be negative.
+ *
+ * @param int \$first First node to be shifted (L value)
+ * @param int \$last Last node to be shifted (L value)
+ * @param int \$delta Value to be shifted by, can be negative
+ * @param PropelPDO \$con Connection to use.
+ * @return array Shifted L and R values
+ */
+ protected static function shiftRLRange(\$first, \$last, \$delta, PropelPDO \$con = null, \$scopeId = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$leftUpdateCol = substr(self::LEFT_COL, strrpos(self::LEFT_COL, '.') + 1);
+ \$rightUpdateCol = substr(self::RIGHT_COL, strrpos(self::RIGHT_COL, '.') + 1);
+
+ // Shift left column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion(self::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion(self::LEFT_COL, \$last, Criteria::LESS_EQUAL));
+ if (self::SCOPE_COL) {
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion(self::SCOPE_COL, \$scopeId, Criteria::EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add(
+ self::LEFT_COL,
+ array('raw' => \$leftUpdateCol . ' + ?', 'value' => \$delta),
+ Criteria::CUSTOM_EQUAL);
+
+ {$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+
+ // Shift right column values
+ \$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criterion = \$whereCriteria->getNewCriterion(self::RIGHT_COL, \$first, Criteria::GREATER_EQUAL);
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion(self::RIGHT_COL, \$last, Criteria::LESS_EQUAL));
+ if (self::SCOPE_COL) {
+ \$criterion->addAnd(\$whereCriteria->getNewCriterion(self::SCOPE_COL, \$scopeId, Criteria::EQUAL));
+ }
+ \$whereCriteria->add(\$criterion);
+
+ \$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$valuesCriteria->add(
+ self::RIGHT_COL,
+ array('raw' => \$rightUpdateCol . ' + ?', 'value' => \$delta),
+ Criteria::CUSTOM_EQUAL);
+
+ {$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
+
+ return array('left' => \$first + \$delta, 'right' => \$last + \$delta);
+ }
+";
+ }
+
+} // PHP5NestedSetPeerBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5NodeBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5NodeBuilder.php
new file mode 100644
index 000000000..1f5b0eca1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5NodeBuilder.php
@@ -0,0 +1,1104 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5NodeBuilder extends ObjectBuilder
+{
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubNodeBuilder()->getUnprefixedClassname();
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $script .= "
+/**
+ * Base class that represents a row from the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." implements IteratorAggregate {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the basic OM class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $table = $this->getTable();
+
+ $this->addAttributes($script);
+
+ $this->addConstructor($script);
+
+ $this->addCallOverload($script);
+ $this->addSetIteratorOptions($script);
+ $this->addGetIterator($script);
+
+ $this->addGetNodeObj($script);
+ $this->addGetNodePath($script);
+ $this->addGetNodeIndex($script);
+ $this->addGetNodeLevel($script);
+
+ $this->addHasChildNode($script);
+ $this->addGetChildNodeAt($script);
+ $this->addGetFirstChildNode($script);
+ $this->addGetLastChildNode($script);
+ $this->addGetSiblingNode($script);
+
+ $this->addGetParentNode($script);
+ $this->addGetAncestors($script);
+ $this->addIsRootNode($script);
+
+ $this->addSetNew($script);
+ $this->addSetDeleted($script);
+ $this->addAddChildNode($script);
+ $this->addMoveChildNode($script);
+ $this->addSave($script);
+
+ $this->addDelete($script);
+ $this->addEquals($script);
+
+ $this->addAttachParentNode($script);
+ $this->addAttachChildNode($script);
+ $this->addDetachParentNode($script);
+ $this->addDetachChildNode($script);
+ $this->addShiftChildNodes($script);
+ $this->addInsertNewChildNode($script);
+
+ $this->addAdjustStatus($script);
+ $this->addAdjustNodePath($script);
+
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+
+ /**
+ * Adds class attributes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAttributes(&$script)
+ {
+ $script .= "
+ /**
+ * @var ".$this->getStubObjectBuilder()->getClassname()." object wrapped by this node.
+ */
+ protected \$obj = null;
+
+ /**
+ * The parent node for this node.
+ * @var ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ protected \$parentNode = null;
+
+ /**
+ * Array of child nodes for this node. Nodes indexes are one-based.
+ * @var array
+ */
+ protected \$childNodes = array();
+";
+ }
+
+ /**
+ * Adds the constructor.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addConstructor(&$script)
+ {
+ $script .= "
+ /**
+ * Constructor.
+ *
+ * @param ".$this->getStubObjectBuilder()->getClassname()." \$obj Object wrapped by this node.
+ */
+ public function __construct(\$obj = null)
+ {
+ if (\$obj !== null) {
+ \$this->obj = \$obj;
+ } else {
+ \$setNodePath = 'set' . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_PHPNAME;
+ \$this->obj = new ".$this->getStubObjectBuilder()->getClassname()."();
+ \$this->obj->\$setNodePath('0');
+ }
+ }
+";
+ }
+
+
+
+ protected function addCallOverload(&$script)
+ {
+ $script .= "
+ /**
+ * Convenience overload for wrapped object methods.
+ *
+ * @param string Method name to call on wrapped object.
+ * @param mixed Parameter accepted by wrapped object set method.
+ * @return mixed Return value of wrapped object method.
+ * @throws PropelException Fails if method is not defined for wrapped object.
+ */
+ public function __call(\$name, \$parms)
+ {
+ if (method_exists(\$this->obj, \$name))
+ return call_user_func_array(array(\$this->obj, \$name), \$parms);
+ else
+ throw new PropelException('get method not defined: \$name');
+ }
+";
+ }
+
+ protected function addSetIteratorOptions(&$script)
+ {
+ $script .= "
+
+ /**
+ * Sets the default options for iterators created from this object.
+ * The options are specified in map format. The following options
+ * are supported by all iterators. Some iterators may support other
+ * options:
+ *
+ * \"querydb\" - True if nodes should be retrieved from database.
+ * \"con\" - Connection to use if retrieving from database.
+ *
+ * @param string Type of iterator to use (\"pre\", \"post\", \"level\").
+ * @param array Map of option name => value.
+ * @return void
+ * @todo Implement other iterator types (i.e. post-order, level, etc.)
+ */
+ public function setIteratorOptions(\$type, \$opts)
+ {
+ \$this->itType = \$type;
+ \$this->itOpts = \$opts;
+ }
+";
+ }
+
+ protected function addGetIterator(&$script)
+ {
+ $script .= "
+ /**
+ * Returns a pre-order iterator for this node and its children.
+ *
+ * @param string Type of iterator to use (\"pre\", \"post\", \"level\")
+ * @param array Map of option name => value.
+ * @return NodeIterator
+ */
+ public function getIterator(\$type = null, \$opts = null)
+ {
+ if (\$type === null)
+ \$type = (isset(\$this->itType) ? \$this->itType : 'Pre');
+
+ if (\$opts === null)
+ \$opts = (isset(\$this->itOpts) ? \$this->itOpts : array());
+
+ \$itclass = ucfirst(strtolower(\$type)) . 'OrderNodeIterator';
+
+ require_once('propel/om/' . \$itclass . '.php');
+ return new \$itclass(\$this, \$opts);
+ }
+";
+ }
+
+ protected function addGetNodeObj(&$script)
+ {
+ $script .= "
+ /**
+ * Returns the object wrapped by this class.
+ * @return ".$this->getStubObjectBuilder()->getClassname()."
+ */
+ public function getNodeObj()
+ {
+ return \$this->obj;
+ }
+";
+ }
+
+ protected function addGetNodePath(&$script)
+ {
+ $script .= "
+ /**
+ * Convenience method for retrieving nodepath.
+ * @return string
+ */
+ public function getNodePath()
+ {
+ \$getNodePath = 'get' . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_PHPNAME;
+ return \$this->obj->\$getNodePath();
+ }
+";
+ }
+
+ protected function addGetNodeIndex(&$script)
+ {
+ $script .= "
+ /**
+ * Returns one-based node index among siblings.
+ * @return int
+ */
+ public function getNodeIndex()
+ {
+ \$npath =& \$this->getNodePath();
+ \$sep = strrpos(\$npath, ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP);
+ return (int) (\$sep !== false ? substr(\$npath, \$sep+1) : \$npath);
+ }
+";
+ }
+
+ protected function addGetNodeLevel(&$script)
+ {
+ $script .= "
+ /**
+ * Returns one-based node level within tree (root node is level 1).
+ * @return int
+ */
+ public function getNodeLevel()
+ {
+ return (substr_count(\$this->getNodePath(), ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP) + 1);
+ }
+";
+ }
+
+ protected function addHasChildNode(&$script)
+ {
+ $script .= "
+ /**
+ * Returns true if specified node is a child of this node. If recurse is
+ * true, checks if specified node is a descendant of this node.
+ *
+ * @param ".$this->getStubNodeBuilder()->getClassname()." Node to look for.
+ * @param boolean True if strict comparison should be used.
+ * @param boolean True if all descendants should be checked.
+ * @return boolean
+ */
+ public function hasChildNode(\$node, \$strict = false, \$recurse = false)
+ {
+ foreach (\$this->childNodes as \$childNode)
+ {
+ if (\$childNode->equals(\$node, \$strict))
+ return true;
+
+ if (\$recurse && \$childNode->hasChildNode(\$node, \$recurse))
+ return true;
+ }
+
+ return false;
+ }
+";
+ }
+
+ protected function addGetChildNodeAt(&$script)
+ {
+ $script .= "
+ /**
+ * Returns child node at one-based index. Retrieves from database if not
+ * loaded yet.
+ *
+ * @param int One-based child node index.
+ * @param boolean True if child should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ * @return ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ public function getChildNodeAt(\$i, \$querydb = false, PropelPDO \$con = null)
+ {
+ if (\$querydb &&
+ !\$this->obj->isNew() &&
+ !\$this->obj->isDeleted() &&
+ !isset(\$this->childNodes[\$i]))
+ {
+ \$criteria = new Criteria(".$this->getStubPeerBuilder()->getClassname()."::DATABASE_NAME);
+ \$criteria->add(".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_COLNAME, \$this->getNodePath() . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP . \$i, Criteria::EQUAL);
+
+ if (\$childObj = ".$this->getStubPeerBuilder()->getClassname()."::doSelectOne(\$criteria, \$con))
+ \$this->attachChildNode(new ".$this->getStubNodeBuilder()->getClassname()."(\$childObj));
+ }
+
+ return (isset(\$this->childNodes[\$i]) ? \$this->childNodes[\$i] : null);
+ }
+";
+ }
+
+ protected function addGetFirstChildNode(&$script)
+ {
+ $script .= "
+ /**
+ * Returns first child node (if any). Retrieves from database if not loaded yet.
+ *
+ * @param boolean True if child should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ * @return ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ public function getFirstChildNode(\$querydb = false, PropelPDO \$con = null)
+ {
+ return \$this->getChildNodeAt(1, \$querydb, \$con);
+ }
+";
+ }
+
+ protected function addGetLastChildNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Returns last child node (if any).
+ *
+ * @param boolean True if child should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ */
+ public function getLastChildNode(\$querydb = false, PropelPDO \$con = null)
+ {
+ \$lastNode = null;
+
+ if (\$this->obj->isNew() || \$this->obj->isDeleted())
+ {
+ end(\$this->childNodes);
+ \$lastNode = (count(\$this->childNodes) ? current(\$this->childNodes) : null);
+ }
+ else if (\$querydb)
+ {
+ \$db = Propel::getDb($peerClassname::DATABASE_NAME);
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criteria->add($nodePeerClassname::NPATH_COLNAME, \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . '%', Criteria::LIKE);
+ \$criteria->addAnd($nodePeerClassname::NPATH_COLNAME, \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . '%' . $nodePeerClassname::NPATH_SEP . '%', Criteria::NOT_LIKE);
+ $peerClassname::addSelectColumns(\$criteria);
+ \$criteria->addAsColumn('npathlen', \$db->strLength($nodePeerClassname::NPATH_COLNAME));
+ \$criteria->addDescendingOrderByColumn('npathlen');
+ \$criteria->addDescendingOrderByColumn($nodePeerClassname::NPATH_COLNAME);
+
+ \$lastObj = $peerClassname::doSelectOne(\$criteria, \$con);
+
+ if (\$lastObj !== null)
+ {
+ \$lastNode = new ".$this->getStubNodeBuilder()->getClassname()."(\$lastObj);
+
+ end(\$this->childNodes);
+ \$endNode = (count(\$this->childNodes) ? current(\$this->childNodes) : null);
+
+ if (\$endNode)
+ {
+ if (\$endNode->getNodePath() > \$lastNode->getNodePath())
+ throw new PropelException('Cached child node inconsistent with database.');
+ else if (\$endNode->getNodePath() == \$lastNode->getNodePath())
+ \$lastNode = \$endNode;
+ else
+ \$this->attachChildNode(\$lastNode);
+ }
+ else
+ {
+ \$this->attachChildNode(\$lastNode);
+ }
+ }
+ }
+
+ return \$lastNode;
+ }
+";
+ }
+
+ protected function addGetSiblingNode(&$script)
+ {
+ $script .= "
+ /**
+ * Returns next (or previous) sibling node or null. Retrieves from database if
+ * not loaded yet.
+ *
+ * @param boolean True if previous sibling should be returned.
+ * @param boolean True if sibling should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ * @return ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ public function getSiblingNode(\$prev = false, \$querydb = false, PropelPDO \$con = null)
+ {
+ \$nidx = \$this->getNodeIndex();
+
+ if (\$this->isRootNode())
+ {
+ return null;
+ }
+ else if (\$prev)
+ {
+ if (\$nidx > 1 && (\$parentNode = \$this->getParentNode(\$querydb, \$con)))
+ return \$parentNode->getChildNodeAt(\$nidx-1, \$querydb, \$con);
+ else
+ return null;
+ }
+ else
+ {
+ if (\$parentNode = \$this->getParentNode(\$querydb, \$con))
+ return \$parentNode->getChildNodeAt(\$nidx+1, \$querydb, \$con);
+ else
+ return null;
+ }
+ }
+";
+ }
+
+ protected function addGetParentNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Returns parent node. Loads from database if not cached yet.
+ *
+ * @param boolean True if parent should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ * @return ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ public function getParentNode(\$querydb = true, PropelPDO \$con = null)
+ {
+ if (\$querydb &&
+ \$this->parentNode === null &&
+ !\$this->isRootNode() &&
+ !\$this->obj->isNew() &&
+ !\$this->obj->isDeleted())
+ {
+ \$npath =& \$this->getNodePath();
+ \$sep = strrpos(\$npath, $nodePeerClassname::NPATH_SEP);
+ \$ppath = substr(\$npath, 0, \$sep);
+
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criteria->add($nodePeerClassname::NPATH_COLNAME, \$ppath, Criteria::EQUAL);
+
+ if (\$parentObj = $peerClassname::doSelectOne(\$criteria, \$con))
+ {
+ \$parentNode = new ".$this->getStubNodeBuilder()->getClassname()."(\$parentObj);
+ \$parentNode->attachChildNode(\$this);
+ }
+ }
+
+ return \$this->parentNode;
+ }
+";
+ }
+
+ protected function addGetAncestors(&$script)
+ {
+ $script .= "
+ /**
+ * Returns an array of all ancestor nodes, starting with the root node
+ * first.
+ *
+ * @param boolean True if ancestors should be retrieved from database.
+ * @param PropelPDO Connection to use if retrieving from database.
+ * @return array
+ */
+ public function getAncestors(\$querydb = false, PropelPDO \$con = null)
+ {
+ \$ancestors = array();
+ \$parentNode = \$this;
+
+ while (\$parentNode = \$parentNode->getParentNode(\$querydb, \$con))
+ array_unshift(\$ancestors, \$parentNode);
+
+ return \$ancestors;
+ }
+";
+ }
+
+ protected function addIsRootNode(&$script)
+ {
+ $script .= "
+ /**
+ * Returns true if node is the root node of the tree.
+ * @return boolean
+ */
+ public function isRootNode()
+ {
+ return (\$this->getNodePath() === '1');
+ }
+";
+ }
+
+ protected function addSetNew(&$script)
+ {
+ $script .= "
+ /**
+ * Changes the state of the object and its descendants to 'new'.
+ * Also changes the node path to '0' to indicate that it is not a
+ * stored node.
+ *
+ * @param boolean
+ * @return void
+ */
+ public function setNew(\$b)
+ {
+ \$this->adjustStatus('new', \$b);
+ \$this->adjustNodePath(\$this->getNodePath(), '0');
+ }
+";
+ }
+
+ protected function addSetDeleted(&$script)
+ {
+ $script .= "
+ /**
+ * Changes the state of the object and its descendants to 'deleted'.
+ *
+ * @param boolean
+ * @return void
+ */
+ public function setDeleted(\$b)
+ {
+ \$this->adjustStatus('deleted', \$b);
+ }
+";
+ }
+
+ protected function addAddChildNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Adds the specified node (and its children) as a child to this node. If a
+ * valid \$beforeNode is specified, the node will be inserted in front of
+ * \$beforeNode. If \$beforeNode is not specified the node will be appended to
+ * the end of the child nodes.
+ *
+ * @param ".$this->getStubNodeBuilder()->getClassname()." Node to add.
+ * @param ".$this->getStubNodeBuilder()->getClassname()." Node to insert before.
+ * @param PropelPDO Connection to use.
+ */
+ public function addChildNode(\$node, \$beforeNode = null, PropelPDO \$con = null)
+ {
+ if (\$this->obj->isNew() && !\$node->obj->isNew())
+ throw new PropelException('Cannot add stored nodes to a new node.');
+
+ if (\$this->obj->isDeleted() || \$node->obj->isDeleted())
+ throw new PropelException('Cannot add children in a deleted state.');
+
+ if (\$this->hasChildNode(\$node))
+ throw new PropelException('Node is already a child of this node.');
+
+ if (\$beforeNode && !\$this->hasChildNode(\$beforeNode))
+ throw new PropelException('Invalid beforeNode.');
+
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+
+ if (!\$this->obj->isNew()) \$con->beginTransaction();
+
+ try {
+ if (\$beforeNode)
+ {
+ // Inserting before a node.
+ \$childIdx = \$beforeNode->getNodeIndex();
+ \$this->shiftChildNodes(1, \$beforeNode->getNodeIndex(), \$con);
+ }
+ else
+ {
+ // Appending child node.
+ if (\$lastNode = \$this->getLastChildNode(true, \$con))
+ \$childIdx = \$lastNode->getNodeIndex()+1;
+ else
+ \$childIdx = 1;
+ }
+
+ // Add the child (and its children) at the specified index.
+
+ if (!\$this->obj->isNew() && \$node->obj->isNew())
+ {
+ \$this->insertNewChildNode(\$node, \$childIdx, \$con);
+ }
+ else
+ {
+ // \$this->isNew() && \$node->isNew() ||
+ // !\$this->isNew() && !node->isNew()
+
+ \$srcPath = \$node->getNodePath();
+ \$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$childIdx;
+
+ if (!\$node->obj->isNew())
+ {
+ $nodePeerClassname::moveNodeSubTree(\$srcPath, \$dstPath, \$con);
+ \$parentNode = \$node->getParentNode(true, \$con);
+ }
+ else
+ {
+ \$parentNode = \$node->getParentNode();
+ }
+
+ if (\$parentNode)
+ {
+ \$parentNode->detachChildNode(\$node);
+ \$parentNode->shiftChildNodes(-1, \$node->getNodeIndex()+1, \$con);
+ }
+
+ \$node->adjustNodePath(\$srcPath, \$dstPath);
+ }
+
+ if (!\$this->obj->isNew()) \$con->commit();
+
+ \$this->attachChildNode(\$node);
+
+ } catch (SQLException \$e) {
+ if (!\$this->obj->isNew()) \$con->rollBack();
+ throw new PropelException(\$e);
+ }
+ }
+";
+ }
+
+ protected function addMoveChildNode(&$script)
+ {
+ $script .= "
+ /**
+ * Moves the specified child node in the specified direction.
+ *
+ * @param ".$this->getStubNodeBuilder()->getClassname()." Node to move.
+ * @param int Number of spaces to move among siblings (may be negative).
+ * @param PropelPDO Connection to use.
+ * @throws PropelException
+ */
+ public function moveChildNode(\$node, \$direction, PropelPDO \$con = null)
+ {
+ throw new PropelException('moveChildNode() not implemented yet.');
+ }
+";
+ }
+
+
+ protected function addSave(&$script)
+ {
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Saves modified object data to the datastore.
+ *
+ * @param boolean If true, descendants will be saved as well.
+ * @param PropelPDO Connection to use.
+ */
+ public function save(\$recurse = false, PropelPDO \$con = null)
+ {
+ if (\$this->obj->isDeleted())
+ throw new PropelException('Cannot save deleted node.');
+
+ if (substr(\$this->getNodePath(), 0, 1) == '0')
+ throw new PropelException('Cannot save unattached node.');
+
+ if (\$this->obj->isColumnModified($nodePeerClassname::NPATH_COLNAME))
+ throw new PropelException('Cannot save manually modified node path.');
+
+ \$this->obj->save(\$con);
+
+ if (\$recurse)
+ {
+ foreach (\$this->childNodes as \$childNode)
+ \$childNode->save(\$recurse, \$con);
+ }
+ }
+";
+ }
+
+
+ protected function addDelete(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Removes this object and all descendants from datastore.
+ *
+ * @param PropelPDO Connection to use.
+ * @return void
+ * @throws PropelException
+ */
+ public function delete(PropelPDO \$con = null)
+ {
+ if (\$this->obj->isDeleted()) {
+ throw new PropelException('This node has already been deleted.');
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ if (!\$this->obj->isNew()) {
+ $nodePeerClassname::deleteNodeSubTree(\$this->getNodePath(), \$con);
+ }
+
+ if (\$parentNode = \$this->getParentNode(true, \$con)) {
+ \$parentNode->detachChildNode(\$this);
+ \$parentNode->shiftChildNodes(-1, \$this->getNodeIndex()+1, \$con);
+ }
+
+ \$this->setDeleted(true);
+ }
+";
+ }
+
+ protected function addEquals(&$script)
+ {
+ $nodeClassname = $this->getStubNodeBuilder()->getClassname();
+ $script .= "
+ /**
+ * Compares the object wrapped by this node with that of another node. Use
+ * this instead of equality operators to prevent recursive dependency
+ * errors.
+ *
+ * @param $nodeClassname Node to compare.
+ * @param boolean True if strict comparison should be used.
+ * @return boolean
+ */
+ public function equals(\$node, \$strict = false)
+ {
+ if (\$strict) {
+ return (\$this->obj === \$node->obj);
+ } else {
+ return (\$this->obj == \$node->obj);
+ }
+ }
+";
+ }
+
+ protected function addAttachParentNode(&$script)
+ {
+ $nodeClassname = $this->getStubNodeBuilder()->getClassname();
+ $script .= "
+ /**
+ * This method is used internally when constructing the tree structure
+ * from the database. To set the parent of a node, you should call
+ * addChildNode() on the parent.
+ *
+ * @param $nodeClassname Parent node to attach.
+ * @return void
+ * @throws PropelException
+ */
+ public function attachParentNode(\$node)
+ {
+ if (!\$node->hasChildNode(\$this, true))
+ throw new PropelException('Failed to attach parent node for non-child.');
+
+ \$this->parentNode = \$node;
+ }
+";
+ }
+
+
+ protected function addAttachChildNode(&$script)
+ {
+ $nodeClassname = $this->getStubNodeBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * This method is used internally when constructing the tree structure
+ * from the database. To add a child to a node you should call the
+ * addChildNode() method instead.
+ *
+ * @param $nodeClassname Child node to attach.
+ * @return void
+ * @throws PropelException
+ */
+ public function attachChildNode(\$node)
+ {
+ if (\$this->hasChildNode(\$node))
+ throw new PropelException('Failed to attach child node. Node already exists.');
+
+ if (\$this->obj->isDeleted() || \$node->obj->isDeleted())
+ throw new PropelException('Failed to attach node in deleted state.');
+
+ if (\$this->obj->isNew() && !\$node->obj->isNew())
+ throw new PropelException('Failed to attach non-new child to new node.');
+
+ if (!\$this->obj->isNew() && \$node->obj->isNew())
+ throw new PropelException('Failed to attach new child to non-new node.');
+
+ if (\$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$node->getNodeIndex() != \$node->getNodePath())
+ throw new PropelException('Failed to attach child node. Node path mismatch.');
+
+ \$this->childNodes[\$node->getNodeIndex()] = \$node;
+ ksort(\$this->childNodes);
+
+ \$node->attachParentNode(\$this);
+ }
+";
+ }
+
+ protected function addDetachParentNode(&$script)
+ {
+ $nodeClassname = $this->getStubNodeBuilder()->getClassname();
+ $script .= "
+ /**
+ * This method is used internally when deleting nodes. It is used to break
+ * the link to this node's parent.
+ * @param $nodeClassname Parent node to detach from.
+ * @return void
+ * @throws PropelException
+ */
+ public function detachParentNode(\$node)
+ {
+ if (!\$node->hasChildNode(\$this, true))
+ throw new PropelException('Failed to detach parent node from non-child.');
+
+ unset(\$node->childNodes[\$this->getNodeIndex()]);
+ \$this->parentNode = null;
+ }
+";
+ }
+
+ protected function addDetachChildNode(&$script)
+ {
+ $script .= "
+ /**
+ * This method is used internally when deleting nodes. It is used to break
+ * the link to this between this node and the specified child.
+ * @param ".$this->getStubNodeBuilder()->getClassname()." Child node to detach.
+ * @return void
+ * @throws PropelException
+ */
+ public function detachChildNode(\$node)
+ {
+ if (!\$this->hasChildNode(\$node, true))
+ throw new PropelException('Failed to detach non-existent child node.');
+
+ unset(\$this->childNodes[\$node->getNodeIndex()]);
+ \$node->parentNode = null;
+ }
+";
+ }
+
+ protected function addShiftChildNodes(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Shifts child nodes in the specified direction and offset index. This
+ * method assumes that there is already space available in the
+ * direction/offset indicated.
+ *
+ * @param int Direction/# spaces to shift. 1=leftshift, 1=rightshift
+ * @param int Node index to start shift at.
+ * @param PropelPDO The connection to be used.
+ * @return void
+ * @throws PropelException
+ */
+ protected function shiftChildNodes(\$direction, \$offsetIdx, PropelPDO \$con)
+ {
+ if (\$this->obj->isDeleted())
+ throw new PropelException('Cannot shift nodes for deleted object');
+
+ \$lastNode = \$this->getLastChildNode(true, \$con);
+ \$lastIdx = (\$lastNode !== null ? \$lastNode->getNodeIndex() : 0);
+
+ if (\$lastNode === null || \$offsetIdx > \$lastIdx)
+ return;
+
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+
+ if (!\$this->obj->isNew())
+ {
+ // Shift nodes in database.
+
+ \$con->beginTransaction();
+
+ try {
+ \$n = \$lastIdx - \$offsetIdx + 1;
+ \$i = \$direction < 1 ? \$offsetIdx : \$lastIdx;
+
+ while (\$n--)
+ {
+ \$srcPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$i; // 1.2.2
+ \$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . (\$i+\$direction); // 1.2.3
+
+ $nodePeerClassname::moveNodeSubTree(\$srcPath, \$dstPath, \$con);
+
+ \$i -= \$direction;
+ }
+
+ \$con->commit();
+
+ } catch (SQLException \$e) {
+ \$con->rollBack();
+ throw new PropelException(\$e);
+ }
+ }
+
+ // Shift the in-memory objects.
+
+ \$n = \$lastIdx - \$offsetIdx + 1;
+ \$i = \$direction < 1 ? \$offsetIdx : \$lastIdx;
+
+ while (\$n--)
+ {
+ if (isset(\$this->childNodes[\$i]))
+ {
+ \$srcPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$i; // 1.2.2
+ \$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . (\$i+\$direction); // 1.2.3
+
+ \$this->childNodes[\$i+\$direction] = \$this->childNodes[\$i];
+ \$this->childNodes[\$i+\$direction]->adjustNodePath(\$srcPath, \$dstPath);
+
+ unset(\$this->childNodes[\$i]);
+ }
+
+ \$i -= \$direction;
+ }
+
+ ksort(\$this->childNodes);
+ }
+";
+ }
+
+ protected function addInsertNewChildNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Inserts the node and its children at the specified childIdx.
+ *
+ * @param $nodeClassname Node to insert.
+ * @param int One-based child index to insert at.
+ * @param PropelPDO Connection to use.
+ * @param void
+ */
+ protected function insertNewChildNode(\$node, \$childIdx, PropelPDO \$con)
+ {
+ if (!\$node->obj->isNew())
+ throw new PropelException('Failed to insert non-new node.');
+
+ \$setNodePath = 'set' . $nodePeerClassname::NPATH_PHPNAME;
+
+ \$node->obj->\$setNodePath(\$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$childIdx);
+ \$node->obj->save(\$con);
+
+ \$i = 1;
+ foreach (\$node->childNodes as \$childNode)
+ \$node->insertNewChildNode(\$childNode, \$i++, \$con);
+ }
+";
+ }
+
+ protected function addAdjustStatus(&$script)
+ {
+ $script .= "
+ /**
+ * Adjust new/deleted status of node and all children.
+ *
+ * @param string Status to change ('New' or 'Deleted')
+ * @param boolean Value for status.
+ * @return void
+ */
+ protected function adjustStatus(\$status, \$b)
+ {
+ \$setStatus = 'set' . \$status;
+
+ \$this->obj->\$setStatus(\$b);
+
+ foreach (\$this->childNodes as \$childNode)
+ \$childNode->obj->\$setStatus(\$b);
+ }
+";
+ }
+
+ protected function addAdjustNodePath(&$script)
+ {
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $script .= "
+ /**
+ * Adjust path of node and all children. This is used internally when
+ * inserting/moving nodes.
+ *
+ * @param string Section of old path to change.
+ * @param string New section to replace old path with.
+ * @return void
+ */
+ protected function adjustNodePath(\$oldBasePath, \$newBasePath)
+ {
+ \$setNodePath = 'set' . $nodePeerClassname::NPATH_PHPNAME;
+
+ \$this->obj->\$setNodePath(\$newBasePath . substr(\$this->getNodePath(), strlen(\$oldBasePath)));
+ \$this->obj->resetModified($nodePeerClassname::NPATH_COLNAME);
+
+ foreach (\$this->childNodes as \$childNode)
+ \$childNode->adjustNodePath(\$oldBasePath, \$newBasePath);
+ }
+";
+ }
+
+} // PHP5NodeObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5NodePeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5NodePeerBuilder.php
new file mode 100644
index 000000000..b84021e70
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5NodePeerBuilder.php
@@ -0,0 +1,754 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5NodePeerBuilder extends PeerBuilder
+{
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubNodePeerBuilder()->getUnprefixedClassname();
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $script .= "
+/**
+ * Base static class for performing query operations on the tree contained by the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the basic OM class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $table = $this->getTable();
+
+ // FIXME
+ // - Probably the build needs to be customized for supporting
+ // tables that are "aliases". -- definitely a fringe usecase, though.
+
+ $this->addConstants($script);
+
+ $this->addIsCodeBase($script);
+
+ $this->addRetrieveMethods($script);
+
+ $this->addCreateNewRootNode($script);
+ $this->addInsertNewRootNode($script);
+ $this->addMoveNodeSubTree($script);
+ $this->addDeleteNodeSubTree($script);
+
+ $this->addBuildFamilyCriteria($script);
+ $this->addBuildTree($script);
+
+ $this->addPopulateNodes($script);
+
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+ protected function addConstants(&$script)
+ {
+ $table = $this->getTable();
+
+ $npath_colname = '';
+ $npath_phpname = '';
+ $npath_len = 0;
+ $npath_sep = '';
+ foreach ($table->getColumns() as $col) {
+ if ($col->isNodeKey()) {
+ $npath_colname = $table->getName() . '.' . strtoupper($col->getName());
+ $npath_phpname = $col->getPhpName();
+ $npath_len = $col->getSize();
+ $npath_sep = $col->getNodeKeySep();
+ break;
+ }
+ }
+ $script .= "
+ const NPATH_COLNAME = '$npath_colname';
+ const NPATH_PHPNAME = '$npath_phpname';
+ const NPATH_SEP = '$npath_sep';
+ const NPATH_LEN = $npath_len;
+";
+ }
+
+
+ protected function addIsCodeBase(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Temp function for CodeBase hacks that will go away.
+ */
+ public static function isCodeBase(\$con = null)
+ {
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
+
+ return (get_class(\$con) == 'ODBCConnection' &&
+ get_class(\$con->getAdapter()) == 'CodeBaseAdapter');
+ }
+";
+ }
+
+
+ protected function addCreateNewRootNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Create a new Node at the top of tree. This method will destroy any
+ * existing root node (along with its children).
+ *
+ * Use at your own risk!
+ *
+ * @param $objectClassname Object wrapped by new node.
+ * @param PropelPDO Connection to use.
+ * @return $nodeObjectClassname
+ * @throws PropelException
+ */
+ public static function createNewRootNode(\$obj, PropelPDO \$con = null)
+ {
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+
+ \$con->beginTransaction();
+
+ try {
+ self::deleteNodeSubTree('1', \$con);
+
+ \$setNodePath = 'set' . self::NPATH_PHPNAME;
+
+ \$obj->\$setNodePath('1');
+ \$obj->save(\$con);
+
+ \$con->commit();
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }
+
+ return new $nodeObjectClassname(\$obj);
+ }
+";
+ }
+
+ protected function addInsertNewRootNode(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Inserts a new Node at the top of tree. Any existing root node (along with
+ * its children) will be made a child of the new root node. This is a
+ * safer alternative to createNewRootNode().
+ *
+ * @param $objectClassname Object wrapped by new node.
+ * @param PropelPDO Connection to use.
+ * @return $nodeObjectClassname
+ * @throws PropelException
+ */
+ public static function insertNewRootNode(\$obj, PropelPDO \$con = null)
+ {
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+
+ \$con->beginTransaction();
+ try {
+ // Move root tree to an invalid node path.
+ $nodePeerClassname::moveNodeSubTree('1', '0', \$con);
+
+ \$setNodePath = 'set' . self::NPATH_PHPNAME;
+
+ // Insert the new root node.
+ \$obj->\$setNodePath('1');
+ \$obj->save(\$con);
+
+ // Move the old root tree as a child of the new root.
+ $nodePeerClassname::moveNodeSubTree('0', '1' . self::NPATH_SEP . '1', \$con);
+
+ \$con->commit();
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }
+
+ return new $nodeObjectClassname(\$obj);
+ }
+";
+ }
+
+ /**
+ * Adds the methods for retrieving nodes.
+ */
+ protected function addRetrieveMethods(&$script)
+ {
+ $this->addRetrieveNodes($script);
+ $this->addRetrieveNodeByPK($script);
+ $this->addRetrieveNodeByNP($script);
+ $this->addRetrieveRootNode($script);
+
+ }
+
+ protected function addRetrieveNodes(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Retrieves an array of tree nodes based on specified criteria. Optionally
+ * includes all parent and/or child nodes of the matching nodes.
+ *
+ * @param Criteria Criteria to use.
+ * @param boolean True if ancestors should also be retrieved.
+ * @param boolean True if descendants should also be retrieved.
+ * @param PropelPDO Connection to use.
+ * @return array Array of root nodes.
+ */
+ public static function retrieveNodes(\$criteria, \$ancestors = false, \$descendants = false, PropelPDO \$con = null)
+ {
+ \$criteria = $nodePeerClassname::buildFamilyCriteria(\$criteria, \$ancestors, \$descendants);
+ \$stmt = ".$this->getStubPeerBuilder()->getClassname()."::doSelectStmt(\$criteria, \$con);
+ return self::populateNodes(\$stmt, \$criteria);
+ }
+";
+ }
+
+ protected function addRetrieveNodeByPK(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Retrieves a tree node based on a primary key. Optionally includes all
+ * parent and/or child nodes of the matching node.
+ *
+ * @param mixed $objectClassname primary key (array for composite keys)
+ * @param boolean True if ancestors should also be retrieved.
+ * @param boolean True if descendants should also be retrieved.
+ * @param PropelPDO Connection to use.
+ * @return $nodeObjectClassname
+ */
+ public static function retrieveNodeByPK(\$pk, \$ancestors = false, \$descendants = false, PropelPDO \$con = null)
+ {
+ throw new PropelException('retrieveNodeByPK() not implemented yet.');
+ }
+";
+ }
+
+ protected function addRetrieveNodeByNP(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Retrieves a tree node based on a node path. Optionally includes all
+ * parent and/or child nodes of the matching node.
+ *
+ * @param string Node path to retrieve.
+ * @param boolean True if ancestors should also be retrieved.
+ * @param boolean True if descendants should also be retrieved.
+ * @param PropelPDO Connection to use.
+ * @return $objectClassname
+ */
+ public static function retrieveNodeByNP(\$np, \$ancestors = false, \$descendants = false, PropelPDO \$con = null)
+ {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criteria->add(self::NPATH_COLNAME, \$np, Criteria::EQUAL);
+ \$criteria = self::buildFamilyCriteria(\$criteria, \$ancestors, \$descendants);
+ \$stmt = $peerClassname::doSelectStmt(\$criteria, \$con);
+ \$nodes = self::populateNodes(\$stmt, \$criteria);
+ return (count(\$nodes) == 1 ? \$nodes[0] : null);
+ }
+";
+ }
+
+ protected function addRetrieveRootNode(&$script)
+ {
+ $script .= "
+ /**
+ * Retrieves the root node.
+ *
+ * @param string Node path to retrieve.
+ * @param boolean True if descendants should also be retrieved.
+ * @param PropelPDO Connection to use.
+ * @return ".$this->getStubNodeBuilder()->getClassname()."
+ */
+ public static function retrieveRootNode(\$descendants = false, PropelPDO \$con = null)
+ {
+ return self::retrieveNodeByNP('1', false, \$descendants, \$con);
+ }
+";
+ }
+
+ protected function addMoveNodeSubTree(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Moves the node subtree at srcpath to the dstpath. This method is intended
+ * for internal use by the BaseNode object. Note that it does not check for
+ * preexisting nodes at the dstpath. It also does not update the node path
+ * of any Node objects that might currently be in memory.
+ *
+ * Use at your own risk!
+ *
+ * @param string Source node path to move (root of the src subtree).
+ * @param string Destination node path to move to (root of the dst subtree).
+ * @param PropelPDO Connection to use.
+ * @return void
+ * @throws PropelException
+ * @todo This is currently broken for simulated 'onCascadeDelete's.
+ * @todo Need to abstract the SQL better. The CONCAT sql function doesn't
+ * seem to be standardized (i.e. mssql), so maybe it needs to be moved
+ * to DBAdapter.
+ */
+ public static function moveNodeSubTree(\$srcPath, \$dstPath, PropelPDO \$con = null)
+ {
+ if (substr(\$dstPath, 0, strlen(\$srcPath)) == \$srcPath)
+ throw new PropelException('Cannot move a node subtree within itself.');
+
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+
+ /**
+ * Example:
+ * UPDATE table
+ * SET npath = CONCAT('1.3', SUBSTRING(npath, 6, 74))
+ * WHERE npath = '1.2.2' OR npath LIKE '1.2.2.%'
+ */
+
+ \$npath = $nodePeerClassname::NPATH_COLNAME;
+ //the following dot isn`t mean`t a nodeKeySeperator
+ \$setcol = substr(\$npath, strrpos(\$npath, '.')+1);
+ \$setcollen = $nodePeerClassname::NPATH_LEN;
+ \$db = Propel::getDb($peerClassname::DATABASE_NAME);
+
+ //
+ if ($nodePeerClassname::isCodeBase(\$con))
+ {
+ // This is a hack to get CodeBase working. It will eventually be removed.
+ // It is a workaround for the following CodeBase bug:
+ // -Prepared statement parameters cannot be embedded in SQL functions (i.e. CONCAT)
+ \$sql = \"UPDATE \" . $peerClassname::TABLE_NAME . \" \" .
+ \"SET \$setcol=\" . \$db->concatString(\"'\$dstPath'\", \$db->subString(\$npath, strlen(\$srcPath)+1, \$setcollen)) . \" \" .
+ \"WHERE \$npath = '\$srcPath' OR \$npath LIKE '\" . \$srcPath . $nodePeerClassname::NPATH_SEP . \"%'\";
+
+ \$con->executeUpdate(\$sql);
+ }
+ else
+ {
+ //
+ \$sql = \"UPDATE \" . $peerClassname::TABLE_NAME . \" \" .
+ \"SET \$setcol=\" . \$db->concatString('?', \$db->subString(\$npath, '?', '?')) . \" \" .
+ \"WHERE \$npath = ? OR \$npath LIKE ?\";
+
+ \$stmt = \$con->prepare(\$sql);
+ \$stmt->bindValue(1, \$dstPath); // string
+ \$srcPathPlus1 = strlen(\$srcPath)+1;
+ \$stmt->bindValue(2, \$srcPathPlus1); // int
+ \$stmt->bindValue(3, \$setcollen);// int
+ \$stmt->bindValue(4, \$srcPath);// string
+ \$srcPathWC = \$srcPath . $nodePeerClassname::NPATH_SEP . '%';
+ \$stmt->bindValue(5, \$srcPathWC); // string
+ \$stmt->execute();
+ //
+ }
+ }
+";
+ }
+
+ protected function addDeleteNodeSubTree(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Deletes the node subtree at the specified node path from the database.
+ *
+ * @param string Node path to delete
+ * @param PropelPDO Connection to use.
+ * @return void
+ * @throws PropelException
+ * @todo This is currently broken for simulated 'onCascadeDelete's.
+ */
+ public static function deleteNodeSubTree(\$nodePath, PropelPDO \$con = null)
+ {
+ if (\$con === null)
+ \$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
+
+ /**
+ * DELETE FROM table
+ * WHERE npath = '1.2.2' OR npath LIKE '1.2.2.%'
+ */
+
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ \$criteria->add($nodePeerClassname::NPATH_COLNAME, \$nodePath, Criteria::EQUAL);
+ \$criteria->addOr($nodePeerClassname::NPATH_COLNAME, \$nodePath . self::NPATH_SEP . '%', Criteria::LIKE);
+ {$this->basePeerClassname}::doDelete(\$criteria, \$con);
+ }
+";
+ }
+
+ protected function addBuildFamilyCriteria(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Builds the criteria needed to retrieve node ancestors and/or descendants.
+ *
+ * @param Criteria Criteria to start with
+ * @param boolean True if ancestors should be retrieved.
+ * @param boolean True if descendants should be retrieved.
+ * @return Criteria
+ */
+ public static function buildFamilyCriteria(\$criteria, \$ancestors = false, \$descendants = false)
+ {
+ /*
+ Example SQL to retrieve nodepath '1.2.3' with both ancestors and descendants:
+
+ SELECT L.NPATH, L.LABEL, test.NPATH, UCASE(L.NPATH)
+ FROM test L, test
+ WHERE test.NPATH='1.2.3' AND
+ (L.NPATH=SUBSTRING(test.NPATH, 1, LENGTH(L.NPATH)) OR
+ test.NPATH=SUBSTRING(L.NPATH, 1, LENGTH(test.NPATH)))
+ ORDER BY UCASE(L.NPATH) ASC
+ */
+
+ if (\$criteria === null)
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+
+ if (!\$criteria->getSelectColumns())
+ $peerClassname::addSelectColumns(\$criteria);
+
+ \$db = Propel::getDb(\$criteria->getDbName());
+
+ if ((\$ancestors || \$descendants) && \$criteria->size())
+ {
+ // If we are retrieving ancestors/descendants, we need to do a
+ // self-join to locate them. The exception to this is if no search
+ // criteria is specified. In this case we're retrieving all nodes
+ // anyway, so there is no need to do a self-join.
+
+ // The left-side of the self-join will contain the columns we'll
+ // use to build node objects (target node records along with their
+ // ancestors and/or descendants). The right-side of the join will
+ // contain the target node records specified by the initial criteria.
+ // These are used to match the appropriate ancestor/descendant on
+ // the left.
+
+ // Specify an alias for the left-side table to use.
+ \$criteria->addAlias('L', $peerClassname::TABLE_NAME);
+
+ // Make sure we have select columns to begin with.
+ if (!\$criteria->getSelectColumns())
+ $peerClassname::addSelectColumns(\$criteria);
+
+ // Replace any existing columns for the right-side table with the
+ // left-side alias.
+ \$selectColumns = \$criteria->getSelectColumns();
+ \$criteria->clearSelectColumns();
+ foreach (\$selectColumns as \$colName)
+ \$criteria->addSelectColumn(str_replace($peerClassname::TABLE_NAME, 'L', \$colName));
+
+ \$a = null;
+ \$d = null;
+
+ \$npathL = $peerClassname::alias('L', $nodePeerClassname::NPATH_COLNAME);
+ \$npathR = $nodePeerClassname::NPATH_COLNAME;
+ \$npath_len = $nodePeerClassname::NPATH_LEN;
+
+ if (\$ancestors)
+ {
+ // For ancestors, match left-side node paths which are contained
+ // by right-side node paths.
+ \$a = \$criteria->getNewCriterion(\$npathL,
+ \"\$npathL=\" . \$db->subString(\$npathR, 1, \$db->strLength(\$npathL), \$npath_len),
+ Criteria::CUSTOM);
+ }
+
+ if (\$descendants)
+ {
+ // For descendants, match left-side node paths which contain
+ // right-side node paths.
+ \$d = \$criteria->getNewCriterion(\$npathR,
+ \"\$npathR=\" . \$db->subString(\$npathL, 1, \$db->strLength(\$npathR), \$npath_len),
+ Criteria::CUSTOM);
+ }
+
+ if (\$a)
+ {
+ if (\$d) \$a->addOr(\$d);
+ \$criteria->addAnd(\$a);
+ }
+ else if (\$d)
+ {
+ \$criteria->addAnd(\$d);
+ }
+
+ // Add the target node path column. This is used by populateNodes().
+ \$criteria->addSelectColumn(\$npathR);
+
+ // Sort by node path to speed up tree construction in populateNodes()
+ \$criteria->addAsColumn('npathlen', \$db->strLength(\$npathL));
+ \$criteria->addAscendingOrderByColumn('npathlen');
+ \$criteria->addAscendingOrderByColumn(\$npathL);
+ }
+ else
+ {
+ // Add the target node path column. This is used by populateNodes().
+ \$criteria->addSelectColumn($nodePeerClassname::NPATH_COLNAME);
+
+ // Sort by node path to speed up tree construction in populateNodes()
+ \$criteria->addAsColumn('npathlen', \$db->strLength($nodePeerClassname::NPATH_COLNAME));
+ \$criteria->addAscendingOrderByColumn('npathlen');
+ \$criteria->addAscendingOrderByColumn($nodePeerClassname::NPATH_COLNAME);
+ }
+
+ return \$criteria;
+ }
+";
+ }
+
+ protected function addBuildTree(&$script)
+ {
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * This method reconstructs as much of the tree structure as possible from
+ * the given array of objects. Depending on how you execute your query, it
+ * is possible for the ResultSet to contain multiple tree fragments (i.e.
+ * subtrees). The array returned by this method will contain one entry
+ * for each subtree root node it finds. The remaining subtree nodes are
+ * accessible from the $nodeObjectClassname methods of the
+ * subtree root nodes.
+ *
+ * @param array Array of $nodeObjectClassname objects
+ * @return array Array of $nodeObjectClassname objects
+ */
+ public static function buildTree(\$nodes)
+ {
+ // Subtree root nodes to return
+ \$rootNodes = array();
+
+ // Build the tree relations
+ foreach (\$nodes as \$node)
+ {
+ \$sep = strrpos(\$node->getNodePath(), $nodePeerClassname::NPATH_SEP);
+ \$parentPath = (\$sep !== false ? substr(\$node->getNodePath(), 0, \$sep) : '');
+ \$parentNode = null;
+
+ // Scan other nodes for parent.
+ foreach (\$nodes as \$pnode)
+ {
+ if (\$pnode->getNodePath() === \$parentPath)
+ {
+ \$parentNode = \$pnode;
+ break;
+ }
+ }
+
+ // If parent was found, attach as child, otherwise its a subtree root
+ if (\$parentNode)
+ \$parentNode->attachChildNode(\$node);
+ else
+ \$rootNodes[] = \$node;
+ }
+
+ return \$rootNodes;
+ }
+";
+ }
+
+ protected function addPopulateNodes(&$script)
+ {
+ $table = $this->getTable();
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $objectClassname = $this->getStubObjectBuilder()->getClassname();
+
+ $nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
+ $nodeObjectClassname = $this->getStubNodeBuilder()->getClassname();
+
+ $script .= "
+ /**
+ * Populates the $objectClassname objects from the
+ * specified ResultSet, wraps them in $nodeObjectClassname
+ * objects and build the appropriate node relationships.
+ * The array returned by this method will only include the initial targets
+ * of the query, even if ancestors/descendants were also requested.
+ * The ancestors/descendants will be cached in memory and are accessible via
+ * the getNode() methods.
+ *
+ * @param PDOStatement \$stmt Executed PDOStatement
+ * @param Criteria
+ * @return array Array of $nodeObjectClassname objects.
+ */
+ public static function populateNodes(PDOStatement \$stmt, \$criteria)
+ {
+ \$nodes = array();
+ \$targets = array();
+ \$targetfld = count(\$criteria->getSelectColumns());
+";
+
+ if (!$table->getChildrenColumn()) {
+ $script .= "
+ // set the class once to avoid overhead in the loop
+ \$cls = $peerClassname::getOMClass();
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ // populate the object(s)
+ foreach(\$stmt->fetchAll() AS \$row)
+ {
+ if (!isset(\$nodes[\$row[0]]))
+ {
+";
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ // class must be set each time from the record row
+ \$cls = $peerClassname::getOMClass(\$row, 1);
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
+ \$obj->hydrate(\$row);
+
+ \$nodes[\$row[0]] = new $nodeObjectClassname(\$obj);
+ }
+
+ \$node = \$nodes[\$row[0]];
+
+ if (\$node->getNodePath() === \$row[\$targetfld])
+ \$targets[\$node->getNodePath()] = \$node;
+ }
+
+ $nodePeerClassname::buildTree(\$nodes);
+
+ return array_values(\$targets);
+ }
+";
+ }
+
+} // PHP5NodePeerBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectBuilder.php
new file mode 100644
index 000000000..e77e547b8
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectBuilder.php
@@ -0,0 +1,4250 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5ObjectBuilder extends ObjectBuilder
+{
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ public function getNamespace()
+ {
+ if ($namespace = parent::getNamespace()) {
+ if ($this->getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceOm')) {
+ return $namespace . '\\' . $omns;
+ } else {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubObjectBuilder()->getUnprefixedClassname();
+ }
+
+ /**
+ * Validates the current table to make sure that it won't
+ * result in generated code that will not parse.
+ *
+ * This method may emit warnings for code which may cause problems
+ * and will throw exceptions for errors that will definitely cause
+ * problems.
+ */
+ protected function validateModel()
+ {
+ parent::validateModel();
+
+ $table = $this->getTable();
+
+ // Check to see whether any generated foreign key names
+ // will conflict with column names.
+
+ $colPhpNames = array();
+ $fkPhpNames = array();
+
+ foreach ($table->getColumns() as $col) {
+ $colPhpNames[] = $col->getPhpName();
+ }
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $fkPhpNames[] = $this->getFKPhpNameAffix($fk, $plural = false);
+ }
+
+ $intersect = array_intersect($colPhpNames, $fkPhpNames);
+ if (!empty($intersect)) {
+ throw new EngineException("One or more of your column names for [" . $table->getName() . "] table conflict with foreign key names (" . implode(", ", $intersect) . ")");
+ }
+
+ // Check foreign keys to see if there are any foreign keys that
+ // are also matched with an inversed referencing foreign key
+ // (this is currently unsupported behavior)
+ // see: http://propel.phpdb.org/trac/ticket/549
+
+ foreach ($table->getForeignKeys() as $fk) {
+ if ($fk->isMatchedByInverseFK()) {
+ throw new EngineException("The 1:1 relationship expressed by foreign key " . $fk->getName() . " is defined in both directions; Propel does not currently support this (if you must have both foreign key constraints, consider adding this constraint with a custom SQL file.)" );
+ }
+ }
+ }
+
+ /**
+ * Returns the appropriate formatter (from platform) for a date/time column.
+ * @param Column $col
+ * @return string
+ */
+ protected function getTemporalFormatter(Column $col)
+ {
+ $fmt = null;
+ if ($col->getType() === PropelTypes::DATE) {
+ $fmt = $this->getPlatform()->getDateFormatter();
+ } elseif ($col->getType() === PropelTypes::TIME) {
+ $fmt = $this->getPlatform()->getTimeFormatter();
+ } elseif ($col->getType() === PropelTypes::TIMESTAMP) {
+ $fmt = $this->getPlatform()->getTimestampFormatter();
+ }
+ return $fmt;
+ }
+
+ /**
+ * Returns the type-casted and stringified default value for the specified Column.
+ * This only works for scalar default values currently.
+ * @return string The default value or 'NULL' if there is none.
+ */
+ protected function getDefaultValueString(Column $col)
+ {
+ $defaultValue = var_export(null, true);
+ if (($val = $col->getPhpDefaultValue()) !== null) {
+ if ($col->isTemporalType()) {
+ $fmt = $this->getTemporalFormatter($col);
+ try {
+ if (!($this->getPlatform() instanceof MysqlPlatform &&
+ ($val === '0000-00-00 00:00:00' || $val === '0000-00-00'))) {
+ // while technically this is not a default value of NULL,
+ // this seems to be closest in meaning.
+ $defDt = new DateTime($val);
+ $defaultValue = var_export($defDt->format($fmt), true);
+ }
+ } catch (Exception $x) {
+ // prevent endless loop when timezone is undefined
+ date_default_timezone_set('America/Los_Angeles');
+ throw new EngineException("Unable to parse default temporal value for " . $col->getFullyQualifiedName() . ": " .$this->getDefaultValueString($col), $x);
+ }
+ } else {
+ if ($col->isPhpPrimitiveType()) {
+ settype($val, $col->getPhpType());
+ $defaultValue = var_export($val, true);
+ } elseif ($col->isPhpObjectType()) {
+ $defaultValue = 'new '.$col->getPhpType().'(' . var_export($val, true) . ')';
+ } else {
+ throw new EngineException("Cannot get default value string for " . $col->getFullyQualifiedName());
+ }
+ }
+ }
+ return $defaultValue;
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+ $interface = $this->getInterface();
+ $parentClass = $this->getBehaviorContent('parentClass');
+ $parentClass = (null !== $parentClass) ? $parentClass : ClassTools::classname($this->getBaseClass());
+ $script .= "
+/**
+ * Base class that represents a row from the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." extends ".$parentClass." ";
+
+ $interface = ClassTools::getInterface($table);
+ if ($interface) {
+ $script .= " implements " . ClassTools::classname($interface);
+ }
+ if ($this->getTable()->getInterface()) {
+ $this->declareClassFromBuilder($this->getInterfaceBuilder());
+ }
+
+ $script .= "
+{
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the basic OM class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $this->declareClassFromBuilder($this->getStubPeerBuilder());
+ $this->declareClassFromBuilder($this->getStubQueryBuilder());
+ $this->declareClasses('Propel', 'PropelException', 'PDO', 'PropelPDO', 'Criteria', 'BaseObject', 'Persistent', 'BasePeer', 'PropelObjectcollection');
+
+ $table = $this->getTable();
+ if (!$table->isAlias()) {
+ $this->addConstants($script);
+ $this->addAttributes($script);
+ }
+
+ if ($this->hasDefaultValues()) {
+ $this->addApplyDefaultValues($script);
+ $this->addConstructor($script);
+ }
+
+ $this->addColumnAccessorMethods($script);
+ $this->addColumnMutatorMethods($script);
+
+ $this->addHasOnlyDefaultValues($script);
+
+ $this->addHydrate($script);
+ $this->addEnsureConsistency($script);
+
+ if (!$table->isReadOnly()) {
+ $this->addManipulationMethods($script);
+ }
+
+ if ($this->isAddValidateMethod()) {
+ $this->addValidationMethods($script);
+ }
+
+ if ($this->isAddGenericAccessors()) {
+ $this->addGetByName($script);
+ $this->addGetByPosition($script);
+ $this->addToArray($script);
+ }
+
+ if ($this->isAddGenericMutators()) {
+ $this->addSetByName($script);
+ $this->addSetByPosition($script);
+ $this->addFromArray($script);
+ }
+
+ $this->addBuildCriteria($script);
+ $this->addBuildPkeyCriteria($script);
+ $this->addGetPrimaryKey($script);
+ $this->addSetPrimaryKey($script);
+ $this->addIsPrimaryKeyNull($script);
+
+ $this->addCopy($script);
+
+ if (!$table->isAlias()) {
+ $this->addGetPeer($script);
+ }
+
+ $this->addFKMethods($script);
+ $this->addRefFKMethods($script);
+ $this->addCrossFKMethods($script);
+ $this->addClear($script);
+ $this->addClearAllReferences($script);
+
+ $this->addPrimaryString($script);
+
+ // apply behaviors
+ $this->applyBehaviorModifier('objectMethods', $script, " ");
+
+ $this->addMagicCall($script);
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('objectFilter', $script, "");
+ }
+
+ /**
+ * Adds any constants to the class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addConstants(&$script)
+ {
+ $script .= "
+ /**
+ * Peer class name
+ */
+ const PEER = '" . addslashes($this->getStubPeerBuilder()->getFullyQualifiedClassname()) . "';
+";
+ }
+
+ /**
+ * Adds class attributes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAttributes(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * The Peer class.
+ * Instance provides a convenient way of calling static methods on a class
+ * that calling code may not be able to identify.
+ * @var ".$this->getPeerClassname()."
+ */
+ protected static \$peer;
+";
+ if (!$table->isAlias()) {
+ $this->addColumnAttributes($script);
+ }
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $this->addFKAttributes($script, $fk);
+ }
+
+ foreach ($table->getReferrers() as $refFK) {
+ $this->addRefFKAttributes($script, $refFK);
+ }
+
+ // many-to-many relationships
+ foreach ($table->getCrossFks() as $fkList) {
+ $crossFK = $fkList[1];
+ $this->addCrossFKAttributes($script, $crossFK);
+ }
+
+
+ $this->addAlreadyInSaveAttribute($script);
+ $this->addAlreadyInValidationAttribute($script);
+
+ // apply behaviors
+ $this->applyBehaviorModifier('objectAttributes', $script, " ");
+ }
+
+ /**
+ * Adds variables that store column values.
+ * @param string &$script The script will be modified in this method.
+ * @see addColumnNameConstants()
+ */
+ protected function addColumnAttributes(&$script)
+ {
+
+ $table = $this->getTable();
+
+ foreach ($table->getColumns() as $col) {
+ $this->addColumnAttributeComment($script, $col);
+ $this->addColumnAttributeDeclaration($script, $col);
+ if ($col->isLazyLoad() ) {
+ $this->addColumnAttributeLoaderComment($script, $col);
+ $this->addColumnAttributeLoaderDeclaration($script, $col);
+ }
+ }
+ }
+
+ /**
+ * Add comment about the attribute (variable) that stores column values
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col
+ **/
+ protected function addColumnAttributeComment(&$script, Column $col)
+ {
+ $cptype = $col->getPhpType();
+ $clo = strtolower($col->getName());
+
+ $script .= "
+ /**
+ * The value for the $clo field.";
+ if ($col->getDefaultValue()) {
+ if ($col->getDefaultValue()->isExpression()) {
+ $script .= "
+ * Note: this column has a database default value of: (expression) ".$col->getDefaultValue()->getValue();
+ } else {
+ $script .= "
+ * Note: this column has a database default value of: ". $this->getDefaultValueString($col);
+ }
+ }
+ $script .= "
+ * @var $cptype
+ */";
+ }
+
+ /**
+ * Adds the declaration of a column value storage attribute
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col
+ **/
+ protected function addColumnAttributeDeclaration(&$script, Column $col)
+ {
+ $clo = strtolower($col->getName());
+ $script .= "
+ protected \$" . $clo . ";
+";
+ }
+
+ /**
+ * Adds the comment about the attribute keeping track if an attribute value has been loaded
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col
+ **/
+ protected function addColumnAttributeLoaderComment(&$script, Column $col)
+ {
+ $clo = strtolower($col->getName());
+ $script .= "
+ /**
+ * Whether the lazy-loaded \$$clo value has been loaded from database.
+ * This is necessary to avoid repeated lookups if \$$clo column is NULL in the db.
+ * @var boolean
+ */";
+ }
+
+ /**
+ * Adds the declaration of the attribute keeping track of an attribute's loaded state
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col
+ **/
+ protected function addColumnAttributeLoaderDeclaration(&$script, Column $col)
+ {
+ $clo = strtolower($col->getName());
+ $script .= "
+ protected \$".$clo."_isLoaded = false;
+";
+ }
+
+ /**
+ * Adds the getPeer() method.
+ * This is a convenient, non introspective way of getting the Peer class for a particular object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPeer(&$script)
+ {
+ $this->addGetPeerComment($script);
+ $this->addGetPeerFunctionOpen($script);
+ $this->addGetPeerFunctionBody($script);
+ $this->addGetPeerFunctionClose($script);
+ }
+
+ /**
+ * Add the comment for the getPeer method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetPeerComment(&$script) {
+ $script .= "
+ /**
+ * Returns a peer instance associated with this om.
+ *
+ * Since Peer classes are not to have any instance attributes, this method returns the
+ * same instance for all member of this class. The method could therefore
+ * be static, but this would prevent one from overriding the behavior.
+ *
+ * @return ".$this->getPeerClassname()."
+ */";
+ }
+
+ /**
+ * Adds the function declaration (function opening) for the getPeer method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetPeerFunctionOpen(&$script) {
+ $script .= "
+ public function getPeer()
+ {";
+ }
+
+ /**
+ * Adds the body of the getPeer method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetPeerFunctionBody(&$script) {
+ $script .= "
+ if (self::\$peer === null) {
+ " . $this->buildObjectInstanceCreationCode('self::$peer', $this->getPeerClassname()) . "
+ }
+ return self::\$peer;";
+ }
+
+ /**
+ * Add the function close for the getPeer method
+ * Note: this is just a } and the body ends with a return statement, so it's quite useless. But it's here anyway for consisency, cause there's a close function for all functions and in some other instances, they are useful
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetPeerFunctionClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the constructor for this object.
+ * @param string &$script The script will be modified in this method.
+ * @see addConstructor()
+ */
+ protected function addConstructor(&$script)
+ {
+ $this->addConstructorComment($script);
+ $this->addConstructorOpen($script);
+ $this->addConstructorBody($script);
+ $this->addConstructorClose($script);
+ }
+
+ /**
+ * Adds the comment for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorComment(&$script) {
+ $script .= "
+ /**
+ * Initializes internal state of ".$this->getClassname()." object.
+ * @see applyDefaults()
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorOpen(&$script) {
+ $script .= "
+ public function __construct()
+ {";
+ }
+
+ /**
+ * Adds the function body for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorBody(&$script) {
+ $script .= "
+ parent::__construct();
+ \$this->applyDefaultValues();";
+ }
+
+ /**
+ * Adds the function close for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the applyDefaults() method, which is called from the constructor.
+ * @param string &$script The script will be modified in this method.
+ * @see addConstructor()
+ */
+ protected function addApplyDefaultValues(&$script)
+ {
+ $this->addApplyDefaultValuesComment($script);
+ $this->addApplyDefaultValuesOpen($script);
+ $this->addApplyDefaultValuesBody($script);
+ $this->addApplyDefaultValuesClose($script);
+ }
+
+ /**
+ * Adds the comment for the applyDefaults method
+ * @param string &$script The script will be modified in this method.
+ * @see addApplyDefaultValues()
+ **/
+ protected function addApplyDefaultValuesComment(&$script) {
+ $script .= "
+ /**
+ * Applies default values to this object.
+ * This method should be called from the object's constructor (or
+ * equivalent initialization method).
+ * @see __construct()
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the applyDefaults method
+ * @param string &$script The script will be modified in this method.
+ * @see addApplyDefaultValues()
+ **/
+ protected function addApplyDefaultValuesOpen(&$script) {
+ $script .= "
+ public function applyDefaultValues()
+ {";
+ }
+
+ /**
+ * Adds the function body of the applyDefault method
+ * @param string &$script The script will be modified in this method.
+ * @see addApplyDefaultValues()
+ **/
+ protected function addApplyDefaultValuesBody(&$script) {
+ $table = $this->getTable();
+ // FIXME - Apply support for PHP default expressions here
+ // see: http://propel.phpdb.org/trac/ticket/378
+
+ $colsWithDefaults = array();
+ foreach ($table->getColumns() as $col) {
+ $def = $col->getDefaultValue();
+ if ($def !== null && !$def->isExpression()) {
+ $colsWithDefaults[] = $col;
+ }
+ }
+
+ $colconsts = array();
+ foreach ($colsWithDefaults as $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ \$this->".$clo." = ".$this->getDefaultValueString($col).";";
+
+ }
+ }
+
+
+ /**
+ * Adds the function close for the applyDefaults method
+ * @param string &$script The script will be modified in this method.
+ * @see addApplyDefaultValues()
+ **/
+ protected function addApplyDefaultValuesClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ // --------------------------------------------------------------
+ //
+ // A C C E S S O R M E T H O D S
+ //
+ // --------------------------------------------------------------
+
+ /**
+ * Adds a date/time/timestamp getter method.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnAccessors()
+ */
+ protected function addTemporalAccessor(&$script, Column $col)
+ {
+ $this->addTemporalAccessorComment($script, $col);
+ $this->addTemporalAccessorOpen($script, $col);
+ $this->addTemporalAccessorBody($script, $col);
+ $this->addTemporalAccessorClose($script, $col);
+ } // addTemporalAccessor
+
+
+ /**
+ * Adds the comment for a temporal accessor
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addTemporalAccessor
+ **/
+ protected function addTemporalAccessorComment(&$script, Column $col) {
+ $clo = strtolower($col->getName());
+ $useDateTime = $this->getBuildProperty('useDateTimeClass');
+
+ $dateTimeClass = $this->getBuildProperty('dateTimeClass');
+ if (!$dateTimeClass) {
+ $dateTimeClass = 'DateTime';
+ }
+
+ $handleMysqlDate = false;
+ if ($this->getPlatform() instanceof MysqlPlatform) {
+ if ($col->getType() === PropelTypes::TIMESTAMP) {
+ $handleMysqlDate = true;
+ $mysqlInvalidDateString = '0000-00-00 00:00:00';
+ } elseif ($col->getType() === PropelTypes::DATE) {
+ $handleMysqlDate = true;
+ $mysqlInvalidDateString = '0000-00-00';
+ }
+ // 00:00:00 is a valid time, so no need to check for that.
+ }
+
+ $script .= "
+ /**
+ * Get the [optionally formatted] temporal [$clo] column value.
+ * ".$col->getDescription();
+ if (!$useDateTime) {
+ $script .= "
+ * This accessor only only work with unix epoch dates. Consider enabling the propel.useDateTimeClass
+ * option in order to avoid converstions to integers (which are limited in the dates they can express).";
+ }
+ $script .= "
+ *
+ * @param string \$format The date/time format string (either date()-style or strftime()-style).
+ * If format is NULL, then the raw ".($useDateTime ? 'DateTime object' : 'unix timestamp integer')." will be returned.";
+ if ($useDateTime) {
+ $script .= "
+ * @return mixed Formatted date/time value as string or $dateTimeClass object (if format is NULL), NULL if column is NULL" .($handleMysqlDate ? ', and 0 if column value is ' . $mysqlInvalidDateString : '');
+ } else {
+ $script .= "
+ * @return mixed Formatted date/time value as string or (integer) unix timestamp (if format is NULL), NULL if column is NULL".($handleMysqlDate ? ', and 0 if column value is ' . $mysqlInvalidDateString : '');
+ }
+ $script .= "
+ * @throws PropelException - if unable to parse/validate the date/time value.
+ */";
+ }
+
+
+ /**
+ * Adds the function declaration for a temporal accessor
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addTemporalAccessor
+ **/
+ protected function addTemporalAccessorOpen(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+
+ $defaultfmt = null;
+ $visibility = $col->getAccessorVisibility();
+
+ // Default date/time formatter strings are specified in build.properties
+ if ($col->getType() === PropelTypes::DATE) {
+ $defaultfmt = $this->getBuildProperty('defaultDateFormat');
+ } elseif ($col->getType() === PropelTypes::TIME) {
+ $defaultfmt = $this->getBuildProperty('defaultTimeFormat');
+ } elseif ($col->getType() === PropelTypes::TIMESTAMP) {
+ $defaultfmt = $this->getBuildProperty('defaultTimeStampFormat');
+ }
+ if (empty($defaultfmt)) { $defaultfmt = null; }
+
+ $script .= "
+ ".$visibility." function get$cfc(\$format = ".var_export($defaultfmt, true)."";
+ if ($col->isLazyLoad()) $script .= ", \$con = null";
+ $script .= ")
+ {";
+ }
+
+ /**
+ * Adds the body of the temporal accessor
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addTemporalAccessor
+ **/
+ protected function addTemporalAccessorBody(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $clo = strtolower($col->getName());
+
+ $useDateTime = $this->getBuildProperty('useDateTimeClass');
+
+ $dateTimeClass = $this->getBuildProperty('dateTimeClass');
+ if (!$dateTimeClass) {
+ $dateTimeClass = 'DateTime';
+ }
+
+ $defaultfmt = null;
+
+ // Default date/time formatter strings are specified in build.properties
+ if ($col->getType() === PropelTypes::DATE) {
+ $defaultfmt = $this->getBuildProperty('defaultDateFormat');
+ } elseif ($col->getType() === PropelTypes::TIME) {
+ $defaultfmt = $this->getBuildProperty('defaultTimeFormat');
+ } elseif ($col->getType() === PropelTypes::TIMESTAMP) {
+ $defaultfmt = $this->getBuildProperty('defaultTimeStampFormat');
+ }
+ if (empty($defaultfmt)) { $defaultfmt = null; }
+
+ $handleMysqlDate = false;
+ if ($this->getPlatform() instanceof MysqlPlatform) {
+ if ($col->getType() === PropelTypes::TIMESTAMP) {
+ $handleMysqlDate = true;
+ $mysqlInvalidDateString = '0000-00-00 00:00:00';
+ } elseif ($col->getType() === PropelTypes::DATE) {
+ $handleMysqlDate = true;
+ $mysqlInvalidDateString = '0000-00-00';
+ }
+ // 00:00:00 is a valid time, so no need to check for that.
+ }
+
+ if ($col->isLazyLoad()) {
+ $script .= "
+ if (!\$this->".$clo."_isLoaded && \$this->$clo === null && !\$this->isNew()) {
+ \$this->load$cfc(\$con);
+ }
+";
+ }
+ $script .= "
+ if (\$this->$clo === null) {
+ return null;
+ }
+
+";
+ if ($handleMysqlDate) {
+ $script .= "
+ if (\$this->$clo === '$mysqlInvalidDateString') {
+ // while technically this is not a default value of NULL,
+ // this seems to be closest in meaning.
+ return null;
+ } else {
+ try {
+ \$dt = new $dateTimeClass(\$this->$clo);
+ } catch (Exception \$x) {
+ throw new PropelException(\"Internally stored date/time/timestamp value could not be converted to $dateTimeClass: \" . var_export(\$this->$clo, true), \$x);
+ }
+ }
+";
+ } else {
+ $script .= "
+
+ try {
+ \$dt = new $dateTimeClass(\$this->$clo);
+ } catch (Exception \$x) {
+ throw new PropelException(\"Internally stored date/time/timestamp value could not be converted to $dateTimeClass: \" . var_export(\$this->$clo, true), \$x);
+ }
+";
+ } // if handleMyqlDate
+
+ $script .= "
+ if (\$format === null) {";
+ if ($useDateTime) {
+ $script .= "
+ // Because propel.useDateTimeClass is TRUE, we return a $dateTimeClass object.
+ return \$dt;";
+ } else {
+ $script .= "
+ // We cast here to maintain BC in API; obviously we will lose data if we're dealing with pre-/post-epoch dates.
+ return (int) \$dt->format('U');";
+ }
+ $script .= "
+ } elseif (strpos(\$format, '%') !== false) {
+ return strftime(\$format, \$dt->format('U'));
+ } else {
+ return \$dt->format(\$format);
+ }";
+ }
+
+
+ /**
+ * Adds the body of the temporal accessor
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addTemporalAccessorClose
+ **/
+ protected function addTemporalAccessorClose(&$script, Column $col) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds a normal (non-temporal) getter method.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnAccessors()
+ */
+ protected function addDefaultAccessor(&$script, Column $col)
+ {
+ $this->addDefaultAccessorComment($script, $col);
+ $this->addDefaultAccessorOpen($script, $col);
+ $this->addDefaultAccessorBody($script, $col);
+ $this->addDefaultAccessorClose($script, $col);
+ }
+
+ /**
+ * Add the comment for a default accessor method (a getter)
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addDefaultAccessor()
+ **/
+ protected function addDefaultAccessorComment(&$script, Column $col) {
+ $clo=strtolower($col->getName());
+
+ $script .= "
+ /**
+ * Get the [$clo] column value.
+ * ".$col->getDescription();
+ if ($col->isLazyLoad()) {
+ $script .= "
+ * @param PropelPDO An optional PropelPDO connection to use for fetching this lazy-loaded column.";
+ }
+ $script .= "
+ * @return ".$col->getPhpType()."
+ */";
+ }
+
+ /**
+ * Adds the function declaration for a default accessor
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addDefaultAccessor()
+ **/
+ protected function addDefaultAccessorOpen(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $visibility = $col->getAccessorVisibility();
+
+ $script .= "
+ ".$visibility." function get$cfc(";
+ if ($col->isLazyLoad()) $script .= "PropelPDO \$con = null";
+ $script .= ")
+ {";
+ }
+
+ /**
+ * Adds the function body for a default accessor method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addDefaultAccessor()
+ **/
+ protected function addDefaultAccessorBody(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $clo = strtolower($col->getName());
+ if ($col->isLazyLoad()) {
+ $script .= "
+ if (!\$this->".$clo."_isLoaded && \$this->$clo === null && !\$this->isNew()) {
+ \$this->load$cfc(\$con);
+ }
+";
+ }
+
+ $script .= "
+ return \$this->$clo;";
+ }
+
+ /**
+ * Adds the function close for a default accessor method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addDefaultAccessor()
+ **/
+ protected function addDefaultAccessorClose(&$script, Column $col) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the lazy loader method.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnAccessors()
+ */
+ protected function addLazyLoader(&$script, Column $col)
+ {
+ $this->addLazyLoaderComment($script, $col);
+ $this->addLazyLoaderOpen($script, $col);
+ $this->addLazyLoaderBody($script, $col);
+ $this->addLazyLoaderClose($script, $col);
+ }
+
+ /**
+ * Adds the comment for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderComment(&$script, Column $col) {
+ $clo = strtolower($col->getName());
+
+ $script .= "
+ /**
+ * Load the value for the lazy-loaded [$clo] column.
+ *
+ * This method performs an additional query to return the value for
+ * the [$clo] column, since it is not populated by
+ * the hydrate() method.
+ *
+ * @param \$con PropelPDO (optional) The PropelPDO connection to use.
+ * @return void
+ * @throws PropelException - any underlying error will be wrapped and re-thrown.
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderOpen(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $script .= "
+ protected function load$cfc(PropelPDO \$con = null)
+ {";
+ }
+
+ /**
+ * Adds the function body for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderBody(&$script, Column $col) {
+ $platform = $this->getPlatform();
+ $clo = strtolower($col->getName());
+
+ $script .= "
+ \$c = \$this->buildPkeyCriteria();
+ \$c->addSelectColumn(".$this->getColumnConstant($col).");
+ try {
+ \$stmt = ".$this->getPeerClassname()."::doSelectStmt(\$c, \$con);
+ \$row = \$stmt->fetch(PDO::FETCH_NUM);
+ \$stmt->closeCursor();";
+
+ if ($col->getType() === PropelTypes::CLOB && $this->getPlatform() instanceof OraclePlatform) {
+ // PDO_OCI returns a stream for CLOB objects, while other PDO adapters return a string...
+ $script .= "
+ \$this->$clo = stream_get_contents(\$row[0]);";
+ } elseif ($col->isLobType() && !$platform->hasStreamBlobImpl()) {
+ $script .= "
+ if (\$row[0] !== null) {
+ \$this->$clo = fopen('php://memory', 'r+');
+ fwrite(\$this->$clo, \$row[0]);
+ rewind(\$this->$clo);
+ } else {
+ \$this->$clo = null;
+ }";
+ } elseif ($col->isPhpPrimitiveType()) {
+ $script .= "
+ \$this->$clo = (\$row[0] !== null) ? (".$col->getPhpType().") \$row[0] : null;";
+ } elseif ($col->isPhpObjectType()) {
+ $script .= "
+ \$this->$clo = (\$row[0] !== null) ? new ".$col->getPhpType()."(\$row[0]) : null;";
+ } else {
+ $script .= "
+ \$this->$clo = \$row[0];";
+ }
+
+ $script .= "
+ \$this->".$clo."_isLoaded = true;
+ } catch (Exception \$e) {
+ throw new PropelException(\"Error loading value for [$clo] column on demand.\", \$e);
+ }";
+ }
+
+ /**
+ * Adds the function close for the lazy loader
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderClose(&$script, Column $col) {
+ $script .= "
+ }";
+ } // addLazyLoader()
+
+ // --------------------------------------------------------------
+ //
+ // M U T A T O R M E T H O D S
+ //
+ // --------------------------------------------------------------
+
+ /**
+ * Adds the open of the mutator (setter) method for a column.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ */
+ protected function addMutatorOpen(&$script, Column $col)
+ {
+ $this->addMutatorComment($script, $col);
+ $this->addMutatorOpenOpen($script, $col);
+ $this->addMutatorOpenBody($script, $col);
+ }
+
+ /**
+ * Adds the comment for a mutator
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addMutatorOpen()
+ **/
+ protected function addMutatorComment(&$script, Column $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ /**
+ * Set the value of [$clo] column.
+ * ".$col->getDescription()."
+ * @param ".$col->getPhpType()." \$v new value
+ * @return ".$this->getObjectClassname()." The current object (for fluent API support)
+ */";
+ }
+
+ /**
+ * Adds the mutator function declaration
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addMutatorOpen()
+ **/
+ protected function addMutatorOpenOpen(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $visibility = $col->getMutatorVisibility();
+
+ $script .= "
+ ".$visibility." function set$cfc(\$v)
+ {";
+ }
+
+ /**
+ * Adds the mutator open body part
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addMutatorOpen()
+ **/
+ protected function addMutatorOpenBody(&$script, Column $col) {
+ $clo = strtolower($col->getName());
+ $cfc = $col->getPhpName();
+ if ($col->isLazyLoad()) {
+ $script .= "
+ // explicitly set the is-loaded flag to true for this lazy load col;
+ // it doesn't matter if the value is actually set or not (logic below) as
+ // any attempt to set the value means that no db lookup should be performed
+ // when the get$cfc() method is called.
+ \$this->".$clo."_isLoaded = true;
+";
+ }
+ }
+
+ /**
+ * Adds the close of the mutator (setter) method for a column.
+ *
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ */
+ protected function addMutatorClose(&$script, Column $col)
+ {
+ $this->addMutatorCloseBody($script, $col);
+ $this->addMutatorCloseClose($script, $col);
+ }
+
+ /**
+ * Adds the body of the close part of a mutator
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addMutatorClose()
+ **/
+ protected function addMutatorCloseBody(&$script, Column $col) {
+ $table = $this->getTable();
+ $cfc = $col->getPhpName();
+ $clo = strtolower($col->getName());
+
+ if ($col->isForeignKey()) {
+
+ foreach ($col->getForeignKeys() as $fk) {
+
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $colFK = $tblFK->getColumn($fk->getMappedForeignColumn($col->getName()));
+
+ $varName = $this->getFKVarName($fk);
+
+ $script .= "
+ if (\$this->$varName !== null && \$this->".$varName."->get".$colFK->getPhpName()."() !== \$v) {
+ \$this->$varName = null;
+ }
+";
+ } // foreach fk
+ } /* if col is foreign key */
+
+ foreach ($col->getReferrers() as $refFK) {
+
+ $tblFK = $this->getDatabase()->getTable($refFK->getForeignTableName());
+
+ if ( $tblFK->getName() != $table->getName() ) {
+
+ foreach ($col->getForeignKeys() as $fk) {
+
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $colFK = $tblFK->getColumn($fk->getMappedForeignColumn($col->getName()));
+
+ if ($refFK->isLocalPrimaryKey()) {
+ $varName = $this->getPKRefFKVarName($refFK);
+ $script .= "
+ // update associated ".$tblFK->getPhpName()."
+ if (\$this->$varName !== null) {
+ \$this->{$varName}->set".$colFK->getPhpName()."(\$v);
+ }
+";
+ } else {
+ $collName = $this->getRefFKCollVarName($refFK);
+ $script .= "
+
+ // update associated ".$tblFK->getPhpName()."
+ if (\$this->$collName !== null) {
+ foreach (\$this->$collName as \$referrerObject) {
+ \$referrerObject->set".$colFK->getPhpName()."(\$v);
+ }
+ }
+";
+ } // if (isLocalPrimaryKey
+ } // foreach col->getPrimaryKeys()
+ } // if tablFk != table
+
+ } // foreach
+ }
+
+ /**
+ * Adds the close for the mutator close
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addMutatorClose()
+ **/
+ protected function addMutatorCloseClose(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $script .= "
+ return \$this;
+ } // set$cfc()
+";
+ }
+
+ /**
+ * Adds a setter for BLOB columns.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnMutators()
+ */
+ protected function addLobMutator(&$script, Column $col)
+ {
+ $this->addMutatorOpen($script, $col);
+ $clo = strtolower($col->getName());
+ $script .= "
+ // Because BLOB columns are streams in PDO we have to assume that they are
+ // always modified when a new value is passed in. For example, the contents
+ // of the stream itself may have changed externally.
+ if (!is_resource(\$v) && \$v !== null) {
+ \$this->$clo = fopen('php://memory', 'r+');
+ fwrite(\$this->$clo, \$v);
+ rewind(\$this->$clo);
+ } else { // it's already a stream
+ \$this->$clo = \$v;
+ }
+ \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
+";
+ $this->addMutatorClose($script, $col);
+ } // addLobMutatorSnippet
+
+ /**
+ * Adds a setter method for date/time/timestamp columns.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnMutators()
+ */
+ protected function addTemporalMutator(&$script, Column $col)
+ {
+ $cfc = $col->getPhpName();
+ $clo = strtolower($col->getName());
+ $visibility = $col->getMutatorVisibility();
+
+ $dateTimeClass = $this->getBuildProperty('dateTimeClass');
+ if (!$dateTimeClass) {
+ $dateTimeClass = 'DateTime';
+ }
+
+ $script .= "
+ /**
+ * Sets the value of [$clo] column to a normalized version of the date/time value specified.
+ * ".$col->getDescription()."
+ * @param mixed \$v string, integer (timestamp), or DateTime value. Empty string will
+ * be treated as NULL for temporal objects.
+ * @return ".$this->getObjectClassname()." The current object (for fluent API support)
+ */
+ ".$visibility." function set$cfc(\$v)
+ {";
+ if ($col->isLazyLoad()) {
+ $script .= "
+ // explicitly set the is-loaded flag to true for this lazy load col;
+ // it doesn't matter if the value is actually set or not (logic below) as
+ // any attempt to set the value means that no db lookup should be performed
+ // when the get$cfc() method is called.
+ \$this->".$clo."_isLoaded = true;
+";
+ }
+
+ $fmt = var_export($this->getTemporalFormatter($col), true);
+
+ $script .= "
+ // we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
+ // -- which is unexpected, to say the least.
+ if (\$v === null || \$v === '') {
+ \$dt = null;
+ } elseif (\$v instanceof DateTime) {
+ \$dt = \$v;
+ } else {
+ // some string/numeric value passed; we normalize that so that we can
+ // validate it.
+ try {
+ if (is_numeric(\$v)) { // if it's a unix timestamp
+ \$dt = new $dateTimeClass('@'.\$v, new DateTimeZone('UTC'));
+ // We have to explicitly specify and then change the time zone because of a
+ // DateTime bug: http://bugs.php.net/bug.php?id=43003
+ \$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
+ } else {
+ \$dt = new $dateTimeClass(\$v);
+ }
+ } catch (Exception \$x) {
+ throw new PropelException('Error parsing date/time value: ' . var_export(\$v, true), \$x);
+ }
+ }
+
+ if ( \$this->$clo !== null || \$dt !== null ) {
+ // (nested ifs are a little easier to read in this case)
+
+ \$currNorm = (\$this->$clo !== null && \$tmpDt = new $dateTimeClass(\$this->$clo)) ? \$tmpDt->format($fmt) : null;
+ \$newNorm = (\$dt !== null) ? \$dt->format($fmt) : null;
+
+ if ( (\$currNorm !== \$newNorm) // normalized values don't match ";
+
+ if (($def = $col->getDefaultValue()) !== null && !$def->isExpression()) {
+ $defaultValue = $this->getDefaultValueString($col);
+ $script .= "
+ || (\$dt->format($fmt) === $defaultValue) // or the entered value matches the default";
+ }
+
+ $script .= "
+ )
+ {
+ \$this->$clo = (\$dt ? \$dt->format($fmt) : null);
+ \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
+ }
+ } // if either are not null
+";
+ $this->addMutatorClose($script, $col);
+ }
+
+ /**
+ * Adds setter method for "normal" columns.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnMutators()
+ */
+ protected function addDefaultMutator(&$script, Column $col)
+ {
+ $clo = strtolower($col->getName());
+
+ $this->addMutatorOpen($script, $col);
+
+ // Perform type-casting to ensure that we can use type-sensitive
+ // checking in mutators.
+ if ($col->isPhpPrimitiveType()) {
+ $script .= "
+ if (\$v !== null) {
+ \$v = (".$col->getPhpType().") \$v;
+ }
+";
+ }
+
+ $script .= "
+ if (\$this->$clo !== \$v";
+ if (($def = $col->getDefaultValue()) !== null && !$def->isExpression()) {
+ $script .= " || \$this->isNew()";
+ }
+ $script .= ") {
+ \$this->$clo = \$v;
+ \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
+ }
+";
+ $this->addMutatorClose($script, $col);
+ }
+
+ /**
+ * Adds the hasOnlyDefaultValues() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addHasOnlyDefaultValues(&$script)
+ {
+ $this->addHasOnlyDefaultValuesComment($script);
+ $this->addHasOnlyDefaultValuesOpen($script);
+ $this->addHasOnlyDefaultValuesBody($script);
+ $this->addHasOnlyDefaultValuesClose($script);
+ }
+
+ /**
+ * Adds the comment for the hasOnlyDefaultValues method
+ * @param string &$script The script will be modified in this method.
+ * @see addHasOnlyDefaultValues
+ **/
+ protected function addHasOnlyDefaultValuesComment(&$script) {
+ $script .= "
+ /**
+ * Indicates whether the columns in this object are only set to default values.
+ *
+ * This method can be used in conjunction with isModified() to indicate whether an object is both
+ * modified _and_ has some values set which are non-default.
+ *
+ * @return boolean Whether the columns in this object are only been set with default values.
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the hasOnlyDefaultValues method
+ * @param string &$script The script will be modified in this method.
+ * @see addHasOnlyDefaultValues
+ **/
+ protected function addHasOnlyDefaultValuesOpen(&$script) {
+ $script .= "
+ public function hasOnlyDefaultValues()
+ {";
+ }
+
+ /**
+ * Adds the function body for the hasOnlyDefaultValues method
+ * @param string &$script The script will be modified in this method.
+ * @see addHasOnlyDefaultValues
+ **/
+ protected function addHasOnlyDefaultValuesBody(&$script) {
+ $table = $this->getTable();
+ $colsWithDefaults = array();
+ foreach ($table->getColumns() as $col) {
+ $def = $col->getDefaultValue();
+ if ($def !== null && !$def->isExpression()) {
+ $colsWithDefaults[] = $col;
+ }
+ }
+
+ foreach ($colsWithDefaults as $col) {
+
+ $clo = strtolower($col->getName());
+ $def = $col->getDefaultValue();
+
+ $script .= "
+ if (\$this->$clo !== " . $this->getDefaultValueString($col).") {
+ return false;
+ }
+";
+ }
+ }
+
+ /**
+ * Adds the function close for the hasOnlyDefaultValues method
+ * @param string &$script The script will be modified in this method.
+ * @see addHasOnlyDefaultValues
+ **/
+ protected function addHasOnlyDefaultValuesClose(&$script) {
+ $script .= "
+ // otherwise, everything was equal, so return TRUE
+ return true;";
+ $script .= "
+ } // hasOnlyDefaultValues()
+";
+ }
+
+ /**
+ * Adds the hydrate() method, which sets attributes of the object based on a ResultSet.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addHydrate(&$script)
+ {
+ $this->addHydrateComment($script);
+ $this->addHydrateOpen($script);
+ $this->addHydrateBody($script);
+ $this->addHydrateClose($script);
+ }
+
+ /**
+ * Adds the comment for the hydrate method
+ * @param string &$script The script will be modified in this method.
+ * @see addHydrate()
+ */
+ protected function addHydrateComment(&$script) {
+ $script .= "
+ /**
+ * Hydrates (populates) the object variables with values from the database resultset.
+ *
+ * An offset (0-based \"start column\") is specified so that objects can be hydrated
+ * with a subset of the columns in the resultset rows. This is needed, for example,
+ * for results of JOIN queries where the resultset row includes columns from two or
+ * more tables.
+ *
+ * @param array \$row The row returned by PDOStatement->fetch(PDO::FETCH_NUM)
+ * @param int \$startcol 0-based offset column which indicates which restultset column to start with.
+ * @param boolean \$rehydrate Whether this object is being re-hydrated from the database.
+ * @return int next starting column
+ * @throws PropelException - Any caught Exception will be rewrapped as a PropelException.
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the hydrate method
+ * @param string &$script The script will be modified in this method.
+ * @see addHydrate()
+ */
+ protected function addHydrateOpen(&$script) {
+ $script .= "
+ public function hydrate(\$row, \$startcol = 0, \$rehydrate = false)
+ {";
+ }
+
+ /**
+ * Adds the function body for the hydrate method
+ * @param string &$script The script will be modified in this method.
+ * @see addHydrate()
+ */
+ protected function addHydrateBody(&$script) {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ $script .= "
+ try {
+";
+ $n = 0;
+ foreach ($table->getColumns() as $col) {
+ if (!$col->isLazyLoad()) {
+ $clo = strtolower($col->getName());
+ if ($col->getType() === PropelTypes::CLOB_EMU && $this->getPlatform() instanceof OraclePlatform) {
+ // PDO_OCI returns a stream for CLOB objects, while other PDO adapters return a string...
+ $script .= "
+ \$this->$clo = stream_get_contents(\$row[\$startcol + $n]);";
+ } elseif ($col->isLobType() && !$platform->hasStreamBlobImpl()) {
+ $script .= "
+ if (\$row[\$startcol + $n] !== null) {
+ \$this->$clo = fopen('php://memory', 'r+');
+ fwrite(\$this->$clo, \$row[\$startcol + $n]);
+ rewind(\$this->$clo);
+ } else {
+ \$this->$clo = null;
+ }";
+ } elseif ($col->isPhpPrimitiveType()) {
+ $script .= "
+ \$this->$clo = (\$row[\$startcol + $n] !== null) ? (".$col->getPhpType().") \$row[\$startcol + $n] : null;";
+ } elseif ($col->isPhpObjectType()) {
+ $script .= "
+ \$this->$clo = (\$row[\$startcol + $n] !== null) ? new ".$col->getPhpType()."(\$row[\$startcol + $n]) : null;";
+ } else {
+ $script .= "
+ \$this->$clo = \$row[\$startcol + $n];";
+ }
+ $n++;
+ } // if col->isLazyLoad()
+ } /* foreach */
+
+ if ($this->getBuildProperty("addSaveMethod")) {
+ $script .= "
+ \$this->resetModified();
+";
+ }
+
+ $script .= "
+ \$this->setNew(false);
+
+ if (\$rehydrate) {
+ \$this->ensureConsistency();
+ }
+
+ return \$startcol + $n; // $n = ".$this->getPeerClassname()."::NUM_COLUMNS - ".$this->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS).
+
+ } catch (Exception \$e) {
+ throw new PropelException(\"Error populating ".$this->getStubObjectBuilder()->getClassname()." object\", \$e);
+ }";
+ }
+
+ /**
+ * Adds the function close for the hydrate method
+ * @param string &$script The script will be modified in this method.
+ * @see addHydrate()
+ */
+ protected function addHydrateClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addBuildPkeyCriteria(&$script) {
+ $this->addBuildPkeyCriteriaComment($script);
+ $this->addBuildPkeyCriteriaOpen($script);
+ $this->addBuildPkeyCriteriaBody($script);
+ $this->addBuildPkeyCriteriaClose($script);
+ }
+
+ /**
+ * Adds the comment for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaComment(&$script) {
+ $script .= "
+ /**
+ * Builds a Criteria object containing the primary key for this object.
+ *
+ * Unlike buildCriteria() this method includes the primary key values regardless
+ * of whether or not they have been modified.
+ *
+ * @return Criteria The Criteria object containing value(s) for primary key(s).
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaOpen(&$script) {
+ $script .= "
+ public function buildPkeyCriteria()
+ {";
+ }
+
+ /**
+ * Adds the function body for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaBody(&$script) {
+ $script .= "
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";
+ foreach ($this->getTable()->getPrimaryKey() as $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);";
+ }
+ }
+
+ /**
+ * Adds the function close for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaClose(&$script) {
+ $script .= "
+
+ return \$criteria;
+ }
+";
+ }
+
+ /**
+ * Adds the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addBuildCriteria(&$script)
+ {
+ $this->addBuildCriteriaComment($script);
+ $this->addBuildCriteriaOpen($script);
+ $this->addBuildCriteriaBody($script);
+ $this->addBuildCriteriaClose($script);
+ }
+
+ /**
+ * Adds comment for the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaComment(&$script) {
+ $script .= "
+ /**
+ * Build a Criteria object containing the values of all modified columns in this object.
+ *
+ * @return Criteria The Criteria object containing all modified values.
+ */";
+ }
+
+ /**
+ * Adds the function declaration of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaOpen(&$script) {
+ $script .= "
+ public function buildCriteria()
+ {";
+ }
+
+ /**
+ * Adds the function body of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaBody(&$script) {
+ $script .= "
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
+";
+ foreach ($this->getTable()->getColumns() as $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ if (\$this->isColumnModified(".$this->getColumnConstant($col).")) \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);";
+ }
+ }
+
+ /**
+ * Adds the function close of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaClose(&$script) {
+ $script .= "
+
+ return \$criteria;
+ }
+";
+ }
+
+ /**
+ * Adds the toArray method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addToArray(&$script)
+ {
+ $fks = $this->getTable()->getForeignKeys();
+ $hasFks = count($fks) > 0;
+ $script .= "
+ /**
+ * Exports the object as an array.
+ *
+ * You can specify the key type of the array by passing one of the class
+ * type constants.
+ *
+ * @param string \$keyType (optional) One of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME,
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM.
+ * Defaults to BasePeer::TYPE_PHPNAME.
+ * @param boolean \$includeLazyLoadColumns (optional) Whether to include lazy loaded columns. Defaults to TRUE.";
+ if ($hasFks) {
+ $script .= "
+ * @param boolean \$includeForeignObjects (optional) Whether to include hydrated related objects. Default to FALSE.";
+ }
+ $script .= "
+ *
+ * @return array an associative array containing the field names (as keys) and field values
+ */
+ public function toArray(\$keyType = BasePeer::TYPE_PHPNAME, \$includeLazyLoadColumns = true" . ($hasFks ? ", \$includeForeignObjects = false" : '') . ")
+ {
+ \$keys = ".$this->getPeerClassname()."::getFieldNames(\$keyType);
+ \$result = array(";
+ foreach ($this->getTable()->getColumns() as $num => $col) {
+ if ($col->isLazyLoad()) {
+ $script .= "
+ \$keys[$num] => (\$includeLazyLoadColumns) ? \$this->get".$col->getPhpName()."() : null,";
+ } else {
+ $script .= "
+ \$keys[$num] => \$this->get".$col->getPhpName()."(),";
+ }
+ }
+ $script .= "
+ );";
+ if ($hasFks) {
+ $script .= "
+ if (\$includeForeignObjects) {";
+ foreach ($fks as $fk) {
+ $script .= "
+ if (null !== \$this->" . $this->getFKVarName($fk) . ") {
+ \$result['" . $this->getFKPhpNameAffix($fk, $plural = false) . "'] = \$this->" . $this->getFKVarName($fk) . "->toArray(\$keyType, \$includeLazyLoadColumns, true);
+ }";
+ }
+ $script .= "
+ }";
+ }
+ $script .= "
+ return \$result;
+ }
+";
+ } // addToArray()
+
+ /**
+ * Adds the getByName method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetByName(&$script)
+ {
+ $this->addGetByNameComment($script);
+ $this->addGetByNameOpen($script);
+ $this->addGetByNameBody($script);
+ $this->addGetByNameClose($script);
+ }
+
+ /**
+ * Adds the comment for the getByName method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByName
+ **/
+ protected function addGetByNameComment(&$script) {
+ $script .= "
+ /**
+ * Retrieves a field from the object by name passed in as a string.
+ *
+ * @param string \$name name
+ * @param string \$type The type of fieldname the \$name is of:
+ * one of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
+ * @return mixed Value of field.
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the getByName method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByName
+ **/
+ protected function addGetByNameOpen(&$script) {
+ $script .= "
+ public function getByName(\$name, \$type = BasePeer::TYPE_PHPNAME)
+ {";
+ }
+
+ /**
+ * Adds the function body for the getByName method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByName
+ **/
+ protected function addGetByNameBody(&$script) {
+ $script .= "
+ \$pos = ".$this->getPeerClassname()."::translateFieldName(\$name, \$type, BasePeer::TYPE_NUM);
+ \$field = \$this->getByPosition(\$pos);";
+ }
+
+ /**
+ * Adds the function close for the getByName method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByName
+ **/
+ protected function addGetByNameClose(&$script) {
+ $script .= "
+ return \$field;
+ }
+";
+ }
+
+ /**
+ * Adds the getByPosition method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addGetByPosition(&$script)
+ {
+ $this->addGetByPositionComment($script);
+ $this->addGetByPositionOpen($script);
+ $this->addGetByPositionBody($script);
+ $this->addGetByPositionClose($script);
+ }
+
+ /**
+ * Adds comment for the getByPosition method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByPosition
+ **/
+ protected function addGetByPositionComment(&$script) {
+ $script .= "
+ /**
+ * Retrieves a field from the object by Position as specified in the xml schema.
+ * Zero-based.
+ *
+ * @param int \$pos position in xml schema
+ * @return mixed Value of field at \$pos
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the getByPosition method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByPosition
+ **/
+ protected function addGetByPositionOpen(&$script) {
+ $script .= "
+ public function getByPosition(\$pos)
+ {";
+ }
+
+ /**
+ * Adds the function body for the getByPosition method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByPosition
+ **/
+ protected function addGetByPositionBody(&$script) {
+ $table = $this->getTable();
+ $script .= "
+ switch(\$pos) {";
+ $i = 0;
+ foreach ($table->getColumns() as $col) {
+ $cfc = $col->getPhpName();
+ $script .= "
+ case $i:
+ return \$this->get$cfc();
+ break;";
+ $i++;
+ } /* foreach */
+ $script .= "
+ default:
+ return null;
+ break;
+ } // switch()";
+ }
+
+ /**
+ * Adds the function close for the getByPosition method
+ * @param string &$script The script will be modified in this method.
+ * @see addGetByPosition
+ **/
+ protected function addGetByPositionClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ protected function addSetByName(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Sets a field from the object by name passed in as a string.
+ *
+ * @param string \$name peer name
+ * @param mixed \$value field value
+ * @param string \$type The type of fieldname the \$name is of:
+ * one of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
+ * @return void
+ */
+ public function setByName(\$name, \$value, \$type = BasePeer::TYPE_PHPNAME)
+ {
+ \$pos = ".$this->getPeerClassname()."::translateFieldName(\$name, \$type, BasePeer::TYPE_NUM);
+ return \$this->setByPosition(\$pos, \$value);
+ }
+";
+ }
+
+ protected function addSetByPosition(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Sets a field from the object by Position as specified in the xml schema.
+ * Zero-based.
+ *
+ * @param int \$pos position in xml schema
+ * @param mixed \$value field value
+ * @return void
+ */
+ public function setByPosition(\$pos, \$value)
+ {
+ switch(\$pos) {";
+ $i = 0;
+ foreach ($table->getColumns() as $col) {
+ $cfc = $col->getPhpName();
+ $cptype = $col->getPhpType();
+ $script .= "
+ case $i:
+ \$this->set$cfc(\$value);
+ break;";
+ $i++;
+ } /* foreach */
+ $script .= "
+ } // switch()
+ }
+";
+ } // addSetByPosition()
+
+ protected function addFromArray(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Populates the object using an array.
+ *
+ * This is particularly useful when populating an object from one of the
+ * request arrays (e.g. \$_POST). This method goes through the column
+ * names, checking to see whether a matching key exists in populated
+ * array. If so the setByName() method is called for that column.
+ *
+ * You can specify the key type of the array by additionally passing one
+ * of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME,
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM.
+ * The default key type is the column's phpname (e.g. 'AuthorId')
+ *
+ * @param array \$arr An array to populate the object from.
+ * @param string \$keyType The type of keys the array uses.
+ * @return void
+ */
+ public function fromArray(\$arr, \$keyType = BasePeer::TYPE_PHPNAME)
+ {
+ \$keys = ".$this->getPeerClassname()."::getFieldNames(\$keyType);
+";
+ foreach ($table->getColumns() as $num => $col) {
+ $cfc = $col->getPhpName();
+ $cptype = $col->getPhpType();
+ $script .= "
+ if (array_key_exists(\$keys[$num], \$arr)) \$this->set$cfc(\$arr[\$keys[$num]]);";
+ } /* foreach */
+ $script .= "
+ }
+";
+ } // addFromArray
+
+ /**
+ * Adds a delete() method to remove the object form the datastore.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDelete(&$script)
+ {
+ $this->addDeleteComment($script);
+ $this->addDeleteOpen($script);
+ $this->addDeleteBody($script);
+ $this->addDeleteClose($script);
+ }
+
+ /**
+ * Adds the comment for the delete function
+ * @param string &$script The script will be modified in this method.
+ * @see addDelete()
+ **/
+ protected function addDeleteComment(&$script) {
+ $script .= "
+ /**
+ * Removes this object from datastore and sets delete attribute.
+ *
+ * @param PropelPDO \$con
+ * @return void
+ * @throws PropelException
+ * @see BaseObject::setDeleted()
+ * @see BaseObject::isDeleted()
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the delete function
+ * @param string &$script The script will be modified in this method.
+ * @see addDelete()
+ **/
+ protected function addDeleteOpen(&$script) {
+ $script .= "
+ public function delete(PropelPDO \$con = null)
+ {";
+ }
+
+ /**
+ * Adds the function body for the delete function
+ * @param string &$script The script will be modified in this method.
+ * @see addDelete()
+ **/
+ protected function addDeleteBody(&$script) {
+ $script .= "
+ if (\$this->isDeleted()) {
+ throw new PropelException(\"This object has already been deleted.\");
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$con->beginTransaction();
+ try {";
+ if($this->getGeneratorConfig()->getBuildProperty('addHooks')) {
+ $script .= "
+ \$ret = \$this->preDelete(\$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('preDelete', $script, " ");
+ $script .= "
+ if (\$ret) {
+ ".$this->getQueryClassname()."::create()
+ ->filterByPrimaryKey(\$this->getPrimaryKey())
+ ->delete(\$con);
+ \$this->postDelete(\$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('postDelete', $script, " ");
+ $script .= "
+ \$con->commit();
+ \$this->setDeleted(true);
+ } else {
+ \$con->commit();
+ }";
+ } else {
+ // apply behaviors
+ $this->applyBehaviorModifier('preDelete', $script, " ");
+ $script .= "
+ ".$this->getPeerClassname()."::doDelete(\$this, \$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('postDelete', $script, " ");
+ $script .= "
+ \$con->commit();
+ \$this->setDeleted(true);";
+ }
+
+ $script .= "
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }";
+ }
+
+ /**
+ * Adds the function close for the delete function
+ * @param string &$script The script will be modified in this method.
+ * @see addDelete()
+ **/
+ protected function addDeleteClose(&$script) {
+ $script .= "
+ }
+";
+ } // addDelete()
+
+ /**
+ * Adds a reload() method to re-fetch the data for this object from the database.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addReload(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Reloads this object from datastore based on primary key and (optionally) resets all associated objects.
+ *
+ * This will only work if the object has been saved and has a valid primary key set.
+ *
+ * @param boolean \$deep (optional) Whether to also de-associated any related objects.
+ * @param PropelPDO \$con (optional) The PropelPDO connection to use.
+ * @return void
+ * @throws PropelException - if this object is deleted, unsaved or doesn't have pk match in db
+ */
+ public function reload(\$deep = false, PropelPDO \$con = null)
+ {
+ if (\$this->isDeleted()) {
+ throw new PropelException(\"Cannot reload a deleted object.\");
+ }
+
+ if (\$this->isNew()) {
+ throw new PropelException(\"Cannot reload an unsaved object.\");
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ // We don't need to alter the object instance pool; we're just modifying this instance
+ // already in the pool.
+
+ \$stmt = ".$this->getPeerClassname()."::doSelectStmt(\$this->buildPkeyCriteria(), \$con);
+ \$row = \$stmt->fetch(PDO::FETCH_NUM);
+ \$stmt->closeCursor();
+ if (!\$row) {
+ throw new PropelException('Cannot find matching row in the database to reload object values.');
+ }
+ \$this->hydrate(\$row, 0, true); // rehydrate
+";
+
+ // support for lazy load columns
+ foreach ($table->getColumns() as $col) {
+ if ($col->isLazyLoad()) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ // Reset the $clo lazy-load column
+ \$this->" . $clo . " = null;
+ \$this->".$clo."_isLoaded = false;
+";
+ }
+ }
+
+ $script .= "
+ if (\$deep) { // also de-associate any related objects?
+";
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $varName = $this->getFKVarName($fk);
+ $script .= "
+ \$this->".$varName." = null;";
+ }
+
+ foreach ($table->getReferrers() as $refFK) {
+ if ($refFK->isLocalPrimaryKey()) {
+ $script .= "
+ \$this->".$this->getPKRefFKVarName($refFK)." = null;
+";
+ } else {
+ $script .= "
+ \$this->".$this->getRefFKCollVarName($refFK)." = null;
+";
+ }
+ }
+
+ $script .= "
+ } // if (deep)
+ }
+";
+ } // addReload()
+
+ /**
+ * Adds the methods related to refreshing, saving and deleting the object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addManipulationMethods(&$script)
+ {
+ $this->addReload($script);
+ $this->addDelete($script);
+ $this->addSave($script);
+ $this->addDoSave($script);
+ }
+
+ /**
+ * Adds the methods related to validationg the object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addValidationMethods(&$script)
+ {
+ $this->addValidationFailuresAttribute($script);
+ $this->addGetValidationFailures($script);
+ $this->addValidate($script);
+ $this->addDoValidate($script);
+ }
+
+ /**
+ * Adds the $validationFailures attribute to store ValidationFailed objects.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addValidationFailuresAttribute(&$script)
+ {
+ $script .= "
+ /**
+ * Array of ValidationFailed objects.
+ * @var array ValidationFailed[]
+ */
+ protected \$validationFailures = array();
+";
+ }
+
+ /**
+ * Adds the getValidationFailures() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetValidationFailures(&$script)
+ {
+ $script .= "
+ /**
+ * Gets any ValidationFailed objects that resulted from last call to validate().
+ *
+ *
+ * @return array ValidationFailed[]
+ * @see validate()
+ */
+ public function getValidationFailures()
+ {
+ return \$this->validationFailures;
+ }
+";
+ } // addGetValidationFailures()
+
+ /**
+ * Adds the correct getPrimaryKey() method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPrimaryKey(&$script)
+ {
+ $pkeys = $this->getTable()->getPrimaryKey();
+ if (count($pkeys) == 1) {
+ $this->addGetPrimaryKey_SinglePK($script);
+ } elseif (count($pkeys) > 1) {
+ $this->addGetPrimaryKey_MultiPK($script);
+ } else {
+ // no primary key -- this is deprecated, since we don't *need* this method anymore
+ $this->addGetPrimaryKey_NoPK($script);
+ }
+ }
+
+ /**
+ * Adds the getPrimaryKey() method for tables that contain a single-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPrimaryKey_SinglePK(&$script)
+ {
+ $table = $this->getTable();
+ $pkeys = $table->getPrimaryKey();
+ $cptype = $pkeys[0]->getPhpType();
+
+ $script .= "
+ /**
+ * Returns the primary key for this object (row).
+ * @return $cptype
+ */
+ public function getPrimaryKey()
+ {
+ return \$this->get".$pkeys[0]->getPhpName()."();
+ }
+";
+ } // addetPrimaryKey_SingleFK
+
+ /**
+ * Adds the setPrimaryKey() method for tables that contain a multi-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPrimaryKey_MultiPK(&$script)
+ {
+
+ $script .= "
+ /**
+ * Returns the composite primary key for this object.
+ * The array elements will be in same order as specified in XML.
+ * @return array
+ */
+ public function getPrimaryKey()
+ {
+ \$pks = array();";
+ $i = 0;
+ foreach ($this->getTable()->getPrimaryKey() as $pk) {
+ $script .= "
+ \$pks[$i] = \$this->get".$pk->getPhpName()."();";
+ $i++;
+ } /* foreach */
+ $script .= "
+
+ return \$pks;
+ }
+";
+ } // addGetPrimaryKey_MultiFK()
+
+ /**
+ * Adds the getPrimaryKey() method for objects that have no primary key.
+ * This "feature" is dreprecated, since the getPrimaryKey() method is not required
+ * by the Persistent interface (or used by the templates). Hence, this method is also
+ * deprecated.
+ * @param string &$script The script will be modified in this method.
+ * @deprecated
+ */
+ protected function addGetPrimaryKey_NoPK(&$script)
+ {
+ $script .= "
+ /**
+ * Returns NULL since this table doesn't have a primary key.
+ * This method exists only for BC and is deprecated!
+ * @return null
+ */
+ public function getPrimaryKey()
+ {
+ return null;
+ }
+";
+ }
+ /**
+ * Adds the correct setPrimaryKey() method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addSetPrimaryKey(&$script)
+ {
+ $pkeys = $this->getTable()->getPrimaryKey();
+ if (count($pkeys) == 1) {
+ $this->addSetPrimaryKey_SinglePK($script);
+ } elseif (count($pkeys) > 1) {
+ $this->addSetPrimaryKey_MultiPK($script);
+ } else {
+ // no primary key -- this is deprecated, since we don't *need* this method anymore
+ $this->addSetPrimaryKey_NoPK($script);
+ }
+ }
+
+ /**
+ * Adds the setPrimaryKey() method for tables that contain a single-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addSetPrimaryKey_SinglePK(&$script)
+ {
+
+ $pkeys = $this->getTable()->getPrimaryKey();
+ $col = $pkeys[0];
+ $clo=strtolower($col->getName());
+ $ctype = $col->getPhpType();
+
+ $script .= "
+ /**
+ * Generic method to set the primary key ($clo column).
+ *
+ * @param $ctype \$key Primary key.
+ * @return void
+ */
+ public function setPrimaryKey(\$key)
+ {
+ \$this->set".$col->getPhpName()."(\$key);
+ }
+";
+ } // addSetPrimaryKey_SinglePK
+
+ /**
+ * Adds the setPrimaryKey() method for tables that contain a multi-columnprimary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addSetPrimaryKey_MultiPK(&$script)
+ {
+
+ $script .="
+ /**
+ * Set the [composite] primary key.
+ *
+ * @param array \$keys The elements of the composite key (order must match the order in XML file).
+ * @return void
+ */
+ public function setPrimaryKey(\$keys)
+ {";
+ $i = 0;
+ foreach ($this->getTable()->getPrimaryKey() as $pk) {
+ $pktype = $pk->getPhpType();
+ $script .= "
+ \$this->set".$pk->getPhpName()."(\$keys[$i]);";
+ $i++;
+ } /* foreach ($table->getPrimaryKey() */
+ $script .= "
+ }
+";
+ } // addSetPrimaryKey_MultiPK
+
+ /**
+ * Adds the setPrimaryKey() method for objects that have no primary key.
+ * This "feature" is dreprecated, since the setPrimaryKey() method is not required
+ * by the Persistent interface (or used by the templates). Hence, this method is also
+ * deprecated.
+ * @param string &$script The script will be modified in this method.
+ * @deprecated
+ */
+ protected function addSetPrimaryKey_NoPK(&$script)
+ {
+ $script .="
+ /**
+ * Dummy primary key setter.
+ *
+ * This function only exists to preserve backwards compatibility. It is no longer
+ * needed or required by the Persistent interface. It will be removed in next BC-breaking
+ * release of Propel.
+ *
+ * @deprecated
+ */
+ public function setPrimaryKey(\$pk)
+ {
+ // do nothing, because this object doesn't have any primary keys
+ }
+";
+ }
+
+ /**
+ * Adds the isPrimaryKeyNull() method
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIsPrimaryKeyNull(&$script)
+ {
+ $table = $this->getTable();
+ $pkeys = $table->getPrimaryKey();
+
+ $script .= "
+ /**
+ * Returns true if the primary key for this object is null.
+ * @return boolean
+ */
+ public function isPrimaryKeyNull()
+ {";
+ if (count($pkeys) == 1) {
+ $script .= "
+ return null === \$this->get" . $pkeys[0]->getPhpName() . "();";
+ } else {
+ $tests = array();
+ foreach ($pkeys as $pkey) {
+ $tests[]= "(null === \$this->get" . $pkey->getPhpName() . "())";
+ }
+ $script .= "
+ return " . join(' && ', $tests) . ";";
+ }
+ $script .= "
+ }
+";
+ } // addetPrimaryKey_SingleFK
+
+ // --------------------------------------------------------------------
+ // Complex OM Methods
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructs variable name for fkey-related objects.
+ * @param ForeignKey $fk
+ * @return string
+ */
+ protected function getFKVarName(ForeignKey $fk)
+ {
+ return 'a' . $this->getFKPhpNameAffix($fk, $plural = false);
+ }
+
+ /**
+ * Constructs variable name for objects which referencing current table by specified foreign key.
+ * @param ForeignKey $fk
+ * @return string
+ */
+ protected function getRefFKCollVarName(ForeignKey $fk)
+ {
+ return 'coll' . $this->getRefFKPhpNameAffix($fk, $plural = true);
+ }
+
+ /**
+ * Constructs variable name for single object which references current table by specified foreign key
+ * which is ALSO a primary key (hence one-to-one relationship).
+ * @param ForeignKey $fk
+ * @return string
+ */
+ protected function getPKRefFKVarName(ForeignKey $fk)
+ {
+ return 'single' . $this->getRefFKPhpNameAffix($fk, $plural = false);
+ }
+
+ // ----------------------------------------------------------------
+ //
+ // F K M E T H O D S
+ //
+ // ----------------------------------------------------------------
+
+ /**
+ * Adds the methods that get & set objects related by foreign key to the current object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKMethods(&$script)
+ {
+ foreach ($this->getTable()->getForeignKeys() as $fk) {
+ $this->declareClassFromBuilder($this->getNewStubObjectBuilder($fk->getForeignTable()));
+ $this->declareClassFromBuilder($this->getNewStubQueryBuilder($fk->getForeignTable()));
+ $this->addFKMutator($script, $fk);
+ $this->addFKAccessor($script, $fk);
+ } // foreach fk
+ }
+
+ /**
+ * Adds the class attributes that are needed to store fkey related objects.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKAttributes(&$script, ForeignKey $fk)
+ {
+ $className = $this->getForeignTable($fk)->getPhpName();
+ $varName = $this->getFKVarName($fk);
+
+ $script .= "
+ /**
+ * @var $className
+ */
+ protected $".$varName.";
+";
+ }
+
+ /**
+ * Adds the mutator (setter) method for setting an fkey related object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKMutator(&$script, ForeignKey $fk)
+ {
+ $table = $this->getTable();
+ $tblFK = $this->getForeignTable($fk);
+
+ $joinTableObjectBuilder = $this->getNewObjectBuilder($tblFK);
+ $className = $joinTableObjectBuilder->getObjectClassname();
+
+ $varName = $this->getFKVarName($fk);
+
+ $script .= "
+ /**
+ * Declares an association between this object and a $className object.
+ *
+ * @param $className \$v
+ * @return ".$this->getObjectClassname()." The current object (for fluent API support)
+ * @throws PropelException
+ */
+ public function set".$this->getFKPhpNameAffix($fk, $plural = false)."($className \$v = null)
+ {";
+ foreach ($fk->getLocalColumns() as $columnName) {
+ $column = $table->getColumn($columnName);
+ $lfmap = $fk->getLocalForeignMapping();
+ $colFKName = $lfmap[$columnName];
+ $colFK = $tblFK->getColumn($colFKName);
+ $script .= "
+ if (\$v === null) {
+ \$this->set".$column->getPhpName()."(".$this->getDefaultValueString($column).");
+ } else {
+ \$this->set".$column->getPhpName()."(\$v->get".$colFK->getPhpName()."());
+ }
+";
+
+ } /* foreach local col */
+
+ $script .= "
+ \$this->$varName = \$v;
+";
+
+ // Now add bi-directional relationship binding, taking into account whether this is
+ // a one-to-one relationship.
+
+ if ($fk->isLocalPrimaryKey()) {
+ $script .= "
+ // Add binding for other direction of this 1:1 relationship.
+ if (\$v !== null) {
+ \$v->set".$this->getRefFKPhpNameAffix($fk, $plural = false)."(\$this);
+ }
+";
+ } else {
+ $script .= "
+ // Add binding for other direction of this n:n relationship.
+ // If this object has already been added to the $className object, it will not be re-added.
+ if (\$v !== null) {
+ \$v->add".$this->getRefFKPhpNameAffix($fk, $plural = false)."(\$this);
+ }
+";
+
+ }
+
+ $script .= "
+ return \$this;
+ }
+";
+ }
+
+ /**
+ * Adds the accessor (getter) method for getting an fkey related object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKAccessor(&$script, ForeignKey $fk)
+ {
+ $table = $this->getTable();
+
+ $varName = $this->getFKVarName($fk);
+ $pCollName = $this->getFKPhpNameAffix($fk, $plural = true);
+
+ $fkPeerBuilder = $this->getNewPeerBuilder($this->getForeignTable($fk));
+ $fkQueryBuilder = $this->getNewStubQueryBuilder($this->getForeignTable($fk));
+ $fkObjectBuilder = $this->getNewObjectBuilder($this->getForeignTable($fk))->getStubObjectBuilder();
+ $className = $fkObjectBuilder->getClassname(); // get the Classname that has maybe a prefix
+
+ $and = "";
+ $comma = "";
+ $conditional = "";
+ $argmap = array(); // foreign -> local mapping
+ $argsize = 0;
+ foreach ($fk->getLocalColumns() as $columnName) {
+
+ $lfmap = $fk->getLocalForeignMapping();
+
+ $localColumn = $table->getColumn($columnName);
+ $foreignColumn = $fk->getForeignTable()->getColumn($lfmap[$columnName]);
+
+ $column = $table->getColumn($columnName);
+ $cptype = $column->getPhpType();
+ $clo = strtolower($column->getName());
+
+ if ($cptype == "integer" || $cptype == "float" || $cptype == "double") {
+ $conditional .= $and . "\$this->". $clo ." != 0";
+ } elseif ($cptype == "string") {
+ $conditional .= $and . "(\$this->" . $clo ." !== \"\" && \$this->".$clo." !== null)";
+ } else {
+ $conditional .= $and . "\$this->" . $clo ." !== null";
+ }
+
+ $argmap[] = array('foreign' => $foreignColumn, 'local' => $localColumn);
+ $and = " && ";
+ $comma = ", ";
+ $argsize = $argsize + 1;
+ }
+
+ // If the related column is a primary kay and if it's a simple association,
+ // The use retrieveByPk() instead of doSelect() to take advantage of instance pooling
+ $useRetrieveByPk = count($argmap) == 1 && $argmap[0]['foreign']->isPrimaryKey();
+
+ $script .= "
+
+ /**
+ * Get the associated $className object
+ *
+ * @param PropelPDO Optional Connection object.
+ * @return $className The associated $className object.
+ * @throws PropelException
+ */
+ public function get".$this->getFKPhpNameAffix($fk, $plural = false)."(PropelPDO \$con = null)
+ {";
+ $script .= "
+ if (\$this->$varName === null && ($conditional)) {";
+ if ($useRetrieveByPk) {
+ $script .= "
+ \$this->$varName = ".$fkQueryBuilder->getClassname()."::create()->findPk(\$this->$clo, \$con);";
+ } else {
+ $script .= "
+ \$this->$varName = ".$fkQueryBuilder->getClassname()."::create()
+ ->filterBy" . $this->getRefFKPhpNameAffix($fk, $plural = false) . "(\$this) // here
+ ->findOne(\$con);";
+ }
+ if ($fk->isLocalPrimaryKey()) {
+ $script .= "
+ // Because this foreign key represents a one-to-one relationship, we will create a bi-directional association.
+ \$this->{$varName}->set".$this->getRefFKPhpNameAffix($fk, $plural = false)."(\$this);";
+ } else {
+ $script .= "
+ /* The following can be used additionally to
+ guarantee the related object contains a reference
+ to this object. This level of coupling may, however, be
+ undesirable since it could result in an only partially populated collection
+ in the referenced object.
+ \$this->{$varName}->add".$this->getRefFKPhpNameAffix($fk, $plural = true)."(\$this);
+ */";
+ }
+
+ $script .= "
+ }
+ return \$this->$varName;
+ }
+";
+
+ } // addFKAccessor
+
+ /**
+ * Adds a convenience method for setting a related object by specifying the primary key.
+ * This can be used in conjunction with the getPrimaryKey() for systems where nothing is known
+ * about the actual objects being related.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKByKeyMutator(&$script, ForeignKey $fk)
+ {
+ $table = $this->getTable();
+
+ #$className = $this->getForeignTable($fk)->getPhpName();
+ $methodAffix = $this->getFKPhpNameAffix($fk);
+ #$varName = $this->getFKVarName($fk);
+
+ $script .= "
+ /**
+ * Provides convenient way to set a relationship based on a
+ * key. e.g.
+ * \$bar->setFooKey(\$foo->getPrimaryKey())
+ *";
+ if (count($fk->getLocalColumns()) > 1) {
+ $script .= "
+ * Note: It is important that the xml schema used to create this class
+ * maintains consistency in the order of related columns between
+ * ".$table->getName()." and ". $tblFK->getName().".
+ * If for some reason this is impossible, this method should be
+ * overridden in ".$table->getPhpName().".";
+ }
+ $script .= "
+ * @return ".$this->getObjectClassname()." The current object (for fluent API support)
+ * @throws PropelException
+ */
+ public function set".$methodAffix."Key(\$key)
+ {
+";
+ if (count($fk->getLocalColumns()) > 1) {
+ $i = 0;
+ foreach ($fk->getLocalColumns() as $colName) {
+ $col = $table->getColumn($colName);
+ $fktype = $col->getPhpType();
+ $script .= "
+ \$this->set".$col->getPhpName()."( ($fktype) \$key[$i] );
+";
+ $i++;
+ } /* foreach */
+ } else {
+ $lcols = $fk->getLocalColumns();
+ $colName = $lcols[0];
+ $col = $table->getColumn($colName);
+ $fktype = $col->getPhpType();
+ $script .= "
+ \$this->set".$col->getPhpName()."( ($fktype) \$key);
+";
+ }
+ $script .= "
+ return \$this;
+ }
+";
+ } // addFKByKeyMutator()
+
+ /**
+ * Adds the method that fetches fkey-related (referencing) objects but also joins in data from another table.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKGetJoinMethods(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+ $join_behavior = $this->getGeneratorConfig()->getBuildProperty('useLeftJoinsInDoJoinMethods') ? 'Criteria::LEFT_JOIN' : 'Criteria::INNER_JOIN';
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $fkQueryClassname = $this->getNewStubQueryBuilder($refFK->getTable())->getClassname();
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural=true);
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $fkPeerBuilder = $this->getNewPeerBuilder($tblFK);
+ $className = $fkPeerBuilder->getObjectClassname();
+
+ $lastTable = "";
+ foreach ($tblFK->getForeignKeys() as $fk2) {
+
+ $tblFK2 = $this->getForeignTable($fk2);
+ $doJoinGet = !$tblFK2->isForReferenceOnly();
+
+ // it doesn't make sense to join in rows from the curent table, since we are fetching
+ // objects related to *this* table (i.e. the joined rows will all be the same row as current object)
+ if ($this->getTable()->getPhpName() == $tblFK2->getPhpName()) {
+ $doJoinGet = false;
+ }
+
+ $relCol2 = $this->getFKPhpNameAffix($fk2, $plural = false);
+
+ if ( $this->getRelatedBySuffix($refFK) != "" &&
+ ($this->getRelatedBySuffix($refFK) == $this->getRelatedBySuffix($fk2))) {
+ $doJoinGet = false;
+ }
+
+ if ($doJoinGet) {
+ $script .= "
+
+ /**
+ * If this collection has already been initialized with
+ * an identical criteria, it returns the collection.
+ * Otherwise if this ".$table->getPhpName()." is new, it will return
+ * an empty collection; or if this ".$table->getPhpName()." has previously
+ * been saved, it will retrieve related $relCol from storage.
+ *
+ * This method is protected by default in order to keep the public
+ * api reasonable. You can provide public methods for those you
+ * actually need in ".$table->getPhpName().".
+ *
+ * @param Criteria \$criteria optional Criteria object to narrow the query
+ * @param PropelPDO \$con optional connection object
+ * @param string \$join_behavior optional join type to use (defaults to $join_behavior)
+ * @return PropelCollection|array {$className}[] List of $className objects
+ */
+ public function get".$relCol."Join".$relCol2."(\$criteria = null, \$con = null, \$join_behavior = $join_behavior)
+ {";
+ $script .= "
+ \$query = $fkQueryClassname::create(null, \$criteria);
+ \$query->joinWith('" . $this->getFKPhpNameAffix($fk2, $plural=false) . "', \$join_behavior);
+
+ return \$this->get". $relCol . "(\$query, \$con);
+ }
+";
+ } /* end if ($doJoinGet) */
+
+ } /* end foreach ($tblFK->getForeignKeys() as $fk2) { */
+
+ } // function
+
+
+ // ----------------------------------------------------------------
+ //
+ // R E F E R R E R F K M E T H O D S
+ //
+ // ----------------------------------------------------------------
+
+ /**
+ * Adds the attributes used to store objects that have referrer fkey relationships to this object.
+ * protected collVarName;
+ * private lastVarNameCriteria = null;
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKAttributes(&$script, ForeignKey $refFK)
+ {
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ if ($refFK->isLocalPrimaryKey()) {
+ $script .= "
+ /**
+ * @var $className one-to-one related $className object
+ */
+ protected $".$this->getPKRefFKVarName($refFK).";
+";
+ } else {
+ $script .= "
+ /**
+ * @var array {$className}[] Collection to store aggregation of $className objects.
+ */
+ protected $".$this->getRefFKCollVarName($refFK).";
+";
+ }
+ }
+
+ /**
+ * Adds the methods for retrieving, initializing, adding objects that are related to this one by foreign keys.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKMethods(&$script)
+ {
+ foreach ($this->getTable()->getReferrers() as $refFK) {
+ $this->declareClassFromBuilder($this->getNewStubObjectBuilder($refFK->getTable()));
+ $this->declareClassFromBuilder($this->getNewStubQueryBuilder($refFK->getTable()));
+ if ($refFK->isLocalPrimaryKey()) {
+ $this->addPKRefFKGet($script, $refFK);
+ $this->addPKRefFKSet($script, $refFK);
+ } else {
+ $this->addRefFKClear($script, $refFK);
+ $this->addRefFKInit($script, $refFK);
+ $this->addRefFKGet($script, $refFK);
+ $this->addRefFKCount($script, $refFK);
+ $this->addRefFKAdd($script, $refFK);
+ $this->addRefFKGetJoinMethods($script, $refFK);
+ }
+ }
+ }
+
+ /**
+ * Adds the method that clears the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKClear(&$script, ForeignKey $refFK) {
+
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $script .= "
+ /**
+ * Clears out the $collName collection
+ *
+ * This does not modify the database; however, it will remove any associated objects, causing
+ * them to be refetched by subsequent calls to accessor method.
+ *
+ * @return void
+ * @see add$relCol()
+ */
+ public function clear$relCol()
+ {
+ \$this->$collName = null; // important to set this to NULL since that means it is uninitialized
+ }
+";
+ } // addRefererClear()
+
+ /**
+ * Adds the method that initializes the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKInit(&$script, ForeignKey $refFK) {
+
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $script .= "
+ /**
+ * Initializes the $collName collection.
+ *
+ * By default this just sets the $collName collection to an empty array (like clear$collName());
+ * however, you may wish to override this method in your stub class to provide setting appropriate
+ * to your application -- for example, setting the initial array to the values stored in database.
+ *
+ * @return void
+ */
+ public function init$relCol()
+ {
+ \$this->$collName = new PropelObjectCollection();
+ \$this->{$collName}->setModel('" . $this->getNewStubObjectBuilder($refFK->getTable())->getClassname() . "');
+ }
+";
+ } // addRefererInit()
+
+ /**
+ * Adds the method that adds an object into the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKAdd(&$script, ForeignKey $refFK)
+ {
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $script .= "
+ /**
+ * Method called to associate a $className object to this object
+ * through the $className foreign key attribute.
+ *
+ * @param $className \$l $className
+ * @return void
+ * @throws PropelException
+ */
+ public function add".$this->getRefFKPhpNameAffix($refFK, $plural = false)."($className \$l)
+ {
+ if (\$this->$collName === null) {
+ \$this->init".$this->getRefFKPhpNameAffix($refFK, $plural = true)."();
+ }
+ if (!\$this->{$collName}->contains(\$l)) { // only add it if the **same** object is not already associated
+ \$this->{$collName}[]= \$l;
+ \$l->set".$this->getFKPhpNameAffix($refFK, $plural = false)."(\$this);
+ }
+ }
+";
+ } // addRefererAdd
+
+ /**
+ * Adds the method that returns the size of the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKCount(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $fkQueryClassname = $this->getNewStubQueryBuilder($refFK->getTable())->getClassname();
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $script .= "
+ /**
+ * Returns the number of related $className objects.
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct
+ * @param PropelPDO \$con
+ * @return int Count of related $className objects.
+ * @throws PropelException
+ */
+ public function count$relCol(Criteria \$criteria = null, \$distinct = false, PropelPDO \$con = null)
+ {
+ if(null === \$this->$collName || null !== \$criteria) {
+ if (\$this->isNew() && null === \$this->$collName) {
+ return 0;
+ } else {
+ \$query = $fkQueryClassname::create(null, \$criteria);
+ if(\$distinct) {
+ \$query->distinct();
+ }
+ return \$query
+ ->filterBy" . $this->getFKPhpNameAffix($refFK) . "(\$this)
+ ->count(\$con);
+ }
+ } else {
+ return count(\$this->$collName);
+ }
+ }
+";
+ } // addRefererCount
+
+ /**
+ * Adds the method that returns the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKGet(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $fkQueryClassname = $this->getNewStubQueryBuilder($refFK->getTable())->getClassname();
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $script .= "
+ /**
+ * Gets an array of $className objects which contain a foreign key that references this object.
+ *
+ * If the \$criteria is not null, it is used to always fetch the results from the database.
+ * Otherwise the results are fetched from the database the first time, then cached.
+ * Next time the same method is called without \$criteria, the cached collection is returned.
+ * If this ".$this->getObjectClassname()." is new, it will return
+ * an empty collection or the current collection; the criteria is ignored on a new object.
+ *
+ * @param Criteria \$criteria optional Criteria object to narrow the query
+ * @param PropelPDO \$con optional connection object
+ * @return PropelCollection|array {$className}[] List of $className objects
+ * @throws PropelException
+ */
+ public function get$relCol(\$criteria = null, PropelPDO \$con = null)
+ {
+ if(null === \$this->$collName || null !== \$criteria) {
+ if (\$this->isNew() && null === \$this->$collName) {
+ // return empty collection
+ \$this->init".$this->getRefFKPhpNameAffix($refFK, $plural = true)."();
+ } else {
+ \$$collName = $fkQueryClassname::create(null, \$criteria)
+ ->filterBy" . $this->getFKPhpNameAffix($refFK) . "(\$this)
+ ->find(\$con);
+ if (null !== \$criteria) {
+ return \$$collName;
+ }
+ \$this->$collName = \$$collName;
+ }
+ }
+ return \$this->$collName;
+ }
+";
+ } // addRefererGet()
+
+ /**
+ * Adds the method that gets a one-to-one related referrer fkey.
+ * This is for one-to-one relationship special case.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPKRefFKGet(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $queryClassname = $this->getNewStubQueryBuilder($refFK->getTable())->getClassname();
+
+ $varName = $this->getPKRefFKVarName($refFK);
+
+ $script .= "
+ /**
+ * Gets a single $className object, which is related to this object by a one-to-one relationship.
+ *
+ * @param PropelPDO \$con optional connection object
+ * @return $className
+ * @throws PropelException
+ */
+ public function get".$this->getRefFKPhpNameAffix($refFK, $plural = false)."(PropelPDO \$con = null)
+ {
+";
+ $script .= "
+ if (\$this->$varName === null && !\$this->isNew()) {
+ \$this->$varName = $queryClassname::create()->findPk(\$this->getPrimaryKey(), \$con);
+ }
+
+ return \$this->$varName;
+ }
+";
+ } // addPKRefFKGet()
+
+ /**
+ * Adds the method that sets a one-to-one related referrer fkey.
+ * This is for one-to-one relationships special case.
+ * @param string &$script The script will be modified in this method.
+ * @param ForeignKey $refFK The referencing foreign key.
+ */
+ protected function addPKRefFKSet(&$script, ForeignKey $refFK)
+ {
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $varName = $this->getPKRefFKVarName($refFK);
+
+ $script .= "
+ /**
+ * Sets a single $className object as related to this object by a one-to-one relationship.
+ *
+ * @param $className \$v $className
+ * @return ".$this->getObjectClassname()." The current object (for fluent API support)
+ * @throws PropelException
+ */
+ public function set".$this->getRefFKPhpNameAffix($refFK, $plural = false)."($className \$v = null)
+ {
+ \$this->$varName = \$v;
+
+ // Make sure that that the passed-in $className isn't already associated with this object
+ if (\$v !== null && \$v->get".$this->getFKPhpNameAffix($refFK, $plural = false)."() === null) {
+ \$v->set".$this->getFKPhpNameAffix($refFK, $plural = false)."(\$this);
+ }
+
+ return \$this;
+ }
+";
+ } // addPKRefFKSet
+
+ protected function addCrossFKAttributes(&$script, ForeignKey $crossFK)
+ {
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($crossFK->getForeignTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+ $script .= "
+ /**
+ * @var array {$className}[] Collection to store aggregation of $className objects.
+ */
+ protected $" . $this->getCrossFKVarName($crossFK) . ";
+";
+ }
+
+ protected function getCrossFKVarName(ForeignKey $crossFK)
+ {
+ return 'coll' . $this->getFKPhpNameAffix($crossFK, $plural = true);
+ }
+
+ protected function addCrossFKMethods(&$script)
+ {
+ foreach ($this->getTable()->getCrossFks() as $fkList) {
+ list($refFK, $crossFK) = $fkList;
+ $this->declareClassFromBuilder($this->getNewStubObjectBuilder($crossFK->getForeignTable()));
+ $this->declareClassFromBuilder($this->getNewStubQueryBuilder($crossFK->getForeignTable()));
+
+ $this->addCrossFKClear($script, $crossFK);
+ $this->addCrossFKInit($script, $crossFK);
+ $this->addCrossFKGet($script, $refFK, $crossFK);
+ $this->addCrossFKCount($script, $refFK, $crossFK);
+ $this->addCrossFKAdd($script, $refFK, $crossFK);
+ }
+ }
+
+ /**
+ * Adds the method that clears the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addCrossFKClear(&$script, ForeignKey $crossFK) {
+
+ $relCol = $this->getFKPhpNameAffix($crossFK, $plural = true);
+ $collName = $this->getCrossFKVarName($crossFK);
+
+ $script .= "
+ /**
+ * Clears out the $collName collection
+ *
+ * This does not modify the database; however, it will remove any associated objects, causing
+ * them to be refetched by subsequent calls to accessor method.
+ *
+ * @return void
+ * @see add$relCol()
+ */
+ public function clear$relCol()
+ {
+ \$this->$collName = null; // important to set this to NULL since that means it is uninitialized
+ }
+";
+ } // addRefererClear()
+
+ /**
+ * Adds the method that initializes the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addCrossFKInit(&$script, ForeignKey $crossFK) {
+
+ $relCol = $this->getFKPhpNameAffix($crossFK, $plural = true);
+ $collName = $this->getCrossFKVarName($crossFK);
+ $relatedObjectClassName = $this->getNewStubObjectBuilder($crossFK->getForeignTable())->getClassname();
+
+ $script .= "
+ /**
+ * Initializes the $collName collection.
+ *
+ * By default this just sets the $collName collection to an empty collection (like clear$relCol());
+ * however, you may wish to override this method in your stub class to provide setting appropriate
+ * to your application -- for example, setting the initial array to the values stored in database.
+ *
+ * @return void
+ */
+ public function init$relCol()
+ {
+ \$this->$collName = new PropelObjectCollection();
+ \$this->{$collName}->setModel('$relatedObjectClassName');
+ }
+";
+ }
+
+ protected function addCrossFKGet(&$script, $refFK, $crossFK)
+ {
+ $relatedName = $this->getFKPhpNameAffix($crossFK, $plural = true);
+ $relatedObjectClassName = $this->getNewStubObjectBuilder($crossFK->getForeignTable())->getClassname();
+ $selfRelationName = $this->getFKPhpNameAffix($refFK, $plural = false);
+ $relatedQueryClassName = $this->getNewStubQueryBuilder($crossFK->getForeignTable())->getClassname();
+ $crossRefTableName = $crossFK->getTableName();
+ $collName = $this->getCrossFKVarName($crossFK);
+ $script .= "
+ /**
+ * Gets a collection of $relatedObjectClassName objects related by a many-to-many relationship
+ * to the current object by way of the $crossRefTableName cross-reference table.
+ *
+ * If the \$criteria is not null, it is used to always fetch the results from the database.
+ * Otherwise the results are fetched from the database the first time, then cached.
+ * Next time the same method is called without \$criteria, the cached collection is returned.
+ * If this ".$this->getObjectClassname()." is new, it will return
+ * an empty collection or the current collection; the criteria is ignored on a new object.
+ *
+ * @param Criteria \$criteria Optional query object to filter the query
+ * @param PropelPDO \$con Optional connection object
+ *
+ * @return PropelCollection|array {$relatedObjectClassName}[] List of {$relatedObjectClassName} objects
+ */
+ public function get{$relatedName}(\$criteria = null, PropelPDO \$con = null)
+ {
+ if(null === \$this->$collName || null !== \$criteria) {
+ if (\$this->isNew() && null === \$this->$collName) {
+ // return empty collection
+ \$this->init{$relatedName}();
+ } else {
+ \$$collName = $relatedQueryClassName::create(null, \$criteria)
+ ->filterBy{$selfRelationName}(\$this)
+ ->find(\$con);
+ if (null !== \$criteria) {
+ return \$$collName;
+ }
+ \$this->$collName = \$$collName;
+ }
+ }
+ return \$this->$collName;
+ }
+";
+ }
+
+ protected function addCrossFKCount(&$script, $refFK, $crossFK)
+ {
+ $relatedName = $this->getFKPhpNameAffix($crossFK, $plural = true);
+ $relatedObjectClassName = $this->getNewStubObjectBuilder($crossFK->getForeignTable())->getClassname();
+ $selfRelationName = $this->getFKPhpNameAffix($refFK, $plural = false);
+ $relatedQueryClassName = $this->getNewStubQueryBuilder($crossFK->getForeignTable())->getClassname();
+ $crossRefTableName = $refFK->getTableName();
+ $collName = $this->getCrossFKVarName($crossFK);
+ $script .= "
+ /**
+ * Gets the number of $relatedObjectClassName objects related by a many-to-many relationship
+ * to the current object by way of the $crossRefTableName cross-reference table.
+ *
+ * @param Criteria \$criteria Optional query object to filter the query
+ * @param boolean \$distinct Set to true to force count distinct
+ * @param PropelPDO \$con Optional connection object
+ *
+ * @return int the number of related $relatedObjectClassName objects
+ */
+ public function count{$relatedName}(\$criteria = null, \$distinct = false, PropelPDO \$con = null)
+ {
+ if(null === \$this->$collName || null !== \$criteria) {
+ if (\$this->isNew() && null === \$this->$collName) {
+ return 0;
+ } else {
+ \$query = $relatedQueryClassName::create(null, \$criteria);
+ if(\$distinct) {
+ \$query->distinct();
+ }
+ return \$query
+ ->filterBy{$selfRelationName}(\$this)
+ ->count(\$con);
+ }
+ } else {
+ return count(\$this->$collName);
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the method that adds an object into the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addCrossFKAdd(&$script, ForeignKey $refFK, ForeignKey $crossFK)
+ {
+ $relCol = $this->getFKPhpNameAffix($crossFK, $plural = true);
+ $collName = $this->getCrossFKVarName($crossFK);
+
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $foreignObjectName = '$' . $tblFK->getStudlyPhpName();
+ $crossObjectName = '$' . $crossFK->getForeignTable()->getStudlyPhpName();
+ $crossObjectClassName = $this->getNewObjectBuilder($crossFK->getForeignTable())->getObjectClassname();
+
+ $script .= "
+ /**
+ * Associate a " . $crossObjectClassName . " object to this object
+ * through the " . $tblFK->getName() . " cross reference table.
+ *
+ * @param " . $crossObjectClassName . " " . $crossObjectName . " The $className object to relate
+ * @return void
+ */
+ public function add" . $this->getFKPhpNameAffix($crossFK, $plural = false) . "(" . $crossObjectName. ")
+ {
+ if (\$this->" . $collName . " === null) {
+ \$this->init" . $relCol . "();
+ }
+ if (!\$this->" . $collName . "->contains(" . $crossObjectName . ")) { // only add it if the **same** object is not already associated
+ " . $foreignObjectName . " = new " . $className . "();
+ " . $foreignObjectName . "->set" . $this->getFKPhpNameAffix($crossFK, $plural = false) . "(" . $crossObjectName . ");
+ \$this->add" . $this->getRefFKPhpNameAffix($refFK, $plural = false) . "(" . $foreignObjectName . ");
+
+ \$this->" . $collName . "[]= " . $crossObjectName . ";
+ }
+ }
+";
+ }
+
+ // ----------------------------------------------------------------
+ //
+ // M A N I P U L A T I O N M E T H O D S
+ //
+ // ----------------------------------------------------------------
+
+ /**
+ * Adds the workhourse doSave() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSave(&$script)
+ {
+ $table = $this->getTable();
+
+ $reloadOnUpdate = $table->isReloadOnUpdate();
+ $reloadOnInsert = $table->isReloadOnInsert();
+
+ $script .= "
+ /**
+ * Performs the work of inserting or updating the row in the database.
+ *
+ * If the object is new, it inserts it; otherwise an update is performed.
+ * All related objects are also updated in this method.
+ *
+ * @param PropelPDO \$con";
+ if ($reloadOnUpdate || $reloadOnInsert) {
+ $script .= "
+ * @param boolean \$skipReload Whether to skip the reload for this object from database.";
+ }
+ $script .= "
+ * @return int The number of rows affected by this insert/update and any referring fk objects' save() operations.
+ * @throws PropelException
+ * @see save()
+ */
+ protected function doSave(PropelPDO \$con".($reloadOnUpdate || $reloadOnInsert ? ", \$skipReload = false" : "").")
+ {
+ \$affectedRows = 0; // initialize var to track total num of affected rows
+ if (!\$this->alreadyInSave) {
+ \$this->alreadyInSave = true;
+";
+ if ($reloadOnInsert || $reloadOnUpdate) {
+ $script .= "
+ \$reloadObject = false;
+";
+ }
+
+ if (count($table->getForeignKeys())) {
+
+ $script .= "
+ // We call the save method on the following object(s) if they
+ // were passed to this object by their coresponding set
+ // method. This object relates to these object(s) by a
+ // foreign key reference.
+";
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $aVarName = $this->getFKVarName($fk);
+ $script .= "
+ if (\$this->$aVarName !== null) {
+ if (\$this->" . $aVarName . "->isModified() || \$this->" . $aVarName . "->isNew()) {
+ \$affectedRows += \$this->" . $aVarName . "->save(\$con);
+ }
+ \$this->set".$this->getFKPhpNameAffix($fk, $plural = false)."(\$this->$aVarName);
+ }
+";
+ } // foreach foreign k
+ } // if (count(foreign keys))
+
+ if ($table->hasAutoIncrementPrimaryKey() ) {
+ $script .= "
+ if (\$this->isNew() ) {
+ \$this->modifiedColumns[] = " . $this->getColumnConstant($table->getAutoIncrementPrimaryKey() ) . ";
+ }";
+ }
+
+ $script .= "
+
+ // If this object has been modified, then save it to the database.
+ if (\$this->isModified()) {
+ if (\$this->isNew()) {
+ \$criteria = \$this->buildCriteria();";
+
+
+ foreach ($table->getColumns() as $col) {
+ if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && !$table->isAllowPkInsert()) {
+ $colConst = $this->getColumnConstant($col);
+ $script .= "
+ if (\$criteria->keyContainsValue(" . $colConst . ") ) {
+ throw new PropelException('Cannot insert a value for auto-increment primary key ('." . $colConst . ".')');
+ }
+";
+ if (!$this->getPlatform()->supportsInsertNullPk()) {
+ $script .= "
+ // remove pkey col since this table uses auto-increment and passing a null value for it is not valid
+ \$criteria->remove(" . $colConst . ");
+";
+ }
+ } elseif ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && $table->isAllowPkInsert() && !$this->getPlatform()->supportsInsertNullPk()) {
+ $script .= "
+ // remove pkey col if it is null since this table does not accept that
+ if (\$criteria->containsKey(" . $colConst . ") && !\$criteria->keyContainsValue(" . $colConst . ") ) {
+ \$criteria->remove(" . $colConst . ");
+ }";
+ }
+ }
+
+ $script .= "
+ \$pk = " . $this->getNewPeerBuilder($table)->getBasePeerClassname() . "::doInsert(\$criteria, \$con);";
+ if ($reloadOnInsert) {
+ $script .= "
+ if (!\$skipReload) {
+ \$reloadObject = true;
+ }";
+ }
+ $operator = count($table->getForeignKeys()) ? '+=' : '=';
+ $script .= "
+ \$affectedRows " . $operator . " 1;";
+ if ($table->getIdMethod() != IDMethod::NO_ID_METHOD) {
+
+ if (count($pks = $table->getPrimaryKey())) {
+ foreach ($pks as $pk) {
+ if ($pk->isAutoIncrement()) {
+ if ($table->isAllowPkInsert()) {
+ $script .= "
+ if (\$pk !== null) {
+ \$this->set".$pk->getPhpName()."(\$pk); //[IMV] update autoincrement primary key
+ }";
+ } else {
+ $script .= "
+ \$this->set".$pk->getPhpName()."(\$pk); //[IMV] update autoincrement primary key";
+ }
+ }
+ }
+ }
+ } // if (id method != "none")
+
+ $script .= "
+ \$this->setNew(false);
+ } else {";
+ if ($reloadOnUpdate) {
+ $script .= "
+ if (!\$skipReload) {
+ \$reloadObject = true;
+ }";
+ }
+ $operator = count($table->getForeignKeys()) ? '+=' : '=';
+ $script .= "
+ \$affectedRows " . $operator . " ".$this->getPeerClassname()."::doUpdate(\$this, \$con);
+ }
+";
+
+ // We need to rewind any LOB columns
+ foreach ($table->getColumns() as $col) {
+ $clo = strtolower($col->getName());
+ if ($col->isLobType()) {
+ $script .= "
+ // Rewind the $clo LOB column, since PDO does not rewind after inserting value.
+ if (\$this->$clo !== null && is_resource(\$this->$clo)) {
+ rewind(\$this->$clo);
+ }
+";
+ }
+ }
+
+ $script .= "
+ \$this->resetModified(); // [HL] After being saved an object is no longer 'modified'
+ }
+";
+
+ foreach ($table->getReferrers() as $refFK) {
+
+ if ($refFK->isLocalPrimaryKey()) {
+ $varName = $this->getPKRefFKVarName($refFK);
+ $script .= "
+ if (\$this->$varName !== null) {
+ if (!\$this->{$varName}->isDeleted()) {
+ \$affectedRows += \$this->{$varName}->save(\$con);
+ }
+ }
+";
+ } else {
+ $collName = $this->getRefFKCollVarName($refFK);
+ $script .= "
+ if (\$this->$collName !== null) {
+ foreach (\$this->$collName as \$referrerFK) {
+ if (!\$referrerFK->isDeleted()) {
+ \$affectedRows += \$referrerFK->save(\$con);
+ }
+ }
+ }
+";
+ } // if refFK->isLocalPrimaryKey()
+
+ } /* foreach getReferrers() */
+ $script .= "
+ \$this->alreadyInSave = false;
+";
+ if ($reloadOnInsert || $reloadOnUpdate) {
+ $script .= "
+ if (\$reloadObject) {
+ \$this->reload(\$con);
+ }
+";
+ }
+ $script .= "
+ }
+ return \$affectedRows;
+ } // doSave()
+";
+
+ }
+
+ /**
+ * Adds the $alreadyInSave attribute, which prevents attempting to re-save the same object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAlreadyInSaveAttribute(&$script)
+ {
+ $script .= "
+ /**
+ * Flag to prevent endless save loop, if this object is referenced
+ * by another object which falls in this transaction.
+ * @var boolean
+ */
+ protected \$alreadyInSave = false;
+";
+ }
+
+ /**
+ * Adds the save() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addSave(&$script)
+ {
+ $this->addSaveComment($script);
+ $this->addSaveOpen($script);
+ $this->addSaveBody($script);
+ $this->addSaveClose($script);
+ }
+
+ /**
+ * Adds the comment for the save method
+ * @param string &$script The script will be modified in this method.
+ * @see addSave()
+ **/
+ protected function addSaveComment(&$script) {
+ $table = $this->getTable();
+ $reloadOnUpdate = $table->isReloadOnUpdate();
+ $reloadOnInsert = $table->isReloadOnInsert();
+
+ $script .= "
+ /**
+ * Persists this object to the database.
+ *
+ * If the object is new, it inserts it; otherwise an update is performed.
+ * All modified related objects will also be persisted in the doSave()
+ * method. This method wraps all precipitate database operations in a
+ * single transaction.";
+ if ($reloadOnUpdate) {
+ $script .= "
+ *
+ * Since this table was configured to reload rows on update, the object will
+ * be reloaded from the database if an UPDATE operation is performed (unless
+ * the \$skipReload parameter is TRUE).";
+ }
+ if ($reloadOnInsert) {
+ $script .= "
+ *
+ * Since this table was configured to reload rows on insert, the object will
+ * be reloaded from the database if an INSERT operation is performed (unless
+ * the \$skipReload parameter is TRUE).";
+ }
+ $script .= "
+ *
+ * @param PropelPDO \$con";
+ if ($reloadOnUpdate || $reloadOnInsert) {
+ $script .= "
+ * @param boolean \$skipReload Whether to skip the reload for this object from database.";
+ }
+ $script .= "
+ * @return int The number of rows affected by this insert/update and any referring fk objects' save() operations.
+ * @throws PropelException
+ * @see doSave()
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the save method
+ * @param string &$script The script will be modified in this method.
+ * @see addSave()
+ **/
+ protected function addSaveOpen(&$script) {
+ $table = $this->getTable();
+ $reloadOnUpdate = $table->isReloadOnUpdate();
+ $reloadOnInsert = $table->isReloadOnInsert();
+ $script .= "
+ public function save(PropelPDO \$con = null".($reloadOnUpdate || $reloadOnInsert ? ", \$skipReload = false" : "").")
+ {";
+ }
+
+ /**
+ * Adds the function body for the save method
+ * @param string &$script The script will be modified in this method.
+ * @see addSave()
+ **/
+ protected function addSaveBody(&$script) {
+ $table = $this->getTable();
+ $reloadOnUpdate = $table->isReloadOnUpdate();
+ $reloadOnInsert = $table->isReloadOnInsert();
+
+ $script .= "
+ if (\$this->isDeleted()) {
+ throw new PropelException(\"You cannot save an object that has been deleted.\");
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$con->beginTransaction();
+ \$isInsert = \$this->isNew();
+ try {";
+
+ if($this->getGeneratorConfig()->getBuildProperty('addHooks')) {
+ // save with runtime hools
+ $script .= "
+ \$ret = \$this->preSave(\$con);";
+ $this->applyBehaviorModifier('preSave', $script, " ");
+ $script .= "
+ if (\$isInsert) {
+ \$ret = \$ret && \$this->preInsert(\$con);";
+ $this->applyBehaviorModifier('preInsert', $script, " ");
+ $script .= "
+ } else {
+ \$ret = \$ret && \$this->preUpdate(\$con);";
+ $this->applyBehaviorModifier('preUpdate', $script, " ");
+ $script .= "
+ }
+ if (\$ret) {
+ \$affectedRows = \$this->doSave(\$con".($reloadOnUpdate || $reloadOnInsert ? ", \$skipReload" : "").");
+ if (\$isInsert) {
+ \$this->postInsert(\$con);";
+ $this->applyBehaviorModifier('postInsert', $script, " ");
+ $script .= "
+ } else {
+ \$this->postUpdate(\$con);";
+ $this->applyBehaviorModifier('postUpdate', $script, " ");
+ $script .= "
+ }
+ \$this->postSave(\$con);";
+ $this->applyBehaviorModifier('postSave', $script, " ");
+ $script .= "
+ ".$this->getPeerClassname()."::addInstanceToPool(\$this);
+ } else {
+ \$affectedRows = 0;
+ }
+ \$con->commit();
+ return \$affectedRows;";
+ } else {
+ // save without runtime hooks
+ $this->applyBehaviorModifier('preSave', $script, " ");
+ if ($this->hasBehaviorModifier('preUpdate'))
+ {
+ $script .= "
+ if(!\$isInsert) {";
+ $this->applyBehaviorModifier('preUpdate', $script, " ");
+ $script .= "
+ }";
+ }
+ if ($this->hasBehaviorModifier('preInsert'))
+ {
+ $script .= "
+ if(\$isInsert) {";
+ $this->applyBehaviorModifier('preInsert', $script, " ");
+ $script .= "
+ }";
+ }
+ $script .= "
+ \$affectedRows = \$this->doSave(\$con".($reloadOnUpdate || $reloadOnInsert ? ", \$skipReload" : "").");";
+ $this->applyBehaviorModifier('postSave', $script, " ");
+ if ($this->hasBehaviorModifier('postUpdate'))
+ {
+ $script .= "
+ if(!\$isInsert) {";
+ $this->applyBehaviorModifier('postUpdate', $script, " ");
+ $script .= "
+ }";
+ }
+ if ($this->hasBehaviorModifier('postInsert'))
+ {
+ $script .= "
+ if(\$isInsert) {";
+ $this->applyBehaviorModifier('postInsert', $script, " ");
+ $script .= "
+ }";
+ }
+ $script .= "
+ \$con->commit();
+ ".$this->getPeerClassname()."::addInstanceToPool(\$this);
+ return \$affectedRows;";
+ }
+
+ $script .= "
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }";
+ }
+
+ /**
+ * Adds the function close for the save method
+ * @param string &$script The script will be modified in this method.
+ * @see addSave()
+ **/
+ protected function addSaveClose(&$script) {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the $alreadyInValidation attribute, which prevents attempting to re-validate the same object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAlreadyInValidationAttribute(&$script)
+ {
+ $script .= "
+ /**
+ * Flag to prevent endless validation loop, if this object is referenced
+ * by another object which falls in this transaction.
+ * @var boolean
+ */
+ protected \$alreadyInValidation = false;
+";
+ }
+
+ /**
+ * Adds the validate() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addValidate(&$script)
+ {
+ $script .= "
+ /**
+ * Validates the objects modified field values and all objects related to this table.
+ *
+ * If \$columns is either a column name or an array of column names
+ * only those columns are validated.
+ *
+ * @param mixed \$columns Column name or an array of column names.
+ * @return boolean Whether all columns pass validation.
+ * @see doValidate()
+ * @see getValidationFailures()
+ */
+ public function validate(\$columns = null)
+ {
+ \$res = \$this->doValidate(\$columns);
+ if (\$res === true) {
+ \$this->validationFailures = array();
+ return true;
+ } else {
+ \$this->validationFailures = \$res;
+ return false;
+ }
+ }
+";
+ } // addValidate()
+
+ /**
+ * Adds the workhourse doValidate() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoValidate(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * This function performs the validation work for complex object models.
+ *
+ * In addition to checking the current object, all related objects will
+ * also be validated. If all pass then true is returned; otherwise
+ * an aggreagated array of ValidationFailed objects will be returned.
+ *
+ * @param array \$columns Array of column names to validate.
+ * @return mixed true if all validations pass; array of ValidationFailed objets otherwise.
+ */
+ protected function doValidate(\$columns = null)
+ {
+ if (!\$this->alreadyInValidation) {
+ \$this->alreadyInValidation = true;
+ \$retval = null;
+
+ \$failureMap = array();
+";
+ if (count($table->getForeignKeys()) != 0) {
+ $script .= "
+
+ // We call the validate method on the following object(s) if they
+ // were passed to this object by their coresponding set
+ // method. This object relates to these object(s) by a
+ // foreign key reference.
+";
+ foreach ($table->getForeignKeys() as $fk) {
+ $aVarName = $this->getFKVarName($fk);
+ $script .= "
+ if (\$this->".$aVarName." !== null) {
+ if (!\$this->".$aVarName."->validate(\$columns)) {
+ \$failureMap = array_merge(\$failureMap, \$this->".$aVarName."->getValidationFailures());
+ }
+ }
+";
+ } /* for () */
+ } /* if count(fkeys) */
+
+ $script .= "
+
+ if ((\$retval = ".$this->getPeerClassname()."::doValidate(\$this, \$columns)) !== true) {
+ \$failureMap = array_merge(\$failureMap, \$retval);
+ }
+
+";
+
+ foreach ($table->getReferrers() as $refFK) {
+ if ($refFK->isLocalPrimaryKey()) {
+ $varName = $this->getPKRefFKVarName($refFK);
+ $script .= "
+ if (\$this->$varName !== null) {
+ if (!\$this->".$varName."->validate(\$columns)) {
+ \$failureMap = array_merge(\$failureMap, \$this->".$varName."->getValidationFailures());
+ }
+ }
+";
+ } else {
+ $collName = $this->getRefFKCollVarName($refFK);
+ $script .= "
+ if (\$this->$collName !== null) {
+ foreach (\$this->$collName as \$referrerFK) {
+ if (!\$referrerFK->validate(\$columns)) {
+ \$failureMap = array_merge(\$failureMap, \$referrerFK->getValidationFailures());
+ }
+ }
+ }
+";
+ }
+ } /* foreach getReferrers() */
+
+ $script .= "
+
+ \$this->alreadyInValidation = false;
+ }
+
+ return (!empty(\$failureMap) ? \$failureMap : true);
+ }
+";
+ } // addDoValidate()
+
+ /**
+ * Adds the ensureConsistency() method to ensure that internal state is correct.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addEnsureConsistency(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * Checks and repairs the internal consistency of the object.
+ *
+ * This method is executed after an already-instantiated object is re-hydrated
+ * from the database. It exists to check any foreign keys to make sure that
+ * the objects related to the current object are correct based on foreign key.
+ *
+ * You can override this method in the stub class, but you should always invoke
+ * the base method from the overridden method (i.e. parent::ensureConsistency()),
+ * in case your model changes.
+ *
+ * @throws PropelException
+ */
+ public function ensureConsistency()
+ {
+";
+ foreach ($table->getColumns() as $col) {
+
+ $clo=strtolower($col->getName());
+
+ if ($col->isForeignKey()) {
+ foreach ($col->getForeignKeys() as $fk) {
+
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $colFK = $tblFK->getColumn($fk->getMappedForeignColumn($col->getName()));
+ $varName = $this->getFKVarName($fk);
+
+ $script .= "
+ if (\$this->".$varName." !== null && \$this->$clo !== \$this->".$varName."->get".$colFK->getPhpName()."()) {
+ \$this->$varName = null;
+ }";
+ } // foraech
+ } /* if col is foreign key */
+
+ } // foreach
+
+ $script .= "
+ } // ensureConsistency
+";
+ } // addCheckRelConsistency
+
+ /**
+ * Adds the copy() method, which (in complex OM) includes the $deepCopy param for making copies of related objects.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addCopy(&$script)
+ {
+ $this->addCopyInto($script);
+
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * Makes a copy of this object that will be inserted as a new row in table when saved.
+ * It creates a new object filling in the simple attributes, but skipping any primary
+ * keys that are defined for the table.
+ *
+ * If desired, this method can also make copies of all associated (fkey referrers)
+ * objects.
+ *
+ * @param boolean \$deepCopy Whether to also copy all rows that refer (by fkey) to the current row.
+ * @return ".$this->getObjectClassname()." Clone of current object.
+ * @throws PropelException
+ */
+ public function copy(\$deepCopy = false)
+ {
+ // we use get_class(), because this might be a subclass
+ \$clazz = get_class(\$this);
+ " . $this->buildObjectInstanceCreationCode('$copyObj', '$clazz') . "
+ \$this->copyInto(\$copyObj, \$deepCopy);
+ return \$copyObj;
+ }
+";
+ } // addCopy()
+
+ /**
+ * Adds the copyInto() method, which takes an object and sets contents to match current object.
+ * In complex OM this method includes the $deepCopy param for making copies of related objects.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addCopyInto(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * Sets contents of passed object to values from current object.
+ *
+ * If desired, this method can also make copies of all associated (fkey referrers)
+ * objects.
+ *
+ * @param object \$copyObj An object of ".$this->getObjectClassname()." (or compatible) type.
+ * @param boolean \$deepCopy Whether to also copy all rows that refer (by fkey) to the current row.
+ * @throws PropelException
+ */
+ public function copyInto(\$copyObj, \$deepCopy = false)
+ {";
+
+ $autoIncCols = array();
+ foreach ($table->getColumns() as $col) {
+ /* @var $col Column */
+ if ($col->isAutoIncrement()) {
+ $autoIncCols[] = $col;
+ }
+ }
+
+ foreach ($table->getColumns() as $col) {
+ if (!in_array($col, $autoIncCols, true)) {
+ $script .= "
+ \$copyObj->set".$col->getPhpName()."(\$this->".strtolower($col->getName()).");";
+ }
+ } // foreach
+
+ // Avoid useless code by checking to see if there are any referrers
+ // to this table:
+ if (count($table->getReferrers()) > 0) {
+ $script .= "
+
+ if (\$deepCopy) {
+ // important: temporarily setNew(false) because this affects the behavior of
+ // the getter/setter methods for fkey referrer objects.
+ \$copyObj->setNew(false);
+";
+ foreach ($table->getReferrers() as $fk) {
+ //HL: commenting out self-referrential check below
+ // it seems to work as expected and is probably desireable to have those referrers from same table deep-copied.
+ //if ( $fk->getTable()->getName() != $table->getName() ) {
+
+ if ($fk->isLocalPrimaryKey()) {
+
+ $afx = $this->getRefFKPhpNameAffix($fk, $plural = false);
+ $script .= "
+ \$relObj = \$this->get$afx();
+ if (\$relObj) {
+ \$copyObj->set$afx(\$relObj->copy(\$deepCopy));
+ }
+";
+ } else {
+
+ $script .= "
+ foreach (\$this->get".$this->getRefFKPhpNameAffix($fk, true)."() as \$relObj) {
+ if (\$relObj !== \$this) { // ensure that we don't try to copy a reference to ourselves
+ \$copyObj->add".$this->getRefFKPhpNameAffix($fk)."(\$relObj->copy(\$deepCopy));
+ }
+ }
+";
+ }
+ // HL: commenting out close of self-referential check
+ // } /* if tblFK != table */
+ } /* foreach */
+ $script .= "
+ } // if (\$deepCopy)
+";
+ } /* if (count referrers > 0 ) */
+
+ $script .= "
+
+ \$copyObj->setNew(true);";
+
+ // Note: we're no longer resetting non-autoincrement primary keys to default values
+ // due to: http://propel.phpdb.org/trac/ticket/618
+ foreach ($autoIncCols as $col) {
+ $coldefval = $col->getPhpDefaultValue();
+ $coldefval = var_export($coldefval, true);
+ $script .= "
+ \$copyObj->set".$col->getPhpName() ."($coldefval); // this is a auto-increment column, so set to default value";
+ } // foreach
+ $script .= "
+ }
+";
+ } // addCopyInto()
+
+ /**
+ * Adds clear method
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClear(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+ /**
+ * Clears the current object and sets all attributes to their default values
+ */
+ public function clear()
+ {";
+ foreach ($table->getColumns() as $col) {
+ $script .= "
+ \$this->" . strtolower($col->getName()) . " = null;";
+ }
+
+ $script .= "
+ \$this->alreadyInSave = false;
+ \$this->alreadyInValidation = false;
+ \$this->clearAllReferences();";
+
+ if ($this->hasDefaultValues()) {
+ $script .= "
+ \$this->applyDefaultValues();";
+ }
+
+ $script .= "
+ \$this->resetModified();
+ \$this->setNew(true);
+ \$this->setDeleted(false);
+ }
+";
+ }
+
+
+ /**
+ * Adds clearAllReferencers() method which resets all the collections of referencing
+ * fk objects.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClearAllReferences(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Resets all collections of referencing foreign keys.
+ *
+ * This method is a user-space workaround for PHP's inability to garbage collect objects
+ * with circular references. This is currently necessary when using Propel in certain
+ * daemon or large-volumne/high-memory operations.
+ *
+ * @param boolean \$deep Whether to also clear the references on all associated objects.
+ */
+ public function clearAllReferences(\$deep = false)
+ {
+ if (\$deep) {";
+ $vars = array();
+ foreach ($this->getTable()->getReferrers() as $refFK) {
+ if ($refFK->isLocalPrimaryKey()) {
+ $varName = $this->getPKRefFKVarName($refFK);
+ $vars[] = $varName;
+ $script .= "
+ if (\$this->$varName) {
+ \$this->{$varName}->clearAllReferences(\$deep);
+ }";
+ } else {
+ $varName = $this->getRefFKCollVarName($refFK);
+ $vars[] = $varName;
+ $script .= "
+ if (\$this->$varName) {
+ foreach ((array) \$this->$varName as \$o) {
+ \$o->clearAllReferences(\$deep);
+ }
+ }";
+ }
+ }
+
+ $script .= "
+ } // if (\$deep)
+";
+
+ $this->applyBehaviorModifier('objectClearReferences', $script, " ");
+
+ foreach ($vars as $varName) {
+ $script .= "
+ \$this->$varName = null;";
+ }
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $className = $this->getForeignTable($fk)->getPhpName();
+ $varName = $this->getFKVarName($fk);
+ $script .= "
+ \$this->$varName = null;";
+ }
+
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds a magic __toString() method if a string column was defined as primary string
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPrimaryString(&$script)
+ {
+ foreach ($this->getTable()->getColumns() as $column) {
+ if ($column->isPrimaryString()) {
+ $script .= "
+ /**
+ * Return the string representation of this object
+ *
+ * @return string The value of the '{$column->getName()}' column
+ */
+ public function __toString()
+ {
+ return (string) \$this->get{$column->getPhpName()}();
+ }
+";
+ break;
+ }
+ }
+ }
+
+ /**
+ * Adds a magic __call() method
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addMagicCall(&$script)
+ {
+ $script .= "
+ /**
+ * Catches calls to virtual methods
+ */
+ public function __call(\$name, \$params)
+ {";
+ $this->applyBehaviorModifier('objectCall', $script, " ");
+ $script .= "
+ if (preg_match('/get(\w+)/', \$name, \$matches)) {
+ \$virtualColumn = \$matches[1];
+ if (\$this->hasVirtualColumn(\$virtualColumn)) {
+ return \$this->getVirtualColumn(\$virtualColumn);
+ }
+ // no lcfirst in php<5.3...
+ \$virtualColumn[0] = strtolower(\$virtualColumn[0]);
+ if (\$this->hasVirtualColumn(\$virtualColumn)) {
+ return \$this->getVirtualColumn(\$virtualColumn);
+ }
+ }
+ throw new PropelException('Call to undefined method: ' . \$name);
+ }
+";
+ }
+} // PHP5ObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectNoCollectionBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectNoCollectionBuilder.php
new file mode 100644
index 000000000..8b96998a9
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5ObjectNoCollectionBuilder.php
@@ -0,0 +1,962 @@
+
+ * propel.builder.object.class = builder.om.PHP5ObjectNoCollectionBuilder
+ *
+ *
+ * @deprecated Since Propel 1.5
+ * @author Hans Lellelid
+ * @package propel.generator.builder.om
+ */
+class PHP5ObjectNoCollectionBuilder extends PHP5ObjectBuilder
+{
+
+ /**
+ * Adds the lazy loader method.
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see parent::addColumnAccessors()
+ */
+ protected function addLazyLoader(&$script, Column $col)
+ {
+ $this->addLazyLoaderComment($script, $col);
+ $this->addLazyLoaderOpen($script, $col);
+ $this->addLazyLoaderBody($script, $col);
+ $this->addLazyLoaderClose($script, $col);
+ }
+
+ /**
+ * Adds the comment for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderComment(&$script, Column $col) {
+ $clo = strtolower($col->getName());
+
+ $script .= "
+ /**
+ * Load the value for the lazy-loaded [$clo] column.
+ *
+ * This method performs an additional query to return the value for
+ * the [$clo] column, since it is not populated by
+ * the hydrate() method.
+ *
+ * @param \$con PropelPDO (optional) The PropelPDO connection to use.
+ * @return void
+ * @throws PropelException - any underlying error will be wrapped and re-thrown.
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderOpen(&$script, Column $col) {
+ $cfc = $col->getPhpName();
+ $script .= "
+ protected function load$cfc(PropelPDO \$con = null)
+ {";
+ }
+
+ /**
+ * Adds the function body for the lazy loader method
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderBody(&$script, Column $col) {
+ $platform = $this->getPlatform();
+ $clo = strtolower($col->getName());
+
+ $script .= "
+ \$c = \$this->buildPkeyCriteria();
+ \$c->addSelectColumn(".$this->getColumnConstant($col).");
+ try {
+ \$stmt = ".$this->getPeerClassname()."::doSelectStmt(\$c, \$con);
+ \$row = \$stmt->fetch(PDO::FETCH_NUM);
+ \$stmt->closeCursor();";
+
+ if ($col->getType() === PropelTypes::CLOB && $this->getPlatform() instanceof OraclePlatform) {
+ // PDO_OCI returns a stream for CLOB objects, while other PDO adapters return a string...
+ $script .= "
+ \$this->$clo = stream_get_contents(\$row[0]);";
+ } elseif ($col->isLobType() && !$platform->hasStreamBlobImpl()) {
+ $script .= "
+ if (\$row[0] !== null) {
+ \$this->$clo = fopen('php://memory', 'r+');
+ fwrite(\$this->$clo, \$row[0]);
+ rewind(\$this->$clo);
+ } else {
+ \$this->$clo = null;
+ }";
+ } elseif ($col->isPhpPrimitiveType()) {
+ $script .= "
+ \$this->$clo = (\$row[0] !== null) ? (".$col->getPhpType().") \$row[0] : null;";
+ } elseif ($col->isPhpObjectType()) {
+ $script .= "
+ \$this->$clo = (\$row[0] !== null) ? new ".$col->getPhpType()."(\$row[0]) : null;";
+ } else {
+ $script .= "
+ \$this->$clo = \$row[0];";
+ }
+
+ $script .= "
+ \$this->".$clo."_isLoaded = true;
+ } catch (Exception \$e) {
+ throw new PropelException(\"Error loading value for [$clo] column on demand.\", \$e);
+ }";
+ }
+
+ /**
+ * Adds the function close for the lazy loader
+ * @param string &$script The script will be modified in this method.
+ * @param Column $col The current column.
+ * @see addLazyLoader()
+ **/
+ protected function addLazyLoaderClose(&$script, Column $col) {
+ $script .= "
+ }";
+ } // addLazyLoader()
+
+ /**
+ * Adds the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addBuildPkeyCriteria(&$script) {
+ $this->addBuildPkeyCriteriaComment($script);
+ $this->addBuildPkeyCriteriaOpen($script);
+ $this->addBuildPkeyCriteriaBody($script);
+ $this->addBuildPkeyCriteriaClose($script);
+ }
+
+ /**
+ * Adds the comment for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaComment(&$script) {
+ $script .= "
+ /**
+ * Builds a Criteria object containing the primary key for this object.
+ *
+ * Unlike buildCriteria() this method includes the primary key values regardless
+ * of whether or not they have been modified.
+ *
+ * @return Criteria The Criteria object containing value(s) for primary key(s).
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaOpen(&$script) {
+ $script .= "
+ public function buildPkeyCriteria()
+ {";
+ }
+
+ /**
+ * Adds the function body for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaBody(&$script) {
+ $script .= "
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";
+ foreach ($this->getTable()->getColumns() as $col) {
+ $clo = strtolower($col->getName());
+ if ($col->isPrimaryKey()) {
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);";
+ }
+ }
+ }
+
+ /**
+ * Adds the function close for the buildPkeyCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildPkeyCriteria()
+ **/
+ protected function addBuildPkeyCriteriaClose(&$script) {
+ $script .= "
+
+ return \$criteria;
+ }
+";
+ }
+
+ /**
+ * Adds the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addBuildCriteria(&$script)
+ {
+ $this->addBuildCriteriaComment($script);
+ $this->addBuildCriteriaOpen($script);
+ $this->addBuildCriteriaBody($script);
+ $this->addBuildCriteriaClose($script);
+ }
+
+ /**
+ * Adds comment for the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaComment(&$script) {
+ $script .= "
+ /**
+ * Build a Criteria object containing the values of all modified columns in this object.
+ *
+ * @return Criteria The Criteria object containing all modified values.
+ */";
+ }
+
+ /**
+ * Adds the function declaration of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaOpen(&$script) {
+ $script .= "
+ public function buildCriteria()
+ {";
+ }
+
+ /**
+ * Adds the function body of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaBody(&$script) {
+ $script .= "
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
+";
+ foreach ($this->getTable()->getColumns() as $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ if (\$this->isColumnModified(".$this->getColumnConstant($col).")) \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);";
+ }
+ }
+
+ /**
+ * Adds the function close of the buildCriteria method
+ * @param string &$script The script will be modified in this method.
+ * @see addBuildCriteria()
+ **/
+ protected function addBuildCriteriaClose(&$script) {
+ $script .= "
+
+ return \$criteria;
+ }
+";
+ }
+
+ /**
+ * Adds the function body for the delete function
+ * @param string &$script The script will be modified in this method.
+ * @see addDelete()
+ **/
+ protected function addDeleteBody(&$script) {
+ $script .= "
+ if (\$this->isDeleted()) {
+ throw new PropelException(\"This object has already been deleted.\");
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$con->beginTransaction();
+ try {";
+ if($this->getGeneratorConfig()->getBuildProperty('addHooks')) {
+ $script .= "
+ \$ret = \$this->preDelete(\$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('preDelete', $script, " ");
+ $script .= "
+ if (\$ret) {
+ ".$this->getPeerClassname()."::doDelete(\$this, \$con);
+ \$this->postDelete(\$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('postDelete', $script, " ");
+ $script .= "
+ \$con->commit();
+ \$this->setDeleted(true);
+ } else {
+ \$con->commit();
+ }";
+ } else {
+ // apply behaviors
+ $this->applyBehaviorModifier('preDelete', $script, " ");
+ $script .= "
+ ".$this->getPeerClassname()."::doDelete(\$this, \$con);";
+ // apply behaviors
+ $this->applyBehaviorModifier('postDelete', $script, " ");
+ $script .= "
+ \$con->commit();
+ \$this->setDeleted(true);";
+ }
+
+ $script .= "
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }";
+ }
+
+ /**
+ * Adds a reload() method to re-fetch the data for this object from the database.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addReload(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Reloads this object from datastore based on primary key and (optionally) resets all associated objects.
+ *
+ * This will only work if the object has been saved and has a valid primary key set.
+ *
+ * @param boolean \$deep (optional) Whether to also de-associated any related objects.
+ * @param PropelPDO \$con (optional) The PropelPDO connection to use.
+ * @return void
+ * @throws PropelException - if this object is deleted, unsaved or doesn't have pk match in db
+ */
+ public function reload(\$deep = false, PropelPDO \$con = null)
+ {
+ if (\$this->isDeleted()) {
+ throw new PropelException(\"Cannot reload a deleted object.\");
+ }
+
+ if (\$this->isNew()) {
+ throw new PropelException(\"Cannot reload an unsaved object.\");
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ // We don't need to alter the object instance pool; we're just modifying this instance
+ // already in the pool.
+
+ \$stmt = ".$this->getPeerClassname()."::doSelectStmt(\$this->buildPkeyCriteria(), \$con);
+ \$row = \$stmt->fetch(PDO::FETCH_NUM);
+ \$stmt->closeCursor();
+ if (!\$row) {
+ throw new PropelException('Cannot find matching row in the database to reload object values.');
+ }
+ \$this->hydrate(\$row, 0, true); // rehydrate
+";
+
+ // support for lazy load columns
+ foreach ($table->getColumns() as $col) {
+ if ($col->isLazyLoad()) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ // Reset the $clo lazy-load column
+ \$this->" . $clo . " = null;
+ \$this->".$clo."_isLoaded = false;
+";
+ }
+ }
+
+ $script .= "
+ if (\$deep) { // also de-associate any related objects?
+";
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $varName = $this->getFKVarName($fk);
+ $script .= "
+ \$this->".$varName." = null;";
+ }
+
+ foreach ($table->getReferrers() as $refFK) {
+ if ($refFK->isLocalPrimaryKey()) {
+ $script .= "
+ \$this->".$this->getPKRefFKVarName($refFK)." = null;
+";
+ } else {
+ $script .= "
+ \$this->".$this->getRefFKCollVarName($refFK)." = null;
+ \$this->".$this->getRefFKLastCriteriaVarName($refFK)." = null;
+";
+ }
+ }
+
+ $script .= "
+ } // if (deep)
+ }
+";
+ } // addReload()
+
+ /**
+ * Gets variable name for the Criteria which was used to fetch the objects which
+ * referencing current table by specified foreign key.
+ * @param ForeignKey $fk
+ * @return string
+ */
+ protected function getRefFKLastCriteriaVarName(ForeignKey $fk)
+ {
+ return 'last' . $this->getRefFKPhpNameAffix($fk, $plural = false) . 'Criteria';
+ }
+
+
+
+ /**
+ * Adds the accessor (getter) method for getting an fkey related object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFKAccessor(&$script, ForeignKey $fk)
+ {
+ $table = $this->getTable();
+
+ $varName = $this->getFKVarName($fk);
+ $pCollName = $this->getFKPhpNameAffix($fk, $plural = true);
+
+ $fkPeerBuilder = $this->getNewPeerBuilder($this->getForeignTable($fk));
+ $fkObjectBuilder = $this->getNewObjectBuilder($this->getForeignTable($fk))->getStubObjectBuilder();
+ $className = $fkObjectBuilder->getClassname(); // get the Classname that has maybe a prefix
+
+ $and = "";
+ $comma = "";
+ $conditional = "";
+ $argmap = array(); // foreign -> local mapping
+ $argsize = 0;
+ foreach ($fk->getLocalColumns() as $columnName) {
+
+ $lfmap = $fk->getLocalForeignMapping();
+
+ $localColumn = $table->getColumn($columnName);
+ $foreignColumn = $fk->getForeignTable()->getColumn($lfmap[$columnName]);
+
+ $column = $table->getColumn($columnName);
+ $cptype = $column->getPhpType();
+ $clo = strtolower($column->getName());
+
+ if ($cptype == "integer" || $cptype == "float" || $cptype == "double") {
+ $conditional .= $and . "\$this->". $clo ." != 0";
+ } elseif ($cptype == "string") {
+ $conditional .= $and . "(\$this->" . $clo ." !== \"\" && \$this->".$clo." !== null)";
+ } else {
+ $conditional .= $and . "\$this->" . $clo ." !== null";
+ }
+
+ $argmap[] = array('foreign' => $foreignColumn, 'local' => $localColumn);
+ $and = " && ";
+ $comma = ", ";
+ $argsize = $argsize + 1;
+ }
+
+ // If the related column is a primary kay and if it's a simple association,
+ // The use retrieveByPk() instead of doSelect() to take advantage of instance pooling
+ $useRetrieveByPk = count($argmap) == 1 && $argmap[0]['foreign']->isPrimaryKey();
+
+ $script .= "
+
+ /**
+ * Get the associated $className object
+ *
+ * @param PropelPDO Optional Connection object.
+ * @return $className The associated $className object.
+ * @throws PropelException
+ */
+ public function get".$this->getFKPhpNameAffix($fk, $plural = false)."(PropelPDO \$con = null)
+ {";
+ $script .= "
+ if (\$this->$varName === null && ($conditional)) {";
+ if ($useRetrieveByPk) {
+ $script .= "
+ \$this->$varName = ".$fkPeerBuilder->getPeerClassname()."::retrieveByPk(\$this->$clo);";
+ } else {
+ $script .= "
+ \$c = new Criteria(".$fkPeerBuilder->getPeerClassname()."::DATABASE_NAME);";
+ foreach ($argmap as $el) {
+ $fcol = $el['foreign'];
+ $lcol = $el['local'];
+ $clo = strtolower($lcol->getName());
+ $script .= "
+ \$c->add(".$fkPeerBuilder->getColumnConstant($fcol).", \$this->".$clo.");";
+ }
+ $script .= "
+ \$this->$varName = ".$fkPeerBuilder->getPeerClassname()."::doSelectOne(\$c, \$con);";
+ }
+ if ($fk->isLocalPrimaryKey()) {
+ $script .= "
+ // Because this foreign key represents a one-to-one relationship, we will create a bi-directional association.
+ \$this->{$varName}->set".$this->getRefFKPhpNameAffix($fk, $plural = false)."(\$this);";
+ } else {
+ $script .= "
+ /* The following can be used additionally to
+ guarantee the related object contains a reference
+ to this object. This level of coupling may, however, be
+ undesirable since it could result in an only partially populated collection
+ in the referenced object.
+ \$this->{$varName}->add".$this->getRefFKPhpNameAffix($fk, $plural = true)."(\$this);
+ */";
+ }
+
+ $script .= "
+ }
+ return \$this->$varName;
+ }
+";
+
+ } // addFKAccessor
+
+
+ /**
+ * Adds the method that fetches fkey-related (referencing) objects but also joins in data from another table.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKGetJoinMethods(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+ $join_behavior = $this->getGeneratorConfig()->getBuildProperty('useLeftJoinsInDoJoinMethods') ? 'Criteria::LEFT_JOIN' : 'Criteria::INNER_JOIN';
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural=true);
+ $collName = $this->getRefFKCollVarName($refFK);
+ $lastCriteriaName = $this->getRefFKLastCriteriaVarName($refFK);
+
+ $fkPeerBuilder = $this->getNewPeerBuilder($tblFK);
+
+ $lastTable = "";
+ foreach ($tblFK->getForeignKeys() as $fk2) {
+
+ $tblFK2 = $this->getForeignTable($fk2);
+ $doJoinGet = !$tblFK2->isForReferenceOnly();
+
+ // it doesn't make sense to join in rows from the curent table, since we are fetching
+ // objects related to *this* table (i.e. the joined rows will all be the same row as current object)
+ if ($this->getTable()->getPhpName() == $tblFK2->getPhpName()) {
+ $doJoinGet = false;
+ }
+
+ $relCol2 = $this->getFKPhpNameAffix($fk2, $plural = false);
+
+ if ( $this->getRelatedBySuffix($refFK) != "" &&
+ ($this->getRelatedBySuffix($refFK) == $this->getRelatedBySuffix($fk2))) {
+ $doJoinGet = false;
+ }
+
+ if ($doJoinGet) {
+ $script .= "
+
+ /**
+ * If this collection has already been initialized with
+ * an identical criteria, it returns the collection.
+ * Otherwise if this ".$table->getPhpName()." is new, it will return
+ * an empty collection; or if this ".$table->getPhpName()." has previously
+ * been saved, it will retrieve related $relCol from storage.
+ *
+ * This method is protected by default in order to keep the public
+ * api reasonable. You can provide public methods for those you
+ * actually need in ".$table->getPhpName().".
+ */
+ public function get".$relCol."Join".$relCol2."(\$criteria = null, \$con = null, \$join_behavior = $join_behavior)
+ {";
+ $script .= "
+ if (\$criteria === null) {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ }
+ elseif (\$criteria instanceof Criteria)
+ {
+ \$criteria = clone \$criteria;
+ }
+
+ if (\$this->$collName === null) {
+ if (\$this->isNew()) {
+ \$this->$collName = array();
+ } else {
+";
+ foreach ($refFK->getForeignColumns() as $columnName) {
+ $column = $table->getColumn($columnName);
+ $flMap = $refFK->getForeignLocalMapping();
+ $colFKName = $flMap[$columnName];
+ $colFK = $tblFK->getColumn($colFKName);
+ if ($colFK === null) {
+ throw new EngineException("Column $colFKName not found in " . $tblFK->getName());
+ }
+ $clo = strtolower($column->getName());
+ $script .= "
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } // end foreach ($fk->getForeignColumns()
+
+ $script .= "
+ \$this->$collName = ".$fkPeerBuilder->getPeerClassname()."::doSelectJoin$relCol2(\$criteria, \$con, \$join_behavior);
+ }
+ } else {
+ // the following code is to determine if a new query is
+ // called for. If the criteria is the same as the last
+ // one, just return the collection.
+";
+ foreach ($refFK->getForeignColumns() as $columnName) {
+ $column = $table->getColumn($columnName);
+ $flMap = $refFK->getForeignLocalMapping();
+ $colFKName = $flMap[$columnName];
+ $colFK = $tblFK->getColumn($colFKName);
+ $clo = strtolower($column->getName());
+ $script .= "
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } /* end foreach ($fk->getForeignColumns() */
+
+ $script .= "
+ if (!isset(\$this->$lastCriteriaName) || !\$this->".$lastCriteriaName."->equals(\$criteria)) {
+ \$this->$collName = ".$fkPeerBuilder->getPeerClassname()."::doSelectJoin$relCol2(\$criteria, \$con, \$join_behavior);
+ }
+ }
+ \$this->$lastCriteriaName = \$criteria;
+
+ return \$this->$collName;
+ }
+";
+ } /* end if ($doJoinGet) */
+
+ } /* end foreach ($tblFK->getForeignKeys() as $fk2) { */
+
+ } // function
+
+ /**
+ * Adds the method that initializes the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKInit(&$script, ForeignKey $refFK) {
+
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $script .= "
+ /**
+ * Initializes the $collName collection (array).
+ *
+ * By default this just sets the $collName collection to an empty array (like clear$collName());
+ * however, you may wish to override this method in your stub class to provide setting appropriate
+ * to your application -- for example, setting the initial array to the values stored in database.
+ *
+ * @return void
+ */
+ public function init$relCol()
+ {
+ \$this->$collName = array();
+ }
+";
+ } // addRefererInit()
+
+ /**
+ * Adds the method that adds an object into the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKAdd(&$script, ForeignKey $refFK)
+ {
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $collName = $this->getRefFKCollVarName($refFK);
+
+ $script .= "
+ /**
+ * Method called to associate a $className object to this object
+ * through the $className foreign key attribute.
+ *
+ * @param $className \$l $className
+ * @return void
+ * @throws PropelException
+ */
+ public function add".$this->getRefFKPhpNameAffix($refFK, $plural = false)."($className \$l)
+ {
+ if (\$this->$collName === null) {
+ \$this->init".$this->getRefFKPhpNameAffix($refFK, $plural = true)."();
+ }
+ if (!in_array(\$l, \$this->$collName, true)) { // only add it if the **same** object is not already associated
+ array_push(\$this->$collName, \$l);
+ \$l->set".$this->getFKPhpNameAffix($refFK, $plural = false)."(\$this);
+ }
+ }
+";
+ } // addRefererAdd
+
+ /**
+ * Adds the method that returns the size of the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKCount(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+
+ $fkPeerBuilder = $this->getNewPeerBuilder($refFK->getTable());
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+
+ $collName = $this->getRefFKCollVarName($refFK);
+ $lastCriteriaName = $this->getRefFKLastCriteriaVarName($refFK);
+
+ $className = $fkPeerBuilder->getObjectClassname();
+
+ $script .= "
+ /**
+ * Returns the number of related $className objects.
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct
+ * @param PropelPDO \$con
+ * @return int Count of related $className objects.
+ * @throws PropelException
+ */
+ public function count$relCol(Criteria \$criteria = null, \$distinct = false, PropelPDO \$con = null)
+ {";
+
+ $script .= "
+ if (\$criteria === null) {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ } else {
+ \$criteria = clone \$criteria;
+ }
+
+ if (\$distinct) {
+ \$criteria->setDistinct();
+ }
+
+ \$count = null;
+
+ if (\$this->$collName === null) {
+ if (\$this->isNew()) {
+ \$count = 0;
+ } else {
+";
+ foreach ($refFK->getLocalColumns() as $colFKName) {
+ // $colFKName is local to the referring table (i.e. foreign to this table)
+ $lfmap = $refFK->getLocalForeignMapping();
+ $localColumn = $this->getTable()->getColumn($lfmap[$colFKName]);
+ $colFK = $refFK->getTable()->getColumn($colFKName);
+ $clo = strtolower($localColumn->getName());
+ $script .= "
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } // end foreach ($fk->getForeignColumns()
+
+ $script .= "
+ \$count = ".$fkPeerBuilder->getPeerClassname()."::doCount(\$criteria, false, \$con);
+ }
+ } else {
+ // criteria has no effect for a new object
+ if (!\$this->isNew()) {
+ // the following code is to determine if a new query is
+ // called for. If the criteria is the same as the last
+ // one, just return count of the collection.
+";
+ foreach ($refFK->getLocalColumns() as $colFKName) {
+ // $colFKName is local to the referring table (i.e. foreign to this table)
+ $lfmap = $refFK->getLocalForeignMapping();
+ $localColumn = $this->getTable()->getColumn($lfmap[$colFKName]);
+ $colFK = $refFK->getTable()->getColumn($colFKName);
+ $clo = strtolower($localColumn->getName());
+ $script .= "
+
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } // foreach ($fk->getForeignColumns()
+ $script .= "
+ if (!isset(\$this->$lastCriteriaName) || !\$this->".$lastCriteriaName."->equals(\$criteria)) {
+ \$count = ".$fkPeerBuilder->getPeerClassname()."::doCount(\$criteria, false, \$con);
+ } else {
+ \$count = count(\$this->$collName);
+ }
+ } else {
+ \$count = count(\$this->$collName);
+ }
+ }
+ return \$count;
+ }
+";
+ } // addRefererCount
+
+ /**
+ * Adds the method that returns the referrer fkey collection.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRefFKGet(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $peerClassname = $this->getStubPeerBuilder()->getClassname();
+ $fkPeerBuilder = $this->getNewPeerBuilder($refFK->getTable());
+ $relCol = $this->getRefFKPhpNameAffix($refFK, $plural = true);
+
+ $collName = $this->getRefFKCollVarName($refFK);
+ $lastCriteriaName = $this->getRefFKLastCriteriaVarName($refFK);
+
+ $className = $fkPeerBuilder->getObjectClassname();
+
+ $script .= "
+ /**
+ * Gets an array of $className objects which contain a foreign key that references this object.
+ *
+ * If this collection has already been initialized with an identical Criteria, it returns the collection.
+ * Otherwise if this ".$this->getObjectClassname()." has previously been saved, it will retrieve
+ * related $relCol from storage. If this ".$this->getObjectClassname()." is new, it will return
+ * an empty collection or the current collection, the criteria is ignored on a new object.
+ *
+ * @param PropelPDO \$con
+ * @param Criteria \$criteria
+ * @return array {$className}[]
+ * @throws PropelException
+ */
+ public function get$relCol(\$criteria = null, PropelPDO \$con = null)
+ {";
+
+ $script .= "
+ if (\$criteria === null) {
+ \$criteria = new Criteria($peerClassname::DATABASE_NAME);
+ }
+ elseif (\$criteria instanceof Criteria)
+ {
+ \$criteria = clone \$criteria;
+ }
+
+ if (\$this->$collName === null) {
+ if (\$this->isNew()) {
+ \$this->$collName = array();
+ } else {
+";
+ foreach ($refFK->getLocalColumns() as $colFKName) {
+ // $colFKName is local to the referring table (i.e. foreign to this table)
+ $lfmap = $refFK->getLocalForeignMapping();
+ $localColumn = $this->getTable()->getColumn($lfmap[$colFKName]);
+ $colFK = $refFK->getTable()->getColumn($colFKName);
+
+ $clo = strtolower($localColumn->getName());
+
+ $script .= "
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } // end foreach ($fk->getForeignColumns()
+
+ $script .= "
+ ".$fkPeerBuilder->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$this->$collName = ".$fkPeerBuilder->getPeerClassname()."::doSelect(\$criteria, \$con);
+ }
+ } else {
+ // criteria has no effect for a new object
+ if (!\$this->isNew()) {
+ // the following code is to determine if a new query is
+ // called for. If the criteria is the same as the last
+ // one, just return the collection.
+";
+ foreach ($refFK->getLocalColumns() as $colFKName) {
+ // $colFKName is local to the referring table (i.e. foreign to this table)
+ $lfmap = $refFK->getLocalForeignMapping();
+ $localColumn = $this->getTable()->getColumn($lfmap[$colFKName]);
+ $colFK = $refFK->getTable()->getColumn($colFKName);
+ $clo = strtolower($localColumn->getName());
+ $script .= "
+
+ \$criteria->add(".$fkPeerBuilder->getColumnConstant($colFK).", \$this->$clo);
+";
+ } // foreach ($fk->getForeignColumns()
+ $script .= "
+ ".$fkPeerBuilder->getPeerClassname()."::addSelectColumns(\$criteria);
+ if (!isset(\$this->$lastCriteriaName) || !\$this->".$lastCriteriaName."->equals(\$criteria)) {
+ \$this->$collName = ".$fkPeerBuilder->getPeerClassname()."::doSelect(\$criteria, \$con);
+ }
+ }
+ }
+ \$this->$lastCriteriaName = \$criteria;
+ return \$this->$collName;
+ }
+";
+ } // addRefererGet()
+
+ /**
+ * Adds the method that gets a one-to-one related referrer fkey.
+ * This is for one-to-one relationship special case.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPKRefFKGet(&$script, ForeignKey $refFK)
+ {
+ $table = $this->getTable();
+ $tblFK = $refFK->getTable();
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $joinedTablePeerBuilder = $this->getNewObjectBuilder($refFK->getTable());
+ $className = $joinedTableObjectBuilder->getObjectClassname();
+
+ $varName = $this->getPKRefFKVarName($refFK);
+
+ $script .= "
+ /**
+ * Gets a single $className object, which is related to this object by a one-to-one relationship.
+ *
+ * @param PropelPDO \$con
+ * @return $className
+ * @throws PropelException
+ */
+ public function get".$this->getRefFKPhpNameAffix($refFK, $plural = false)."(PropelPDO \$con = null)
+ {
+";
+ $script .= "
+ if (\$this->$varName === null && !\$this->isNew()) {";
+
+ $lfmap = $refFK->getLocalForeignMapping();
+
+ // remember: this object represents the foreign table,
+ // so we need foreign columns of the reffk to know the local columns
+ // that we need to set :)
+
+ $localcols = $refFK->getForeignColumns();
+
+ // we know that at least every column in the primary key of the foreign table
+ // is represented in this foreign key
+
+ $params = array();
+ foreach ($tblFK->getPrimaryKey() as $col) {
+ $localColumn = $table->getColumn($lfmap[$col->getName()]);
+ $clo = strtolower($localColumn->getName());
+ $params[] = "\$this->$clo";
+ }
+
+ $script .= "
+ \$this->$varName = ".$joinedTableObjectBuilder->getPeerClassname()."::retrieveByPK(".implode(", ", $params).", \$con);
+ }
+
+ return \$this->$varName;
+ }
+";
+ } // addPKRefFKGet()
+
+} // PHP5ObjectBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5PeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5PeerBuilder.php
new file mode 100644
index 000000000..bcb486b13
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5PeerBuilder.php
@@ -0,0 +1,2761 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5PeerBuilder extends PeerBuilder
+{
+
+ /**
+ * Validates the current table to make sure that it won't
+ * result in generated code that will not parse.
+ *
+ * This method may emit warnings for code which may cause problems
+ * and will throw exceptions for errors that will definitely cause
+ * problems.
+ */
+ protected function validateModel()
+ {
+ parent::validateModel();
+
+ $table = $this->getTable();
+
+ // Check to see if any of the column constants are PHP reserved words.
+ $colConstants = array();
+
+ foreach ($table->getColumns() as $col) {
+ $colConstants[] = $this->getColumnName($col);
+ }
+
+ $reservedConstants = array_map('strtoupper', ClassTools::getPhpReservedWords());
+
+ $intersect = array_intersect($reservedConstants, $colConstants);
+ if (!empty($intersect)) {
+ throw new EngineException("One or more of your column names for [" . $table->getName() . "] table conflict with a PHP reserved word (" . implode(", ", $intersect) . ")");
+ }
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubPeerBuilder()->getUnprefixedClassname();
+ }
+
+ /**
+ * Gets the package for the [base] peer classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ public function getNamespace()
+ {
+ if ($namespace = parent::getNamespace()) {
+ if ($this->getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceOm')) {
+ return $namespace . '\\' . $omns;
+ } else {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script) {
+
+ $tableName = $this->getTable()->getName();
+ $tableDesc = $this->getTable()->getDescription();
+
+ $script .= "
+/**
+ * Base static class for performing query and update operations on the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+
+ $extendingPeerClass = '';
+ $parentClass = $this->getBehaviorContent('parentClass');
+ if (null !== $parentClass) {
+ $extendingPeerClass = ' extends ' . $parentClass;
+ } elseif ($this->basePeerClassname !== 'BasePeer') {
+ $extendingPeerClass = ' extends ' . $this->basePeerClassname;
+ }
+
+ $script .= "
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname(). $extendingPeerClass . " {
+";
+ }
+
+ protected function addClassBody(&$script)
+ {
+ $this->declareClassFromBuilder($this->getStubPeerBuilder());
+ $this->declareClassFromBuilder($this->getStubObjectBuilder());
+ parent::addClassBody($script);
+ $this->declareClasses('Propel', 'PropelException', 'PropelPDO', 'BasePeer', 'Criteria', 'PDO', 'PDOStatement');
+ }
+
+ /**
+ * Closes class.
+ * Adds closing brace at end of class and the static map builder registration code.
+ * @param string &$script The script will be modified in this method.
+ * @see addStaticTableMapRegistration()
+ */
+ protected function addClassClose(&$script)
+ {
+ // apply behaviors
+ $this->applyBehaviorModifier('staticMethods', $script, " ");
+
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->addStaticTableMapRegistration($script);
+ }
+
+ /**
+ * Adds the static map builder registration code.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addStaticTableMapRegistration(&$script)
+ {
+ $table = $this->getTable();
+
+ $script .= "
+// This is the static code needed to register the TableMap for this table with the main Propel class.
+//
+".$this->getClassName()."::buildTableMap();
+
+";
+ $this->applyBehaviorModifier('peerFilter', $script, "");
+ }
+
+ public function getTableMapClass()
+ {
+ return $this->getTablePhpName() . 'TableMap';
+ }
+
+ public function getTablePhpName()
+ {
+ return ($this->getTable()->isAbstract() ? '' : $this->getStubObjectBuilder()->getClassname());
+ }
+
+ /**
+ * Adds constant and variable declarations that go at the top of the class.
+ * @param string &$script The script will be modified in this method.
+ * @see addColumnNameConstants()
+ */
+ protected function addConstantsAndAttributes(&$script)
+ {
+ $dbName = $this->getDatabase()->getName();
+ $tableName = $this->getTable()->getName();
+ $tablePhpName = $this->getTable()->isAbstract() ? '' : addslashes($this->getStubObjectBuilder()->getFullyQualifiedClassname());
+ $script .= "
+ /** the default database name for this class */
+ const DATABASE_NAME = '$dbName';
+
+ /** the table name for this class */
+ const TABLE_NAME = '$tableName';
+
+ /** the related Propel class for this table */
+ const OM_CLASS = '$tablePhpName';
+
+ /** A class that can be returned by this peer. */
+ const CLASS_DEFAULT = '".$this->getStubObjectBuilder()->getClasspath()."';
+
+ /** the related TableMap class for this table */
+ const TM_CLASS = '".$this->getTableMapClass()."';
+
+ /** The total number of columns. */
+ const NUM_COLUMNS = ".$this->getTable()->getNumColumns().";
+
+ /** The number of lazy-loaded columns. */
+ const NUM_LAZY_LOAD_COLUMNS = ".$this->getTable()->getNumLazyLoadColumns().";
+";
+ $this->addColumnNameConstants($script);
+ $this->addInheritanceColumnConstants($script);
+
+ $script .= "
+ /**
+ * An identiy map to hold any loaded instances of ".$this->getObjectClassname()." objects.
+ * This must be public so that other peer classes can access this when hydrating from JOIN
+ * queries.
+ * @var array ".$this->getObjectClassname()."[]
+ */
+ public static \$instances = array();
+
+";
+
+ // apply behaviors
+ $this->applyBehaviorModifier('staticAttributes', $script, " ");
+
+ $this->addFieldNamesAttribute($script);
+ $this->addFieldKeysAttribute($script);
+ }
+
+ /**
+ * Adds the COLUMN_NAME contants to the class definition.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addColumnNameConstants(&$script)
+ {
+ foreach ($this->getTable()->getColumns() as $col) {
+ $script .= "
+ /** the column name for the ".strtoupper($col->getName()) ." field */
+ const ".$this->getColumnName($col) ." = '" . $this->getTable()->getName() . ".".strtoupper($col->getName())."';
+";
+ } // foreach
+ }
+
+ protected function addFieldNamesAttribute(&$script)
+ {
+ $table = $this->getTable();
+
+ $tableColumns = $table->getColumns();
+
+ $script .= "
+ /**
+ * holds an array of fieldnames
+ *
+ * first dimension keys are the type constants
+ * e.g. self::\$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
+ */
+ private static \$fieldNames = array (
+ BasePeer::TYPE_PHPNAME => array (";
+ foreach ($tableColumns as $col) {
+ $script .= "'".$col->getPhpName()."', ";
+ }
+ $script .= "),
+ BasePeer::TYPE_STUDLYPHPNAME => array (";
+ foreach ($tableColumns as $col) {
+ $script .= "'".$col->getStudlyPhpName()."', ";
+ }
+ $script .= "),
+ BasePeer::TYPE_COLNAME => array (";
+ foreach ($tableColumns as $col) {
+ $script .= $this->getColumnConstant($col, 'self').", ";
+ }
+ $script .= "),
+ BasePeer::TYPE_RAW_COLNAME => array (";
+ foreach ($tableColumns as $col) {
+ $script .= "'" . $col->getConstantColumnName() . "', ";
+ }
+ $script .= "),
+ BasePeer::TYPE_FIELDNAME => array (";
+ foreach ($tableColumns as $col) {
+ $script .= "'".$col->getName()."', ";
+ }
+ $script .= "),
+ BasePeer::TYPE_NUM => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "$num, ";
+ }
+ $script .= ")
+ );
+";
+ }
+
+ protected function addFieldKeysAttribute(&$script)
+ {
+ $table = $this->getTable();
+
+ $tableColumns = $table->getColumns();
+
+ $script .= "
+ /**
+ * holds an array of keys for quick access to the fieldnames array
+ *
+ * first dimension keys are the type constants
+ * e.g. self::\$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
+ */
+ private static \$fieldKeys = array (
+ BasePeer::TYPE_PHPNAME => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "'".$col->getPhpName()."' => $num, ";
+ }
+ $script .= "),
+ BasePeer::TYPE_STUDLYPHPNAME => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "'".$col->getStudlyPhpName()."' => $num, ";
+ }
+ $script .= "),
+ BasePeer::TYPE_COLNAME => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= $this->getColumnConstant($col, 'self')." => $num, ";
+ }
+ $script .= "),
+ BasePeer::TYPE_RAW_COLNAME => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "'" . $col->getConstantColumnName() . "' => $num, ";
+ }
+ $script .= "),
+ BasePeer::TYPE_FIELDNAME => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "'".$col->getName()."' => $num, ";
+ }
+ $script .= "),
+ BasePeer::TYPE_NUM => array (";
+ foreach ($tableColumns as $num => $col) {
+ $script .= "$num, ";
+ }
+ $script .= ")
+ );
+";
+ } // addFielKeysAttribute
+
+
+ protected function addGetFieldNames(&$script)
+ {
+ $script .= "
+ /**
+ * Returns an array of field names.
+ *
+ * @param string \$type The type of fieldnames to return:
+ * One of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
+ * @return array A list of field names
+ */
+
+ static public function getFieldNames(\$type = BasePeer::TYPE_PHPNAME)
+ {
+ if (!array_key_exists(\$type, self::\$fieldNames)) {
+ throw new PropelException('Method getFieldNames() expects the parameter \$type to be one of the class constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME, BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM. ' . \$type . ' was given.');
+ }
+ return self::\$fieldNames[\$type];
+ }
+";
+
+ } // addGetFieldNames()
+
+ protected function addTranslateFieldName(&$script)
+ {
+ $script .= "
+ /**
+ * Translates a fieldname to another type
+ *
+ * @param string \$name field name
+ * @param string \$fromType One of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
+ * BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
+ * @param string \$toType One of the class type constants
+ * @return string translated name of the field.
+ * @throws PropelException - if the specified name could not be found in the fieldname mappings.
+ */
+ static public function translateFieldName(\$name, \$fromType, \$toType)
+ {
+ \$toNames = self::getFieldNames(\$toType);
+ \$key = isset(self::\$fieldKeys[\$fromType][\$name]) ? self::\$fieldKeys[\$fromType][\$name] : null;
+ if (\$key === null) {
+ throw new PropelException(\"'\$name' could not be found in the field names of type '\$fromType'. These are: \" . print_r(self::\$fieldKeys[\$fromType], true));
+ }
+ return \$toNames[\$key];
+ }
+";
+ } // addTranslateFieldName()
+
+ /**
+ * Adds the buildTableMap() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBuildTableMap(&$script)
+ {
+ $this->declareClassFromBuilder($this->getTableMapBuilder());
+ $script .= "
+ /**
+ * Add a TableMap instance to the database for this peer class.
+ */
+ public static function buildTableMap()
+ {
+ \$dbMap = Propel::getDatabaseMap(".$this->getClassname()."::DATABASE_NAME);
+ if (!\$dbMap->hasTable(".$this->getClassname()."::TABLE_NAME))
+ {
+ \$dbMap->addTableObject(new ".$this->getTableMapClass()."());
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the CLASSKEY_* and CLASSNAME_* constants used for inheritance.
+ * @param string &$script The script will be modified in this method.
+ */
+ public function addInheritanceColumnConstants(&$script)
+ {
+ if ($this->getTable()->getChildrenColumn()) {
+
+ $col = $this->getTable()->getChildrenColumn();
+ $cfc = $col->getPhpName();
+
+ if ($col->isEnumeratedClasses()) {
+
+ if ($col->isPhpPrimitiveNumericType()) $quote = "";
+ else $quote = '"';
+
+ foreach ($col->getChildren() as $child) {
+ $childBuilder = $this->getMultiExtendObjectBuilder();
+ $childBuilder->setChild($child);
+
+ $script .= "
+ /** A key representing a particular subclass */
+ const CLASSKEY_".strtoupper($child->getKey())." = '" . $child->getKey() . "';
+";
+
+ if (strtoupper($child->getClassname()) != strtoupper($child->getKey())) {
+ $script .= "
+ /** A key representing a particular subclass */
+ const CLASSKEY_".strtoupper($child->getClassname())." = '" . $child->getKey() . "';
+";
+ }
+
+ $script .= "
+ /** A class that can be returned by this peer. */
+ const CLASSNAME_".strtoupper($child->getKey())." = '". $childBuilder->getClasspath() . "';
+";
+ } /* foreach children */
+ } /* if col->isenumerated...() */
+ } /* if table->getchildrencolumn() */
+
+ } //
+
+ /**
+ * Adds the alias() utility method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAlias(&$script)
+ {
+ $script .= "
+ /**
+ * Convenience method which changes table.column to alias.column.
+ *
+ * Using this method you can maintain SQL abstraction while using column aliases.
+ *
+ * \$c->addAlias(\"alias1\", TablePeer::TABLE_NAME);
+ * \$c->addJoin(TablePeer::alias(\"alias1\", TablePeer::PRIMARY_KEY_COLUMN), TablePeer::PRIMARY_KEY_COLUMN);
+ *
+ * @param string \$alias The alias for the current table.
+ * @param string \$column The column name for current table. (i.e. ".$this->getPeerClassname()."::COLUMN_NAME).
+ * @return string
+ */
+ public static function alias(\$alias, \$column)
+ {
+ return str_replace(".$this->getPeerClassname()."::TABLE_NAME.'.', \$alias.'.', \$column);
+ }
+";
+ } // addAliasMethod
+
+ /**
+ * Adds the addSelectColumns() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAddSelectColumns(&$script)
+ {
+ $script .= "
+ /**
+ * Add all the columns needed to create a new object.
+ *
+ * Note: any columns that were marked with lazyLoad=\"true\" in the
+ * XML schema will not be added to the select list and only loaded
+ * on demand.
+ *
+ * @param Criteria \$criteria object containing the columns to add.
+ * @param string \$alias optional table alias
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function addSelectColumns(Criteria \$criteria, \$alias = null)
+ {
+ if (null === \$alias) {";
+ foreach ($this->getTable()->getColumns() as $col) {
+ if (!$col->isLazyLoad()) {
+ $script .= "
+ \$criteria->addSelectColumn(".$this->getPeerClassname()."::".$this->getColumnName($col).");";
+ } // if !col->isLazyLoad
+ } // foreach
+ $script .= "
+ } else {";
+ foreach ($this->getTable()->getColumns() as $col) {
+ if (!$col->isLazyLoad()) {
+ $script .= "
+ \$criteria->addSelectColumn(\$alias . '." . $col->getConstantColumnName()."');";
+ } // if !col->isLazyLoad
+ } // foreach
+ $script .= "
+ }";
+ $script .="
+ }
+";
+ } // addAddSelectColumns()
+
+ /**
+ * Adds the doCount() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoCount(&$script)
+ {
+ $script .= "
+ /**
+ * Returns the number of rows matching criteria.
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
+ * @param PropelPDO \$con
+ * @return int Number of matching rows.
+ */
+ public static function doCount(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null)
+ {
+ // we may modify criteria, so copy it first
+ \$criteria = clone \$criteria;
+
+ // We need to set the primary table name, since in the case that there are no WHERE columns
+ // it will be impossible for the BasePeer::createSelectSql() method to determine which
+ // tables go into the FROM clause.
+ \$criteria->setPrimaryTableName(".$this->getPeerClassname()."::TABLE_NAME);
+
+ if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
+ \$criteria->setDistinct();
+ }
+
+ if (!\$criteria->hasSelectClause()) {
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ }
+
+ \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count
+ \$criteria->setDbName(self::DATABASE_NAME); // Set the correct dbName
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }";
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ // BasePeer returns a PDOStatement
+ \$stmt = ".$this->basePeerClassname."::doCount(\$criteria, \$con);
+
+ if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$count = (int) \$row[0];
+ } else {
+ \$count = 0; // no rows returned; we infer that means 0 matches.
+ }
+ \$stmt->closeCursor();
+ return \$count;
+ }";
+ }
+
+ /**
+ * Adds the doSelectOne() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelectOne(&$script)
+ {
+ $script .= "
+ /**
+ * Method to select one object from the DB.
+ *
+ * @param Criteria \$criteria object used to create the SELECT statement.
+ * @param PropelPDO \$con
+ * @return ".$this->getObjectClassname()."
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doSelectOne(Criteria \$criteria, PropelPDO \$con = null)
+ {
+ \$critcopy = clone \$criteria;
+ \$critcopy->setLimit(1);
+ \$objects = ".$this->getPeerClassname()."::doSelect(\$critcopy, \$con);
+ if (\$objects) {
+ return \$objects[0];
+ }
+ return null;
+ }";
+ }
+
+ /**
+ * Adds the doSelect() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelect(&$script)
+ {
+ $script .= "
+ /**
+ * Method to do selects.
+ *
+ * @param Criteria \$criteria The Criteria object used to build the SELECT statement.
+ * @param PropelPDO \$con
+ * @return array Array of selected Objects
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doSelect(Criteria \$criteria, PropelPDO \$con = null)
+ {
+ return ".$this->getPeerClassname()."::populateObjects(".$this->getPeerClassname()."::doSelectStmt(\$criteria, \$con));
+ }";
+ }
+
+ /**
+ * Adds the doSelectStmt() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelectStmt(&$script)
+ {
+
+ $script .= "
+ /**
+ * Prepares the Criteria object and uses the parent doSelect() method to execute a PDOStatement.
+ *
+ * Use this method directly if you want to work with an executed statement durirectly (for example
+ * to perform your own object hydration).
+ *
+ * @param Criteria \$criteria The Criteria object used to build the SELECT statement.
+ * @param PropelPDO \$con The connection to use
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ * @return PDOStatement The executed PDOStatement object.
+ * @see ".$this->basePeerClassname."::doSelect()
+ */
+ public static function doSelectStmt(Criteria \$criteria, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ if (!\$criteria->hasSelectClause()) {
+ \$criteria = clone \$criteria;
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ }
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);";
+ // apply behaviors
+ if ($this->hasBehaviorModifier('preSelect'))
+ {
+ $this->applyBehaviorModifier('preSelect', $script);
+ }
+ $script .= "
+
+ // BasePeer returns a PDOStatement
+ return ".$this->basePeerClassname."::doSelect(\$criteria, \$con);
+ }";
+ }
+
+ /**
+ * Adds the PHP code to return a instance pool key for the passed-in primary key variable names.
+ *
+ * @param array $pkphp An array of PHP var names / method calls representing complete pk.
+ */
+ public function getInstancePoolKeySnippet($pkphp)
+ {
+ $pkphp = (array) $pkphp; // make it an array if it is not.
+ $script = "";
+ if (count($pkphp) > 1) {
+ $script .= "serialize(array(";
+ $i = 0;
+ foreach ($pkphp as $pkvar) {
+ $script .= ($i++ ? ', ' : '') . "(string) $pkvar";
+ }
+ $script .= "))";
+ } else {
+ $script .= "(string) " . $pkphp[0];
+ }
+ return $script;
+ }
+
+ /**
+ * Creates a convenience method to add objects to an instance pool.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAddInstanceToPool(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Adds an object to the instance pool.
+ *
+ * Propel keeps cached copies of objects in an instance pool when they are retrieved
+ * from the database. In some cases -- especially when you override doSelect*()
+ * methods in your stub classes -- you may need to explicitly add objects
+ * to the cache in order to ensure that the same objects are always returned by doSelect*()
+ * and retrieveByPK*() calls.
+ *
+ * @param ".$this->getObjectClassname()." \$value A ".$this->getObjectClassname()." object.
+ * @param string \$key (optional) key to use for instance map (for performance boost if key was already calculated externally).
+ */
+ public static function addInstanceToPool(".$this->getObjectClassname()." \$obj, \$key = null)
+ {
+ if (Propel::isInstancePoolingEnabled()) {
+ if (\$key === null) {";
+
+ $pks = $this->getTable()->getPrimaryKey();
+
+ $php = array();
+ foreach ($pks as $pk) {
+ $php[] = '$obj->get' . $pk->getPhpName() . '()';
+ }
+ $script .= "
+ \$key = ".$this->getInstancePoolKeySnippet($php).";";
+ $script .= "
+ } // if key === null
+ self::\$instances[\$key] = \$obj;
+ }
+ }
+";
+ } // addAddInstanceToPool()
+
+ /**
+ * Creates a convenience method to remove objects form an instance pool.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRemoveInstanceFromPool(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Removes an object from the instance pool.
+ *
+ * Propel keeps cached copies of objects in an instance pool when they are retrieved
+ * from the database. In some cases -- especially when you override doDelete
+ * methods in your stub classes -- you may need to explicitly remove objects
+ * from the cache in order to prevent returning objects that no longer exist.
+ *
+ * @param mixed \$value A ".$this->getObjectClassname()." object or a primary key value.
+ */
+ public static function removeInstanceFromPool(\$value)
+ {";
+ $script .= "
+ if (Propel::isInstancePoolingEnabled() && \$value !== null) {";
+ $pks = $table->getPrimaryKey();
+
+ $script .= "
+ if (is_object(\$value) && \$value instanceof ".$this->getObjectClassname().") {";
+
+ $php = array();
+ foreach ($pks as $pk) {
+ $php[] = '$value->get' . $pk->getPhpName() . '()';
+ }
+ $script .= "
+ \$key = ".$this->getInstancePoolKeySnippet($php).";";
+
+ $script .= "
+ } elseif (".(count($pks) > 1 ? "is_array(\$value) && count(\$value) === " . count($pks) : "is_scalar(\$value)").") {
+ // assume we've been passed a primary key";
+
+ if (count($pks) > 1) {
+ $php = array();
+ for ($i=0; $i < count($pks); $i++) {
+ $php[] = "\$value[$i]";
+ }
+ } else {
+ $php = '$value';
+ }
+ $script .= "
+ \$key = ".$this->getInstancePoolKeySnippet($php).";";
+ $script .= "
+ } else {
+ \$e = new PropelException(\"Invalid value passed to removeInstanceFromPool(). Expected primary key or ".$this->getObjectClassname()." object; got \" . (is_object(\$value) ? get_class(\$value) . ' object.' : var_export(\$value,true)));
+ throw \$e;
+ }
+
+ unset(self::\$instances[\$key]);
+ }
+ } // removeInstanceFromPool()
+";
+ } // addRemoveFromInstancePool()
+
+ /**
+ * Adds method to clear the instance pool.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClearInstancePool(&$script)
+ {
+ $script .= "
+ /**
+ * Clear the instance pool.
+ *
+ * @return void
+ */
+ public static function clearInstancePool()
+ {
+ self::\$instances = array();
+ }
+ ";
+ }
+
+ /**
+ * Adds method to clear the instance pool of related tables.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClearRelatedInstancePool(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Method to invalidate the instance pool of all tables related to " . $table->getName() . "
+ * by a foreign key with ON DELETE CASCADE
+ */
+ public static function clearRelatedInstancePool()
+ {";
+ // Handle ON DELETE CASCADE for updating instance pool
+
+ foreach ($table->getReferrers() as $fk) {
+
+ // $fk is the foreign key in the other table, so localTableName will
+ // actually be the table name of other table
+ $tblFK = $fk->getTable();
+
+ $joinedTablePeerBuilder = $this->getNewStubPeerBuilder($tblFK);
+ $this->declareClassFromBuilder($joinedTablePeerBuilder);
+ $tblFKPackage = $joinedTablePeerBuilder->getStubPeerBuilder()->getPackage();
+
+ if (!$tblFK->isForReferenceOnly()) {
+ // we can't perform operations on tables that are
+ // not within the schema (i.e. that we have no map for, etc.)
+
+ if ($fk->getOnDelete() == ForeignKey::CASCADE || $fk->getOnDelete() == ForeignKey::SETNULL) {
+ $script .= "
+ // Invalidate objects in ".$joinedTablePeerBuilder->getClassname()." instance pool,
+ // since one or more of them may be deleted by ON DELETE CASCADE/SETNULL rule.
+ ".$joinedTablePeerBuilder->getClassname()."::clearInstancePool();";
+ } // if fk is on delete cascade
+ } // if (! for ref only)
+ } // foreach
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds method to get an the instance from the pool, given a key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetInstanceFromPool(&$script)
+ {
+ $script .= "
+ /**
+ * Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
+ *
+ * For tables with a single-column primary key, that simple pkey value will be returned. For tables with
+ * a multi-column primary key, a serialize()d version of the primary key will be returned.
+ *
+ * @param string \$key The key (@see getPrimaryKeyHash()) for this instance.
+ * @return ".$this->getObjectClassname()." Found object or NULL if 1) no instance exists for specified key or 2) instance pooling has been disabled.
+ * @see getPrimaryKeyHash()
+ */
+ public static function getInstanceFromPool(\$key)
+ {
+ if (Propel::isInstancePoolingEnabled()) {
+ if (isset(self::\$instances[\$key])) {
+ return self::\$instances[\$key];
+ }
+ }
+ return null; // just to be explicit
+ }
+ ";
+ }
+
+ /**
+ * Adds method to get a version of the primary key that can be used as a unique key for identifier map.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPrimaryKeyHash(&$script)
+ {
+ $script .= "
+ /**
+ * Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
+ *
+ * For tables with a single-column primary key, that simple pkey value will be returned. For tables with
+ * a multi-column primary key, a serialize()d version of the primary key will be returned.
+ *
+ * @param array \$row PropelPDO resultset row.
+ * @param int \$startcol The 0-based offset for reading from the resultset row.
+ * @return string A string version of PK or NULL if the components of primary key in result array are all null.
+ */
+ public static function getPrimaryKeyHashFromRow(\$row, \$startcol = 0)
+ {";
+
+ // We have to iterate through all the columns so that we know the offset of the primary
+ // key columns.
+ $n = 0;
+ $pk = array();
+ $cond = array();
+ foreach ($this->getTable()->getColumns() as $col) {
+ if (!$col->isLazyLoad()) {
+ if ($col->isPrimaryKey()) {
+ $part = $n ? "\$row[\$startcol + $n]" : "\$row[\$startcol]";
+ $cond[] = $part . " === null";
+ $pk[] = $part;
+ }
+ $n++;
+ }
+ }
+
+ $script .= "
+ // If the PK cannot be derived from the row, return NULL.
+ if (".implode(' && ', $cond).") {
+ return null;
+ }
+ return ".$this->getInstancePoolKeySnippet($pk).";
+ }
+";
+ } // addGetPrimaryKeyHash
+
+ /**
+ * Adds method to get the primary key from a row
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetPrimaryKeyFromRow(&$script)
+ {
+ $script .= "
+ /**
+ * Retrieves the primary key from the DB resultset row
+ * For tables with a single-column primary key, that simple pkey value will be returned. For tables with
+ * a multi-column primary key, an array of the primary key columns will be returned.
+ *
+ * @param array \$row PropelPDO resultset row.
+ * @param int \$startcol The 0-based offset for reading from the resultset row.
+ * @return mixed The primary key of the row
+ */
+ public static function getPrimaryKeyFromRow(\$row, \$startcol = 0)
+ {";
+
+ // We have to iterate through all the columns so that we know the offset of the primary
+ // key columns.
+ $table = $this->getTable();
+ $n = 0;
+ $pks = array();
+ foreach ($table->getColumns() as $col) {
+ if (!$col->isLazyLoad()) {
+ if ($col->isPrimaryKey()) {
+ $pk = '(' . $col->getPhpType() . ') ' . ($n ? "\$row[\$startcol + $n]" : "\$row[\$startcol]");
+ if ($table->hasCompositePrimaryKey()) {
+ $pks[] = $pk;
+ }
+ }
+ $n++;
+ }
+ }
+ if ($table->hasCompositePrimaryKey()) {
+ $script .= "
+ return array(" . implode($pks, ', '). ");";
+ } else {
+ $script .= "
+ return " . $pk . ";";
+ }
+ $script .= "
+ }
+ ";
+ } // addGetPrimaryKeyFromRow
+
+ /**
+ * Adds the populateObjects() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPopulateObjects(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * The returned array will contain objects of the default type or
+ * objects that inherit from the default.
+ *
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function populateObjects(PDOStatement \$stmt)
+ {
+ \$results = array();
+ ";
+ if (!$table->getChildrenColumn()) {
+ $script .= "
+ // set the class once to avoid overhead in the loop
+ \$cls = ".$this->getPeerClassname()."::getOMClass(false);";
+ }
+
+ $script .= "
+ // populate the object(s)
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key = ".$this->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$obj = ".$this->getPeerClassname()."::getInstanceFromPool(\$key))) {
+ // We no longer rehydrate the object, since this can cause data loss.
+ // See http://www.propelorm.org/ticket/509
+ // \$obj->hydrate(\$row, 0, true); // rehydrate
+ \$results[] = \$obj;
+ } else {";
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ // class must be set each time from the record row
+ \$cls = ".$this->getPeerClassname()."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
+ " . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
+ \$obj->hydrate(\$row);
+ \$results[] = \$obj;
+ ".$this->getPeerClassname()."::addInstanceToPool(\$obj, \$key);";
+ } else {
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
+ \$obj->hydrate(\$row);
+ \$results[] = \$obj;
+ ".$this->getPeerClassname()."::addInstanceToPool(\$obj, \$key);";
+ }
+ $script .= "
+ } // if key exists
+ }
+ \$stmt->closeCursor();
+ return \$results;
+ }";
+ }
+
+ /**
+ * Adds the populateObject() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPopulateObject(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Populates an object of the default type or an object that inherit from the default.
+ *
+ * @param array \$row PropelPDO resultset row.
+ * @param int \$startcol The 0-based offset for reading from the resultset row.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ * @return array (" . $this->getStubObjectBuilder()->getClassName(). " object, last column rank)
+ */
+ public static function populateObject(\$row, \$startcol = 0)
+ {
+ \$key = ".$this->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, \$startcol);
+ if (null !== (\$obj = ".$this->getPeerClassname()."::getInstanceFromPool(\$key))) {
+ // We no longer rehydrate the object, since this can cause data loss.
+ // See http://www.propelorm.org/ticket/509
+ // \$obj->hydrate(\$row, \$startcol, true); // rehydrate
+ \$col = \$startcol + " . $this->getPeerClassname() . "::NUM_COLUMNS;";
+ if ($table->isAbstract()) {
+ $script .= "
+ } elseif (null == \$key) {
+ // empty resultset, probably from a left join
+ // since this table is abstract, we can't hydrate an empty object
+ \$obj = null;
+ \$col = \$startcol + " . $this->getPeerClassname() . "::NUM_COLUMNS;";
+ }
+ $script .= "
+ } else {";
+ if (!$table->getChildrenColumn()) {
+ $script .= "
+ \$cls = ".$this->getPeerClassname()."::OM_CLASS;";
+ } else {
+ $script .= "
+ \$cls = ".$this->getPeerClassname()."::getOMClass(\$row, \$startcol, false);";
+ }
+ $script .= "
+ \$obj = new \$cls();
+ \$col = \$obj->hydrate(\$row, \$startcol);
+ " . $this->getPeerClassname() . "::addInstanceToPool(\$obj, \$key);
+ }
+ return array(\$obj, \$col);
+ }";
+ }
+
+ /**
+ * Adds a getOMClass() for non-abstract tables that have inheritance.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetOMClass_Inheritance(&$script)
+ {
+ $col = $this->getTable()->getChildrenColumn();
+ $script .= "
+ /**
+ * The returned Class will contain objects of the default type or
+ * objects that inherit from the default.
+ *
+ * @param array \$row PropelPDO result row.
+ * @param int \$colnum Column to examine for OM class information (first is 0).
+ * @param boolean \$withPrefix Whether or not to return the path with the class name
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function getOMClass(\$row, \$colnum, \$withPrefix = true)
+ {
+ try {
+";
+ if ($col->isEnumeratedClasses()) {
+ $script .= "
+ \$omClass = null;
+ \$classKey = \$row[\$colnum + " . ($col->getPosition() - 1) . "];
+
+ switch(\$classKey) {
+";
+ foreach ($col->getChildren() as $child) {
+ $script .= "
+ case self::CLASSKEY_".strtoupper($child->getKey()).":
+ \$omClass = self::CLASSNAME_".strtoupper($child->getKey()).";
+ break;
+";
+ } /* foreach */
+ $script .= "
+ default:
+ \$omClass = self::CLASS_DEFAULT;
+";
+ $script .= "
+ } // switch
+ if (!\$withPrefix) {
+ \$omClass = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+ }
+";
+ } else { /* if not enumerated */
+ $script .= "
+ \$omClass = \$row[\$colnum + ".($col->getPosition()-1)."];
+ \$omClass = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ }
+ $script .= "
+ } catch (Exception \$e) {
+ throw new PropelException('Unable to get OM class.', \$e);
+ }
+ return \$omClass;
+ }
+";
+ }
+
+ /**
+ * Adds a getOMClass() for non-abstract tables that do note use inheritance.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetOMClass_NoInheritance(&$script)
+ {
+ $script .= "
+ /**
+ * The class that the Peer will make instances of.
+ *
+ * If \$withPrefix is true, the returned path
+ * uses a dot-path notation which is tranalted into a path
+ * relative to a location on the PHP include_path.
+ * (e.g. path.to.MyClass -> 'path/to/MyClass.php')
+ *
+ * @param boolean \$withPrefix Whether or not to return the path with the class name
+ * @return string path.to.ClassName
+ */
+ public static function getOMClass(\$withPrefix = true)
+ {
+ return \$withPrefix ? ".$this->getPeerClassname()."::CLASS_DEFAULT : ".$this->getPeerClassname()."::OM_CLASS;
+ }
+";
+ }
+
+ /**
+ * Adds a getOMClass() signature for abstract tables that do not have inheritance.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetOMClass_NoInheritance_Abstract(&$script)
+ {
+ $script .= "
+ /**
+ * The class that the Peer will make instances of.
+ *
+ * This method must be overridden by the stub subclass, because
+ * ".$this->getObjectClassname()." is declared abstract in the schema.
+ */
+ abstract public static function getOMClass(\$withPrefix = true);
+";
+ }
+
+ /**
+ * Adds the doInsert() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoInsert(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Method perform an INSERT on the database, given a ".$this->getObjectClassname()." or Criteria object.
+ *
+ * @param mixed \$values Criteria or ".$this->getObjectClassname()." object containing data that is used to create the INSERT statement.
+ * @param PropelPDO \$con the PropelPDO connection to use
+ * @return mixed The new primary key.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doInsert(\$values, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ if (\$values instanceof Criteria) {
+ \$criteria = clone \$values; // rename for clarity
+ } else {
+ \$criteria = \$values->buildCriteria(); // build Criteria from ".$this->getObjectClassname()." object
+ }
+";
+
+ foreach ($table->getColumns() as $col) {
+ $cfc = $col->getPhpName();
+ if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && !$table->isAllowPkInsert()) {
+ $script .= "
+ if (\$criteria->containsKey(".$this->getColumnConstant($col).") && \$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) {
+ throw new PropelException('Cannot insert a value for auto-increment primary key ('.".$this->getColumnConstant($col).".')');
+ }
+";
+ if (!$this->getPlatform()->supportsInsertNullPk())
+ {
+ $script .= "
+ // remove pkey col since this table uses auto-increment and passing a null value for it is not valid
+ \$criteria->remove(".$this->getColumnConstant($col).");
+";
+ }
+ } elseif ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && $table->isAllowPkInsert() && !$this->getPlatform()->supportsInsertNullPk()) {
+ $script .= "
+ // remove pkey col if it is null since this table does not accept that
+ if (\$criteria->containsKey(".$this->getColumnConstant($col).") && !\$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) {
+ \$criteria->remove(".$this->getColumnConstant($col).");
+ }
+";
+ }
+ }
+ $script .= "
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ try {
+ // use transaction because \$criteria could contain info
+ // for more than one table (I guess, conceivably)
+ \$con->beginTransaction();
+ \$pk = ".$this->basePeerClassname."::doInsert(\$criteria, \$con);
+ \$con->commit();
+ } catch(PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }
+
+ return \$pk;
+ }
+";
+ }
+
+ /**
+ * Adds the doUpdate() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoUpdate(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Method perform an UPDATE on the database, given a ".$this->getObjectClassname()." or Criteria object.
+ *
+ * @param mixed \$values Criteria or ".$this->getObjectClassname()." object containing data that is used to create the UPDATE statement.
+ * @param PropelPDO \$con The connection to use (specify PropelPDO connection object to exert more control over transactions).
+ * @return int The number of affected rows (if supported by underlying database driver).
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doUpdate(\$values, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ \$selectCriteria = new Criteria(self::DATABASE_NAME);
+
+ if (\$values instanceof Criteria) {
+ \$criteria = clone \$values; // rename for clarity
+";
+ foreach ($table->getColumns() as $col) {
+ if ($col->isPrimaryKey()) {
+ $script .= "
+ \$comparison = \$criteria->getComparison(".$this->getColumnConstant($col).");
+ \$value = \$criteria->remove(".$this->getColumnConstant($col).");
+ if (\$value) {
+ \$selectCriteria->add(".$this->getColumnConstant($col).", \$value, \$comparison);
+ } else {
+ \$selectCriteria->setPrimaryTableName(".$this->getPeerClassname()."::TABLE_NAME);
+ }
+";
+ } /* if col is prim key */
+ } /* foreach */
+
+ $script .= "
+ } else { // \$values is ".$this->getObjectClassname()." object
+ \$criteria = \$values->buildCriteria(); // gets full criteria
+ \$selectCriteria = \$values->buildPkeyCriteria(); // gets criteria w/ primary key(s)
+ }
+
+ // set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ return {$this->basePeerClassname}::doUpdate(\$selectCriteria, \$criteria, \$con);
+ }
+";
+ }
+
+ /**
+ * Adds the doDeleteAll() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoDeleteAll(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Method to DELETE all rows from the ".$table->getName()." table.
+ *
+ * @return int The number of affected rows (if supported by underlying database driver).
+ */
+ public static function doDeleteAll(\$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+ \$affectedRows = 0; // initialize var to track total num of affected rows
+ try {
+ // use transaction because \$criteria could contain info
+ // for more than one table or we could emulating ON DELETE CASCADE, etc.
+ \$con->beginTransaction();
+ ";
+ if ($this->isDeleteCascadeEmulationNeeded()) {
+ $script .="\$affectedRows += ".$this->getPeerClassname()."::doOnDeleteCascade(new Criteria(".$this->getPeerClassname()."::DATABASE_NAME), \$con);
+ ";
+ }
+ if ($this->isDeleteSetNullEmulationNeeded()) {
+ $script .= $this->getPeerClassname() . "::doOnDeleteSetNull(new Criteria(".$this->getPeerClassname() . "::DATABASE_NAME), \$con);
+ ";
+ }
+ $script .= "\$affectedRows += {$this->basePeerClassname}::doDeleteAll(".$this->getPeerClassname()."::TABLE_NAME, \$con, ".$this->getPeerClassname()."::DATABASE_NAME);
+ // Because this db requires some delete cascade/set null emulation, we have to
+ // clear the cached instance *after* the emulation has happened (since
+ // instances get re-added by the select statement contained therein).
+ ".$this->getPeerClassname()."::clearInstancePool();
+ ".$this->getPeerClassname()."::clearRelatedInstancePool();
+ \$con->commit();
+ return \$affectedRows;
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the doDelete() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoDelete(&$script)
+ {
+ $table = $this->getTable();
+ $emulateCascade = $this->isDeleteCascadeEmulationNeeded() || $this->isDeleteSetNullEmulationNeeded();
+ $script .= "
+ /**
+ * Method perform a DELETE on the database, given a ".$this->getObjectClassname()." or Criteria object OR a primary key value.
+ *
+ * @param mixed \$values Criteria or ".$this->getObjectClassname()." object or primary key or array of primary keys
+ * which is used to create the DELETE statement
+ * @param PropelPDO \$con the connection to use
+ * @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
+ * if supported by native driver or if emulated using Propel.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doDelete(\$values, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_WRITE);
+ }
+
+ if (\$values instanceof Criteria) {";
+ if (!$emulateCascade) {
+ $script .= "
+ // invalidate the cache for all objects of this type, since we have no
+ // way of knowing (without running a query) what objects should be invalidated
+ // from the cache based on this Criteria.
+ ".$this->getPeerClassname()."::clearInstancePool();";
+ }
+ $script .= "
+ // rename for clarity
+ \$criteria = clone \$values;
+ } elseif (\$values instanceof ".$this->getObjectClassname().") { // it's a model object";
+ if (!$emulateCascade) {
+ $script .= "
+ // invalidate the cache for this single object
+ ".$this->getPeerClassname()."::removeInstanceFromPool(\$values);";
+ }
+ if (count($table->getPrimaryKey()) > 0) {
+ $script .= "
+ // create criteria based on pk values
+ \$criteria = \$values->buildPkeyCriteria();";
+ } else {
+ $script .= "
+ // create criteria based on pk value
+ \$criteria = \$values->buildCriteria();";
+ }
+
+ $script .= "
+ } else { // it's a primary key, or an array of pks";
+ $script .= "
+ \$criteria = new Criteria(self::DATABASE_NAME);";
+
+ if (count($table->getPrimaryKey()) === 1) {
+ $pkey = $table->getPrimaryKey();
+ $col = array_shift($pkey);
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($col).", (array) \$values, Criteria::IN);";
+ if (!$emulateCascade) {
+ $script .= "
+ // invalidate the cache for this object(s)
+ foreach ((array) \$values as \$singleval) {
+ ".$this->getPeerClassname()."::removeInstanceFromPool(\$singleval);
+ }";
+ }
+ } else {
+ $script .= "
+ // primary key is composite; we therefore, expect
+ // the primary key passed to be an array of pkey values
+ if (count(\$values) == count(\$values, COUNT_RECURSIVE)) {
+ // array is not multi-dimensional
+ \$values = array(\$values);
+ }
+ foreach (\$values as \$value) {";
+ $i=0;
+ foreach ($table->getPrimaryKey() as $col) {
+ if ($i == 0) {
+ $script .= "
+ \$criterion = \$criteria->getNewCriterion(".$this->getColumnConstant($col).", \$value[$i]);";
+ } else {
+ $script .= "
+ \$criterion->addAnd(\$criteria->getNewCriterion(".$this->getColumnConstant($col).", \$value[$i]));";
+ }
+ $i++;
+ }
+ $script .= "
+ \$criteria->addOr(\$criterion);";
+ if (!$emulateCascade) {
+ $script .= "
+ // we can invalidate the cache for this single PK
+ ".$this->getPeerClassname()."::removeInstanceFromPool(\$value);";
+ }
+ $script .= "
+ }";
+ } /* if count(table->getPrimaryKeys()) */
+
+ $script .= "
+ }
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ \$affectedRows = 0; // initialize var to track total num of affected rows
+
+ try {
+ // use transaction because \$criteria could contain info
+ // for more than one table or we could emulating ON DELETE CASCADE, etc.
+ \$con->beginTransaction();
+ ";
+
+ if ($this->isDeleteCascadeEmulationNeeded()) {
+ $script .= "
+ // cloning the Criteria in case it's modified by doSelect() or doSelectStmt()
+ \$c = clone \$criteria;
+ \$affectedRows += ".$this->getPeerClassname()."::doOnDeleteCascade(\$c, \$con);
+ ";
+ }
+ if ($this->isDeleteSetNullEmulationNeeded()) {
+ $script .= "
+ // cloning the Criteria in case it's modified by doSelect() or doSelectStmt()
+ \$c = clone \$criteria;
+ " . $this->getPeerClassname() . "::doOnDeleteSetNull(\$c, \$con);
+ ";
+ }
+
+ if ($emulateCascade) {
+ $script .= "
+ // Because this db requires some delete cascade/set null emulation, we have to
+ // clear the cached instance *after* the emulation has happened (since
+ // instances get re-added by the select statement contained therein).
+ if (\$values instanceof Criteria) {
+ ".$this->getPeerClassname()."::clearInstancePool();
+ } elseif (\$values instanceof ".$this->getObjectClassname().") { // it's a model object
+ ".$this->getPeerClassname()."::removeInstanceFromPool(\$values);
+ } else { // it's a primary key, or an array of pks
+ foreach ((array) \$values as \$singleval) {
+ ".$this->getPeerClassname()."::removeInstanceFromPool(\$singleval);
+ }
+ }
+ ";
+ }
+
+ $script .= "
+ \$affectedRows += {$this->basePeerClassname}::doDelete(\$criteria, \$con);
+ ".$this->getPeerClassname()."::clearRelatedInstancePool();
+ \$con->commit();
+ return \$affectedRows;
+ } catch (PropelException \$e) {
+ \$con->rollBack();
+ throw \$e;
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the doOnDeleteCascade() method, which provides ON DELETE CASCADE emulation.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoOnDeleteCascade(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * This is a method for emulating ON DELETE CASCADE for DBs that don't support this
+ * feature (like MySQL or SQLite).
+ *
+ * This method is not very speedy because it must perform a query first to get
+ * the implicated records and then perform the deletes by calling those Peer classes.
+ *
+ * This method should be used within a transaction if possible.
+ *
+ * @param Criteria \$criteria
+ * @param PropelPDO \$con
+ * @return int The number of affected rows (if supported by underlying database driver).
+ */
+ protected static function doOnDeleteCascade(Criteria \$criteria, PropelPDO \$con)
+ {
+ // initialize var to track total num of affected rows
+ \$affectedRows = 0;
+
+ // first find the objects that are implicated by the \$criteria
+ \$objects = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
+ foreach (\$objects as \$obj) {
+";
+
+ foreach ($table->getReferrers() as $fk) {
+
+ // $fk is the foreign key in the other table, so localTableName will
+ // actually be the table name of other table
+ $tblFK = $fk->getTable();
+
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($tblFK);
+ $tblFKPackage = $joinedTablePeerBuilder->getStubPeerBuilder()->getPackage();
+
+ if (!$tblFK->isForReferenceOnly()) {
+ // we can't perform operations on tables that are
+ // not within the schema (i.e. that we have no map for, etc.)
+
+ $fkClassName = $joinedTablePeerBuilder->getObjectClassname();
+
+ if ($fk->getOnDelete() == ForeignKey::CASCADE) {
+
+ // backwards on purpose
+ $columnNamesF = $fk->getLocalColumns();
+ $columnNamesL = $fk->getForeignColumns();
+
+ $script .= "
+
+ // delete related $fkClassName objects
+ \$criteria = new Criteria(".$joinedTablePeerBuilder->getPeerClassname()."::DATABASE_NAME);
+ ";
+ for ($x=0,$xlen=count($columnNamesF); $x < $xlen; $x++) {
+ $columnFK = $tblFK->getColumn($columnNamesF[$x]);
+ $columnL = $table->getColumn($columnNamesL[$x]);
+
+ $script .= "
+ \$criteria->add(".$joinedTablePeerBuilder->getColumnConstant($columnFK) .", \$obj->get".$columnL->getPhpName()."());";
+ }
+
+ $script .= "
+ \$affectedRows += ".$joinedTablePeerBuilder->getPeerClassname()."::doDelete(\$criteria, \$con);";
+
+ } // if cascade && fkey table name != curr table name
+
+ } // if not for ref only
+ } // foreach foreign keys
+ $script .= "
+ }
+ return \$affectedRows;
+ }
+";
+ } // end addDoOnDeleteCascade
+
+ /**
+ * Adds the doOnDeleteSetNull() method, which provides ON DELETE SET NULL emulation.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoOnDeleteSetNull(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * This is a method for emulating ON DELETE SET NULL DBs that don't support this
+ * feature (like MySQL or SQLite).
+ *
+ * This method is not very speedy because it must perform a query first to get
+ * the implicated records and then perform the deletes by calling those Peer classes.
+ *
+ * This method should be used within a transaction if possible.
+ *
+ * @param Criteria \$criteria
+ * @param PropelPDO \$con
+ * @return void
+ */
+ protected static function doOnDeleteSetNull(Criteria \$criteria, PropelPDO \$con)
+ {
+
+ // first find the objects that are implicated by the \$criteria
+ \$objects = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
+ foreach (\$objects as \$obj) {
+";
+
+ // This logic is almost exactly the same as that in doOnDeleteCascade()
+ // it may make sense to refactor this, provided that thigns don't
+ // get too complicated.
+
+ foreach ($table->getReferrers() as $fk) {
+
+ // $fk is the foreign key in the other table, so localTableName will
+ // actually be the table name of other table
+ $tblFK = $fk->getTable();
+ $refTablePeerBuilder = $this->getNewPeerBuilder($tblFK);
+
+ if (!$tblFK->isForReferenceOnly()) {
+ // we can't perform operations on tables that are
+ // not within the schema (i.e. that we have no map for, etc.)
+
+ $fkClassName = $refTablePeerBuilder->getObjectClassname();
+
+ if ($fk->getOnDelete() == ForeignKey::SETNULL) {
+
+ // backwards on purpose
+ $columnNamesF = $fk->getLocalColumns();
+ $columnNamesL = $fk->getForeignColumns(); // should be same num as foreign
+ $script .= "
+ // set fkey col in related $fkClassName rows to NULL
+ \$selectCriteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
+ \$updateValues = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";
+
+ for ($x=0,$xlen=count($columnNamesF); $x < $xlen; $x++) {
+ $columnFK = $tblFK->getColumn($columnNamesF[$x]);
+ $columnL = $table->getColumn($columnNamesL[$x]);
+ $script .= "
+ \$selectCriteria->add(".$refTablePeerBuilder->getColumnConstant($columnFK).", \$obj->get".$columnL->getPhpName()."());
+ \$updateValues->add(".$refTablePeerBuilder->getColumnConstant($columnFK).", null);
+";
+ }
+
+ $script .= "
+ {$this->basePeerClassname}::doUpdate(\$selectCriteria, \$updateValues, \$con); // use BasePeer because generated Peer doUpdate() methods only update using pkey
+";
+ } // if setnull && fkey table name != curr table name
+ } // if not for ref only
+ } // foreach foreign keys
+
+ $script .= "
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the doValidate() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoValidate(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Validates all modified columns of given ".$this->getObjectClassname()." object.
+ * If parameter \$columns is either a single column name or an array of column names
+ * than only those columns are validated.
+ *
+ * NOTICE: This does not apply to primary or foreign keys for now.
+ *
+ * @param ".$this->getObjectClassname()." \$obj The object to validate.
+ * @param mixed \$cols Column name or array of column names.
+ *
+ * @return mixed TRUE if all columns are valid or the error message of the first invalid column.
+ */
+ public static function doValidate(".$this->getObjectClassname()." \$obj, \$cols = null)
+ {
+ \$columns = array();
+
+ if (\$cols) {
+ \$dbMap = Propel::getDatabaseMap(".$this->getPeerClassname()."::DATABASE_NAME);
+ \$tableMap = \$dbMap->getTable(".$this->getPeerClassname()."::TABLE_NAME);
+
+ if (! is_array(\$cols)) {
+ \$cols = array(\$cols);
+ }
+
+ foreach (\$cols as \$colName) {
+ if (\$tableMap->containsColumn(\$colName)) {
+ \$get = 'get' . \$tableMap->getColumn(\$colName)->getPhpName();
+ \$columns[\$colName] = \$obj->\$get();
+ }
+ }
+ } else {
+";
+ foreach ($table->getValidators() as $val) {
+ $col = $val->getColumn();
+ if (!$col->isAutoIncrement()) {
+ $script .= "
+ if (\$obj->isNew() || \$obj->isColumnModified(".$this->getColumnConstant($col)."))
+ \$columns[".$this->getColumnConstant($col)."] = \$obj->get".$col->getPhpName()."();
+";
+ } // if
+ } // foreach
+
+ $script .= "
+ }
+
+ return {$this->basePeerClassname}::doValidate(".$this->getPeerClassname()."::DATABASE_NAME, ".$this->getPeerClassname()."::TABLE_NAME, \$columns);
+ }
+";
+ } // end addDoValidate()
+
+ /**
+ * Adds the retrieveByPK method for tables with single-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRetrieveByPK_SinglePK(&$script)
+ {
+ $table = $this->getTable();
+ $pks = $table->getPrimaryKey();
+ $col = $pks[0];
+
+ $script .= "
+ /**
+ * Retrieve a single object by pkey.
+ *
+ * @param ".$col->getPhpType()." \$pk the primary key.
+ * @param PropelPDO \$con the connection to use
+ * @return " .$this->getObjectClassname(). "
+ */
+ public static function ".$this->getRetrieveMethodName()."(\$pk, PropelPDO \$con = null)
+ {
+
+ if (null !== (\$obj = ".$this->getPeerClassname()."::getInstanceFromPool(".$this->getInstancePoolKeySnippet('$pk')."))) {
+ return \$obj;
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
+ \$criteria->add(".$this->getColumnConstant($col).", \$pk);
+
+ \$v = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
+
+ return !empty(\$v) > 0 ? \$v[0] : null;
+ }
+";
+ }
+
+ /**
+ * Adds the retrieveByPKs method for tables with single-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRetrieveByPKs_SinglePK(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Retrieve multiple objects by pkey.
+ *
+ * @param array \$pks List of primary keys
+ * @param PropelPDO \$con the connection to use
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function ".$this->getRetrieveMethodName()."s(\$pks, PropelPDO \$con = null)
+ {
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+
+ \$objs = null;
+ if (empty(\$pks)) {
+ \$objs = array();
+ } else {
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";
+ $k1 = $table->getPrimaryKey();
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($k1[0]).", \$pks, Criteria::IN);";
+ $script .= "
+ \$objs = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
+ }
+ return \$objs;
+ }
+";
+ }
+
+ /**
+ * Adds the retrieveByPK method for tables with multi-column primary key.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRetrieveByPK_MultiPK(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ /**
+ * Retrieve object using using composite pkey values.";
+ foreach ($table->getPrimaryKey() as $col) {
+ $clo = strtolower($col->getName());
+ $cptype = $col->getPhpType();
+ $script .= "
+ * @param $cptype $".$clo;
+ }
+ $script .= "
+ * @param PropelPDO \$con
+ * @return ".$this->getObjectClassname()."
+ */
+ public static function ".$this->getRetrieveMethodName()."(";
+
+ $php = array();
+ foreach ($table->getPrimaryKey() as $col) {
+ $clo = strtolower($col->getName());
+ $php[] = '$' . $clo;
+ } /* foreach */
+
+ $script .= implode(', ', $php);
+
+ $script .= ", PropelPDO \$con = null) {
+ \$_instancePoolKey = ".$this->getInstancePoolKeySnippet($php).";";
+ $script .= "
+ if (null !== (\$obj = ".$this->getPeerClassname()."::getInstanceFromPool(\$_instancePoolKey))) {
+ return \$obj;
+ }
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+ \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";
+ foreach ($table->getPrimaryKey() as $col) {
+ $clo = strtolower($col->getName());
+ $script .= "
+ \$criteria->add(".$this->getColumnConstant($col).", $".$clo.");";
+ }
+ $script .= "
+ \$v = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
+
+ return !empty(\$v) ? \$v[0] : null;
+ }";
+ }
+
+ /**
+ * Adds the getTableMap() method which is a convenience method for apps to get DB metadata.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetTableMap(&$script)
+ {
+ $script .= "
+ /**
+ * Returns the TableMap related to this peer.
+ * This method is not needed for general use but a specific application could have a need.
+ * @return TableMap
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function getTableMap()
+ {
+ return Propel::getDatabaseMap(self::DATABASE_NAME)->getTable(self::TABLE_NAME);
+ }
+";
+ }
+
+ /**
+ * Adds the complex OM methods to the base addSelectMethods() function.
+ * @param string &$script The script will be modified in this method.
+ * @see PeerBuilder::addSelectMethods()
+ */
+ protected function addSelectMethods(&$script)
+ {
+ $table = $this->getTable();
+
+ parent::addSelectMethods($script);
+
+ $this->addDoCountJoin($script);
+ $this->addDoSelectJoin($script);
+
+ $countFK = count($table->getForeignKeys());
+
+ $includeJoinAll = true;
+
+ foreach ($this->getTable()->getForeignKeys() as $fk) {
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $this->declareClassFromBuilder($this->getNewStubPeerBuilder($tblFK));
+ if ($tblFK->isForReferenceOnly()) {
+ $includeJoinAll = false;
+ }
+ }
+
+ if ($includeJoinAll) {
+ if ($countFK > 0) {
+ $this->addDoCountJoinAll($script);
+ $this->addDoSelectJoinAll($script);
+ }
+ if ($countFK > 1) {
+ $this->addDoCountJoinAllExcept($script);
+ $this->addDoSelectJoinAllExcept($script);
+ }
+ }
+
+ }
+
+ /**
+ * Get the column offsets of the primary key(s) for specified table.
+ *
+ * @param Table $tbl
+ * @return array int[] The column offsets of the primary key(s).
+ */
+ protected function getPrimaryKeyColOffsets(Table $tbl)
+ {
+ $offsets = array();
+ $idx = 0;
+ foreach ($tbl->getColumns() as $col) {
+ if ($col->isPrimaryKey()) {
+ $offsets[] = $idx;
+ }
+ $idx++;
+ }
+ return $offsets;
+ }
+
+ public function addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder)
+ {
+ $script = '';
+ $lfMap = $fk->getLocalForeignMapping();
+ $lftCols = $fk->getLocalColumns();
+ if (count($lftCols) == 1)
+ {
+ // simple foreign key
+ $lftCol = $lftCols[0];
+ $script .= sprintf("
+ \$criteria->addJoin(%s, %s, \$join_behavior);\n",
+ $this->getColumnConstant($table->getColumn($lftCol) ),
+ $joinedTablePeerBuilder->getColumnConstant($joinTable->getColumn( $lfMap[$lftCol] ) ));
+ }
+ else
+ {
+ // composite foreign key
+ $script .= "
+ \$criteria->addMultipleJoin(array(\n";
+ foreach ($lftCols as $columnName ) {
+ $script .= sprintf(" array(%s, %s),\n",
+ $this->getColumnConstant($table->getColumn($columnName) ),
+ $joinedTablePeerBuilder->getColumnConstant($joinTable->getColumn( $lfMap[$columnName] ) )
+ );
+ }
+ $script .= " ), \$join_behavior);\n";
+ }
+ return $script;
+ }
+
+ /**
+ * Adds the doSelectJoin*() methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelectJoin(&$script)
+ {
+ $table = $this->getTable();
+ $className = $this->getObjectClassname();
+ $countFK = count($table->getForeignKeys());
+ $join_behavior = $this->getJoinBehavior();
+
+ if ($countFK >= 1) {
+
+ foreach ($table->getForeignKeys() as $fk) {
+
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ if (!$joinTable->isForReferenceOnly()) {
+
+ // This condition is necessary because Propel lacks a system for
+ // aliasing the table if it is the same table.
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+
+ $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+
+ $joinClassName = $joinedTableObjectBuilder->getObjectClassname();
+
+ $script .= "
+
+ /**
+ * Selects a collection of $className objects pre-filled with their $joinClassName objects.
+ * @param Criteria \$criteria
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return array Array of $className objects.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doSelectJoin".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)."(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
+ {
+ \$criteria = clone \$criteria;
+
+ // Set the correct dbName if it has not been overridden
+ if (\$criteria->getDbName() == Propel::getDefaultDB()) {
+ \$criteria->setDbName(self::DATABASE_NAME);
+ }
+
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$startcol = (".$this->getPeerClassname()."::NUM_COLUMNS - ".$this->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS);
+ ".$joinedTablePeerBuilder->getPeerClassname()."::addSelectColumns(\$criteria);
+";
+
+ $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ \$stmt = ".$this->basePeerClassname."::doSelect(\$criteria, \$con);
+ \$results = array();
+
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key1 = ".$this->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$obj1 = ".$this->getPeerClassname()."::getInstanceFromPool(\$key1))) {
+ // We no longer rehydrate the object, since this can cause data loss.
+ // See http://www.propelorm.org/ticket/509
+ // \$obj1->hydrate(\$row, 0, true); // rehydrate
+ } else {
+";
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$this->getPeerClassname()."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$this->getPeerClassname()."::getOMClass(false);
+";
+ }
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
+ \$obj1->hydrate(\$row);
+ ".$this->getPeerClassname()."::addInstanceToPool(\$obj1, \$key1);
+ } // if \$obj1 already loaded
+
+ \$key2 = ".$joinedTablePeerBuilder->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, \$startcol);
+ if (\$key2 !== null) {
+ \$obj2 = ".$joinedTablePeerBuilder->getPeerClassname()."::getInstanceFromPool(\$key2);
+ if (!\$obj2) {
+";
+ if ($joinTable->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(\$row, \$startcol);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(false);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj2', '$cls') . "
+ \$obj2->hydrate(\$row, \$startcol);
+ ".$joinedTablePeerBuilder->getPeerClassname()."::addInstanceToPool(\$obj2, \$key2);
+ } // if obj2 already loaded
+
+ // Add the \$obj1 (".$this->getObjectClassname().") to \$obj2 (".$joinedTablePeerBuilder->getObjectClassname().")";
+ if ($fk->isLocalPrimaryKey()) {
+ $script .= "
+ // one to one relationship
+ \$obj1->set" . $joinedTablePeerBuilder->getObjectClassname() . "(\$obj2);";
+ } else {
+ $script .= "
+ \$obj2->add" . $joinedTableObjectBuilder->getRefFKPhpNameAffix($fk, $plural = false)."(\$obj1);";
+ }
+ $script .= "
+
+ } // if joined row was not null
+
+ \$results[] = \$obj1;
+ }
+ \$stmt->closeCursor();
+ return \$results;
+ }
+";
+ } // if fk table name != this table name
+ } // if ! is reference only
+ } // foreach column
+ } // if count(fk) > 1
+
+ } // addDoSelectJoin()
+
+ /**
+ * Adds the doCountJoin*() methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoCountJoin(&$script)
+ {
+ $table = $this->getTable();
+ $className = $this->getObjectClassname();
+ $countFK = count($table->getForeignKeys());
+ $join_behavior = $this->getJoinBehavior();
+
+ if ($countFK >= 1) {
+
+ foreach ($table->getForeignKeys() as $fk) {
+
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ if (!$joinTable->isForReferenceOnly()) {
+
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+
+ $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+
+ $joinClassName = $joinedTableObjectBuilder->getObjectClassname();
+
+ $script .= "
+
+ /**
+ * Returns the number of rows matching criteria, joining the related ".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)." table
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return int Number of matching rows.
+ */
+ public static function doCountJoin".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)."(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
+ {
+ // we're going to modify criteria, so copy it first
+ \$criteria = clone \$criteria;
+
+ // We need to set the primary table name, since in the case that there are no WHERE columns
+ // it will be impossible for the BasePeer::createSelectSql() method to determine which
+ // tables go into the FROM clause.
+ \$criteria->setPrimaryTableName(".$this->getPeerClassname()."::TABLE_NAME);
+
+ if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
+ \$criteria->setDistinct();
+ }
+
+ if (!\$criteria->hasSelectClause()) {
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ }
+
+ \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+";
+ $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ \$stmt = ".$this->basePeerClassname."::doCount(\$criteria, \$con);
+
+ if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$count = (int) \$row[0];
+ } else {
+ \$count = 0; // no rows returned; we infer that means 0 matches.
+ }
+ \$stmt->closeCursor();
+ return \$count;
+ }
+";
+ } // if fk table name != this table name
+ } // if ! is reference only
+ } // foreach column
+ } // if count(fk) > 1
+
+ } // addDoCountJoin()
+
+ /**
+ * Adds the doSelectJoinAll() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelectJoinAll(&$script)
+ {
+ $table = $this->getTable();
+ $className = $this->getObjectClassname();
+ $join_behavior = $this->getJoinBehavior();
+
+ $script .= "
+
+ /**
+ * Selects a collection of $className objects pre-filled with all related objects.
+ *
+ * @param Criteria \$criteria
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return array Array of $className objects.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doSelectJoinAll(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
+ {
+ \$criteria = clone \$criteria;
+
+ // Set the correct dbName if it has not been overridden
+ if (\$criteria->getDbName() == Propel::getDefaultDB()) {
+ \$criteria->setDbName(self::DATABASE_NAME);
+ }
+
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$startcol2 = (".$this->getPeerClassname()."::NUM_COLUMNS - ".$this->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS);
+";
+ $index = 2;
+ foreach ($table->getForeignKeys() as $fk) {
+
+ // Want to cover this case, but the code is not there yet.
+ // Propel lacks a system for aliasing tables of the same name.
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $new_index = $index + 1;
+
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $joinClassName = $joinedTablePeerBuilder->getObjectClassname();
+
+ $script .= "
+ ".$joinedTablePeerBuilder->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$startcol$new_index = \$startcol$index + (".$joinedTablePeerBuilder->getPeerClassname()."::NUM_COLUMNS - ".$joinedTablePeerBuilder->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS);
+";
+ $index = $new_index;
+
+ } // if fk->getForeignTableName != table->getName
+ } // foreach [sub] foreign keys
+
+ foreach ($table->getForeignKeys() as $fk) {
+ // want to cover this case, but the code is not there yet.
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
+ }
+ }
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ \$stmt = ".$this->basePeerClassname."::doSelect(\$criteria, \$con);
+ \$results = array();
+
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key1 = ".$this->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$obj1 = ".$this->getPeerClassname()."::getInstanceFromPool(\$key1))) {
+ // We no longer rehydrate the object, since this can cause data loss.
+ // See http://www.propelorm.org/ticket/509
+ // \$obj1->hydrate(\$row, 0, true); // rehydrate
+ } else {";
+
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$this->getPeerClassname()."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$this->getPeerClassname()."::getOMClass(false);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
+ \$obj1->hydrate(\$row);
+ ".$this->getPeerClassname()."::addInstanceToPool(\$obj1, \$key1);
+ } // if obj1 already loaded
+";
+
+ $index = 1;
+ foreach ($table->getForeignKeys() as $fk ) {
+ // want to cover this case, but the code is not there yet.
+ // Why not? -because we'd have to alias the tables in the JOIN
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+
+
+ $joinClassName = $joinedTableObjectBuilder->getObjectClassname();
+ $interfaceName = $joinClassName;
+
+ if ($joinTable->getInterface()) {
+ $interfaceName = $this->prefixClassname($joinTable->getInterface());
+ }
+
+ $index++;
+
+ $script .= "
+ // Add objects for joined $joinClassName rows
+
+ \$key$index = ".$joinedTablePeerBuilder->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, \$startcol$index);
+ if (\$key$index !== null) {
+ \$obj$index = ".$joinedTablePeerBuilder->getPeerClassname()."::getInstanceFromPool(\$key$index);
+ if (!\$obj$index) {
+";
+ if ($joinTable->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(\$row, \$startcol$index);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(false);
+";
+ } /* $joinTable->getChildrenColumn() */
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj' . $index, '$cls') . "
+ \$obj".$index."->hydrate(\$row, \$startcol$index);
+ ".$joinedTablePeerBuilder->getPeerClassname()."::addInstanceToPool(\$obj$index, \$key$index);
+ } // if obj$index loaded
+
+ // Add the \$obj1 (".$this->getObjectClassname().") to the collection in \$obj".$index." (".$joinedTablePeerBuilder->getObjectClassname().")";
+ if ($fk->isLocalPrimaryKey()) {
+ $script .= "
+ \$obj1->set".$joinedTablePeerBuilder->getObjectClassname()."(\$obj".$index.");";
+ } else {
+ $script .= "
+ \$obj".$index."->add".$joinedTableObjectBuilder->getRefFKPhpNameAffix($fk, $plural = false)."(\$obj1);";
+ }
+ $script .= "
+ } // if joined row not null
+";
+
+ } // $fk->getForeignTableName() != $table->getName()
+ } //foreach foreign key
+
+ $script .= "
+ \$results[] = \$obj1;
+ }
+ \$stmt->closeCursor();
+ return \$results;
+ }
+";
+
+ } // end addDoSelectJoinAll()
+
+
+ /**
+ * Adds the doCountJoinAll() method.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoCountJoinAll(&$script)
+ {
+ $table = $this->getTable();
+ $className = $this->getObjectClassname();
+ $join_behavior = $this->getJoinBehavior();
+
+ $script .= "
+
+ /**
+ * Returns the number of rows matching criteria, joining all related tables
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return int Number of matching rows.
+ */
+ public static function doCountJoinAll(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
+ {
+ // we're going to modify criteria, so copy it first
+ \$criteria = clone \$criteria;
+
+ // We need to set the primary table name, since in the case that there are no WHERE columns
+ // it will be impossible for the BasePeer::createSelectSql() method to determine which
+ // tables go into the FROM clause.
+ \$criteria->setPrimaryTableName(".$this->getPeerClassname()."::TABLE_NAME);
+
+ if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
+ \$criteria->setDistinct();
+ }
+
+ if (!\$criteria->hasSelectClause()) {
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ }
+
+ \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+";
+
+ foreach ($table->getForeignKeys() as $fk) {
+ // want to cover this case, but the code is not there yet.
+ if ( $fk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
+ } // if fk->getForeignTableName != table->getName
+ } // foreach [sub] foreign keys
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ \$stmt = ".$this->basePeerClassname."::doCount(\$criteria, \$con);
+
+ if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$count = (int) \$row[0];
+ } else {
+ \$count = 0; // no rows returned; we infer that means 0 matches.
+ }
+ \$stmt->closeCursor();
+ return \$count;
+ }";
+ } // end addDoCountJoinAll()
+
+ /**
+ * Adds the doSelectJoinAllExcept*() methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoSelectJoinAllExcept(&$script)
+ {
+ $table = $this->getTable();
+ $join_behavior = $this->getJoinBehavior();
+
+ // ------------------------------------------------------------------------
+ // doSelectJoinAllExcept*()
+ // ------------------------------------------------------------------------
+
+ // 2) create a bunch of doSelectJoinAllExcept*() methods
+ // -- these were existing in original Torque, so we should keep them for compatibility
+
+ $fkeys = $table->getForeignKeys(); // this sep assignment is necessary otherwise sub-loops over
+ // getForeignKeys() will cause this to only execute one time.
+ foreach ($fkeys as $fk ) {
+
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ $excludedTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
+ $excludedTableObjectBuilder = $this->getNewObjectBuilder($excludedTable);
+ $excludedTablePeerBuilder = $this->getNewPeerBuilder($excludedTable);
+
+ $excludedClassName = $excludedTableObjectBuilder->getObjectClassname();
+
+
+ $script .= "
+
+ /**
+ * Selects a collection of ".$this->getObjectClassname()." objects pre-filled with all related objects except ".$thisTableObjectBuilder->getFKPhpNameAffix($fk).".
+ *
+ * @param Criteria \$criteria
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return array Array of ".$this->getObjectClassname()." objects.
+ * @throws PropelException Any exceptions caught during processing will be
+ * rethrown wrapped into a PropelException.
+ */
+ public static function doSelectJoinAllExcept".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)."(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
+ {
+ \$criteria = clone \$criteria;
+
+ // Set the correct dbName if it has not been overridden
+ // \$criteria->getDbName() will return the same object if not set to another value
+ // so == check is okay and faster
+ if (\$criteria->getDbName() == Propel::getDefaultDB()) {
+ \$criteria->setDbName(self::DATABASE_NAME);
+ }
+
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$startcol2 = (".$this->getPeerClassname()."::NUM_COLUMNS - ".$this->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS);
+";
+ $index = 2;
+ foreach ($table->getForeignKeys() as $subfk) {
+ // want to cover this case, but the code is not there yet.
+ // Why not? - because we would have to alias the tables in the join
+ if ( !($subfk->getForeignTableName() == $table->getName())) {
+ $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
+ $joinTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $joinClassName = $joinTablePeerBuilder->getObjectClassname();
+
+ if ($joinClassName != $excludedClassName) {
+ $new_index = $index + 1;
+ $script .= "
+ ".$joinTablePeerBuilder->getPeerClassname()."::addSelectColumns(\$criteria);
+ \$startcol$new_index = \$startcol$index + (".$joinTablePeerBuilder->getPeerClassname()."::NUM_COLUMNS - ".$joinTablePeerBuilder->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS);
+";
+ $index = $new_index;
+ } // if joinClassName not excludeClassName
+ } // if subfk is not curr table
+ } // foreach [sub] foreign keys
+
+ foreach ($table->getForeignKeys() as $subfk) {
+ // want to cover this case, but the code is not there yet.
+ if ( $subfk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $joinClassName = $joinedTablePeerBuilder->getObjectClassname();
+
+ if ($joinClassName != $excludedClassName)
+ {
+ $script .= $this->addCriteriaJoin($subfk, $table, $joinTable, $joinedTablePeerBuilder);
+ }
+ }
+ } // foreach fkeys
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+
+ \$stmt = ".$this->basePeerClassname ."::doSelect(\$criteria, \$con);
+ \$results = array();
+
+ while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$key1 = ".$this->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, 0);
+ if (null !== (\$obj1 = ".$this->getPeerClassname()."::getInstanceFromPool(\$key1))) {
+ // We no longer rehydrate the object, since this can cause data loss.
+ // See http://www.propelorm.org/ticket/509
+ // \$obj1->hydrate(\$row, 0, true); // rehydrate
+ } else {";
+ if ($table->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$this->getPeerClassname()."::getOMClass(\$row, 0);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$this->getPeerClassname()."::getOMClass(false);
+";
+ }
+
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
+ \$obj1->hydrate(\$row);
+ ".$this->getPeerClassname()."::addInstanceToPool(\$obj1, \$key1);
+ } // if obj1 already loaded
+";
+
+ $index = 1;
+ foreach ($table->getForeignKeys() as $subfk ) {
+ // want to cover this case, but the code is not there yet.
+ if ( $subfk->getForeignTableName() != $table->getName() ) {
+
+ $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
+
+ $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+
+ $joinClassName = $joinedTableObjectBuilder->getObjectClassname();
+
+ $interfaceName = $joinClassName;
+ if ($joinTable->getInterface()) {
+ $interfaceName = $this->prefixClassname($joinTable->getInterface());
+ }
+
+ if ($joinClassName != $excludedClassName) {
+
+ $index++;
+
+ $script .= "
+ // Add objects for joined $joinClassName rows
+
+ \$key$index = ".$joinedTablePeerBuilder->getPeerClassname()."::getPrimaryKeyHashFromRow(\$row, \$startcol$index);
+ if (\$key$index !== null) {
+ \$obj$index = ".$joinedTablePeerBuilder->getPeerClassname()."::getInstanceFromPool(\$key$index);
+ if (!\$obj$index) {
+ ";
+
+ if ($joinTable->getChildrenColumn()) {
+ $script .= "
+ \$omClass = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(\$row, \$startcol$index);
+ \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
+";
+ } else {
+ $script .= "
+ \$cls = ".$joinedTablePeerBuilder->getPeerClassname()."::getOMClass(false);
+";
+ } /* $joinTable->getChildrenColumn() */
+ $script .= "
+ " . $this->buildObjectInstanceCreationCode('$obj' . $index, '$cls') . "
+ \$obj".$index."->hydrate(\$row, \$startcol$index);
+ ".$joinedTablePeerBuilder->getPeerClassname()."::addInstanceToPool(\$obj$index, \$key$index);
+ } // if \$obj$index already loaded
+
+ // Add the \$obj1 (".$this->getObjectClassname().") to the collection in \$obj".$index." (".$joinedTablePeerBuilder->getObjectClassname().")";
+ if ($subfk->isLocalPrimaryKey()) {
+ $script .= "
+ \$obj1->set".$joinedTablePeerBuilder->getObjectClassname()."(\$obj".$index.");";
+ } else {
+ $script .= "
+ \$obj".$index."->add".$joinedTableObjectBuilder->getRefFKPhpNameAffix($subfk, $plural = false)."(\$obj1);";
+ }
+ $script .= "
+
+ } // if joined row is not null
+";
+ } // if ($joinClassName != $excludedClassName) {
+ } // $subfk->getForeignTableName() != $table->getName()
+ } // foreach
+ $script .= "
+ \$results[] = \$obj1;
+ }
+ \$stmt->closeCursor();
+ return \$results;
+ }
+";
+ } // foreach fk
+
+ } // addDoSelectJoinAllExcept
+
+ /**
+ * Adds the doCountJoinAllExcept*() methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addDoCountJoinAllExcept(&$script)
+ {
+ $table = $this->getTable();
+ $join_behavior = $this->getJoinBehavior();
+
+ $fkeys = $table->getForeignKeys(); // this sep assignment is necessary otherwise sub-loops over
+ // getForeignKeys() will cause this to only execute one time.
+ foreach ($fkeys as $fk ) {
+
+ $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ $excludedTable = $table->getDatabase()->getTable($fk->getForeignTableName());
+
+ $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
+ $excludedTableObjectBuilder = $this->getNewObjectBuilder($excludedTable);
+ $excludedTablePeerBuilder = $this->getNewPeerBuilder($excludedTable);
+
+ $excludedClassName = $excludedTableObjectBuilder->getObjectClassname();
+
+ $script .= "
+
+ /**
+ * Returns the number of rows matching criteria, joining the related ".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)." table
+ *
+ * @param Criteria \$criteria
+ * @param boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
+ * @param PropelPDO \$con
+ * @param String \$join_behavior the type of joins to use, defaults to $join_behavior
+ * @return int Number of matching rows.
+ */
+ public static function doCountJoinAllExcept".$thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false)."(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
+ {
+ // we're going to modify criteria, so copy it first
+ \$criteria = clone \$criteria;
+
+ // We need to set the primary table name, since in the case that there are no WHERE columns
+ // it will be impossible for the BasePeer::createSelectSql() method to determine which
+ // tables go into the FROM clause.
+ \$criteria->setPrimaryTableName(".$this->getPeerClassname()."::TABLE_NAME);
+
+ if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
+ \$criteria->setDistinct();
+ }
+
+ if (!\$criteria->hasSelectClause()) {
+ ".$this->getPeerClassname()."::addSelectColumns(\$criteria);
+ }
+
+ \$criteria->clearOrderByColumns(); // ORDER BY should not affect count
+
+ // Set the correct dbName
+ \$criteria->setDbName(self::DATABASE_NAME);
+
+ if (\$con === null) {
+ \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME, Propel::CONNECTION_READ);
+ }
+ ";
+
+ foreach ($table->getForeignKeys() as $subfk) {
+ // want to cover this case, but the code is not there yet.
+ if ( $subfk->getForeignTableName() != $table->getName() ) {
+ $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
+ $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
+ $joinClassName = $joinedTablePeerBuilder->getObjectClassname();
+
+ if ($joinClassName != $excludedClassName)
+ {
+ $script .= $this->addCriteriaJoin($subfk, $table, $joinTable, $joinedTablePeerBuilder);
+ }
+ }
+ } // foreach fkeys
+
+ // apply behaviors
+ $this->applyBehaviorModifier('preSelect', $script);
+
+ $script .= "
+ \$stmt = ".$this->basePeerClassname."::doCount(\$criteria, \$con);
+
+ if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
+ \$count = (int) \$row[0];
+ } else {
+ \$count = 0; // no rows returned; we infer that means 0 matches.
+ }
+ \$stmt->closeCursor();
+ return \$count;
+ }
+";
+ } // foreach fk
+
+ } // addDoCountJoinAllExcept
+
+ /**
+ * returns the desired join behavior as set in the build properties
+ * see trac ticket #588, #491
+ *
+ */
+ protected function getJoinBehavior()
+ {
+ return $this->getGeneratorConfig()->getBuildProperty('useLeftJoinsInDoJoinMethods') ? 'Criteria::LEFT_JOIN' : 'Criteria::INNER_JOIN';
+ }
+
+} // PHP5PeerBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PHP5TableMapBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PHP5TableMapBuilder.php
new file mode 100644
index 000000000..a156b29bf
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PHP5TableMapBuilder.php
@@ -0,0 +1,354 @@
+
+ * @package propel.generator.builder.om
+ */
+class PHP5TableMapBuilder extends OMBuilder
+{
+
+ /**
+ * Gets the package for the map builder classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . '.map';
+ }
+
+ public function getNamespace()
+ {
+ if ($namespace = parent::getNamespace()) {
+ if ($this->getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceMap')) {
+ return $namespace . '\\' . $omns;
+ } else {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getTable()->getPhpName() . 'TableMap';
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+
+/**
+ * This class defines the structure of the '".$table->getName()."' table.
+ *
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ *
+ * This map class is used by Propel to do runtime db structure discovery.
+ * For example, the createSelectSql() method checks the type of a given column used in an
+ * ORDER BY clause to know whether it needs to apply SQL to make the ORDER BY case-insensitive
+ * (i.e. if it's a text column type).
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class ".$this->getClassname()." extends TableMap {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the map builder class.
+ * This can be overridden by subclasses that wish to add more methods.
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $this->declareClasses('TableMap', 'RelationMap');
+ $this->addConstants($script);
+ $this->addAttributes($script);
+ $this->addInitialize($script);
+ $this->addBuildRelations($script);
+ $this->addGetBehaviors($script);
+ }
+
+ /**
+ * Adds any constants needed for this TableMap class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addConstants(&$script)
+ {
+ $script .= "
+ /**
+ * The (dot-path) name of this class
+ */
+ const CLASS_NAME = '".$this->getClasspath()."';
+";
+ }
+
+ /**
+ * Adds any attributes needed for this TableMap class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addAttributes(&$script)
+ {
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('tableMapFilter', $script, "");
+ }
+
+ /**
+ * Adds the addInitialize() method to the table map class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addInitialize(&$script)
+ {
+
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ $ddlBuilder = $this->getDDLBuilder();
+
+ $script .= "
+ /**
+ * Initialize the table attributes, columns and validators
+ * Relations are not initialized by this method since they are lazy loaded
+ *
+ * @return void
+ * @throws PropelException
+ */
+ public function initialize()
+ {
+ // attributes
+ \$this->setName('".$table->getName()."');
+ \$this->setPhpName('".$table->getPhpName()."');
+ \$this->setClassname('" . addslashes($this->getStubObjectBuilder()->getFullyQualifiedClassname()) . "');
+ \$this->setPackage('" . parent::getPackage() . "');";
+ if ($table->getIdMethod() == "native") {
+ $script .= "
+ \$this->setUseIdGenerator(true);";
+ } else {
+ $script .= "
+ \$this->setUseIdGenerator(false);";
+ }
+
+ if ($table->getIdMethodParameters()) {
+ $params = $table->getIdMethodParameters();
+ $imp = $params[0];
+ $script .= "
+ \$this->setPrimaryKeyMethodInfo('".$imp->getValue()."');";
+ } elseif ($table->getIdMethod() == IDMethod::NATIVE && ($platform->getNativeIdMethod() == Platform::SEQUENCE || $platform->getNativeIdMethod() == Platform::SERIAL)) {
+ $script .= "
+ \$this->setPrimaryKeyMethodInfo('".$ddlBuilder->getSequenceName()."');";
+ }
+
+ if ($this->getTable()->getChildrenColumn()) {
+ $script .= "
+ \$this->setSingleTableInheritance(true);";
+ }
+
+ // Add columns to map
+ $script .= "
+ // columns";
+ foreach ($table->getColumns() as $col) {
+ $cup=strtoupper($col->getName());
+ $cfc=$col->getPhpName();
+ if (!$col->getSize()) {
+ $size = "null";
+ } else {
+ $size = $col->getSize();
+ }
+ $default = $col->getDefaultValueString();
+ if ($col->isPrimaryKey()) {
+ if ($col->isForeignKey()) {
+ foreach ($col->getForeignKeys() as $fk) {
+ $script .= "
+ \$this->addForeignPrimaryKey('$cup', '$cfc', '".$col->getType()."' , '".$fk->getForeignTableName()."', '".strtoupper($fk->getMappedForeignColumn($col->getName()))."', ".($col->isNotNull() ? 'true' : 'false').", ".$size.", $default);";
+ }
+ } else {
+ $script .= "
+ \$this->addPrimaryKey('$cup', '$cfc', '".$col->getType()."', ".var_export($col->isNotNull(), true).", ".$size.", $default);";
+ }
+ } else {
+ if ($col->isForeignKey()) {
+ foreach ($col->getForeignKeys() as $fk) {
+ $script .= "
+ \$this->addForeignKey('$cup', '$cfc', '".$col->getType()."', '".$fk->getForeignTableName()."', '".strtoupper($fk->getMappedForeignColumn($col->getName()))."', ".($col->isNotNull() ? 'true' : 'false').", ".$size.", $default);";
+ }
+ } else {
+ $script .= "
+ \$this->addColumn('$cup', '$cfc', '".$col->getType()."', ".var_export($col->isNotNull(), true).", ".$size.", $default);";
+ }
+ } // if col-is prim key
+ } // foreach
+
+ // validators
+ $script .= "
+ // validators";
+ foreach ($table->getValidators() as $val) {
+ $col = $val->getColumn();
+ $cup = strtoupper($col->getName());
+ foreach ($val->getRules() as $rule) {
+ if ($val->getTranslate() !== Validator::TRANSLATE_NONE) {
+ $script .= "
+ \$this->addValidator('$cup', '".$rule->getName()."', '".$rule->getClass()."', '".str_replace("'", "\'", $rule->getValue())."', ".$val->getTranslate()."('".str_replace("'", "\'", $rule->getMessage())."'));";
+ } else {
+ $script .= "
+ \$this->addValidator('$cup', '".$rule->getName()."', '".$rule->getClass()."', '".str_replace("'", "\'", $rule->getValue())."', '".str_replace("'", "\'", $rule->getMessage())."');";
+ } // if ($rule->getTranslation() ...
+ } // foreach rule
+ } // foreach validator
+
+ $script .= "
+ } // initialize()
+";
+
+ }
+
+ /**
+ * Adds the method that build the RelationMap objects
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBuildRelations(&$script)
+ {
+ $script .= "
+ /**
+ * Build the RelationMap objects for this table relationships
+ */
+ public function buildRelations()
+ {";
+ foreach ($this->getTable()->getForeignKeys() as $fkey)
+ {
+ $columnMapping = 'array(';
+ foreach ($fkey->getLocalForeignMapping() as $key => $value)
+ {
+ $columnMapping .= "'$key' => '$value', ";
+ }
+ $columnMapping .= ')';
+ $onDelete = $fkey->hasOnDelete() ? "'" . $fkey->getOnDelete() . "'" : 'null';
+ $onUpdate = $fkey->hasOnUpdate() ? "'" . $fkey->getOnUpdate() . "'" : 'null';
+ $script .= "
+ \$this->addRelation('" . $this->getFKPhpNameAffix($fkey) . "', '" . addslashes($this->getNewStubObjectBuilder($fkey->getForeignTable())->getFullyQualifiedClassname()) . "', RelationMap::MANY_TO_ONE, $columnMapping, $onDelete, $onUpdate);";
+ }
+ foreach ($this->getTable()->getReferrers() as $fkey)
+ {
+ $columnMapping = 'array(';
+ foreach ($fkey->getForeignLocalMapping() as $key => $value)
+ {
+ $columnMapping .= "'$key' => '$value', ";
+ }
+ $columnMapping .= ')';
+ $onDelete = $fkey->hasOnDelete() ? "'" . $fkey->getOnDelete() . "'" : 'null';
+ $onUpdate = $fkey->hasOnUpdate() ? "'" . $fkey->getOnUpdate() . "'" : 'null';
+ $script .= "
+ \$this->addRelation('" . $this->getRefFKPhpNameAffix($fkey) . "', '" . addslashes($this->getNewStubObjectBuilder($fkey->getTable())->getFullyQualifiedClassname()) . "', RelationMap::ONE_TO_" . ($fkey->isLocalPrimaryKey() ? "ONE" : "MANY") .", $columnMapping, $onDelete, $onUpdate);";
+ }
+ foreach ($this->getTable()->getCrossFks() as $fkList)
+ {
+ list($refFK, $crossFK) = $fkList;
+ $onDelete = $fkey->hasOnDelete() ? "'" . $fkey->getOnDelete() . "'" : 'null';
+ $onUpdate = $fkey->hasOnUpdate() ? "'" . $fkey->getOnUpdate() . "'" : 'null';
+ $script .= "
+ \$this->addRelation('" . $this->getFKPhpNameAffix($crossFK) . "', '" . addslashes($this->getNewStubObjectBuilder($crossFK->getForeignTable())->getFullyQualifiedClassname()) . "', RelationMap::MANY_TO_MANY, array(), $onDelete, $onUpdate);";
+ }
+ $script .= "
+ } // buildRelations()
+";
+ }
+
+ /**
+ * Adds the behaviors getter
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetBehaviors(&$script)
+ {
+ if ($behaviors = $this->getTable()->getBehaviors())
+ {
+ $script .= "
+ /**
+ *
+ * Gets the list of behaviors registered for this table
+ *
+ * @return array Associative array (name => parameters) of behaviors
+ */
+ public function getBehaviors()
+ {
+ return array(";
+ foreach ($behaviors as $behavior)
+ {
+ $script .= "
+ '{$behavior->getName()}' => array(";
+ foreach ($behavior->getParameters() as $key => $value)
+ {
+ $script .= "'$key' => '$value', ";
+ }
+ $script .= "),";
+ }
+ $script .= "
+ );
+ } // getBehaviors()
+";
+ }
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier = null)
+ {
+ return parent::hasBehaviorModifier($hookName, 'TableMapBuilderModifier');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifier($hookName, &$script, $tab = " ")
+ {
+ return $this->applyBehaviorModifierBase($hookName, 'TableMapBuilderModifier', $script, $tab);
+ }
+} // PHP5TableMapBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/om/PeerBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/PeerBuilder.php
new file mode 100644
index 000000000..36c8ecb6f
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/PeerBuilder.php
@@ -0,0 +1,305 @@
+
+ * @package propel.generator.builder.om
+ */
+abstract class PeerBuilder extends OMBuilder
+{
+
+ protected $basePeerClass;
+ protected $basePeerClassname;
+
+ /**
+ * Constructs a new PeerBuilder subclass.
+ */
+ public function __construct(Table $table) {
+ parent::__construct($table);
+ $this->basePeerClassname = $this->basePeerClass = $this->getBasePeer($table);
+ $pos = strrpos($this->basePeerClassname, '.');
+ if ($pos !== false) {
+ $this->basePeerClassname = substr($this->basePeerClassname, $pos + 1);
+ }
+ }
+
+ /**
+ * Adds the addSelectColumns(), doCount(), etc. methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addSelectMethods(&$script)
+ {
+ $this->addAddSelectColumns($script);
+
+ $this->addDoCount($script);
+
+ // consider refactoring the doSelect stuff
+ // into a top-level method
+ $this->addDoSelectOne($script);
+ $this->addDoSelect($script);
+ $this->addDoSelectStmt($script); // <-- there's PDO code in here
+
+ $this->addAddInstanceToPool($script);
+ $this->addRemoveInstanceFromPool($script);
+ $this->addGetInstanceFromPool($script);
+ $this->addClearInstancePool($script);
+ $this->addClearRelatedInstancePool($script);
+
+ $this->addGetPrimaryKeyHash($script);
+ $this->addGetPrimaryKeyFromRow($script);
+ $this->addPopulateObjects($script); // <-- there's PDO code in here
+ $this->addPopulateObject($script);
+
+ }
+
+ /**
+ * Adds the correct getOMClass() method, depending on whether this table uses inheritance.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addGetOMClassMethod(&$script)
+ {
+ $table = $this->getTable();
+ if ($table->getChildrenColumn()) {
+ $this->addGetOMClass_Inheritance($script);
+ } else {
+ if ($table->isAbstract()) {
+ $this->addGetOMClass_NoInheritance_Abstract($script);
+ } else {
+ $this->addGetOMClass_NoInheritance($script);
+ }
+ }
+ }
+
+ /**
+ * Adds the doInsert(), doUpdate(), doDeleteAll(), doValidate(), etc. methods.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addUpdateMethods(&$script)
+ {
+ $this->addDoInsert($script);
+ $this->addDoUpdate($script);
+ $this->addDoDeleteAll($script);
+ $this->addDoDelete($script);
+ if ($this->isDeleteCascadeEmulationNeeded()) {
+ $this->addDoOnDeleteCascade($script);
+ }
+ if ($this->isDeleteSetNullEmulationNeeded()) {
+ $this->addDoOnDeleteSetNull($script);
+ }
+ $this->addDoValidate($script);
+ }
+
+ /**
+ * Adds the retrieveByPK() (and possibly retrieveByPKs()) method(s) appropriate for this class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addRetrieveByPKMethods(&$script)
+ {
+ if (count($this->getTable()->getPrimaryKey()) === 1) {
+ $this->addRetrieveByPK_SinglePK($script);
+ $this->addRetrieveByPKs_SinglePK($script);
+ } else {
+ $this->addRetrieveByPK_MultiPK($script);
+ }
+ }
+
+ /**
+ * This method adds the contents of the generated class to the script.
+ *
+ * This method contains the high-level logic that determines which methods
+ * get generated.
+ *
+ * Hint: Override this method in your subclass if you want to reorganize or
+ * drastically change the contents of the generated peer class.
+ *
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassBody(&$script)
+ {
+
+ $table = $this->getTable();
+
+ if (!$table->isAlias()) {
+ $this->addConstantsAndAttributes($script);
+ }
+
+ $this->addTranslateFieldName($script);
+ $this->addGetFieldNames($script);
+
+ if (!$table->isAlias()) {
+ $this->addAlias($script); // alias() utility method (deprecated?)
+ $this->addSelectMethods($script);
+ $this->addGetTableMap($script);
+ }
+
+ $this->addBuildTableMap($script);
+
+ $this->addGetOMClassMethod($script);
+
+ // add the insert, update, delete, validate etc. methods
+ if (!$table->isAlias() && !$table->isReadOnly()) {
+ $this->addUpdateMethods($script);
+ }
+
+ if (count($table->getPrimaryKey()) > 0) {
+ $this->addRetrieveByPKMethods($script);
+ }
+ }
+
+ /**
+ * Whether the platform in use requires ON DELETE CASCADE emulation and whether there are references to this table.
+ * @return boolean
+ */
+ protected function isDeleteCascadeEmulationNeeded()
+ {
+ $table = $this->getTable();
+ if ((!$this->getPlatform()->supportsNativeDeleteTrigger() || $this->getBuildProperty('emulateForeignKeyConstraints')) && count($table->getReferrers()) > 0) {
+ foreach ($table->getReferrers() as $fk) {
+ if ($fk->getOnDelete() == ForeignKey::CASCADE) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Whether the platform in use requires ON DELETE SETNULL emulation and whether there are references to this table.
+ * @return boolean
+ */
+ protected function isDeleteSetNullEmulationNeeded()
+ {
+ $table = $this->getTable();
+ if ((!$this->getPlatform()->supportsNativeDeleteTrigger() || $this->getBuildProperty('emulateForeignKeyConstraints')) && count($table->getReferrers()) > 0) {
+ foreach ($table->getReferrers() as $fk) {
+ if ($fk->getOnDelete() == ForeignKey::SETNULL) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Whether to add the generic mutator methods (setByName(), setByPosition(), fromArray()).
+ * This is based on the build property propel.addGenericMutators, and also whether the
+ * table is read-only or an alias.
+ * @return boolean
+ */
+ protected function isAddGenericMutators()
+ {
+ $table = $this->getTable();
+ return (!$table->isAlias() && $this->getBuildProperty('addGenericMutators') && !$table->isReadOnly());
+ }
+
+ /**
+ * Whether to add the generic accessor methods (getByName(), getByPosition(), toArray()).
+ * This is based on the build property propel.addGenericAccessors, and also whether the
+ * table is an alias.
+ * @return boolean
+ */
+ protected function isAddGenericAccessors()
+ {
+ $table = $this->getTable();
+ return (!$table->isAlias() && $this->getBuildProperty('addGenericAccessors'));
+ }
+
+ /**
+ * Returns the retrieveByPK method name to use for this table.
+ * If the table is an alias then the method name looks like "retrieveTablenameByPK"
+ * otherwise simply "retrieveByPK".
+ * @return string
+ */
+ public function getRetrieveMethodName()
+ {
+ if ($this->getTable()->isAlias()) {
+ $retrieveMethod = "retrieve" . $this->getTable()->getPhpName() . "ByPK";
+ } else {
+ $retrieveMethod = "retrieveByPK";
+ }
+ return $retrieveMethod;
+ }
+
+
+ /**
+ * COMPATIBILITY: Get the column constant name (e.g. PeerName::COLUMN_NAME).
+ *
+ * This method exists simply because it belonged to the 'PeerBuilder' that this
+ * class is replacing (because of name conflict more than actual functionality overlap).
+ * When the new builder model is finished this method will be removed.
+ *
+ * @param Column $col The column we need a name for.
+ * @param string $phpName The PHP Name of the peer class. The 'Peer' is appended automatically.
+ *
+ * @return string If $phpName is provided, then will return {$phpName}Peer::COLUMN_NAME; if not, just COLUMN_NAME.
+ * @deprecated
+ */
+ public static function getColumnName(Column $col, $phpName = null) {
+ // was it overridden in schema.xml ?
+ if ($col->getPeerName()) {
+ $const = strtoupper($col->getPeerName());
+ } else {
+ $const = strtoupper($col->getName());
+ }
+ if ($phpName !== null) {
+ return $phpName . 'Peer::' . $const;
+ } else {
+ return $const;
+ }
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier = null)
+ {
+ return parent::hasBehaviorModifier($hookName, 'PeerBuilderModifier');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifier($hookName, &$script, $tab = " ")
+ {
+ return $this->applyBehaviorModifierBase($hookName, 'PeerBuilderModifier', $script, $tab);
+ }
+
+ /**
+ * Checks whether any registered behavior content creator on that table exists a contentName
+ * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
+ */
+ public function getBehaviorContent($contentName)
+ {
+ return $this->getBehaviorContentBase($contentName, 'PeerBuilderModifier');
+ }
+
+ /**
+ * Get the BasePeer class name for the current table (e.g. 'BasePeer')
+ *
+ * @return string The Base Peer Class name
+ */
+ public function getBasePeerClassname()
+ {
+ return $this->basePeerClassname;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/QueryBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/QueryBuilder.php
new file mode 100644
index 000000000..1d21aa59a
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/QueryBuilder.php
@@ -0,0 +1,1065 @@
+getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceOm')) {
+ return $namespace . '\\' . $omns;
+ } else {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the current class being built.
+ * @return string
+ */
+ public function getUnprefixedClassname()
+ {
+ return $this->getBuildProperty('basePrefix') . $this->getStubQueryBuilder()->getUnprefixedClassname();
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ }
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $modelClass = $this->getStubObjectBuilder()->getClassname();
+ $parentClass = $this->getBehaviorContent('parentClass');
+ $parentClass = null === $parentClass ? 'ModelCriteria' : $parentClass;
+ $script .= "
+/**
+ * Base class that represents a query for the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+
+ // magic orderBy() methods, for IDE completion
+ foreach ($this->getTable()->getColumns() as $column) {
+ $script .= "
+ * @method $queryClass orderBy" . $column->getPhpName() . "(\$order = Criteria::ASC) Order by the " . $column->getName() . " column";
+ }
+ $script .= "
+ *";
+
+ // magic groupBy() methods, for IDE completion
+ foreach ($this->getTable()->getColumns() as $column) {
+ $script .= "
+ * @method $queryClass groupBy" . $column->getPhpName() . "() Group by the " . $column->getName() . " column";
+ }
+
+ // override the signature of ModelCriteria::left-, right- and innerJoin to specify the class of the returned object, for IDE completion
+ $script .= "
+ *
+ * @method $queryClass leftJoin(\$relation) Adds a LEFT JOIN clause to the query
+ * @method $queryClass rightJoin(\$relation) Adds a RIGHT JOIN clause to the query
+ * @method $queryClass innerJoin(\$relation) Adds a INNER JOIN clause to the query
+ *";
+
+ // magic XXXjoinYYY() methods, for IDE completion
+ foreach ($this->getTable()->getForeignKeys() as $fk) {
+ $relationName = $this->getFKPhpNameAffix($fk);
+
+ $script .= "
+ * @method $queryClass leftJoin" . $relationName . "(\$relationAlias = '') Adds a LEFT JOIN clause to the query using the " . $relationName . " relation
+ * @method $queryClass rightJoin" . $relationName . "(\$relationAlias = '') Adds a RIGHT JOIN clause to the query using the " . $relationName . " relation
+ * @method $queryClass innerJoin" . $relationName . "(\$relationAlias = '') Adds a INNER JOIN clause to the query using the " . $relationName . " relation
+ *";
+ }
+ foreach ($this->getTable()->getReferrers() as $refFK) {
+ $relationName = $this->getRefFKPhpNameAffix($refFK);
+
+ $script .= "
+ * @method $queryClass leftJoin" . $relationName . "(\$relationAlias = '') Adds a LEFT JOIN clause to the query using the " . $relationName . " relation
+ * @method $queryClass rightJoin" . $relationName . "(\$relationAlias = '') Adds a RIGHT JOIN clause to the query using the " . $relationName . " relation
+ * @method $queryClass innerJoin" . $relationName . "(\$relationAlias = '') Adds a INNER JOIN clause to the query using the " . $relationName . " relation
+ *";
+ }
+
+ // override the signature of ModelCriteria::findOne() to specify the class of the returned object, for IDE completion
+ $script .= "
+ * @method $modelClass findOne(PropelPDO \$con = null) Return the first $modelClass matching the query
+ * @method $modelClass findOneOrCreate(PropelPDO \$con = null) Return the first $modelClass matching the query, or a new $modelClass object populated from the query conditions when no match is found
+ *";
+
+ // magic findBy() methods, for IDE completion
+ foreach ($this->getTable()->getColumns() as $column) {
+ $script .= "
+ * @method $modelClass findOneBy" . $column->getPhpName() . "(" . $column->getPhpType() . " \$" . $column->getName() . ") Return the first $modelClass filtered by the " . $column->getName() . " column";
+ }
+ $script .= "
+ *";
+ foreach ($this->getTable()->getColumns() as $column) {
+ $script .= "
+ * @method array findBy" . $column->getPhpName() . "(" . $column->getPhpType() . " \$" . $column->getName() . ") Return $modelClass objects filtered by the " . $column->getName() . " column";
+ }
+
+ $script .= "
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+abstract class ".$this->getClassname()." extends " . $parentClass . "
+{
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ // namespaces
+ $this->declareClasses('ModelCriteria', 'Criteria', 'ModelJoin');
+ $this->declareClassFromBuilder($this->getStubQueryBuilder());
+ $this->declareClassFromBuilder($this->getStubPeerBuilder());
+
+ // apply behaviors
+ $this->applyBehaviorModifier('queryAttributes', $script, " ");
+ $this->addConstructor($script);
+ $this->addFactory($script);
+ $this->addFindPk($script);
+ $this->addFindPks($script);
+ $this->addFilterByPrimaryKey($script);
+ $this->addFilterByPrimaryKeys($script);
+ foreach ($this->getTable()->getColumns() as $col) {
+ $this->addFilterByCol($script, $col);
+ }
+ foreach ($this->getTable()->getForeignKeys() as $fk) {
+ $this->addFilterByFK($script, $fk);
+ $this->addJoinFk($script, $fk);
+ $this->addUseFKQuery($script, $fk);
+ }
+ foreach ($this->getTable()->getReferrers() as $refFK) {
+ $this->addFilterByRefFK($script, $refFK);
+ $this->addJoinRefFk($script, $refFK);
+ $this->addUseRefFKQuery($script, $refFK);
+ }
+ foreach ($this->getTable()->getCrossFks() as $fkList) {
+ list($refFK, $crossFK) = $fkList;
+ $this->addFilterByCrossFK($script, $refFK, $crossFK);
+ }
+ $this->addPrune($script);
+ $this->addBasePreSelect($script);
+ $this->addBasePreDelete($script);
+ $this->addBasePostDelete($script);
+ $this->addBasePreUpdate($script);
+ $this->addBasePostUpdate($script);
+ // apply behaviors
+ $this->applyBehaviorModifier('queryMethods', $script, " ");
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ $this->applyBehaviorModifier('queryFilter', $script, "");
+ }
+
+ /**
+ * Adds the constructor for this object.
+ * @param string &$script The script will be modified in this method.
+ * @see addConstructor()
+ */
+ protected function addConstructor(&$script)
+ {
+ $this->addConstructorComment($script);
+ $this->addConstructorOpen($script);
+ $this->addConstructorBody($script);
+ $this->addConstructorClose($script);
+ }
+
+ /**
+ * Adds the comment for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorComment(&$script)
+ {
+ $script .= "
+ /**
+ * Initializes internal state of ".$this->getClassname()." object.
+ *
+ * @param string \$dbName The dabase name
+ * @param string \$modelName The phpName of a model, e.g. 'Book'
+ * @param string \$modelAlias The alias for the model in this query, e.g. 'b'
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorOpen(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+ public function __construct(\$dbName = '" . $table->getDatabase()->getName() . "', \$modelName = '" . addslashes($this->getNewStubObjectBuilder($table)->getFullyQualifiedClassname()) . "', \$modelAlias = null)
+ {";
+ }
+
+ /**
+ * Adds the function body for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorBody(&$script)
+ {
+ $script .= "
+ parent::__construct(\$dbName, \$modelName, \$modelAlias);";
+ }
+
+ /**
+ * Adds the function close for the constructor
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addConstructorClose(&$script)
+ {
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the factory for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFactory(&$script)
+ {
+ $this->addFactoryComment($script);
+ $this->addFactoryOpen($script);
+ $this->addFactoryBody($script);
+ $this->addFactoryClose($script);
+ }
+
+ /**
+ * Adds the comment for the factory
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addFactoryComment(&$script)
+ {
+ $classname = $this->getNewStubQueryBuilder($this->getTable())->getClassname();
+ $script .= "
+ /**
+ * Returns a new " . $classname . " object.
+ *
+ * @param string \$modelAlias The alias of a model in the query
+ * @param Criteria \$criteria Optional Criteria to build the query from
+ *
+ * @return " . $classname . "
+ */";
+ }
+
+ /**
+ * Adds the function declaration for the factory
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addFactoryOpen(&$script)
+ {
+ $script .= "
+ public static function create(\$modelAlias = null, \$criteria = null)
+ {";
+ }
+
+ /**
+ * Adds the function body for the factory
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addFactoryBody(&$script)
+ {
+ $classname = $this->getNewStubQueryBuilder($this->getTable())->getClassname();
+ $script .= "
+ if (\$criteria instanceof " . $classname . ") {
+ return \$criteria;
+ }
+ \$query = new " . $classname . "();
+ if (null !== \$modelAlias) {
+ \$query->setModelAlias(\$modelAlias);
+ }
+ if (\$criteria instanceof Criteria) {
+ \$query->mergeWith(\$criteria);
+ }
+ return \$query;";
+ }
+
+ /**
+ * Adds the function close for the factory
+ * @param string &$script The script will be modified in this method.
+ **/
+ protected function addFactoryClose(&$script)
+ {
+ $script .= "
+ }
+";
+ }
+
+
+ /**
+ * Adds the findPk method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFindPk(&$script)
+ {
+ $table = $this->getTable();
+ $pks = $table->getPrimaryKey();
+ $class = $class = $this->getStubObjectBuilder()->getClassname();
+ $script .= "
+ /**
+ * Find object by primary key";
+ if (count($pks) === 1) {
+ $pkType = 'mixed';
+ $script .= "
+ * Use instance pooling to avoid a database query if the object exists
+ *
+ * \$obj = \$c->findPk(12, \$con);";
+ } else {
+ $examplePk = array_slice(array(12, 34, 56, 78, 91), 0, count($pks));
+ $colNames = array();
+ foreach ($pks as $col) {
+ $colNames[]= '$' . $col->getName();
+ }
+ $pkType = 'array['. join($colNames, ', ') . ']';
+ $script .= "
+ *
+ * \$obj = \$c->findPk(array(" . join($examplePk, ', ') . "), \$con);";
+ }
+ $script .= "
+ *
+ * @param " . $pkType . " \$key Primary key to use for the query
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return " . $class . "|array|mixed the result, formatted by the current formatter
+ */
+ public function findPk(\$key, \$con = null)
+ {";
+ if (count($pks) === 1) {
+ $poolKeyHashParams = '$key';
+ } else {
+ $poolKeyHashParams = array();
+ for ($i = 0, $count = count($pks); $i < $count; $i++) {
+ $poolKeyHashParams[]= '$key[' . $i . ']';
+ }
+ }
+ // tip: we don't use findOne() to avoid putting an unecessary LIMIT 1 statement,
+ // which may be costly on platforms not natively supporting LIMIT (like Oracle)
+ $script .= "
+ if ((null !== (\$obj = ".$this->getPeerClassname()."::getInstanceFromPool(".$this->getPeerBuilder()->getInstancePoolKeySnippet($poolKeyHashParams)."))) && \$this->getFormatter()->isObjectFormatter()) {
+ // the object is alredy in the instance pool
+ return \$obj;
+ } else {
+ // the object has not been requested yet, or the formatter is not an object formatter
+ \$criteria = \$this->isKeepQuery() ? clone \$this : \$this;
+ \$stmt = \$criteria
+ ->filterByPrimaryKey(\$key)
+ ->getSelectStatement(\$con);
+ return \$criteria->getFormatter()->init(\$criteria)->formatOne(\$stmt);
+ }
+ }
+";
+ }
+
+ /**
+ * Adds the findPks method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFindPks(&$script)
+ {
+ $table = $this->getTable();
+ $pks = $table->getPrimaryKey();
+ $count = count($pks);
+ $script .= "
+ /**
+ * Find objects by primary key
+ * ";
+ if ($count === 1) {
+ $script .= "
+ * \$objs = \$c->findPks(array(12, 56, 832), \$con);";
+ } else {
+ $script .= "
+ * \$objs = \$c->findPks(array(array(12, 56), array(832, 123), array(123, 456)), \$con);";
+ }
+ $script .= "
+ *
+ * @param array \$keys Primary keys to use for the query
+ * @param PropelPDO \$con an optional connection object
+ *
+ * @return PropelObjectCollection|array|mixed the list of results, formatted by the current formatter
+ */
+ public function findPks(\$keys, \$con = null)
+ {
+ \$criteria = \$this->isKeepQuery() ? clone \$this : \$this;
+ return \$this
+ ->filterByPrimaryKeys(\$keys)
+ ->find(\$con);
+ }
+";
+ }
+
+ /**
+ * Adds the filterByPrimaryKey method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFilterByPrimaryKey(&$script)
+ {
+ $script .= "
+ /**
+ * Filter the query by primary key
+ *
+ * @param mixed \$key Primary key to use for the query
+ *
+ * @return " . $this->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
+ */
+ public function filterByPrimaryKey(\$key)
+ {";
+ $table = $this->getTable();
+ $pks = $table->getPrimaryKey();
+ if (count($pks) === 1) {
+ // simple primary key
+ $col = $pks[0];
+ $const = $this->getColumnConstant($col);
+ $script .= "
+ return \$this->addUsingAlias($const, \$key, Criteria::EQUAL);";
+ } else {
+ // composite primary key
+ $i = 0;
+ foreach ($pks as $col) {
+ $const = $this->getColumnConstant($col);
+ $script .= "
+ \$this->addUsingAlias($const, \$key[$i], Criteria::EQUAL);";
+ $i++;
+ }
+ $script .= "
+
+ return \$this;";
+ }
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the filterByPrimaryKey method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFilterByPrimaryKeys(&$script)
+ {
+ $script .= "
+ /**
+ * Filter the query by a list of primary keys
+ *
+ * @param array \$keys The list of primary key to use for the query
+ *
+ * @return " . $this->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
+ */
+ public function filterByPrimaryKeys(\$keys)
+ {";
+ $table = $this->getTable();
+ $pks = $table->getPrimaryKey();
+ if (count($pks) === 1) {
+ // simple primary key
+ $col = $pks[0];
+ $const = $this->getColumnConstant($col);
+ $script .= "
+ return \$this->addUsingAlias($const, \$keys, Criteria::IN);";
+ } else {
+ // composite primary key
+ $script .= "
+ foreach (\$keys as \$key) {";
+ $i = 0;
+ foreach ($pks as $col) {
+ $const = $this->getColumnConstant($col);
+ $script .= "
+ \$cton$i = \$this->getNewCriterion($const, \$key[$i], Criteria::EQUAL);";
+ if ($i>0) {
+ $script .= "
+ \$cton0->addAnd(\$cton$i);";
+ }
+ $i++;
+ }
+ $script .= "
+ \$this->addOr(\$cton0);
+ }";
+ $script .= "
+
+ return \$this;";
+ }
+ $script .= "
+ }
+";
+ }
+
+ /**
+ * Adds the filterByCol method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFilterByCol(&$script, $col)
+ {
+ $colPhpName = $col->getPhpName();
+ $colName = $col->getName();
+ $variableName = $col->getStudlyPhpName();
+ $qualifiedName = $this->getColumnConstant($col);
+ $script .= "
+ /**
+ * Filter the query on the $colName column
+ * ";
+ if ($col->isNumericType() || $col->isTemporalType()) {
+ $script .= "
+ * @param " . $col->getPhpType() . "|array \$$variableName The value to use as filter.
+ * Accepts an associative array('min' => \$minValue, 'max' => \$maxValue)";
+ } elseif ($col->isTextType()) {
+ $script .= "
+ * @param string \$$variableName The value to use as filter.
+ * Accepts wildcards (* and % trigger a LIKE)";
+ } elseif ($col->isBooleanType()) {
+ $script .= "
+ * @param boolean|string \$$variableName The value to use as filter.
+ * Accepts strings ('false', 'off', '-', 'no', 'n', and '0' are false, the rest is true)";
+ } else {
+ $script .= "
+ * @param mixed \$$variableName The value to use as filter";
+ }
+ $script .= "
+ * @param string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+ *
+ * @return " . $this->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
+ */
+ public function filterBy$colPhpName(\$$variableName = null, \$comparison = null)
+ {";
+ if ($col->isPrimaryKey() && ($col->getType() == PropelTypes::INTEGER || $col->getType() == PropelTypes::BIGINT)) {
+ $script .= "
+ if (is_array(\$$variableName) && null === \$comparison) {
+ \$comparison = Criteria::IN;
+ }";
+ } elseif ($col->isNumericType() || $col->isTemporalType()) {
+ $script .= "
+ if (is_array(\$$variableName)) {
+ \$useMinMax = false;
+ if (isset(\${$variableName}['min'])) {
+ \$this->addUsingAlias($qualifiedName, \${$variableName}['min'], Criteria::GREATER_EQUAL);
+ \$useMinMax = true;
+ }
+ if (isset(\${$variableName}['max'])) {
+ \$this->addUsingAlias($qualifiedName, \${$variableName}['max'], Criteria::LESS_EQUAL);
+ \$useMinMax = true;
+ }
+ if (\$useMinMax) {
+ return \$this;
+ }
+ if (null === \$comparison) {
+ \$comparison = Criteria::IN;
+ }
+ }";
+ } elseif ($col->isTextType()) {
+ $script .= "
+ if (null === \$comparison) {
+ if (is_array(\$$variableName)) {
+ \$comparison = Criteria::IN;
+ } elseif (preg_match('/[\%\*]/', \$$variableName)) {
+ \$$variableName = str_replace('*', '%', \$$variableName);
+ \$comparison = Criteria::LIKE;
+ }
+ }";
+ } elseif ($col->isBooleanType()) {
+ $script .= "
+ if (is_string(\$$variableName)) {
+ \$$colName = in_array(strtolower(\$$variableName), array('false', 'off', '-', 'no', 'n', '0')) ? false : true;
+ }";
+ }
+ $script .= "
+ return \$this->addUsingAlias($qualifiedName, \$$variableName, \$comparison);
+ }
+";
+ }
+
+ /**
+ * Adds the filterByFk method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFilterByFk(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $fkTable = $this->getForeignTable($fk);
+ $fkPhpName = $this->getNewStubObjectBuilder($fkTable)->getClassname();
+ $relationName = $this->getFKPhpNameAffix($fk);
+ $objectName = '$' . $fkTable->getStudlyPhpName();
+ $script .= "
+ /**
+ * Filter the query by a related $fkPhpName object
+ *
+ * @param $fkPhpName $objectName the related object to use as filter
+ * @param string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+ *
+ * @return $queryClass The current query, for fluid interface
+ */
+ public function filterBy$relationName($objectName, \$comparison = null)
+ {
+ return \$this";
+ foreach ($fk->getLocalForeignMapping() as $localColumn => $foreignColumn) {
+ $localColumnObject = $table->getColumn($localColumn);
+ $foreignColumnObject = $fkTable->getColumn($foreignColumn);
+ $script .= "
+ ->addUsingAlias(" . $this->getColumnConstant($localColumnObject) . ", " . $objectName . "->get" . $foreignColumnObject->getPhpName() . "(), \$comparison)";
+ }
+ $script .= ";
+ }
+";
+ }
+
+ /**
+ * Adds the filterByRefFk method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFilterByRefFk(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName());
+ $fkPhpName = $this->getNewStubObjectBuilder($fkTable)->getClassname();
+ $relationName = $this->getRefFKPhpNameAffix($fk);
+ $objectName = '$' . $fkTable->getStudlyPhpName();
+ $script .= "
+ /**
+ * Filter the query by a related $fkPhpName object
+ *
+ * @param $fkPhpName $objectName the related object to use as filter
+ * @param string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+ *
+ * @return $queryClass The current query, for fluid interface
+ */
+ public function filterBy$relationName($objectName, \$comparison = null)
+ {
+ return \$this";
+ foreach ($fk->getForeignLocalMapping() as $localColumn => $foreignColumn) {
+ $localColumnObject = $table->getColumn($localColumn);
+ $foreignColumnObject = $fkTable->getColumn($foreignColumn);
+ $script .= "
+ ->addUsingAlias(" . $this->getColumnConstant($localColumnObject) . ", " . $objectName . "->get" . $foreignColumnObject->getPhpName() . "(), \$comparison)";
+ }
+ $script .= ";
+ }
+";
+ }
+
+ /**
+ * Adds the joinFk method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addJoinFk(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $fkTable = $this->getForeignTable($fk);
+ $relationName = $this->getFKPhpNameAffix($fk);
+ $joinType = $this->getJoinType($fk);
+ $this->addJoinRelated($script, $fkTable, $queryClass, $relationName, $joinType);
+ }
+
+ /**
+ * Adds the joinRefFk method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addJoinRefFk(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName());
+ $relationName = $this->getRefFKPhpNameAffix($fk);
+ $joinType = $this->getJoinType($fk);
+ $this->addJoinRelated($script, $fkTable, $queryClass, $relationName, $joinType);
+ }
+
+ /**
+ * Adds a joinRelated method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addJoinRelated(&$script, $fkTable, $queryClass, $relationName, $joinType)
+ {
+ $script .= "
+ /**
+ * Adds a JOIN clause to the query using the " . $relationName . " relation
+ *
+ * @param string \$relationAlias optional alias for the relation
+ * @param string \$joinType Accepted values are null, 'left join', 'right join', 'inner join'
+ *
+ * @return ". $queryClass . " The current query, for fluid interface
+ */
+ public function join" . $relationName . "(\$relationAlias = '', \$joinType = " . $joinType . ")
+ {
+ \$tableMap = \$this->getTableMap();
+ \$relationMap = \$tableMap->getRelation('" . $relationName . "');
+
+ // create a ModelJoin object for this join
+ \$join = new ModelJoin();
+ \$join->setJoinType(\$joinType);
+ \$join->setRelationMap(\$relationMap, \$this->useAliasInSQL ? \$this->getModelAlias() : null, \$relationAlias);
+ if (\$previousJoin = \$this->getPreviousJoin()) {
+ \$join->setPreviousJoin(\$previousJoin);
+ }
+
+ // add the ModelJoin to the current object
+ if(\$relationAlias) {
+ \$this->addAlias(\$relationAlias, \$relationMap->getRightTable()->getName());
+ \$this->addJoinObject(\$join, \$relationAlias);
+ } else {
+ \$this->addJoinObject(\$join, '" . $relationName . "');
+ }
+
+ return \$this;
+ }
+";
+ }
+
+ /**
+ * Adds the useFkQuery method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addUseFkQuery(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $fkTable = $this->getForeignTable($fk);
+ $queryClass = $this->getNewStubQueryBuilder($fkTable)->getClassname();
+ $relationName = $this->getFKPhpNameAffix($fk);
+ $joinType = $this->getJoinType($fk);
+ $this->addUseRelatedQuery($script, $fkTable, $queryClass, $relationName, $joinType);
+ }
+
+ /**
+ * Adds the useFkQuery method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addUseRefFkQuery(&$script, $fk)
+ {
+ $table = $this->getTable();
+ $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName());
+ $queryClass = $this->getNewStubQueryBuilder($fkTable)->getClassname();
+ $relationName = $this->getRefFKPhpNameAffix($fk);
+ $joinType = $this->getJoinType($fk);
+ $this->addUseRelatedQuery($script, $fkTable, $queryClass, $relationName, $joinType);
+ }
+
+ /**
+ * Adds a useRelatedQuery method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addUseRelatedQuery(&$script, $fkTable, $queryClass, $relationName, $joinType)
+ {
+ $script .= "
+ /**
+ * Use the $relationName relation " . $fkTable->getPhpName() . " object
+ *
+ * @see useQuery()
+ *
+ * @param string \$relationAlias optional alias for the relation,
+ * to be used as main alias in the secondary query
+ * @param string \$joinType Accepted values are null, 'left join', 'right join', 'inner join'
+ *
+ * @return $queryClass A secondary query class using the current class as primary query
+ */
+ public function use" . $relationName . "Query(\$relationAlias = '', \$joinType = " . $joinType . ")
+ {
+ return \$this
+ ->join" . $relationName . "(\$relationAlias, \$joinType)
+ ->useQuery(\$relationAlias ? \$relationAlias : '$relationName', '$queryClass');
+ }
+";
+ }
+
+ protected function addFilterByCrossFK(&$script, $refFK, $crossFK)
+ {
+ $queryClass = $this->getStubQueryBuilder()->getClassname();
+ $crossRefTable = $crossFK->getTable();
+ $foreignTable = $crossFK->getForeignTable();
+ $fkPhpName = $foreignTable->getPhpName();
+ $crossTableName = $crossRefTable->getName();
+ $relName = $this->getFKPhpNameAffix($crossFK, $plural = false);
+ $objectName = '$' . $foreignTable->getStudlyPhpName();
+ $relationName = $this->getRefFKPhpNameAffix($refFK, $plural = false);
+ $script .= "
+ /**
+ * Filter the query by a related $fkPhpName object
+ * using the $crossTableName table as cross reference
+ *
+ * @param $fkPhpName $objectName the related object to use as filter
+ * @param string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+ *
+ * @return $queryClass The current query, for fluid interface
+ */
+ public function filterBy{$relName}($objectName, \$comparison = Criteria::EQUAL)
+ {
+ return \$this
+ ->use{$relationName}Query()
+ ->filterBy{$relName}($objectName, \$comparison)
+ ->endUse();
+ }
+ ";
+ }
+
+ /**
+ * Adds the prune method for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addPrune(&$script)
+ {
+ $table = $this->getTable();
+ $class = $this->getStubObjectBuilder()->getClassname();
+ $objectName = '$' . $table->getStudlyPhpName();
+ $script .= "
+ /**
+ * Exclude object from result
+ *
+ * @param $class $objectName Object to remove from the list of results
+ *
+ * @return " . $this->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
+ */
+ public function prune($objectName = null)
+ {
+ if ($objectName) {";
+ $pks = $table->getPrimaryKey();
+ if (count($pks) > 1) {
+ $i = 0;
+ $conditions = array();
+ foreach ($pks as $col) {
+ $const = $this->getColumnConstant($col);
+ $condName = "'pruneCond" . $i . "'";
+ $conditions[]= $condName;
+ $script .= "
+ \$this->addCond(". $condName . ", \$this->getAliasedColName($const), " . $objectName . "->get" . $col->getPhpName() . "(), Criteria::NOT_EQUAL);";
+ $i++;
+ }
+ $conditionsString = implode(', ', $conditions);
+ $script .= "
+ \$this->combine(array(" . $conditionsString . "), Criteria::LOGICAL_OR);";
+ } else {
+ $col = $pks[0];
+ $const = $this->getColumnConstant($col);
+ $script .= "
+ \$this->addUsingAlias($const, " . $objectName . "->get" . $col->getPhpName() . "(), Criteria::NOT_EQUAL);";
+ }
+ $script .= "
+ }
+
+ return \$this;
+ }
+";
+ }
+
+ /**
+ * Adds the basePreSelect hook for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBasePreSelect(&$script)
+ {
+ $behaviorCode = '';
+ $this->applyBehaviorModifier('preSelectQuery', $behaviorCode, " ");
+ if (!$behaviorCode) {
+ return;
+ }
+ $script .= "
+ /**
+ * Code to execute before every SELECT statement
+ *
+ * @param PropelPDO \$con The connection object used by the query
+ */
+ protected function basePreSelect(PropelPDO \$con)
+ {" . $behaviorCode . "
+
+ return \$this->preSelect(\$con);
+ }
+";
+ }
+
+ /**
+ * Adds the basePreDelete hook for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBasePreDelete(&$script)
+ {
+ $behaviorCode = '';
+ $this->applyBehaviorModifier('preDeleteQuery', $behaviorCode, " ");
+ if (!$behaviorCode) {
+ return;
+ }
+ $script .= "
+ /**
+ * Code to execute before every DELETE statement
+ *
+ * @param PropelPDO \$con The connection object used by the query
+ */
+ protected function basePreDelete(PropelPDO \$con)
+ {" . $behaviorCode . "
+
+ return \$this->preDelete(\$con);
+ }
+";
+ }
+
+ /**
+ * Adds the basePostDelete hook for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBasePostDelete(&$script)
+ {
+ $behaviorCode = '';
+ $this->applyBehaviorModifier('postDeleteQuery', $behaviorCode, " ");
+ if (!$behaviorCode) {
+ return;
+ }
+ $script .= "
+ /**
+ * Code to execute after every DELETE statement
+ *
+ * @param int \$affectedRows the number of deleted rows
+ * @param PropelPDO \$con The connection object used by the query
+ */
+ protected function basePostDelete(\$affectedRows, PropelPDO \$con)
+ {" . $behaviorCode . "
+
+ return \$this->postDelete(\$affectedRows, \$con);
+ }
+";
+ }
+
+ /**
+ * Adds the basePreUpdate hook for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBasePreUpdate(&$script)
+ {
+ $behaviorCode = '';
+ $this->applyBehaviorModifier('preUpdateQuery', $behaviorCode, " ");
+ if (!$behaviorCode) {
+ return;
+ }
+ $script .= "
+ /**
+ * Code to execute before every UPDATE statement
+ *
+ * @param array \$values The associatiove array of columns and values for the update
+ * @param PropelPDO \$con The connection object used by the query
+ * @param boolean \$forceIndividualSaves If false (default), the resulting call is a BasePeer::doUpdate(), ortherwise it is a series of save() calls on all the found objects
+ */
+ protected function basePreUpdate(&\$values, PropelPDO \$con, \$forceIndividualSaves = false)
+ {" . $behaviorCode . "
+
+ return \$this->preUpdate(\$values, \$con, \$forceIndividualSaves);
+ }
+";
+ }
+
+ /**
+ * Adds the basePostUpdate hook for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addBasePostUpdate(&$script)
+ {
+ $behaviorCode = '';
+ $this->applyBehaviorModifier('postUpdateQuery', $behaviorCode, " ");
+ if (!$behaviorCode) {
+ return;
+ }
+ $script .= "
+ /**
+ * Code to execute after every UPDATE statement
+ *
+ * @param int \$affectedRows the number of udated rows
+ * @param PropelPDO \$con The connection object used by the query
+ */
+ protected function basePostUpdate(\$affectedRows, PropelPDO \$con)
+ {" . $behaviorCode . "
+
+ return \$this->postUpdate(\$affectedRows, \$con);
+ }
+";
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @return boolean
+ */
+ public function hasBehaviorModifier($hookName, $modifier = null)
+ {
+ return parent::hasBehaviorModifier($hookName, 'QueryBuilderModifier');
+ }
+
+ /**
+ * Checks whether any registered behavior on that table has a modifier for a hook
+ * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
+ * @param string &$script The script will be modified in this method.
+ */
+ public function applyBehaviorModifier($hookName, &$script, $tab = " ")
+ {
+ return $this->applyBehaviorModifierBase($hookName, 'QueryBuilderModifier', $script, $tab);
+ }
+
+ /**
+ * Checks whether any registered behavior content creator on that table exists a contentName
+ * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
+ */
+ public function getBehaviorContent($contentName)
+ {
+ return $this->getBehaviorContentBase($contentName, 'QueryBuilderModifier');
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/om/QueryInheritanceBuilder.php b/3rd_party/php/propel/generator/lib/builder/om/QueryInheritanceBuilder.php
new file mode 100755
index 000000000..316a87949
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/om/QueryInheritanceBuilder.php
@@ -0,0 +1,276 @@
+getBuildProperty('basePrefix') . $this->getNewStubQueryInheritanceBuilder($this->getChild())->getUnprefixedClassname();
+ }
+
+ /**
+ * Gets the package for the [base] object classes.
+ * @return string
+ */
+ public function getPackage()
+ {
+ return parent::getPackage() . ".om";
+ }
+
+ public function getNamespace()
+ {
+ if ($namespace = parent::getNamespace()) {
+ if ($this->getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceOm')) {
+ return $namespace . '\\' . $omns;
+ } else {
+ return $namespace;
+ }
+ }
+ }
+
+ /**
+ * Set the child object that we're operating on currrently.
+ * @param $child Inheritance
+ */
+ public function setChild(Inheritance $child)
+ {
+ $this->child = $child;
+ }
+
+ /**
+ * Returns the child object we're operating on currently.
+ * @return Inheritance
+ * @throws BuildException - if child was not set.
+ */
+ public function getChild()
+ {
+ if (!$this->child) {
+ throw new BuildException("The PHP5MultiExtendObjectBuilder needs to be told which child class to build (via setChild() method) before it can build the stub class.");
+ }
+ return $this->child;
+ }
+
+ /**
+ * Adds the include() statements for files that this class depends on or utilizes.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addIncludes(&$script)
+ {
+ $requiredClassFilePath = $this->getStubQueryBuilder()->getClassFilePath();
+
+ $script .="
+require '".$requiredClassFilePath."';
+";
+ } // addIncludes()
+
+ /**
+ * Adds class phpdoc comment and openning of class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassOpen(&$script)
+ {
+ $table = $this->getTable();
+ $tableName = $table->getName();
+ $tableDesc = $table->getDescription();
+
+ $baseBuilder = $this->getStubQueryBuilder();
+ $this->declareClassFromBuilder($baseBuilder);
+ $baseClassname = $baseBuilder->getClassname();
+
+ $script .= "
+/**
+ * Skeleton subclass for representing a query for one of the subclasses of the '$tableName' table.
+ *
+ * $tableDesc
+ *";
+ if ($this->getBuildProperty('addTimeStamp')) {
+ $now = strftime('%c');
+ $script .= "
+ * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
+ *
+ * $now
+ *";
+ }
+ $script .= "
+ * You should add additional methods to this class to meet the
+ * application requirements. This class will only be generated as
+ * long as it does not already exist in the output directory.
+ *
+ * @package propel.generator.".$this->getPackage()."
+ */
+class " .$this->getClassname() . " extends " . $baseClassname . " {
+";
+ }
+
+ /**
+ * Specifies the methods that are added as part of the stub object class.
+ *
+ * By default there are no methods for the empty stub classes; override this method
+ * if you want to change that behavior.
+ *
+ * @see ObjectBuilder::addClassBody()
+ */
+ protected function addClassBody(&$script)
+ {
+ $this->declareClassFromBuilder($this->getStubPeerBuilder());
+ $this->declareClasses('PropelPDO', 'Criteria');
+ $this->addFactory($script);
+ $this->addPreSelect($script);
+ $this->addPreUpdate($script);
+ $this->addPreDelete($script);
+ $this->addDoDeleteAll($script);
+ }
+
+ /**
+ * Adds the factory for this object.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addFactory(&$script)
+ {
+ $builder = $this->getNewStubQueryInheritanceBuilder($this->getChild());
+ $this->declareClassFromBuilder($builder);
+ $classname = $builder->getClassname();
+ $script .= "
+ /**
+ * Returns a new " . $classname . " object.
+ *
+ * @param string \$modelAlias The alias of a model in the query
+ * @param Criteria \$criteria Optional Criteria to build the query from
+ *
+ * @return " . $classname . "
+ */
+ public static function create(\$modelAlias = null, \$criteria = null)
+ {
+ if (\$criteria instanceof " . $classname . ") {
+ return \$criteria;
+ }
+ \$query = new " . $classname . "();
+ if (null !== \$modelAlias) {
+ \$query->setModelAlias(\$modelAlias);
+ }
+ if (\$criteria instanceof Criteria) {
+ \$query->mergeWith(\$criteria);
+ }
+ return \$query;
+ }
+";
+ }
+
+ protected function addPreSelect(&$script)
+ {
+ $child = $this->getChild();
+ $col = $child->getColumn();
+
+ $script .= "
+ /**
+ * Filters the query to target only " . $child->getClassname() . " objects.
+ */
+ public function preSelect(PropelPDO \$con)
+ {
+ " . $this->getClassKeyCondition() . "
+ }
+";
+ }
+
+ protected function addPreUpdate(&$script)
+ {
+ $child = $this->getChild();
+ $col = $child->getColumn();
+
+ $script .= "
+ /**
+ * Filters the query to target only " . $child->getClassname() . " objects.
+ */
+ public function preUpdate(&\$values, PropelPDO \$con, \$forceIndividualSaves = false)
+ {
+ " . $this->getClassKeyCondition() . "
+ }
+";
+ }
+
+ protected function addPreDelete(&$script)
+ {
+ $child = $this->getChild();
+ $col = $child->getColumn();
+
+ $script .= "
+ /**
+ * Filters the query to target only " . $child->getClassname() . " objects.
+ */
+ public function preDelete(PropelPDO \$con)
+ {
+ " . $this->getClassKeyCondition() . "
+ }
+";
+ }
+
+ protected function getClassKeyCondition()
+ {
+ $child = $this->getChild();
+ $col = $child->getColumn();
+ return "\$this->addUsingAlias(" . $col->getConstantName() . ", " . $this->getPeerClassname()."::CLASSKEY_".strtoupper($child->getKey()).");";
+ }
+
+ protected function addDoDeleteAll(&$script)
+ {
+ $child = $this->getChild();
+
+ $script .= "
+ /**
+ * Issue a DELETE query based on the current ModelCriteria deleting all rows in the table
+ * Having the " . $child->getClassname() . " class.
+ * This method is called by ModelCriteria::deleteAll() inside a transaction
+ *
+ * @param PropelPDO \$con a connection object
+ *
+ * @return integer the number of deleted rows
+ */
+ public function doDeleteAll(\$con)
+ {
+ // condition on class key is already added in preDelete()
+ return parent::doDelete(\$con);
+ }
+";
+ }
+
+ /**
+ * Closes class.
+ * @param string &$script The script will be modified in this method.
+ */
+ protected function addClassClose(&$script)
+ {
+ $script .= "
+} // " . $this->getClassname() . "
+";
+ }
+
+} // MultiExtensionQueryBuilder
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/DDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/DDLBuilder.php
new file mode 100644
index 000000000..db7fffa79
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/DDLBuilder.php
@@ -0,0 +1,166 @@
+
+ * @package propel.generator.builder.sql
+ */
+abstract class DDLBuilder extends DataModelBuilder
+{
+
+ /**
+ * Builds the SQL for current table and returns it as a string.
+ *
+ * This is the main entry point and defines a basic structure that classes should follow.
+ * In most cases this method will not need to be overridden by subclasses.
+ *
+ * @return string The resulting SQL DDL.
+ */
+ public function build()
+ {
+ $script = "";
+ $this->addTable($script);
+ $this->addIndices($script);
+ $this->addForeignKeys($script);
+ return $script;
+ }
+
+ /**
+ * Gets the name to use for creating a sequence for the current table.
+ *
+ * This will create a new name or use one specified in an id-method-parameter
+ * tag, if specified.
+ *
+ * @return string Sequence name for this table.
+ */
+ public function getSequenceName()
+ {
+ $table = $this->getTable();
+ static $longNamesMap = array();
+ $result = null;
+ if ($table->getIdMethod() == IDMethod::NATIVE) {
+ $idMethodParams = $table->getIdMethodParameters();
+ $maxIdentifierLength = $table->getDatabase()->getPlatform()->getMaxColumnNameLength();
+ if (empty($idMethodParams)) {
+ if (strlen($table->getName() . "_SEQ") > $maxIdentifierLength) {
+ if (!isset($longNamesMap[$table->getName()])) {
+ $longNamesMap[$table->getName()] = strval(count($longNamesMap) + 1);
+ }
+ $result = substr($table->getName(), 0, $maxIdentifierLength - strlen("_SEQ_" . $longNamesMap[$table->getName()])) . "_SEQ_" . $longNamesMap[$table->getName()];
+ }
+ else {
+ $result = substr($table->getName(), 0, $maxIdentifierLength -4) . "_SEQ";
+ }
+ } else {
+ $result = substr($idMethodParams[0]->getValue(), 0, $maxIdentifierLength);
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Builds the DDL SQL for a Column object.
+ * @return string
+ */
+ public function getColumnDDL(Column $col)
+ {
+ $platform = $this->getPlatform();
+ $domain = $col->getDomain();
+
+ $sb = "";
+ $sb .= $this->quoteIdentifier($col->getName()) . " ";
+ $sb .= $domain->getSqlType();
+ if ($platform->hasSize($domain->getSqlType())) {
+ $sb .= $domain->printSize();
+ }
+ $sb .= " ";
+ $sb .= $col->getDefaultSetting() . " ";
+ $sb .= $col->getNotNullString() . " ";
+ $sb .= $col->getAutoIncrementString();
+
+ return trim($sb);
+ }
+
+ /**
+ * Creates a delimiter-delimited string list of column names, quoted using quoteIdentifier().
+ * @param array Column[] or string[]
+ * @param string $delim The delimiter to use in separating the column names.
+ * @return string
+ */
+ public function getColumnList($columns, $delim=',')
+ {
+ $list = array();
+ foreach ($columns as $col) {
+ if ($col instanceof Column) {
+ $col = $col->getName();
+ }
+ $list[] = $this->quoteIdentifier($col);
+ }
+ return implode($delim, $list);
+ }
+
+ /**
+ * This function adds any _database_ start/initialization SQL.
+ * This is designed to be called for a database, not a specific table, hence it is static.
+ * @return string The DDL is returned as astring.
+ */
+ public static function getDatabaseStartDDL()
+ {
+ return '';
+ }
+
+ /**
+ * This function adds any _database_ end/cleanup SQL.
+ * This is designed to be called for a database, not a specific table, hence it is static.
+ * @return string The DDL is returned as astring.
+ */
+ public static function getDatabaseEndDDL()
+ {
+ return '';
+ }
+
+ /**
+ * Resets any static variables between building a SQL file for a database.
+ *
+ * Theoretically, Propel could build multiple .sql files for multiple databases; in
+ * many cases we don't want static values to persist between these. This method provides
+ * a way to clear out static values between iterations, if the subclasses choose to implement
+ * it.
+ */
+ public static function reset()
+ {
+ // nothing by default
+ }
+
+ /**
+ * Adds table definition.
+ * @param string &$script The script will be modified in this method.
+ */
+ abstract protected function addTable(&$script);
+
+ /**
+ * Adds index definitions.
+ * @param string &$script The script will be modified in this method.
+ */
+ abstract protected function addIndices(&$script);
+
+ /**
+ * Adds foreign key constraint definitions.
+ * @param string &$script The script will be modified in this method.
+ */
+ abstract protected function addForeignKeys(&$script);
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/DataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/DataSQLBuilder.php
new file mode 100644
index 000000000..012498602
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/DataSQLBuilder.php
@@ -0,0 +1,253 @@
+
+ * @package propel.generator.builder.sql
+ */
+abstract class DataSQLBuilder extends DataModelBuilder
+{
+
+ /**
+ * Perform any reset between runs of this builder.
+ *
+ * This can be used, for example, to clear any stored start/end SQL.
+ */
+ public static function reset()
+ {
+ // does nothing by default
+ }
+
+ /**
+ * Gets any SQL to place at the start of all the row inserts.
+ *
+ * @return string
+ */
+ public static function getDatabaseStartSql()
+ {
+ return '';
+ }
+
+ /**
+ * Gets any SQL to place at the end of all the row inserts.
+ *
+ * @return string
+ */
+ public static function getDatabaseEndSql()
+ {
+ return '';
+ }
+
+ /**
+ * Gets any SQL to place before row inserts for a new table.
+ *
+ * @return string
+ */
+ public function getTableStartSql()
+ {
+ return '';
+ }
+
+ /**
+ * Gets any SQL to place at the end of row inserts for a table.
+ *
+ * @return string
+ */
+ public function getTableEndSql()
+ {
+ return '';
+ }
+
+ /**
+ * The main method in this class, returns the SQL for INSERTing data into a row.
+ * @param DataRow $row The row to process.
+ * @return string
+ */
+ public function buildRowSql(DataRow $row)
+ {
+ $sql = "";
+ $platform = $this->getPlatform();
+ $table = $this->getTable();
+
+ $sql .= "INSERT INTO ".$this->quoteIdentifier($this->getTable()->getName())." (";
+
+ // add column names to SQL
+ $colNames = array();
+ foreach ($row->getColumnValues() as $colValue) {
+ $colNames[] = $this->quoteIdentifier($colValue->getColumn()->getName());
+ }
+
+ $sql .= implode(',', $colNames);
+
+ $sql .= ") VALUES (";
+
+ $colVals = array();
+ foreach ($row->getColumnValues() as $colValue) {
+ $colVals[] = $this->getColumnValueSql($colValue);
+ }
+
+ $sql .= implode(',', $colVals);
+ $sql .= ");
+";
+
+ return $sql;
+ }
+
+ /**
+ * Gets the propertly escaped (and quoted) value for a column.
+ * @param ColumnValue $colValue
+ * @return mixed The proper value to be added to the string.
+ */
+ protected function getColumnValueSql(ColumnValue $colValue)
+ {
+ $column = $colValue->getColumn();
+ $method = 'get' . $column->getPhpNative() . 'Sql';
+ return $this->$method($colValue->getValue());
+ }
+
+
+
+ /**
+ * Gets a representation of a binary value suitable for use in a SQL statement.
+ * Default behavior is true = 1, false = 0.
+ * @param boolean $value
+ * @return int
+ */
+ protected function getBooleanSql($value)
+ {
+ return (int) $value;
+ }
+
+
+ /**
+ * Gets a representation of a BLOB/LONGVARBINARY value suitable for use in a SQL statement.
+ * @param mixed $blob Blob object or string data.
+ * @return string
+ */
+ protected function getBlobSql($blob)
+ {
+ // they took magic __toString() out of PHP5.0.0; this sucks
+ if (is_object($blob)) {
+ return $this->getPlatform()->quote($blob->__toString());
+ } else {
+ return $this->getPlatform()->quote($blob);
+ }
+ }
+
+ /**
+ * Gets a representation of a CLOB/LONGVARCHAR value suitable for use in a SQL statement.
+ * @param mixed $clob Clob object or string data.
+ * @return string
+ */
+ protected function getClobSql($clob)
+ {
+ // they took magic __toString() out of PHP5.0.0; this sucks
+ if (is_object($clob)) {
+ return $this->getPlatform()->quote($clob->__toString());
+ } else {
+ return $this->getPlatform()->quote($clob);
+ }
+ }
+
+ /**
+ * Gets a representation of a date value suitable for use in a SQL statement.
+ * @param string $value
+ * @return string
+ */
+ protected function getDateSql($value)
+ {
+ return "'" . date('Y-m-d', strtotime($value)) . "'";
+ }
+
+ /**
+ * Gets a representation of a decimal value suitable for use in a SQL statement.
+ * @param double $value
+ * @return float
+ */
+ protected function getDecimalSql($value)
+ {
+ return (float) $value;
+ }
+
+ /**
+ * Gets a representation of a double value suitable for use in a SQL statement.
+ * @param double $value
+ * @return double
+ */
+ protected function getDoubleSql($value)
+ {
+ return (double) $value;
+ }
+
+ /**
+ * Gets a representation of a float value suitable for use in a SQL statement.
+ * @param float $value
+ * @return float
+ */
+ protected function getFloatSql($value)
+ {
+ return (float) $value;
+ }
+
+ /**
+ * Gets a representation of an integer value suitable for use in a SQL statement.
+ * @param int $value
+ * @return int
+ */
+ protected function getIntSql($value)
+ {
+ return (int) $value;
+ }
+
+ /**
+ * Gets a representation of a NULL value suitable for use in a SQL statement.
+ * @return null
+ */
+ protected function getNullSql()
+ {
+ return 'NULL';
+ }
+
+ /**
+ * Gets a representation of a string value suitable for use in a SQL statement.
+ * @param string $value
+ * @return string
+ */
+ protected function getStringSql($value)
+ {
+ return $this->getPlatform()->quote($value);
+ }
+
+ /**
+ * Gets a representation of a time value suitable for use in a SQL statement.
+ * @param string $value
+ * @return string
+ */
+ protected function getTimeSql($paramIndex, $value)
+ {
+ return "'" . date('H:i:s', strtotime($value)) . "'";
+ }
+
+ /**
+ * Gets a representation of a timestamp value suitable for use in a SQL statement.
+ * @param string $value
+ * @return string
+ */
+ function getTimestampSql($value)
+ {
+ return "'" . date('Y-m-d H:i:s', strtotime($value)) . "'";
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDDLBuilder.php
new file mode 100644
index 000000000..2bd923e3b
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDDLBuilder.php
@@ -0,0 +1,173 @@
+
+ * @package propel.generator.builder.sql.pgsql
+ */
+class MssqlDDLBuilder extends DDLBuilder
+{
+
+ private static $dropCount = 0;
+
+ /**
+ *
+ * @see parent::addDropStatement()
+ */
+ protected function addDropStatements(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $script .= "
+IF EXISTS (SELECT 1 FROM sysobjects WHERE type ='RI' AND name='".$fk->getName()."')
+ ALTER TABLE ".$this->quoteIdentifier($table->getName())." DROP CONSTRAINT ".$this->quoteIdentifier($fk->getName()).";
+";
+ }
+
+
+ self::$dropCount++;
+
+ $script .= "
+IF EXISTS (SELECT 1 FROM sysobjects WHERE type = 'U' AND name = '".$table->getName()."')
+BEGIN
+ DECLARE @reftable_".self::$dropCount." nvarchar(60), @constraintname_".self::$dropCount." nvarchar(60)
+ DECLARE refcursor CURSOR FOR
+ select reftables.name tablename, cons.name constraintname
+ from sysobjects tables,
+ sysobjects reftables,
+ sysobjects cons,
+ sysreferences ref
+ where tables.id = ref.rkeyid
+ and cons.id = ref.constid
+ and reftables.id = ref.fkeyid
+ and tables.name = '".$table->getName()."'
+ OPEN refcursor
+ FETCH NEXT from refcursor into @reftable_".self::$dropCount.", @constraintname_".self::$dropCount."
+ while @@FETCH_STATUS = 0
+ BEGIN
+ exec ('alter table '+@reftable_".self::$dropCount."+' drop constraint '+@constraintname_".self::$dropCount.")
+ FETCH NEXT from refcursor into @reftable_".self::$dropCount.", @constraintname_".self::$dropCount."
+ END
+ CLOSE refcursor
+ DEALLOCATE refcursor
+ DROP TABLE ".$this->quoteIdentifier($table->getName())."
+END
+";
+ }
+
+ /**
+ * @see parent::addColumns()
+ */
+ protected function addTable(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+/* ---------------------------------------------------------------------- */
+/* ".$table->getName()." */
+/* ---------------------------------------------------------------------- */
+
+";
+
+ $this->addDropStatements($script);
+
+ $script .= "
+
+CREATE TABLE ".$this->quoteIdentifier($table->getName())."
+(
+ ";
+
+ $lines = array();
+
+ foreach ($table->getColumns() as $col) {
+ $lines[] = $this->getColumnDDL($col);
+ }
+
+ if ($table->hasPrimaryKey()) {
+ $lines[] = "CONSTRAINT ".$this->quoteIdentifier($table->getName()."_PK") . " PRIMARY KEY (".$this->getColumnList($table->getPrimaryKey()).")";
+ }
+
+ foreach ($table->getUnices() as $unique ) {
+ $lines[] = "UNIQUE (".$this->getColumnList($unique->getColumns()).")";
+ }
+
+ $sep = ",
+ ";
+ $script .= implode($sep, $lines);
+ $script .= "
+);
+";
+ }
+
+ /**
+ * Adds CREATE INDEX statements for this table.
+ * @see parent::addIndices()
+ */
+ protected function addIndices(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getIndices() as $index) {
+ $script .= "
+CREATE ";
+ if ($index->getIsUnique()) {
+ $script .= "UNIQUE";
+ }
+ $script .= "INDEX ".$this->quoteIdentifier($index->getName())." ON ".$this->quoteIdentifier($table->getName())." (".$this->getColumnList($index->getColumns()).");
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addForeignKeys()
+ */
+ protected function addForeignKeys(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $script .= "
+BEGIN
+ALTER TABLE ".$this->quoteIdentifier($table->getName())." ADD CONSTRAINT ".$this->quoteIdentifier($fk->getName())." FOREIGN KEY (".$this->getColumnList($fk->getLocalColumns()) .") REFERENCES ".$this->quoteIdentifier($fk->getForeignTableName())." (".$this->getColumnList($fk->getForeignColumns()).")";
+ if ($fk->hasOnUpdate()) {
+ if ($fk->getOnUpdate() == ForeignKey::SETNULL) { // there may be others that also won't work
+ // we have to skip this because it's unsupported.
+ $this->warn("MSSQL doesn't support the 'SET NULL' option for ON UPDATE (ignoring for ".$this->getColumnList($fk->getLocalColumns())." fk).");
+ } else {
+ $script .= " ON UPDATE ".$fk->getOnUpdate();
+ }
+
+ }
+ if ($fk->hasOnDelete()) {
+ if ($fk->getOnDelete() == ForeignKey::SETNULL) { // there may be others that also won't work
+ // we have to skip this because it's unsupported.
+ $this->warn("MSSQL doesn't support the 'SET NULL' option for ON DELETE (ignoring for ".$this->getColumnList($fk->getLocalColumns())." fk).");
+ } else {
+ $script .= " ON DELETE ".$fk->getOnDelete();
+ }
+ }
+ $script .= "
+END
+;
+";
+ }
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDataSQLBuilder.php
new file mode 100644
index 000000000..a72bad2e0
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/mssql/MssqlDataSQLBuilder.php
@@ -0,0 +1,37 @@
+
+ * @package propel.generator.builder.sql.mssql
+ */
+class MssqlDataSQLBuilder extends DataSQLBuilder
+{
+
+ /**
+ *
+ * @param mixed $blob Blob object or string containing data.
+ * @return string
+ */
+ protected function getBlobSql($blob)
+ {
+ // they took magic __toString() out of PHP5.0.0; this sucks
+ if (is_object($blob)) {
+ $blob = $blob->__toString();
+ }
+ $data = unpack("H*hex", $blob);
+ return '0x'.$data['hex']; // no surrounding quotes!
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDDLBuilder.php
new file mode 100644
index 000000000..67fb15b19
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDDLBuilder.php
@@ -0,0 +1,413 @@
+
+ * @package propel.generator.builder.sql.mysql
+ */
+class MysqlDDLBuilder extends DDLBuilder
+{
+
+ /**
+ * Returns some header SQL that disables foreign key checking.
+ * @return string DDL
+ */
+ public static function getDatabaseStartDDL()
+ {
+ $ddl = "
+# This is a fix for InnoDB in MySQL >= 4.1.x
+# It \"suspends judgement\" for fkey relationships until are tables are set.
+SET FOREIGN_KEY_CHECKS = 0;
+";
+ return $ddl;
+ }
+
+ /**
+ * Returns some footer SQL that re-enables foreign key checking.
+ * @return string DDL
+ */
+ public static function getDatabaseEndDDL()
+ {
+ $ddl = "
+# This restores the fkey checks, after having unset them earlier
+SET FOREIGN_KEY_CHECKS = 1;
+";
+ return $ddl;
+ }
+
+
+ /**
+ *
+ * @see parent::addDropStatement()
+ */
+ protected function addDropStatements(&$script)
+ {
+ $script .= "
+DROP TABLE IF EXISTS ".$this->quoteIdentifier($this->getTable()->getName()).";
+";
+ }
+
+ /**
+ * Builds the SQL for current table and returns it as a string.
+ *
+ * This is the main entry point and defines a basic structure that classes should follow.
+ * In most cases this method will not need to be overridden by subclasses.
+ *
+ * @return string The resulting SQL DDL.
+ */
+ public function build()
+ {
+ $script = "";
+ $this->addTable($script);
+ return $script;
+ }
+
+ /**
+ *
+ * @see parent::addColumns()
+ */
+ protected function addTable(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+#-----------------------------------------------------------------------------
+#-- ".$table->getName()."
+#-----------------------------------------------------------------------------
+";
+
+ $this->addDropStatements($script);
+
+ $script .= "
+
+CREATE TABLE ".$this->quoteIdentifier($table->getName())."
+(
+ ";
+
+ $lines = array();
+
+ $databaseType = $this->getPlatform()->getDatabaseType();
+
+ foreach ($table->getColumns() as $col) {
+ $entry = $this->getColumnDDL($col);
+ $colinfo = $col->getVendorInfoForType($databaseType);
+ if ( $colinfo->hasParameter('Charset') ) {
+ $entry .= ' CHARACTER SET '.$platform->quote($colinfo->getParameter('Charset'));
+ }
+ if ( $colinfo->hasParameter('Collation') ) {
+ $entry .= ' COLLATE '.$platform->quote($colinfo->getParameter('Collation'));
+ } elseif ( $colinfo->hasParameter('Collate') ) {
+ $entry .= ' COLLATE '.$platform->quote($colinfo->getParameter('Collate'));
+ }
+ if ($col->getDescription()) {
+ $entry .= " COMMENT ".$platform->quote($col->getDescription());
+ }
+ $lines[] = $entry;
+ }
+
+ if ($table->hasPrimaryKey()) {
+ $lines[] = "PRIMARY KEY (".$this->getColumnList($table->getPrimaryKey()).")";
+ }
+
+ $this->addIndicesLines($lines);
+ $this->addForeignKeysLines($lines);
+
+ $sep = ",
+ ";
+ $script .= implode($sep, $lines);
+
+ $script .= "
+)";
+
+ $vendorSpecific = $table->getVendorInfoForType($this->getPlatform()->getDatabaseType());
+ if ($vendorSpecific->hasParameter('Type')) {
+ $mysqlTableType = $vendorSpecific->getParameter('Type');
+ } elseif ($vendorSpecific->hasParameter('Engine')) {
+ $mysqlTableType = $vendorSpecific->getParameter('Engine');
+ } else {
+ $mysqlTableType = $this->getBuildProperty("mysqlTableType");
+ }
+
+ $script .= sprintf(' %s=%s', $this->getBuildProperty("mysqlTableEngineKeyword"), $mysqlTableType);
+
+ $dbVendorSpecific = $table->getDatabase()->getVendorInfoForType($databaseType);
+ $tableVendorSpecific = $table->getVendorInfoForType($databaseType);
+ $vendorSpecific = $dbVendorSpecific->getMergedVendorInfo($tableVendorSpecific);
+
+ if ( $vendorSpecific->hasParameter('Charset') ) {
+ $script .= ' CHARACTER SET '.$platform->quote($vendorSpecific->getParameter('Charset'));
+ }
+ if ( $vendorSpecific->hasParameter('Collate') ) {
+ $script .= ' COLLATE '.$platform->quote($vendorSpecific->getParameter('Collate'));
+ }
+ if ( $vendorSpecific->hasParameter('Checksum') ) {
+ $script .= ' CHECKSUM='.$platform->quote($vendorSpecific->getParameter('Checksum'));
+ }
+ if ( $vendorSpecific->hasParameter('Pack_Keys') ) {
+ $script .= ' PACK_KEYS='.$platform->quote($vendorSpecific->getParameter('Pack_Keys'));
+ }
+ if ( $vendorSpecific->hasParameter('Delay_key_write') ) {
+ $script .= ' DELAY_KEY_WRITE='.$platform->quote($vendorSpecific->getParameter('Delay_key_write'));
+ }
+
+ if ($table->getDescription()) {
+ $script .= " COMMENT=".$platform->quote($table->getDescription());
+ }
+ $script .= ";
+";
+ }
+
+ /**
+ * Creates a comma-separated list of column names for the index.
+ * For MySQL unique indexes there is the option of specifying size, so we cannot simply use
+ * the getColumnsList() method.
+ * @param Index $index
+ * @return string
+ */
+ private function getIndexColumnList(Index $index)
+ {
+ $platform = $this->getPlatform();
+
+ $cols = $index->getColumns();
+ $list = array();
+ foreach ($cols as $col) {
+ $list[] = $this->quoteIdentifier($col) . ($index->hasColumnSize($col) ? '(' . $index->getColumnSize($col) . ')' : '');
+ }
+ return implode(', ', $list);
+ }
+
+ /**
+ * Adds indexes
+ */
+ protected function addIndicesLines(&$lines)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getUnices() as $unique) {
+ $lines[] = "UNIQUE KEY ".$this->quoteIdentifier($unique->getName())." (".$this->getIndexColumnList($unique).")";
+ }
+
+ foreach ($table->getIndices() as $index ) {
+ $vendorInfo = $index->getVendorInfoForType($platform->getDatabaseType());
+ $lines[] .= (($vendorInfo && $vendorInfo->getParameter('Index_type') == 'FULLTEXT') ? 'FULLTEXT ' : '') . "KEY " . $this->quoteIdentifier($index->getName()) . "(" . $this->getIndexColumnList($index) . ")";
+ }
+
+ }
+
+ /**
+ * Adds foreign key declarations & necessary indexes for mysql (if they don't exist already).
+ * @see parent::addForeignKeys()
+ */
+ protected function addForeignKeysLines(&$lines)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+
+ /**
+ * A collection of indexed columns. The keys is the column name
+ * (concatenated with a comma in the case of multi-col index), the value is
+ * an array with the names of the indexes that index these columns. We use
+ * it to determine which additional indexes must be created for foreign
+ * keys. It could also be used to detect duplicate indexes, but this is not
+ * implemented yet.
+ * @var array
+ */
+ $_indices = array();
+
+ $this->collectIndexedColumns('PRIMARY', $table->getPrimaryKey(), $_indices, 'getName');
+
+ $_tableIndices = array_merge($table->getIndices(), $table->getUnices());
+ foreach ($_tableIndices as $_index) {
+ $this->collectIndexedColumns($_index->getName(), $_index->getColumns(), $_indices);
+ }
+
+ // we're determining which tables have foreign keys that point to this table, since MySQL needs an index on
+ // any column that is referenced by another table (yep, MySQL _is_ a PITA)
+ $counter = 0;
+ $allTables = $table->getDatabase()->getTables();
+ foreach ($allTables as $_table) {
+ foreach ($_table->getForeignKeys() as $_foreignKey) {
+ if ($_foreignKey->getForeignTableName() == $table->getName()) {
+ $referencedColumns = $_foreignKey->getForeignColumns();
+ $referencedColumnsHash = $this->getColumnList($referencedColumns);
+ if (!array_key_exists($referencedColumnsHash, $_indices)) {
+ // no matching index defined in the schema, so we have to create one
+ $indexName = "I_referenced_".$_foreignKey->getName()."_".(++$counter);
+ $lines[] = "INDEX ".$this->quoteIdentifier($indexName)." (" .$referencedColumnsHash.")";
+ // Add this new index to our collection, otherwise we might add it again (bug #725)
+ $this->collectIndexedColumns($indexName, $referencedColumns, $_indices);
+ }
+ }
+ }
+ }
+
+ foreach ($table->getForeignKeys() as $fk) {
+
+ $indexName = $this->quoteIdentifier(substr_replace($fk->getName(), 'FI_', strrpos($fk->getName(), 'FK_'), 3));
+
+ $localColumns = $fk->getLocalColumns();
+ $localColumnsHash = $this->getColumnList($localColumns);
+
+ if (!array_key_exists($localColumnsHash, $_indices)) {
+ // no matching index defined in the schema, so we have to create one. MySQL needs indices on any columns that serve as foreign keys. these are not auto-created prior to 4.1.2
+ $lines[] = "INDEX $indexName (".$localColumnsHash.")";
+ $this->collectIndexedColumns($indexName, $localColumns, $_indices);
+ }
+ $str = "CONSTRAINT ".$this->quoteIdentifier($fk->getName())."
+ FOREIGN KEY (".$this->getColumnList($fk->getLocalColumns()).")
+ REFERENCES ".$this->quoteIdentifier($fk->getForeignTableName()) . " (".$this->getColumnList($fk->getForeignColumns()).")";
+ if ($fk->hasOnUpdate()) {
+ $str .= "
+ ON UPDATE ".$fk->getOnUpdate();
+ }
+ if ($fk->hasOnDelete()) {
+ $str .= "
+ ON DELETE ".$fk->getOnDelete();
+ }
+ $lines[] = $str;
+ }
+ }
+
+ /**
+ * Helper function to collect indexed columns.
+ * @param array $columns The column names, or objects with a $callback method
+ * @param array $indexedColumns The collected indexes
+ * @param string $callback The name of a method to call on each of $columns to get the column name, if needed.
+ * @return unknown_type
+ */
+ private function collectIndexedColumns($indexName, $columns, &$collectedIndexes, $callback = null)
+ {
+ // Get the actual column names, using the callback if needed.
+ // DDLBuilder::getColumnList tests $col instanceof Column, and no callback - maybe we should too?
+ $colnames = $columns;
+ if ($callback) {
+ $colnames = array();
+ foreach ($columns as $col) {
+ $colnames[] = $col->$callback();
+ }
+ }
+
+ /**
+ * "If the table has a multiple-column index, any leftmost prefix of the
+ * index can be used by the optimizer to find rows. For example, if you
+ * have a three-column index on (col1, col2, col3), you have indexed search
+ * capabilities on (col1), (col1, col2), and (col1, col2, col3)."
+ * @link http://dev.mysql.com/doc/refman/5.5/en/mysql-indexes.html
+ */
+ $indexedColumns = array();
+ foreach ($colnames as $colname) {
+ $indexedColumns[] = $this->quoteIdentifier($colname);
+ $indexedColumnsHash = implode(',', $indexedColumns);
+ if (!array_key_exists($indexedColumnsHash, $collectedIndexes)) {
+ $collectedIndexes[$indexedColumnsHash] = array();
+ }
+ $collectedIndexes[$indexedColumnsHash][] = $indexName;
+ }
+ }
+
+ /**
+ * Checks whether passed-in array of Column objects contains a column with specified name.
+ * @param array Column[] or string[]
+ * @param string $searchcol Column name to search for
+ */
+ private function containsColname($columns, $searchcol)
+ {
+ foreach ($columns as $col) {
+ if ($col instanceof Column) {
+ $col = $col->getName();
+ }
+ if ($col == $searchcol) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Not used for MySQL since foreign keys are declared inside table declaration.
+ * @see addForeignKeysLines()
+ */
+ protected function addForeignKeys(&$script)
+ {
+ }
+
+ /**
+ * Not used for MySQL since indexes are declared inside table declaration.
+ * @see addIndicesLines()
+ */
+ protected function addIndices(&$script)
+ {
+ }
+
+ /**
+ * Builds the DDL SQL for a Column object.
+ * @return string
+ */
+ public function getColumnDDL(Column $col)
+ {
+ $platform = $this->getPlatform();
+ $domain = $col->getDomain();
+ $sqlType = $domain->getSqlType();
+ $notNullString = $col->getNotNullString();
+ $defaultSetting = $col->getDefaultSetting();
+
+ // Special handling of TIMESTAMP/DATETIME types ...
+ // See: http://propel.phpdb.org/trac/ticket/538
+ if ($sqlType == 'DATETIME') {
+ $def = $domain->getDefaultValue();
+ if ($def && $def->isExpression()) { // DATETIME values can only have constant expressions
+ $sqlType = 'TIMESTAMP';
+ }
+ } elseif ($sqlType == 'DATE') {
+ $def = $domain->getDefaultValue();
+ if ($def && $def->isExpression()) {
+ throw new EngineException("DATE columns cannot have default *expressions* in MySQL.");
+ }
+ } elseif ($sqlType == 'TEXT' || $sqlType == 'BLOB') {
+ if ($domain->getDefaultValue()) {
+ throw new EngineException("BLOB and TEXT columns cannot have DEFAULT values. in MySQL.");
+ }
+ }
+
+ $sb = "";
+ $sb .= $this->quoteIdentifier($col->getName()) . " ";
+ $sb .= $sqlType;
+ if ($platform->hasSize($sqlType)) {
+ $sb .= $domain->printSize();
+ }
+ $sb .= " ";
+
+ if ($sqlType == 'TIMESTAMP') {
+ $notNullString = $col->getNotNullString();
+ $defaultSetting = $col->getDefaultSetting();
+ if ($notNullString == '') {
+ $notNullString = 'NULL';
+ }
+ if ($defaultSetting == '' && $notNullString == 'NOT NULL') {
+ $defaultSetting = 'DEFAULT CURRENT_TIMESTAMP';
+ }
+ $sb .= $notNullString . " " . $defaultSetting . " ";
+ } else {
+ $sb .= $defaultSetting . " ";
+ $sb .= $notNullString . " ";
+ }
+ $sb .= $col->getAutoIncrementString();
+
+ return trim($sb);
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDataSQLBuilder.php
new file mode 100644
index 000000000..5938b6e64
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/mysql/MysqlDataSQLBuilder.php
@@ -0,0 +1,22 @@
+
+ * @package propel.generator.builder.sql.mysql
+ */
+class MysqlDataSQLBuilder extends DataSQLBuilder
+{
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDDLBuilder.php
new file mode 100644
index 000000000..8503f1834
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDDLBuilder.php
@@ -0,0 +1,185 @@
+
+ * @package propel.generator.builder.sql.pgsql
+ */
+class OracleDDLBuilder extends DDLBuilder
+{
+
+ /**
+ * This function adds any _database_ start/initialization SQL.
+ * This is designed to be called for a database, not a specific table, hence it is static.
+ * @see parent::getDatabaseStartDDL()
+ *
+ * @return string The DDL is returned as astring.
+ */
+ public static function getDatabaseStartDDL()
+ {
+ return "
+ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD';
+ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS';
+";
+ }
+
+ /**
+ *
+ * @see parent::addDropStatement()
+ */
+ protected function addDropStatements(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ $script .= "
+DROP TABLE ".$this->quoteIdentifier($table->getName())." CASCADE CONSTRAINTS;
+";
+ if ($table->getIdMethod() == "native") {
+ $script .= "
+DROP SEQUENCE ".$this->quoteIdentifier($this->getSequenceName()).";
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addColumns()
+ */
+ protected function addTable(&$script)
+ {
+ $table = $this->getTable();
+ $script .= "
+
+-----------------------------------------------------------------------
+-- ".$table->getName()."
+-----------------------------------------------------------------------
+";
+
+ $this->addDropStatements($script);
+
+ $script .= "
+CREATE TABLE ".$this->quoteIdentifier($table->getName())."
+(
+ ";
+
+ $lines = array();
+
+ foreach ($table->getColumns() as $col) {
+ $lines[] = $this->getColumnDDL($col);
+ }
+
+ $sep = ",
+ ";
+ $script .= implode($sep, $lines);
+ $script .= "
+);
+";
+ $this->addPrimaryKey($script);
+ $this->addSequences($script);
+
+ }
+
+ /**
+ *
+ *
+ */
+ protected function addPrimaryKey(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ $tableName = $table->getName();
+ $length = strlen($tableName);
+ if ($length > 27) {
+ $length = 27;
+ }
+ if ( is_array($table->getPrimaryKey()) && count($table->getPrimaryKey()) ) {
+ $script .= "
+ALTER TABLE ".$this->quoteIdentifier($table->getName())."
+ ADD CONSTRAINT ".$this->quoteIdentifier(substr($tableName,0,$length)."_PK")."
+ PRIMARY KEY (";
+ $delim = "";
+ foreach ($table->getPrimaryKey() as $col) {
+ $script .= $delim . $this->quoteIdentifier($col->getName());
+ $delim = ",";
+ }
+ $script .= ");
+";
+ }
+ }
+
+ /**
+ * Adds CREATE SEQUENCE statements for this table.
+ *
+ */
+ protected function addSequences(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ if ($table->getIdMethod() == "native") {
+ $script .= "
+CREATE SEQUENCE ".$this->quoteIdentifier($this->getSequenceName())."
+ INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE NOCACHE ORDER;
+";
+ }
+ }
+
+
+ /**
+ * Adds CREATE INDEX statements for this table.
+ * @see parent::addIndices()
+ */
+ protected function addIndices(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ foreach ($table->getIndices() as $index) {
+ $script .= "
+CREATE ";
+ if ($index->getIsUnique()) {
+ $script .= "UNIQUE";
+ }
+ $script .= "INDEX ".$this->quoteIdentifier($index->getName()) ." ON ".$this->quoteIdentifier($table->getName())." (".$this->getColumnList($index->getColumns()).");
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addForeignKeys()
+ */
+ protected function addForeignKeys(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+ foreach ($table->getForeignKeys() as $fk) {
+ $script .= "
+ALTER TABLE ".$this->quoteIdentifier($table->getName())."
+ ADD CONSTRAINT ".$this->quoteIdentifier($fk->getName())."
+ FOREIGN KEY (".$this->getColumnList($fk->getLocalColumns()) .") REFERENCES ".$this->quoteIdentifier($fk->getForeignTableName())." (".$this->getColumnList($fk->getForeignColumns()).")";
+ if ($fk->hasOnUpdate()) {
+ $this->warn("ON UPDATE not yet implemented for Oracle builder.(ignoring for ".$this->getColumnList($fk->getLocalColumns())." fk).");
+ //$script .= " ON UPDATE ".$fk->getOnUpdate();
+ }
+ if ($fk->hasOnDelete()) {
+ $script .= "
+ ON DELETE ".$fk->getOnDelete();
+ }
+ $script .= ";
+";
+ }
+ }
+
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDataSQLBuilder.php
new file mode 100644
index 000000000..bf7a35dae
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/oracle/OracleDataSQLBuilder.php
@@ -0,0 +1,22 @@
+
+ * @package propel.generator.builder.sql.oracle
+ */
+class OracleDataSQLBuilder extends DataSQLBuilder
+{
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDDLBuilder.php
new file mode 100644
index 000000000..92e0185d1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDDLBuilder.php
@@ -0,0 +1,304 @@
+
+ * @package propel.generator.builder.sql.pgsql
+ */
+class PgsqlDDLBuilder extends DDLBuilder
+{
+
+ /**
+ * Array that keeps track of already
+ * added schema names
+ *
+ * @var Array of schema names
+ */
+ protected static $addedSchemas = array();
+
+ /**
+ * Queue of constraint SQL that will be added to script at the end.
+ *
+ * PostgreSQL seems (now?) to not like constraints for tables that don't exist,
+ * so the solution is to queue up the statements and execute it at the end.
+ *
+ * @var array
+ */
+ protected static $queuedConstraints = array();
+
+ /**
+ * Reset static vars between db iterations.
+ */
+ public static function reset()
+ {
+ self::$addedSchemas = array();
+ self::$queuedConstraints = array();
+ }
+
+ /**
+ * Returns all the ALTER TABLE ADD CONSTRAINT lines for inclusion at end of file.
+ * @return string DDL
+ */
+ public static function getDatabaseEndDDL()
+ {
+ $ddl = implode("", self::$queuedConstraints);
+ return $ddl;
+ }
+
+ /**
+ * Get the schema for the current table
+ *
+ * @author Markus Lervik
+ * @access protected
+ * @return schema name if table has one, else
+ * null
+ **/
+ protected function getSchema()
+ {
+ $table = $this->getTable();
+ $vi = $table->getVendorInfoForType($this->getPlatform()->getDatabaseType());
+ if ($vi->hasParameter('schema')) {
+ return $vi->getParameter('schema');
+ }
+ return null;
+ }
+
+ /**
+ * Add a schema to the generated SQL script
+ *
+ * @author Markus Lervik
+ * @access protected
+ * @return string with CREATE SCHEMA statement if
+ * applicable, else empty string
+ **/
+ protected function addSchema()
+ {
+
+ $schemaName = $this->getSchema();
+
+ if ($schemaName !== null) {
+
+ if (!in_array($schemaName, self::$addedSchemas)) {
+ $platform = $this->getPlatform();
+ self::$addedSchemas[] = $schemaName;
+ return "\nCREATE SCHEMA " . $this->quoteIdentifier($schemaName) . ";\n";
+ }
+ }
+
+ return '';
+
+ }
+
+ /**
+ *
+ * @see parent::addDropStatement()
+ */
+ protected function addDropStatements(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+DROP TABLE ".$this->quoteIdentifier($table->getName())." CASCADE;
+";
+
+ if ($table->getIdMethod() == IDMethod::NATIVE && $table->getIdMethodParameters()) {
+ $script .= "
+DROP SEQUENCE ".$this->quoteIdentifier(strtolower($this->getSequenceName())).";
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addColumns()
+ */
+ protected function addTable(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+-----------------------------------------------------------------------------
+-- ".$table->getName()."
+-----------------------------------------------------------------------------
+";
+
+ $script .= $this->addSchema();
+
+ $schemaName = $this->getSchema();
+ if ($schemaName !== null) {
+ $script .= "\nSET search_path TO " . $this->quoteIdentifier($schemaName) . ";\n";
+ }
+
+ $this->addDropStatements($script);
+ $this->addSequences($script);
+
+ $script .= "
+
+CREATE TABLE ".$this->quoteIdentifier($table->getName())."
+(
+ ";
+
+ $lines = array();
+
+ foreach ($table->getColumns() as $col) {
+ /* @var $col Column */
+ $colDDL = $this->getColumnDDL($col);
+ if ($col->isAutoIncrement() && $table->getIdMethodParameters() == null) {
+ if ($col->getType() === PropelTypes::BIGINT) {
+ $colDDL = str_replace($col->getDomain()->getSqlType(), 'bigserial', $colDDL);
+ } else {
+ $colDDL = str_replace($col->getDomain()->getSqlType(), 'serial', $colDDL);
+ }
+ }
+ $lines[] = $colDDL;
+ }
+
+ if ($table->hasPrimaryKey()) {
+ $lines[] = "PRIMARY KEY (".$this->getColumnList($table->getPrimaryKey()).")";
+ }
+
+ foreach ($table->getUnices() as $unique ) {
+ $lines[] = "CONSTRAINT ".$this->quoteIdentifier($unique->getName())." UNIQUE (".$this->getColumnList($unique->getColumns()).")";
+ }
+
+ $sep = ",
+ ";
+ $script .= implode($sep, $lines);
+ $script .= "
+);
+
+COMMENT ON TABLE ".$this->quoteIdentifier($table->getName())." IS " . $platform->quote($table->getDescription()).";
+
+";
+
+ $this->addColumnComments($script);
+
+ $script .= "\nSET search_path TO public;";
+
+ }
+
+ /**
+ * Adds comments for the columns.
+ *
+ */
+ protected function addColumnComments(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($this->getTable()->getColumns() as $col) {
+ if ( $col->getDescription() != '' ) {
+ $script .= "
+COMMENT ON COLUMN ".$this->quoteIdentifier($table->getName()).".".$this->quoteIdentifier($col->getName())." IS ".$platform->quote($col->getDescription()) .";
+";
+ }
+ }
+ }
+
+ /**
+ * Override to provide sequence names that conform to postgres' standard when
+ * no id-method-parameter specified.
+ *
+ * @see DataModelBuilder::getSequenceName()
+ * @return string
+ */
+ public function getSequenceName()
+ {
+ $table = $this->getTable();
+ static $longNamesMap = array();
+ $result = null;
+ if ($table->getIdMethod() == IDMethod::NATIVE) {
+ $idMethodParams = $table->getIdMethodParameters();
+ if (empty($idMethodParams)) {
+ $result = null;
+ // We're going to ignore a check for max length (mainly
+ // because I'm not sure how Postgres would handle this w/ SERIAL anyway)
+ foreach ($table->getColumns() as $col) {
+ if ($col->isAutoIncrement()) {
+ $result = $table->getName() . '_' . $col->getName() . '_seq';
+ break; // there's only one auto-increment column allowed
+ }
+ }
+ } else {
+ $result = $idMethodParams[0]->getValue();
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Adds CREATE SEQUENCE statements for this table.
+ *
+ */
+ protected function addSequences(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ if ($table->getIdMethod() == IDMethod::NATIVE && $table->getIdMethodParameters() != null) {
+ $script .= "
+CREATE SEQUENCE ".$this->quoteIdentifier(strtolower($this->getSequenceName())).";
+";
+ }
+ }
+
+
+ /**
+ * Adds CREATE INDEX statements for this table.
+ * @see parent::addIndices()
+ */
+ protected function addIndices(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getIndices() as $index) {
+ $script .= "
+CREATE ";
+ if ($index->getIsUnique()) {
+ $script .= "UNIQUE";
+ }
+ $script .= "INDEX ".$this->quoteIdentifier($index->getName())." ON ".$this->quoteIdentifier($table->getName())." (".$this->getColumnList($index->getColumns()).");
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addForeignKeys()
+ */
+ protected function addForeignKeys(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $privscript = "
+ALTER TABLE ".$this->quoteIdentifier($table->getName())." ADD CONSTRAINT ".$this->quoteIdentifier($fk->getName())." FOREIGN KEY (".$this->getColumnList($fk->getLocalColumns()) .") REFERENCES ".$this->quoteIdentifier($fk->getForeignTableName())." (".$this->getColumnList($fk->getForeignColumns()).")";
+ if ($fk->hasOnUpdate()) {
+ $privscript .= " ON UPDATE ".$fk->getOnUpdate();
+ }
+ if ($fk->hasOnDelete()) {
+ $privscript .= " ON DELETE ".$fk->getOnDelete();
+ }
+ $privscript .= ";
+";
+ self::$queuedConstraints[] = $privscript;
+ }
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDataSQLBuilder.php
new file mode 100644
index 000000000..c5a644bba
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/pgsql/PgsqlDataSQLBuilder.php
@@ -0,0 +1,102 @@
+
+ * @package propel.generator.builder.sql.pgsql
+ */
+class PgsqlDataSQLBuilder extends DataSQLBuilder
+{
+
+ /**
+ * The largets serial value encountered this far.
+ *
+ * @var int
+ */
+ private $maxSeqVal;
+
+ /**
+ * Construct a new PgsqlDataSQLBuilder object.
+ *
+ * @param Table $table
+ */
+ public function __construct(Table $table)
+ {
+ parent::__construct($table);
+ }
+
+ /**
+ * The main method in this class, returns the SQL for INSERTing data into a row.
+ * @param DataRow $row The row to process.
+ * @return string
+ */
+ public function buildRowSql(DataRow $row)
+ {
+ $sql = parent::buildRowSql($row);
+
+ $table = $this->getTable();
+
+ if ($table->hasAutoIncrementPrimaryKey() && $table->getIdMethod() == IDMethod::NATIVE) {
+ foreach ($row->getColumnValues() as $colValue) {
+ if ($colValue->getColumn()->isAutoIncrement()) {
+ if ($colValue->getValue() > $this->maxSeqVal) {
+ $this->maxSeqVal = $colValue->getValue();
+ }
+ }
+ }
+ }
+
+ return $sql;
+ }
+
+ public function getTableEndSql()
+ {
+ $table = $this->getTable();
+ $sql = "";
+ if ($table->hasAutoIncrementPrimaryKey() && $table->getIdMethod() == IDMethod::NATIVE) {
+ $seqname = $this->getDDLBuilder()->getSequenceName();
+ $sql .= "SELECT pg_catalog.setval('$seqname', ".((int)$this->maxSeqVal).");
+";
+ }
+ return $sql;
+ }
+
+ /**
+ * Get SQL value to insert for Postgres BOOLEAN column.
+ * @param boolean $value
+ * @return string The representation of boolean for Postgres ('t' or 'f').
+ */
+ protected function getBooleanSql($value)
+ {
+ if ($value === 'f' || $value === 'false' || $value === "0") {
+ $value = false;
+ }
+ return ($value ? "'t'" : "'f'");
+ }
+
+ /**
+ *
+ * @param mixed $blob Blob object or string containing data.
+ * @return string
+ */
+ protected function getBlobSql($blob)
+ {
+ // they took magic __toString() out of PHP5.0.0; this sucks
+ if (is_object($blob)) {
+ $blob = $blob->__toString();
+ }
+ return "'" . pg_escape_bytea($blob) . "'";
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDDLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDDLBuilder.php
new file mode 100644
index 000000000..3a2c3c041
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDDLBuilder.php
@@ -0,0 +1,119 @@
+
+ * @package propel.generator.builder.sql.pgsql
+ */
+class SqliteDDLBuilder extends DDLBuilder
+{
+
+ /**
+ *
+ * @see parent::addDropStatement()
+ */
+ protected function addDropStatements(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+DROP TABLE ".$this->quoteIdentifier($table->getName()).";
+";
+ }
+
+ /**
+ *
+ * @see parent::addColumns()
+ */
+ protected function addTable(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ $script .= "
+-----------------------------------------------------------------------------
+-- ".$table->getName()."
+-----------------------------------------------------------------------------
+";
+
+ $this->addDropStatements($script);
+
+ $script .= "
+
+CREATE TABLE ".$this->quoteIdentifier($table->getName())."
+(
+ ";
+
+ $lines = array();
+
+ foreach ($table->getColumns() as $col) {
+ $lines[] = $this->getColumnDDL($col);
+ }
+
+ if ($table->hasPrimaryKey() && count($table->getPrimaryKey()) > 1) {
+ $lines[] = "PRIMARY KEY (".$this->getColumnList($table->getPrimaryKey()).")";
+ }
+
+ foreach ($table->getUnices() as $unique ) {
+ $lines[] = "UNIQUE (".$this->getColumnList($unique->getColumns()).")";
+ }
+
+ $sep = ",
+ ";
+ $script .= implode($sep, $lines);
+ $script .= "
+);
+";
+ }
+
+ /**
+ * Adds CREATE INDEX statements for this table.
+ * @see parent::addIndices()
+ */
+ protected function addIndices(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getIndices() as $index) {
+ $script .= "
+CREATE ";
+ if ($index->getIsUnique()) {
+ $script .= "UNIQUE";
+ }
+ $script .= "INDEX ".$this->quoteIdentifier($index->getName())." ON ".$this->quoteIdentifier($table->getName())." (".$this->getColumnList($index->getColumns()).");
+";
+ }
+ }
+
+ /**
+ *
+ * @see parent::addForeignKeys()
+ */
+ protected function addForeignKeys(&$script)
+ {
+ $table = $this->getTable();
+ $platform = $this->getPlatform();
+
+ foreach ($table->getForeignKeys() as $fk) {
+ $script .= "
+-- SQLite does not support foreign keys; this is just for reference
+-- FOREIGN KEY (".$this->getColumnList($fk->getLocalColumns()).") REFERENCES ".$fk->getForeignTableName()." (".$this->getColumnList($fk->getForeignColumns()).")
+";
+ }
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDataSQLBuilder.php b/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDataSQLBuilder.php
new file mode 100644
index 000000000..7f266afb4
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/sql/sqlite/SqliteDataSQLBuilder.php
@@ -0,0 +1,36 @@
+
+ * @package propel.generator.builder.sql.sqlite
+ */
+class SqliteDataSQLBuilder extends DataSQLBuilder
+{
+
+ /**
+ * Returns string processed by sqlite_udf_encode_binary() to ensure that binary contents will be handled correctly by sqlite.
+ * @param mixed $blob Blob or string
+ * @return string encoded text
+ */
+ protected function getBlobSql($blob)
+ {
+ // they took magic __toString() out of PHP5.0.0; this sucks
+ if (is_object($blob)) {
+ $blob = $blob->__toString();
+ }
+ return "'" . sqlite_udf_encode_binary($blob) . "'";
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/util/DefaultEnglishPluralizer.php b/3rd_party/php/propel/generator/lib/builder/util/DefaultEnglishPluralizer.php
new file mode 100644
index 000000000..7d0fad699
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/util/DefaultEnglishPluralizer.php
@@ -0,0 +1,33 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.builder.util
+ */
+class DefaultEnglishPluralizer implements Pluralizer
+{
+
+ /**
+ * Generate a plural name based on the passed in root.
+ * @param string $root The root that needs to be pluralized (e.g. Author)
+ * @return string The plural form of $root (e.g. Authors).
+ */
+ public function getPluralForm($root)
+ {
+ return $root . 's';
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/util/Pluralizer.php b/3rd_party/php/propel/generator/lib/builder/util/Pluralizer.php
new file mode 100644
index 000000000..9f382718a
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/util/Pluralizer.php
@@ -0,0 +1,28 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.builder.util
+ */
+interface Pluralizer
+{
+
+ /**
+ * Generate a plural name based on the passed in root.
+ * @param string $root The root that needs to be pluralized (e.g. Author)
+ * @return string The plural form of $root.
+ */
+ public function getPluralForm($root);
+
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/util/PropelStringReader.php b/3rd_party/php/propel/generator/lib/builder/util/PropelStringReader.php
new file mode 100755
index 000000000..fb9fd0992
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/util/PropelStringReader.php
@@ -0,0 +1,91 @@
+_string = $string;
+ }
+
+ public function skip($n)
+ {
+ $this->currPos = $this->currPos + $n;
+ }
+
+ public function eof()
+ {
+ return $this->currPos == strlen($this->_string);
+ }
+
+ public function read($len = null)
+ {
+ if ($len === null) {
+ return $this->_string;
+ } else {
+ if ($this->currPos >= strlen($this->_string)) {
+ return -1;
+ }
+ $out = substr($this->_string, $this->currPos, $len);
+ $this->currPos += $len;
+ return $out;
+ }
+ }
+
+ public function mark()
+ {
+ $this->mark = $this->currPos;
+ }
+
+ public function reset()
+ {
+ $this->currPos = $this->mark;
+ }
+
+ public function close() {}
+
+ public function open() {}
+
+ public function ready() {}
+
+ public function markSupported()
+ {
+ return true;
+ }
+
+ public function getResource()
+ {
+ return '(string) "'.$this->_string . '"';
+ }
+}
\ No newline at end of file
diff --git a/3rd_party/php/propel/generator/lib/builder/util/PropelTemplate.php b/3rd_party/php/propel/generator/lib/builder/util/PropelTemplate.php
new file mode 100644
index 000000000..dd8f937a4
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/util/PropelTemplate.php
@@ -0,0 +1,92 @@
+
+ * $template->setTemplate('This is ');
+ *
+ *
+ * @param string $template the template string
+ */
+ public function setTemplate($template)
+ {
+ $this->template = $template;
+ }
+
+ /**
+ * Set a file as a template. The file can be any regular PHP file.
+ *
+ *
+ * $template->setTemplateFile(dirname(__FILE__) . '/template/foo.php');
+ *
+ *
+ * @param string $filePath The (absolute or relative to the include path) file path
+ */
+ public function setTemplateFile($filePath)
+ {
+ $this->templateFile = $filePath;
+ }
+
+ /**
+ * Render the template using the variable provided as arguments.
+ *
+ *
+ * $template = new PropelTemplate();
+ * $template->setTemplate('This is ');
+ * echo $template->render(array('name' => 'Mike'));
+ * // This is Mike
+ *
+ *
+ * @param array $vars An associative array of argumens to be rendered
+ *
+ * @return string The rendered template
+ */
+ public function render($vars = array())
+ {
+ if (null === $this->templateFile && null === $this->template) {
+ throw new InvalidArgumentException('You must set a template or a template file before rendering');
+ }
+
+ extract($vars);
+ ob_start();
+ ob_implicit_flush(0);
+
+ try
+ {
+ if (null !== $this->templateFile) {
+ require($this->templateFile);
+ } else {
+ eval('?>' . $this->template . ' (Propel)
+ * @author Leon Messerschmidt (Torque)
+ * @author Jason van Zyl (Torque)
+ * @author Martin Poeschl (Torque)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1764 $
+ * @package propel.generator.builder.util
+ */
+class XmlToAppData extends AbstractHandler
+{
+
+ /** enables debug output */
+ const DEBUG = false;
+
+ private $app;
+ private $platform;
+ private $currDB;
+ private $currTable;
+ private $currColumn;
+ private $currFK;
+ private $currIndex;
+ private $currUnique;
+ private $currValidator;
+ private $currBehavior;
+ private $currVendorObject;
+
+ private $isForReferenceOnly;
+ private $currentPackage;
+ private $currentXmlFile;
+ private $defaultPackage;
+
+ private $encoding;
+
+ /** two-dimensional array,
+ first dimension is for schemas(key is the path to the schema file),
+ second is for tags within the schema */
+ private $schemasTagsStack = array();
+
+ public $parser;
+
+ /**
+ * Creates a new instance for the specified database type.
+ *
+ * @param Platform $platform The type of database for the application.
+ * @param string $defaultPackage the default PHP package used for the om
+ * @param string $encoding The database encoding.
+ */
+ public function __construct(Platform $platform, $defaultPackage, $encoding = 'iso-8859-1')
+ {
+ $this->app = new AppData($platform);
+ $this->platform = $platform;
+ $this->defaultPackage = $defaultPackage;
+ $this->firstPass = true;
+ $this->encoding = $encoding;
+ }
+
+ /**
+ * Parses a XML input file and returns a newly created and
+ * populated AppData structure.
+ *
+ * @param string $xmlFile The input file to parse.
+ * @return AppData populated by xmlFile.
+ */
+ public function parseFile($xmlFile)
+ {
+ // we don't want infinite recursion
+ if ($this->isAlreadyParsed($xmlFile)) {
+ return;
+ }
+
+ $f = new PhingFile($xmlFile);
+
+ return $this->parseString($f->contents(), $xmlFile);
+ }
+
+ /**
+ * Parses a XML input string and returns a newly created and
+ * populated AppData structure.
+ *
+ * @param string $xmlString The input string to parse.
+ * @param string $xmlFile The input file name.
+ * @return AppData populated by xmlFile.
+ */
+ public function parseString($xmlString, $xmlFile)
+ {
+ // we don't want infinite recursion
+ if ($this->isAlreadyParsed($xmlFile)) {
+ return;
+ }
+
+ // store current schema file path
+ $this->schemasTagsStack[$xmlFile] = array();
+
+ $this->currentXmlFile = $xmlFile;
+
+ try {
+ $sr = new PropelStringReader($xmlString);
+
+ } catch (Exception $e) {
+ $f = new PhingFile($xmlFile);
+ throw new Exception("XML File not found: " . $f->getAbsolutePath());
+ }
+
+ $br = new BufferedReader($sr);
+
+ $this->parser = new ExpatParser($br);
+ $this->parser->parserSetOption(XML_OPTION_CASE_FOLDING, 0);
+ $this->parser->setHandler($this);
+
+ try {
+ $this->parser->parse();
+ } catch (Exception $e) {
+ $br->close();
+ throw $e;
+ }
+ $br->close();
+
+ array_pop($this->schemasTagsStack);
+
+ return $this->app;
+ }
+
+ /**
+ * Handles opening elements of the xml file.
+ *
+ * @param string $uri
+ * @param string $localName The local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param string $rawName The qualified name (with prefix), or the empty string if
+ * qualified names are not available.
+ * @param string $attributes The specified or defaulted attributes
+ */
+ public function startElement($name, $attributes) {
+
+ try {
+
+ $parentTag = $this->peekCurrentSchemaTag();
+
+ if ($parentTag === false) {
+
+ switch($name) {
+ case "database":
+ if ($this->isExternalSchema()) {
+ $this->currentPackage = @$attributes["package"];
+ if ($this->currentPackage === null) {
+ $this->currentPackage = $this->defaultPackage;
+ }
+ } else {
+ $this->currDB = $this->app->addDatabase($attributes);
+ }
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "database") {
+
+ switch($name) {
+
+ case "external-schema":
+ $xmlFile = @$attributes["filename"];
+
+ //"referenceOnly" attribute is valid in the main schema XML file only,
+ //and it's ingnored in the nested external-schemas
+ if (!$this->isExternalSchema()) {
+ $isForRefOnly = @$attributes["referenceOnly"];
+ $this->isForReferenceOnly = ($isForRefOnly !== null ? (strtolower($isForRefOnly) === "true") : true); // defaults to TRUE
+ }
+
+ if ($xmlFile{0} != '/') {
+ $f = new PhingFile($this->currentXmlFile);
+ $xf = new PhingFile($f->getParent(), $xmlFile);
+ $xmlFile = $xf->getPath();
+ }
+
+ $this->parseFile($xmlFile);
+ break;
+
+ case "domain":
+ $this->currDB->addDomain($attributes);
+ break;
+
+ case "table":
+ $this->currTable = $this->currDB->addTable($attributes);
+ if ($this->isExternalSchema()) {
+ $this->currTable->setForReferenceOnly($this->isForReferenceOnly);
+ $this->currTable->setPackage($this->currentPackage);
+ }
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currDB->addVendorInfo($attributes);
+ break;
+
+ case "behavior":
+ $this->currBehavior = $this->currDB->addBehavior($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "table") {
+
+ switch($name) {
+ case "column":
+ $this->currColumn = $this->currTable->addColumn($attributes);
+ break;
+
+ case "foreign-key":
+ $this->currFK = $this->currTable->addForeignKey($attributes);
+ break;
+
+ case "index":
+ $this->currIndex = $this->currTable->addIndex($attributes);
+ break;
+
+ case "unique":
+ $this->currUnique = $this->currTable->addUnique($attributes);
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currTable->addVendorInfo($attributes);
+ break;
+
+ case "validator":
+ $this->currValidator = $this->currTable->addValidator($attributes);
+ break;
+
+ case "id-method-parameter":
+ $this->currTable->addIdMethodParameter($attributes);
+ break;
+
+ case "behavior":
+ $this->currBehavior = $this->currTable->addBehavior($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "column") {
+
+ switch($name) {
+ case "inheritance":
+ $this->currColumn->addInheritance($attributes);
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currColumn->addVendorInfo($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "foreign-key") {
+
+ switch($name) {
+ case "reference":
+ $this->currFK->addReference($attributes);
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "index") {
+
+ switch($name) {
+ case "index-column":
+ $this->currIndex->addColumn($attributes);
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currIndex->addVendorInfo($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } elseif ($parentTag == "unique") {
+
+ switch($name) {
+ case "unique-column":
+ $this->currUnique->addColumn($attributes);
+ break;
+
+ case "vendor":
+ $this->currVendorObject = $this->currUnique->addVendorInfo($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+ } elseif ($parentTag == "behavior") {
+
+ switch($name) {
+ case "parameter":
+ $this->currBehavior->addParameter($attributes);
+ break;
+
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+ } elseif ($parentTag == "validator") {
+ switch($name) {
+ case "rule":
+ $this->currValidator->addRule($attributes);
+ break;
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+ } elseif ($parentTag == "vendor") {
+
+ switch($name) {
+ case "parameter":
+ $this->currVendorObject->addParameter($attributes);
+ break;
+ default:
+ $this->_throwInvalidTagException($name);
+ }
+
+ } else {
+ // it must be an invalid tag
+ $this->_throwInvalidTagException($name);
+ }
+
+ $this->pushCurrentSchemaTag($name);
+
+ } catch (BuildException $e) {
+ throw $e;
+ } catch (Exception $e) {
+ echo $e;
+ echo "\n";
+ throw $e;
+ }
+ }
+
+ function _throwInvalidTagException($tag_name)
+ {
+ throw new BuildException("Unexpected tag <" . $tag_name . ">", $this->parser->getLocation());
+ }
+
+ /**
+ * Handles closing elements of the xml file.
+ *
+ * @param uri
+ * @param localName The local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param rawName The qualified name (with prefix), or the empty string if
+ * qualified names are not available.
+ */
+ public function endElement($name)
+ {
+ if (self::DEBUG) {
+ print("endElement(" . $name . ") called\n");
+ }
+
+ $this->popCurrentSchemaTag();
+ }
+
+ protected function peekCurrentSchemaTag()
+ {
+ $keys = array_keys($this->schemasTagsStack);
+ return end($this->schemasTagsStack[end($keys)]);
+ }
+
+ protected function popCurrentSchemaTag()
+ {
+ $keys = array_keys($this->schemasTagsStack);
+ array_pop($this->schemasTagsStack[end($keys)]);
+ }
+
+ protected function pushCurrentSchemaTag($tag)
+ {
+ $keys = array_keys($this->schemasTagsStack);
+ $this->schemasTagsStack[end($keys)][] = $tag;
+ }
+
+ protected function isExternalSchema()
+ {
+ return count($this->schemasTagsStack) > 1;
+ }
+
+ protected function isAlreadyParsed($filePath)
+ {
+ return isset($this->schemasTagsStack[$filePath]);
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/builder/util/XmlToDataSQL.php b/3rd_party/php/propel/generator/lib/builder/util/XmlToDataSQL.php
new file mode 100644
index 000000000..09854080e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/builder/util/XmlToDataSQL.php
@@ -0,0 +1,265 @@
+ (Propel)
+ * @version $Revision: 1612 $
+ * @package propel.generator.builder.util
+ */
+class XmlToDataSQL extends AbstractHandler
+{
+
+ /**
+ * The GeneratorConfig associated with the build.
+ *
+ * @var GeneratorConfig
+ */
+ private $generatorConfig;
+
+ /**
+ * The database.
+ *
+ * @var Database
+ */
+ private $database;
+
+ /**
+ * The output writer for the SQL file.
+ *
+ * @var Writer
+ */
+ private $sqlWriter;
+
+ /**
+ * The database (and output SQL file) encoding.
+ *
+ * Values will be converted to this encoding in the output file.
+ *
+ * @var string
+ */
+ private $encoding;
+
+ /**
+ * The classname of the static class that will perform the building.
+ *
+ * This is needed because there are some pre/post methods that get called
+ * on the static class.
+ *
+ * @var string
+ */
+ private $builderClazz;
+
+ /**
+ * The name of the current table being processed.
+ *
+ * @var string
+ */
+ private $currTableName;
+
+ /**
+ * The DataSQLBuilder for the current table.
+ *
+ * @var DataSQLBuilder
+ */
+ private $currBuilder;
+
+ /**
+ * Expat Parser.
+ *
+ * @var ExpatParser
+ */
+ public $parser;
+
+ /**
+ * Flag for enabing debug output to aid in parser tracing.
+ */
+ const DEBUG = false;
+
+ /**
+ * Construct new XmlToDataSQL class.
+ *
+ * This class is passed the Database object so that it knows what to expect from
+ * the XML file.
+ *
+ * @param Database $database
+ * @param GeneratorConfig $config
+ * @param string $encoding Database encoding
+ */
+ public function __construct(Database $database, GeneratorConfig $config, $encoding = 'iso-8859-1')
+ {
+ $this->database = $database;
+ $this->generatorConfig = $config;
+ $this->encoding = $encoding;
+ }
+
+ /**
+ * Transform the data dump input file into SQL and writes it to the output stream.
+ *
+ * @param PhingFile $xmlFile
+ * @param Writer $out
+ */
+ public function transform(PhingFile $xmlFile, Writer $out)
+ {
+ $this->sqlWriter = $out;
+
+ // Reset some vars just in case this is being run multiple times.
+ $this->currTableName = $this->currBuilder = null;
+
+ $this->builderClazz = $this->generatorConfig->getBuilderClassname('datasql');
+
+ try {
+ $fr = new FileReader($xmlFile);
+ } catch (Exception $e) {
+ throw new BuildException("XML File not found: " . $xmlFile->getAbsolutePath());
+ }
+
+ $br = new BufferedReader($fr);
+
+ $this->parser = new ExpatParser($br);
+ $this->parser->parserSetOption(XML_OPTION_CASE_FOLDING, 0);
+ $this->parser->setHandler($this);
+
+ try {
+ $this->parser->parse();
+ } catch (Exception $e) {
+ print $e->getMessage() . "\n";
+ $br->close();
+ }
+ $br->close();
+ }
+
+ /**
+ * Handles opening elements of the xml file.
+ */
+ public function startElement($name, $attributes)
+ {
+ try {
+ if ($name == "dataset") {
+ // Clear any start/end DLL
+ call_user_func(array($this->builderClazz, 'reset'));
+ $this->sqlWriter->write(call_user_func(array($this->builderClazz, 'getDatabaseStartSql')));
+ } else {
+
+ // we're processing a row of data
+ // where tag name is phpName e.g.
+
+ $table = $this->database->getTableByPhpName($name);
+
+ $columnValues = array();
+ foreach ($attributes as $name => $value) {
+ $col = $table->getColumnByPhpName($name);
+ $columnValues[] = new ColumnValue($col, iconv('utf-8',$this->encoding, $value));
+ }
+
+ $data = new DataRow($table, $columnValues);
+
+ if ($this->currTableName !== $table->getName()) {
+ // new table encountered
+
+ if ($this->currBuilder !== null) {
+ $this->sqlWriter->write($this->currBuilder->getTableEndSql());
+ }
+
+ $this->currTableName = $table->getName();
+ $this->currBuilder = $this->generatorConfig->getConfiguredBuilder($table, 'datasql');
+
+ $this->sqlWriter->write($this->currBuilder->getTableStartSql());
+ }
+
+ // Write the SQL
+ $this->sqlWriter->write($this->currBuilder->buildRowSql($data));
+
+ }
+
+ } catch (Exception $e) {
+ // Exceptions have traditionally not bubbled up nicely from the expat parser,
+ // so we also print the stack trace here.
+ print $e;
+ throw $e;
+ }
+ }
+
+
+ /**
+ * Handles closing elements of the xml file.
+ *
+ * @param $name The local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ */
+ public function endElement($name)
+ {
+ if (self::DEBUG) {
+ print("endElement(" . $name . ") called\n");
+ }
+ if ($name == "dataset") {
+ if ($this->currBuilder !== null) {
+ $this->sqlWriter->write($this->currBuilder->getTableEndSql());
+ }
+ $this->sqlWriter->write(call_user_func(array($this->builderClazz, 'getDatabaseEndSql')));
+ }
+ }
+
+} // XmlToData
+
+/**
+ * "inner class"
+ * @package propel.generator.builder.util
+ */
+class DataRow
+{
+ private $table;
+ private $columnValues;
+
+ public function __construct(Table $table, $columnValues)
+ {
+ $this->table = $table;
+ $this->columnValues = $columnValues;
+ }
+
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ public function getColumnValues()
+ {
+ return $this->columnValues;
+ }
+}
+
+/**
+ * "inner" class
+ * @package propel.generator.builder.util
+ */
+class ColumnValue {
+
+ private $col;
+ private $val;
+
+ public function __construct(Column $col, $val)
+ {
+ $this->col = $col;
+ $this->val = $val;
+ }
+
+ public function getColumn()
+ {
+ return $this->col;
+ }
+
+ public function getValue()
+ {
+ return $this->val;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/config/GeneratorConfig.php b/3rd_party/php/propel/generator/lib/config/GeneratorConfig.php
new file mode 100644
index 000000000..119007910
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/config/GeneratorConfig.php
@@ -0,0 +1,217 @@
+
+ * @package propel.generator.config
+ */
+class GeneratorConfig {
+
+ /**
+ * The build properties.
+ *
+ * @var array
+ */
+ private $buildProperties = array();
+
+ /**
+ * Construct a new GeneratorConfig.
+ * @param mixed $props Array or Iterator
+ */
+ public function __construct($props = null)
+ {
+ if ($props) $this->setBuildProperties($props);
+ }
+
+ /**
+ * Gets the build properties.
+ * @return array
+ */
+ public function getBuildProperties()
+ {
+ return $this->buildProperties;
+ }
+
+ /**
+ * Parses the passed-in properties, renaming and saving eligible properties in this object.
+ *
+ * Renames the propel.xxx properties to just xxx and renames any xxx.yyy properties
+ * to xxxYyy as PHP doesn't like the xxx.yyy syntax.
+ *
+ * @param mixed $props Array or Iterator
+ */
+ public function setBuildProperties($props)
+ {
+ $this->buildProperties = array();
+
+ $renamedPropelProps = array();
+ foreach ($props as $key => $propValue) {
+ if (strpos($key, "propel.") === 0) {
+ $newKey = substr($key, strlen("propel."));
+ $j = strpos($newKey, '.');
+ while ($j !== false) {
+ $newKey = substr($newKey, 0, $j) . ucfirst(substr($newKey, $j + 1));
+ $j = strpos($newKey, '.');
+ }
+ $this->setBuildProperty($newKey, $propValue);
+ }
+ }
+ }
+
+ /**
+ * Gets a specific propel (renamed) property from the build.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getBuildProperty($name)
+ {
+ return isset($this->buildProperties[$name]) ? $this->buildProperties[$name] : null;
+ }
+
+ /**
+ * Sets a specific propel (renamed) property from the build.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setBuildProperty($name, $value)
+ {
+ $this->buildProperties[$name] = $value;
+ }
+
+ /**
+ * Resolves and returns the class name based on the specified property value.
+ *
+ * @param string $propname The name of the property that holds the class path (dot-path notation).
+ * @return string The class name.
+ * @throws BuildException If the classname cannot be determined or class cannot be loaded.
+ */
+ public function getClassname($propname)
+ {
+ $classpath = $this->getBuildProperty($propname);
+ if (empty($classpath)) {
+ throw new BuildException("Unable to find class path for '$propname' property.");
+ }
+
+ // This is a slight hack to workaround camel case inconsistencies for the DDL classes.
+ // Basically, we want to turn ?.?.?.sqliteDDLBuilder into ?.?.?.SqliteDDLBuilder
+ $lastdotpos = strrpos($classpath, '.');
+ if ($lastdotpos !== null) {
+ $classpath{$lastdotpos+1} = strtoupper($classpath{$lastdotpos+1});
+ } else {
+ $classpath = ucfirst($classpath);
+ }
+
+ if (empty($classpath)) {
+ throw new BuildException("Unable to find class path for '$propname' property.");
+ }
+
+ $clazz = Phing::import($classpath);
+
+ return $clazz;
+ }
+
+ /**
+ * Resolves and returns the builder class name.
+ *
+ * @param string $type
+ * @return string The class name.
+ */
+ public function getBuilderClassname($type)
+ {
+ $propname = 'builder' . ucfirst(strtolower($type)) . 'Class';
+ return $this->getClassname($propname);
+ }
+
+ /**
+ * Creates and configures a new Platform class.
+ *
+ * @param PDO $con
+ * @return Platform
+ */
+ public function getConfiguredPlatform(PDO $con = null)
+ {
+ $clazz = $this->getClassname("platformClass");
+ $platform = new $clazz();
+
+ if (!$platform instanceof Platform) {
+ throw new BuildException("Specified platform class ($clazz) does not implement Platform interface.", $this->getLocation());
+ }
+
+ $platform->setConnection($con);
+ $platform->setGeneratorConfig($this);
+ return $platform;
+ }
+
+ /**
+ * Creates and configures a new SchemaParser class for specified platform.
+ * @param PDO $con
+ * @return SchemaParser
+ */
+ public function getConfiguredSchemaParser(PDO $con = null)
+ {
+ $clazz = $this->getClassname("reverseParserClass");
+ $parser = new $clazz();
+ if (!$parser instanceof SchemaParser) {
+ throw new BuildException("Specified platform class ($clazz) does implement SchemaParser interface.", $this->getLocation());
+ }
+ $parser->setConnection($con);
+ $parser->setGeneratorConfig($this);
+ return $parser;
+ }
+
+ /**
+ * Gets a configured data model builder class for specified table and based on type.
+ *
+ * @param Table $table
+ * @param string $type The type of builder ('ddl', 'sql', etc.)
+ * @return DataModelBuilder
+ */
+ public function getConfiguredBuilder(Table $table, $type, $cache = true)
+ {
+ $classname = $this->getBuilderClassname($type);
+ $builder = new $classname($table);
+ $builder->setGeneratorConfig($this);
+ return $builder;
+ }
+
+ /**
+ * Gets a configured Pluralizer class.
+ *
+ * @return Pluralizer
+ */
+ public function getConfiguredPluralizer()
+ {
+ $classname = $this->getBuilderClassname('pluralizer');
+ $pluralizer = new $classname();
+ return $pluralizer;
+ }
+
+ /**
+ * Gets a configured behavior class
+ *
+ * @param string $name a behavior name
+ * @return string a behavior class name
+ */
+ public function getConfiguredBehavior($name)
+ {
+ $propname = 'behavior' . ucfirst(strtolower($name)) . 'Class';
+ try {
+ $ret = $this->getClassname($propname);
+ } catch (BuildException $e) {
+ // class path not configured
+ $ret = false;
+ }
+ return $ret;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/exception/EngineException.php b/3rd_party/php/propel/generator/lib/exception/EngineException.php
new file mode 100644
index 000000000..e296c6bbc
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/exception/EngineException.php
@@ -0,0 +1,22 @@
+ (Propel)
+ * @author Daniel Rall (Torque)
+ * @author Jason van Zyl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.exception
+ */
+class EngineException extends BuildException {}
diff --git a/3rd_party/php/propel/generator/lib/model/AppData.php b/3rd_party/php/propel/generator/lib/model/AppData.php
new file mode 100644
index 000000000..39bb749d0
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/AppData.php
@@ -0,0 +1,222 @@
+ (Propel)
+ * @author Leon Messerschmidt (Torque)
+ * @author John McNally (Torque)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1640 $
+ * @package propel.generator.model
+ */
+class AppData
+{
+
+ /**
+ * The list of databases for this application.
+ * @var array Database[]
+ */
+ private $dbList = array();
+
+ /**
+ * The platform class for our database(s).
+ * @var string
+ */
+ private $platform;
+
+ /**
+ * Name of the database. Only one database definition
+ * is allowed in one XML descriptor.
+ */
+ private $name;
+
+ /**
+ * Flag to ensure that initialization is performed only once.
+ * @var boolean
+ */
+ private $isInitialized = false;
+
+ /**
+ * Creates a new instance for the specified database type.
+ *
+ * @param Platform $platform The platform object to use for any databases added to this application model.
+ */
+ public function __construct(Platform $platform)
+ {
+ $this->platform = $platform;
+ }
+
+ /**
+ * Gets the platform object to use for any databases added to this application model.
+ *
+ * @return Platform
+ */
+ public function getPlatform()
+ {
+ return $this->platform;
+ }
+
+ /**
+ * Set the name of the database.
+ *
+ * @param name of the database.
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Get the name of the database.
+ *
+ * @return String name
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get the short name of the database (without the '-schema' postfix).
+ *
+ * @return String name
+ */
+ public function getShortName()
+ {
+ return str_replace("-schema", "", $this->name);
+ }
+
+ /**
+ * Return an array of all databases
+ *
+ * @return Array of Database objects
+ */
+ public function getDatabases($doFinalInit = true)
+ {
+ // this is temporary until we'll have a clean solution
+ // for packaging datamodels/requiring schemas
+ if ($doFinalInit) {
+ $this->doFinalInitialization();
+ }
+ return $this->dbList;
+ }
+
+ /**
+ * Returns whether this application has multiple databases.
+ *
+ * @return boolean True if the application has multiple databases
+ */
+ public function hasMultipleDatabases()
+ {
+ return (count($this->dbList) > 1);
+ }
+
+ /**
+ * Return the database with the specified name.
+ *
+ * @param name database name
+ * @return A Database object. If it does not exist it returns null
+ */
+ public function getDatabase($name = null, $doFinalInit = true)
+ {
+ // this is temporary until we'll have a clean solution
+ // for packaging datamodels/requiring schemas
+ if ($doFinalInit) {
+ $this->doFinalInitialization();
+ }
+
+ if ($name === null) {
+ return $this->dbList[0];
+ }
+
+ for ($i=0,$size=count($this->dbList); $i < $size; $i++) {
+ $db = $this->dbList[$i];
+ if ($db->getName() === $name) {
+ return $db;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether a database with the specified nam exists in this AppData
+ *
+ * @param name database name
+ * @return boolean
+ */
+ public function hasDatabase($name)
+ {
+ foreach ($this->dbList as $db) {
+ if ($db->getName() === $name) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a database to the list and sets the AppData property to this
+ * AppData
+ *
+ * @param db the database to add
+ */
+ public function addDatabase($db)
+ {
+ if ($db instanceof Database) {
+ $db->setAppData($this);
+ if ($db->getPlatform() === null) {
+ $db->setPlatform($this->platform);
+ }
+ $this->dbList[] = $db;
+ return $db;
+ } else {
+ // XML attributes array / hash
+ $d = new Database();
+ $d->setAppData($this);
+ if ($d->getPlatform() === null) {
+ $d->setPlatform($this->platform);
+ }
+ $d->loadFromXML($db);
+ return $this->addDatabase($d); // calls self w/ different param type
+ }
+
+ }
+
+ public function doFinalInitialization()
+ {
+ if (!$this->isInitialized) {
+ for ($i=0, $size=count($this->dbList); $i < $size; $i++) {
+ $this->dbList[$i]->doFinalInitialization();
+ }
+ $this->isInitialized = true;
+ }
+ }
+
+ /**
+ * Creats a string representation of this AppData.
+ * The representation is given in xml format.
+ *
+ * @return string Representation in xml format
+ */
+ public function toString()
+ {
+ $result = "\n";
+ for ($i=0,$size=count($this->dbList); $i < $size; $i++) {
+ $result .= $this->dbList[$i]->toString();
+ }
+ $result .= "";
+ return $result;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Behavior.php b/3rd_party/php/propel/generator/lib/model/Behavior.php
new file mode 100644
index 000000000..281158311
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Behavior.php
@@ -0,0 +1,247 @@
+name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setTable(Table $table)
+ {
+ $this->table = $table;
+ }
+
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ public function setDatabase(Database $database)
+ {
+ $this->database = $database;
+ }
+
+ public function getDatabase()
+ {
+ return $this->database;
+ }
+
+ /**
+ * Add a parameter
+ * Expects an associative array looking like array('name' => 'foo', 'value' => bar)
+ *
+ * @param array associative array with name and value keys
+ */
+ public function addParameter($attribute)
+ {
+ $attribute = array_change_key_case($attribute, CASE_LOWER);
+ $this->parameters[$attribute['name']] = $attribute['value'];
+ }
+
+ /**
+ * Overrides the behavior parameters
+ * Expects an associative array looking like array('foo' => 'bar')
+ *
+ * @param array associative array
+ */
+ public function setParameters($parameters)
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Get the associative array of parameters
+ * @return array
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ public function getParameter($name)
+ {
+ return $this->parameters[$name];
+ }
+
+ /**
+ * This method is automatically called on database behaviors when the database model is finished
+ * Propagate the behavior to the tables of the database
+ * Override this method to have a database behavior do something special
+ */
+ public function modifyDatabase()
+ {
+ foreach ($this->getDatabase()->getTables() as $table)
+ {
+ $b = clone $this;
+ $table->addBehavior($b);
+ }
+ }
+
+ /**
+ * This method is automatically called on table behaviors when the database model is finished
+ * Override it to add columns to the current table
+ */
+ public function modifyTable()
+ {
+ }
+
+ public function setTableModified($bool)
+ {
+ $this->isTableModified = $bool;
+ }
+
+ public function isTableModified()
+ {
+ return $this->isTableModified;
+ }
+
+ public function setEarly($bool = true)
+ {
+ $this->isEarly = $bool;
+ }
+
+ public function isEarly()
+ {
+ return $this->isEarly;
+ }
+
+ /**
+ * Use Propel's simple templating system to render a PHP file
+ * using variables passed as arguments.
+ *
+ * @param string $filename The template file name, relative to the behavior's dirname
+ * @param array $vars An associative array of argumens to be rendered
+ * @param string $templateDir The name of the template subdirectory
+ *
+ * @return string The rendered template
+ */
+ public function renderTemplate($filename, $vars = array(), $templateDir = '/templates/')
+ {
+ $filePath = $this->getDirname() . $templateDir . $filename;
+ if (!file_exists($filePath)) {
+ // try with '.php' at the end
+ $filePath = $filePath . '.php';
+ if (!file_exists($filePath)) {
+ throw new InvalidArgumentException(sprintf('Template "%s" not found in "%s" directory',
+ $filename,
+ $this->getDirname() . $templateDir
+ ));
+ }
+ }
+ $template = new PropelTemplate();
+ $template->setTemplateFile($filePath);
+ $vars = array_merge($vars, array('behavior' => $this));
+
+ return $template->render($vars);
+ }
+
+ /**
+ * Returns the current dirname of this behavior (also works for descendants)
+ *
+ * @return string The absolute directory name
+ */
+ protected function getDirname()
+ {
+ if (null === $this->dirname) {
+ $r = new ReflectionObject($this);
+ $this->dirname = dirname($r->getFileName());
+ }
+ return $this->dirname;
+ }
+
+ /**
+ * Retrieve a column object using a name stored in the behavior parameters
+ * Useful for table behaviors
+ *
+ * @param string $param Name of the parameter storing the column name
+ * @return ColumnMap The column of the table supporting the behavior
+ */
+ public function getColumnForParameter($param)
+ {
+ return $this->getTable()->getColumn($this->getParameter($param));
+ }
+
+ /**
+ * Sets up the Behavior object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->name = $this->getAttribute("name");
+ }
+
+ /**
+ * @see parent::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $bNode = $node->appendChild($doc->createElement('behavior'));
+ $bNode->setAttribute('name', $this->getName());
+
+ foreach ($this->parameters as $name => $value) {
+ $parameterNode = $bNode->appendChild($doc->createElement('parameter'));
+ $parameterNode->setAttribute('name', $name);
+ $parameterNode->setAttribute('value', $value);
+ }
+ }
+
+ public function getTableModifier()
+ {
+ return $this;
+ }
+
+ public function getObjectBuilderModifier()
+ {
+ return $this;
+ }
+
+ public function getQueryBuilderModifier()
+ {
+ return $this;
+ }
+
+ public function getPeerBuilderModifier()
+ {
+ return $this;
+ }
+
+ public function getTableMapBuilderModifier()
+ {
+ return $this;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Column.php b/3rd_party/php/propel/generator/lib/model/Column.php
new file mode 100644
index 000000000..053d78066
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Column.php
@@ -0,0 +1,1188 @@
+ (Propel)
+ * @author Leon Messerschmidt (Torque)
+ * @author Jason van Zyl (Torque)
+ * @author Jon S. Stevens (Torque)
+ * @author Daniel Rall (Torque)
+ * @author Byron Foster (Torque)
+ * @author Bernd Goldschmidt
+ * @version $Revision: 1778 $
+ * @package propel.generator.model
+ */
+class Column extends XMLElement
+{
+
+ const DEFAULT_TYPE = "VARCHAR";
+ const DEFAULT_VISIBILITY = 'public';
+ public static $valid_visibilities = array('public', 'protected', 'private');
+
+ private $name;
+ private $description;
+ private $phpName = null;
+ private $phpNamingMethod;
+ private $isNotNull = false;
+ private $size;
+ private $namePrefix;
+ private $accessorVisibility;
+ private $mutatorVisibility;
+
+ /**
+ * The name to use for the Peer constant that identifies this column.
+ * (Will be converted to all-uppercase in the templates.)
+ * @var string
+ */
+ private $peerName;
+
+ /**
+ * Native PHP type (scalar or class name)
+ * @var string "string", "boolean", "int", "double"
+ */
+ private $phpType;
+
+ /**
+ * @var Table
+ */
+ private $parentTable;
+
+ private $position;
+ private $isPrimaryKey = false;
+ private $isNodeKey = false;
+ private $nodeKeySep;
+ private $isNestedSetLeftKey = false;
+ private $isNestedSetRightKey = false;
+ private $isTreeScopeKey = false;
+ private $isUnique = false;
+ private $isAutoIncrement = false;
+ private $isLazyLoad = false;
+ private $defaultValue;
+ private $referrers;
+ private $isPrimaryString = false;
+
+ // only one type is supported currently, which assumes the
+ // column either contains the classnames or a key to
+ // classnames specified in the schema. Others may be
+ // supported later.
+ private $inheritanceType;
+ private $isInheritance;
+ private $isEnumeratedClasses;
+ private $inheritanceList;
+ private $needsTransactionInPostgres; //maybe this can be retrieved from vendorSpecificInfo
+
+ /** class name to do input validation on this column */
+ private $inputValidator = null;
+
+ /**
+ * @var Domain The domain object associated with this Column.
+ */
+ private $domain;
+
+ /**
+ * Creates a new column and set the name
+ *
+ * @param name column name
+ */
+ public function __construct($name = null)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Return a comma delimited string listing the specified columns.
+ *
+ * @param columns Either a list of Column objects, or
+ * a list of String objects with column names.
+ * @deprecated Use the DDLBuilder->getColumnList() method instead; this will be removed in 1.3
+ */
+ public static function makeList($columns, Platform $platform)
+ {
+ $list = array();
+ foreach ($columns as $col) {
+ if ($col instanceof Column) {
+ $col = $col->getName();
+ }
+ $list[] = $platform->quoteIdentifier($col);
+ }
+ return implode(", ", $list);
+ }
+
+ /**
+ * Sets up the Column object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ try {
+ $dom = $this->getAttribute("domain");
+ if ($dom) {
+ $this->getDomain()->copy($this->getTable()->getDatabase()->getDomain($dom));
+ } else {
+ $type = strtoupper($this->getAttribute("type"));
+ if ($type) {
+ $this->getDomain()->copy($this->getPlatform()->getDomainForType($type));
+ } else {
+ $this->getDomain()->copy($this->getPlatform()->getDomainForType(self::DEFAULT_TYPE));
+ }
+ }
+
+ $this->name = $this->getAttribute("name");
+ $this->phpName = $this->getAttribute("phpName");
+ $this->phpType = $this->getAttribute("phpType");
+
+ if ($this->getAttribute("prefix", null) !== null) {
+ $this->namePrefix = $this->getAttribute("prefix");
+ } elseif ($this->getTable()->getAttribute('columnPrefix', null) !== null) {
+ $this->namePrefix = $this->getTable()->getAttribute('columnPrefix');
+ } else {
+ $this->namePrefix = '';
+ }
+
+ // Accessor visibility
+ if ($this->getAttribute('accessorVisibility', null) !== null) {
+ $this->setAccessorVisibility($this->getAttribute('accessorVisibility'));
+ } elseif ($this->getTable()->getAttribute('defaultAccessorVisibility', null) !== null) {
+ $this->setAccessorVisibility($this->getTable()->getAttribute('defaultAccessorVisibility'));
+ } elseif ($this->getTable()->getDatabase()->getAttribute('defaultAccessorVisibility', null) !== null) {
+ $this->setAccessorVisibility($this->getTable()->getDatabase()->getAttribute('defaultAccessorVisibility'));
+ } else {
+ $this->setAccessorVisibility(self::DEFAULT_VISIBILITY);
+ }
+
+ // Mutator visibility
+ if ($this->getAttribute('mutatorVisibility', null) !== null) {
+ $this->setMutatorVisibility($this->getAttribute('mutatorVisibility'));
+ } elseif ($this->getTable()->getAttribute('defaultMutatorVisibility', null) !== null) {
+ $this->setMutatorVisibility($this->getTable()->getAttribute('defaultMutatorVisibility'));
+ } elseif ($this->getTable()->getDatabase()->getAttribute('defaultMutatorVisibility', null) !== null) {
+ $this->setMutatorVisibility($this->getTable()->getDatabase()->getAttribute('defaultMutatorVisibility'));
+ } else {
+ $this->setMutatorVisibility(self::DEFAULT_VISIBILITY);
+ }
+
+ $this->peerName = $this->getAttribute("peerName");
+
+ // retrieves the method for converting from specified name to a PHP name, defaulting to parent tables default method
+ $this->phpNamingMethod = $this->getAttribute("phpNamingMethod", $this->parentTable->getDatabase()->getDefaultPhpNamingMethod());
+
+ $this->isPrimaryString = $this->booleanValue($this->getAttribute("primaryString"));
+
+ $this->isPrimaryKey = $this->booleanValue($this->getAttribute("primaryKey"));
+
+ $this->isNodeKey = $this->booleanValue($this->getAttribute("nodeKey"));
+ $this->nodeKeySep = $this->getAttribute("nodeKeySep", ".");
+
+ $this->isNestedSetLeftKey = $this->booleanValue($this->getAttribute("nestedSetLeftKey"));
+ $this->isNestedSetRightKey = $this->booleanValue($this->getAttribute("nestedSetRightKey"));
+ $this->isTreeScopeKey = $this->booleanValue($this->getAttribute("treeScopeKey"));
+
+ $this->isNotNull = ($this->booleanValue($this->getAttribute("required"), false) || $this->isPrimaryKey); // primary keys are required
+
+ //AutoIncrement/Sequences
+ $this->isAutoIncrement = $this->booleanValue($this->getAttribute("autoIncrement"));
+ $this->isLazyLoad = $this->booleanValue($this->getAttribute("lazyLoad"));
+
+ // Add type, size information to associated Domain object
+ $this->getDomain()->replaceSqlType($this->getAttribute("sqlType"));
+ if (!$this->getAttribute("size") && $this->getDomain()->getType() == 'VARCHAR' && !$this->getAttribute("sqlType")) {
+ $size = 255;
+ } else {
+ $size = $this->getAttribute("size");
+ }
+ $this->getDomain()->replaceSize($size);
+ $this->getDomain()->replaceScale($this->getAttribute("scale"));
+
+ $defval = $this->getAttribute("defaultValue", $this->getAttribute("default"));
+ if ($defval !== null && strtolower($defval) !== 'null') {
+ $this->getDomain()->setDefaultValue(new ColumnDefaultValue($defval, ColumnDefaultValue::TYPE_VALUE));
+ } elseif ($this->getAttribute("defaultExpr") !== null) {
+ $this->getDomain()->setDefaultValue(new ColumnDefaultValue($this->getAttribute("defaultExpr"), ColumnDefaultValue::TYPE_EXPR));
+ }
+
+ $this->inheritanceType = $this->getAttribute("inheritance");
+ $this->isInheritance = ($this->inheritanceType !== null
+ && $this->inheritanceType !== "false"); // here we are only checking for 'false', so don't
+ // use boleanValue()
+
+ $this->inputValidator = $this->getAttribute("inputValidator");
+ $this->description = $this->getAttribute("description");
+ } catch (Exception $e) {
+ throw new EngineException("Error setting up column " . var_export($this->getAttribute("name"), true) . ": " . $e->getMessage());
+ }
+ }
+
+ /**
+ * Gets domain for this column, creating a new empty domain object if none is set.
+ * @return Domain
+ */
+ public function getDomain()
+ {
+ if ($this->domain === null) {
+ $this->domain = new Domain();
+ }
+ return $this->domain;
+ }
+
+ /**
+ * Returns table.column
+ */
+ public function getFullyQualifiedName()
+ {
+ return ($this->parentTable->getName() . '.' . strtoupper($this->getName()));
+ }
+
+ /**
+ * Get the name of the column
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the column
+ */
+ public function setName($newName)
+ {
+ $this->name = $newName;
+ }
+
+ /**
+ * Get the description for the Table
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set the description for the Table
+ *
+ * @param newDescription description for the Table
+ */
+ public function setDescription($newDescription)
+ {
+ $this->description = $newDescription;
+ }
+
+ /**
+ * Get name to use in PHP sources. It will set & return
+ * a self-generated phpName from it's name if it's
+ * not already set.
+ * @return string
+ */
+ public function getPhpName()
+ {
+ if ($this->phpName === null) {
+ $this->setPhpName();
+ }
+ return $this->phpName;
+ }
+
+ /**
+ * Set name to use in PHP sources.
+ *
+ * It will generate a phpName from it's name if no
+ * $phpName is passed.
+ *
+ * @param String $phpName PhpName to be set
+ */
+ public function setPhpName($phpName = null)
+ {
+ if ($phpName == null) {
+ $this->phpName = self::generatePhpName($this->name, $this->phpNamingMethod, $this->namePrefix);
+ } else {
+ $this->phpName = $phpName;
+ }
+ }
+
+ /**
+ * Get studly version of PHP name.
+ *
+ * The studly name is the PHP name with the first character lowercase.
+ *
+ * @return string
+ */
+ public function getStudlyPhpName()
+ {
+ $phpname = $this->getPhpName();
+ if (strlen($phpname) > 1) {
+ return strtolower(substr($phpname, 0, 1)) . substr($phpname, 1);
+ } else { // 0 or 1 chars (I suppose that's rare)
+ return strtolower($phpname);
+ }
+ }
+
+ /**
+ * Get the visibility of the accessors of this column / attribute
+ * @return string
+ */
+ public function getAccessorVisibility() {
+ if ($this->accessorVisibility !== null) {
+ return $this->accessorVisibility;
+ } else {
+ return self::DEFAULT_VISIBILITY;
+ }
+ }
+
+ /**
+ * Set the visibility of the accessor methods for this column / attribute
+ * @param $newVisibility string
+ */
+ public function setAccessorVisibility($newVisibility) {
+ if (in_array($newVisibility, self::$valid_visibilities)) {
+ $this->accessorVisibility = $newVisibility;
+ } else {
+ $this->accessorVisibility = self::DEFAULT_VISIBILITY;
+ }
+
+ }
+
+ /**
+ * Get the visibility of the mutator of this column / attribute
+ * @return string
+ */
+ public function getMutatorVisibility() {
+ if ($this->mutatorVisibility !== null) {
+ return $this->mutatorVisibility;
+ } else {
+ return self::DEFAULT_VISIBILITY;
+ }
+ }
+
+ /**
+ * Set the visibility of the mutator methods for this column / attribute
+ * @param $newVisibility string
+ */
+ public function setMutatorVisibility($newVisibility) {
+ if (in_array($newVisibility, self::$valid_visibilities)) {
+ $this->mutatorVisibility = $newVisibility;
+ } else {
+ $this->mutatorVisibility = self::DEFAULT_VISIBILITY;
+ }
+
+ }
+
+ /**
+ * Get the column constant name (e.g. PeerName::COLUMN_NAME).
+ *
+ * @return string A column constant name for insertion into PHP code
+ */
+ public function getConstantName()
+ {
+ $classname = $this->getTable()->getPhpName() . 'Peer';
+ $const = $this->getConstantColumnName();
+ return $classname.'::'.$const;
+ }
+
+ public function getConstantColumnName()
+ {
+ // was it overridden in schema.xml ?
+ if ($this->getPeerName()) {
+ return strtoupper($this->getPeerName());
+ } else {
+ return strtoupper($this->getName());
+ }
+ }
+
+ /**
+ * Get the Peer constant name that will identify this column.
+ * @return string
+ */
+ public function getPeerName() {
+ return $this->peerName;
+ }
+
+ /**
+ * Set the Peer constant name that will identify this column.
+ * @param $name string
+ */
+ public function setPeerName($name) {
+ $this->peerName = $name;
+ }
+
+ /**
+ * Get type to use in PHP sources.
+ *
+ * If no type has been specified, then uses results of getPhpNative().
+ *
+ * @return string The type name.
+ * @see getPhpNative()
+ */
+ public function getPhpType()
+ {
+ if ($this->phpType !== null) {
+ return $this->phpType;
+ }
+ return $this->getPhpNative();
+ }
+
+ /**
+ * Get the location of this column within the table (one-based).
+ * @return int value of position.
+ */
+ public function getPosition()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Get the location of this column within the table (one-based).
+ * @param int $v Value to assign to position.
+ */
+ public function setPosition($v)
+ {
+ $this->position = $v;
+ }
+
+ /**
+ * Set the parent Table of the column
+ */
+ public function setTable(Table $parent)
+ {
+ $this->parentTable = $parent;
+ }
+
+ /**
+ * Get the parent Table of the column
+ */
+ public function getTable()
+ {
+ return $this->parentTable;
+ }
+
+ /**
+ * Returns the Name of the table the column is in
+ */
+ public function getTableName()
+ {
+ return $this->parentTable->getName();
+ }
+
+ /**
+ * Adds a new inheritance definition to the inheritance list and set the
+ * parent column of the inheritance to the current column
+ * @param mixed $inhdata Inheritance or XML data.
+ */
+ public function addInheritance($inhdata)
+ {
+ if ($inhdata instanceof Inheritance) {
+ $inh = $inhdata;
+ $inh->setColumn($this);
+ if ($this->inheritanceList === null) {
+ $this->inheritanceList = array();
+ $this->isEnumeratedClasses = true;
+ }
+ $this->inheritanceList[] = $inh;
+ return $inh;
+ } else {
+ $inh = new Inheritance();
+ $inh->loadFromXML($inhdata);
+ return $this->addInheritance($inh);
+ }
+ }
+
+ /**
+ * Get the inheritance definitions.
+ */
+ public function getChildren()
+ {
+ return $this->inheritanceList;
+ }
+
+ /**
+ * Determine if this column is a normal property or specifies a
+ * the classes that are represented in the table containing this column.
+ */
+ public function isInheritance()
+ {
+ return $this->isInheritance;
+ }
+
+ /**
+ * Determine if possible classes have been enumerated in the xml file.
+ */
+ public function isEnumeratedClasses()
+ {
+ return $this->isEnumeratedClasses;
+ }
+
+ /**
+ * Return the isNotNull property of the column
+ */
+ public function isNotNull()
+ {
+ return $this->isNotNull;
+ }
+
+ /**
+ * Set the isNotNull property of the column
+ */
+ public function setNotNull($status)
+ {
+ $this->isNotNull = (boolean) $status;
+ }
+
+ /**
+ * Return NOT NULL String for this column
+ *
+ * @return "NOT NULL" if null values are not allowed or an empty string.
+ */
+ public function getNotNullString()
+ {
+ return $this->getTable()->getDatabase()->getPlatform()->getNullString($this->isNotNull());
+ }
+
+ /**
+ * Set whether the column is the primary string,
+ * i.e. whether its value is the default string representation of the table
+ * @param boolean $v
+ */
+ public function setPrimaryString($v)
+ {
+ $this->isPrimaryString = (boolean) $v;
+ }
+
+ /**
+ * Return true if the column is the primary string,
+ * i.e. if its value is the default string representation of the table
+ */
+ public function isPrimaryString()
+ {
+ return $this->isPrimaryString;
+ }
+
+ /**
+ * Set whether the column is a primary key or not.
+ * @param boolean $v
+ */
+ public function setPrimaryKey($v)
+ {
+ $this->isPrimaryKey = (boolean) $v;
+ }
+
+ /**
+ * Return true if the column is a primary key
+ */
+ public function isPrimaryKey()
+ {
+ return $this->isPrimaryKey;
+ }
+
+ /**
+ * Set if the column is the node key of a tree
+ */
+ public function setNodeKey($nk)
+ {
+ $this->isNodeKey = (boolean) $nk;
+ }
+
+ /**
+ * Return true if the column is a node key of a tree
+ */
+ public function isNodeKey()
+ {
+ return $this->isNodeKey;
+ }
+
+ /**
+ * Set if the column is the node key of a tree
+ */
+ public function setNodeKeySep($sep)
+ {
+ $this->nodeKeySep = (string) $sep;
+ }
+
+ /**
+ * Return true if the column is a node key of a tree
+ */
+ public function getNodeKeySep()
+ {
+ return $this->nodeKeySep;
+ }
+
+ /**
+ * Set if the column is the nested set left key of a tree
+ */
+ public function setNestedSetLeftKey($nslk)
+ {
+ $this->isNestedSetLeftKey = (boolean) $nslk;
+ }
+
+ /**
+ * Return true if the column is a nested set key of a tree
+ */
+ public function isNestedSetLeftKey()
+ {
+ return $this->isNestedSetLeftKey;
+ }
+
+ /**
+ * Set if the column is the nested set right key of a tree
+ */
+ public function setNestedSetRightKey($nsrk)
+ {
+ $this->isNestedSetRightKey = (boolean) $nsrk;
+ }
+
+ /**
+ * Return true if the column is a nested set right key of a tree
+ */
+ public function isNestedSetRightKey()
+ {
+ return $this->isNestedSetRightKey;
+ }
+
+ /**
+ * Set if the column is the scope key of a tree
+ */
+ public function setTreeScopeKey($tsk)
+ {
+ $this->isTreeScopeKey = (boolean) $tsk;
+ }
+
+ /**
+ * Return true if the column is a scope key of a tree
+ * @return boolean
+ */
+ public function isTreeScopeKey()
+ {
+ return $this->isTreeScopeKey;
+ }
+
+ /**
+ * Set true if the column is UNIQUE
+ * @param boolean $u
+ */
+ public function setUnique($u)
+ {
+ $this->isUnique = $u;
+ }
+
+ /**
+ * Get the UNIQUE property.
+ * @return boolean
+ */
+ public function isUnique()
+ {
+ return $this->isUnique;
+ }
+
+ /**
+ * Return true if the column requires a transaction in Postgres
+ * @return boolean
+ */
+ public function requiresTransactionInPostgres()
+ {
+ return $this->needsTransactionInPostgres;
+ }
+
+ /**
+ * Utility method to determine if this column is a foreign key.
+ * @return boolean
+ */
+ public function isForeignKey()
+ {
+ return (count($this->getForeignKeys()) > 0);
+ }
+
+ /**
+ * Whether this column is a part of more than one foreign key.
+ * @return boolean
+ */
+ public function hasMultipleFK()
+ {
+ return (count($this->getForeignKeys()) > 1);
+ }
+
+ /**
+ * Get the foreign key objects for this column (if it is a foreign key or part of a foreign key)
+ * @return array
+ */
+ public function getForeignKeys()
+ {
+ return $this->parentTable->getColumnForeignKeys($this->name);
+ }
+
+ /**
+ * Adds the foreign key from another table that refers to this column.
+ */
+ public function addReferrer(ForeignKey $fk)
+ {
+ if ($this->referrers === null) {
+ $this->referrers = array();
+ }
+ $this->referrers[] = $fk;
+ }
+
+ /**
+ * Get list of references to this column.
+ */
+ public function getReferrers()
+ {
+ if ($this->referrers === null) {
+ $this->referrers = array();
+ }
+ return $this->referrers;
+ }
+
+ /**
+ * Sets the domain up for specified Propel type.
+ *
+ * Calling this method will implicitly overwrite any previously set type,
+ * size, scale (or other domain attributes).
+ *
+ * @param string $propelType
+ */
+ public function setDomainForType($propelType)
+ {
+ $this->getDomain()->copy($this->getPlatform()->getDomainForType($propelType));
+ }
+
+ /**
+ * Sets the propel colunm type.
+ * @param string $propelType
+ * @see Domain::setType()
+ */
+ public function setType($propelType)
+ {
+ $this->getDomain()->setType($propelType);
+ if ($propelType == PropelTypes::VARBINARY|| $propelType == PropelTypes::LONGVARBINARY || $propelType == PropelTypes::BLOB) {
+ $this->needsTransactionInPostgres = true;
+ }
+ }
+
+ /**
+ * Returns the Propel column type as a string.
+ * @return string The constant representing Propel type: e.g. "VARCHAR".
+ * @see Domain::getType()
+ */
+ public function getType()
+ {
+ return $this->getDomain()->getType();
+ }
+
+ /**
+ * Returns the column PDO type integer for this column's Propel type.
+ * @return int The integer value representing PDO type param: e.g. PDO::PARAM_INT
+ */
+ public function getPDOType()
+ {
+ return PropelTypes::getPDOType($this->getType());
+ }
+
+ /**
+ * Returns the column type as given in the schema as an object
+ */
+ public function getPropelType()
+ {
+ return $this->getType();
+ }
+
+ /**
+ * Utility method to know whether column needs Blob/Lob handling.
+ * @return boolean
+ */
+ public function isLobType()
+ {
+ return PropelTypes::isLobType($this->getType());
+ }
+
+ /**
+ * Utility method to see if the column is text type.
+ */
+ public function isTextType()
+ {
+ return PropelTypes::isTextType($this->getType());
+ }
+
+ /**
+ * Utility method to see if the column is numeric type.
+ * @return boolean
+ */
+ public function isNumericType()
+ {
+ return PropelTypes::isNumericType($this->getType());
+ }
+
+ /**
+ * Utility method to see if the column is boolean type.
+ * @return boolean
+ */
+ public function isBooleanType()
+ {
+ return PropelTypes::isBooleanType($this->getType());
+ }
+
+ /**
+ * Utility method to know whether column is a temporal column.
+ * @return boolean
+ */
+ public function isTemporalType()
+ {
+ return PropelTypes::isTemporalType($this->getType());
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $colNode = $node->appendChild($doc->createElement('column'));
+ $colNode->setAttribute('name', $this->name);
+
+ if ($this->phpName !== null) {
+ $colNode->setAttribute('phpName', $this->getPhpName());
+ }
+
+ $colNode->setAttribute('type', $this->getType());
+
+ $domain = $this->getDomain();
+
+ if ($domain->getSize() !== null) {
+ $colNode->setAttribute('size', $domain->getSize());
+ }
+
+ if ($domain->getScale() !== null) {
+ $colNode->setAttribute('scale', $domain->getScale());
+ }
+
+ if ($this->isPrimaryKey) {
+ $colNode->setAttribute('primaryKey', var_export($this->isPrimaryKey, true));
+ }
+
+ if ($this->isAutoIncrement) {
+ $colNode->setAttribute('autoIncrement', var_export($this->isAutoIncrement, true));
+ }
+
+ if ($this->isNotNull) {
+ $colNode->setAttribute('required', 'true');
+ } else {
+ $colNode->setAttribute('required', 'false');
+ }
+
+ if ($domain->getDefaultValue() !== null) {
+ $def = $domain->getDefaultValue();
+ if ($def->isExpression()) {
+ $colNode->setAttribute('defaultExpr', $def->getValue());
+ } else {
+ $colNode->setAttribute('defaultValue', $def->getValue());
+ }
+ }
+
+ if ($this->isInheritance()) {
+ $colNode->setAttribute('inheritance', $this->inheritanceType);
+ foreach ($this->inheritanceList as $inheritance) {
+ $inheritance->appendXml($colNode);
+ }
+ }
+
+ if ($this->isNodeKey()) {
+ $colNode->setAttribute('nodeKey', 'true');
+ if ($this->getNodeKeySep() !== null) {
+ $colNode->setAttribute('nodeKeySep', $this->nodeKeySep);
+ }
+ }
+
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($colNode);
+ }
+ }
+
+ /**
+ * Returns the size of the column
+ * @return string
+ */
+ public function getSize()
+ {
+ return $this->domain->getSize();
+ }
+
+ /**
+ * Set the size of the column
+ * @param string $newSize
+ */
+ public function setSize($newSize)
+ {
+ $this->domain->setSize($newSize);
+ }
+
+ /**
+ * Returns the scale of the column
+ * @return string
+ */
+ public function getScale()
+ {
+ return $this->domain->getScale();
+ }
+
+ /**
+ * Set the scale of the column
+ * @param string $newScale
+ */
+ public function setScale($newScale)
+ {
+ $this->domain->setScale($newScale);
+ }
+
+ /**
+ * Return the size in brackets for use in an sql
+ * schema if the type is String. Otherwise return an empty string
+ */
+ public function printSize()
+ {
+ return $this->domain->printSize();
+ }
+
+ /**
+ * Return a string that will give this column a default value.
+ * @return string
+ */
+ public function getDefaultSetting()
+ {
+ $dflt = "";
+ $defaultValue = $this->getDefaultValue();
+ if ($defaultValue !== null) {
+ $dflt .= "default ";
+
+ if ($this->getDefaultValue()->isExpression()) {
+ $dflt .= $this->getDefaultValue()->getValue();
+ } else {
+ if ($this->isTextType()) {
+ $dflt .= $this->getPlatform()->quote($defaultValue->getValue());
+ } elseif ($this->getType() == PropelTypes::BOOLEAN) {
+ $dflt .= $this->getPlatform()->getBooleanString($defaultValue->getValue());
+ } else {
+ $dflt .= $defaultValue->getValue();
+ }
+ }
+ }
+ return $dflt;
+ }
+
+ /**
+ * Return a string that will give this column a default value in PHP
+ * @return string
+ */
+ public function getDefaultValueString()
+ {
+ $defaultValue = $this->getDefaultValue();
+ if ($defaultValue !== null) {
+ if ($this->isNumericType()) {
+ $dflt = (float) $defaultValue->getValue();
+ } elseif ($this->isTextType() || $this->getDefaultValue()->isExpression()) {
+ $dflt = "'" . str_replace("'", "\'", $defaultValue->getValue()) . "'";
+ } elseif ($this->getType() == PropelTypes::BOOLEAN) {
+ $dflt = $this->booleanValue($defaultValue->getValue()) ? 'true' : 'false';
+ } else {
+ $dflt = "'" . $defaultValue->getValue() . "'";
+ }
+ } else {
+ $dflt = "null";
+ }
+ return $dflt;
+ }
+
+ /**
+ * Set a string that will give this column a default value.
+ */
+ public function setDefaultValue($def)
+ {
+ $this->domain->setDefaultValue($def);
+ }
+
+ /**
+ * Get the default value object for this column.
+ * @return ColumnDefaultValue
+ * @see Domain::getDefaultValue()
+ */
+ public function getDefaultValue()
+ {
+ return $this->domain->getDefaultValue();
+ }
+
+ /**
+ * Get the default value suitable for use in PHP.
+ * @return mixed
+ * @see Domain::getPhpDefaultValue()
+ */
+ public function getPhpDefaultValue()
+ {
+ return $this->domain->getPhpDefaultValue();
+ }
+
+ /**
+ * Returns the class name to do input validation
+ */
+ public function getInputValidator()
+ {
+ return $this->inputValidator;
+ }
+
+ /**
+ * Return auto increment/sequence string for the target database. We need to
+ * pass in the props for the target database!
+ */
+ public function isAutoIncrement()
+ {
+ return $this->isAutoIncrement;
+ }
+
+ /**
+ * Return auto increment/sequence string for the target database. We need to
+ * pass in the props for the target database!
+ */
+ public function isLazyLoad()
+ {
+ return $this->isLazyLoad;
+ }
+
+ /**
+ * Gets the auto-increment string.
+ * @return string
+ */
+ public function getAutoIncrementString()
+ {
+ if ($this->isAutoIncrement()&& IDMethod::NATIVE === $this->getTable()->getIdMethod()) {
+ return $this->getPlatform()->getAutoIncrement();
+ } elseif ($this->isAutoIncrement()) {
+ throw new EngineException("You have specified autoIncrement for column '" . $this->name . "' but you have not specified idMethod=\"native\" for table '" . $this->getTable()->getName() . "'.");
+ }
+ return "";
+ }
+
+ /**
+ * Set the auto increment value.
+ * Use isAutoIncrement() to find out if it is set or not.
+ */
+ public function setAutoIncrement($value)
+ {
+ $this->isAutoIncrement = (boolean) $value;
+ }
+
+ /**
+ * Set the column type from a string property
+ * (normally a string from an sql input file)
+ *
+ * @deprecated Do not use; this will be removed in next release.
+ */
+ public function setTypeFromString($typeName, $size)
+ {
+ $tn = strtoupper($typeName);
+ $this->setType($tn);
+
+ if ($size !== null) {
+ $this->size = $size;
+ }
+
+ if (strpos($tn, "CHAR") !== false) {
+ $this->domain->setType(PropelTypes::VARCHAR);
+ } elseif (strpos($tn, "INT") !== false) {
+ $this->domain->setType(PropelTypes::INTEGER);
+ } elseif (strpos($tn, "FLOAT") !== false) {
+ $this->domain->setType(PropelTypes::FLOAT);
+ } elseif (strpos($tn, "DATE") !== false) {
+ $this->domain->setType(PropelTypes::DATE);
+ } elseif (strpos($tn, "TIME") !== false) {
+ $this->domain->setType(PropelTypes::TIMESTAMP);
+ } else if (strpos($tn, "BINARY") !== false) {
+ $this->domain->setType(PropelTypes::LONGVARBINARY);
+ } else {
+ $this->domain->setType(PropelTypes::VARCHAR);
+ }
+ }
+
+ /**
+ * Return a string representation of the native PHP type which corresponds
+ * to the propel type of this column. Use in the generation of Base objects.
+ *
+ * @return string PHP datatype used by propel.
+ */
+ public function getPhpNative()
+ {
+ return PropelTypes::getPhpNative($this->getType());
+ }
+
+ /**
+ * Returns true if the column's PHP native type is an boolean, int, long, float, double, string.
+ * @return boolean
+ * @see PropelTypes::isPhpPrimitiveType()
+ */
+ public function isPhpPrimitiveType()
+ {
+ return PropelTypes::isPhpPrimitiveType($this->getPhpType());
+ }
+
+ /**
+ * Return true if column's PHP native type is an boolean, int, long, float, double.
+ * @return boolean
+ * @see PropelTypes::isPhpPrimitiveNumericType()
+ */
+ public function isPhpPrimitiveNumericType()
+ {
+ return PropelTypes::isPhpPrimitiveNumericType($this->getPhpType());
+ }
+
+ /**
+ * Returns true if the column's PHP native type is a class name.
+ * @return boolean
+ * @see PropelTypes::isPhpObjectType()
+ */
+ public function isPhpObjectType()
+ {
+ return PropelTypes::isPhpObjectType($this->getPhpType());
+ }
+
+ /**
+ * Get the platform/adapter impl.
+ *
+ * @return Platform
+ */
+ public function getPlatform()
+ {
+ return $this->getTable()->getDatabase()->getPlatform();
+ }
+
+ /**
+ *
+ * @return string
+ * @deprecated Use DDLBuilder->getColumnDDL() instead; this will be removed in 1.3
+ */
+ public function getSqlString()
+ {
+ $sb = "";
+ $sb .= $this->getPlatform()->quoteIdentifier($this->getName()) . " ";
+ $sb .= $this->getDomain()->getSqlType();
+ if ($this->getPlatform()->hasSize($this->getDomain()->getSqlType())) {
+ $sb .= $this->getDomain()->printSize();
+ }
+ $sb .= " ";
+ $sb .= $this->getDefaultSetting() . " ";
+ $sb .= $this->getNotNullString() . " ";
+ $sb .= $this->getAutoIncrementString();
+ return trim($sb);
+ }
+
+ public static function generatePhpName($name, $phpNamingMethod = PhpNameGenerator::CONV_METHOD_CLEAN, $namePrefix = null) {
+ return NameFactory::generateName(NameFactory::PHP_GENERATOR, array($name, $phpNamingMethod, $namePrefix));
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/ColumnDefaultValue.php b/3rd_party/php/propel/generator/lib/model/ColumnDefaultValue.php
new file mode 100644
index 000000000..212fc44e4
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/ColumnDefaultValue.php
@@ -0,0 +1,91 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class ColumnDefaultValue
+{
+
+ const TYPE_VALUE = "value";
+ const TYPE_EXPR = "expr";
+
+ /**
+ * @var string The default value, as specified in the schema.
+ */
+ private $value;
+
+ /**
+ * @var string The type of value represented by this object (DefaultValue::TYPE_VALUE or DefaultValue::TYPE_EXPR).
+ */
+ private $type = ColumnDefaultValue::TYPE_VALUE;
+
+ /**
+ * Creates a new DefaultValue object.
+ *
+ * @param string $value The default value, as specified in the schema.
+ * @param string $type The type of default value (DefaultValue::TYPE_VALUE or DefaultValue::TYPE_EXPR)
+ */
+ public function __construct($value, $type = null)
+ {
+ $this->setValue($value);
+ if ($type !== null) {
+ $this->setType($type);
+ }
+ }
+
+ /**
+ * @return string The type of default value (DefaultValue::TYPE_VALUE or DefaultValue::TYPE_EXPR)
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param string $type The type of default value (DefaultValue::TYPE_VALUE or DefaultValue::TYPE_EXPR)
+ */
+ public function setType($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * Convenience method to indicate whether the value in this object is an expression (as opposed to simple value).
+ *
+ * @return boolean Whether value this object holds is an expression.
+ */
+ public function isExpression()
+ {
+ return ($this->type == self::TYPE_EXPR);
+ }
+
+ /**
+ * @return string The value, as specified in the schema.
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * @param string $value The value, as specified in the schema.
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+
+}
diff --git a/3rd_party/php/propel/generator/lib/model/ConstraintNameGenerator.php b/3rd_party/php/propel/generator/lib/model/ConstraintNameGenerator.php
new file mode 100644
index 000000000..735fea1b8
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/ConstraintNameGenerator.php
@@ -0,0 +1,72 @@
+NameGenerator implementation for table-specific
+ * constraints. Conforms to the maximum column name length for the
+ * type of database in use.
+ *
+ * @author Hans Lellelid (Propel)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class ConstraintNameGenerator implements NameGenerator
+{
+ /**
+ * Conditional compilation flag.
+ */
+ const DEBUG = false;
+
+ /**
+ * First element of inputs should be of type {@link Database}, second
+ * should be a table name, third is the type identifier (spared if
+ * trimming is necessary due to database type length constraints),
+ * and the fourth is a Integer indicating the number
+ * of this contraint.
+ *
+ * @see NameGenerator
+ * @throws EngineException
+ */
+ public function generateName($inputs)
+ {
+
+ $db = $inputs[0];
+ $name = $inputs[1];
+ $namePostfix = $inputs[2];
+ $constraintNbr = (string) $inputs[3];
+
+ // Calculate maximum RDBMS-specific column character limit.
+ $maxBodyLength = -1;
+ try {
+ $maxColumnNameLength = (int) $db->getPlatform()->getMaxColumnNameLength();
+ $maxBodyLength = ($maxColumnNameLength - strlen($namePostfix)
+ - strlen($constraintNbr) - 2);
+
+ if (self::DEBUG) {
+ print("maxColumnNameLength=" . $maxColumnNameLength
+ . " maxBodyLength=" . $maxBodyLength . "\n");
+ }
+ } catch (EngineException $e) {
+ echo $e;
+ throw $e;
+ }
+
+ // Do any necessary trimming.
+ if ($maxBodyLength !== -1 && strlen($name) > $maxBodyLength) {
+ $name = substr($name, 0, $maxBodyLength);
+ }
+
+ $name .= self::STD_SEPARATOR_CHAR . $namePostfix
+ . self::STD_SEPARATOR_CHAR . $constraintNbr;
+
+ return $name;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Database.php b/3rd_party/php/propel/generator/lib/model/Database.php
new file mode 100644
index 000000000..e34e4485d
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Database.php
@@ -0,0 +1,676 @@
+ (Propel)
+ * @author Leon Messerschmidt (Torque)
+ * @author John McNally (Torque)
+ * @author Martin Poeschl (Torque)
+ * @author Daniel Rall (Torque)
+ * @author Byron Foster (Torque)
+ * @version $Revision: 1802 $
+ * @package propel.generator.model
+ */
+class Database extends XMLElement
+{
+
+ private $platform;
+ private $tableList = array();
+ private $curColumn;
+ private $name;
+ private $pkg;
+
+ /**
+ * Namespace for the generated OM.
+ *
+ * @var string
+ */
+ protected $namespace;
+
+ private $baseClass;
+ private $basePeer;
+ private $defaultIdMethod;
+ private $defaultPhpNamingMethod;
+ private $defaultTranslateMethod;
+ private $dbParent;
+ private $tablesByName = array();
+ private $tablesByPhpName = array();
+ private $heavyIndexing;
+ protected $tablePrefix = '';
+
+ private $domainMap = array();
+
+ /**
+ * List of behaviors registered for this table
+ *
+ * @var array
+ */
+ protected $behaviors = array();
+
+ /**
+ * Constructs a new Database object.
+ *
+ * @param string $name
+ */
+ public function __construct($name = null)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets up the Database object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->name = $this->getAttribute("name");
+ $namespace = $this->getAttribute("namespace", '');
+ $package = $this->getAttribute("package");
+ if ($namespace && !$package && $this->getBuildProperty('namespaceAutoPackage')) {
+ $package = str_replace('\\', '.', $namespace);
+ }
+ $this->namespace = $namespace;
+ $this->pkg = $package;
+ $this->baseClass = $this->getAttribute("baseClass");
+ $this->basePeer = $this->getAttribute("basePeer");
+ $this->defaultIdMethod = $this->getAttribute("defaultIdMethod", IDMethod::NATIVE);
+ $this->defaultPhpNamingMethod = $this->getAttribute("defaultPhpNamingMethod", NameGenerator::CONV_METHOD_UNDERSCORE);
+ $this->defaultTranslateMethod = $this->getAttribute("defaultTranslateMethod", Validator::TRANSLATE_NONE);
+ $this->heavyIndexing = $this->booleanValue($this->getAttribute("heavyIndexing"));
+ $this->tablePrefix = $this->getAttribute('tablePrefix', $this->getBuildProperty('tablePrefix'));
+ }
+
+ /**
+ * Returns the Platform implementation for this database.
+ *
+ * @return Platform a Platform implementation
+ */
+ public function getPlatform()
+ {
+ return $this->platform;
+ }
+
+ /**
+ * Sets the Platform implementation for this database.
+ *
+ * @param Platform $platform A Platform implementation
+ */
+ public function setPlatform($platform)
+ {
+ $this->platform = $platform;
+ }
+
+ /**
+ * Get the name of the Database
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the Database
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Get the value of package.
+ * @return value of package.
+ */
+ public function getPackage()
+ {
+ return $this->pkg;
+ }
+
+ /**
+ * Set the value of package.
+ * @param v Value to assign to package.
+ */
+ public function setPackage($v)
+ {
+ $this->pkg = $v;
+ }
+
+ /**
+ * Get the value of the namespace.
+ * @return value of namespace.
+ */
+ public function getNamespace()
+ {
+ return $this->namespace;
+ }
+
+ /**
+ * Set the value of the namespace.
+ * @param v Value to assign to namespace.
+ */
+ public function setNamespace($v)
+ {
+ $this->namespace = $v;
+ }
+
+ /**
+ * Get the value of baseClass.
+ * @return value of baseClass.
+ */
+ public function getBaseClass()
+ {
+ return $this->baseClass;
+ }
+
+ /**
+ * Set the value of baseClass.
+ * @param v Value to assign to baseClass.
+ */
+ public function setBaseClass($v)
+ {
+ $this->baseClass = $v;
+ }
+
+ /**
+ * Get the value of basePeer.
+ * @return value of basePeer.
+ */
+ public function getBasePeer()
+ {
+ return $this->basePeer;
+ }
+
+ /**
+ * Set the value of basePeer.
+ * @param v Value to assign to basePeer.
+ */
+ public function setBasePeer($v)
+ {
+ $this->basePeer = $v;
+ }
+
+ /**
+ * Get the value of defaultIdMethod.
+ * @return value of defaultIdMethod.
+ */
+ public function getDefaultIdMethod()
+ {
+ return $this->defaultIdMethod;
+ }
+
+ /**
+ * Set the value of defaultIdMethod.
+ * @param v Value to assign to defaultIdMethod.
+ */
+ public function setDefaultIdMethod($v)
+ {
+ $this->defaultIdMethod = $v;
+ }
+
+ /**
+ * Get the value of defaultPHPNamingMethod which specifies the
+ * method for converting schema names for table and column to PHP names.
+ * @return string The default naming conversion used by this database.
+ */
+ public function getDefaultPhpNamingMethod()
+ {
+ return $this->defaultPhpNamingMethod;
+ }
+
+ /**
+ * Set the value of defaultPHPNamingMethod.
+ * @param string $v The default naming conversion for this database to use.
+ */
+ public function setDefaultPhpNamingMethod($v)
+ {
+ $this->defaultPhpNamingMethod = $v;
+ }
+
+ /**
+ * Get the value of defaultTranslateMethod which specifies the
+ * method for translate validator error messages.
+ * @return string The default translate method.
+ */
+ public function getDefaultTranslateMethod()
+ {
+ return $this->defaultTranslateMethod;
+ }
+
+ /**
+ * Set the value of defaultTranslateMethod.
+ * @param string $v The default translate method to use.
+ */
+ public function setDefaultTranslateMethod($v)
+ {
+ $this->defaultTranslateMethod = $v;
+ }
+
+ /**
+ * Get the value of heavyIndexing.
+ *
+ * This is a synonym for getHeavyIndexing().
+ *
+ * @return boolean Value of heavyIndexing.
+ * @see getHeavyIndexing()
+ */
+ public function isHeavyIndexing()
+ {
+ return $this->getHeavyIndexing();
+ }
+
+ /**
+ * Get the value of heavyIndexing.
+ *
+ * @return boolean Value of heavyIndexing.
+ */
+ public function getHeavyIndexing()
+ {
+ return $this->heavyIndexing;
+ }
+
+ /**
+ * Set the value of heavyIndexing.
+ * @param boolean $v Value to assign to heavyIndexing.
+ */
+ public function setHeavyIndexing($v)
+ {
+ $this->heavyIndexing = (boolean) $v;
+ }
+
+ /**
+ * Return an array of all tables
+ */
+ public function getTables()
+ {
+ return $this->tableList;
+ }
+
+ /**
+ * Check whether the database has a table.
+ * @return boolean
+ */
+ public function hasTable($name)
+ {
+ return array_key_exists($name, $this->tablesByName);
+ }
+
+ /**
+ * Return the table with the specified name.
+ * @param string $name The name of the table (e.g. 'my_table')
+ * @return Table a Table object or null if it doesn't exist
+ */
+ public function getTable($name)
+ {
+ if ($this->hasTable($name)) {
+ return $this->tablesByName[$name];
+ }
+ return null; // just to be explicit
+ }
+
+ /**
+ * Return the table with the specified phpName.
+ * @param string $phpName the PHP Name of the table (e.g. 'MyTable')
+ * @return Table a Table object or null if it doesn't exist
+ */
+ public function getTableByPhpName($phpName)
+ {
+ if (isset($this->tablesByPhpName[$phpName])) {
+ return $this->tablesByPhpName[$phpName];
+ }
+ return null; // just to be explicit
+ }
+
+ /**
+ * An utility method to add a new table from an xml attribute.
+ */
+ public function addTable($data)
+ {
+ if ($data instanceof Table) {
+ $tbl = $data; // alias
+ $tbl->setDatabase($this);
+ if (isset($this->tablesByName[$tbl->getName()])) {
+ throw new EngineException("Duplicate table declared: " . $tbl->getName());
+ }
+ $this->tableList[] = $tbl;
+ $this->tablesByName[ $tbl->getName() ] = $tbl;
+ $this->tablesByPhpName[ $tbl->getPhpName() ] = $tbl;
+ if ($tbl->getPackage() === null) {
+ $tbl->setPackage($this->getPackage());
+ }
+ return $tbl;
+ } else {
+ $tbl = new Table();
+ $tbl->setDatabase($this);
+ $tbl->loadFromXML($data);
+ return $this->addTable($tbl); // call self w/ different param
+ }
+ }
+
+ /**
+ * Set the parent of the database
+ */
+ public function setAppData(AppData $parent)
+ {
+ $this->dbParent = $parent;
+ }
+
+ /**
+ * Get the parent of the table
+ */
+ public function getAppData()
+ {
+ return $this->dbParent;
+ }
+
+ /**
+ * Adds Domain object from tag.
+ * @param mixed XML attributes (array) or Domain object.
+ */
+ public function addDomain($data) {
+
+ if ($data instanceof Domain) {
+ $domain = $data; // alias
+ $domain->setDatabase($this);
+ $this->domainMap[ $domain->getName() ] = $domain;
+ return $domain;
+ } else {
+ $domain = new Domain();
+ $domain->setDatabase($this);
+ $domain->loadFromXML($data);
+ return $this->addDomain($domain); // call self w/ different param
+ }
+ }
+
+ /**
+ * Get already configured Domain object by name.
+ * @return Domain
+ */
+ public function getDomain($domainName)
+ {
+ if (isset($this->domainMap[$domainName])) {
+ return $this->domainMap[$domainName];
+ }
+ return null; // just to be explicit
+ }
+
+ public function getGeneratorConfig()
+ {
+ if ($this->getAppData() && $this->getAppData()->getPlatform()) {
+ return $this->getAppData()->getPlatform()->getGeneratorConfig();
+ } else {
+ return null;
+ }
+ }
+
+ public function getBuildProperty($key)
+ {
+ if($config = $this->getGeneratorConfig()) {
+ return $config->getBuildProperty($key);
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Adds a new Behavior to the database
+ * @return Behavior A behavior instance
+ */
+ public function addBehavior($bdata)
+ {
+ if ($bdata instanceof Behavior) {
+ $behavior = $bdata;
+ $behavior->setDatabase($this);
+ $this->behaviors[$behavior->getName()] = $behavior;
+ return $behavior;
+ } else {
+ $class = $this->getConfiguredBehavior($bdata['name']);
+ $behavior = new $class();
+ $behavior->loadFromXML($bdata);
+ return $this->addBehavior($behavior);
+ }
+ }
+
+ /**
+ * Get the database behaviors
+ * @return Array of Behavior objects
+ */
+ public function getBehaviors()
+ {
+ return $this->behaviors;
+ }
+
+ /**
+ * check if the database has a behavior by name
+ *
+ * @param string $name the behavior name
+ * @return boolean True if the behavior exists
+ */
+ public function hasBehavior($name)
+ {
+ return array_key_exists($name, $this->behaviors);
+ }
+
+ /**
+ * Get one database behavior by name
+ * @param string $name the behavior name
+ * @return Behavior a behavior object
+ */
+ public function getBehavior($name)
+ {
+ return $this->behaviors[$name];
+ }
+
+ /**
+ * Get the table prefix for this database
+ *
+ * @return string the table prefix
+ */
+ public function getTablePrefix()
+ {
+ return $this->tablePrefix;
+ }
+
+
+ public function doFinalInitialization()
+ {
+ if($defaultBehaviors = $this->getBuildProperty('behaviorDefault')) {
+ // add generic behaviors from build.properties
+ $defaultBehaviors = explode(',', $defaultBehaviors);
+ foreach ($defaultBehaviors as $behavior) {
+ $this->addBehavior(array('name' => trim($behavior)));
+ }
+ }
+
+ // execute behavior database modifiers
+ foreach ($this->getBehaviors() as $behavior) {
+ $behavior->modifyDatabase();
+ }
+
+ $tables = $this->getTables();
+
+ // execute early table behaviors
+ foreach ($tables as $table) {
+ foreach ($table->getEarlyBehaviors() as $behavior) {
+ if (!$behavior->isTableModified()) {
+ $behavior->getTableModifier()->modifyTable();
+ $behavior->setTableModified(true);
+ }
+ }
+ }
+
+ for ($i=0,$size=count($tables); $i < $size; $i++) {
+ $currTable = $tables[$i];
+
+ // check schema integrity
+ // if idMethod="autoincrement", make sure a column is
+ // specified as autoIncrement="true"
+ // FIXME: Handle idMethod="native" via DB adapter.
+ /*
+
+ --- REMOVING THIS BECAUSE IT'S ANNOYING
+
+ if ($currTable->getIdMethod() == IDMethod::NATIVE ) {
+ $columns = $currTable->getColumns();
+ $foundOne = false;
+ for ($j=0, $cLen=count($columns); $j < $cLen && !$foundOne; $j++) {
+ $foundOne = $columns[$j]->isAutoIncrement();
+ }
+
+ if (!$foundOne) {
+ $errorMessage = "Table '" . $currTable->getName()
+ . "' is set to use native id generation, but it does not "
+ . "have a column which declared as the one to "
+ . "auto increment (i.e. autoIncrement=\"true\")";
+
+ throw new BuildException($errorMessage);
+ }
+ }
+ */
+
+ $currTable->doFinalInitialization();
+
+ // setup reverse fk relations
+ $fks = $currTable->getForeignKeys();
+ for ($j=0, $fksLen=count($fks); $j < $fksLen; $j++) {
+ $currFK = $fks[$j];
+ $foreignTable = $this->getTable($currFK->getForeignTableName());
+ if ($foreignTable === null) {
+ throw new BuildException("ERROR!! Attempt to set foreign"
+ . " key to nonexistent table, "
+ . $currFK->getForeignTableName() . "!");
+ }
+
+ $referrers = $foreignTable->getReferrers();
+ if ($referrers === null || !in_array($currFK, $referrers, true) ) {
+ $foreignTable->addReferrer($currFK);
+ }
+
+ // local column references
+ $localColumnNames = $currFK->getLocalColumns();
+
+ for ($k=0,$lcnLen=count($localColumnNames); $k < $lcnLen; $k++) {
+
+ $local = $currTable->getColumn($localColumnNames[$k]);
+
+ // give notice of a schema inconsistency.
+ // note we do not prevent the npe as there is nothing
+ // that we can do, if it is to occur.
+ if ($local === null) {
+ throw new BuildException("ERROR!! Attempt to define foreign"
+ . " key with nonexistent column, "
+ . $localColumnNames[$k] . ", in table, "
+ . $currTable->getName() . "!");
+ }
+
+ //check for foreign pk's
+ if ($local->isPrimaryKey()) {
+ $currTable->setContainsForeignPK(true);
+ }
+
+ } // for each local col name
+
+ // foreign column references
+ $foreignColumnNames = $currFK->getForeignColumns();
+ for ($k=0,$fcnLen=count($localColumnNames); $k < $fcnLen; $k++) {
+ $foreign = $foreignTable->getColumn($foreignColumnNames[$k]);
+ // if the foreign column does not exist, we may have an
+ // external reference or a misspelling
+ if ($foreign === null) {
+ throw new BuildException("ERROR!! Attempt to set foreign"
+ . " key to nonexistent column, "
+ . $foreignColumnNames[$k] . ", in table, "
+ . $foreignTable->getName() . "!");
+ } else {
+ $foreign->addReferrer($currFK);
+ }
+ } // for each foreign col ref
+ }
+ }
+
+ // Behaviors may have added behaviors of their own
+ // These behaviors must launch their modifyTable() method,
+ // Until there is no behavior left
+ $behaviorsLeft = true;
+ while ($behaviorsLeft) {
+ $behaviorsLeft = false;
+ foreach ($tables as $table) {
+ foreach ($table->getBehaviors() as $behavior) {
+ if (!$behavior->isTableModified()) {
+ $behavior->getTableModifier()->modifyTable();
+ $behavior->setTableModified(true);
+ $behaviorsLeft = true;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $dbNode = $node->appendChild($doc->createElement('database'));
+
+ $dbNode->setAttribute('name', $this->name);
+
+ if ($this->pkg) {
+ $dbNode->setAttribute('package', $this->pkg);
+ }
+
+ if ($this->defaultIdMethod) {
+ $dbNode->setAttribute('defaultIdMethod', $this->defaultIdMethod);
+ }
+
+ if ($this->baseClass) {
+ $dbNode->setAttribute('baseClass', $this->baseClass);
+ }
+
+ if ($this->basePeer) {
+ $dbNode->setAttribute('basePeer', $this->basePeer);
+ }
+
+ if ($this->defaultPhpNamingMethod) {
+ $dbNode->setAttribute('defaultPhpNamingMethod', $this->defaultPhpNamingMethod);
+ }
+
+ if ($this->defaultTranslateMethod) {
+ $dbNode->setAttribute('defaultTranslateMethod', $this->defaultTranslateMethod);
+ }
+
+ /*
+
+ FIXME - Before we can add support for domains in the schema, we need
+ to have a method of the Column that indicates whether the column was mapped
+ to a SPECIFIC domain (since Column->getDomain() will always return a Domain object)
+
+ foreach ($this->domainMap as $domain) {
+ $domain->appendXml($dbNode);
+ }
+ */
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($dbNode);
+ }
+
+ foreach ($this->tableList as $table) {
+ $table->appendXml($dbNode);
+ }
+
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Domain.php b/3rd_party/php/propel/generator/lib/model/Domain.php
new file mode 100644
index 000000000..632e3f374
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Domain.php
@@ -0,0 +1,386 @@
+ (Propel)
+ * @author Martin Poeschl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Domain extends XMLElement
+{
+
+ /**
+ * @var string The name of this domain
+ */
+ private $name;
+
+ /**
+ * @var string Description for this domain.
+ */
+ private $description;
+
+ /**
+ * @var int Size
+ */
+ private $size;
+
+ /**
+ * @var int Scale
+ */
+ private $scale;
+
+ /**
+ * @var int Propel type from schema
+ */
+ private $propelType;
+
+ /**
+ * @var string The SQL type to use for this column
+ */
+ private $sqlType;
+
+ /**
+ * @var ColumnDefaultValue A default value
+ */
+ private $defaultValue;
+
+ /**
+ * @var Database
+ */
+ private $database;
+
+ /**
+ * Creates a new Domain object.
+ * If this domain needs a name, it must be specified manually.
+ *
+ * @param string $type Propel type.
+ * @param string $sqlType SQL type.
+ * @param string $size
+ * @param string $scale
+ */
+ public function __construct($type = null, $sqlType = null, $size = null, $scale = null)
+ {
+ $this->propelType = $type;
+ $this->sqlType = ($sqlType !== null) ? $sqlType : $type;
+ $this->size = $size;
+ $this->scale = $scale;
+ }
+
+ /**
+ * Copy the values from current object into passed-in Domain.
+ * @param Domain $domain Domain to copy values into.
+ */
+ public function copy(Domain $domain)
+ {
+ $this->defaultValue = $domain->getDefaultValue();
+ $this->description = $domain->getDescription();
+ $this->name = $domain->getName();
+ $this->scale = $domain->getScale();
+ $this->size = $domain->getSize();
+ $this->sqlType = $domain->getSqlType();
+ $this->propelType = $domain->getType();
+ }
+
+ /**
+ * Sets up the Domain object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $schemaType = strtoupper($this->getAttribute("type"));
+ $this->copy($this->getDatabase()->getPlatform()->getDomainForType($schemaType));
+
+ //Name
+ $this->name = $this->getAttribute("name");
+
+ // Default value
+ $defval = $this->getAttribute("defaultValue", $this->getAttribute("default"));
+ if ($defval !== null) {
+ $this->setDefaultValue(new ColumnDefaultValue($defval, ColumnDefaultValue::TYPE_VALUE));
+ } elseif ($this->getAttribute("defaultExpr") !== null) {
+ $this->setDefaultValue(new ColumnDefaultValue($this->getAttribute("defaultExpr"), ColumnDefaultValue::TYPE_EXPR));
+ }
+
+ $this->size = $this->getAttribute("size");
+ $this->scale = $this->getAttribute("scale");
+ $this->description = $this->getAttribute("description");
+ }
+
+ /**
+ * Sets the owning database object (if this domain is being setup via XML).
+ * @param Database $database
+ */
+ public function setDatabase(Database $database)
+ {
+ $this->database = $database;
+ }
+
+ /**
+ * Gets the owning database object (if this domain was setup via XML).
+ * @return Database
+ */
+ public function getDatabase()
+ {
+ return $this->database;
+ }
+
+ /**
+ * @return string Returns the description.
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * @param string $description The description to set.
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+ }
+
+ /**
+ * @return string Returns the name.
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $name The name to set.
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * @return string Returns the scale.
+ */
+ public function getScale()
+ {
+ return $this->scale;
+ }
+
+ /**
+ * @param string $scale The scale to set.
+ */
+ public function setScale($scale)
+ {
+ $this->scale = $scale;
+ }
+
+ /**
+ * Replaces the size if the new value is not null.
+ *
+ * @param string $value The size to set.
+ */
+ public function replaceScale($value)
+ {
+ if ($value !== null) {
+ $this->scale = $value;
+ }
+ }
+
+ /**
+ * @return int Returns the size.
+ */
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * @param int $size The size to set.
+ */
+ public function setSize($size)
+ {
+ $this->size = $size;
+ }
+
+ /**
+ * Replaces the size if the new value is not null.
+ *
+ * @param int $value The size to set.
+ */
+ public function replaceSize($value)
+ {
+ if ($value !== null) {
+ $this->size = $value;
+ }
+ }
+
+ /**
+ * @return string Returns the propelType.
+ */
+ public function getType()
+ {
+ return $this->propelType;
+ }
+
+ /**
+ * @param string $propelType The PropelTypes type to set.
+ */
+ public function setType($propelType)
+ {
+ $this->propelType = $propelType;
+ }
+
+ /**
+ * Replaces the type if the new value is not null.
+ *
+ * @param string $value The tyep to set.
+ */
+ public function replaceType($value)
+ {
+ if ($value !== null) {
+ $this->propelType = $value;
+ }
+ }
+
+ /**
+ * Gets the default value object.
+ * @return ColumnDefaultValue The default value object for this domain.
+ */
+ public function getDefaultValue()
+ {
+ return $this->defaultValue;
+ }
+
+ /**
+ * Gets the default value, type-casted for use in PHP OM.
+ * @return mixed
+ * @see getDefaultValue()
+ */
+ public function getPhpDefaultValue()
+ {
+ if ($this->defaultValue === null) {
+ return null;
+ } else {
+ if ($this->defaultValue->isExpression()) {
+ throw new EngineException("Cannot get PHP version of default value for default value EXPRESSION.");
+ }
+ if ($this->propelType === PropelTypes::BOOLEAN || $this->propelType === PropelTypes::BOOLEAN_EMU) {
+ return $this->booleanValue($this->defaultValue->getValue());
+ } else {
+ return $this->defaultValue->getValue();
+ }
+ }
+ }
+
+ /**
+ * @param ColumnDefaultValue $value The column default value to set.
+ */
+ public function setDefaultValue(ColumnDefaultValue $value)
+ {
+ $this->defaultValue = $value;
+ }
+
+ /**
+ * Replaces the default value if the new value is not null.
+ *
+ * @param ColumnDefaultValue $value The defualt value object
+ */
+ public function replaceDefaultValue(ColumnDefaultValue $value = null)
+ {
+ if ($value !== null) {
+ $this->defaultValue = $value;
+ }
+ }
+
+ /**
+ * @return string Returns the sqlType.
+ */
+ public function getSqlType()
+ {
+ return $this->sqlType;
+ }
+
+ /**
+ * @param string $sqlType The sqlType to set.
+ */
+ public function setSqlType($sqlType)
+ {
+ $this->sqlType = $sqlType;
+ }
+
+ /**
+ * Replaces the SQL type if the new value is not null.
+ * @param string $sqlType The native SQL type to use for this domain.
+ */
+ public function replaceSqlType($sqlType)
+ {
+ if ($sqlType !== null) {
+ $this->sqlType = $sqlType;
+ }
+ }
+
+ /**
+ * Return the size and scale in brackets for use in an sql schema.
+ *
+ * @return string Size and scale or an empty String if there are no values
+ * available.
+ */
+ public function printSize()
+ {
+ if ($this->size !== null && $this->scale !== null) {
+ return '(' . $this->size . ',' . $this->scale . ')';
+ } elseif ($this->size !== null) {
+ return '(' . $this->size . ')';
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $domainNode = $node->appendChild($doc->createElement('domain'));
+ $domainNode->setAttribute('type', $this->getType());
+ $domainNode->setAttribute('name', $this->getName());
+
+ if ($this->sqlType !== $this->getType()) {
+ $domainNode->setAttribute('sqlType', $this->sqlType);
+ }
+
+ $def = $this->getDefaultValue();
+ if ($def) {
+ if ($def->isExpression()) {
+ $domainNode->setAttribute('defaultExpr', $def->getValue());
+ } else {
+ $domainNode->setAttribute('defaultValue', $def->getValue());
+ }
+ }
+
+ if ($this->size) {
+ $domainNode->setAttribute('size', $this->size);
+ }
+
+ if ($this->scale) {
+ $domainNode->setAttribute('scale', $this->scale);
+ }
+
+ if ($this->description) {
+ $domainNode->setAttribute('description', $this->description);
+ }
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/model/ForeignKey.php b/3rd_party/php/propel/generator/lib/model/ForeignKey.php
new file mode 100644
index 000000000..75b11584e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/ForeignKey.php
@@ -0,0 +1,509 @@
+
+ * @author Fedor
+ * @author Daniel Rall
+ * @version $Revision: 1778 $
+ * @package propel.generator.model
+ */
+class ForeignKey extends XMLElement
+{
+
+ protected $foreignTableName;
+ protected $name;
+ protected $phpName;
+ protected $refPhpName;
+ protected $defaultJoin;
+ protected $onUpdate;
+ protected $onDelete;
+ protected $parentTable;
+ protected $localColumns = array();
+ protected $foreignColumns = array();
+
+ // the uppercase equivalent of the onDelete/onUpdate values in the dtd
+ const NONE = ""; // No "ON [ DELETE | UPDATE]" behaviour specified.
+ const NOACTION = "NO ACTION";
+ const CASCADE = "CASCADE";
+ const RESTRICT = "RESTRICT";
+ const SETDEFAULT = "SET DEFAULT";
+ const SETNULL = "SET NULL";
+
+ /**
+ * Constructs a new ForeignKey object.
+ *
+ * @param string $name
+ */
+ public function __construct($name=null)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets up the ForeignKey object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->foreignTableName = $this->getTable()->getDatabase()->getTablePrefix() . $this->getAttribute("foreignTable");
+ $this->name = $this->getAttribute("name");
+ $this->phpName = $this->getAttribute("phpName");
+ $this->refPhpName = $this->getAttribute("refPhpName");
+ $this->defaultJoin = $this->getAttribute('defaultJoin');
+ $this->onUpdate = $this->normalizeFKey($this->getAttribute("onUpdate"));
+ $this->onDelete = $this->normalizeFKey($this->getAttribute("onDelete"));
+ }
+
+ /**
+ * normalizes the input of onDelete, onUpdate attributes
+ */
+ private function normalizeFKey($attrib)
+ {
+ if ($attrib === null || strtoupper($attrib) == "NONE") {
+ $attrib = self::NONE;
+ }
+ $attrib = strtoupper($attrib);
+ if ($attrib == "SETNULL") {
+ $attrib = self::SETNULL;
+ }
+ return $attrib;
+ }
+
+ /**
+ * returns whether or not the onUpdate attribute is set
+ */
+ public function hasOnUpdate()
+ {
+ return ($this->onUpdate !== self::NONE);
+ }
+
+ /**
+ * returns whether or not the onDelete attribute is set
+ */
+ public function hasOnDelete()
+ {
+ return ($this->onDelete !== self::NONE);
+ }
+
+ /**
+ * returns the onUpdate attribute
+ * @return string
+ */
+ public function getOnUpdate()
+ {
+ return $this->onUpdate;
+ }
+
+ /**
+ * Returns the onDelete attribute
+ * @return string
+ */
+ public function getOnDelete()
+ {
+ return $this->onDelete;
+ }
+
+ /**
+ * sets the onDelete attribute
+ */
+ public function setOnDelete($value)
+ {
+ $this->onDelete = $this->normalizeFKey($value);
+ }
+
+ /**
+ * sets the onUpdate attribute
+ */
+ public function setOnUpdate($value)
+ {
+ $this->onUpdate = $this->normalizeFKey($value);
+ }
+
+ /**
+ * Returns the name attribute.
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Sets the name attribute.
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Gets the phpName for this foreign key (if any).
+ * @return string
+ */
+ public function getPhpName()
+ {
+ return $this->phpName;
+ }
+
+ /**
+ * Sets a phpName to use for this foreign key.
+ * @param string $name
+ */
+ public function setPhpName($name)
+ {
+ $this->phpName = $name;
+ }
+
+ /**
+ * Gets the refPhpName for this foreign key (if any).
+ * @return string
+ */
+ public function getRefPhpName()
+ {
+ return $this->refPhpName;
+ }
+
+ /**
+ * Sets a refPhpName to use for this foreign key.
+ * @param string $name
+ */
+ public function setRefPhpName($name)
+ {
+ $this->refPhpName = $name;
+ }
+
+ /**
+ * Gets the defaultJoin for this foreign key (if any).
+ * @return string
+ */
+ public function getDefaultJoin()
+ {
+ return $this->defaultJoin;
+ }
+
+ /**
+ * Sets a defaultJoin to use for this foreign key.
+ * @param string $name
+ */
+ public function setDefaultJoin($defaultJoin)
+ {
+ $this->defaultJoin = $defaultJoin;
+ }
+
+ /**
+ * Get the foreignTableName of the FK
+ */
+ public function getForeignTableName()
+ {
+ return $this->foreignTableName;
+ }
+
+ /**
+ * Set the foreignTableName of the FK
+ */
+ public function setForeignTableName($tableName)
+ {
+ $this->foreignTableName = $tableName;
+ }
+
+ /**
+ * Gets the resolved foreign Table model object.
+ * @return Table
+ */
+ public function getForeignTable()
+ {
+ return $this->getTable()->getDatabase()->getTable($this->getForeignTableName());
+ }
+
+ /**
+ * Set the parent Table of the foreign key
+ */
+ public function setTable(Table $parent)
+ {
+ $this->parentTable = $parent;
+ }
+
+ /**
+ * Get the parent Table of the foreign key
+ */
+ public function getTable()
+ {
+ return $this->parentTable;
+ }
+
+ /**
+ * Returns the Name of the table the foreign key is in
+ */
+ public function getTableName()
+ {
+ return $this->parentTable->getName();
+ }
+
+ /**
+ * Adds a new reference entry to the foreign key.
+ */
+ public function addReference($p1, $p2 = null)
+ {
+ if (is_array($p1)) {
+ $this->addReference(@$p1["local"], @$p1["foreign"]);
+ } else {
+ if ($p1 instanceof Column) {
+ $p1 = $p1->getName();
+ }
+ if ($p2 instanceof Column) {
+ $p2 = $p2->getName();
+ }
+ $this->localColumns[] = $p1;
+ $this->foreignColumns[] = $p2;
+ }
+ }
+
+ /**
+ * Clear the references of this foreign key
+ */
+ public function clearReferences()
+ {
+ $this->localColumns[] = array();
+ $this->foreignColumns[] = array();
+ }
+
+ /**
+ * Return a comma delimited string of local column names
+ * @deprecated because Column::makeList() is deprecated; use the array-returning getLocalColumns() and DDLBuilder->getColumnList() instead instead.
+ */
+ public function getLocalColumnNames()
+ {
+ return Column::makeList($this->getLocalColumns(), $this->getTable()->getDatabase()->getPlatform());
+ }
+
+ /**
+ * Return a comma delimited string of foreign column names
+ * @deprecated because Column::makeList() is deprecated; use the array-returning getForeignColumns() and DDLBuilder->getColumnList() instead instead.
+ */
+ public function getForeignColumnNames()
+ {
+ return Column::makeList($this->getForeignColumns(), $this->getTable()->getDatabase()->getPlatform());
+ }
+
+ /**
+ * Return an array of local column names.
+ * @return array string[]
+ */
+ public function getLocalColumns()
+ {
+ return $this->localColumns;
+ }
+
+ /**
+ * Utility method to get local column to foreign column
+ * mapping for this foreign key.
+ */
+ public function getLocalForeignMapping()
+ {
+ $h = array();
+ for ($i=0, $size=count($this->localColumns); $i < $size; $i++) {
+ $h[$this->localColumns[$i]] = $this->foreignColumns[$i];
+ }
+ return $h;
+ }
+
+ /**
+ * Utility method to get local column to foreign column
+ * mapping for this foreign key.
+ */
+ public function getForeignLocalMapping()
+ {
+ $h = array();
+ for ($i=0, $size=count($this->localColumns); $i < $size; $i++) {
+ $h[$this->foreignColumns[$i]] = $this->localColumns[$i];
+ }
+ return $h;
+ }
+
+ /**
+ * Utility method to get local and foreign column objects
+ * mapping for this foreign key.
+ */
+ public function getColumnObjectsMapping()
+ {
+ $mapping = array();
+ $localTable = $this->getTable();
+ $foreignTable = $this->getForeignTable();
+ for ($i=0, $size=count($this->localColumns); $i < $size; $i++) {
+ $mapping[]= array(
+ 'local' => $localTable->getColumn($this->localColumns[$i]),
+ 'foreign' => $foreignTable->getColumn($this->foreignColumns[$i]),
+ );
+ }
+ return $mapping;
+ }
+
+ /**
+ * Get the foreign column mapped to specified local column.
+ * @return string Column name.
+ */
+ public function getMappedForeignColumn($local)
+ {
+ $m = $this->getLocalForeignMapping();
+ if (isset($m[$local])) {
+ return $m[$local];
+ }
+ return null;
+ }
+
+ /**
+ * Get the local column mapped to specified foreign column.
+ * @return string Column name.
+ */
+ public function getMappedLocalColumn($foreign)
+ {
+ $m = $this->getForeignLocalMapping();
+ if (isset($m[$foreign])) {
+ return $m[$foreign];
+ }
+ return null;
+ }
+
+ /**
+ * Return an array of foreign column objects.
+ * @return array Column[]
+ */
+ public function getForeignColumns()
+ {
+ return $this->foreignColumns;
+ }
+
+ /**
+ * Whether this foreign key uses a required column, or a list or required columns.
+ *
+ * @return boolean
+ */
+ public function isLocalColumnsRequired()
+ {
+ foreach ($this->getLocalColumns() as $columnName) {
+ if (!$this->getTable()->getColumn($columnName)->isNotNull()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Whether this foreign key is also the primary key of the local table.
+ *
+ * @return boolean
+ */
+ public function isLocalPrimaryKey()
+ {
+ $localCols = $this->getLocalColumns();
+
+ $localPKColumnObjs = $this->getTable()->getPrimaryKey();
+
+ $localPKCols = array();
+ foreach ($localPKColumnObjs as $lPKCol) {
+ $localPKCols[] = $lPKCol->getName();
+ }
+
+ return (!array_diff($localPKCols, $localCols));
+ }
+
+ /**
+ * Whether this foreign key is matched by an invertes foreign key (on foreign table).
+ *
+ * This is to prevent duplicate columns being generated for a 1:1 relationship that is represented
+ * by foreign keys on both tables. I don't know if that's good practice ... but hell, why not
+ * support it.
+ *
+ * @param ForeignKey $fk
+ * @return boolean
+ * @link http://propel.phpdb.org/trac/ticket/549
+ */
+ public function isMatchedByInverseFK()
+ {
+ return (bool) $this->getInverseFK();
+ }
+
+ public function getInverseFK()
+ {
+ $foreignTable = $this->getForeignTable();
+ $map = $this->getForeignLocalMapping();
+
+ foreach ($foreignTable->getForeignKeys() as $refFK) {
+ $fkMap = $refFK->getLocalForeignMapping();
+ if ( ($refFK->getTableName() == $this->getTableName()) && ($map == $fkMap) ) { // compares keys and values, but doesn't care about order, included check to make sure it's the same table (fixes #679)
+ return $refFK;
+ }
+ }
+ }
+
+ /**
+ * Get the other foreign keys starting on the same table
+ * Used in many-to-many relationships
+ *
+ * @return ForeignKey
+ */
+ public function getOtherFks()
+ {
+ $fks = array();
+ foreach ($this->getTable()->getForeignKeys() as $fk) {
+ if ($fk !== $this) {
+ $fks[]= $fk;
+ }
+ }
+ return $fks;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $fkNode = $node->appendChild($doc->createElement('foreign-key'));
+
+ $fkNode->setAttribute('foreignTable', $this->getForeignTableName());
+ $fkNode->setAttribute('name', $this->getName());
+
+ if ($this->getPhpName()) {
+ $fkNode->setAttribute('phpName', $this->getPhpName());
+ }
+
+ if ($this->getRefPhpName()) {
+ $fkNode->setAttribute('refPhpName', $this->getRefPhpName());
+ }
+
+ if ($this->getDefaultJoin()) {
+ $fkNode->setAttribute('defaultJoin', $this->getDefaultJoin());
+ }
+
+ if ($this->getOnDelete()) {
+ $fkNode->setAttribute('onDelete', $this->getOnDelete());
+ }
+
+ if ($this->getOnUpdate()) {
+ $fkNode->setAttribute('onUpdate', $this->getOnUpdate());
+ }
+
+ for ($i=0, $size=count($this->localColumns); $i < $size; $i++) {
+ $refNode = $fkNode->appendChild($doc->createElement('reference'));
+ $refNode->setAttribute('local', $this->localColumns[$i]);
+ $refNode->setAttribute('foreign', $this->foreignColumns[$i]);
+ }
+
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($fkNode);
+ }
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/IDMethod.php b/3rd_party/php/propel/generator/lib/model/IDMethod.php
new file mode 100644
index 000000000..469ddfd85
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/IDMethod.php
@@ -0,0 +1,35 @@
+ (Propel)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+interface IDMethod
+{
+
+ /**
+ * Key generation via database-specific ID method
+ * (i.e. auto-increment for MySQL, sequence for Oracle, etc.).
+ */
+ const NATIVE = "native";
+
+ /**
+ * No RDBMS key generation (keys may be generated by the
+ * application).
+ */
+ const NO_ID_METHOD = "none";
+
+}
diff --git a/3rd_party/php/propel/generator/lib/model/IdMethodParameter.php b/3rd_party/php/propel/generator/lib/model/IdMethodParameter.php
new file mode 100644
index 000000000..f9f1eb60c
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/IdMethodParameter.php
@@ -0,0 +1,108 @@
+ (Propel)
+ * @author John McNally (Torque)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class IdMethodParameter extends XMLElement
+{
+
+ private $name;
+ private $value;
+ private $parentTable;
+
+ /**
+ * Sets up the IdMethodParameter object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->name = $this->getAttribute("name");
+ $this->value = $this->getAttribute("value");
+ }
+
+ /**
+ * Get the parameter name
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the parameter name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Get the parameter value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set the parameter value
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * Set the parent Table of the id method
+ */
+ public function setTable(Table $parent)
+ {
+ $this->parentTable = $parent;
+ }
+
+ /**
+ * Get the parent Table of the id method
+ */
+ public function getTable()
+ {
+ return $this->parentTable;
+ }
+
+ /**
+ * Returns the Name of the table the id method is in
+ */
+ public function getTableName()
+ {
+ return $this->parentTable->getName();
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $paramNode = $node->appendChild($doc->createElement('id-method-parameter'));
+ if ($this->getName()) {
+ $paramNode->setAttribute('name', $this->getName());
+ }
+ $paramNode->setAttribute('value', $this->getValue());
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Index.php b/3rd_party/php/propel/generator/lib/model/Index.php
new file mode 100644
index 000000000..3e6add9a4
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Index.php
@@ -0,0 +1,285 @@
+
+ * @author Daniel Rall
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Index extends XMLElement
+{
+
+ /** enables debug output */
+ const DEBUG = false;
+
+ private $indexName;
+ private $parentTable;
+
+ /** @var array string[] */
+ private $indexColumns;
+
+ /** @var array */
+ private $indexColumnSizes = array();
+
+ /**
+ * Creates a new Index instance.
+ *
+ * @param string $name
+ */
+ public function __construct($name=null)
+ {
+ $this->indexName = $name;
+ }
+
+ private function createName()
+ {
+ $table = $this->getTable();
+ $inputs = array();
+ $inputs[] = $table->getDatabase();
+ $inputs[] = $table->getName();
+ if ($this->isUnique()) {
+ $inputs[] = "U";
+ } else {
+ $inputs[] = "I";
+ }
+ // ASSUMPTION: This Index not yet added to the list.
+ if ($this->isUnique()) {
+ $inputs[] = count($table->getUnices()) + 1;
+ } else {
+ $inputs[] = count($table->getIndices()) + 1;
+ }
+
+ $this->indexName = NameFactory::generateName(
+ NameFactory::CONSTRAINT_GENERATOR, $inputs);
+ }
+
+ /**
+ * Sets up the Index object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->indexName = $this->getAttribute("name");
+ }
+
+ /**
+ * @see #isUnique()
+ * @deprecated Use isUnique() instead.
+ */
+ public function getIsUnique()
+ {
+ return $this->isUnique();
+ }
+
+ /**
+ * Returns the uniqueness of this index.
+ */
+ public function isUnique()
+ {
+ return false;
+ }
+
+ /**
+ * @see #getName()
+ * @deprecated Use getName() instead.
+ */
+ public function getIndexName()
+ {
+ return $this->getName();
+ }
+
+ /**
+ * Gets the name of this index.
+ */
+ public function getName()
+ {
+ if ($this->indexName === null) {
+ try {
+ // generate an index name if we don't have a supplied one
+ $this->createName();
+ } catch (EngineException $e) {
+ // still no name
+ }
+ }
+ return substr($this->indexName, 0, $this->getTable()->getDatabase()->getPlatform()->getMaxColumnNameLength());
+ }
+
+ /**
+ * @see #setName(String name)
+ * @deprecated Use setName(String name) instead.
+ */
+ public function setIndexName($name)
+ {
+ $this->setName($name);
+ }
+
+ /**
+ * Set the name of this index.
+ */
+ public function setName($name)
+ {
+ $this->indexName = $name;
+ }
+
+ /**
+ * Set the parent Table of the index
+ */
+ public function setTable(Table $parent)
+ {
+ $this->parentTable = $parent;
+ }
+
+ /**
+ * Get the parent Table of the index
+ */
+ public function getTable()
+ {
+ return $this->parentTable;
+ }
+
+ /**
+ * Returns the Name of the table the index is in
+ */
+ public function getTableName()
+ {
+ return $this->parentTable->getName();
+ }
+
+ /**
+ * Adds a new column to an index.
+ * @param mixed $data Column or attributes from XML.
+ */
+ public function addColumn($data)
+ {
+ if ($data instanceof Column) {
+ $column = $data;
+ $this->indexColumns[] = $column->getName();
+ if ($column->getSize()) {
+ $this->indexColumnSizes[$column->getName()] = $column->getSize();
+ }
+ } else {
+ $attrib = $data;
+ $name = $attrib["name"];
+ $this->indexColumns[] = $name;
+ if (isset($attrib["size"])) {
+ $this->indexColumnSizes[$name] = $attrib["size"];
+ }
+ }
+ }
+
+ /**
+ * Sets array of columns to use for index.
+ *
+ * @param array $indexColumns Column[]
+ */
+ public function setColumns(array $indexColumns)
+ {
+ $this->indexColumns = array();
+ $this->indexColumnSizes = array();
+ foreach ($indexColumns as $col) {
+ $this->addColumn($col);
+ }
+ }
+
+ /**
+ * Whether there is a size for the specified column.
+ * @param string $name
+ * @return boolean
+ */
+ public function hasColumnSize($name)
+ {
+ return isset($this->indexColumnSizes[$name]);
+ }
+
+ /**
+ * Returns the size for the specified column, if given.
+ * @param string $name
+ * @return numeric The size or NULL
+ */
+ public function getColumnSize($name)
+ {
+ if (isset($this->indexColumnSizes[$name])) {
+ return $this->indexColumnSizes[$name];
+ }
+ return null; // just to be explicit
+ }
+
+ /**
+ * @see #getColumnList()
+ * @deprecated Use getColumnList() instead (which is not deprecated too!)
+ */
+ public function getIndexColumnList()
+ {
+ return $this->getColumnList();
+ }
+
+ /**
+ * Return a comma delimited string of the columns which compose this index.
+ * @deprecated because Column::makeList() is deprecated; use the array-returning getColumns() and DDLBuilder->getColumnList() instead instead.
+ */
+ public function getColumnList()
+ {
+ return Column::makeList($this->getColumns(), $this->getTable()->getDatabase()->getPlatform());
+ }
+
+ /**
+ * @see #getColumns()
+ * @deprecated Use getColumns() instead.
+ */
+ public function getIndexColumns()
+ {
+ return $this->getColumns();
+ }
+
+
+ /**
+ * Check whether the index has columns.
+ * @return boolean
+ */
+ public function hasColumns()
+ {
+ return count($this->indexColumns) > 0;
+ }
+
+ /**
+ * Return the list of local columns. You should not edit this list.
+ * @return array string[]
+ */
+ public function getColumns()
+ {
+ return $this->indexColumns;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $idxNode = $node->appendChild($doc->createElement('index'));
+ $idxNode->setAttribute('name', $this->getName());
+
+ foreach ($this->indexColumns as $colname) {
+ $idxColNode = $idxNode->appendChild($doc->createElement('index-column'));
+ $idxColNode->setAttribute('name', $colname);
+ }
+
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($idxNode);
+ }
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Inheritance.php b/3rd_party/php/propel/generator/lib/model/Inheritance.php
new file mode 100644
index 000000000..d5dc0336a
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Inheritance.php
@@ -0,0 +1,147 @@
+ (Propel)
+ * @author John McNally (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Inheritance extends XMLElement
+{
+
+ private $key;
+ private $className;
+ private $pkg;
+ private $ancestor;
+ private $parent;
+
+ /**
+ * Sets up the Inheritance object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->key = $this->getAttribute("key");
+ $this->className = $this->getAttribute("class");
+ $this->pkg = $this->getAttribute("package");
+ $this->ancestor = $this->getAttribute("extends");
+ }
+
+ /**
+ * Get the value of key.
+ * @return value of key.
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * Set the value of key.
+ * @param v Value to assign to key.
+ */
+ public function setKey($v)
+ {
+ $this->key = $v;
+ }
+
+ /**
+ * Get the value of parent.
+ * @return value of parent.
+ */
+ public function getColumn()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Set the value of parent.
+ * @param v Value to assign to parent.
+ */
+ public function setColumn(Column $v)
+ {
+ $this->parent = $v;
+ }
+
+ /**
+ * Get the value of className.
+ * @return value of className.
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ /**
+ * Set the value of className.
+ * @param v Value to assign to className.
+ */
+ public function setClassName($v)
+ {
+ $this->className = $v;
+ }
+
+ /**
+ * Get the value of package.
+ * @return value of package.
+ */
+ public function getPackage()
+ {
+ return $this->pkg;
+ }
+
+ /**
+ * Set the value of package.
+ * @param v Value to assign to package.
+ */
+ public function setPackage($v)
+ {
+ $this->pkg = $v;
+ }
+
+ /**
+ * Get the value of ancestor.
+ * @return value of ancestor.
+ */
+ public function getAncestor()
+ {
+ return $this->ancestor;
+ }
+
+ /**
+ * Set the value of ancestor.
+ * @param v Value to assign to ancestor.
+ */
+ public function setAncestor($v)
+ {
+ $this->ancestor = $v;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $inherNode = $node->appendChild($doc->createElement('inheritance'));
+ $inherNode->setAttribute('key', $this->key);
+ $inherNode->setAttribute('class', $this->className);
+
+ if ($this->ancestor !== null) {
+ $inherNode->setAttribute('extends', $this->ancestor);
+ }
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/NameFactory.php b/3rd_party/php/propel/generator/lib/model/NameFactory.php
new file mode 100644
index 000000000..dc78531eb
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/NameFactory.php
@@ -0,0 +1,77 @@
+ (Propel)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class NameFactory
+{
+
+ /**
+ * The class name of the PHP name generator.
+ */
+ const PHP_GENERATOR = 'PhpNameGenerator';
+
+ /**
+ * The fully qualified class name of the constraint name generator.
+ */
+ const CONSTRAINT_GENERATOR = 'ConstraintNameGenerator';
+
+ /**
+ * The single instance of this class.
+ */
+ private static $instance;
+
+ /**
+ * The cache of NameGenerator algorithms in use for
+ * name generation, keyed by fully qualified class name.
+ */
+ private static $algorithms = array();
+
+ /**
+ * Factory method which retrieves an instance of the named generator.
+ *
+ * @param name The fully qualified class name of the name
+ * generation algorithm to retrieve.
+ */
+ protected static function getAlgorithm($name)
+ {
+ if (!isset(self::$algorithms[$name])) {
+ self::$algorithms[$name] = new $name();
+ }
+ return self::$algorithms[$name];
+ }
+
+ /**
+ * Given a list of String objects, implements an
+ * algorithm which produces a name.
+ *
+ * @param string $algorithmName The fully qualified class name of the {@link NameGenerator}
+ * implementation to use to generate names.
+ * @param array $inputs Inputs used to generate a name.
+ * @return The generated name.
+ * @throws EngineException
+ */
+ public static function generateName($algorithmName, $inputs)
+ {
+ $algorithm = self::getAlgorithm($algorithmName);
+ return $algorithm->generateName($inputs);
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/NameGenerator.php b/3rd_party/php/propel/generator/lib/model/NameGenerator.php
new file mode 100644
index 000000000..78b9077c2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/NameGenerator.php
@@ -0,0 +1,73 @@
+ (Propel)
+ * @author Daniel Rall (Torque)
+ * @author Byron Foster (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+interface NameGenerator
+{
+
+ /**
+ * The character used by most implementations as the separator
+ * between name elements.
+ */
+ const STD_SEPARATOR_CHAR = '_';
+
+ /**
+ * Traditional method for converting schema table and column names
+ * to PHP names. The CONV_METHOD_XXX constants
+ * define how names for columns and tables in the database schema
+ * will be converted to PHP source names.
+ *
+ * @see PhpNameGenerator::underscoreMethod()
+ */
+ const CONV_METHOD_UNDERSCORE = "underscore";
+
+ /**
+ * Heavier method for converting schema table and column names
+ * to PHP names. Similar to {@link #CONV_METHOD_UNDERSCORE} but
+ * this one will pass only letters and numbers through and will
+ * use as separator any character that is not a letter or a number
+ * inside the string to be converted. The CONV_METHOD_XXX
+ * constants define how names for columns and tales in the
+ * database schema will be converted to PHP source names.
+ */
+ const CONV_METHOD_CLEAN = "clean";
+
+ /**
+ * Similar to {@link #CONV_METHOD_UNDERSCORE} except nothing is
+ * converted to lowercase.
+ *
+ * @see PhpNameGenerator::phpnameMethod()
+ */
+ const CONV_METHOD_PHPNAME = "phpname";
+
+ /**
+ * Specifies no modification when converting from a schema column
+ * or table name to a PHP name.
+ */
+ const CONV_METHOD_NOCHANGE = "nochange";
+
+ /**
+ * Given a list of String objects, implements an
+ * algorithm which produces a name.
+ *
+ * @param inputs Inputs used to generate a name.
+ * @return The generated name.
+ * @throws EngineException
+ */
+ public function generateName($inputs);
+}
diff --git a/3rd_party/php/propel/generator/lib/model/PhpNameGenerator.php b/3rd_party/php/propel/generator/lib/model/PhpNameGenerator.php
new file mode 100644
index 000000000..fc777ce02
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/PhpNameGenerator.php
@@ -0,0 +1,167 @@
+NameGenerator implementation for PHP-esque names.
+ *
+ * @author Hans Lellelid (Propel)
+ * @author Daniel Rall (Torque)
+ * @author Byron Foster (Torque)
+ * @author Bernd Goldschmidt
+ * @version $Revision: 1793 $
+ * @package propel.generator.model
+ */
+class PhpNameGenerator implements NameGenerator
+{
+
+ /**
+ * inputs should consist of two (three) elements, the
+ * original name of the database element and the method for
+ * generating the name.
+ * The optional third element may contain a prefix that will be
+ * stript from name prior to generate the resulting name.
+ * There are currently three methods:
+ * CONV_METHOD_NOCHANGE - xml names are converted
+ * directly to php names without modification.
+ * CONV_METHOD_UNDERSCORE will capitalize the first
+ * letter, remove underscores, and capitalize each letter before
+ * an underscore. All other letters are lowercased. "phpname"
+ * works the same as the CONV_METHOD_PHPNAME method
+ * but will not lowercase any characters.
+ *
+ * @param inputs list expected to contain two (optional: three) parameters,
+ * element 0 contains name to convert, element 1 contains method for conversion,
+ * optional element 2 contains prefix to be striped from name
+ * @return The generated name.
+ * @see NameGenerator
+ */
+ public function generateName($inputs)
+ {
+ $schemaName = $inputs[0];
+ $method = $inputs[1];
+
+ if (count($inputs)>2) {
+ $prefix = $inputs[2];
+ if ($prefix != '' && substr($schemaName, 0, strlen($prefix)) == $prefix) {
+ $schemaName = substr($schemaName, strlen($prefix));
+ }
+ }
+
+ $phpName = null;
+
+ switch ($method) {
+ case self::CONV_METHOD_CLEAN:
+ $phpName = $this->cleanMethod($schemaName);
+ break;
+ case self::CONV_METHOD_PHPNAME:
+ $phpName = $this->phpnameMethod($schemaName);
+ break;
+ case self::CONV_METHOD_NOCHANGE:
+ $phpName = $this->nochangeMethod($schemaName);
+ break;
+ case self::CONV_METHOD_UNDERSCORE:
+ default:
+ $phpName = $this->underscoreMethod($schemaName);
+ }
+
+ return $phpName;
+ }
+
+ /**
+ * Converts a database schema name to php object name by Camelization.
+ * Removes STD_SEPARATOR_CHAR, capitilizes first letter
+ * of name and each letter after the STD_SEPERATOR,
+ * converts the rest of the letters to lowercase.
+ *
+ * This method should be named camelizeMethod() for clarity
+ *
+ * my_CLASS_name -> MyClassName
+ *
+ * @param string $schemaName name to be converted.
+ * @return string Converted name.
+ * @see NameGenerator
+ * @see #underscoreMethod()
+ */
+ protected function underscoreMethod($schemaName)
+ {
+ $name = "";
+ $tok = strtok($schemaName, self::STD_SEPARATOR_CHAR);
+ while ($tok) {
+ $name .= ucfirst(strtolower($tok));
+ $tok = strtok(self::STD_SEPARATOR_CHAR);
+ }
+ return $name;
+ }
+
+ /**
+ * Converts a database schema name to php object name. Removes
+ * any character that is not a letter or a number and capitilizes
+ * first letter of the name, the first letter of each alphanumeric
+ * block and converts the rest of the letters to lowercase.
+ *
+ * T$NAMA$RFO_max => TNamaRfoMax
+ *
+ * @param string $schemaName name to be converted.
+ * @return string Converted name.
+ * @see NameGenerator
+ * @see #underscoreMethod()
+ */
+ protected function cleanMethod($schemaName)
+ {
+ $name = "";
+ $regexp = '/([a-z0-9]+)/i';
+ $matches = array();
+ if (preg_match_all($regexp, $schemaName, $matches)) {
+ foreach($matches[1] AS $tok) {
+ $name .= ucfirst(strtolower($tok));
+ }
+ } else {
+ return $schemaName;
+ }
+ return $name;
+ }
+
+ /**
+ * Converts a database schema name to php object name. Operates
+ * same as underscoreMethod but does not convert anything to
+ * lowercase.
+ *
+ * my_CLASS_name -> MyCLASSName
+ *
+ * @param string $schemaName name to be converted.
+ * @return string Converted name.
+ * @see NameGenerator
+ * @see #underscoreMethod(String)
+ */
+ protected function phpnameMethod($schemaName)
+ {
+ $name = "";
+ $tok = strtok($schemaName, self::STD_SEPARATOR_CHAR);
+ while ($tok !== false) {
+ $name .= ucfirst($tok);
+ $tok = strtok(self::STD_SEPARATOR_CHAR);
+ }
+ return $name;
+ }
+
+ /**
+ * Converts a database schema name to PHP object name. In this
+ * case no conversion is made.
+ *
+ * @param string $name name to be converted.
+ * @return string The name parameter, unchanged.
+ */
+ protected function nochangeMethod($name)
+ {
+ return $name;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/PropelTypes.php b/3rd_party/php/propel/generator/lib/model/PropelTypes.php
new file mode 100644
index 000000000..8a037f553
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/PropelTypes.php
@@ -0,0 +1,343 @@
+ (Propel)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class PropelTypes
+{
+
+ const CHAR = "CHAR";
+ const VARCHAR = "VARCHAR";
+ const LONGVARCHAR = "LONGVARCHAR";
+ const CLOB = "CLOB";
+ const CLOB_EMU = "CLOB_EMU";
+ const NUMERIC = "NUMERIC";
+ const DECIMAL = "DECIMAL";
+ const TINYINT = "TINYINT";
+ const SMALLINT = "SMALLINT";
+ const INTEGER = "INTEGER";
+ const BIGINT = "BIGINT";
+ const REAL = "REAL";
+ const FLOAT = "FLOAT";
+ const DOUBLE = "DOUBLE";
+ const BINARY = "BINARY";
+ const VARBINARY = "VARBINARY";
+ const LONGVARBINARY = "LONGVARBINARY";
+ const BLOB = "BLOB";
+ const DATE = "DATE";
+ const TIME = "TIME";
+ const TIMESTAMP = "TIMESTAMP";
+ const BU_DATE = "BU_DATE";
+ const BU_TIMESTAMP = "BU_TIMESTAMP";
+ const BOOLEAN = "BOOLEAN";
+ const BOOLEAN_EMU = "BOOLEAN_EMU";
+
+ private static $TEXT_TYPES = array(
+ self::CHAR, self::VARCHAR, self::LONGVARCHAR, self::CLOB, self::DATE, self::TIME, self::TIMESTAMP, self::BU_DATE, self::BU_TIMESTAMP
+ );
+
+ private static $LOB_TYPES = array(
+ self::VARBINARY, self::LONGVARBINARY, self::BLOB
+ );
+
+ private static $TEMPORAL_TYPES = array(
+ self::DATE, self::TIME, self::TIMESTAMP, self::BU_DATE, self::BU_TIMESTAMP
+ );
+
+ private static $NUMERIC_TYPES = array(
+ self::SMALLINT, self::TINYINT, self::INTEGER, self::BIGINT, self::FLOAT, self::DOUBLE, self::NUMERIC, self::DECIMAL, self::REAL
+ );
+
+ private static $BOOLEAN_TYPES = array(
+ self::BOOLEAN, self::BOOLEAN_EMU
+ );
+
+ const CHAR_NATIVE_TYPE = "string";
+ const VARCHAR_NATIVE_TYPE = "string";
+ const LONGVARCHAR_NATIVE_TYPE = "string";
+ const CLOB_NATIVE_TYPE = "string";
+ const CLOB_EMU_NATIVE_TYPE = "resource";
+ const NUMERIC_NATIVE_TYPE = "string";
+ const DECIMAL_NATIVE_TYPE = "string";
+ const TINYINT_NATIVE_TYPE = "int";
+ const SMALLINT_NATIVE_TYPE = "int";
+ const INTEGER_NATIVE_TYPE = "int";
+ const BIGINT_NATIVE_TYPE = "string";
+ const REAL_NATIVE_TYPE = "double";
+ const FLOAT_NATIVE_TYPE = "double";
+ const DOUBLE_NATIVE_TYPE = "double";
+ const BINARY_NATIVE_TYPE = "string";
+ const VARBINARY_NATIVE_TYPE = "string";
+ const LONGVARBINARY_NATIVE_TYPE = "string";
+ const BLOB_NATIVE_TYPE = "resource";
+ const BU_DATE_NATIVE_TYPE = "string";
+ const DATE_NATIVE_TYPE = "string";
+ const TIME_NATIVE_TYPE = "string";
+ const TIMESTAMP_NATIVE_TYPE = "string";
+ const BU_TIMESTAMP_NATIVE_TYPE = "string";
+ const BOOLEAN_NATIVE_TYPE = "boolean";
+ const BOOLEAN_EMU_NATIVE_TYPE = "boolean";
+
+ /**
+ * Mapping between Propel types and PHP native types.
+ *
+ * @var array
+ */
+ private static $propelToPHPNativeMap = array(
+ self::CHAR => self::CHAR_NATIVE_TYPE,
+ self::VARCHAR => self::VARCHAR_NATIVE_TYPE,
+ self::LONGVARCHAR => self::LONGVARCHAR_NATIVE_TYPE,
+ self::CLOB => self::CLOB_NATIVE_TYPE,
+ self::CLOB_EMU => self::CLOB_EMU_NATIVE_TYPE,
+ self::NUMERIC => self::NUMERIC_NATIVE_TYPE,
+ self::DECIMAL => self::DECIMAL_NATIVE_TYPE,
+ self::TINYINT => self::TINYINT_NATIVE_TYPE,
+ self::SMALLINT => self::SMALLINT_NATIVE_TYPE,
+ self::INTEGER => self::INTEGER_NATIVE_TYPE,
+ self::BIGINT => self::BIGINT_NATIVE_TYPE,
+ self::REAL => self::REAL_NATIVE_TYPE,
+ self::FLOAT => self::FLOAT_NATIVE_TYPE,
+ self::DOUBLE => self::DOUBLE_NATIVE_TYPE,
+ self::BINARY => self::BINARY_NATIVE_TYPE,
+ self::VARBINARY => self::VARBINARY_NATIVE_TYPE,
+ self::LONGVARBINARY => self::LONGVARBINARY_NATIVE_TYPE,
+ self::BLOB => self::BLOB_NATIVE_TYPE,
+ self::DATE => self::DATE_NATIVE_TYPE,
+ self::BU_DATE => self::BU_DATE_NATIVE_TYPE,
+ self::TIME => self::TIME_NATIVE_TYPE,
+ self::TIMESTAMP => self::TIMESTAMP_NATIVE_TYPE,
+ self::BU_TIMESTAMP => self::BU_TIMESTAMP_NATIVE_TYPE,
+ self::BOOLEAN => self::BOOLEAN_NATIVE_TYPE,
+ self::BOOLEAN_EMU => self::BOOLEAN_EMU_NATIVE_TYPE,
+ );
+
+ /**
+ * Mapping between Propel types and Creole types (for rev-eng task)
+ *
+ * @var array
+ */
+ private static $propelTypeToCreoleTypeMap = array(
+
+ self::CHAR => self::CHAR,
+ self::VARCHAR => self::VARCHAR,
+ self::LONGVARCHAR => self::LONGVARCHAR,
+ self::CLOB => self::CLOB,
+ self::NUMERIC => self::NUMERIC,
+ self::DECIMAL => self::DECIMAL,
+ self::TINYINT => self::TINYINT,
+ self::SMALLINT => self::SMALLINT,
+ self::INTEGER => self::INTEGER,
+ self::BIGINT => self::BIGINT,
+ self::REAL => self::REAL,
+ self::FLOAT => self::FLOAT,
+ self::DOUBLE => self::DOUBLE,
+ self::BINARY => self::BINARY,
+ self::VARBINARY => self::VARBINARY,
+ self::LONGVARBINARY => self::LONGVARBINARY,
+ self::BLOB => self::BLOB,
+ self::DATE => self::DATE,
+ self::TIME => self::TIME,
+ self::TIMESTAMP => self::TIMESTAMP,
+ self::BOOLEAN => self::BOOLEAN,
+ self::BOOLEAN_EMU => self::BOOLEAN_EMU,
+
+ // These are pre-epoch dates, which we need to map to String type
+ // since they cannot be properly handled using strtotime() -- or even numeric
+ // timestamps on Windows.
+ self::BU_DATE => self::VARCHAR,
+ self::BU_TIMESTAMP => self::VARCHAR,
+
+ );
+
+ /**
+ * Mapping between Propel types and PDO type contants (for prepared statement setting).
+ *
+ * @var array
+ */
+ private static $propelTypeToPDOTypeMap = array(
+ self::CHAR => PDO::PARAM_STR,
+ self::VARCHAR => PDO::PARAM_STR,
+ self::LONGVARCHAR => PDO::PARAM_STR,
+ self::CLOB => PDO::PARAM_STR,
+ self::CLOB_EMU => PDO::PARAM_STR,
+ self::NUMERIC => PDO::PARAM_INT,
+ self::DECIMAL => PDO::PARAM_STR,
+ self::TINYINT => PDO::PARAM_INT,
+ self::SMALLINT => PDO::PARAM_INT,
+ self::INTEGER => PDO::PARAM_INT,
+ self::BIGINT => PDO::PARAM_INT,
+ self::REAL => PDO::PARAM_STR,
+ self::FLOAT => PDO::PARAM_STR,
+ self::DOUBLE => PDO::PARAM_STR,
+ self::BINARY => PDO::PARAM_STR,
+ self::VARBINARY => PDO::PARAM_LOB,
+ self::LONGVARBINARY => PDO::PARAM_LOB,
+ self::BLOB => PDO::PARAM_LOB,
+ self::DATE => PDO::PARAM_STR,
+ self::TIME => PDO::PARAM_STR,
+ self::TIMESTAMP => PDO::PARAM_STR,
+ self::BOOLEAN => PDO::PARAM_BOOL,
+ self::BOOLEAN_EMU => PDO::PARAM_INT,
+
+ // These are pre-epoch dates, which we need to map to String type
+ // since they cannot be properly handled using strtotime() -- or even numeric
+ // timestamps on Windows.
+ self::BU_DATE => PDO::PARAM_STR,
+ self::BU_TIMESTAMP => PDO::PARAM_STR,
+ );
+
+ /**
+ * Return native PHP type which corresponds to the
+ * Creole type provided. Use in the base object class generation.
+ *
+ * @param $propelType The Propel type name.
+ * @return string Name of the native PHP type
+ */
+ public static function getPhpNative($propelType)
+ {
+ return self::$propelToPHPNativeMap[$propelType];
+ }
+
+ /**
+ * Returns the correct Creole type _name_ for propel added types
+ *
+ * @param $type the propel added type.
+ * @return string Name of the the correct Creole type (e.g. "VARCHAR").
+ */
+ public static function getCreoleType($type)
+ {
+ return self::$propelTypeToCreoleTypeMap[$type];
+ }
+
+ /**
+ * Resturns the PDO type (PDO::PARAM_* constant) value.
+ * @return int
+ */
+ public static function getPDOType($type)
+ {
+ return self::$propelTypeToPDOTypeMap[$type];
+ }
+
+ /**
+ * Returns Propel type constant corresponding to Creole type code.
+ * Used but Propel Creole task.
+ *
+ * @param int $sqlType The Creole SQL type constant.
+ * @return string The Propel type to use or NULL if none found.
+ */
+ public static function getPropelType($sqlType)
+ {
+ if (isset(self::$creoleToPropelTypeMap[$sqlType])) {
+ return self::$creoleToPropelTypeMap[$sqlType];
+ }
+ }
+
+ /**
+ * Get array of Propel types.
+ *
+ * @return array string[]
+ */
+ public static function getPropelTypes()
+ {
+ return array_keys(self::$propelTypeToCreoleTypeMap);
+ }
+
+ /**
+ * Whether passed type is a temporal (date/time/timestamp) type.
+ *
+ * @param string $type Propel type
+ * @return boolean
+ */
+ public static function isTemporalType($type)
+ {
+ return in_array($type, self::$TEMPORAL_TYPES);
+ }
+
+ /**
+ * Returns true if values for the type need to be quoted.
+ *
+ * @param string $type The Propel type to check.
+ * @return boolean True if values for the type need to be quoted.
+ */
+ public static function isTextType($type)
+ {
+ return in_array($type, self::$TEXT_TYPES);
+ }
+
+ /**
+ * Returns true if values for the type are numeric.
+ *
+ * @param string $type The Propel type to check.
+ * @return boolean True if values for the type need to be quoted.
+ */
+ public static function isNumericType($type)
+ {
+ return in_array($type, self::$NUMERIC_TYPES);
+ }
+
+ /**
+ * Returns true if values for the type are boolean.
+ *
+ * @param string $type The Propel type to check.
+ * @return boolean True if values for the type need to be quoted.
+ */
+ public static function isBooleanType($type)
+ {
+ return in_array($type, self::$BOOLEAN_TYPES);
+ }
+
+ /**
+ * Returns true if type is a LOB type (i.e. would be handled by Blob/Clob class).
+ * @param string $type Propel type to check.
+ * @return boolean
+ */
+ public static function isLobType($type)
+ {
+ return in_array($type, self::$LOB_TYPES);
+ }
+
+ /**
+ * Convenience method to indicate whether a passed-in PHP type is a primitive.
+ *
+ * @param string $phpType The PHP type to check
+ * @return boolean Whether the PHP type is a primitive (string, int, boolean, float)
+ */
+ public static function isPhpPrimitiveType($phpType)
+ {
+ return in_array($phpType, array("boolean", "int", "double", "float", "string"));
+ }
+
+ /**
+ * Convenience method to indicate whether a passed-in PHP type is a numeric primitive.
+ *
+ * @param string $phpType The PHP type to check
+ * @return boolean Whether the PHP type is a primitive (string, int, boolean, float)
+ */
+ public static function isPhpPrimitiveNumericType($phpType)
+ {
+ return in_array($phpType, array("boolean", "int", "double", "float"));
+ }
+
+ /**
+ * Convenience method to indicate whether a passed-in PHP type is an object.
+ *
+ * @param string $phpType The PHP type to check
+ * @return boolean Whether the PHP type is a primitive (string, int, boolean, float)
+ */
+ public static function isPhpObjectType($phpType)
+ {
+ return (!self::isPhpPrimitiveType($phpType) && !in_array($phpType, array("resource", "array")));
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Rule.php b/3rd_party/php/propel/generator/lib/model/Rule.php
new file mode 100644
index 000000000..d69e4e76b
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Rule.php
@@ -0,0 +1,194 @@
+ (Propel)
+ * @author John McNally (Intake)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Rule extends XMLElement
+{
+
+ private $name;
+ private $value;
+ private $message;
+ private $validator;
+ private $classname;
+
+ /**
+ * Sets up the Rule object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->name = $this->getAttribute("name");
+ $this->value = $this->getAttribute("value");
+ $this->classname = $this->getAttribute("class");
+
+ /*
+ * Set some default values if they are not specified.
+ * This is escpecially useful for maxLength; the size
+ * is already known by the column and this way it is
+ * not necessary to manage the same size two times.
+ *
+ * Currently there is only one such supported default:
+ * - maxLength value = column max length
+ * (this default cannot be easily set at runtime w/o changing
+ * design of class system in undesired ways)
+ */
+ if ($this->value === null) {
+ switch($this->name) {
+ case 'maxLength':
+ $this->value = $this->validator->getColumn()->getSize();
+ break;
+ }
+ }
+
+ $this->message = $this->getAttribute("message");
+ }
+
+ /**
+ * Sets the owning validator for this rule.
+ * @param Validator $validator
+ * @see Validator::addRule()
+ */
+ public function setValidator(Validator $validator)
+ {
+ $this->validator = $validator;
+ }
+
+ /**
+ * Gets the owning validator for this rule.
+ * @return Validator
+ */
+ public function getValidator()
+ {
+ return $this->validator;
+ }
+
+ /**
+ * Sets the dot-path name of class to use for rule.
+ * If no class is specified in XML, then a classname will
+ * be built based on the 'name' attrib.
+ * @param string $classname dot-path classname (e.g. myapp.propel.MyValidator)
+ */
+ public function setClass($classname)
+ {
+ $this->classname = $classname;
+ }
+
+ /**
+ * Gets the dot-path name of class to use for rule.
+ * If no class was specified, this method will build a default classname
+ * based on the 'name' attribute. E.g. 'maxLength' -> 'propel.validator.MaxLengthValidator'
+ * @return string dot-path classname (e.g. myapp.propel.MyValidator)
+ */
+ public function getClass()
+ {
+ if ($this->classname === null && $this->name !== null) {
+ return "propel.validator." . ucfirst($this->name) . "Validator";
+ }
+ return $this->classname;
+ }
+
+ /**
+ * Sets the name of the validator for this rule.
+ * This name is used to build the classname if none was specified.
+ * @param string $name Validator name for this rule (e.g. "maxLength", "required").
+ * @see getClass()
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Gets the name of the validator for this rule.
+ * @return string Validator name for this rule (e.g. "maxLength", "required").
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Sets the value parameter for this validator rule.
+ * Note: not all validators need a value parameter (e.g. 'required' validator
+ * does not).
+ * @param string $value
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * Gets the value parameter for this validator rule.
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Sets the message that will be displayed to the user if validation fails.
+ * This message may be a Gettext msgid (if translation="gettext") or some other
+ * id for an alternative not-yet-supported translation system. It may also
+ * be a simple, single-language string.
+ * @param string $message
+ * @see setTranslation()
+ */
+ public function setMessage($message)
+ {
+ $this->message = $message;
+ }
+
+ /**
+ * Gets the message that will be displayed to the user if validation fails.
+ * This message may be a Gettext msgid (if translation="gettext") or some other
+ * id for an alternative not-yet-supported translation system. It may also
+ * be a simple, single-language string.
+ * @return string
+ * @see setTranslation()
+ */
+ public function getMessage()
+ {
+ $message = str_replace('${value}', $this->getValue(), $this->message);
+ return $message;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $ruleNode = $node->appendChild($doc->createElement('rule'));
+ $ruleNode->setAttribute('name', $this->getName());
+
+ if ($this->getValue() !== null) {
+ $ruleNode->setAttribute('value', $this->getValue());
+ }
+
+ if ($this->classname !== null) {
+ $ruleNode->setAttribute('class', $this->getClass());
+ }
+
+ $ruleNode->setAttribute('message', $this->getMessage());
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Table.php b/3rd_party/php/propel/generator/lib/model/Table.php
new file mode 100644
index 000000000..9198e27b1
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Table.php
@@ -0,0 +1,1579 @@
+ (Propel)
+ * @author Leon Messerschmidt (Torque)
+ * @author Jason van Zyl (Torque)
+ * @author Martin Poeschl (Torque)
+ * @author John McNally (Torque)
+ * @author Daniel Rall (Torque)
+ * @author Byron Foster (Torque)
+ * @version $Revision: 1802 $
+ * @package propel.generator.model
+ */
+class Table extends XMLElement implements IDMethod
+{
+
+ /**
+ * Enables some debug printing.
+ */
+ const DEBUG = false;
+
+ /**
+ * Columns for this table.
+ *
+ * @var array Column[]
+ */
+ private $columnList = array();
+
+ /**
+ * Validators for this table.
+ *
+ * @var array Validator[]
+ */
+ private $validatorList = array();
+
+ /**
+ * Foreign keys for this table.
+ *
+ * @var array ForeignKey[]
+ */
+ private $foreignKeys = array();
+
+ /**
+ * Indexes for this table.
+ *
+ * @var array Index[]
+ */
+ private $indices = array();
+
+ /**
+ * Unique indexes for this table.
+ *
+ * @var array Unique[]
+ */
+ private $unices = array();
+
+ /**
+ * Any parameters for the ID method (currently supports changing sequence name).
+ *
+ * @var array
+ */
+ private $idMethodParameters = array();
+
+ /**
+ * Table name.
+ *
+ * @var string
+ */
+ private $name;
+
+ /**
+ * Table description.
+ *
+ * @var string
+ */
+ private $description;
+
+ /**
+ * phpName for the table.
+ *
+ * @var string
+ */
+ private $phpName;
+
+ /**
+ * Namespace for the generated OM.
+ *
+ * @var string
+ */
+ protected $namespace;
+
+ /**
+ * ID method for the table (e.g. IDMethod::NATIVE, IDMethod::NONE).
+ *
+ * @var string
+ */
+ private $idMethod;
+
+ /**
+ * Wether an INSERT with set PK is allowed on tables with IDMethod::NATIVE
+ *
+ * @var boolean
+ */
+ private $allowPkInsert;
+
+ /**
+ * Strategry to use for converting column name to phpName.
+ *
+ * @var string
+ */
+ private $phpNamingMethod;
+
+ /**
+ * The Database that this table belongs to.
+ *
+ * @var Database
+ */
+ private $database;
+
+ /**
+ * Foreign Keys that refer to this table.
+ *
+ * @var array ForeignKey[]
+ */
+ private $referrers = array();
+
+ /**
+ * Names of foreign tables.
+ *
+ * @var array string[]
+ */
+ private $foreignTableNames;
+
+ /**
+ * Whether this table contains a foreign primary key.
+ *
+ * @var boolean
+ */
+ private $containsForeignPK;
+
+ /**
+ * The inheritance column for this table (if any).
+ *
+ * @var Column
+ */
+ private $inheritanceColumn;
+
+ /**
+ * Whether to skip generation of SQL for this table.
+ *
+ * @var boolean
+ */
+ private $skipSql;
+
+ /**
+ * Whether this table is "read-only".
+ *
+ * @var boolean
+ */
+ private $readOnly;
+
+ /**
+ * Whether this table should result in abstract OM classes.
+ *
+ * @var boolean
+ */
+ private $abstractValue;
+
+ /**
+ * Whether this table is an alias for another table.
+ *
+ * @var string
+ */
+ private $alias;
+
+ /**
+ * The interface that the generated "object" class should implement.
+ *
+ * @var string
+ */
+ private $enterface;
+
+ /**
+ * The package for the generated OM.
+ *
+ * @var string
+ */
+ private $pkg;
+
+ /**
+ * The base class to extend for the generated "object" class.
+ *
+ * @var string
+ */
+ private $baseClass;
+
+ /**
+ * The base peer class to extend for generated "peer" class.
+ *
+ * @var string
+ */
+ private $basePeer;
+
+ /**
+ * Map of columns by name.
+ *
+ * @var array
+ */
+ private $columnsByName = array();
+
+ /**
+ * Map of columns by phpName.
+ *
+ * @var array
+ */
+ private $columnsByPhpName = array();
+
+ /**
+ * Whether this table needs to use transactions in Postgres.
+ *
+ * @var string
+ * @deprecated
+ */
+ private $needsTransactionInPostgres;
+
+ /**
+ * Whether to perform additional indexing on this table.
+ *
+ * @var boolean
+ */
+ private $heavyIndexing;
+
+ /**
+ * Whether this table is for reference only.
+ *
+ * @var boolean
+ */
+ private $forReferenceOnly;
+
+ /**
+ * The tree mode (nested set, etc.) implemented by this table.
+ *
+ * @var string
+ */
+ private $treeMode;
+
+ /**
+ * Whether to reload the rows in this table after insert.
+ *
+ * @var boolean
+ */
+ private $reloadOnInsert;
+
+ /**
+ * Whether to reload the rows in this table after update.
+ *
+ * @var boolean
+ */
+ private $reloadOnUpdate;
+
+ /**
+ * List of behaviors registered for this table
+ *
+ * @var array
+ */
+ protected $behaviors = array();
+
+ /**
+ * Whether this table is a cross-reference table for a many-to-many relationship
+ *
+ * @var boolean
+ */
+ protected $isCrossRef = false;
+
+ /**
+ * Constructs a table object with a name
+ *
+ * @param string $name table name
+ */
+ public function __construct($name = null)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets up the Rule object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ public function setupObject()
+ {
+ $this->name = $this->getDatabase()->getTablePrefix() . $this->getAttribute("name");
+ // retrieves the method for converting from specified name to a PHP name.
+ $this->phpNamingMethod = $this->getAttribute("phpNamingMethod", $this->getDatabase()->getDefaultPhpNamingMethod());
+ $this->phpName = $this->getAttribute("phpName", $this->buildPhpName($this->getAttribute('name')));
+
+ $namespace = $this->getAttribute("namespace", '');
+ $package = $this->getAttribute("package");
+ if ($namespace && !$package && $this->getDatabase()->getBuildProperty('namespaceAutoPackage')) {
+ $package = str_replace('\\', '.', $namespace);
+ }
+ $this->namespace = $namespace;
+ $this->pkg = $package;
+
+ $this->namespace = $this->getAttribute("namespace");
+ $this->idMethod = $this->getAttribute("idMethod", $this->getDatabase()->getDefaultIdMethod());
+ $this->allowPkInsert = $this->booleanValue($this->getAttribute("allowPkInsert"));
+
+
+ $this->skipSql = $this->booleanValue($this->getAttribute("skipSql"));
+ $this->readOnly = $this->booleanValue($this->getAttribute("readOnly"));
+
+ $this->abstractValue = $this->booleanValue($this->getAttribute("abstract"));
+ $this->baseClass = $this->getAttribute("baseClass");
+ $this->basePeer = $this->getAttribute("basePeer");
+ $this->alias = $this->getAttribute("alias");
+
+ $this->heavyIndexing = ( $this->booleanValue($this->getAttribute("heavyIndexing"))
+ || ("false" !== $this->getAttribute("heavyIndexing")
+ && $this->getDatabase()->isHeavyIndexing() ) );
+ $this->description = $this->getAttribute("description");
+ $this->enterface = $this->getAttribute("interface"); // sic ('interface' is reserved word)
+ $this->treeMode = $this->getAttribute("treeMode");
+
+ $this->reloadOnInsert = $this->booleanValue($this->getAttribute("reloadOnInsert"));
+ $this->reloadOnUpdate = $this->booleanValue($this->getAttribute("reloadOnUpdate"));
+ $this->isCrossRef = $this->getAttribute("isCrossRef", false);
+ }
+
+ /**
+ *
A hook for the SAX XML parser to call when this table has
+ * been fully loaded from the XML, and all nested elements have
+ * been processed.
+ *
+ *
Performs heavy indexing and naming of elements which weren't
+ * provided with a name.
+ */
+ public function doFinalInitialization()
+ {
+ // Heavy indexing must wait until after all columns composing
+ // a table's primary key have been parsed.
+ if ($this->heavyIndexing) {
+ $this->doHeavyIndexing();
+ }
+
+ // Name any indices which are missing a name using the
+ // appropriate algorithm.
+ $this->doNaming();
+
+ // execute behavior table modifiers
+ foreach ($this->getBehaviors() as $behavior)
+ {
+ if (!$behavior->isTableModified()) {
+ $behavior->getTableModifier()->modifyTable();
+ $behavior->setTableModified(true);
+ }
+ }
+
+ // if idMethod is "native" and in fact there are no autoIncrement
+ // columns in the table, then change it to "none"
+ $anyAutoInc = false;
+ foreach ($this->getColumns() as $col) {
+ if ($col->isAutoIncrement()) {
+ $anyAutoInc = true;
+ }
+ }
+ if ($this->getIdMethod() === IDMethod::NATIVE && !$anyAutoInc) {
+ $this->setIdMethod(IDMethod::NO_ID_METHOD);
+ }
+
+ // If there is no PK, then throw an error. Propel 1.3 requires primary keys.
+ $pk = $this->getPrimaryKey();
+ if (empty($pk)) {
+ throw new EngineException("Table '".$this->getName()."' does not have a primary key defined. Propel requires all tables to have a primary key.");
+ }
+
+ }
+
+ /**
+ *
Adds extra indices for multi-part primary key columns.
+ *
+ *
For databases like MySQL, values in a where clause much
+ * match key part order from the left to right. So, in the key
+ * definition PRIMARY KEY (FOO_ID, BAR_ID),
+ * FOO_IDmust be the first element used in
+ * the where clause of the SQL query used against
+ * this table for the primary key index to be used. This feature
+ * could cause problems under MySQL with heavily indexed tables,
+ * as MySQL currently only supports 16 indices per table (i.e. it
+ * might cause too many indices to be created).
+ *
+ *
See the
+ * manual for a better description of why heavy indexing is
+ * useful for quickly searchable database tables.
+ */
+ private function doHeavyIndexing()
+ {
+ if (self::DEBUG) {
+ print("doHeavyIndex() called on table " . $this->name."\n");
+ }
+
+ $pk = $this->getPrimaryKey();
+ $size = count($pk);
+
+ // We start at an offset of 1 because the entire column
+ // list is generally implicitly indexed by the fact that
+ // it's a primary key.
+ for ($i=1; $i < $size; $i++) {
+ $idx = new Index();
+ $idx->setColumns(array_slice($pk, $i, $size));
+ $this->addIndex($idx);
+ }
+ }
+
+ /**
+ * Names composing objects which haven't yet been named. This
+ * currently consists of foreign-key and index entities.
+ */
+ public function doNaming() {
+
+ // Assure names are unique across all databases.
+ try {
+ for ($i=0, $size = count($this->foreignKeys); $i < $size; $i++) {
+ $fk = $this->foreignKeys[$i];
+ $name = $fk->getName();
+ if (empty($name)) {
+ $name = $this->acquireConstraintName("FK", $i + 1);
+ $fk->setName($name);
+ }
+ }
+
+ for ($i = 0, $size = count($this->indices); $i < $size; $i++) {
+ $index = $this->indices[$i];
+ $name = $index->getName();
+ if (empty($name)) {
+ $name = $this->acquireConstraintName("I", $i + 1);
+ $index->setName($name);
+ }
+ }
+
+ for ($i = 0, $size = count($this->unices); $i < $size; $i++) {
+ $index = $this->unices[$i];
+ $name = $index->getName();
+ if (empty($name)) {
+ $name = $this->acquireConstraintName("U", $i + 1);
+ $index->setName($name);
+ }
+ }
+
+ // NOTE: Most RDBMSes can apparently name unique column
+ // constraints/indices themselves (using MySQL and Oracle
+ // as test cases), so we'll assume that we needn't add an
+ // entry to the system name list for these.
+ } catch (EngineException $nameAlreadyInUse) {
+ print $nameAlreadyInUse->getMessage() . "\n";
+ print $nameAlreadyInUse->getTraceAsString();
+ }
+ }
+
+ /**
+ * Macro to a constraint name.
+ *
+ * @param nameType constraint type
+ * @param nbr unique number for this constraint type
+ * @return unique name for constraint
+ * @throws EngineException
+ */
+ private function acquireConstraintName($nameType, $nbr)
+ {
+ $inputs = array();
+ $inputs[] = $this->getDatabase();
+ $inputs[] = $this->getName();
+ $inputs[] = $nameType;
+ $inputs[] = $nbr;
+ return NameFactory::generateName(NameFactory::CONSTRAINT_GENERATOR, $inputs);
+ }
+
+ /**
+ * Gets the value of base class for classes produced from this table.
+ *
+ * @return The base class for classes produced from this table.
+ */
+ public function getBaseClass()
+ {
+ if ($this->isAlias() && $this->baseClass === null) {
+ return $this->alias;
+ } elseif ($this->baseClass === null) {
+ return $this->getDatabase()->getBaseClass();
+ } else {
+ return $this->baseClass;
+ }
+ }
+
+ /**
+ * Set the value of baseClass.
+ * @param v Value to assign to baseClass.
+ */
+ public function setBaseClass($v)
+ {
+ $this->baseClass = $v;
+ }
+
+ /**
+ * Get the value of basePeer.
+ * @return value of basePeer.
+ */
+ public function getBasePeer()
+ {
+ if ($this->isAlias() && $this->basePeer === null) {
+ return $this->alias . "Peer";
+ } elseif ($this->basePeer === null) {
+ return $this->getDatabase()->getBasePeer();
+ } else {
+ return $this->basePeer;
+ }
+ }
+
+ /**
+ * Set the value of basePeer.
+ * @param v Value to assign to basePeer.
+ */
+ public function setBasePeer($v)
+ {
+ $this->basePeer = $v;
+ }
+
+ /**
+ * A utility function to create a new column from attrib and add it to this
+ * table.
+ *
+ * @param $coldata xml attributes or Column class for the column to add
+ * @return the added column
+ */
+ public function addColumn($data)
+ {
+ if ($data instanceof Column) {
+ $col = $data;
+ $col->setTable($this);
+ if ($col->isInheritance()) {
+ $this->inheritanceColumn = $col;
+ }
+ if (isset($this->columnsByName[$col->getName()])) {
+ throw new EngineException('Duplicate column declared: ' . $col->getName());
+ }
+ $this->columnList[] = $col;
+ $this->columnsByName[$col->getName()] = $col;
+ $this->columnsByPhpName[$col->getPhpName()] = $col;
+ $col->setPosition(count($this->columnList));
+ $this->needsTransactionInPostgres |= $col->requiresTransactionInPostgres();
+ return $col;
+ } else {
+ $col = new Column();
+ $col->setTable($this);
+ $col->loadFromXML($data);
+ return $this->addColumn($col); // call self w/ different param
+ }
+ }
+
+ /**
+ * Add a validator to this table.
+ *
+ * Supports two signatures:
+ * - addValidator(Validator $validator)
+ * - addValidator(array $attribs)
+ *
+ * @param mixed $data Validator object or XML attribs (array) from element.
+ * @return Validator The added Validator.
+ * @throws EngineException
+ */
+ public function addValidator($data)
+ {
+ if ($data instanceof Validator) {
+ $validator = $data;
+ $col = $this->getColumn($validator->getColumnName());
+ if ($col == null) {
+ throw new EngineException("Failed adding validator to table '" . $this->getName() .
+ "': column '" . $validator->getColumnName() . "' does not exist !");
+ }
+ $validator->setColumn($col);
+ $validator->setTable($this);
+ $this->validatorList[] = $validator;
+ return $validator;
+ } else {
+ $validator = new Validator();
+ $validator->setTable($this);
+ $validator->loadFromXML($data);
+ return $this->addValidator($validator);
+ }
+ }
+
+ /**
+ * A utility function to create a new foreign key
+ * from attrib and add it to this table.
+ */
+ public function addForeignKey($fkdata)
+ {
+ if ($fkdata instanceof ForeignKey) {
+ $fk = $fkdata;
+ $fk->setTable($this);
+ $this->foreignKeys[] = $fk;
+
+ if ($this->foreignTableNames === null) {
+ $this->foreignTableNames = array();
+ }
+ if (!in_array($fk->getForeignTableName(), $this->foreignTableNames)) {
+ $this->foreignTableNames[] = $fk->getForeignTableName();
+ }
+ return $fk;
+ } else {
+ $fk = new ForeignKey();
+ $fk->setTable($this);
+ $fk->loadFromXML($fkdata);
+ return $this->addForeignKey($fk);
+ }
+ }
+
+ /**
+ * Gets the column that subclasses of the class representing this
+ * table can be produced from.
+ * @return Column
+ */
+ public function getChildrenColumn()
+ {
+ return $this->inheritanceColumn;
+ }
+
+ /**
+ * Get the subclasses that can be created from this table.
+ * @return array string[] Class names
+ */
+ public function getChildrenNames()
+ {
+ if ($this->inheritanceColumn === null
+ || !$this->inheritanceColumn->isEnumeratedClasses()) {
+ return null;
+ }
+ $children = $this->inheritanceColumn->getChildren();
+ $names = array();
+ for ($i = 0, $size=count($children); $i < $size; $i++) {
+ $names[] = get_class($children[$i]);
+ }
+ return $names;
+ }
+
+ /**
+ * Adds the foreign key from another table that refers to this table.
+ */
+ public function addReferrer(ForeignKey $fk)
+ {
+ if ($this->referrers === null) {
+ $this->referrers = array();
+ }
+ $this->referrers[] = $fk;
+ }
+
+ /**
+ * Get list of references to this table.
+ */
+ public function getReferrers()
+ {
+ return $this->referrers;
+ }
+
+ public function getCrossFks()
+ {
+ $crossFks = array();
+ foreach ($this->getReferrers() as $refFK) {
+ if ($refFK->getTable()->getIsCrossRef()) {
+ foreach ($refFK->getOtherFks() as $crossFK) {
+ $crossFks[]= array($refFK, $crossFK);
+ }
+ }
+ }
+ return $crossFks;
+ }
+
+ /**
+ * Set whether this table contains a foreign PK
+ */
+ public function setContainsForeignPK($b)
+ {
+ $this->containsForeignPK = (boolean) $b;
+ }
+
+ /**
+ * Determine if this table contains a foreign PK
+ */
+ public function getContainsForeignPK()
+ {
+ return $this->containsForeignPK;
+ }
+
+ /**
+ * A list of tables referenced by foreign keys in this table
+ */
+ public function getForeignTableNames()
+ {
+ if ($this->foreignTableNames === null) {
+ $this->foreignTableNames = array();
+ }
+ return $this->foreignTableNames;
+ }
+
+ /**
+ * Return true if the column requires a transaction in Postgres
+ */
+ public function requiresTransactionInPostgres()
+ {
+ return $this->needsTransactionInPostgres;
+ }
+
+ /**
+ * A utility function to create a new id method parameter
+ * from attrib or object and add it to this table.
+ */
+ public function addIdMethodParameter($impdata)
+ {
+ if ($impdata instanceof IdMethodParameter) {
+ $imp = $impdata;
+ $imp->setTable($this);
+ if ($this->idMethodParameters === null) {
+ $this->idMethodParameters = array();
+ }
+ $this->idMethodParameters[] = $imp;
+ return $imp;
+ } else {
+ $imp = new IdMethodParameter();
+ $imp->loadFromXML($impdata);
+ return $this->addIdMethodParameter($imp); // call self w/ diff param
+ }
+ }
+
+ /**
+ * Adds a new index to the index list and set the
+ * parent table of the column to the current table
+ */
+ public function addIndex($idxdata)
+ {
+ if ($idxdata instanceof Index) {
+ $index = $idxdata;
+ $index->setTable($this);
+ $index->getName(); // we call this method so that the name is created now if it doesn't already exist.
+ $this->indices[] = $index;
+ return $index;
+ } else {
+ $index = new Index($this);
+ $index->loadFromXML($idxdata);
+ return $this->addIndex($index);
+ }
+ }
+
+ /**
+ * Adds a new Unique to the Unique list and set the
+ * parent table of the column to the current table
+ */
+ public function addUnique($unqdata)
+ {
+ if ($unqdata instanceof Unique) {
+ $unique = $unqdata;
+ $unique->setTable($this);
+ $unique->getName(); // we call this method so that the name is created now if it doesn't already exist.
+ $this->unices[] = $unique;
+ return $unique;
+ } else {
+ $unique = new Unique($this);
+ $unique->loadFromXML($unqdata);
+ return $this->addUnique($unique);
+ }
+ }
+
+ /**
+ * Retrieves the configuration object, filled by build.properties
+ *
+ * @return GeneratorConfig
+ */
+ public function getGeneratorConfig()
+ {
+ return $this->getDatabase()->getAppData()->getPlatform()->getGeneratorConfig();
+ }
+
+ /**
+ * Adds a new Behavior to the table
+ * @return Behavior A behavior instance
+ */
+ public function addBehavior($bdata)
+ {
+ if ($bdata instanceof Behavior) {
+ $behavior = $bdata;
+ $behavior->setTable($this);
+ $this->behaviors[$behavior->getName()] = $behavior;
+ return $behavior;
+ } else {
+ $class = $this->getConfiguredBehavior($bdata['name']);
+ $behavior = new $class();
+ $behavior->loadFromXML($bdata);
+ return $this->addBehavior($behavior);
+ }
+ }
+
+ /**
+ * Get the table behaviors
+ * @return Array of Behavior objects
+ */
+ public function getBehaviors()
+ {
+ return $this->behaviors;
+ }
+
+ /**
+ * Get the early table behaviors
+ * @return Array of Behavior objects
+ */
+ public function getEarlyBehaviors()
+ {
+ $behaviors = array();
+ foreach ($this->behaviors as $name => $behavior) {
+ if ($behavior->isEarly()) {
+ $behaviors[$name] = $behavior;
+ }
+ }
+ return $behaviors;
+ }
+
+ /**
+ * check if the table has a behavior by name
+ *
+ * @param string $name the behavior name
+ * @return boolean True if the behavior exists
+ */
+ public function hasBehavior($name)
+ {
+ return array_key_exists($name, $this->behaviors);
+ }
+
+ /**
+ * Get one table behavior by name
+ *
+ * @param string $name the behavior name
+ * @return Behavior a behavior object
+ */
+ public function getBehavior($name)
+ {
+ return $this->behaviors[$name];
+ }
+
+ /**
+ * Get the name of the Table
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the Table
+ */
+ public function setName($newName)
+ {
+ $this->name = $newName;
+ }
+
+ /**
+ * Get the description for the Table
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set the description for the Table
+ *
+ * @param newDescription description for the Table
+ */
+ public function setDescription($newDescription)
+ {
+ $this->description = $newDescription;
+ }
+
+ /**
+ * Get name to use in PHP sources
+ * @return string
+ */
+ public function getPhpName()
+ {
+ if ($this->phpName === null) {
+ $inputs = array();
+ $inputs[] = $this->name;
+ $inputs[] = $this->phpNamingMethod;
+ try {
+ $this->phpName = NameFactory::generateName(NameFactory::PHP_GENERATOR, $inputs);
+ } catch (EngineException $e) {
+ print $e->getMessage() . "\n";
+ print $e->getTraceAsString();
+ }
+ }
+ return $this->phpName;
+ }
+
+ /**
+ * Set name to use in PHP sources
+ * @param string $phpName
+ */
+ public function setPhpName($phpName)
+ {
+ $this->phpName = $phpName;
+ }
+
+ public function buildPhpName($name)
+ {
+ return NameFactory::generateName(NameFactory::PHP_GENERATOR, array($name, $this->phpNamingMethod));
+ }
+
+ /**
+ * Get studly version of PHP name.
+ *
+ * The studly name is the PHP name with the first character lowercase.
+ *
+ * @return string
+ */
+ public function getStudlyPhpName()
+ {
+ $phpname = $this->getPhpName();
+ if (strlen($phpname) > 1) {
+ return strtolower(substr($phpname, 0, 1)) . substr($phpname, 1);
+ } else { // 0 or 1 chars (I suppose that's rare)
+ return strtolower($phpname);
+ }
+ }
+
+ /**
+ * Get the value of the namespace.
+ * @return value of namespace.
+ */
+ public function getNamespace()
+ {
+ if (strpos($this->namespace, '\\') === 0) {
+ // absolute table namespace
+ return substr($this->namespace, 1);
+ } elseif ($this->namespace && $this->getDatabase() && $this->getDatabase()->getNamespace()) {
+ return $this->getDatabase()->getNamespace() . '\\' . $this->namespace;
+ } elseif ($this->getDatabase() && $this->getDatabase()->getNamespace()) {
+ return $this->getDatabase()->getNamespace();
+ } else {
+ return $this->namespace;
+ }
+ }
+
+ /**
+ * Set the value of the namespace.
+ * @param v Value to assign to namespace.
+ */
+ public function setNamespace($v)
+ {
+ $this->namespace = $v;
+ }
+
+ /**
+ * Get the method for generating pk's
+ * [HL] changing behavior so that Database default method is returned
+ * if no method has been specified for the table.
+ *
+ * @return string
+ */
+ public function getIdMethod()
+ {
+ if ($this->idMethod === null) {
+ return IDMethod::NO_ID_METHOD;
+ } else {
+ return $this->idMethod;
+ }
+ }
+
+ /**
+ * Whether we allow to insert primary keys on tables with
+ * idMethod=native
+ *
+ * @return boolean
+ */
+ public function isAllowPkInsert()
+ {
+ return $this->allowPkInsert;
+ }
+
+
+ /**
+ * Set the method for generating pk's
+ */
+ public function setIdMethod($idMethod)
+ {
+ $this->idMethod = $idMethod;
+ }
+
+ /**
+ * Skip generating sql for this table (in the event it should
+ * not be created from scratch).
+ * @return boolean Value of skipSql.
+ */
+ public function isSkipSql()
+ {
+ return ($this->skipSql || $this->isAlias() || $this->isForReferenceOnly());
+ }
+
+ /**
+ * Is table read-only, in which case only accessors (and relationship setters)
+ * will be created.
+ * @return boolan Value of readOnly.
+ */
+ public function isReadOnly()
+ {
+ return $this->readOnly;
+ }
+
+ /**
+ * Set whether this table should have its creation sql generated.
+ * @param boolean $v Value to assign to skipSql.
+ */
+ public function setSkipSql($v)
+ {
+ $this->skipSql = $v;
+ }
+
+ /**
+ * Whether to force object to reload on INSERT.
+ * @return boolean
+ */
+ public function isReloadOnInsert()
+ {
+ return $this->reloadOnInsert;
+ }
+
+ /**
+ * Whether to force object to reload on UPDATE.
+ * @return boolean
+ */
+ public function isReloadOnUpdate()
+ {
+ return $this->reloadOnUpdate;
+ }
+
+ /**
+ * PhpName of om object this entry references.
+ * @return value of external.
+ */
+ public function getAlias()
+ {
+ return $this->alias;
+ }
+
+ /**
+ * Is this table specified in the schema or is there just
+ * a foreign key reference to it.
+ * @return value of external.
+ */
+ public function isAlias()
+ {
+ return ($this->alias !== null);
+ }
+
+ /**
+ * Set whether this table specified in the schema or is there just
+ * a foreign key reference to it.
+ * @param v Value to assign to alias.
+ */
+ public function setAlias($v)
+ {
+ $this->alias = $v;
+ }
+
+
+ /**
+ * Interface which objects for this table will implement
+ * @return value of interface.
+ */
+ public function getInterface()
+ {
+ return $this->enterface;
+ }
+
+ /**
+ * Interface which objects for this table will implement
+ * @param v Value to assign to interface.
+ */
+ public function setInterface($v)
+ {
+ $this->enterface = $v;
+ }
+
+ /**
+ * When a table is abstract, it marks the business object class that is
+ * generated as being abstract. If you have a table called "FOO", then the
+ * Foo BO will be public abstract class Foo
+ * This helps support class hierarchies
+ *
+ * @return value of abstractValue.
+ */
+ public function isAbstract()
+ {
+ return $this->abstractValue;
+ }
+
+ /**
+ * When a table is abstract, it marks the business object
+ * class that is generated as being abstract. If you have a
+ * table called "FOO", then the Foo BO will be
+ * public abstract class Foo
+ * This helps support class hierarchies
+ *
+ * @param v Value to assign to abstractValue.
+ */
+ public function setAbstract($v)
+ {
+ $this->abstractValue = (boolean) $v;
+ }
+
+ /**
+ * Get the value of package.
+ * @return value of package.
+ */
+ public function getPackage()
+ {
+ return $this->pkg;
+ }
+
+ /**
+ * Set the value of package.
+ * @param v Value to assign to package.
+ */
+ public function setPackage($v)
+ {
+ $this->pkg = $v;
+ }
+
+ /**
+ * Returns an Array containing all the columns in the table
+ * @return array Column[]
+ */
+ public function getColumns()
+ {
+ return $this->columnList;
+ }
+
+ /**
+ * Utility method to get the number of columns in this table
+ */
+ public function getNumColumns()
+ {
+ return count($this->columnList);
+ }
+
+ /**
+ * Utility method to get the number of columns in this table
+ */
+ public function getNumLazyLoadColumns()
+ {
+ $count = 0;
+ foreach ($this->columnList as $col) {
+ if ($col->isLazyLoad()) {
+ $count++;
+ }
+ }
+ return $count;
+ }
+
+ /**
+ * Returns an Array containing all the validators in the table
+ * @return array Validator[]
+ */
+ public function getValidators()
+ {
+ return $this->validatorList;
+ }
+
+ /**
+ * Returns an Array containing all the FKs in the table.
+ * @return array ForeignKey[]
+ */
+ public function getForeignKeys()
+ {
+ return $this->foreignKeys;
+ }
+
+ /**
+ * Returns a Collection of parameters relevant for the chosen
+ * id generation method.
+ */
+ public function getIdMethodParameters()
+ {
+ return $this->idMethodParameters;
+ }
+
+ /**
+ * Returns an Array containing all the FKs in the table
+ * @return array Index[]
+ */
+ public function getIndices()
+ {
+ return $this->indices;
+ }
+
+ /**
+ * Returns an Array containing all the UKs in the table
+ * @return array Unique[]
+ */
+ public function getUnices()
+ {
+ return $this->unices;
+ }
+
+ /**
+ * Check whether the table has a column.
+ * @return boolean
+ */
+ public function hasColumn($name)
+ {
+ return array_key_exists($name, $this->columnsByName);
+ }
+
+ /**
+ * Returns a specified column.
+ * @return Column Return a Column object or null if it does not exist.
+ */
+ public function getColumn($name)
+ {
+ return @$this->columnsByName[$name];
+ }
+
+ /**
+ * Returns a specified column.
+ * @return Column Return a Column object or null if it does not exist.
+ */
+ public function getColumnByPhpName($phpName)
+ {
+ return @$this->columnsByPhpName[$phpName];
+ }
+
+ /**
+ * Get all the foreign keys from this table to the specified table.
+ * @return array ForeignKey[]
+ */
+ public function getForeignKeysReferencingTable($tablename)
+ {
+ $matches = array();
+ $keys = $this->getForeignKeys();
+ foreach ($keys as $fk) {
+ if ($fk->getForeignTableName() === $tablename) {
+ $matches[] = $fk;
+ }
+ }
+ return $matches;
+ }
+
+ /**
+ * Return the foreign keys that includes col in it's list of local columns.
+ * Eg. Foreign key (a,b,c) refrences tbl(x,y,z) will be returned of col is either a,b or c.
+ * @param string $col
+ * @return array ForeignKey[] or null if there is no FK for specified column.
+ */
+ public function getColumnForeignKeys($colname)
+ {
+ $matches = array();
+ foreach ($this->foreignKeys as $fk) {
+ if (in_array($colname, $fk->getLocalColumns())) {
+ $matches[] = $fk;
+ }
+ }
+ return $matches;
+ }
+
+ /**
+ * Returns true if the table contains a specified column
+ * @param mixed $col Column or column name.
+ */
+ public function containsColumn($col)
+ {
+ if ($col instanceof Column) {
+ return in_array($col, $this->columnList);
+ } else {
+ return ($this->getColumn($col) !== null);
+ }
+ }
+
+ /**
+ * Set the database that contains this table.
+ *
+ * @param Database $db
+ */
+ public function setDatabase(Database $db)
+ {
+ $this->database = $db;
+ }
+
+ /**
+ * Get the database that contains this table.
+ *
+ * @return Database
+ */
+ public function getDatabase()
+ {
+ return $this->database;
+ }
+
+ /**
+ * Flag to determine if code/sql gets created for this table.
+ * Table will be skipped, if return true.
+ * @return boolean
+ */
+ public function isForReferenceOnly()
+ {
+ return $this->forReferenceOnly;
+ }
+
+ /**
+ * Flag to determine if code/sql gets created for this table.
+ * Table will be skipped, if set to true.
+ * @param boolean $v
+ */
+ public function setForReferenceOnly($v)
+ {
+ $this->forReferenceOnly = (boolean) $v;
+ }
+
+ /**
+ * Flag to determine if tree node class should be generated for this table.
+ * @return valur of treeMode
+ */
+ public function treeMode()
+ {
+ return $this->treeMode;
+ }
+
+ /**
+ * Flag to determine if tree node class should be generated for this table.
+ * @param v Value to assign to treeMode.
+ */
+ public function setTreeMode($v)
+ {
+ $this->treeMode = $v;
+ }
+
+ /**
+ * Appends XML nodes to passed-in DOMNode.
+ *
+ * @param DOMNode $node
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $tableNode = $node->appendChild($doc->createElement('table'));
+ $tableNode->setAttribute('name', $this->getName());
+
+ if ($this->phpName !== null) {
+ $tableNode->setAttribute('phpName', $this->phpName);
+ }
+
+ if ($this->idMethod !== null) {
+ $tableNode->setAttribute('idMethod', $this->idMethod);
+ }
+
+ if ($this->skipSql !== null) {
+ $tableNode->setAttribute('idMethod', var_export($this->skipSql, true));
+ }
+
+ if ($this->readOnly !== null) {
+ $tableNode->setAttribute('readOnly', var_export($this->readOnly, true));
+ }
+
+ if ($this->treeMode !== null) {
+ $tableNode->setAttribute('treeMode', $this->treeMode);
+ }
+
+ if ($this->reloadOnInsert !== null) {
+ $tableNode->setAttribute('reloadOnInsert', var_export($this->reloadOnInsert, true));
+ }
+
+ if ($this->reloadOnUpdate !== null) {
+ $tableNode->setAttribute('reloadOnUpdate', var_export($this->reloadOnUpdate, true));
+ }
+
+ if ($this->forReferenceOnly !== null) {
+ $tableNode->setAttribute('forReferenceOnly', var_export($this->forReferenceOnly, true));
+ }
+
+ if ($this->abstractValue !== null) {
+ $tableNode->setAttribute('abstract', var_export($this->abstractValue, true));
+ }
+
+ if ($this->enterface !== null) {
+ $tableNode->setAttribute('interface', $this->enterface);
+ }
+
+ if ($this->description !== null) {
+ $tableNode->setAttribute('description', $this->description);
+ }
+
+ if ($this->baseClass !== null) {
+ $tableNode->setAttribute('baseClass', $this->baseClass);
+ }
+
+ if ($this->basePeer !== null) {
+ $tableNode->setAttribute('basePeer', $this->basePeer);
+ }
+
+ if ($this->getIsCrossRef()) {
+ $tableNode->setAttribute('isCrossRef', $this->getIsCrossRef());
+ }
+
+
+ foreach ($this->columnList as $col) {
+ $col->appendXml($tableNode);
+ }
+
+ foreach ($this->validatorList as $validator) {
+ $validator->appendXml($tableNode);
+ }
+
+ foreach ($this->foreignKeys as $fk) {
+ $fk->appendXml($tableNode);
+ }
+
+ foreach ($this->idMethodParameters as $param) {
+ $param->appendXml($tableNode);
+ }
+
+ foreach ($this->indices as $index) {
+ $index->appendXml($tableNode);
+ }
+
+ foreach ($this->unices as $unique) {
+ $unique->appendXml($tableNode);
+ }
+
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($tableNode);
+ }
+
+ }
+
+ /**
+ * Returns the collection of Columns which make up the single primary
+ * key for this table.
+ *
+ * @return array Column[] A list of the primary key parts.
+ */
+ public function getPrimaryKey()
+ {
+ $pk = array();
+ foreach ($this->columnList as $col) {
+ if ($col->isPrimaryKey()) {
+ $pk[] = $col;
+ }
+ }
+ return $pk;
+ }
+
+ /**
+ * Determine whether this table has a primary key.
+ *
+ * @return boolean Whether this table has any primary key parts.
+ */
+ public function hasPrimaryKey()
+ {
+ return (count($this->getPrimaryKey()) > 0);
+ }
+
+ /**
+ * Determine whether this table has a composite primary key.
+ *
+ * @return boolean Whether this table has more than one primary key parts.
+ */
+ public function hasCompositePrimaryKey()
+ {
+ return (count($this->getPrimaryKey()) > 1);
+ }
+
+ /**
+ * Determine whether this table has any auto-increment primary key(s).
+ *
+ * @return boolean Whether this table has a non-"none" id method and has a primary key column that is auto-increment.
+ */
+ public function hasAutoIncrementPrimaryKey()
+ {
+ if ($this->getIdMethod() != IDMethod::NO_ID_METHOD) {
+ $pks =$this->getPrimaryKey();
+ foreach ($pks as $pk) {
+ if ($pk->isAutoIncrement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the auto increment PK
+ *
+ * @return Column if any auto increment PK column
+ */
+ public function getAutoIncrementPrimaryKey()
+ {
+ if ($this->getIdMethod() != IDMethod::NO_ID_METHOD) {
+ $pks =$this->getPrimaryKey();
+ foreach ($pks as $pk) {
+ if ($pk->isAutoIncrement()) {
+ return $pk;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns all parts of the primary key, separated by commas.
+ *
+ * @return A CSV list of primary key parts.
+ * @deprecated Use the DDLBuilder->getColumnList() with the #getPrimaryKey() method.
+ */
+ public function printPrimaryKey()
+ {
+ return $this->printList($this->columnList);
+ }
+
+ /**
+ * Gets the crossRef status for this foreign key
+ * @return boolean
+ */
+ public function getIsCrossRef()
+ {
+ return $this->isCrossRef;
+ }
+
+ /**
+ * Sets a crossref status for this foreign key.
+ * @param boolean $isCrossRef
+ */
+ public function setIsCrossRef($isCrossRef)
+ {
+ $this->isCrossRef = (bool) $isCrossRef;
+ }
+
+ /**
+ * Returns the elements of the list, separated by commas.
+ * @param array $list
+ * @return A CSV list.
+ * @deprecated Use the DDLBuilder->getColumnList() with the #getPrimaryKey() method.
+ */
+ private function printList($list){
+ $result = "";
+ $comma = 0;
+ for ($i=0,$_i=count($list); $i < $_i; $i++) {
+ $col = $list[$i];
+ if ($col->isPrimaryKey()) {
+ $result .= ($comma++ ? ',' : '') . $this->getDatabase()->getPlatform()->quoteIdentifier($col->getName());
+ }
+ }
+ return $result;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Unique.php b/3rd_party/php/propel/generator/lib/model/Unique.php
new file mode 100644
index 000000000..feec89fcd
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Unique.php
@@ -0,0 +1,58 @@
+ (Propel)
+ * @author Jason van Zyl (Torque)
+ * @author Daniel Rall (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Unique extends Index
+{
+
+ /**
+ * Returns true.
+ */
+ public function isUnique()
+ {
+ return true;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $uniqueNode = $node->appendChild($doc->createElement('unique'));
+ $uniqueNode->setAttribute('name', $this->getName());
+ $columns = $this->getColumns();
+ foreach ($this->getColumns() as $colname) {
+ $uniqueColNode = $uniqueNode->appendChild($doc->createElement('unique-column'));
+ $uniqueColNode->setAttribute('name', $colname);
+ }
+
+ foreach ($this->vendorInfos as $vi) {
+ $vi->appendXml($uniqueNode);
+ }
+ }
+
+
+}
diff --git a/3rd_party/php/propel/generator/lib/model/Validator.php b/3rd_party/php/propel/generator/lib/model/Validator.php
new file mode 100644
index 000000000..10e3d8575
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/Validator.php
@@ -0,0 +1,184 @@
+ (Propel)
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class Validator extends XMLElement
+{
+
+ const TRANSLATE_NONE = "none";
+ const TRANSLATE_GETTEXT = "gettext";
+
+ /**
+ * The column this validator applies to.
+ *
+ * @var Column
+ */
+ private $column;
+
+ /**
+ * The rules for the validation.
+ *
+ * @var array Rule[]
+ */
+ private $ruleList = array();
+
+ /**
+ * The translation mode.
+ *
+ * @var string
+ */
+ private $translate;
+
+ /**
+ * Parent table.
+ *
+ * @var Table
+ */
+ private $table;
+
+ /**
+ * Sets up the Validator object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->column = $this->getTable()->getColumn($this->getAttribute("column"));
+ $this->translate = $this->getAttribute("translate", $this->getTable()->getDatabase()->getDefaultTranslateMethod());;
+ }
+
+ /**
+ * Add a Rule to this validator.
+ * Supports two signatures:
+ * - addRule(Rule $rule)
+ * - addRule(array $attribs)
+ * @param mixed $data Rule object or XML attribs (array) from element.
+ * @return Rule The added Rule.
+ */
+ public function addRule($data)
+ {
+ if ($data instanceof Rule) {
+ $rule = $data; // alias
+ $rule->setValidator($this);
+ $this->ruleList[] = $rule;
+ return $rule;
+ }
+ else {
+ $rule = new Rule();
+ $rule->setValidator($this);
+ $rule->loadFromXML($data);
+ return $this->addRule($rule); // call self w/ different param
+ }
+ }
+
+ /**
+ * Gets an array of all added rules for this validator.
+ * @return array Rule[]
+ */
+ public function getRules()
+ {
+ return $this->ruleList;
+ }
+
+ /**
+ * Gets the name of the column that this Validator applies to.
+ * @return string
+ */
+ public function getColumnName()
+ {
+ return $this->column->getName();
+ }
+
+ /**
+ * Sets the Column object that this validator applies to.
+ * @param Column $column
+ * @see Table::addValidator()
+ */
+ public function setColumn(Column $column)
+ {
+ $this->column = $column;
+ }
+
+ /**
+ * Gets the Column object that this validator applies to.
+ * @return Column
+ */
+ public function getColumn()
+ {
+ return $this->column;
+ }
+
+ /**
+ * Set the owning Table.
+ * @param Table $table
+ */
+ public function setTable(Table $table)
+ {
+ $this->table = $table;
+ }
+
+ /**
+ * Get the owning Table.
+ * @return Table
+ */
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ /**
+ * Set the translation mode to use for the message.
+ * Currently only "gettext" and "none" are supported. The default is "none".
+ * @param string $method Translation method ("gettext", "none").
+ */
+ public function setTranslate($method)
+ {
+ $this->translate = $method;
+ }
+
+ /**
+ * Get the translation mode to use for the message.
+ * Currently only "gettext" and "none" are supported. The default is "none".
+ * @return string Translation method ("gettext", "none").
+ */
+ public function getTranslate()
+ {
+ return $this->translate;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $valNode = $node->appendChild($doc->createElement('validator'));
+ $valNode->setAttribute('column', $this->getColumnName());
+
+ if ($this->translate !== null) {
+ $valNode->setAttribute('translate', $this->translate);
+ }
+
+ foreach ($this->ruleList as $rule) {
+ $rule->appendXml($valNode);
+ }
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/VendorInfo.php b/3rd_party/php/propel/generator/lib/model/VendorInfo.php
new file mode 100644
index 000000000..9f4f19f39
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/VendorInfo.php
@@ -0,0 +1,172 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+class VendorInfo extends XMLElement
+{
+
+ /**
+ * The vendor RDBMS type.
+ *
+ * @var string
+ */
+ private $type;
+
+ /**
+ * Vendor parameters.
+ *
+ * @var array
+ */
+ private $parameters = array();
+
+ /**
+ * Creates a new VendorInfo instance.
+ *
+ * @param string $type RDBMS type (optional)
+ */
+ public function __construct($type = null)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * Sets up this object based on the attributes that were passed to loadFromXML().
+ * @see parent::loadFromXML()
+ */
+ protected function setupObject()
+ {
+ $this->type = $this->getAttribute("type");
+ }
+
+ /**
+ * Set RDBMS type for this vendor-specific info.
+ *
+ * @param string $v
+ */
+ public function setType($v)
+ {
+ $this->type = $v;
+ }
+
+ /**
+ * Get RDBMS type for this vendor-specific info.
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Adds a new vendor parameter to this object.
+ * @param array $attrib Attributes from XML.
+ */
+ public function addParameter($attrib)
+ {
+ $name = $attrib["name"];
+ $this->parameters[$name] = $attrib["value"];
+ }
+
+ /**
+ * Sets parameter value.
+ *
+ * @param string $name
+ * @param mixed $value The value for the parameter.
+ */
+ public function setParameter($name, $value)
+ {
+ $this->parameters[$name] = $value;
+ }
+
+ /**
+ * Gets parameter value.
+ *
+ * @param string $name
+ * @return mixed Paramter value.
+ */
+ public function getParameter($name)
+ {
+ if (isset($this->parameters[$name])) {
+ return $this->parameters[$name];
+ }
+ return null; // just to be explicit
+ }
+
+ /**
+ * Whether parameter exists.
+ *
+ * @param string $name
+ */
+ public function hasParameter($name)
+ {
+ return isset($this->parameters[$name]);
+ }
+
+ /**
+ * Sets assoc array of parameters for venfor specific info.
+ *
+ * @param array $params Paramter data.
+ */
+ public function setParameters(array $params = array())
+ {
+ $this->parameters = $params;
+ }
+
+ /**
+ * Gets assoc array of parameters for venfor specific info.
+ *
+ * @return array
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Gets a new merged VendorInfo object.
+ * @param VendorInfo $info
+ * @return VendorInfo new object with merged parameters
+ */
+ public function getMergedVendorInfo(VendorInfo $merge)
+ {
+ $newParams = array_merge($this->getParameters(), $merge->getParameters());
+ $newInfo = new VendorInfo($this->getType());
+ $newInfo->setParameters($newParams);
+ return $newInfo;
+ }
+
+ /**
+ * @see XMLElement::appendXml(DOMNode)
+ */
+ public function appendXml(DOMNode $node)
+ {
+ $doc = ($node instanceof DOMDocument) ? $node : $node->ownerDocument;
+
+ $vendorNode = $node->appendChild($doc->createElement("vendor"));
+ $vendorNode->setAttribute("type", $this->getType());
+
+ foreach ($this->parameters as $key => $value) {
+ $parameterNode = $doc->createElement("parameter");
+ $parameterNode->setAttribute("name", $key);
+ $parameterNode->setAttribute("value", $value);
+ $vendorNode->appendChild($parameterNode);
+ }
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/model/XMLElement.php b/3rd_party/php/propel/generator/lib/model/XMLElement.php
new file mode 100644
index 000000000..cb8008c40
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/model/XMLElement.php
@@ -0,0 +1,182 @@
+
+ * @version $Revision: 1612 $
+ * @package propel.generator.model
+ */
+abstract class XMLElement
+{
+
+ /**
+ * The name => value attributes from XML.
+ *
+ * @var array
+ */
+ protected $attributes = array();
+
+ /**
+ * Any associated vendor-specific information objects.
+ *
+ * @var array VendorInfo[]
+ */
+ protected $vendorInfos = array();
+
+ /**
+ * Replaces the old loadFromXML() so that we can use loadFromXML() to load the attribs into the class.
+ */
+ abstract protected function setupObject();
+
+ /**
+ * This is the entry point method for loading data from XML.
+ * It calls a setupObject() method that must be implemented by the child class.
+ * @param array $attributes The attributes for the XML tag.
+ */
+ public function loadFromXML($attributes)
+ {
+ $this->attributes = array_change_key_case($attributes, CASE_LOWER);
+ $this->setupObject();
+ }
+
+ /**
+ * Returns the assoc array of attributes.
+ * All attribute names (keys) are lowercase.
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Gets a particular attribute by [case-insensitive] name.
+ * If attribute is not set then the $defaultValue is returned.
+ * @param string $name The [case-insensitive] name of the attribute to lookup.
+ * @param mixed $defaultValue The default value to use in case the attribute is not set.
+ * @return mixed The value of the attribute or $defaultValue if not set.
+ */
+ public function getAttribute($name, $defaultValue = null)
+ {
+ $name = strtolower($name);
+ if (isset($this->attributes[$name])) {
+ return $this->attributes[$name];
+ } else {
+ return $defaultValue;
+ }
+ }
+
+ /**
+ * Converts value specified in XML to a boolean value.
+ * This is to support the default value when used w/ a boolean column.
+ * @return value
+ */
+ protected function booleanValue($val)
+ {
+ if (is_numeric($val)) {
+ return (bool) $val;
+ } else {
+ return (in_array(strtolower($val), array('true', 't', 'y', 'yes'), true) ? true : false);
+ }
+ }
+
+ /**
+ * Appends DOM elements to represent this object in XML.
+ * @param DOMNode $node
+ */
+ abstract public function appendXml(DOMNode $node);
+
+ /**
+ * Sets an associated VendorInfo object.
+ *
+ * @param mixed $data VendorInfo object or XML attrib data (array)
+ * @return VendorInfo
+ */
+ public function addVendorInfo($data)
+ {
+ if ($data instanceof VendorInfo) {
+ $vi = $data;
+ $this->vendorInfos[$vi->getType()] = $vi;
+ return $vi;
+ } else {
+ $vi = new VendorInfo();
+ $vi->loadFromXML($data);
+ return $this->addVendorInfo($vi); // call self w/ different param
+ }
+ }
+
+ /**
+ * Gets the any associated VendorInfo object.
+ * @return VendorInfo
+ */
+ public function getVendorInfoForType($type)
+ {
+ if (isset($this->vendorInfos[$type])) {
+ return $this->vendorInfos[$type];
+ } else {
+ // return an empty object
+ return new VendorInfo();
+ }
+ }
+
+ /**
+ * Find the best class name for a given behavior
+ * Looks in build.properties for path like propel.behavior.[bname].class
+ * If not found, tries to autoload [Bname]Behavior
+ * If no success, returns 'Behavior'
+ *
+ * @param string $bname behavior name, e.g. 'timestampable'
+ * @return string behavior class name, e.g. 'TimestampableBehavior'
+ */
+ public function getConfiguredBehavior($bname)
+ {
+ if ($config = $this->getGeneratorConfig()) {
+ if ($class = $config->getConfiguredBehavior($bname)) {
+ return $class;
+ }
+ }
+ // first fallback: maybe the behavior is loaded or autoloaded
+ $gen = new PhpNameGenerator();
+ if(class_exists($class = $gen->generateName($bname, PhpNameGenerator::CONV_METHOD_PHPNAME) . 'Behavior')) {
+ return $class;
+ }
+ // second fallback: use parent behavior class (mostly for unit tests)
+ return 'Behavior';
+ }
+
+ /**
+ * String representation of the current object.
+ *
+ * This is an xml representation with the XML declaration removed.
+ *
+ * @see appendXml()
+ */
+ public function toString()
+ {
+ $doc = new DOMDocument('1.0');
+ $doc->formatOutput = true;
+ $this->appendXml($doc);
+ $xmlstr = $doc->saveXML();
+ return trim(preg_replace('/<\?xml.*?\?>/', '', $xmlstr));
+ }
+
+ /**
+ * Magic string method
+ * @see toString()
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/DefaultPlatform.php b/3rd_party/php/propel/generator/lib/platform/DefaultPlatform.php
new file mode 100644
index 000000000..889d55ec9
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/DefaultPlatform.php
@@ -0,0 +1,299 @@
+ (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.platform
+ */
+class DefaultPlatform implements Platform
+{
+
+ /**
+ * Mapping from Propel types to Domain objects.
+ *
+ * @var array
+ */
+ protected $schemaDomainMap;
+
+ /**
+ * GeneratorConfig object holding build properties.
+ *
+ * @var GeneratorConfig
+ */
+ private $generatorConfig;
+
+ /**
+ * @var PDO Database connection.
+ */
+ private $con;
+
+ /**
+ * Default constructor.
+ * @param PDO $con Optional database connection to use in this platform.
+ */
+ public function __construct(PDO $con = null)
+ {
+ if ($con) $this->setConnection($con);
+ $this->initialize();
+ }
+
+ /**
+ * Set the database connection to use for this Platform class.
+ * @param PDO $con Database connection to use in this platform.
+ */
+ public function setConnection(PDO $con = null)
+ {
+ $this->con = $con;
+ }
+
+ /**
+ * Sets the GeneratorConfig to use in the parsing.
+ *
+ * @param GeneratorConfig $config
+ */
+ public function setGeneratorConfig(GeneratorConfig $config)
+ {
+ $this->generatorConfig = $config;
+ }
+
+ /**
+ * Gets the GeneratorConfig option.
+ *
+ * @return GeneratorConfig
+ */
+ public function getGeneratorConfig()
+ {
+ return $this->generatorConfig;
+ }
+
+ /**
+ * Gets a specific propel (renamed) property from the build.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ protected function getBuildProperty($name)
+ {
+ if ($this->generatorConfig !== null) {
+ return $this->generatorConfig->getBuildProperty($name);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the database connection to use for this Platform class.
+ * @return PDO The database connection or NULL if none has been set.
+ */
+ public function getConnection()
+ {
+ return $this->con;
+ }
+
+ /**
+ * Initialize the type -> Domain mapping.
+ */
+ protected function initialize()
+ {
+ $this->schemaDomainMap = array();
+ foreach (PropelTypes::getPropelTypes() as $type) {
+ $this->schemaDomainMap[$type] = new Domain($type);
+ }
+ // BU_* no longer needed, so map these to the DATE/TIMESTAMP domains
+ $this->schemaDomainMap[PropelTypes::BU_DATE] = new Domain(PropelTypes::DATE);
+ $this->schemaDomainMap[PropelTypes::BU_TIMESTAMP] = new Domain(PropelTypes::TIMESTAMP);
+
+ // Boolean is a bit special, since typically it must be mapped to INT type.
+ $this->schemaDomainMap[PropelTypes::BOOLEAN] = new Domain(PropelTypes::BOOLEAN, "INTEGER");
+ }
+
+ /**
+ * Adds a mapping entry for specified Domain.
+ * @param Domain $domain
+ */
+ protected function setSchemaDomainMapping(Domain $domain)
+ {
+ $this->schemaDomainMap[$domain->getType()] = $domain;
+ }
+
+ /**
+ * Returns the short name of the database type that this platform represents.
+ * For example MysqlPlatform->getDatabaseType() returns 'mysql'.
+ * @return string
+ */
+ public function getDatabaseType()
+ {
+ $clazz = get_class($this);
+ $pos = strpos($clazz, 'Platform');
+ return strtolower(substr($clazz,0,$pos));
+ }
+
+ /**
+ * @see Platform::getMaxColumnNameLength()
+ */
+ public function getMaxColumnNameLength()
+ {
+ return 64;
+ }
+
+ /**
+ * @see Platform::getNativeIdMethod()
+ */
+ public function getNativeIdMethod()
+ {
+ return Platform::IDENTITY;
+ }
+
+ /**
+ * @see Platform::getDomainForType()
+ */
+ public function getDomainForType($propelType)
+ {
+ if (!isset($this->schemaDomainMap[$propelType])) {
+ throw new EngineException("Cannot map unknown Propel type " . var_export($propelType, true) . " to native database type.");
+ }
+ return $this->schemaDomainMap[$propelType];
+ }
+
+ /**
+ * @return string Returns the SQL fragment to use if null values are disallowed.
+ * @see Platform::getNullString(boolean)
+ */
+ public function getNullString($notNull)
+ {
+ return ($notNull ? "NOT NULL" : "");
+ }
+
+ /**
+ * @see Platform::getAutoIncrement()
+ */
+ public function getAutoIncrement()
+ {
+ return "IDENTITY";
+ }
+
+ /**
+ * @see Platform::hasScale(String)
+ */
+ public function hasScale($sqlType)
+ {
+ return true;
+ }
+
+ /**
+ * @see Platform::hasSize(String)
+ */
+ public function hasSize($sqlType)
+ {
+ return true;
+ }
+
+ /**
+ * @see Platform::quote()
+ */
+ public function quote($text)
+ {
+ if ($this->getConnection()) {
+ return $this->getConnection()->quote($text);
+ } else {
+ return "'" . $this->disconnectedEscapeText($text) . "'";
+ }
+ }
+
+ /**
+ * Method to escape text when no connection has been set.
+ *
+ * The subclasses can implement this using string replacement functions
+ * or native DB methods.
+ *
+ * @param string $text Text that needs to be escaped.
+ * @return string
+ */
+ protected function disconnectedEscapeText($text)
+ {
+ return str_replace("'", "''", $text);
+ }
+
+ /**
+ * @see Platform::quoteIdentifier()
+ */
+ public function quoteIdentifier($text)
+ {
+ return '"' . $text . '"';
+ }
+
+ /**
+ * @see Platform::supportsNativeDeleteTrigger()
+ */
+ public function supportsNativeDeleteTrigger()
+ {
+ return false;
+ }
+
+ /**
+ * @see Platform::supportsInsertNullPk()
+ */
+ public function supportsInsertNullPk()
+ {
+ return true;
+ }
+
+ /**
+ * Whether the underlying PDO driver for this platform returns BLOB columns as streams (instead of strings).
+ * @return boolean
+ */
+ public function hasStreamBlobImpl()
+ {
+ return false;
+ }
+
+ /**
+ * @see Platform::getBooleanString()
+ */
+ public function getBooleanString($b)
+ {
+ $b = ($b === true || strtolower($b) === 'true' || $b === 1 || $b === '1' || strtolower($b) === 'y' || strtolower($b) === 'yes');
+ return ($b ? '1' : '0');
+ }
+
+ /**
+ * Gets the preferred timestamp formatter for setting date/time values.
+ * @return string
+ */
+ public function getTimestampFormatter()
+ {
+ return DateTime::ISO8601;
+ }
+
+ /**
+ * Gets the preferred time formatter for setting date/time values.
+ * @return string
+ */
+ public function getTimeFormatter()
+ {
+ return 'H:i:s';
+ }
+
+ /**
+ * Gets the preferred date formatter for setting date/time values.
+ * @return string
+ */
+ public function getDateFormatter()
+ {
+ return 'Y-m-d';
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/MssqlPlatform.php b/3rd_party/php/propel/generator/lib/platform/MssqlPlatform.php
new file mode 100644
index 000000000..b8330d9b8
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/MssqlPlatform.php
@@ -0,0 +1,107 @@
+ (Propel)
+ * @author Martin Poeschl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.platform
+ */
+class MssqlPlatform extends DefaultPlatform
+{
+
+ /**
+ * Initializes db specific domain mapping.
+ */
+ protected function initialize()
+ {
+ parent::initialize();
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::INTEGER, "INT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BOOLEAN, "INT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DOUBLE, "FLOAT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, "TEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, "TEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DATE, "DATETIME"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BU_DATE, "DATETIME"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TIME, "DATETIME"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TIMESTAMP, "DATETIME"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BU_TIMESTAMP, "DATETIME"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, "BINARY(7132)"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, "IMAGE"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, "IMAGE"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BLOB, "IMAGE"));
+ }
+
+ /**
+ * @see Platform#getMaxColumnNameLength()
+ */
+ public function getMaxColumnNameLength()
+ {
+ return 128;
+ }
+
+ /**
+ * @return Explicitly returns NULL if null values are
+ * allowed (as recomended by Microsoft).
+ * @see Platform#getNullString(boolean)
+ */
+ public function getNullString($notNull)
+ {
+ return ($notNull ? "NOT NULL" : "NULL");
+ }
+
+ /**
+ * @see Platform::supportsNativeDeleteTrigger()
+ */
+ public function supportsNativeDeleteTrigger()
+ {
+ return true;
+ }
+
+ /**
+ * @see Platform::supportsInsertNullPk()
+ */
+ public function supportsInsertNullPk()
+ {
+ return false;
+ }
+
+ /**
+ * @see Platform::hasSize(String)
+ */
+ public function hasSize($sqlType)
+ {
+ return !("INT" == $sqlType || "TEXT" == $sqlType);
+ }
+
+ /**
+ * @see Platform::quoteIdentifier()
+ */
+ public function quoteIdentifier($text)
+ {
+ return '[' . $text . ']';
+ }
+
+ /**
+ * Gets the preferred timestamp formatter for setting date/time values.
+ * @return string
+ */
+ public function getTimestampFormatter()
+ {
+ return 'Y-m-d H:i:s';
+ }
+
+
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/MysqlPlatform.php b/3rd_party/php/propel/generator/lib/platform/MysqlPlatform.php
new file mode 100644
index 000000000..8bbc0b9db
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/MysqlPlatform.php
@@ -0,0 +1,110 @@
+ (Propel)
+ * @author Martin Poeschl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.platform
+ */
+class MysqlPlatform extends DefaultPlatform
+{
+
+ /**
+ * Initializes db specific domain mapping.
+ */
+ protected function initialize()
+ {
+ parent::initialize();
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BOOLEAN, "TINYINT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, "DECIMAL"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, "TEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, "BLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, "MEDIUMBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, "LONGBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BLOB, "LONGBLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, "LONGTEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TIMESTAMP, "DATETIME"));
+ }
+
+ /**
+ * @see Platform#getAutoIncrement()
+ */
+ public function getAutoIncrement()
+ {
+ return "AUTO_INCREMENT";
+ }
+
+ /**
+ * @see Platform#getMaxColumnNameLength()
+ */
+ public function getMaxColumnNameLength()
+ {
+ return 64;
+ }
+
+ /**
+ * @see Platform::supportsNativeDeleteTrigger()
+ */
+ public function supportsNativeDeleteTrigger()
+ {
+ $usingInnoDB = false;
+ if (class_exists('DataModelBuilder', false))
+ {
+ $usingInnoDB = strtolower($this->getBuildProperty('mysqlTableType')) == 'innodb';
+ }
+ return $usingInnoDB || false;
+ }
+
+ /**
+ * @see Platform#hasSize(String)
+ */
+ public function hasSize($sqlType)
+ {
+ return !("MEDIUMTEXT" == $sqlType || "LONGTEXT" == $sqlType
+ || "BLOB" == $sqlType || "MEDIUMBLOB" == $sqlType
+ || "LONGBLOB" == $sqlType);
+ }
+
+ /**
+ * Escape the string for RDBMS.
+ * @param string $text
+ * @return string
+ */
+ public function disconnectedEscapeText($text)
+ {
+ if (function_exists('mysql_escape_string')) {
+ return mysql_escape_string($text);
+ } else {
+ return addslashes($text);
+ }
+ }
+
+ /**
+ * @see Platform::quoteIdentifier()
+ */
+ public function quoteIdentifier($text)
+ {
+ return '`' . $text . '`';
+ }
+
+ /**
+ * Gets the preferred timestamp formatter for setting date/time values.
+ * @return string
+ */
+ public function getTimestampFormatter()
+ {
+ return 'Y-m-d H:i:s';
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/OraclePlatform.php b/3rd_party/php/propel/generator/lib/platform/OraclePlatform.php
new file mode 100644
index 000000000..7ecf100b7
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/OraclePlatform.php
@@ -0,0 +1,113 @@
+ (Propel)
+ * @author Martin Poeschl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.platform
+ */
+class OraclePlatform extends DefaultPlatform
+{
+
+ /**
+ * Initializes db specific domain mapping.
+ */
+ protected function initialize()
+ {
+ parent::initialize();
+ $this->schemaDomainMap[PropelTypes::BOOLEAN] = new Domain(PropelTypes::BOOLEAN_EMU, "NUMBER", "1", "0");
+ $this->schemaDomainMap[PropelTypes::CLOB] = new Domain(PropelTypes::CLOB_EMU, "CLOB");
+ $this->schemaDomainMap[PropelTypes::CLOB_EMU] = $this->schemaDomainMap[PropelTypes::CLOB];
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TINYINT, "NUMBER", "3", "0"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::SMALLINT, "NUMBER", "5", "0"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::INTEGER, "NUMBER"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BIGINT, "NUMBER", "20", "0"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::REAL, "NUMBER"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DOUBLE, "FLOAT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DECIMAL, "NUMBER"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::NUMERIC, "NUMBER"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARCHAR, "NVARCHAR2"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, "NVARCHAR2", "2000"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TIME, "DATE"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DATE, "DATE"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TIMESTAMP, "TIMESTAMP"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, "LONG RAW"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, "BLOB"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, "LONG RAW"));
+ }
+
+ /**
+ * @see Platform#getMaxColumnNameLength()
+ */
+ public function getMaxColumnNameLength()
+ {
+ return 30;
+ }
+
+ /**
+ * @see Platform#getNativeIdMethod()
+ */
+ public function getNativeIdMethod()
+ {
+ return Platform::SEQUENCE;
+ }
+
+ /**
+ * @see Platform#getAutoIncrement()
+ */
+ public function getAutoIncrement()
+ {
+ return "";
+ }
+
+ /**
+ * @see Platform::supportsNativeDeleteTrigger()
+ */
+ public function supportsNativeDeleteTrigger()
+ {
+ return true;
+ }
+
+ /**
+ * Whether the underlying PDO driver for this platform returns BLOB columns as streams (instead of strings).
+ * @return boolean
+ */
+ public function hasStreamBlobImpl()
+ {
+ return true;
+ }
+
+ /**
+ * Quotes identifiers used in database SQL.
+ * @see Platform::quoteIdentifier()
+ * @param string $text
+ * @return string Quoted identifier.
+ */
+ public function quoteIdentifier($text)
+ {
+ return $text;
+ }
+
+ /**
+ * Gets the preferred timestamp formatter for setting date/time values.
+ * @see Platform::getTimestampFormatter()
+ * @return string
+ */
+ public function getTimestampFormatter()
+ {
+ return 'Y-m-d H:i:s';
+ }
+
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/PgsqlPlatform.php b/3rd_party/php/propel/generator/lib/platform/PgsqlPlatform.php
new file mode 100644
index 000000000..8e8511b9e
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/PgsqlPlatform.php
@@ -0,0 +1,118 @@
+ (Propel)
+ * @author Martin Poeschl (Torque)
+ * @version $Revision: 1612 $
+ * @package propel.generator.platform
+ */
+class PgsqlPlatform extends DefaultPlatform
+{
+
+ /**
+ * Initializes db specific domain mapping.
+ */
+ protected function initialize()
+ {
+ parent::initialize();
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BOOLEAN, "BOOLEAN"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::TINYINT, "INT2"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::SMALLINT, "INT2"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BIGINT, "INT8"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::REAL, "FLOAT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::DOUBLE, "DOUBLE PRECISION"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARCHAR, "TEXT"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BINARY, "BYTEA"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::VARBINARY, "BYTEA"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::LONGVARBINARY, "BYTEA"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::BLOB, "BYTEA"));
+ $this->setSchemaDomainMapping(new Domain(PropelTypes::CLOB, "TEXT"));
+ }
+
+ /**
+ * @see Platform#getNativeIdMethod()
+ */
+ public function getNativeIdMethod()
+ {
+ return Platform::SERIAL;
+ }
+
+ /**
+ * @see Platform#getAutoIncrement()
+ */
+ public function getAutoIncrement()
+ {
+ return "";
+ }
+
+ /**
+ * @see Platform#getMaxColumnNameLength()
+ */
+ public function getMaxColumnNameLength()
+ {
+ return 32;
+ }
+
+ /**
+ * Escape the string for RDBMS.
+ * @param string $text
+ * @return string
+ */
+ public function disconnectedEscapeText($text)
+ {
+ if (function_exists('pg_escape_string')) {
+ return pg_escape_string($text);
+ } else {
+ return parent::disconnectedEscapeText($text);
+ }
+ }
+
+ /**
+ * @see Platform::getBooleanString()
+ */
+ public function getBooleanString($b)
+ {
+ // parent method does the checking for allowes tring
+ // representations & returns integer
+ $b = parent::getBooleanString($b);
+ return ($b ? "'t'" : "'f'");
+ }
+
+ /**
+ * @see Platform::supportsNativeDeleteTrigger()
+ */
+ public function supportsNativeDeleteTrigger()
+ {
+ return true;
+ }
+
+ /**
+ * @see Platform::hasSize(String)
+ * TODO collect info for all platforms
+ */
+ public function hasSize($sqlType)
+ {
+ return !("BYTEA" == $sqlType || "TEXT" == $sqlType);
+ }
+
+ /**
+ * Whether the underlying PDO driver for this platform returns BLOB columns as streams (instead of strings).
+ * @return boolean
+ */
+ public function hasStreamBlobImpl()
+ {
+ return true;
+ }
+}
diff --git a/3rd_party/php/propel/generator/lib/platform/Platform.php b/3rd_party/php/propel/generator/lib/platform/Platform.php
new file mode 100644
index 000000000..184116ce2
--- /dev/null
+++ b/3rd_party/php/propel/generator/lib/platform/Platform.php
@@ -0,0 +1,182 @@
+ (Propel)
+ * @author Martin Poeschl