Difference between revisions of "Extension:ArticleProperties"

From Organic Design wiki
(update to current usage method)
Line 1: Line 1:
MediaWiki has a [[MW:Manual:Page props table|page properties table]] which is used to store and manipulate named property lists for articles. The parser includes the required hooks to integrate with this properties system in an event-driven way so that other functionality that depends on these values can be prompted to update their state when changes occur.
+
This extension was created in early 2012 and is available from our extensions repository [http://svn.organicdesign.co.nz/listing.php?repname=extensions&path=%2FArticleProperties%2F here]. The extension which creates a new ''ArticleProperties'' class that is a sub-class of ''Article''. As the name indicates, the extensions main idea was to provide a mechanism by which different classes of articles could be created that each have their own collection of persistent properties.
  
Since template parameters play such an important role in wiki based data structures, it makes sense that the named parameters of the template should be stored in the properties table, so that templates can effectively become objects that have both properties and methods.
+
The ''ArticleProperties'' class is an abstract class (one that's designed to be sub-classed, and not to be instantiated directly). It provides the common functionality allowing all its sub-classes to declare the names and types of properties that should be associated with each article if that type. This common class also offers a simple means by which the page can render its MediaWiki actions in their own specific ways.
  
It also allows much more efficient querying of properties defined in template syntax since these queries can be carried out directly on the properties table rather than having to convert wikitext which is much more expensive even with caching.
+
Each ''ArticleProperties'' sub-class has three static variables, '''table''' which is the name of the database table the class will store it's instances data in, '''columns''' which determine the property names and their database data-types, and an option '''prefix''' to use for column names so they don't conflict with column names of other tables.
  
The first useful application would be to change the ''#recordtable'' parser-function to use the properties table instead of referring to the wikitext. The RA extension has methods for converting to and from value-arrays and wikitext template syntax, so this could be implemented easily be effectively using the properties table as a "live" cache tied into those two methods. But also the queries in the parser-function should be updated too since they no longer need to refer to the text field of the articles.
+
A special page called ''Special:ArticleProperties'' allows the creation of the tables for the sub-classes or to add new columns to them.
  
== ArticleProperties extension ==
+
== Simple example ==
I've created a new extension called [http://svn.organicdesign.co.nz/listing.php?repname=extensions&path=%2FArticleProperties%2F ArticleProperties] which creates a new ''ArticleProperties'' class that is a sub-class of ''Article''. It main purpose is to add a '''properties''' method allowing named properties to be read from and written to an articles ''article_properties'' table.
+
Here's a basic example ''ArticleProperties'' sub-class definition.
 
 
Note that the ''page_props'' table actually already has a use and can't be used for general property storage like this, so I've made the extension create a new table called '''article_properties''' instead. I've added a column for ''namespace'' too which will make querying the properties more efficient since the namespace serves as the ''type'', or ''class'' of property set.
 
{{code|<pre>
 
+--------------+---------------+------+-----+---------+-------+
 
| Field        | Type          | Null | Key | Default | Extra |
 
+--------------+---------------+------+-----+---------+-------+
 
| ap_page      | int(11)      | NO  |    | NULL    |      |
 
| ap_namespace | int(11)      | NO  |    | NULL    |      |
 
| ap_propname  | varbinary(30) | NO  |    | NULL    |      |
 
| ap_value    | blob          | NO  |    | NULL    |      |
 
+--------------+---------------+------+-----+---------+-------+
 
</pre>}}
 
 
 
 
 
It also provides a hook called "ArticlePropertiesClassFromTitle" which allows its sub-classes to easily define rules by which they should become the class used for a MediaWiki page, for example a specified namespace could use a specific ArticleProperties class instead of the standard Article. In this example, the ''FooArticle'' class will be used if the article is in the ''NS_FOO'' namespace:
 
{{code|<php>
 
$wgHooks['ArticlePropertiesClassFromTitle'][] = 'wfUseFoo';
 
function wfUseFoo( $title, &$class ) {
 
if( $title->getNamespace() == NS_FOO ) $class = 'FooArticle';
 
return true;
 
}
 
</php>}}
 
 
 
 
 
These sub-classes can use '''view()''', '''edit()''' and '''save()''' methods to override the default article functionality. The ''edit'' method allows new form fields to be added to the edit form, and the ''save'' method allows these to be processed and written into the ''page_props'' table. To continue the example above, here's s possible definition of the ''FooArticle'' sub-class which creates an additional property.
 
{{code|<php>
 
class FooArticle extends ArticleProperties {
 
 
 
function edit( &$editpage, $out ) {
 
$out->addHTML( $this->label( 'foo-description', 'Foo' ) . $this->input( 'Foo' ) );
 
}
 
 
 
function save( $request ) {
 
$this->properties( array( 'Foo' => $request->getText( 'wpFoo' ) ) );
 
}
 
 
 
}
 
</php>}}
 
The '''input''' and '''label''' methods are supplied by the ''ArticleProperties'' class (along with a number of other useful methods for constructing forms and dealing with page properties). It creates an input named '''wpFoo''' and a label for it using an ''i18n'' message. The '''save''' method then writes this value into the page properties. Any existing value will automatically be shown in the input field.
 
 
 
The ''ArticleProperties'' class also provides a '''query''' method which allows conditions and options to be sent in the same format used by the SQL ''Database::select'' method, and it returns a list of ''Title'' objects for matching articles.
 
 
 
And it also provides a '''table''' method that allows a list of titles to be rendered as an HTML table. The method takes three parameters, the second two are optional. The first is the title-list, the second an array of HTML attributes such as ''class'' and ''id'' that the resulting table should have. And the third parameter is an array of columns that the table should use. If the columns are not provided, they will be extracted from the properties of the first article from the title list.
 
 
 
== ArticleProperties 2.0 ==
 
After using the extension in a real development project for a while, it became obvious that using a single table for all the different ArticleProperties sub-classes was an unnecessary complication and a serious limitation to performance.
 
 
 
For this reason, the ''article_properties'' table has now been deprecated and each ArticleProperties sub-class now has some static class variables to specify its own database table name and structure. This involves three new static variables, '''table''' which is the name of the database table the class will store it's instances data in, '''columns''' which determine the property names and their database data-types, and an option '''prefix''' to use for column names so they don't conflict with column names of other tables. A special page called ''Special:ArticleProperties'' has been created to create the tables for these sub-classes or to add new columns to them. Here's a basic example class:
 
 
{{code|<php>
 
{{code|<php>
 
class LocationExample extends ArticleProperties {
 
class LocationExample extends ArticleProperties {
Line 79: Line 31:
  
  
A column called "page" is automatically added which will contain the page ID of the MediaWiki article that the properties apply to. There is only a single row for any given page.
+
A column called "page" is automatically added which will contain the page ID of the MediaWiki article that the properties apply to. There is only a single row for any given page. Here's the "locations" table created by the ''ArticleProperties'' class for the sub-class shown above.
 
{{code|<pre>
 
{{code|<pre>
 
+------------------+-------------+------+-----+---------+-------+
 
+------------------+-------------+------+-----+---------+-------+
Line 95: Line 47:
 
+------------------+-------------+------+-----+---------+-------+
 
+------------------+-------------+------+-----+---------+-------+
 
</pre>}}
 
</pre>}}
 +
 +
 +
== Determining which class a page should instantiate as ==
 +
The extension provides a hook called "ArticlePropertiesClassFromTitle" which allows its sub-classes to easily define rules by which they should become the class used for a MediaWiki page, for example a specified namespace could use a specific ''ArticleProperties'' sub-class instead of the standard Article. In this example, the ''LocationExample'' class defined above will be used if the article is in the ''NS_FOO'' namespace:
 +
{{code|<php>
 +
$wgHooks['ArticlePropertiesClassFromTitle'][] = 'wfUseFoo';
 +
function wfUseFoo( $title, &$class ) {
 +
if( $title->getNamespace() == NS_FOO ) $class = 'LocationExample';
 +
return true;
 +
}
 +
</php>}}
 +
 +
 +
== Overriding default MediaWiki actions ==
 +
Not only does the ''ArticleProperties'' class allow its sub-classes to have persistent properties, but it also allows the default rendering, editing and saving actions to be overridden so that they can incorporate their properties appropriately. A number of other methods are provided for making interaction with the properties easier which are described in more detail below.
 +
 +
The sub-classes can use '''view()''', '''edit()''' and '''save()''' methods to override the default article functionality. The ''edit'' method allows new form fields to be added to the edit form, and the ''save'' method allows these to be processed and written into the ''page_props'' table. To continue the example above, here's an ''edit'' and ''save'' method added to integrate the page with one of the persistent properties (the ''Notes'' property).
 +
{{code|<php>
 +
class LocationExample extends ArticleProperties {
 +
 +
public static $table = 'locations';
 +
public static $prefix = 'example_';
 +
public static $columns = array(
 +
'Lat'      => 'FLOAT',
 +
'Lon'      => 'FLOAT',
 +
'StreetNo' => 'VARCHAR(8)',
 +
'Street'  => 'VARCHAR(32)',
 +
'City'    => 'VARCHAR(32)',
 +
'Postal'  => 'VARCHAR(16)',
 +
'Country'  => 'VARCHAR(32)',
 +
'Notes'    => 'TEXT'
 +
);
 +
 +
function __construct( $title ) {
 +
parent::__construct( $title );
 +
}
 +
 +
function edit( &$editpage, $out ) {
 +
$out->addHTML( $this->label( 'notes-description', 'Notes' ) . $this->input( 'Notes' ) );
 +
}
 +
 +
function save( $request ) {
 +
$this->properties( array( 'Notes' => $request->getText( 'wpNotes' ) ) );
 +
}
 +
 +
}
 +
</php>}}
 +
The '''input''' and '''label''' methods are supplied by the ''ArticleProperties'' class (along with a number of other useful methods for constructing forms and dealing with page properties). It creates an input named '''wpNotes''' and a label for it using an ''i18n'' message. The '''save''' method then writes this value into the page properties. Any existing value will automatically be shown in the input field.
 +
 +
The ''ArticleProperties'' class also provides a '''query''' method which allows conditions and options to be sent in the same format used by the SQL ''Database::select'' method, and it returns a list of ''Title'' objects for matching articles.
 +
 +
And it also provides a '''table''' method that allows a list of titles to be rendered as an HTML table. The method takes three parameters, the second two are optional. The first is the title-list, the second an array of HTML attributes such as ''class'' and ''id'' that the resulting table should have. And the third parameter is an array of columns that the table should use. If the columns are not provided, they will be extracted from the properties of the first article from the title list.
  
 
=== Passive and non-passive instantiations ===
 
=== Passive and non-passive instantiations ===
Another aspect that has been found to be required is a way for the sub-classes constructors to differentiate between a "passive" instantiation used for example to call methods on ''ArticleProperties'' items in a search result, and non-passive instantiation which occurs when the instance represents the wiki article being currently viewed. The constructor needs to know because the class may make adjustments to the skin or other global elements of the system in the latter case, but should leave them alone for passive instantiations.
+
Another aspect that has been found to be required is a way for the sub-classes constructors to differentiate between a "passive" instantiation used for example to call methods on ''ArticleProperties'' instances in a search result, and "non-passive" instantiation which occurs when the instance represents a wiki article being currently viewed. The constructor needs to know because the class may make adjustments to the skin or other global elements of the system in the latter case, but should leave them alone for passive instantiations.
  
 
When the ''ArticleProperties'' class creates one of the sub-classes via the [[MW:Manual:Hooks/ArticleFromTitle|ArticleFromTitle hook]] it calls the constructor along with the extra '''$passive''' parameter set to ''false''. The following example demonstrates how a sub-class defines its constructor to make adjustments to the skin in the ''non-passive'' case.
 
When the ''ArticleProperties'' class creates one of the sub-classes via the [[MW:Manual:Hooks/ArticleFromTitle|ArticleFromTitle hook]] it calls the constructor along with the extra '''$passive''' parameter set to ''false''. The following example demonstrates how a sub-class defines its constructor to make adjustments to the skin in the ''non-passive'' case.
Line 111: Line 115:
 
</php>}}
 
</php>}}
  
== ArticleInstances extension (possibly include functionality in ArticleProperties) ==
+
== JavaScript integration ==
This extension adds further functionality to the ''ArticleProperties'' class by allowing a special-purpose property to exist in ''ArticleProperties'' articles called "instances".
+
Often ''ArticleProperties'' sub-classes have JavaScript aspects to their ''view'' and ''edit'' methods and these client-side code needs to have access to some of the instances properties. The ''ArticleProperties'' class provides a simple solution to this which is to add an array to the sub-class that defines the object properties which should be made available to the client side. These will be placed into a JavaScript object of the same name as the ''ArticleProperties'' sub-class. Here's an example continuing with the "ExampleLocation" class defined above.
 +
{{code|<php>
 +
class LocationExample extends ArticleProperties {
  
This property is a list of classes and instances (names of other ''ArticleProperties'' classes or ''ArticleID'' values of actual articles of these type). Whenever a method is executed in an ''ArticleProperties'' instance, each instance ID in it's ''instance'' list will be checked to see if it implements that method, and the method called if so. In the case of the item in the ''instance'' list being a class-name instead of an instance ID, the class will be instantiated and will have a "parent" property that refers to the instance that had it in it's list (so that it can access the parent instance and modify it's properties if necessary). This allows the ''ArticleProperties'' instances to behave together as if inherited from one-another using multi-inheritance.
+
var $js = array( 'Lat', 'Lon' );
 
+
</php>}}
An example usage of this functionality could be a "AuthoriseByTxt" class. Another ''Articleproperties'' class such as a User could then click a checkbox in their preferences that ''implements'' an instance of the ''AuthoriseByTxt'' class. This new class can then be responsible for carrying out and presenting the current state of the authorisation procedure in the user's home page.
+
This makes two of the persistent properties available to the JavaScript as ''window.LocationExample.Lat'' and ''window.LocationExample.Lon''.
 
 
The normal revision text of the article can act as a kind of historical log of activity for the instance. When it's finished it removes itself from the parent context and may delete all its properties, but it still exists as still at least one initial revision in history.
 
  
 
== TemplateProperties extension ==
 
== TemplateProperties extension ==
Another extension called [http://svn.organicdesign.co.nz/listing.php?repname=extensions&path=%2FTemplateProperties%2F TemplateProperties] then extends the ''ArticleProperties'' concept by tying in to the events of a specified list of templates (similar to the concept of "record" articles in [[MW:Extension:RecordAdmin|RecordAdmin]]). It adds events so that the named parameters in these specified templates are synchronised with the article's properties.
+
Another extension which is still in development called [http://svn.organicdesign.co.nz/listing.php?repname=extensions&path=%2FTemplateProperties%2F TemplateProperties] then extends the ''ArticleProperties'' concept by tying in to the events of a specified list of templates (similar to the concept of "record" articles in [[MW:Extension:RecordAdmin|RecordAdmin]]). It adds events so that the named parameters in these specified templates are synchronised with the article's properties.
  
 
The extension also adds three parser-functions to allow access to the property information from within article text. The three parser-functions allow for the retrieval of a single property, a query resulting in a simple list, and a query resulting in a sortable table respectively.
 
The extension also adds three parser-functions to allow access to the property information from within article text. The three parser-functions allow for the retrieval of a single property, a query resulting in a simple list, and a query resulting in a sortable table respectively.

Revision as of 00:34, 31 May 2012

This extension was created in early 2012 and is available from our extensions repository here. The extension which creates a new ArticleProperties class that is a sub-class of Article. As the name indicates, the extensions main idea was to provide a mechanism by which different classes of articles could be created that each have their own collection of persistent properties.

The ArticleProperties class is an abstract class (one that's designed to be sub-classed, and not to be instantiated directly). It provides the common functionality allowing all its sub-classes to declare the names and types of properties that should be associated with each article if that type. This common class also offers a simple means by which the page can render its MediaWiki actions in their own specific ways.

Each ArticleProperties sub-class has three static variables, table which is the name of the database table the class will store it's instances data in, columns which determine the property names and their database data-types, and an option prefix to use for column names so they don't conflict with column names of other tables.

A special page called Special:ArticleProperties allows the creation of the tables for the sub-classes or to add new columns to them.

Simple example

Here's a basic example ArticleProperties sub-class definition.

{{{1}}}


A column called "page" is automatically added which will contain the page ID of the MediaWiki article that the properties apply to. There is only a single row for any given page. Here's the "locations" table created by the ArticleProperties class for the sub-class shown above.

+------------------+-------------+------+-----+---------+-------+
| Field            | Type        | Null | Key | Default | Extra |
+------------------+-------------+------+-----+---------+-------+
| example_page     | int(11)     | NO   |     | NULL    |       |
| example_lat      | float       | YES  |     | NULL    |       |
| example_lon      | float       | YES  |     | NULL    |       |
| example_streetno | varchar(8)  | YES  |     | NULL    |       |
| example_street   | varchar(32) | YES  |     | NULL    |       |
| example_city     | varchar(32) | YES  |     | NULL    |       |
| example_postal   | varchar(16) | YES  |     | NULL    |       |
| example_country  | varchar(32) | YES  |     | NULL    |       |
| example_notes    | text        | YES  |     | NULL    |       |
+------------------+-------------+------+-----+---------+-------+


Determining which class a page should instantiate as

The extension provides a hook called "ArticlePropertiesClassFromTitle" which allows its sub-classes to easily define rules by which they should become the class used for a MediaWiki page, for example a specified namespace could use a specific ArticleProperties sub-class instead of the standard Article. In this example, the LocationExample class defined above will be used if the article is in the NS_FOO namespace:

{{{1}}}


Overriding default MediaWiki actions

Not only does the ArticleProperties class allow its sub-classes to have persistent properties, but it also allows the default rendering, editing and saving actions to be overridden so that they can incorporate their properties appropriately. A number of other methods are provided for making interaction with the properties easier which are described in more detail below.

The sub-classes can use view(), edit() and save() methods to override the default article functionality. The edit method allows new form fields to be added to the edit form, and the save method allows these to be processed and written into the page_props table. To continue the example above, here's an edit and save method added to integrate the page with one of the persistent properties (the Notes property).

{{{1}}}

The input and label methods are supplied by the ArticleProperties class (along with a number of other useful methods for constructing forms and dealing with page properties). It creates an input named wpNotes and a label for it using an i18n message. The save method then writes this value into the page properties. Any existing value will automatically be shown in the input field.

The ArticleProperties class also provides a query method which allows conditions and options to be sent in the same format used by the SQL Database::select method, and it returns a list of Title objects for matching articles.

And it also provides a table method that allows a list of titles to be rendered as an HTML table. The method takes three parameters, the second two are optional. The first is the title-list, the second an array of HTML attributes such as class and id that the resulting table should have. And the third parameter is an array of columns that the table should use. If the columns are not provided, they will be extracted from the properties of the first article from the title list.

Passive and non-passive instantiations

Another aspect that has been found to be required is a way for the sub-classes constructors to differentiate between a "passive" instantiation used for example to call methods on ArticleProperties instances in a search result, and "non-passive" instantiation which occurs when the instance represents a wiki article being currently viewed. The constructor needs to know because the class may make adjustments to the skin or other global elements of the system in the latter case, but should leave them alone for passive instantiations.

When the ArticleProperties class creates one of the sub-classes via the ArticleFromTitle hook it calls the constructor along with the extra $passive parameter set to false. The following example demonstrates how a sub-class defines its constructor to make adjustments to the skin in the non-passive case.

{{{1}}}

JavaScript integration

Often ArticleProperties sub-classes have JavaScript aspects to their view and edit methods and these client-side code needs to have access to some of the instances properties. The ArticleProperties class provides a simple solution to this which is to add an array to the sub-class that defines the object properties which should be made available to the client side. These will be placed into a JavaScript object of the same name as the ArticleProperties sub-class. Here's an example continuing with the "ExampleLocation" class defined above.

{{{1}}}

This makes two of the persistent properties available to the JavaScript as window.LocationExample.Lat and window.LocationExample.Lon.

TemplateProperties extension

Another extension which is still in development called TemplateProperties then extends the ArticleProperties concept by tying in to the events of a specified list of templates (similar to the concept of "record" articles in RecordAdmin). It adds events so that the named parameters in these specified templates are synchronised with the article's properties.

The extension also adds three parser-functions to allow access to the property information from within article text. The three parser-functions allow for the retrieval of a single property, a query resulting in a simple list, and a query resulting in a sortable table respectively.

These queries retrieve their data from the article properties so that no text parsing is necessary. But if any value being requested doesn't exist in the page_props database table, then it is extracted from the text (if it exists in the text) and added to the table before being returned.

When the template values are changed by an article's text being edited, the new values are extracted (or possibly obtained by hooking in to the parser's information on the current values, and the page_prop table updated.

It would also make sense for this extension replace RecordAdmin by making it add fields to the edit form for editing the article properties and removing the template syntax from the article.

See also