Version 6, last updated by copesc at 24 Feb 23:34 UTC
98-template
Index
- Quick tips
- KTemplateHelper
- KView
- KDispatcher
- HMVC
- Factory/Instantiate methods
- KDatabaseRowset
- KModel
- KDatabase
- KViewTemplate
- KController
- Template Toolbar
- Order becomes Sort
- Changes to the com_default component
- Behaviors
- KFilter
- KTemplate
- Modules
- KRequest
- KTemplateFilter
- Persistent Tabs
- KCommandChain
Quick tips
ComDefaultControllerView
becomes
ComDefaultControllerDefault
In components (not in modules),
parent::display();
becomes
return parent::display();
KControllerAction
becomes
KControllerView
In view templates, @$ becomes $ (still optional).
KTemplateHelper
I have made changes to the grid.sort and paginator.pagination helpers. They both accept a KModelState object now instead of seperate parameters.
- grid.sort now is called like
<?= @helper('grid.sort', 'Foo', 'foo', @$state); ?>
- paginator.pagination is called like
<?= @helper('admin::com.default.helper.paginator.pagination', @$total,@$state) ?>or
<?= @helper('paginator.pagination', @$total, @$state) ?>
Added new identifier function to KTemplateHelperDefault which creates DOM element ids used by javascript behaviors from a KIdentifier object. (Not fully functional yet) This new function will be used to create id's for html elements and also replace the harcoded adminForm id that is used in Joomla. See :
http://groups.google.com/group/nooku-framework/browse_thread/thread/e9735cc3da541276#
Some more template helpers have been moved to the new syntax using named arrays:
Column header (Title is optional):
<?= @helper('grid.sort', array('title'=>'Created by', 'column'=>'created_by', 'state'=>$state))?>
Grid checkbox:
<?= @helper('grid.id', array('row'=>$boat))?>
Inline grid buttons:
<?= @helper('grid.enable', array('row'=>$boat, 'option'=>'com_harbour', 'view'=>'boats')) ?>
<?= @helper('grid.order', array('row'=>$task, 'option'=>'com_harbour', 'view'=>'boats'))?>
<?= @helper('grid.access', array('row'=>$task, 'option'=>'com_harbour', 'view'=>'boats'))?>
(Don't worry about the option and view params, they're temporary. We're looking into ways to make those automatic, by passing context to the helpers. )
Removed the following template helpers :
1. behavior.tree
2. grid.boolean
Finished the refactoring of the KTemplateHelperSelect. I have tried to make the helpers as simple as possible. We now have 5 select helpers left :
- option
- optionlist
- radiolist
- checklist
- booleanlist
All helpers implement a named config array, for information on the config options please check the code :
http://nooku.assembla.com/code/nooku-framework/subversion/nodes/trunk/code/libraries/koowa/template/helper/select.php?rev=2313
The select helper no longer supports optgroups, if you need optgroups you will need to implement your own helper or use an alternative way to visualise your data, like a UL or OL tree or a javascript widget.
The final helper which still need to be refactored is the behavior helper. Will tackle that next.
If the view state has been set in KViewTemplate it will be pushed to the template object state and be merged with the $config data when loading template helpers. You now don't longer need to push state information to helpers through your templates.
Eg,
<?= @helper('grid.sort', array('column' => 'title', 'state' => $state)); ?>Becomes
<?= @helper('grid.sort', array('column' => 'title')); ?>
Added KTemplateAbstract::loadHelper function. The function merges the view's 'state' data if it hasn't been set in the $config array of the helper. The loadHelper function is called by the @helper template alias.
Added a new KTemplateHelperDate helper which exposes a format function to help render dates in your templates. Eg,
@helper('date.format', array('date' => $date, 'format' => '%Y-%m-%d %H:%M:%S')) ?>
Added editor helper. Editors can now easily be included using @editor(array('row' => $row)); The editor expects the text field to be called 'description'. If your field has a different name you need to include this in the config array. For a complete list of editor config options please see :
http://nooku.assembla.com/code/nooku-framework/subversion/nodes/trunk/code/administrator/components/com_default/helpers/editor.php?rev=2313
Added ComDefaultHelperEditor class. This class is a wrapper around the Joomla editor functionality and makes it easy to include an editor in your own templates
If you have written custom template helpers who include style or script information, you need to return this when returning the output of your helper. Don't try to push this information into the document object because this will make the caching fail down the road. For an example on this this works, take a look at the refactored framework helpers, especially the behavior helper :
http://nooku.assembla.com/code/nooku-framework/subversion/nodes/trunk/code/libraries/koowa/template/helper/behavior.php?rev=2313
If you call helpers you always need to echo their output to the
template. For example,
<? @helper('behavior.tooltip') ?>becomes
<?= @helper('behavior.tooltip') ?>Note the assignment operator.
- KTemplateHelperPaginator now extends KTemplateHelperSelect
- Re-worked template helper factory. The factory is now only be responsible for instantiating a helper. KTemplate::loadHelper is now responsible for calling the helper function.
- Refactored ComDefaultHelperListbox. Added 'column' configuration option and 'model' configuration option to allow to define a custom model identifier. If no model isset we get the model based on the package name. If no 'text' configuration option is set the 'name' -> 'value' configuration option will be used in cascading order.
See :
http://nooku.assembla.com/code/nooku-framework/subversion/nodes/trunk/code/administrator/components/com_default/helpers/listbox.php?rev=2313
The refactoring of the listbox helper makes it very easy to create your own helpers that retrieve information from a certain column in the database for example :
class ComFooHelperListbox extends ComDefaultHelperListbox
{
public function authors( $config = array())
{
$config['model'] = 'foo';
$config['name'] = 'authors';
$config['value'] = 'cretead_by';
$config['text'] = 'created_by_name';
return parent::_listbox($config);
}
}
This is an example of a helper which set's up a listbox for you. It will retrieve all the distinct values from the created_by column of the your components foo model and populate a listbox where the value of each item is the created_by info and the text is the created_by_name info.
For more examples please see :
http://nooku.assembla.com/code/nooku-components/subversion/nodes/trunk/code/administrator/components/com_profiles/helpers/listbox.php?rev=2313
Moved default listbox implementation to KTemplateHelperListbox and made the listbox function protected to prevent it from being called as a helper function.
KView
- Added KViewTemplate::addScript, addStyle, getScripts and getStyles functions. This new functions help with the management of view script and style information and provide full decoupling from the Joomla document API's.
- Removed inline rendering of script and style information in KViewHtml. Moved logic to ComDefaultViewHtml for both site and admin application spaces.
This change implements a pull principle for style and script information in the framework. The style and script data is pushed to Joomla in com_default/mod_default default html classes. This fully decoupled Nooku's style/script handling from Joomla and allows to implement the framework with ease in other web applications.
KDispatcher
Implemented singleton through KDispatcherDefault::instantiate method. The identifier of the signleton object is mapped to lib.koowa.dispatcher for easy access.
This change allows you to gain access to the active dispatcher from anywhere. This could be very useful for example to get the active primary controller. For example :
$dispatcher = KFactory::get('lib.koowa.dispatcher');
$controller = KFactory::get($dispatcher->getController());
The responsibility to hide the main menu now lies with the dispatcher and not the component controller.
HMVC
Major refactoring of the controller and dispatcher packages to decouple the controller from the request. This is the list of changes :
Moved KControllerBread::getAction function to KDispatcher. KDispatcher now fully implements a front controller pattern and is responsible for executing the proper controller based on the information in the request.
Decoupled KControllerBread and Action from KRequest and implement $_request object that holds the controllers request information. Request information can be set using the 'request' configuration option, or by using the magic setter and getter functions or the fluent interface.
Renamed KControllerBread::saveModelState to saveRequest and getRequest to loadRequest. getRequest now returns the request object.
Added 'request', 'auto_display' and 'persistent' configuration options to KControllerBread. 'auto_display' is true by default, if set to false the controller will not render the view on a 'browse' or 'read' request. This can be useful is you just want to get the read data or browse data out of the controller. The 'persistent' option is false by default, when true the request data will be loaded and save into the session before and after a 'browse' action.
Removed default value from the action in the execute function of KControllerBread and KControllerAction. Action and data information always needs to be defined.
This refactoring fully decouples the KController from the request which allows us to use any controller in a HMVC triad. For example you can very easily call any controller and have it render itself. Eg :
KFactory::tmp('admin::com.harbour.controller.boat')->layout('list')->limit(20)->browse();
Or you might just be interested to call the controller and get a rowset of data without the controller rendering the data. In that case you can do :
$result = KFactory::tmp('admin::com.harbour.controller.boat', array('auto_display' => false))->limit(10)->offset(20)->direction('desc')->browse();
This is very similar to :
$result = KFactory::tmp('admin::com.harbour.models.boats')->limit(10)->offset(20)->direction('desc')->getList();
With that difference that if you go through the controller the controller chain will also be fired. If you go directly through the model you are only working on model level, if you go through the controller you are working on a controller level firing of a complete MVC triad.
When you only need the data it's advised to directly through the model, if you wish to render the data you need to go through the controller instead.
Significant changes going on in the controller and dispatchers to solve a few issues when using an HMVC approach. Here is the list of changes :
1. Changed factory component fallback sequenze. We are now first falling back to the default component before falling back to the framework. The new sequenze is : Named Component Generic -> Named Component Default -> Default Component Generic -> Default Component Defaukt -> Framework Generic - Framework Default.
This changes means that you no longer need to fallback to ComDefault manually. This again reduces your code as you don't need empty controllers or views. From now on it's advised to always extend from ComDefault, and only extend from the framework if you have a specific reason to do so.
2. Added ComDefaultDispatcherDefault both to admin and site. Moved toolbar and submenu rendering to ComDefaultDispatcherDefault::_actionRender(). Render action is only triggered for GET requests and performend cleanup.
This change was needed to allow for proper HMVC handling. The issue being solve is that only one toolbar can be rendered per page and this is done by the dispatcher who is repsonsible for firing the initial controller based on the request information, any subsequent controllers being fired will just render and are not allowed to render toolbar buttons. This concept will be further improved during the toolbar refactoring.
3. Renamed ComDefaultControllerView to ComDefaultControllerDefault. Make sure you adapt your own components !
Important changes. Made a number of significant changes to the rendering changes dispatcher -> controller -> view. This changes are made to allow full control over the output returned from a MVC tried, it's the caller who is now responsible to echo the output of a triad and not the triad itself. For example you can now do :
<?= KFactory::tmp('admin::com.profiles.controller.department')
->sort('people')
->direction('desc')
->limit(5)
->layout('table')
->browse();
?>
See also the new dashboard implementation of com_profiles, which is a good example of HMVC at work :
http://nooku.assembla.com/code/nooku-components/subversion/nodes/trunk/code/administrator/components/com_profiles/views/dashboard/tmpl/default.php?rev=2313
1. Added KDispatcherAbstract::_actionRender which is now responsible to echo the result of the controller. The render action is chained after the dispatch action and before the forward action. With this change the dispatcher is moving further in the direction of a front controller.
2. Removed parameter functionality from KMixinCommand. We are now passing the KCommandContext object to the function being called.
- Added KCommandContext $context parameter to KControllerBread::loadRequest and saveRequest function.
- Added KCommandContext $context parameter to KControllerView::saveReferrer and displayView. If the controller is set to auto_display it will push the output of the view into the $context->result returning it through the execute function, either to a calling dispatcher or directly.
3. Removed KViewAbstract::_toString function. KViewAbstract::display function no longer echo's the output but returns it instead.
If you have problems after this update make sure that your views display function is doing a 'return parent::display()';
Important changes. Made a number of significant changes to the style and script rendering in your templates. The goal of these changes is to implement a pull instead of a push mechanism for adding the style and script data to the JDocument object who is responsible to render the webpage head.
In Joomla 1.5/1.6 script and style information is pushed to the
document object using :
- addScript
- addScriptDeclaration
- addStylesheet
- addStyleDeclaration
functions who are part of JDocument class.
In a typical Joomla extension you can use these functions anywhere in your code to inject style and script information into your document. This is great but has one major drawback : caching. With a push approach it becomes very hard to cache the output of a template/view as you don't know which style/script information the view pushed to the document. To solve this issue we introduced the setHeadData and getHeadDate into 1.5. Joomla 1.5 uses the set/getHeadData API's in it's JCacheView class to do view get/set all the head data.. This works great in an MVC where there is only one active view, but fails in an HMVC context where multiple MVC triads can be active at the same time.
To solve this problem I'm switching Nooku Framework to use a pull approach. Instead of pushing data into the JDocument object, KDispatcher will be responsible for pulling the data out of the MVC triad and setting it into the document. This approach allows us to implement specific caching strategies per MVC triad. As an added bonus this also make the code less coupled and more portable to other CMS systems who render their page head using different techniques.
I'm making these changes in a two step process, in a first step i'm implementing a pull principle for the template style and script information, in the second step I will completely implement the pull approach. First step has been completed and committed. The following is a list of changes that have already been made :
Decoupling from JDocument object
The template and view layer will be completely decoupled from the $document object. At the moment the view still holds a reference to the document object. This will be removed in the second step.
Moved $_document variable to ComDefaultViewHtml (this is only temporary, will be removed later) and moved base_url and media_url configuration options to KViewTemplate.
Factory/Instantiate methods
Changes to framework factory methods. Added $identifier parameter to KDatabaseBehavior, KFilter, KTemplateFilter and KTemplateHelper classes to make it easier to create instances and changed the function name from 'instantiate' to 'factory' to indidicate the difference between a factory method and an instantiate method.
The factory functions implement a factory method pattern and are wrappers for the KFactory calls while the instantiate methods are called by KFactory. Factory methods either accept an indentifier string, object or a name. They also perform extra validation to make sure the created object implements the proper interfaces.
KDatabaseRowset
KDatabaseRowset : added a new extract function to allow for easy removal of rows from the set. If the rowset doesn't have an identity_column defined the rows are now stored using the row's object handle.
Small changed to KDatabaseRowset
- Renamed KDatabaseRowset::insert() function to addRow() and KDatabaseRowset::extract() to removeRow()
- KObjectArray now implements Iterator instead of IteratorAggregate. These changes will make it easier to implement hierarchical rowset/row data sets.
DDD
We are now supporting a full DDD (domain driven design approach) in the controllers. Controllers talk directly to the models and retrieve either a KDatabaseRowset or KDatabaseRow object. Both KDatabaseRow and KDatabaseRowset are very loosely coupled to KDatabase and acted as domain enitities.
For example in our controller we have :
$row = $this->getModel()
->set('id', $id)
->getItem()
->setData($data)
->save();
The main difference between this new approach and the old approach is that the controller doesn't talk to the table, but to the model. The row object returned from getItem should know how to save data, even if the model happens to use a db view. The controller doesn't care how or where data is saved, and shoulnd't know anything about how tables, database and db views work.
When you are implementing your own controllers you should always make sure that your custom action always wraps around a basic BREAD action for the following reasons :
- By making sure we are always calling one of the 5 basic actions it becomes very easy to expose a RESTfull interface out of the box. If you define actions that don't wrap around BREAD your extension will not be fully restable.
- It makes implementing ACL and other controller behaviors very easy as we only need to implement ACL's for the 5 BREAD actions.
- For an example on how to do this see the KControllerAction who has custom 'save', 'apply' and 'cancel' actions.
- For more info on the discussion behind this change please see :
http://groups.google.com/group/nooku-framework/browse_thread/thread/9f85005208f22311#
KDatabaseRowAbstract and KDatabaseRowsetAbstract action functions now return a boolean (true/false) instead of $this
KModel
Fixed KModelState::reset function. When calling reset the model state is reverted to it's default. KModelAbstract::reset now also calls KModelState::reset();
Added KModelTable::getColumn function to retrieve a list of items based on the distinct column values. This function will return a complete rowset.
KModelTable no longer sets the column default in the state for unique states to prevent the state from being included in the where query.
KDatabase
Added full support composite unique keys. This is the list of related changes :
- Added KDatabaseAdapter::fetchTableIndexes function and implemented this function in KDatabaeAdpaterMysqli. This function returns the schema metadata information of the indexes. A special generic KDatabaseSchemaIndex class hasn't been implemented yet at the moment the raw data is returned.
- Added 'required' property to KModelState. This property can hold an array of required states that need to be set for a state to evaluate as unique. The getData function has been updated to implement the new behavior.
- If an composite unique index is defined the first column's KDatabaseSchemaColumn object will be set as 'unique' and it's 'related' property will hold the other columns defined in the index in sequenze order.
These changes allow you to define a composite unique index and then load a row based on the column in the index. Only if the first column in the index is unique and if the other columns are defined in the request the row will load. If you don't know how to use this don't worry about it. It's already quite advanced.
Primary key support is now fully working. You can create tables without an identity column (auto_increment). Both the delete and update functions in KDatabaseTableAbstract make use of the tables primary key when creating the where statements for the queries.
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1957
These changes fix the issue as outlined by Christian in the following thread :
http://groups.google.com/group/nooku-framework/msg/1c7f185cf6edfc80
Made small change to KDatabaseQuery::where function to allow WHERE clauses that don't contain a contrain and value but only a SQL function. For example : WHERE FIND_IN_SET(1, tbl.table_id). For more info on this change also see :
http://groups.google.com/group/nooku-framework/browse_thread/thread/a88314b9deec7f6f/5659ac8734cd554f#5659ac8734cd554f
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1975
Reworked the database auto-matic name quoting.
The KDatabaseAdapterAbstract::quoteName function now uses a regex to quote all the identifiers.. This function requires all SQL statements, functions and operators to be uppercase and all identifiers lowercase.
Replaced the KDatabaseAdapterAbstract::replaceTablePrefix function with a regex.
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1974
- Changed database table and column 'size' to 'length' to be more semantically correct.
- Added KDatabaseRowAbstract::count and load functions. The load function will try to load a row from the database using row data, while the count function will count the rows in the table that match the row data.
To load a row you can now do, eg :
$row = KFactory::tmp('admin::com.foo.row.foo');
$row->id = 1;
$row->load();
Which will load the row with id=1
To count rows you can do, eg :
$row = KFactory::tmp('admin::com.foo.row.foo');
$row->enabled = 1;
$count = $row->count();
Which will count all the rows where enabled=1
- KDatabaseTable::select and count functions now also accept associative arrays.
To load a all 'enabled' rows from a table you can now do, eg :
$rowset = KFactory::get('admin::com.foo.table.foo')->select(array('enabled' => 1));To count all 'enabled' rows from a table you can now do, eg :
$count = KFactory::get('admin::com.foo.table.foo')->count(array('enabled' => 1));
Small changes to the database package and query rendering to allow easy modification of query objects in database behaviors;
- Removed bind/unbind functions from KDatabaseQuery. Moved name and value quoting to the toString method. Implemented the where property as an array storing which is storing the individual parts 'property', 'constraint', 'value' and 'condition'.
- KDatabaseTableAbstract::count now calls the select function to allow the query to be intercepted by the command chain. Added KDatabase::FETCH_FIELD mode and implemented in KDatabaseTableAbstract.
KDatabaseTableAbstract::insert no longer unsets the identity column from the data. Fixed issue in filter function, getColumn should use $base.
Added 'distinct' and 'count' properties to KDatabaseQuery. Removed 'operation' property. The operation is now created in the toString method based on the distinct, count and column properties.
KViewTemplate
- Added auto_assign configuration setting to KViewTemplate and implemented this setting in the KViewHtml::display function. If the auto_assign is TRUE (default) the display function will assign the model data to the view automatically.
- The KTemplateView::assign() function now calls the KObject::set() function instead of directly assigning the data to the local scope. This changes allow more control over how the data gets assigned. You can now override the set() to control the behavior.
http://groups.google.com/group/nooku-framework/browse_frm/thread/9928491dbceedd25#
The KViewTemplate::loadTemplate function now also pushed the 'view' and 'document' objects into template object scope. You can easily access those in your own template using $this->view or $this->document.
KViewTemplate cleanup
- Moved assignment of $baseurl and $mediaurl variables from KViewAbstract to KViewTemplate.
- Moved base_url and media_url configuration options from KViewAbstract to KViewTemplate
- Added new 'script' and 'style' filters. Added media:// and base:// alias write filters based on the media_url and base_url configuration settings
KController
In KControllerAction, the methods _actionEnable, _actionOrder, _actionAccess have been removed. These grid actions now use action=edit and enabled|order|access=n in the POST data.
Implement 'auto_events' configuration option. For controllers and thus also dispatchers this is default true, for all other command chains mixins this is default false. This change allows you to turn sending of events of for controller actions.
Important change. Renamed KControllerAction to KControllerView and moved all view related logic from KControllerBread to KControllerView.
Template Toolbar
Small change to toolbar divider and spacer rendering. Put them in an extra table and float each individual toolbar table left. You need to include the com_default admin.css file in your own component or you will get strange effects with the toolbars. Added a simple <? @style(@$mediaurl.'/com_default/css/admin.css') ?> to your templates will do the trick.
The javascript to handle the toolbars and grid buttons is being rewritten. Instead of relying on the form in your template, they are now creating ad hoc forms in javascript to handle the get and post requests. This gives us more control over what we submit to GET and what to POST, resulting in a more correct RESTful handling of the request in Koowa. It's still rough around the edges, I'll be cleaning it up in further commits.
Order becomes Sort
In revision 2073 in the template branch, the 'order' model state was renamed to 'sort'.
We'll use the following definitions from now on:
- 'Order' always indicate a physical ordering, in other words, the ordering is recorded in the data source. In practice, this usually means there is an 'ordering' field, containing integers that indicate the relative position of the rows. The order action was removed from KControllerAction. Changing the ordering now consists of posting action=edit and ordering=X, where X is the relative movement of the row (eg -1 will move the row down, and reorder the others)
- 'Sort' always indicates presentation sorting. For example, if you click the column headers in a grid, they will trigger a GET request with sort=fieldname&direction=asc. This never creates a change in the database. The data is unchanged, it's just displayed differently.
We are aware that in SQL, it's different: the ORDER BY clause in SQL corresponds to Nooku's 'sort'. When using KDatabaseQuery, it's still named 'order'.
All of this shouldn't break much code, if you are using grid.sort and KModelTable, everything should keep working as expected (and even a little better because ordering is now fixed).
Changes to the com_default component
- Added item and list views to com_default admin. Moved search_form and search_letters templates from Nooku Components space to allow for better re-usability.
- Added com_default to media folder. Moved generic css from Nooku Components space to com_default.
Behaviors
Improved the lockable and modifiable behavior to solve issues reported by Torkil.
- Added enable/disable functions and enabled protected property to KCommandChain. If a chain is disabled when calling run the chain will not iterate over the the commands and just return true
- Disabled chain before save in the unlock and lock functions to prevent behaviors being called when locking or unlocking. This prevents issues with modified behavior being called when locking or unlocking a row.
- Improved the modifiable behavior to only set the modified_on and modified_by fields if data was modified. This change allows us to properly implement a revisonable (versioning) behavior and only create new revisions if data was really modified.
I have added specialised save and delete functions to KDatabaseBehaviorAbstract. Both function auto-disable the tables command chain to prevent recursive lopping during insert, update or delete operations. Behaviors no-longer need to implement measure to prevent looping themselves.
Fixed small issue with KDatabaseBehaviorCreatable and also changed the created_by columns to "datetime NOT NULL default '0000-00-00 00:00:00'" instead of using timestamps. Problem with the CURRENT_TIMESTAMP is that they don't work with views so reverting to datetime's instead.
Ladies and gentleman I proud to present to you KDatabaseBehaviorSluggable :
http://nooku.assembla.com/code/nooku-framework/subversion/nodes/trunk/code/libraries/koowa/database/behavior/sluggable.php?rev=2313
This new little gem provides for search engine friendly URL's out of the box. Gone are the days you need SEF extensions to make your URL's more readable. Nooku Framework now just does it out of box !
The sluggable behavior is capable of generating slugs from one or more table columns. If more columns are specified they will be concatenated in FIFO order. By default it will use the 'title' column and a '-' separator, both are configurable. The behavior uses the KFilterSlug to santize the field date.
If you slug column in your database is not set to unique the sluggable behavior will append your slug with the identity field to make it unique.
For example in com_profiles the jos_profiles_people table's slug column doesn't have a UNIQUE key set for the slug, meaning that slugs can be duplicate. On select the behavior will prepend the slug with the identifity field (by default the auto-increment column) to create slugs in the format of 'id-slug'. If your slug column is UNIQUE then the id will not be appended.
com_profiles site router has been updated as an example on how to use this. It is smart enough to differentiate between a slug that contains id information and one that contains only contains slug information and will acted appropriatly to tell the model to either perform a lookup against the id or against the slug. And voila, SEF out of the box !
The simplest way you can use this behavior to your table behaviors.
For example,
class ComFoorTableFoo extends KDatabaseTableAbstract
{
protected function _initialize(KConfig $config)
{
$config->behaviors = array('lockable', 'creatable', 'modifiable', 'sluggable');
parent::_initialize($config);
}
}
The more advanced way to use this behavor is to setup it up and modify its default. For example for the ComProfilesTablePeople table
class ComProfilesTablePeople extends KDatabaseTableAbstract
{
protected function _initialize(KConfig $config)
{
//Create a custom sluggable behavior
$sluggable = KFactory::get('lib.koowa.database.behavior.sluggable', array('columns' => array('firstname', 'lastname')));
$config->behaviors = array('hittable', 'lockable', 'creatable', 'modifiable', $sluggable);
$config->name = 'profiles_view_people';
$config->base = 'profiles_people';
parent::_initialize($config);
}
}
Added KDatabaseBehaviorSluggable and implemented it in com_profiles and com_terms. com_harbour still to follow. Changed all references of 'alias' to 'slug'. If you are testing from the 98-template branch you will need to refresh your database.
If the sluggable behavior is set to be updatable (default) it will check if the slug is empty. If it is it will regenerate the slug. If the slug isn't empty and he been modified it will sanitize the slug.
Smaller changes to the lockable behavior. Here is the list of changes :
- Added KDatabaseBehaviorLockable::locked() and KDatabaseBehaviorLockable::lockMessage() function. Removed virtual 'locked' property. Use the locked() function instead.
- Improve ComDefaultControllerView::_actionRead to show a notice message with the locked information to inform the user about the state of the item.
- Implemented the lockMessage function in KTemplateHelperGrid::id
These changes made sure that you cannot steal a lock for a locked row and futher improve the UI by showing a message when trying to edit a locked item.
http://nooku.assembla.com/spaces/nooku-framework/tickets/116-locked-rows-can-be-overwritten
Reworked the sluggable behavior to always create the slug at update. If you want to the slug to include the identity_column to be able to perform lookup's againt it you need to include it in the columns array when setting up the sluggable behavior.
For example :
class ComFooTableFoor extends KDatabaseTableAbstract
{
protected function _initialize(KConfig $config)
{
//Create a custom sluggable behavior
$sluggable = KFactory::get('lib.koowa.database.behavior.sluggable', array('columns' => array('id', 'title')));
$config->behaviors = array($sluggable);
parent::_initialize($config);
}
}
Added KDatabaseRowInterface and KDatabaseRowsetInterface and removed modified check in KDatabaseRowAbstract::save. Database behaviors now need to implemement their own check to see if they need to run or not.
Added KDatabaseBehaviorInterface::getPriority function. KDatabaseBehaviorAbstract implements a default priority of KCommandChain::PRIORITY_NORMAL. Overriden the priority for KDatabaseBehaviorModifiable to PRIORITY_LOW and for KDatabaseBehaviorLockable to PRIORITY_HIGH. This change make it possible to let a behavior control when it's run.
Added KDatabaseTable::getBehavior function. Re-worked addBehaviors function to store behavior instances in the table metadata object. Both getBehavior and getBehaviors will return a behavior instance and not an identifier.
KFilter
Renamed KFilterAlias to KFilterSlug and added configurable separator and lenght support. Default lenght is 100 and default separator is '-'. If you are using KFilterAlias directly make sure to change it to KFilterSlug.
Added new KFilterJson and new $_walk property to KFilterAbstract to allow a filter to determine if it walks the input data or processes it as a whole. Used in the json filter.
KTemplate
Cleanup in the template layer. Renamed 'rules' in favor of the more declarative 'filters' name.
Important changes to the template handling. These change move the responsability for loading template to the KTemplate package and also introduce a more strict local scope bound template approach. Together with the template filtering this will help improve security and simplicity for template developers.
- Added KTemplate::render function which accpets a template path and an associative array of data to use when loading the template. The render function implements a more strick sandbox approach by extracting variables in local scope.
- View template properties are now stored in a $_data array. Added magic getter and setter functions for transparent accessing of the view template properties. The loadTemplate function now calls KTemplate::render and passes the data array. A custom data array can also be defined to allow easy implicit partials support.
- KTemplate is now a none-static singleton. You can use KFactory::get('lib.koowa.template') instead of KTemplate static calls to access it. This allows dependency injection through the factory
- $this doesn't exist anymore in template scope. If you want to gain access to the view object you can use use the $_view variable which is being passed in KView::loadTemplate. We are also passing a $_document variable to gain easy access to the JDocument object.
- @$ are now autoamtically converted to $ instead of $this-> In your own templates it's advised to no longer use @$varname but use $varname instead.
Possible breakages : If you are using @$escape(...) in your template you will need to change this to @escape() remove the $. Above changes could also introduce other breakages in your templates. If you find any issues please open up a new thread.
- Implemented new KTemplateAbstract class that unifies all the template handling functionality and allows easy creation of your own specialized template classes.KTemplateAbstract. KViewTemplate now implements a ROR pattern and DI through KFactory. (Requested by Ash).
- Added registry pattern to KTemplate to allow storing and retrieving of KTemplateAbstract instances by path. KTemplateStream uses the KTemplate registry to look up the invoking template instance by path.
- Refactored the KTemplateStream::stream_open function to look-up the invoking template sandbox object based on the stream's path through KFactory::get('lib.koowa.template)->get($path);
- KTemplate singleton no longers need to be explicitly setup in koowa plugin. Now called through KFactory::get('lib.koowa.template');
These changes allow you to create your own specialised template classes and easily implement your own filtering. It also makes it easy to load templates from just about anywhere, not only based on identifier. This will make it very easy to implement different templating engines.
- Added KViewTemplate::getTemplate to return a template identifier based on the view name which can be either singular or pluarl. The view object is pushed into the template when it's loaded to allow the template to get access to the view.
- Added KTemplate::getView and setView functions to allow loose-coupling between a template and a view. Changed loadHelper function to retrieve the function from the identifier, create the helper and set the template object in the helper.
Note : these changes force you to use a $config array for your helpers. Helpers which implement multiple function parameters are no longer supported.
Modules
- Added ModDefaultModel and ModDefaultTemplate classes and overridden the ModDefaultHtml::getTemplate and getModel function to return the proper identifiers.
- Implemented factory module fallback sequenze to follow the same conventions as the component factory.
The sequence is : Named Module -> Default Module -> Framework Specific -> Framework Default
These changes don't have any immediate impact only that you can now rely on the file system cascading for your modules too.
KRequest
Added accept header parsing support through the new accept function in KRequest. Removed the languages function, functionality is now part of accept. The accept function can be used for content negotiation.
http://groups.google.com/group/nooku-framework/msg/6c1f3549d8fcc86c
KTemplateFilter
Update : Completely refactored the template filtering. We now have
both read/write filtering support. See :
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1998
- Read filters are called when the template is read from disk and before it's passed of to PHP for execution. Read filters are pre-executing filters and allows you to rewrite the template so it can be executed by PHP. A read filter needs to implement the KTemplateFilterRead interface
- Write filters are called when the template has been executed by PHP but before the template is written to the output. Write filters are post-executing filters and allows you modify the ouput. A write filter needs to implement the KTemplateFilterWrite interface.
- Filters can be dynamically added using the KTemplate::addFilters function or using the KViewTemplate::template_filters config option. This is similar to adding your own database behaviors.
- Added KTemplateAbstract::getFilters and KTemplateAbstract::getFilter function. Re-worked KTemplateAbstract::addFilters function to store filters instances in the template $_filters array. Both getFilter and getFilters will return an filter instance and not an identifier.
- Added KTemplateFilterAlias::append function to allow easy adding of alias filters.
- Added @editor alias filter through ComDefaultViewHtml, in both site and admin applications.
http://nooku.assembla.com/spaces/nooku-framework/tickets/51-@editor-template-tag
- KTemplateFilterInterface now implements a getPriority function which return the filter's priority. By default KTemplateFilterAbstract will return KCommandChain::PRIORITY_NORMAL
- Removed KTemplate. Renamed KTemplate::FILTER_READ to KTemplateFilter::MODE_READ and KTemplate::FILTER_WRITE to KTemplateFilter::MODE_WRITE
How does this impact your code ?
@script() and @style() helpers no longer exist and need to be
replaced with <script> or <style> elements. For example
<? @style($mediaurl.'/com_foo/css/foo.css') ?>
becomes
<style src="media://com_foo/css/foo.css" />
<? @script($mediaurl.'/com_foo/js/foo.js)) ?>
becomes
<script src="media://com_foo/js/foo.js" />
Note the media:// which is an alias for the media folder and can be
used anywhere in your templates.
Added <style> and <script> template filters
- Added KTemplateFilterScript. The script filter is a write filter that will try to find any <script src="" /> or <script></script> elements and push them into the document object.
- Added KTemplateFilterStyle. The style filter is a write filter that will try to find any <style src="" /> or <style></style> elements and push them into the document object.
- Removed KTemplateHelperDefault::stylesheet and script functions.
- Removed script, style, id aliases. Alias filter now also implements KTemplateFilterWrite interface and has a seperate alias map for write aliases. Aliases can easily be added using the append function by specifying the filter mode as the second parameter. Default this is KTemplate::FILTER_READ"
Persistent Tabs
Added persistent tab behavior as proposed by Stian.
http://groups.google.com/group/nooku-framework/browse_thread/thread/54213b62307e016e/d835d52e23ab1b94#d835d52e23ab1b94
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1965
KCommandChain
KCommandChain : Added KCommandChain::isRunning function to be able to easily check if a chain is running or not and implemented a simple object pooling solution in KMixinCommandchain to allow for recursive chain use at chain runtime.
http://groups.google.com/group/nooku-framework/browse_thread/thread/e1ee350de28dc7d8#
The object pool pattern has been replaced by a prototype pattern in KMixinCommandchain.
For info on the pattern please see:
http://www.google.com/url?sa=D&q=http://sourcemaking.com/design_patterns/prototype&usg=AFQjCNHbIPA6tSU0cAaqD3mkGAtbFCGd8A
Code changes see
http://nooku.assembla.com/code/nooku-framework/subversion/changesets/1946
Removed KCommandchain::isRunning. KCommandchain no longers stores the active context object. The getContext functions acts as a factory method and will always return a new KCommandContext object. This solves issues when running a chain recursively.