From de0a9a06291f3bfeaa07e38a7bdaaa7c485a62b5 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 25 Aug 2009 23:03:56 -0500 Subject: [PATCH] August 25, 2009. Commit #256162 by webchick at 22:20 --- developer-docs/CVS/Entries | 8 +- developer-docs/examples/CVS/Entries | 48 +- .../examples/scaffolding_example/CVS/Entries | 10 +- .../scaffolding_example/images/CVS/Entries | 4 +- developer-docs/index.php | 4 +- developer-docs/topics/CVS/Entries | 7 +- .../topics/forms_api_reference.html | 408 ++++++++++------ developer-docs/topics/oop.html | 305 ------------ modules/field/CVS/Entries | 2 +- modules/field/field.test | 435 ++++++++++-------- modules/forum/CVS/Entries | 8 +- modules/image/CVS/Entries | 4 +- modules/image/image.module | 41 +- modules/image/image.test | 11 +- modules/node/CVS/Entries | 2 +- modules/simpletest/tests/CVS/Entries | 2 +- modules/system/CVS/Entries | 4 +- modules/system/system.api.php | 6 +- modules/system/system.module | 8 +- modules/taxonomy/CVS/Entries | 10 +- profiles/default/CVS/Entries | 2 +- 21 files changed, 585 insertions(+), 744 deletions(-) delete mode 100644 developer-docs/topics/oop.html diff --git a/developer-docs/CVS/Entries b/developer-docs/CVS/Entries index c2aca40..dbaebbb 100644 --- a/developer-docs/CVS/Entries +++ b/developer-docs/CVS/Entries @@ -1,6 +1,6 @@ D/examples//// D/topics//// -/example.profile/1.1/Sat Dec 8 18:12:38 2007//THEAD -/globals.php/1.8/Sun Jul 19 14:34:09 2009//THEAD -/index.php/1.23/Sun Jul 19 14:34:09 2009//THEAD -/theme.php/1.2/Sun Jul 19 14:34:09 2009//THEAD +/example.profile/1.1/Fri Aug 7 05:58:20 2009//THEAD +/globals.php/1.8/Fri Aug 7 05:58:20 2009//THEAD +/index.php/1.24/Wed Aug 26 04:03:38 2009//THEAD +/theme.php/1.2/Fri Aug 7 05:58:20 2009//THEAD diff --git a/developer-docs/examples/CVS/Entries b/developer-docs/examples/CVS/Entries index 5539802..c7d5010 100644 --- a/developer-docs/examples/CVS/Entries +++ b/developer-docs/examples/CVS/Entries @@ -1,25 +1,25 @@ -/batch_example.info/1.4/Sun Jul 19 14:34:09 2009//THEAD -/batch_example.install/1.4/Sun Jul 19 14:34:09 2009//THEAD -/batch_example.module/1.5/Sun Jul 19 14:34:09 2009//THEAD -/block_example.info/1.3/Sun Jul 19 14:34:09 2009//THEAD -/block_example.module/1.11/Sun Jul 19 14:34:09 2009//THEAD -/example_element.info/1.3/Sun Jul 19 14:34:09 2009//THEAD -/example_element.module/1.6/Sun Jul 19 14:34:09 2009//THEAD -/filter_example.info/1.4/Sun Jul 19 14:34:09 2009//THEAD -/filter_example.module/1.10/Sun Jul 19 14:34:09 2009//THEAD -/node_access_example.info/1.5/Sun Jul 19 14:34:09 2009//THEAD -/node_access_example.install/1.8/Sun Jul 19 14:34:09 2009//THEAD -/node_access_example.module/1.16/Sun Jul 19 14:34:09 2009//THEAD -/node_example.info/1.4/Sun Jul 19 14:34:09 2009//THEAD -/node_example.install/1.5/Sun Jul 19 14:34:09 2009//THEAD -/node_example.module/1.28/Sun Jul 19 14:34:09 2009//THEAD -/nodeapi_example.info/1.3/Sun Jul 19 14:34:09 2009//THEAD -/nodeapi_example.install/1.4/Fri Oct 5 16:44:50 2007//THEAD -/nodeapi_example.module/1.9/Sun Jul 19 14:34:09 2009//THEAD -/page_example.info/1.3/Sun Jul 19 14:34:09 2009//THEAD -/page_example.module/1.15/Sun Jul 19 14:34:09 2009//THEAD -/trigger_example.info/1.2/Sat Sep 13 16:49:48 2008//THEAD -/trigger_example.module/1.2/Mon Sep 15 21:57:07 2008//THEAD -/xmlrpc_example.info/1.3/Sun Jul 19 14:34:10 2009//THEAD -/xmlrpc_example.module/1.3/Sun Feb 19 23:04:56 2006//THEAD D/scaffolding_example//// +/batch_example.info/1.4/Fri Aug 7 05:58:20 2009// +/batch_example.install/1.4/Fri Aug 7 05:58:20 2009// +/batch_example.module/1.5/Fri Aug 7 05:58:20 2009// +/block_example.info/1.3/Fri Aug 7 05:58:20 2009// +/block_example.module/1.11/Fri Aug 7 05:58:20 2009// +/example_element.info/1.3/Fri Aug 7 05:58:20 2009// +/example_element.module/1.6/Fri Aug 7 05:58:20 2009// +/filter_example.info/1.4/Fri Aug 7 05:58:20 2009// +/filter_example.module/1.10/Fri Aug 7 05:58:20 2009// +/node_access_example.info/1.5/Fri Aug 7 05:58:20 2009// +/node_access_example.install/1.8/Fri Aug 7 05:58:20 2009// +/node_access_example.module/1.16/Fri Aug 7 05:58:20 2009// +/node_example.info/1.4/Fri Aug 7 05:58:20 2009// +/node_example.install/1.5/Fri Aug 7 05:58:20 2009// +/node_example.module/1.28/Fri Aug 7 05:58:20 2009// +/nodeapi_example.info/1.3/Fri Aug 7 05:58:20 2009// +/nodeapi_example.install/1.4/Fri Aug 7 05:58:20 2009// +/nodeapi_example.module/1.9/Fri Aug 7 05:58:20 2009// +/page_example.info/1.3/Fri Aug 7 05:58:20 2009// +/page_example.module/1.15/Fri Aug 7 05:58:20 2009// +/trigger_example.info/1.2/Fri Aug 7 05:58:20 2009// +/trigger_example.module/1.2/Fri Aug 7 05:58:20 2009// +/xmlrpc_example.info/1.3/Fri Aug 7 05:58:20 2009// +/xmlrpc_example.module/1.3/Fri Aug 7 05:58:20 2009// diff --git a/developer-docs/examples/scaffolding_example/CVS/Entries b/developer-docs/examples/scaffolding_example/CVS/Entries index 07b617a..a8196f9 100644 --- a/developer-docs/examples/scaffolding_example/CVS/Entries +++ b/developer-docs/examples/scaffolding_example/CVS/Entries @@ -1,6 +1,6 @@ -/scaffolding_example.admin.inc/1.4/Sun Oct 12 08:52:36 2008//THEAD -/scaffolding_example.info/1.3/Mon Jan 12 06:26:33 2009//THEAD -/scaffolding_example.install/1.4/Sun Oct 12 08:52:37 2008//THEAD -/scaffolding_example.module/1.4/Sun Oct 12 08:52:37 2008//THEAD -/scaffolding_example.pages.inc/1.5/Sun Oct 12 08:52:37 2008//THEAD D/images//// +/scaffolding_example.admin.inc/1.4/Fri Aug 7 05:58:20 2009// +/scaffolding_example.info/1.3/Fri Aug 7 05:58:20 2009// +/scaffolding_example.install/1.4/Fri Aug 7 05:58:20 2009// +/scaffolding_example.module/1.4/Fri Aug 7 05:58:20 2009// +/scaffolding_example.pages.inc/1.5/Fri Aug 7 05:58:20 2009// diff --git a/developer-docs/examples/scaffolding_example/images/CVS/Entries b/developer-docs/examples/scaffolding_example/images/CVS/Entries index fa26ff8..fbff9b0 100644 --- a/developer-docs/examples/scaffolding_example/images/CVS/Entries +++ b/developer-docs/examples/scaffolding_example/images/CVS/Entries @@ -1,3 +1,3 @@ -/edit-delete.png/1.1/Sun Jun 1 18:22:57 2008/-kb/THEAD -/text-editor.png/1.1/Sun Jun 1 18:22:57 2008/-kb/THEAD +/edit-delete.png/1.1/Fri Aug 7 05:58:20 2009/-kb/ +/text-editor.png/1.1/Fri Aug 7 05:58:20 2009/-kb/ D diff --git a/developer-docs/index.php b/developer-docs/index.php index 44b75ab..d16bed6 100755 --- a/developer-docs/index.php +++ b/developer-docs/index.php @@ -1,5 +1,5 @@ - - - - - + + - Form API Reference - - + + + +

Form API Reference

-

This document provides a programmer's reference to the Drupal Form API. If you're interested in step-by-step documentation to help you write forms, please see the Form API QuickStart guide.

+

This document provides a +programmer's reference to the Drupal Form API. If you're interested in +step-by-step documentation to help you write forms, please see the Form API QuickStart guide.

Skip to: Properties | Default Values | Elements

Form Controls

@@ -48,7 +47,7 @@

Form Controls

X = attribute can be used with this type
- = this attribute is not applicable to this type

- + @@ -103,7 +102,7 @@

Form Controls

- + @@ -768,11 +767,11 @@

Form Controls

-
#type checkbox checkboxes X
#ahah#ajax X X - X X
+

Special Elements

- + @@ -821,7 +820,7 @@

Special Elements

- + @@ -1156,7 +1155,7 @@

Special Elements

-
#type button image_button -
#ahah#ajax X X X X -
+

Default Values

Every element automatically has these default values (see system_elements()):

@@ -1175,14 +1174,14 @@

Default Val
  • #name = 'op'
  • #button_type = 'submit'
  • #executes_submit_callback = FALSE
  • -
  • #ahah['event'] = 'click'
  • +
  • #ajax['event'] = 'click'
  • checkbox
  • @@ -1205,7 +1204,7 @@

    Default Val

  • form
  • -
  • +
  • textfield
  • -
  • +
  • @@ -1277,11 +1276,17 @@

    Default Val

     

    Elements

    -

    Note that property names in bold are those that will generally need to be defined when creating this form element. Default values are indicated in parentheses next to property names, if they exist.

    +

    Note that property names in bold +are those that will generally need to be defined when creating this +form element. Default values are indicated in parentheses next to +property names, if they exist.

    button

    -

    Description: Format an action button. When the button is pressed, the form will be submitted to Drupal, where it is validated and rebuilt. The submit handler is not invoked.

    -

    Properties: #access, #after_build, #ahah, #attributes, #button_type (default: submit), #disabled, #element_validate, #executes_submit_callback (default: FALSE), #name (default: op), #parents, #post_render, #prefix, #pre_render, #process, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    +

    Description: +Format an action button. When the button is pressed, the form will be +submitted to Drupal, where it is validated and rebuilt. The submit +handler is not invoked.

    +

    Properties: #access, #after_build, #ajax, #attributes, #button_type (default: submit), #disabled, #element_validate, #executes_submit_callback (default: FALSE), #name (default: op), #parents, #post_render, #prefix, #pre_render, #process, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    Usage example (node.module):

    <?php
    @@ -1293,7 +1298,7 @@

    button

    checkbox

    Description: Format a checkbox.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #return_value (default: 1), #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #return_value (default: 1), #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (contact.module):

    <?php
    @@ -1304,8 +1309,12 @@

    checkbox

    checkboxes

    -

    Description: Format a set of checkboxes. #options is an associative array, where the key is the #return_value of the checkbox and the value is displayed. The #options array can not have a 0 key, as it would not be possible to discern checked and unchecked states.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #description, #disabled, #element_validate, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree (default: TRUE), #type, #weight

    +

    Description: +Format a set of checkboxes. #options is an associative array, where the +key is the #return_value of the checkbox and the value is displayed. +The #options array can not have a 0 key, as it would not be possible to +discern checked and unchecked states.

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #description, #disabled, #element_validate, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree (default: TRUE), #type, #weight

    Usage example (node.module):

    <?php
    @@ -1322,7 +1331,11 @@

    checkboxes

    date

    -

    Description: Format a date selection box. The #default_value will be today's date if no value is supplied. The format for the #default_value and the #return_value is an array with three elements with the keys: 'year', month', and 'day'. For example, array('year' => 2007, 'month' => 2, 'day' => 15)

    +

    Description: +Format a date selection box. The #default_value will be today's date if +no value is supplied. The format for the #default_value and the +#return_value is an array with three elements with the keys: 'year', +month', and 'day'. For example, array('year' => 2007, 'month' => 2, 'day' => 15)

    Properties: #access, #after_build, #attributes, #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (profile.module):

    @@ -1353,7 +1366,12 @@

    fieldset

    file

    Description: Format a file upload field.

    Note: you will need to include $form['#attributes'] = array('enctype' => "multipart/form-data"); in your form. See this handbook page for an example http://drupal.org/node/111782.

    -

    Note: the #required property is not supported (setting it to true will always cause a validation error). Instead, you may want to use your own validation function to do checks on the $_FILES array with #required set to false. You will also have to add your own required asterisk if you would like one.

    +

    Note: +the #required property is not supported (setting it to true will always +cause a validation error). Instead, you may want to use your own +validation function to do checks on the $_FILES array with #required +set to false. You will also have to add your own required asterisk if +you would like one.

    Properties: #access, #after_build, #attributes, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #size (default: 60), #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (upload.module):

    @@ -1372,7 +1390,7 @@

    form

    hidden

    Description: Store data in a hidden form field.

    -

    Properties: #access, #after_build, #ahah, #default_value, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #value, #weight

    +

    Properties: #access, #after_build, #ajax, #default_value, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #value, #weight

    Usage example (block.module):

    <?php
    @@ -1382,11 +1400,13 @@

    hidden

    image_button

    Description: Format a form submit button with an image.

    -

    Properties: #access, #after_build, #ahah, #attributes, #button_type (default: 'submit'), #disabled, #element_validate, #executes_submit_callback (default: TRUE), #parents, #post_render, #prefix, #pre_render, #process, #return_value (default: TRUE), #src, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #button_type (default: 'submit'), #disabled, #element_validate, #executes_submit_callback (default: TRUE), #parents, #post_render, #prefix, #pre_render, #process, #return_value (default: TRUE), #src, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    item

    Description: Generate a display-only form element allowing for an optional title and description.

    -

    Note: since this is a read-only field, setting the #required property will do nothing except theme the form element to look as if it were actually required (i.e. by placing a red star next to the #title).

    +

    Note: since this is a read-only field, setting the #required +property will do nothing except theme the form element to look as if it +were actually required (i.e. by placing a red star next to the #title).

    Properties: #access, #after_build, #description, #element_validate, #markup, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (contact.module):

    @@ -1399,7 +1419,10 @@

    item

    markup

    Description: Generate generic markup for display inside forms. Note that there is no need to declare a form element as #type = 'markup', as this is the default type.

    -

    Note: if you use markup, if your content is not wrapped in tags (generally <p> or <div>), your content will fall outside of collapsed fieldsets.

    +

    Note: +if you use markup, if your content is not wrapped in tags (generally +<p> or <div>), your content will fall outside of collapsed +fieldsets.

    Properties: #access, #after_build, #element_validate, #markup, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #weight

    Usage example (contact.module):

    @@ -1411,7 +1434,7 @@

    markup

    password

    Description: Format a single-line text field that does not display its contents visibly.

    -

    Properties: #access, #after_build, #ahah, #attributes, #description, #disabled, #element_validate, #maxlength (default: 128), #parents, #post_render, #prefix, #pre_render, #process, #required, #size (default: 60), #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #description, #disabled, #element_validate, #maxlength (default: 128), #parents, #post_render, #prefix, #pre_render, #process, #required, #size (default: 60), #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (user.module):

    <?php
    @@ -1438,13 +1461,13 @@

    password_confirm

    radio

    Description: Format a radio button.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #return_value, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #return_value, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example:

    N/A

    radios

    Description: Format a set of radio buttons.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #description, #disabled, #element_validate, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #description, #disabled, #element_validate, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (comment.module):

    <?php
    @@ -1458,7 +1481,7 @@

    radios

    select

    Description: Format a drop-down menu or scrolling selection box.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #description, #disabled, #element_validate, #multiple, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #size, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #description, #disabled, #element_validate, #multiple, #options, #parents, #post_render, #prefix, #pre_render, #process, #required, #size, #suffix, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (system.module):

    <?php
    @@ -1474,7 +1497,7 @@

    select

    submit

    Description: Format a form submit button.

    -

    Properties: #access, #after_build, #ahah, #attributes, #button_type (default: 'submit'), #disabled, #element_validate, #executes_submit_callback (default: TRUE), #name (default: 'op'), #parents, #post_render, #prefix, #pre_render, #process, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #button_type (default: 'submit'), #disabled, #element_validate, #executes_submit_callback (default: TRUE), #name (default: 'op'), #parents, #post_render, #prefix, #pre_render, #process, #submit, #suffix, #theme, #theme_wrapper, #tree, #type, #validate, #value, #weight

    Usage example (locale.module):

    <?php
    @@ -1482,8 +1505,13 @@

    submit

    tableselect

    -

    Description: Format a table of radios or checkboxes, where the columns are given by the #headers property, and the rows are the #options property. The choice of radios or checkboxes depends on the #multiple property, and JavaScript-enabled "check all" buttons are added if the #js_select property is TRUE.

    -

    Properties: #access, #after_build, #ahah, #attributes, #default_value, #element_validate, #empty, #header, #js_select, #multiple, #options, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #weight

    +

    Description: +Format a table of radios or checkboxes, where the columns are given by +the #headers property, and the rows are the #options property. The +choice of radios or checkboxes depends on the #multiple property, and +JavaScript-enabled "check all" buttons are added if the #js_select +property is TRUE.

    +

    Properties: #access, #after_build, #ajax, #attributes, #default_value, #element_validate, #empty, #header, #js_select, #multiple, #options, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #weight

    Usage example (comment.admin.inc):

    <?php
    @@ -1496,7 +1524,7 @@

    tableselect

    textarea

    Description: Format a multiple-line text field.

    -

    Properties: #access, #after_build, #ahah, #attributes, #cols (default: 60), #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #resizable (default: TRUE), #rows (default: 5), #suffix, #text_format, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #cols (default: 60), #default_value, #description, #disabled, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #required, #resizable (default: TRUE), #rows (default: 5), #suffix, #text_format, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (comment.module):

    <?php
    @@ -1510,7 +1538,7 @@

    textarea

    textfield

    Description: Format a single-line text field.

    -

    Properties: #access, #after_build, #ahah, #attributes, #autocomplete_path (default: FALSE), #default_value, #description, #disabled, #element_validate, #field_prefix, #field_suffix, #maxlength (default: 128), #parents, #post_render, #prefix, #pre_render, #process, #required, #size (default: 60), #suffix, #text_format, #theme, #theme_wrapper, #title, #tree, #type, #weight

    +

    Properties: #access, #after_build, #ajax, #attributes, #autocomplete_path (default: FALSE), #default_value, #description, #disabled, #element_validate, #field_prefix, #field_suffix, #maxlength (default: 128), #parents, #post_render, #prefix, #pre_render, #process, #required, #size (default: 60), #suffix, #text_format, #theme, #theme_wrapper, #title, #tree, #type, #weight

    Usage example (forum.module):

    <?php
    @@ -1525,7 +1553,11 @@

    textfield

    token

    -

    Description: Store token data in a hidden form field (generally used to protect against cross-site forgeries). A token element is automatically added to each Drupal form by drupal_prepare_form(), so you don't generally have to add one yourself.

    +

    Description: +Store token data in a hidden form field (generally used to protect +against cross-site forgeries). A token element is automatically added +to each Drupal form by drupal_prepare_form(), so you don't generally +have to add one yourself.

    Properties: #access, #after_build, #default_value, #element_validate, #parents, #post_render, #prefix, #pre_render, #process, #suffix, #theme, #theme_wrapper, #tree, #type, #value, #weight

    value

    @@ -1559,7 +1591,10 @@

    Properties

    #access

    Used by: All elements and forms

    -

    Description: Whether the element is accessible or not; when FALSE, the element is not rendered and the user submitted value is not taken into consideration.

    +

    Description: +Whether the element is accessible or not; when FALSE, the element is +not rendered and the user submitted value is not taken into +consideration.

    Values: TRUE or FALSE.

    #action

    @@ -1570,16 +1605,27 @@

    #action

    <?php
    $form
    ['#action'] = url('comment/reply/'. $edit['nid']);
    ?>

    -

    Do not forget the # before property names.

    #after_build

    Used by: All elements and forms

    Description: An array of function names which will be called after the form or element is built.

    Usage example (system.module):

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    - $form
    ['files']['file_directory_path'] = array(
      '#type' => 'textfield',
      '#title' => t('File system path'),
      '#default_value' => file_directory_path(),
      '#maxlength' => 255,
      '#description' => t('A file system path where the files will be stored. This directory has to exist and be writable by Drupal. If the download method is set to public this directory has to be relative to Drupal installation directory, and be accessible over the web. When download method is set to private this directory should not be accessible over the web. Changing this location after the site has been in use will cause problems so only change this setting on an existing site if you know what you are doing.'),
    + $form
    ['files']['file_directory_path'] = array(
      '#type' => 'textfield',
      '#title' => t('File system path'),
      '#default_value' => file_directory_path(),
      '#maxlength' => 255,
      '#description' => t('A +file system path where the files will be stored. This directory has to +exist and be writable by Drupal. If the download method is set to +public this directory has to be relative to Drupal installation +directory, and be accessible over the web. When download method is set +to private this directory should not be accessible over the web. +Changing this location after the site has been in use will cause +problems so only change this setting on an existing site if you know +what you are doing.'),
    );

    $form['#after_build'] = array('system_check_directory');

    ...
    @@ -1587,9 +1633,8 @@

    #after_build

    function system_check_directory($form_element, &$form_state) {
      file_check_directory($form_element['#value'], FILE_CREATE_DIRECTORY, $form_element['#parents'][0]);
      return $form_element;
    }

    ?>

    -

    Property names without # signs causes havoc.

    -

    #ahah

    +

    #ajax

    Used by: button, checkbox, @@ -1605,100 +1650,136 @@

    #ahah

    textarea, textfield

    -

    An array of elements whose values control the behavior of the element with respect to the Drupal AHAH javascript methods.

    -

    The #ahah name refers to AHAH javascript replacement (Asychronous HTML and HTTP), a relative of AJAX. - AHAH is a form of javascript page replacement that is both easy and straight forward. In a nutshell an AHAH request follows these steps:

    +

    An array of elements whose values control the behavior of the element with respect to the Drupal AJAX javascript methods.

    +

    AJAX +(Asynchronous Javascript and XML) is a term used for dynamic +communication between the browser and the server, without page reloads. +A purist would insist that the Drupal technique is AHAH +(Asychronous HTML and HTTP) because XML is not used. Until Drupal 7, +AJAX was known as AHAH. In a nutshell an AJAX request follows these +steps:

      -
    1. Drupal builds a form element with a set of #ahah properties. The misc/ahah.js file is included on the page automatically.
    2. -
    3. ahah.js finds all the form elements on the page that have an #ahah['callback'] or an #ahah['path'] set and adds an event handler for the #ahah['event'] set on that form element.
    4. -
    5. When the #ahah['event'] occurs on the element (such as 'click'), the AHAH request is made to the Drupal path of the element's #ahah['path']. If an #ahah['callback'] has been specified, this path will always by system/ahah.
      +
    6. Drupal builds a form element with a set of #ajax properties. The misc/ajax.js file is included on the page automatically.
    7. +
    8. ajax.js finds all the form elements on the page that have an #ajax['callback'] or an #ajax['path'] set and adds an event handler for the #ajax['event'] set on that form element.
    9. +
    10. When the #ajax['event'] occurs on the element (such as 'click'), the AJAX request is made to the Drupal path of the element's #ajax['path']. If an #ajax['callback'] has been specified, this path will always by system/ajax.
    11. -
    12. Drupal generates HTML in a second request in the background. While the user waits for the callback to execute a throbber or progress bar is shown as determined by #ahah['progress']. The result is returned to the original page containing the form element.
    13. -
    14. ahah.js gets the result and inserts the returned HTML into the #ahah['wrapper']. - Depending on the #ahah['method'], the result may affect the wrapper in different ways.
    15. +
    16. Drupal +generates HTML in a second request in the background. While the user +waits for the callback to execute a throbber or progress bar is shown +as determined by #ajax['progress']. The result is returned to the original page containing the form element.
    17. +
    18. ajax.js gets the result and inserts the returned HTML into the #ajax['wrapper']. + Depending on the #ajax['method'], the result may affect the wrapper in different ways.
    -

    #ahah['callback']

    +

    #ajax['callback']

    -

    Description: Specifies the name of a callback function which will be called to render the necessary HTML to return to the original page. This callback function is given the $form and $form_state parameters, allowing it to drupal_render() the necessary portion of the form.

    +

    Description: +Specifies the name of a callback function which will be called to +render the necessary HTML to return to the original page. This callback +function is given the $form and $form_state parameters, allowing it to drupal_render() the necessary portion of the form and then return it.

    Note: +'callback' and 'path' are mutually exclusive. 'callback' is easier to +use and requires less code setup, but sometimes does not provide the +capabilities required for some functions.

    -

    Note: This property only works on elements with a #type of submit or button because it needs to call that button's #submit and #validate functions. Use of this property for dynamic forms is recommended rather than using a #ahah['path'], since this approach will degrade easily to a non-JavaScript behavior.

    +

    Note: This property only works on elements with a #type of submit or button because it needs to call that button's #submit and #validate functions. Use of this property for dynamic forms is recommended rather than using a #ajax['path'], since this approach will degrade easily to a non-JavaScript behavior.

    Values: String containing a function name.

    Usage example (poll.module):

    - - <?php
    /**
     * Implementation of hook_form().
     */
    function poll_form(&$node$form_state) {
      ...
      
    $form['choice_wrapper']['poll_more'] = array(
        
    '#type' => 'submit',
        
    '#value' => t('More choices'),
        
    '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."),
        
    '#weight' => 1,
        
    '#submit' => array('poll_more_choices_submit'), // If no javascript action.
        
    '#ahah' => array(
          
    'callback' => 'poll_choice_js',
          
    'wrapper' => 'poll-choices',
          
    'method' => 'replace',
          
    'effect' => 'fade',
        ),
      );
      ...
    }

    /**
     * Menu callback for AHAH additions. Render the new poll choices.
     */
    function poll_choice_js($form$form_state) {
      
    $choice_form $form['choice_wrapper']['choice'];

      
    // Prevent duplicate wrappers.
      
    unset($choice_form['#prefix'], $choice_form['#suffix']);
      
    $output theme('status_messages') . drupal_render($choice_form);

      
    drupal_json(array('status' => TRUE'data' => $output));
    }
    ?>
    + + <?php
    /**
     * Implementation of hook_form().
     */
    function poll_form(&$node$form_state) {
      ...
      
    $form['choice_wrapper']['poll_more'] = array(
        
    '#type' => 'submit',
        
    '#value' => t('More choices'),
        
    '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."),
        
    '#weight' => 1,
        
    '#submit' => array('poll_more_choices_submit'), // If no javascript action.
        
    '#ajax' => array(
          
    'callback' => 'poll_choice_js',
          
    'wrapper' => 'poll-choices',
          
    'method' => 'replace',
          
    'effect' => 'fade',
        ),
      );
      ...
    }

    /**
     * Menu callback for AJAX additions. Render the new poll choices.
     */
    function poll_choice_js($form$form_state) {
      
    $choice_form $form['choice_wrapper']['choice'];

      
    // Prevent duplicate wrappers.
      
    unset($choice_form['#prefix'], $choice_form['#suffix']);
      
    $output theme('status_messages') . drupal_render($choice_form);
      return $output
    ;
    }
    ?>
    -

    #ahah['effect']

    +

    #ajax['effect']

    -

    Description: Specifies the effect used when adding the content from an AHAH request.

    +

    Description: Specifies the effect used when adding the content from an AJAX request.

    Values: String. Possible values: 'none' (default), 'fade', 'slide'. If the interface elements library is installed, any effect with the name effectToggle may also be used.

    -

    #ahah['event']

    +

    #ajax['event']

    Description: When this event occurs to this element, Drupal will perform an HTTP request in the background via Javascript.

    Values: String. Possible values: 'click', 'blur', 'change'. - Note that #ahah['event'] does not need to be explicitly specified. Although it can be manually set, usually the default value will be sufficient.

    + Note that #ajax['event'] does not need to be explicitly specified. Although it can be manually set, usually the default value will be sufficient.

    -

    #ahah['method']

    -

    Description: Modify the behavior of the returned HTML from an AHAH request when inserting into the #ahah_wrapper. If not set, the returned HTML will replace the contents of the wrapper element, but it's also possible to use any of the available jQuery operations for DOM manipulation.

    +

    #ajax['method']

    +

    Description: Modify the behavior of the returned HTML from an AJAX request when inserting into the #ajax['wrapper']. If not set, the returned HTML will replace the contents of the wrapper element, but it's also possible to use any of the available jQuery operations for DOM manipulation.

    Values: String. Possible values: 'replace' (default), 'after', 'append', 'before', 'prepend'.

    -

    #ahah['path']

    +

    #ajax['path']

    -

    Description: If set, this property triggers AHAH behaviors on a form element. This is the Drupal menu path to a callback function which will generate HTML and return the string of HTML to Drupal. The result will be placed in the div specified in #ahah['wrapper']. This property is infrequently used in Drupal 7 and higher, because it is set automatically if using the #ahah['callback'] property. In which case the path is automatically set to system/ahah, which provides a menu callback that can be used for most situations.

    +

    Description: +This is the Drupal menu path to a callback function which will generate +HTML and return the string of HTML to Drupal. The result will be placed +in the div specified in #ajax['wrapper']. This property is infrequently used in Drupal 7 and higher, because it is set automatically if using the #ajax['callback'] property. In which case the path is automatically set to system/ajax, which provides a menu callback that can be used for many situations.

    Note: 'path' and 'callback' are mutually exclusive.

    Values: String containing a Drupal menu path.

    Usage example (upload.module):

    -
    <?php
    /**
    * Implementation of hook_menu().
    */
    function upload_menu() {
     
    $items['upload/js'] = array(
       
    'page callback' => 'upload_js',
       
    'access arguments' => array('upload files'),
       
    'type' => MENU_CALLBACK,
      );
      ...
      return
    $items;
    }

    ...

    function
    _upload_form($node) {
      ...
       
    $form['new']['attach'] = array(
         
    '#type' => 'submit',
         
    '#value' => t('Attach'),
         
    '#name' => 'attach',
         
    '#ahah' => array(
           
    'path' => 'upload/js',
           
    'wrapper' => 'attach-wrapper',
           
    'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
          ),
         
    '#submit' => array('node_form_submit_build_node'),
        );
      ...
    return
    $form;
    ?>
    +
    <?php
    /**
    * Implementation of hook_menu().
    */
    function upload_menu() {
     
    $items['upload/js'] = array(
       
    'page callback' => 'upload_js',
       
    'access arguments' => array('upload files'),
       
    'type' => MENU_CALLBACK,
      );
      ...
      return
    $items;
    }

    ...

    function
    _upload_form($node) {
      ...
       
    $form['new']['attach'] = array(
         
    '#type' => 'submit',
         
    '#value' => t('Attach'),
         
    '#name' => 'attach',
         
    '#ajax' => array(
           
    'path' => 'upload/js',
           
    'wrapper' => 'attach-wrapper',
           
    'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
          ),
         
    '#submit' => array('node_form_submit_build_node'),
        );
      ...
    return
    $form;
    ?>
    -

    #ahah['path'] is set to 'upload/js', which has a menu item defined in the same module. Then the menu hook will call the 'upload_js' function which generates HTML and returns it to original page.

    +

    #ajax['path'] +is set to 'upload/js', which has a menu item defined in the same +module. Then the menu hook will call the 'upload_js' function which +generates HTML and returns it to original page.

    -

    #ahah['progress']

    +

    #ajax['progress']

    -

    Description: Choose either a throbber or progress bar that is displayed while awaiting a response from the callback, and add an optional message.

    +

    Description: +Choose either a throbber or progress bar that is displayed while +awaiting a response from the callback, and add an optional message.

    Values: Array.

    Possible keys: 'type', 'message', 'url', 'interval'

    Possible values:

      -
    • #ahah['progress']['type'] String. Possible values: 'throbber' (default), 'bar'. -
    • -
    • #ahah['progress']['message'] String. An optional message to the user; should be wrapped with t(). +
    • #ajax['progress']['type'] String. Possible values: 'throbber' (default), 'bar'.
    • -
    • #ahah['progress']['url'] String. The optional callback path to use to determine how full the progress bar is (as defined in progress.js). Only useable when 'type' is 'progress'. -
    • -
    • #ahah['progress']['interval'] String. The iterval to be used in updating the progress bar (as defined in progress.js). Ony used if 'url' is defined and 'type' is 'progress'. +
    • #ajax['progress']['message'] String. An optional message to the user; should be wrapped with t().
    • +
    • #ajax['progress']['url'] +String. The optional callback path to use to determine how full the +progress bar is (as defined in progress.js). Only useable when 'type' +is 'progress'.
    • +
    • #ajax['progress']['interval'] +String. The iterval to be used in updating the progress bar (as defined +in progress.js). Ony used if 'url' is defined and 'type' is 'progress'. +
    -

    Usage example: see above useage in upload.module

    - -

    #ahah['wrapper']

    -

    Description: This property defines the HTML id attribute of an element on the page will server as the destination for HTML returned by an AHAH request. Usually, a div element is used as the wrapper, as it provides the most flexibility for placement of elements before, after, or inside of it's HTML tags. This property is required for using AHAH requests in on a form element.

    +

    Usage example: see above useage in upload.module

    + +

    #ajax['wrapper']

    +

    Description: +This property defines the HTML id attribute of an element on the page +which will be replaced by the html returned by the 'path' or 'callback' +function. Usually, a div element is used as the wrapper, as it provides +the most flexibility for placement of elements before, after, or inside +of its HTML tags. This property is required for using AJAX requests in +on a form element.  Note that the entire element with this ID is +replaced, not just the contents of the element.

    Values: String containg a valid id attribute of an HTML element on the same page.

    Usage example (upload.module):

    -
        $form['new']['attach'] = array(
         
    '#type' => 'submit',
         
    '#value' => t('Attach'),
         
    '#name' => 'attach',
         
    '#ahah' => array(
           
    'path' => 'upload/js',
           
    'wrapper' => 'attach-wrapper',
           
    'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
          ),
         
    '#submit' => array('node_form_submit_build_node'),
        );
    +
        $form['new']['attach'] = array(
         
    '#type' => 'submit',
         
    '#value' => t('Attach'),
         
    '#name' => 'attach',
         
    '#ajax' => array(
           
    'path' => 'upload/js',
           
    'wrapper' => 'attach-wrapper',
           
    'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
          ),
         
    '#submit' => array('node_form_submit_build_node'),
        );

    #attributes

    Used by: button, checkbox, checkboxes, date, fieldset, file, form, image_button, password, radio, radios, select, submit, tableselect, textarea, textfield, weight

    Description: Additional HTML attributes, such as 'class' can be set using this mechanism.

    Values: Any HTML attribute not covered by other properties, e.g. class (for control types), enctype (for forms).

    Usage example (search.module):

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    - $form
    ['#attributes'] = array('class' => 'search-form');
    ?>
    + $form['#attributes'] = array('class' => array('search-form'));
    ?>
    -

    The # is mandatory before property names.

    #autocomplete_path

    Used by: textfield

    @@ -1714,14 +1795,12 @@

    #autocomplete_path => $node->name,
      '#weight'
    => -1,
    );
    ?>

    -

    It's very important that you do not forget the # before property names.

    #built

    Used by: form

    Description: Used to ascertain whether or not a form element has been built yet.

    Values: TRUE or FALSE

    Usage example: INTERNAL. See the _form_builder function in form.inc.

    -

    The first character of property names is #.

    #button_type

    Used by: button, image_button, submit

    @@ -1736,7 +1815,6 @@

    #button_type

      '#button_type' => 'submit',
      '#submit'
    => TRUE,
    );
    ?>
    -

    Developers should take care to not forget the first # character in property names.

    #collapsed

    Used by: fieldset

    @@ -1754,7 +1832,6 @@

    #collapsed

      '#tree'
    => TRUE,
    );
    ?>
    -

    Child names do not have a # as first char, but property names do.

    #collapsible

    Used by: fieldset

    @@ -1772,7 +1849,6 @@

    #collapsible

      '#tree'
    => TRUE,
    );
    ?>
    -

    One of the most important things about form API is not forgetting the # where it's appropriate.

    #cols

    Used by: textarea

    @@ -1789,7 +1865,6 @@

    #cols

      '#rows'
    => 5,
    );
    ?>
    -

    The first # lets form API decide between a property name and a child.

    #default_tab

    Used by: vertical_tabs

    @@ -1810,11 +1885,12 @@

    #default_value

      '#required' => TRUE,
    );
    ?>
    -

    How many different forms of warnings can I figure out to tell you that the first # in property names are important?

    #delta

    Used by: weight

    -

    Description: Number of weights to have selectable. For example, with $delta => 10, the weight selection box would display numbers from -10 to 10.

    +

    Description: +Number of weights to have selectable. For example, with $delta => +10, the weight selection box would display numbers from -10 to 10.

    Values: A positive number

    Usage example (menu.module):

    @@ -1827,7 +1903,6 @@

    #delta

      '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
    );
    ?>
    -

    One of the most common errors for 4.7 is leaving out the # in the beginning of property names.

    #description

    Used by: checkbox, checkboxes, date, fieldset, file, item, password, password_confirm, radio, radios, select, textarea, textfield, weight

    @@ -1856,11 +1931,13 @@

    #disabled

            $form['theme_settings'][$name]['#disabled'] = TRUE;
          }
    ?>
    -

    It can not be stressed enough that property names begin with a #.

    #element_validate

    Used by: any element

    -

    Description: A list of custom validation functions that need to be passed. The functions must use form_error() or form_set_error() to set an error if the validation fails.

    +

    Description: +A list of custom validation functions that need to be passed. The +functions must use form_error() or form_set_error() to set an error if +the validation fails.

    Values: an array of function names to be called to validate this element (and/or its children).

    A validation function for an element takes the element and the form state as parameters, and has the form:

    @@ -1874,6 +1951,10 @@

    #element_validate

    Usage example (filter.module):

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    @@ -1890,7 +1971,6 @@

    #element_validate

    ?>
    -

    While #element_validate makes sure that user data is valid, nothing validates the form for missing # in the beginning of property name.

    #empty

    @@ -1950,12 +2030,17 @@

    #field_suffix

    #header

    Used by: tableselect

    Description: Column headers.

    -

    Values: Associative array where the keys are the field names to use for each column, and the values are the translated text to display for each column header.

    +

    Values: +Associative array where the keys are the field names to use for each +column, and the values are the translated text to display for each +column header.

    #js_select

    Used by: tableselect

    Description: Whether to include a select all checkbox.

    -

    Values: TRUE to add a select all box to each row of the table, FALSE to omit the select all box. Only applies if #multiple is also TRUE.

    +

    Values: +TRUE to add a select all box to each row of the table, FALSE to omit +the select all box. Only applies if #multiple is also TRUE.

    #markup

    Used by: item, markup

    @@ -1963,13 +2048,13 @@

    #markup

    Values: Text (valid HTML)

    Usage example (contact.module):

    - <?php
    -   $form
    ['from'] = array(
    -    
    '#type' => 'item',
    -    
    '#title' => t('From'),
    -    
    '#markup' => check_plain($user->name) . ' &lt;' . check_plain($user->mail) . '&gt;',
    + <?php
    +   $form
    ['from'] = array(
    +    
    '#type' => 'item',
    +    
    '#title' => t('From'),
    +    
    '#markup' => check_plain($user->name) . ' &lt;' . check_plain($user->mail) . '&gt;',
      );
    -
    ?>
    +
    ?>

    #maxlength

    @@ -1987,7 +2072,6 @@

    #maxlength

      '#maxlength'
    => 128,
      '#required'
    => TRUE,
    );
    ?>
    -

    You can spare lots of debug time by not forgetting the # in the beginning of property names.

    #method

    @@ -2016,13 +2100,18 @@

    #multiple

      '#size' => $multiple ? min(12, count($options)) : 0,
      
    '#weight' => -15,
    );
    ?> -

    By now, you probably know that the first character of property names is # but I thought some repetition can't hurt.

    #name

    Used by: button, submit

    -

    Description: INTERNAL, except for buttons. All button and submit elements on a form should have the same name, which is set to 'op' by default in Drupal. This does not apply to image buttons. For non-button elements, Drupal sets the name by using 'foo' in $form['foo'] as well as any parents of the element.

    +

    Description: +INTERNAL, except for buttons. All button and submit elements on a form +should have the same name, which is set to 'op' by default in Drupal. +This does not apply to image buttons. For non-button elements, Drupal +sets the name by using 'foo' in $form['foo'] as well as any parents of +the element.

    Values: String.

    +

    #options

    Used by: checkboxes, radios, select, tableselect

    Description: Selectable options for a form element that allows multiple choices.

    @@ -2036,7 +2125,6 @@

    #options

      '#default_value' => variable_get('comment_preview', 1),
      '#options'
    => array(t('Optional'), t('Required')),
    );
    ?> -

    If you are fed up with comments about # being the first character of property names, then sorry, but it's important.

    #parents

    @@ -2054,7 +2142,6 @@

    #parents

      '#options' => array(t('Published'), t('Not published')),
      '#weight'
    => -1,
    );
    ?>
    -

    While the first character of the words parents and property is P, the first character of every property is #.

    #post_render

    @@ -2062,6 +2149,10 @@

    #post_render

    Description: Function(s) to call after rendering in drupal_render() has occured.

    Values: An array of function names to call.

    Usage example:

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    $form
    ['admin']['#post_render'] = array('admin_form_html_cleanup');
    ?>
    @@ -2082,7 +2173,6 @@

    #prefix

      '#suffix' => '</div>',
      '#tree'
    => TRUE,
    );
    ?> -

    The correct prefix of a property name is the #.

    #pre_render

    @@ -2090,6 +2180,10 @@

    #pre_render

    Description: Function(s) to call before rendering in drupal_render() has occured.

    Values: An array of function names to call.

    Usage example:

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    $form
    ['admin']['#pre_render'] = array('admin_form_tab_errors');
    ?>
    @@ -2100,11 +2194,19 @@

    #printed

    INTERNAL. Used to determine whether or not a form element has been printed yet.

    #process

    Used by: All

    -

    Description: An array of functions that are called when an element is processed. Using this callback, modules can "register" further actions. For example the "radios" form type is expanded to multiple radio buttons using a processing function.

    +

    Description: +An array of functions that are called when an element is processed. +Using this callback, modules can "register" further actions. For +example the "radios" form type is expanded to multiple radio buttons +using a processing function.

    Values: Array of function names (strings)

    Usage example (system.module) in system_elements():

    +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    - $type['radios'] = array('#input' => TRUE'#process' => array('expand_radios')); + $type['radios'] = array('#input' => TRUE'#process' => array('expand_radios'));

    In this example, the function expand_radios is called; the entire radios element is passed in as the argument. This function adds new elements of type radio to the existing radios element array, with keys and values corresponding to the processed element's #options.

    @@ -2112,7 +2214,10 @@

    #processed

    INTERNAL. Used to ascertain whether or not a form element has been processed (ie: expanded to multiple elements).

    #redirect

    Used by: form

    -

    Description: The default goto value after form is submitted. This value should be returned by a form's submit callback function, but altering another form's #redirect value by using hook_form_alter() can be useful to change where that form redirects after it is submitted. Also see #action.

    +

    Description: +The default goto value after form is submitted. This value should be +returned by a form's submit callback function, but altering another +form's #redirect value by using hook_form_alter() can be useful to change where that form redirects after it is submitted. Also see #action.

    Values: An internal path or an array of arguments to pass to url(). The value may also be set to FALSE to prevent redirection after form submission.

    Usage example (locale.inc):

    @@ -2120,12 +2225,14 @@

    #redirect

    <?php
    $form
    ['#redirect'] = 'node';
    ?>

    <?php
    $form
    ['#redirect'] = array('user/login', 'destination=node');
    ?>

    <?php
    $form
    ['#redirect'] = FALSE;
    ?>

    -

    Only heathens leave out the # before property names.

    #required

    Used by: checkbox, checkboxes, date, file, password, password_confirm, radio, radios, select, textarea, textfield, weight

    -

    Description: Indicates whether or not the element is required. This automatically validates for empty fields, and flags inputs as required. File fields are NOT allowed to be required.

    +

    Description: +Indicates whether or not the element is required. This automatically +validates for empty fields, and flags inputs as required. File fields +are NOT allowed to be required.

    Values: TRUE or FALSE

    Usage example (forum.module):

    @@ -2138,7 +2245,6 @@

    #required

      '#maxlength' => 128,
      '#required'
    => TRUE,
    );
    ?> -

    You know what's absolutely required? The # in the beginning of property names.

    #resizable

    @@ -2160,7 +2266,6 @@

    #return_value

      '#default_value' => 0,
      '#description'
    => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."),
    );
    ?> -

    Let's make this remark short: #

    #rows

    @@ -2177,7 +2282,6 @@

    #rows

      '#cols' => 60,
      '#rows'
    => 5,
    );
    ?> -

    Your code will row with you if you forget the # in the beginning of property names.

    #size

    @@ -2194,7 +2298,6 @@

    #size

              '#size' => 30,
             
    '#default_value' => $edit['homepage'],
            );
    ?> -

    If you forget the # sign...

    #src

    Used by: image_button

    @@ -2206,7 +2309,10 @@

    #submit

    Description: Contains a list of submit callbacks to be excuted on the form or only when a specific button is clicked.

    Values: An array of function names.

    Usage example (menu.module):

    - +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    @@ -2257,12 +2363,14 @@

    #suffix

      '#suffix'
    => '</div>',
      '#tree'
    => TRUE,
    );
    ?>
    -

    Suffix is almost like suffer, which will happen if you forget the # at the beginning of property names.

    #text_format

    Used by: textarea, textfield

    -

    Description: Specifies the input format used by default for the field. Drupal will automatically expand the element to a 'value' and a 'format' element later.

    +

    Description: +Specifies the input format used by default for the field. Drupal will +automatically expand the element to a 'value' and a 'format' element +later.

    Values: A positive number.

    Usage example (comment.module):

    @@ -2314,7 +2422,10 @@

    #title

    #tree

    Used by: All

    -

    Description: Used to allow collections of form elements. Normally applied to the "parent" element, as the #tree property cascades to sub-elements. Use where you previously used ][ in form_ calls. For more information, see #tree and #parents in the handbook.

    +

    Description: +Used to allow collections of form elements. Normally applied to the +"parent" element, as the #tree property cascades to sub-elements. Use +where you previously used ][ in form_ calls. For more information, see #tree and #parents in the handbook.

    Values: TRUE or FALSE

    Usage example (system.module):

    @@ -2345,8 +2456,15 @@

    #type

    #validate

    Used by: button, image_button, form, submit

    -

    Description: A list of custom validation functions that need to be passed.This is usually used to add additional validation functions to a form, or to use an alternate function rather than the default form validation function which is the form ID with _validate appended to it.

    -

    Values: An array of function names. Each such function will take $form and $form_state as parameters and should use form_set_error() if the form values do not pass validation. For example:

    +

    Description: +A list of custom validation functions that need to be passed.This is +usually used to add additional validation functions to a form, or to +use an alternate function rather than the default form validation +function which is the form ID with _validate appended to it.

    +

    Values: +An array of function names. Each such function will take $form and +$form_state as parameters and should use form_set_error() if the form +values do not pass validation. For example:

    function test_form_validate($form, &$form_state) {
    @@ -2357,7 +2475,10 @@

    #validate

    Usage example (node.module):

    - +

    Note: If you are altering an existing form via +hook_form_alter() or a similar means, be careful with this property! +You will probably want to add to the existing array rather than writing +over it, so don't follow this usage example exactly.

    <?php
    @@ -2408,8 +2529,9 @@

    #weight

      '#default_value'
    => ($node->parent ? $node->parent : arg(4)),
      '#options'
    => book_toc($node->nid),
      '#weight'
    => -15,
    -   
    '#description' => t('The parent that this page belongs in. Note that pages whose parent is &lt;top-level&gt; are regarded as independent, top-level books.'),
    +   
    '#description' => t('The +parent that this page belongs in. Note that pages whose parent is +&lt;top-level&gt; are regarded as independent, top-level books.'),
    );
    ?>
    - - + \ No newline at end of file diff --git a/developer-docs/topics/oop.html b/developer-docs/topics/oop.html deleted file mode 100644 index 306a053..0000000 --- a/developer-docs/topics/oop.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - Drupal Programming from an Object-Oriented Perspective - - - -

    Drupal Programming from an Object-Oriented Perspective

    - -

    Drupal often gets criticized by newcomers who believe that - object-oriented programming (OOP) is always the best way to design - software architecture: since they do not see the word "class" - often in the Drupal code, they believe that Drupal's design must - be inferior. But while it is true that Drupal does not make heavy - use of the OOP features of PHP, it is a mistake to think that the - use of classes is synonymous with object-oriented design. In fact, - Drupal does have many object-oriented design features in its - central architecture. This article describes the architecture of - Drupal from an OOP perspective, so that programmers comfortable - with OOP can feel more at home in the Drupal code base.

    - -

    Motivations for Current Design

    - -

    Back as far as version 4.6 of Drupal, the decision was made not to - use PHP's class - construct. This decision was made for several reasons.

    - -

    First, PHP's support for object-oriented constructs was much less - mature at the time of Drupal's design. Drupal was built on PHP 4, and most - of the improvements in PHP 5 relate to its object-oriented - features.

    - -

    Second, Drupal code is highly compartmentalized into modules, each of - which defines its own set of functions. The inclusion of files is handled - inside functions as well; PHP's performance suffers if unneeded code is - included, so Drupal attempts to load as little code as possible - per request. This is a critical consideration, especially in the absence of - a PHP - accelerator: the act of compiling the code accounts for more than half - of the time to complete a Drupal page request. Functions are therefore - defined inside other functions in Drupal, with respect to the runtime - scope. While this is perfectly legal for bare functions, PHP does not - allow the same kind of nesting with class declarations. This means - that the inclusion of files defining classes must be "top-level," - and not inside any function, which leads either to slower code - (always including the files defining classes) or a large amount of - logic in the main index.php file.

    - -

    Finally, using PHP classes to implement Drupal constructs would - be difficult, due to the use of some advanced object-oriented - design patterns that PHP's OOP system doesn't support. For - instance (see following sections for details), the Drupal theme system - relies on an OOP concept similar to Objective-C's - "categories", which is not present in PHP.

    - -

    In Drupal 7, some of this has changed: Drupal 7, for the first - time, requires PHP 5, and there are now a few modules in Drupal - that use PHP's class system. However, the central structure of - Drupal remains the same, and most of Drupal still does not use the - PHP class construct.

    - -

    OOP Concepts in Drupal

    - -

    Despite the lack of explicitly-declared classes in Drupal, many - object-oriented paradigms are still used in its design. There are many sets - of "essential features" that are said to be necessary to classify a system - as object-oriented; we will look at one of the more - popular definitions and examine some ways in which the central - architecture of Drupal exhibits those characteristics.

    - -

    Objects

    - -

    There are many constructs in Drupal that fit the description of an - "object". Some of the more prominent Drupal components that could be - considered objects are modules, themes, nodes, and users.

    - -

    Nodes are the basic content building blocks of a Drupal site, and - bundle together the data that makes up a "page" or "story" on a typical - site. The methods that operate on this object are defined in node.module, - usually called by the node_invoke() function. User objects similarly - package data together, bringing together information about each account on - the site, profile information, and session tracking. In both cases, the - data structure is defined by a database table instead of a class. Drupal - exploits the relational nature of its supported databases to allow other - modules to extend the objects with additional data fields.

    - -

    Modules and themes are object-like as well, filling the "controller" - role in many ways. Each module is a source file, but also bundles together - related functions and follows a pattern of defining Drupal hooks.

    - -

    Abstraction

    - -

    Drupal's hook - system is the basis for its interface abstraction. Hooks define the - operations that can be performed on or by a module. If a module implements - a hook, it enters into a contract to perform a particular task or - return a particular type of information when the hook is - invoked. The calling code need not know anything about the module - or the way the hook is implemented in order to get useful work - done by invoking the hook.

    - -

    Encapsulation

    - -

    Like most other object-oriented systems, Drupal does not have a way of - strictly limiting access to an object's inner workings, but rather relies - on convention to accomplish this. Since Drupal code is based around - functions, which share a single namespace, this namespace is subdivided by - the use of prefixes. By following this simple convention, each module can - declare its own functions and variables without the worry of conflict with - others.

    - -

    Convention also delineates the public API of a class from its internal - implementation. Internal functions are prefixed by an underscore to - indicate that they should not be called by outside modules. For example, - _user_categories() is a private function which is subject to change without - notice, while user_save() is part of the public interface to the user - object and can be called with the expectation that the user object will be - saved to the database (even though the method of doing this is - private).

    - -

    Polymorphism

    - -

    Nodes are polymorphic in the classical sense. If a module needs to - display a node, for example, it can call node_build() on that node, - followed by drupal_render(), to get an - HTML representation. The actual rendering, though, will depend on which - type of node is passed to the function; this is directly analogous to - having the class of an object determine its behavior when a message is sent - to it. Drupal itself handles the same introspection tasks required of an - OOP language's runtime library.

    - -

    Furthermore, the rendering of the node in this example can be affected - by the active theme. Themes are polymorphic in the same way; the theme is - passed a "render this node" message, and responds to it in a different way - depending on the implementation of the active theme, though the interface - is constant.

    - -

    Inheritance

    - -

    Modules and themes can also be thought of as classes that - inherit their behavior from an abstract base class (although both - can also define whatever functions they please). In the case of - themes, the behavior of the abstract base class is determined by - the functions in theme.inc and the default theming implementations - in enabled modules. A theme can either override the default - rendering of any interface component or let the default rendering - be used. Modules similarly have the selection of all Drupal hooks - to override at will, and may pick and choose which ones to - implement.

    - -

    Design Patterns in Drupal

    - -

    Much of Drupal's internal structure is more complicated than simple - inheritance and polymorphism, however. The more interesting features of - the system result from using established software design patterns. Many of - the patterns detailed in the seminal "Gang of Four" (Gamma, Helm, Johnson, - and Vlissides) Design - Patterns book can be observed in Drupal, for instance.

    - -

    Singleton

    - -

    If we are to think of modules and themes as objects, then they follow - the singleton pattern. In general these objects do not encapsulate data; - what separates one module from another is the set of functions it contains, - so it should be thought of as a class with a singleton instance.

    - -

    Decorator

    - -

    Drupal makes extensive use of the decorator pattern. The polymorphism - of node objects was discussed earlier, but this is only a small piece of - the power of the node system. More interesting is the use of the various - node hooks (hook_node_load(), hook_node_view(), etc.), which allow - arbitrary modules to extend the behavior of all nodes.

    - -

    This feature allows for a wide variety of behaviors to be added to - nodes without the need for subclassing. For instance, a basic story node - has only a few pieces of associated data: title, author, body, teaser, and - a handful of metadata. A common need is for files to be uploaded and - attached to a node, so one could design a new node type that had the story - node's features plus the ability to attach files. Drupal's upload module - satisfies this need in a much more modular fashion by using the node API to - grant every node that requests it the ability to have attached files.

    - -

    This behavior could be imitated by the use of decorators, wrapping them - around each node object. More simply, languages that support categories, - like Objective-C, could augment the common base class of all node objects - to add the new behavior. Drupal's implementation is a simple ramification - of the hook system and the presence of node_invoke().

    - -

    Observer

    - -

    The above interaction is also similar to the use of observers - in object-oriented systems. This Observer pattern is pervasive - throughout Drupal, as many of Drupal's hooks essentially allow - modules to register as observers of Drupal's objects. For - instance, when a modification is made to a vocabulary in Drupal's - taxonomy system, a taxonomy hook such as - hook_taxonomy_vocabulary_update() is called in all modules that - implement it. By implementing the hook, the modules have - registered as observers of the vocabulary object; any changes to - it can then be acted on as is appropriate.

    - -

    Bridge

    - -

    The Drupal database abstraction - layer is implemented in a fashion similar to the Bridge design pattern. - Modules need to be written in a way that is independent of the database - system being used, and the abstraction layer provides for this. New - database layers can be written that conform to the API defined by the - bridge, adding support for additional database systems without the need to - modify module code.

    - -

    Chain of Responsibility

    - -

    Drupal's menu - system follows the Chain of Responsibility pattern. On each page - request, the menu system determines whether there is a module to handle the - request, whether the user has access to the resource requested, and which - function will be called to do the work. To do this, a message is passed to - the menu item corresponding to the path of the request. If the menu item - cannot handle the request, it is passed up the chain. This continues until - a module handles the request, a module denies access to the user, or the - chain is exhausted.

    - -

    Command

    - -

    Many of Drupal's hooks use the Command pattern to reduce the number of - functions that are necessary to implement, passing the operation as a - parameter along with the arguments. In fact, the hook system itself uses - this pattern, so that modules do not have to define every hook, but rather - just the ones they care to implement.

    - -

    Why Not to Use Classes

    - -

    The above hopefully clarifies the ways in which Drupal embodies various - OOP concepts. Why, then, doesn't Drupal move in the direction of using - classes to solve these problems in the future? Some of the reasons are - historical, and were discussed earlier. Others, though, become clearer now - that we have stepped through some of the design patterns used in - Drupal.

    - -

    A good example is the extensibility of the theme system. A theme - defines functions for each of the interface elements it wants to display in - a special way. As noted earlier, this makes themes seem like a good - candidate to inherit from an abstract base class that defines the default - rendering of the elements.

    - -

    What happens, though, when a module is added that adds a new interface - element? The theme should be able to override the rendering of this element - as well, but if a base class is defined, the new module has no way of - adding another method to that class. Complicated patterns could be set up - to emulate this behavior, but Drupal's theme architecture quite elegantly - handles the situation using its own function dispatch system. In this case - and others like it, the classes that on the surface would seem to simplify - the system would end up making it more cumbersome and difficult to - extend.

    - -

    Room for Improvement

    - -

    While Drupal does reflect many object-oriented practices, there are some - aspects of OOP that could be brought to bear on the project in more - powerful ways.

    - -

    Encapsulation, while adequate in theory, is not applied consistently - enough across the code base. Modules should more rigorously define which - functions are public and which are private; the tendency right now is to - publish most functions in the public namespace even if the interface is - volatile. This problem is exacerbated by Drupal's policy of forgoing - backward compatibility in exchange for cleaner APIs whenever necessary. - This policy has led to some very good code, but would need to be excercised - much less often if better encapsulation conventions were followed.

    - -

    Inheritance is also weak in the system. While, as noted above, all - modules share a common set of behavior, it is difficult to extend this to - new modules. One can create new modules easily that augment the behavior of - existing ones, but there is not a way to override just some of a module's - behavior. The impact of this can be marginalized by breaking large modules - into smaller "a la carte" bundles of functionality, so that undesired - aspects of a module may be more easily left out of the system.

    - -

    Multiple Paradigms

    - -

    Drupal is on the surface a procedural system, because it is built in a - procedural language (PHP without classes). The paradigm behind a piece of - software is not entirely dependent on its representation in code, however. - Drupal is not afraid to borrow concepts from many disparate programming - paradigms where it is convenient. A great deal of the power of Drupal comes - from its underlying relational database, and relational programming - techniques that mirror it. The fact that Drupal's work, much like that of - any web application, consists of many reactions to discrete and rapid page - requests should make the behavior of the system resonate with proponents of - event-driven programming. To an aspect-oriented programmer, the invocation - of hooks in arbitrary modules may look strikingly similar to a pointcut. - And, as should be abundantly clear by now, Drupal is no stranger to - object-oriented concepts either.

    - - diff --git a/modules/field/CVS/Entries b/modules/field/CVS/Entries index e912ec1..a34f464 100644 --- a/modules/field/CVS/Entries +++ b/modules/field/CVS/Entries @@ -10,4 +10,4 @@ D/theme//// /field.multilingual.inc/1.1/Tue Aug 25 12:49:02 2009// /field.api.php/1.30/Tue Aug 25 22:59:01 2009// /field.info.inc/1.15/Tue Aug 25 22:59:01 2009// -/field.test/1.43/Tue Aug 25 22:59:01 2009// +/field.test/1.44/Wed Aug 26 04:03:03 2009// diff --git a/modules/field/field.test b/modules/field/field.test index deb7043..d76f178 100644 --- a/modules/field/field.test +++ b/modules/field/field.test @@ -1,5 +1,5 @@ 'Field attach tests', - 'description' => 'Test Field Attach API functions.', - 'group' => 'Field', - ); +class FieldTestCase extends DrupalWebTestCase { + + /** + * Generate random values for a field_test field. + * + * @param $cardinality + * Number of values to generate. + * @return + * An array of random values, in the format expected for field values. + */ + function _generateTestFieldValues($cardinality) { + $values = array(); + for ($i = 0; $i < $cardinality; $i++) { + // field_test fields treat 0 as 'empty value'. + $values[$i]['value'] = mt_rand(1, 127); + } + return $values; } +} +class FieldAttachTestCase extends FieldTestCase { function setUp() { parent::setUp('field_test'); @@ -47,6 +56,22 @@ class FieldAttachTestCase extends DrupalWebTestCase { ); field_create_instance($this->instance); } +} + +/** + * Unit test class for storage-related field_attach_* functions. + * + * All field_attach_* test work with all field_storage plugins and + * all hook_field_attach_pre_{load,insert,update}() hooks. + */ +class FieldAttachStorageTestCase extends FieldAttachTestCase { + public static function getInfo() { + return array( + 'name' => 'Field attach tests (storage-related)', + 'description' => 'Test storage-related Field Attach API functions.', + 'group' => 'Field', + ); + } /** * Check field values insert, update and load. @@ -275,6 +300,162 @@ class FieldAttachTestCase extends DrupalWebTestCase { $this->assertEqual($entity->{$this->field_name}[$langcode], $values, t('Insert: missing field results in default value saved')); } + /** + * Test field_attach_delete(). + */ + function testFieldAttachDelete() { + $entity_type = 'test_entity'; + $langcode = FIELD_LANGUAGE_NONE; + $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + + // Create revision 0 + $values = $this->_generateTestFieldValues($this->field['cardinality']); + $rev[0]->{$this->field_name}[$langcode] = $values; + field_attach_insert($entity_type, $rev[0]); + + // Create revision 1 + $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']); + $rev[1]->{$this->field_name}[$langcode] = $values; + field_attach_update($entity_type, $rev[1]); + + // Create revision 2 + $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']); + $rev[2]->{$this->field_name}[$langcode] = $values; + field_attach_update($entity_type, $rev[2]); + + // Confirm each revision loads + foreach (array_keys($rev) as $vid) { + $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); + field_attach_load_revision($entity_type, array(0 => $read)); + $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values."); + } + + // Delete revision 1, confirm the other two still load. + field_attach_delete_revision($entity_type, $rev[1]); + foreach (array(0, 2) as $vid) { + $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); + field_attach_load_revision($entity_type, array(0 => $read)); + $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values."); + } + + // Confirm the current revision still loads + $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $read)); + $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object current revision has {$this->field['cardinality']} values."); + + // Delete all field data, confirm nothing loads + field_attach_delete($entity_type, $rev[2]); + foreach (array(0, 1, 2) as $vid) { + $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); + field_attach_load_revision($entity_type, array(0 => $read)); + $this->assertIdentical($read->{$this->field_name}, array(), "The test object revision $vid is deleted."); + } + $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $read)); + $this->assertIdentical($read->{$this->field_name}, array(), t('The test object current revision is deleted.')); + } + + /** + * Test field_attach_create_bundle() and field_attach_rename_bundle(). + */ + function testFieldAttachCreateRenameBundle() { + // Create a new bundle. This has to be initiated by the module so that its + // hook_entity_info() is consistent. + $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); + field_test_create_bundle($new_bundle); + + // Add an instance to that bundle. + $this->instance['bundle'] = $new_bundle; + field_create_instance($this->instance); + + // Save an object with data in the field. + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + $langcode = FIELD_LANGUAGE_NONE; + $values = $this->_generateTestFieldValues($this->field['cardinality']); + $entity->{$this->field_name}[$langcode] = $values; + $entity_type = 'test_entity'; + field_attach_insert($entity_type, $entity); + + // Verify the field data is present on load. + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $entity)); + $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle"); + + // Rename the bundle. This has to be initiated by the module so that its + // hook_entity_info() is consistent. + $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); + field_test_rename_bundle($this->instance['bundle'], $new_bundle); + + // Check that the instance definition has been updated. + $this->instance = field_info_instance($this->field_name, $new_bundle); + $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance."); + + // Verify the field data is present on load. + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $entity)); + $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage"); + } + + /** + * Test field_attach_delete_bundle(). + */ + function testFieldAttachDeleteBundle() { + // Create a new bundle. This has to be initiated by the module so that its + // hook_entity_info() is consistent. + $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); + field_test_create_bundle($new_bundle); + + // Add an instance to that bundle. + $this->instance['bundle'] = $new_bundle; + field_create_instance($this->instance); + + // Create a second field for the test bundle + $field_name = drupal_strtolower($this->randomName() . '_field_name'); + $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1); + field_create_field($field); + $instance = array( + 'field_name' => $field_name, + 'bundle' => $this->instance['bundle'], + 'label' => $this->randomName() . '_label', + 'description' => $this->randomName() . '_description', + 'weight' => mt_rand(0, 127), + // test_field has no instance settings + 'widget' => array( + 'type' => 'test_field_widget', + 'settings' => array( + 'size' => mt_rand(0, 255)))); + field_create_instance($instance); + + // Save an object with data for both fields + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + $langcode = FIELD_LANGUAGE_NONE; + $values = $this->_generateTestFieldValues($this->field['cardinality']); + $entity->{$this->field_name}[$langcode] = $values; + $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1); + $entity_type = 'test_entity'; + field_attach_insert($entity_type, $entity); + + // Verify the fields are present on load + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $entity)); + $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded'); + $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded'); + + // Delete the bundle. This has to be initiated by the module so that its + // hook_entity_info() is consistent. + field_test_delete_bundle($this->instance['bundle']); + + // Verify no data gets loaded + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + field_attach_load($entity_type, array(0 => $entity)); + $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field'); + $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field'); + + // Verify that the instances are gone + $this->assertFalse(field_read_instance($this->field_name, $this->instance['bundle']), "First field is deleted"); + $this->assertFalse(field_read_instance($field_name, $instance['bundle']), "Second field is deleted"); + } + /** * Test field_attach_query(). */ @@ -454,7 +635,23 @@ class FieldAttachTestCase extends DrupalWebTestCase { ); $this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result')); } +} +/** + * Unit test class for non-storage related field_attach_* functions. + */ +class FieldAttachOtherTestCase extends FieldAttachTestCase { + public static function getInfo() { + return array( + 'name' => 'Field attach tests (other)', + 'description' => 'Test other Field Attach API functions.', + 'group' => 'Field', + ); + } + + /** + * Test field_attach_views() and field_attach_preprocess(). + */ function testFieldAttachViewAndPreprocess() { $entity_type = 'test_entity'; $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); @@ -546,153 +743,6 @@ class FieldAttachTestCase extends DrupalWebTestCase { $this->assertTrue($result, t('Variable $@field_name correctly populated.', array('@field_name' => $this->field_name))); } - function testFieldAttachDelete() { - $entity_type = 'test_entity'; - $langcode = FIELD_LANGUAGE_NONE; - $rev[0] = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - - // Create revision 0 - $values = $this->_generateTestFieldValues($this->field['cardinality']); - $rev[0]->{$this->field_name}[$langcode] = $values; - field_attach_insert($entity_type, $rev[0]); - - // Create revision 1 - $rev[1] = field_test_create_stub_entity(0, 1, $this->instance['bundle']); - $rev[1]->{$this->field_name}[$langcode] = $values; - field_attach_update($entity_type, $rev[1]); - - // Create revision 2 - $rev[2] = field_test_create_stub_entity(0, 2, $this->instance['bundle']); - $rev[2]->{$this->field_name}[$langcode] = $values; - field_attach_update($entity_type, $rev[2]); - - // Confirm each revision loads - foreach (array_keys($rev) as $vid) { - $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); - field_attach_load_revision($entity_type, array(0 => $read)); - $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values."); - } - - // Delete revision 1, confirm the other two still load. - field_attach_delete_revision($entity_type, $rev[1]); - foreach (array(0, 2) as $vid) { - $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); - field_attach_load_revision($entity_type, array(0 => $read)); - $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object revision $vid has {$this->field['cardinality']} values."); - } - - // Confirm the current revision still loads - $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $read)); - $this->assertEqual(count($read->{$this->field_name}[$langcode]), $this->field['cardinality'], "The test object current revision has {$this->field['cardinality']} values."); - - // Delete all field data, confirm nothing loads - field_attach_delete($entity_type, $rev[2]); - foreach (array(0, 1, 2) as $vid) { - $read = field_test_create_stub_entity(0, $vid, $this->instance['bundle']); - field_attach_load_revision($entity_type, array(0 => $read)); - $this->assertIdentical($read->{$this->field_name}, array(), "The test object revision $vid is deleted."); - } - $read = field_test_create_stub_entity(0, 2, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $read)); - $this->assertIdentical($read->{$this->field_name}, array(), t('The test object current revision is deleted.')); - } - - function testFieldAttachCreateRenameBundle() { - // Create a new bundle. This has to be initiated by the module so that its - // hook_entity_info() is consistent. - $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); - field_test_create_bundle($new_bundle); - - // Add an instance to that bundle. - $this->instance['bundle'] = $new_bundle; - field_create_instance($this->instance); - - // Save an object with data in the field. - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - $langcode = FIELD_LANGUAGE_NONE; - $values = $this->_generateTestFieldValues($this->field['cardinality']); - $entity->{$this->field_name}[$langcode] = $values; - $entity_type = 'test_entity'; - field_attach_insert($entity_type, $entity); - - // Verify the field data is present on load. - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $entity)); - $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle"); - - // Rename the bundle. This has to be initiated by the module so that its - // hook_entity_info() is consistent. - $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); - field_test_rename_bundle($this->instance['bundle'], $new_bundle); - - // Check that the instance definition has been updated. - $this->instance = field_info_instance($this->field_name, $new_bundle); - $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance."); - - // Verify the field data is present on load. - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $entity)); - $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Bundle name has been updated in the field storage"); - } - - function testFieldAttachDeleteBundle() { - // Create a new bundle. This has to be initiated by the module so that its - // hook_entity_info() is consistent. - $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName()); - field_test_create_bundle($new_bundle); - - // Add an instance to that bundle. - $this->instance['bundle'] = $new_bundle; - field_create_instance($this->instance); - - // Create a second field for the test bundle - $field_name = drupal_strtolower($this->randomName() . '_field_name'); - $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1); - field_create_field($field); - $instance = array( - 'field_name' => $field_name, - 'bundle' => $this->instance['bundle'], - 'label' => $this->randomName() . '_label', - 'description' => $this->randomName() . '_description', - 'weight' => mt_rand(0, 127), - // test_field has no instance settings - 'widget' => array( - 'type' => 'test_field_widget', - 'settings' => array( - 'size' => mt_rand(0, 255)))); - field_create_instance($instance); - - // Save an object with data for both fields - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - $langcode = FIELD_LANGUAGE_NONE; - $values = $this->_generateTestFieldValues($this->field['cardinality']); - $entity->{$this->field_name}[$langcode] = $values; - $entity->{$field_name}[$langcode] = $this->_generateTestFieldValues(1); - $entity_type = 'test_entity'; - field_attach_insert($entity_type, $entity); - - // Verify the fields are present on load - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $entity)); - $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded'); - $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded'); - - // Delete the bundle. This has to be initiated by the module so that its - // hook_entity_info() is consistent. - field_test_delete_bundle($this->instance['bundle']); - - // Verify no data gets loaded - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); - field_attach_load($entity_type, array(0 => $entity)); - $this->assertFalse(isset($entity->{$this->field_name}[$langcode]), 'No data for first field'); - $this->assertFalse(isset($entity->{$field_name}[$langcode]), 'No data for second field'); - - // Verify that the instances are gone - $this->assertFalse(field_read_instance($this->field_name, $this->instance['bundle']), "First field is deleted"); - $this->assertFalse(field_read_instance($field_name, $instance['bundle']), "Second field is deleted"); - } - /** * Test field cache. */ @@ -781,8 +831,12 @@ class FieldAttachTestCase extends DrupalWebTestCase { $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete')); } - // Verify that field_attach_validate() invokes the correct - // hook_field_validate. + /** + * Test field_attach_validate(). + * + * Verify that field_attach_validate() invokes the correct + * hook_field_validate. + */ function testFieldAttachValidate() { $entity_type = 'test_entity'; $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); @@ -818,8 +872,12 @@ class FieldAttachTestCase extends DrupalWebTestCase { $this->assertEqual(count($errors[$this->field_name][$langcode]), 0, 'No extraneous errors set'); } - // Validate that FAPI elements are generated. This could be much - // more thorough, but it does verify that the correct widgets show up. + /** + * Test field_attach_form(). + * + * This could be much more thorough, but it does verify that the correct + * widgets show up. + */ function testFieldAttachForm() { $entity_type = 'test_entity'; $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); @@ -835,6 +893,9 @@ class FieldAttachTestCase extends DrupalWebTestCase { } } + /** + * Test field_attach_submit(). + */ function testFieldAttachSubmit() { $entity_type = 'test_entity'; $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); @@ -871,26 +932,9 @@ class FieldAttachTestCase extends DrupalWebTestCase { } $this->assertIdentical($entity->{$this->field_name}[$langcode], $expected_values, 'Submit filters empty values'); } - - /** - * Generate random values for a field_test field. - * - * @param $cardinality - * Number of values to generate. - * @return - * An array of random values, in the format expected for field values. - */ - function _generateTestFieldValues($cardinality) { - $values = array(); - for ($i = 0; $i < $cardinality; $i++) { - // field_test fields treat 0 as 'empty value'. - $values[$i]['value'] = mt_rand(1, 127); - } - return $values; - } } -class FieldInfoTestCase extends DrupalWebTestCase { +class FieldInfoTestCase extends FieldTestCase { public static function getInfo() { return array( @@ -1085,7 +1129,7 @@ class FieldInfoTestCase extends DrupalWebTestCase { } } -class FieldFormTestCase extends DrupalWebTestCase { +class FieldFormTestCase extends FieldTestCase { public static function getInfo() { return array( 'name' => 'Field form tests', @@ -1385,7 +1429,7 @@ class FieldFormTestCase extends DrupalWebTestCase { } } -class FieldCrudTestCase extends DrupalWebTestCase { +class FieldCrudTestCase extends FieldTestCase { public static function getInfo() { return array( 'name' => 'Field CRUD tests', @@ -1645,7 +1689,7 @@ class FieldCrudTestCase extends DrupalWebTestCase { } } -class FieldInstanceCrudTestCase extends DrupalWebTestCase { +class FieldInstanceCrudTestCase extends FieldTestCase { protected $field; public static function getInfo() { @@ -1837,7 +1881,7 @@ class FieldInstanceCrudTestCase extends DrupalWebTestCase { * that only the correct values are returned by * field_multilingual_available_languages(). */ -class FieldTranslationsTestCase extends DrupalWebTestCase { +class FieldTranslationsTestCase extends FieldTestCase { public static function getInfo() { return array( 'name' => 'Field translations tests', @@ -2040,7 +2084,7 @@ class FieldTranslationsTestCase extends DrupalWebTestCase { $object = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']); $field_translations = array(); foreach (field_multilingual_available_languages($obj_type, $this->field) as $langcode) { - $field_translations[$langcode] = FieldAttachTestCase::_generateTestFieldValues($this->field['cardinality']); + $field_translations[$langcode] = _generateTestFieldValues($this->field['cardinality']); } // Save and reload the field translations. @@ -2063,7 +2107,7 @@ class FieldTranslationsTestCase extends DrupalWebTestCase { /** * Unit test class for field bulk delete and batch purge functionality. */ -class FieldBulkDeleteTestCase extends DrupalWebTestCase { +class FieldBulkDeleteTestCase extends FieldTestCase { protected $field; public static function getInfo() { @@ -2074,23 +2118,6 @@ class FieldBulkDeleteTestCase extends DrupalWebTestCase { ); } - /** - * Generate random values for a field_test field. - * - * @param $cardinality - * Number of values to generate. - * @return - * An array of random values, in the format expected for field values. - */ - function _generateTestFieldValues($cardinality) { - $values = array(); - for ($i = 0; $i < $cardinality; $i++) { - // field_test fields treat 0 as 'empty value'. - $values[$i]['value'] = mt_rand(1, 127); - } - return $values; - } - /** * Convenience function for Field API tests. * diff --git a/modules/forum/CVS/Entries b/modules/forum/CVS/Entries index 4f05876..8297b34 100644 --- a/modules/forum/CVS/Entries +++ b/modules/forum/CVS/Entries @@ -7,8 +7,8 @@ /forum.info/1.12/Tue Aug 25 12:49:03 2009// /forum.pages.inc/1.2/Tue Aug 25 12:49:04 2009// /forums.tpl.php/1.5/Tue Aug 25 12:49:04 2009// -/forum.admin.inc/1.24/Tue Aug 25 22:58:27 2009// -/forum.install/1.33/Tue Aug 25 22:58:27 2009// -/forum.module/1.514/Tue Aug 25 22:58:27 2009// -/forum.test/1.29/Tue Aug 25 22:59:01 2009// +/forum.admin.inc/1.24/Wed Aug 26 04:02:49 2009// +/forum.install/1.33/Wed Aug 26 04:02:49 2009// +/forum.module/1.514/Wed Aug 26 04:02:49 2009// +/forum.test/1.29/Wed Aug 26 04:02:49 2009// D diff --git a/modules/image/CVS/Entries b/modules/image/CVS/Entries index f9fdec5..690fea5 100644 --- a/modules/image/CVS/Entries +++ b/modules/image/CVS/Entries @@ -4,7 +4,7 @@ /image.effects.inc/1.2/Tue Aug 25 12:49:04 2009// /image.info/1.2/Tue Aug 25 12:49:04 2009// /image.install/1.2/Tue Aug 25 12:49:04 2009// -/image.module/1.15/Tue Aug 25 12:49:04 2009// -/image.test/1.7/Tue Aug 25 12:49:04 2009// /sample.png/1.1/Tue Aug 25 12:49:04 2009/-kb/ +/image.module/1.16/Wed Aug 26 04:03:03 2009// +/image.test/1.8/Wed Aug 26 04:03:03 2009// D diff --git a/modules/image/image.module b/modules/image/image.module index 00ee54d..2ab00f3 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -1,5 +1,5 @@ source, array('Content-Type' => $image->info['mime_type'], 'Content-Length' => $image->info['file_size'])); - } + // Try to generate the image, unless another thread just did it while we were + // acquiring the lock. + $success = file_exists($destination) || image_style_create_derivative($style, $path, $destination); - // Set a cache entry designating this image as being in-process. - cache_set($cid, $destination, 'cache_image'); + if ($lock_acquired) { + lock_release($lock_name); + } - // Try to generate the image. - if (image_style_create_derivative($style, $path, $destination)) { + if ($success) { $image = image_load($destination); - cache_clear_all($cid, 'cache_image'); file_transfer($image->source, array('Content-Type' => $image->info['mime_type'], 'Content-Length' => $image->info['file_size'])); } else { - cache_clear_all($cid, 'cache_image'); watchdog('image', 'Unable to generate the derived image located at %path.', $destination); drupal_set_header('500 Internal Server Error'); print t('Error generating image.'); diff --git a/modules/image/image.test b/modules/image/image.test index b5ee713..d5338e7 100644 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -1,5 +1,5 @@ assertEqual($actual_generate_url, $expected_generate_url, t('Got the generate URL for a non-existent file.')); // Fetch the URL that generates the file while another process appears to - // be generating the same file (this is signaled using cache_image). - $cid = 'generate:' . $this->style_name . ':' . md5($this->image_filepath); - cache_set($cid, $generated_path, 'cache_image'); + // be generating the same file (this is signaled using a lock). + $lock_name = 'image_style_generate:' . $this->style_name . ':' . md5($this->image_filepath); + $this->assertTrue(lock_acquire($lock_name), t('Lock was acquired.')); $this->drupalGet($expected_generate_url); $this->assertResponse(503, t('Service Unavailable response received.')); $this->assertTrue($this->drupalGetHeader('Retry-After'), t('Retry-After header received.')); - cache_clear_all($cid, 'cache_image'); + lock_release($lock_name); // Fetch the URL that generates the file. $this->drupalGet($expected_generate_url); @@ -102,6 +102,7 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { $generated_image_info = image_get_info($generated_path); $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], t('Expected Content-Type was reported.')); $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], t('Expected Content-Length was reported.')); + $this->assertTrue(lock_may_be_available($lock_name), t('Lock was released.')); // Check that the URL points directly to the generated file. $expected_generated_url = file_create_url($generated_path); diff --git a/modules/node/CVS/Entries b/modules/node/CVS/Entries index 8aa0414..3d7dc84 100644 --- a/modules/node/CVS/Entries +++ b/modules/node/CVS/Entries @@ -8,7 +8,7 @@ D/tests//// /node.info/1.12/Tue Aug 25 12:49:05 2009// /node.install/1.28/Tue Aug 25 12:49:05 2009// /node.js/1.4/Tue Aug 25 12:49:05 2009// -/node.pages.inc/1.78/Tue Aug 25 12:51:05 2009// +/node.pages.inc/1.78/Wed Aug 26 04:02:49 2009// /node.test/1.42/Tue Aug 25 12:51:05 2009// /node.tokens.inc/1.2/Tue Aug 25 12:49:05 2009// /node.tpl.php/1.20/Tue Aug 25 12:49:05 2009// diff --git a/modules/simpletest/tests/CVS/Entries b/modules/simpletest/tests/CVS/Entries index 68305b3..5345834 100644 --- a/modules/simpletest/tests/CVS/Entries +++ b/modules/simpletest/tests/CVS/Entries @@ -52,6 +52,6 @@ /xmlrpc.test/1.14/Tue Aug 25 12:49:10 2009// /xmlrpc_test.info/1.1/Tue Aug 25 12:49:10 2009// /xmlrpc_test.module/1.4/Tue Aug 25 12:49:10 2009// -/common.test/1.69/Tue Aug 25 22:59:01 2009// +/common.test/1.69/Wed Aug 26 04:02:49 2009// /field_test.module/1.18/Tue Aug 25 22:59:01 2009// D diff --git a/modules/system/CVS/Entries b/modules/system/CVS/Entries index f10da9c..019f53f 100644 --- a/modules/system/CVS/Entries +++ b/modules/system/CVS/Entries @@ -18,6 +18,6 @@ /system.tokens.inc/1.2/Tue Aug 25 12:51:06 2009// /page.tpl.php/1.31/Tue Aug 25 19:34:49 2009// /system.admin.inc/1.196/Tue Aug 25 22:59:01 2009// -/system.api.php/1.64/Tue Aug 25 22:59:01 2009// -/system.module/1.774/Tue Aug 25 22:59:01 2009// +/system.api.php/1.65/Wed Aug 26 04:03:03 2009// +/system.module/1.775/Wed Aug 26 04:03:04 2009// D diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 6631bcf..f7b37d0 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1,5 +1,5 @@ ' . t('This page shows you all available administration tasks for each module.') . '

    '; case 'admin/appearance': - $output = '

    ' . t('Select which themes are available to your users and specify the default theme. To configure site-wide display settings, click the "configure" task above. Alternatively, to override these settings in a specific theme, click the "configure" link for that theme. Note that different themes may have different regions available for displaying content; for consistency in presentation, you may wish to enable only one theme.') . '

    '; + $output = '

    ' . t('Select the default theme for Drupal. To configure site-wide display settings, click the "configure" task above. Alternatively, to override these settings in a specific theme, click the "configure" link for that theme. Note that different themes may have different regions available for displaying content.') . '

    '; $output .= '

    ' . t('To change the appearance of your site, a number of contributed themes are available.', array('@themes' => 'http://drupal.org/project/themes')) . '

    '; return $output; case 'admin/appearance/settings/' . $arg[4]: @@ -236,10 +236,6 @@ function system_permission() { 'title' => t('Access site reports'), 'description' => t('View reports from system logs and other status information.'), ), - 'select different theme' => array( - 'title' => t('Select different theme'), - 'description' => t('Select a theme other than the default theme set by the site administrator.'), - ), 'block IP addresses' => array( 'title' => t('Block IP addresses'), 'description' => t('Block IP addresses from accessing your site.'), diff --git a/modules/taxonomy/CVS/Entries b/modules/taxonomy/CVS/Entries index 7ece71c..a85a67f 100644 --- a/modules/taxonomy/CVS/Entries +++ b/modules/taxonomy/CVS/Entries @@ -4,9 +4,9 @@ /taxonomy.js/1.6/Tue Aug 25 12:49:11 2009// /taxonomy.tokens.inc/1.1/Tue Aug 25 12:49:11 2009// /vocabulary.js/1.2/Tue Aug 25 12:49:11 2009// -/taxonomy.admin.inc/1.66/Tue Aug 25 22:58:27 2009// -/taxonomy.install/1.21/Tue Aug 25 22:58:27 2009// -/taxonomy.module/1.505/Tue Aug 25 22:59:01 2009// -/taxonomy.pages.inc/1.35/Tue Aug 25 22:58:27 2009// -/taxonomy.test/1.46/Tue Aug 25 22:59:01 2009// +/taxonomy.admin.inc/1.66/Wed Aug 26 04:02:49 2009// +/taxonomy.install/1.21/Wed Aug 26 04:02:49 2009// +/taxonomy.module/1.505/Wed Aug 26 04:02:49 2009// +/taxonomy.pages.inc/1.35/Wed Aug 26 04:02:49 2009// +/taxonomy.test/1.46/Wed Aug 26 04:02:49 2009// D diff --git a/profiles/default/CVS/Entries b/profiles/default/CVS/Entries index 6f36012..4f497aa 100644 --- a/profiles/default/CVS/Entries +++ b/profiles/default/CVS/Entries @@ -1,4 +1,4 @@ /default.info/1.3/Tue Aug 25 19:34:18 2009// /default.profile/1.63/Tue Aug 25 19:34:18 2009// -/default.install/1.1/Tue Aug 25 22:58:27 2009// +/default.install/1.1/Wed Aug 26 04:02:49 2009// D