diff -Naurw webform_old/CHANGELOG.txt webform/CHANGELOG.txt
--- webform_old/CHANGELOG.txt	1970-01-01 01:00:00.000000000 +0100
+++ webform/CHANGELOG.txt	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,37 @@
+Webform 2.x Changelog
+---------------------
+
+2.x to 3.0
+----------
+- Module directory structure moved around.
+- Webform configuration moved to an entirely separate tab.
+- E-mail templates are now editable by administrators.
+- Conditional fields.
+- Submissions may be saved as a draft and resumed later.
+- Webform may now be attached to any content type.
+- Public API for allowing other modules to provide components.
+- Public API for interacting with submission save, insert, update, and delete.
+- New rendering capabilities for HTML presentation of submissions and e-mails.
+- Print module support.
+- Basic Views module support.
+- Popup calendar support on Date components (with Date Popup module).
+- New Mollom module integration.
+
+1.x to 2.0
+----------
+- Redirect POST option removed.
+- Webform components moved to the "Form components" tab when editing.
+- Webform node structure changed. All webform additions to the node are placed in $node->webform.
+- Clone option added to components.
+- Database storage improved to be more consistent and efficient.
+- Additional e-mails may be sent by modifying the $node->webform['additional_emails'] variable in the Additional Validation field.
+- The values of select and hidden fields may receive e-mails by using the option in Conditional e-mail recipients field.
+- E-mail from name, from address, and from subject may be entered in a text field.
+- The complete webform may be shown in the teaser view of a node.
+- Submit button text may be changed.
+- Theme function theme_webform_create_mailmessage() has been renamed to theme_webform_mail_message().
+- $cid parameter added to theme_webform_mail_message() to create unique e-mails depending on a particular recipient or component.
+- Theme function theme_webform_mail_headers added.
+- Component descriptions are textareas rather than textfields.
+- _webform_filtervalues() has been renamed to _webform_filter_values.
+
diff -Naurw webform_old/LICENSE.txt webform/LICENSE.txt
--- webform_old/LICENSE.txt	1970-01-01 01:00:00.000000000 +0100
+++ webform/LICENSE.txt	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff -Naurw webform_old/README.txt webform/README.txt
--- webform_old/README.txt	1970-01-01 01:00:00.000000000 +0100
+++ webform/README.txt	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,41 @@
+Description
+-----------
+This module adds a webform content type to your Drupal site.
+A webform can be a questionnaire, contact or request form. These can be used 
+by visitor to make contact or to enable a more complex survey than polls
+provide. Submissions from a webform are saved in a database table and 
+can optionally be mailed to e-mail addresses upon submission.
+
+Requirements
+------------
+Drupal 7.x
+
+Installation
+------------
+1. Copy the entire webform directory the Drupal sites/all/modules directory.
+
+2. Login as an administrator. Enable the module in the "Administer" -> "Modules"
+
+3. (Optional) Edit the settings under "Administer" -> "Configuration" ->
+   "Content authoring" -> "Webform settings"
+
+4. Create a webform node at node/add/webform.
+
+Upgrading from previous versions
+--------------------------------
+Note that if you are upgrading from a Drupal 6 installation of Webform, you MUST
+have been running Webform 3.x on your Drupal 6 site before upgrading to Drupal
+7 and Webform 3.x. You cannot upgrade directly from Webform 6.x-2.x to Webform
+7.x-3.x.
+
+1. Copy the entire webform directory the Drupal modules directory.
+
+2. Login as the FIRST user or change the $access_check in update.php to FALSE
+
+3. Run update.php (at http://www.example.com/update.php)
+
+Support
+-------
+Please use the issue queue for filing bugs with this module at
+http://drupal.org/project/issues/webform
+
diff -Naurw webform_old/THEMING.txt webform/THEMING.txt
--- webform_old/THEMING.txt	1970-01-01 01:00:00.000000000 +0100
+++ webform/THEMING.txt	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,115 @@
+Overview
+--------
+Webform supports theming similar to the CCK or Views modules. Any webform
+may be themed on the server side, though doing so may require a reasonable
+amount of knowledge about the Drupal Form API. More information about the Form
+API may be found at http://api.drupal.org/api/file/developer/topics/forms_api.html
+
+Theme submission e-mails
+-----------------------
+The default e-mails sent by webform are fairly basic. If you like, you may
+customize the display of e-mails sent by each individual webform.
+
+- Open the Webform module directory.
+
+- Copy (do not move!) the "webform-mail.tpl.php" file to your theme directory.
+
+- Open up the new file and edit it to your liking. The webform-mail.tpl.php file
+  contains further instructions on how to get started with theming the e-mail.
+
+- If you want to edit the e-mail sent by only one particular webform, rename the
+  file "webform-mail-[node id here].tpl.php", replacing [node id here] with the
+  node ID of the webform.
+
+- Clear the theme cache! Visit admin/settings/performance and click the
+  "Clear cached data" button at the bottom of the page. You may also find
+  using devel module will speed up this process a bit. This needs to be done
+  every time you create or rename a .tpl.php file, but isn't necessary once
+  these files already exist.
+
+- To get a better idea of what variables are available to you, you can include
+  the print_r function in your email. Simply include the line:
+
+  <?php print_r($submission) ?>
+
+  to get a listing of all the available fields you can use in your mail.
+
+- Advanced Webform e-mail Theming: Theming the e-mail headers may also be done
+  by overriding the theme_webform_mail_headers() function from webform.module.
+  Just copy the code out of webform.module and change as necessary in your
+  template.php file. This allows you to customize the e-mail headers.
+
+Theme the confirmation page
+---------------------------
+
+After a user submits a webform, they are directed to a page that contains the
+confirmation message set in the webform node settings (assuming the form doesn't
+direct to a complete URL). These instructions let you customize the format of
+the confirmation page of a single node or all webforms on your site.
+
+- Open the Webform module directory.
+
+- Copy (do not move!) the "webform-confirmation.tpl.php" file to your theme
+  directory.
+
+- Open the new file and change it's contents to the your liking. Here's an
+  example that inserts some additional HTML around the confirmation message and
+  gives links to edit the submission.
+
+  <?php /* Begin sample webform confirmation page */ ?>
+
+  <div class="confirmation-message">
+    <?php print $confirmation_message ?>
+  </div>
+
+  <ul>
+    <li><a href="<?php print url('node/'. $node->nid . '/submission/'. $sid)?>">View your submission</a></li>
+    <li><a href="<?php print url('node/'. $node->nid . '/submission/'. $sid .'/edit')?>">Edit your submission</a></li>
+  </ul>
+  
+  <?php /* End sample webform confirmation page */ ?>
+
+- You may edit the webform-confirmation.tpl.php file in your theme directory,
+  this will affect all the webform mails sent by your entire site. Or, if you
+  want to edit the e-mail sent by only one particular webform, rename the file
+  "webform-confirmation-[node id here].tpl.php", replacing [node id here] with
+  the node ID of the webform.
+
+- Visit admin/settings/performance and click the "Clear cached data" button.
+
+Theme display of an entire webform
+----------------------------------
+
+Theming a webform can be useful for rearranging elements or customizing the
+appearance of multiple components at once. This tutorial assumes usage
+of the phptemplate engine.
+
+- Copy the "webform-form.tpl.php" file from the webform directory to your
+  theme directory. You may rename this file
+  "webform-form-[node id here].tpl.php" if you want to theme one particular
+  webform on your site. Replace [node id here] with the node ID of the webform.
+
+- Open up your new file and customize the webform however you like.
+
+- Visit admin/settings/performance and click the "Clear cached data" button.
+
+- All Webform forms have 2 main fieldsets: "submitted", and "details". Although
+  you may move things around as you wish, keep all your components within the
+  "submitted" fieldset. Only the "submitted" fieldset is displayed and Webform
+  depends on the other two to operate properly, so don't mess with them unless
+  you have good reason to do so (like you're forwarding your webform to a custom
+  PHP or PERL script).
+
+Theme display of a webform submission display
+---------------------------------------------
+
+Theming the display of a webform submission works the same way as theming a
+webform form. Webform uses Drupal "renderable" style arrays for the display of
+submissions, just like most forms in Drupal.
+
+The template file for theming submissions is webform-submission.tpl.php. You can
+use webform-submission-[node id here].tpl.php for individual nodes if desired.
+Note that the contents of this template are used not only for display of
+submissions in the Webform interface but also in e-mails when printing out
+the %email_values token.
+
diff -Naurw webform_old/components/date.inc webform/components/date.inc
--- webform_old/components/date.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/date.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,433 @@
+<?php
+
+/**
+ * @file
+ * Webform module date component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_date() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'timezone' => 'user',
+      'start_date' => '-2 years',
+      'end_date' => '+2 years',
+      'year_textfield' => 0,
+      'datepicker' => 1,
+      'title_display' => 0,
+      'description' => '',
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_date() {
+  return array(
+    'webform_date' => array(
+      'render element' => 'element',
+      'file' => 'components/date.inc',
+    ),
+    'webform_display_date' => array(
+      'render element' => 'element',
+      'file' => 'components/date.inc',
+    ),
+    'webform_calendar' => array(
+      'variables' => array('component' => NULL, 'calendar_classes' => NULL),
+      'template' => 'templates/webform-calendar',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_date($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . '<br />' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>. Strings such as today, +2 months, and Dec 9 2004 are all valid.'),
+    '#size' => 60,
+    '#maxlength' => 127,
+    '#weight' => 0,
+  );
+  $form['extra']['timezone'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default value timezone'),
+    '#default_value' => empty($component['extra']['timezone']) ? 'user' : $component['extra']['timezone'],
+    '#description' => t('If using relative dates for a default value (e.g. "today") base the current day on this timezone.'),
+    '#options' => array('user' => t('User timezone'), 'site' => t('Website timezone')),
+    '#weight' => 2,
+    '#access' => variable_get('configurable_timezones', 1),
+  );
+
+  $form['display']['datepicker'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable popup calendar'),
+    '#default_value' => $component['extra']['datepicker'],
+    '#description' => t('Enable a JavaScript date picker next to the date field.'),
+    '#weight' => 2,
+    '#parents' => array('extra', 'datepicker'),
+  );
+
+  $form['display']['year_textfield'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Use a textfield for year'),
+    '#default_value' => $component['extra']['year_textfield'],
+    '#description' => t('If checked, the generated date field will use a textfield for the year. Otherwise it will use a select list.'),
+    '#weight' => 5,
+    '#parents' => array('extra', 'year_textfield'),
+  );
+
+  $form['validation']['start_date'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Start date'),
+    '#default_value' => $component['extra']['start_date'],
+    '#description' => t('The earliest date that may be entered into the field.') . ' ' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>.'),
+    '#size' => 30,
+    '#weight' => 3,
+    '#parents' => array('extra', 'start_date'),
+  );
+  $form['validation']['end_date'] = array(
+    '#type' => 'textfield',
+    '#title' => t('End date'),
+    '#default_value' => $component['extra']['end_date'],
+    '#description' => t('The latest date that may be entered into the field.') . ' ' . t('Accepts any date in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>.'),
+    '#size' => 30,
+    '#weight' => 4,
+    '#parents' => array('extra', 'end_date'),
+  );
+
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_date($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'date',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#required' => $component['mandatory'],
+    '#start_date' => trim($component['extra']['start_date']),
+    '#end_date' => trim($component['extra']['end_date']),
+    '#year_textfield' => $component['extra']['year_textfield'],
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value'],
+    '#timezone' => $component['extra']['timezone'],
+    '#process' => array('webform_expand_date'),
+    '#theme' => 'webform_date',
+    '#theme_wrappers' => array('webform_element'),
+    '#element_validate' => array('webform_validate_date'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  if ($component['extra']['datepicker']) {
+    $element['#datepicker'] = TRUE;
+    $element['#attached'] = array(
+      'library' => array(
+        array('system', 'ui.datepicker'),
+      ),
+    );
+  }
+
+  // Set the value from Webform.
+  if (isset($value[0]) && $value[0] !== '') {
+    $value = webform_date_array($value[0], 'date');
+    $element['#default_value'] = $value;
+  }
+
+  return $element;
+}
+
+/**
+ * Form API #process function for Webform date fields.
+ */
+function webform_expand_date($element) {
+  // Accept a string or array value for #default_value.
+  if (!empty($element['#default_value']) && is_string($element['#default_value'])) {
+    $timezone = $element['#timezone'] != 'user' ? NULL : 'user';
+    $timestring = webform_strtodate('c', $element['#default_value'], $timezone);
+    $element['#default_value'] = webform_date_array($timestring, 'date');
+  }
+
+  // Set defaults according to existing #default_value (set by Form API)
+  if (isset($element['#default_value']['month']) || isset($element['#default_value']['day']) || isset($element['#default_value']['year'])) {
+    $default_values = array(
+      'month' => $element['#default_value']['month'],
+      'day' => $element['#default_value']['day'],
+      'year' => $element['#default_value']['year'],
+    );
+  }
+  else {
+    $default_values = array(
+      'day' => NULL,
+      'month' => NULL,
+      'year' => NULL,
+    );
+  }
+
+  // Let Drupal do it's normal expansion.
+  $element = form_process_date($element);
+
+  // Set default values.
+  foreach ($default_values as $type => $value) {
+    switch ($type) {
+      case 'month':
+        $none = t('Month');
+        break;
+      case 'day':
+        $none = t('Day');
+        break;
+      case 'year':
+        $none = t('Year');
+        break;
+    }
+    unset($element[$type]['#value']);
+    $element[$type]['#default_value'] = isset($default_values[$type]) ? $default_values[$type] : NULL;
+    $element[$type]['#options'] = array('' => $none) + $element[$type]['#options'];
+  }
+
+  // Convert relative dates to absolute ones.
+  foreach (array('start_date', 'end_date') as $start_end) {
+    $timezone = $element['#timezone'] != 'user' ? NULL : 'user';
+    $date = webform_strtodate('Y-m-d', $element['#' . $start_end], $timezone);
+    $element['#' . $start_end] = $date ? $date : '';
+  }
+
+  // Tweak the year field.
+  if ($element['#year_textfield']) {
+    $element['year']['#type'] = 'textfield';
+    $element['year']['#size'] = 5;
+    $element['year']['#maxlength'] = 4;
+    unset($element['year']['#options']);
+  }
+  elseif ($element['#start_date'] || $element['#end_date']) {
+    $start_year = $element['#start_date'] ? webform_strtodate('Y', $element['#start_date']) : webform_strtodate('Y', '-2 years');
+    $end_year = $element['#end_date'] ? webform_strtodate('Y', $element['#end_date']) : webform_strtodate('Y', '+2 years');
+    $element['year']['#options'] = array('' => t('Year')) + drupal_map_assoc(range($start_year, $end_year));
+  }
+
+  return $element;
+}
+
+/**
+ * Theme a webform date element.
+ */
+function theme_webform_date($variables) {
+  $element = $variables['element'];
+
+  $element['year']['#attributes']['class'] = array('year');
+  $element['month']['#attributes']['class'] = array('month');
+  $element['day']['#attributes']['class'] = array('day');
+
+  // Add error classes to all items within the element.
+  if (form_get_error($element)) {
+    $element['year']['#attributes']['class'][] = 'error';
+    $element['month']['#attributes']['class'][] = 'error';
+    $element['day']['#attributes']['class'][] = 'error';
+  }
+
+  $class = array('webform-container-inline');
+
+  // Add the JavaScript calendar if available (provided by Date module package).
+  if (!empty($element['#datepicker'])) {
+    $class[] = 'webform-datepicker';
+    $calendar_class = array('webform-calendar');
+    if ($element['#start_date']) {
+      $calendar_class[] = 'webform-calendar-start-' . $element['#start_date'];
+    }
+    if ($element['#end_date']) {
+      $calendar_class[] = 'webform-calendar-end-' . $element['#end_date'];
+    }
+    $calendar_class[] ='webform-calendar-day-' . variable_get('date_first_day', 0);
+
+    $calendar = theme('webform_calendar', array('component' => $element['#webform_component'], 'calendar_classes' => $calendar_class));
+  }
+
+  $output = '';
+  $output .= '<div class="' . implode(' ', $class) . '">';
+  $output .= drupal_render_children($element);
+  $output .= isset($calendar) ? $calendar : '';
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * Element validation for Webform date fields.
+ */
+function webform_validate_date($element, $form_state) {
+  // Check if the user filled the required fields.
+  foreach (array('day', 'month', 'year') as $field_type) {
+    if (!is_numeric($element[$field_type]['#value']) && $element['#required']) {
+      form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
+      return;
+    }
+  }
+
+  if ($element['month']['#value'] !== '' || $element['day']['#value'] !== '' || $element['year']['#value'] !== '') {
+    // Check for a valid date.
+    if (!checkdate((int) $element['month']['#value'], (int) $element['day']['#value'], (int) $element['year']['#value'])) {
+      form_error($element, t('Entered !name is not a valid date.', array('!name' => $element['#title'])));
+      return;
+    }
+
+    // Create a timestamp of the entered value for comparison.
+    $timestamp = strtotime($element['year']['#value'] . '-' . $element['month']['#value'] . '-' . $element['day']['#value']);
+    $format = webform_date_format('short');
+
+    // Flip start and end if needed.
+    $date1 = strtotime($element['#start_date']);
+    $date2 = strtotime($element['#end_date']);
+    if ($date1 !== FALSE && $date2 !== FALSE) {
+      $start_date = $date1 < $date2 ? $date1 : $date2;
+      $end_date = $date1 > $date2 ? $date1 : $date2;
+    }
+    else {
+      $start_date = $date1;
+      $end_date = $date2;
+    }
+
+    // Check that the date is after the start date.
+    if ($start_date !== FALSE) {
+      if ($timestamp < $start_date) {
+        form_error($element, t('The entered date must be @start_date or later.', array('@start_date' => date($format, $start_date))));
+      }
+    }
+
+    // Check that the date is before the end date.
+    if ($end_date !== FALSE) {
+      if ($timestamp > $end_date) {
+        form_error($element, t('The entered date must be @end_date or earlier.', array('@end_date' => date($format, $end_date))));
+      }
+    }
+  }
+}
+
+/**
+ * Implements _webform_submit_component().
+ */
+function _webform_submit_date($component, $value) {
+  // Convert the date to an ISO 8601 format.
+  return ($value['year'] && $value['month'] && $value['day']) ? webform_date_string($value, 'date') : '';
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_date($component, $value, $format = 'html') {
+  $value = webform_date_array(isset($value[0]) ? $value[0] : '', 'date');
+
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_date',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#format' => $format,
+    '#value' => $value,
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the text output for this component.
+ */
+function theme_webform_display_date($variables) {
+  $element = $variables['element'];
+  $output = ' ';
+  if ($element['#value']['year'] && $element['#value']['month'] && $element['#value']['day']) {
+    $timestamp = webform_strtotime($element['#value']['month'] . '/' . $element['#value']['day'] . '/' . $element['#value']['year']);
+    $format = webform_date_format('medium');
+    $output = format_date($timestamp, 'custom', $format, 'UTC');
+  }
+
+  return $output;
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_date($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->orderBy('sid');
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $result = $query->execute();
+
+  $dates = array();
+  $submissions = 0;
+  foreach ($result as $row) {
+    $submissions++;
+    if ($row['data']) {
+      $dates[] = webform_date_array($row['data']);
+    }
+  }
+
+  // Display stats.
+  $nonblanks = count($dates);
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User entered value'), $nonblanks);
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_date($component, $value) {
+  if ($value[0]) {
+    $timestamp = webform_strtotime($value[0]);
+    $format = webform_date_format('short');
+    return format_date($timestamp, 'custom', $format, 'UTC');
+  }
+  else {
+    return '';
+  }
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_date($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_date($component, $export_options, $value) {
+  if ($value[0]) {
+    $timestamp = webform_strtotime($value[0]);
+    $format = webform_date_format('short');
+    return format_date($timestamp, 'custom', $format, 'UTC');
+  }
+  else {
+    return '';
+  }
+}
diff -Naurw webform_old/components/email.inc webform/components/email.inc
--- webform_old/components/email.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/email.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,285 @@
+<?php
+
+/**
+ * @file
+ * Webform module email component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_email() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'width' => '',
+      'unique' => 0,
+      'disabled' => 0,
+      'title_display' => 0,
+      'description' => '',
+      'attributes' => array(),
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_email() {
+  return array(
+    'webform_email' => array(
+      'render element' => 'element',
+      'file' => 'components/email.inc',
+    ),
+    'webform_display_email' => array(
+      'render element' => 'element',
+      'file' => 'components/email.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_email($component) {
+  $form['value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . theme('webform_token_help'),
+    '#size' => 60,
+    '#maxlength' => 127,
+    '#weight' => 0,
+    '#attributes' => ($component['value'] == '%useremail' && count(form_get_errors()) == 0) ? array('disabled' => TRUE) : array(),
+    '#id' => 'email-value',
+  );
+  $form['user_email'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('User email as default'),
+    '#default_value' => $component['value'] == '%useremail' ? 1 : 0,
+    '#description' => t('Set the default value of this field to the user email, if he/she is logged in.'),
+    '#attributes' => array('onclick' => 'getElementById("email-value").value = (this.checked ? "%useremail" : ""); getElementById("email-value").disabled = this.checked;'),
+    '#weight' => 0,
+    '#element_validate' => array('_webform_edit_email_validate'),
+  );
+  $form['display']['width'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Width'),
+    '#default_value' => $component['extra']['width'],
+    '#description' => t('Width of the textfield.') . ' ' . t('Leaving blank will use the default size.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#parents' => array('extra', 'width'),
+  );
+  $form['display']['disabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Disabled'),
+    '#return_value' => 1,
+    '#description' => t('Make this field non-editable. Useful for setting an unchangeable default value.'),
+    '#weight' => 11,
+    '#default_value' => $component['extra']['disabled'],
+    '#parents' => array('extra', 'disabled'),
+  );
+  $form['validation']['unique'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Unique'),
+    '#return_value' => 1,
+    '#description' => t('Check that all entered values for this field are unique. The same value is not allowed to be used twice.'),
+    '#weight' => 1,
+    '#default_value' => $component['extra']['unique'],
+    '#parents' => array('extra', 'unique'),
+  );
+  return $form;
+}
+
+/**
+ * Element validation function for the email edit form.
+ */
+function _webform_edit_email_validate($element, &$form_state) {
+  if ($form_state['values']['user_email']) {
+    $form_state['values']['value'] = '%useremail';
+  }
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_email($component, $value = NULL, $filter = TRUE) {
+  global $user;
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'webform_email',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node) : $component['value'],
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#attributes' => $component['extra']['attributes'],
+    '#element_validate'  => array('_webform_validate_email'),
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  // Add an e-mail class for identifying the difference from normal textfields.
+  $element['#attributes']['class'][] = 'email';
+
+  // Enforce uniqueness.
+  if ($component['extra']['unique']) {
+    $element['#element_validate'][] = 'webform_validate_unique';
+  }
+
+  if (isset($value)) {
+    $element['#default_value'] = $value[0];
+  }
+
+  if ($component['extra']['disabled']) {
+    if ($filter) {
+      $element['#attributes']['readonly'] = 'readonly';
+    }
+    else {
+      $element['#disabled'] = TRUE;
+    }
+  }
+
+  // Change the 'width' option to the correct 'size' option.
+  if ($component['extra']['width'] > 0) {
+    $element['#size'] = $component['extra']['width'];
+  }
+
+  return $element;
+}
+
+/**
+ * Theme function to render an email component.
+ */
+function theme_webform_email($variables) {
+  $element = $variables['element'];
+
+  // This IF statement is mostly in place to allow our tests to set type="text"
+  // because SimpleTest does not support type="email".
+  if (!isset($element['#attributes']['type'])) {
+    $element['#attributes']['type'] = 'email';
+  }
+
+  // Convert properties to attributes on the element if set.
+  foreach (array('id', 'name', 'value', 'size') as $property) {
+    if (isset($element['#' . $property]) && $element['#' . $property] !== '') {
+      $element['#attributes'][$property] = $element['#' . $property];
+    }
+  }
+  _form_set_class($element, array('form-text', 'form-email'));
+
+  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+}
+
+/**
+ * A Drupal Form API Validation function. Validates the entered values from
+ * email components on the client-side form.
+ *
+ * @param $form_element
+ *   The e-mail form element.
+ * @param $form_state
+ *   The full form state for the webform.
+ * @return
+ *   None. Calls a form_set_error if the e-mail is not valid.
+ */
+function _webform_validate_email($form_element, &$form_state) {
+  $component = $form_element['#webform_component'];
+  $value = trim($form_element['#value']);
+  if ($value !== '' && !valid_email_address($value)) {
+    form_error($form_element, t('%value is not a valid email address.', array('%value' => $value)));
+  }
+  else {
+    form_set_value($form_element, $value, $form_state);
+  }
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_email($component, $value, $format = 'html') {
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_email',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#format' => $format,
+    '#value' => isset($value[0]) ? $value[0] : '',
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the text output for this component.
+ */
+function theme_webform_display_email($variables) {
+  $element = $variables['element'];
+  $element['#value'] = empty($element['#value']) ? ' ' : $element['#value'];
+  return $element['#format'] == 'html' ? check_plain($element['#value']) : $element['#value'];
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_email($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $nonblanks = 0;
+  $submissions = 0;
+  $wordcount = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    if (drupal_strlen(trim($data['data'])) > 0) {
+      $nonblanks++;
+      $wordcount += str_word_count(trim($data['data']));
+    }
+    $submissions++;
+  }
+
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User entered value'), $nonblanks);
+  $rows[2] = array(t('Average submission length in words (ex blanks)'), ($nonblanks != 0 ? number_format($wordcount/$nonblanks, 2) : '0'));
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_email($component, $value) {
+  return check_plain(empty($value[0]) ? '' : $value[0]);
+}
+
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_email($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_email($component, $export_options, $value) {
+  return empty($value[0]) ? '' : $value[0];
+}
diff -Naurw webform_old/components/fieldset.inc webform/components/fieldset.inc
--- webform_old/components/fieldset.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/fieldset.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Webform module fieldset component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_fieldset() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'extra' => array(
+      'title_display' => 0,
+      'collapsible' => 0,
+      'collapsed' => 0,
+      'description' => '',
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_fieldset($component) {
+  $form = array();
+  $form['display']['collapsible'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Collapsible'),
+    '#default_value' => $component['extra']['collapsible'],
+    '#description' => t('If this fieldset is collapsible, the user may open or close the fieldset.'),
+    '#weight' => 0,
+    '#parents' => array('extra', 'collapsible'),
+  );
+  $form['display']['collapsed'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Collapsed by Default'),
+    '#default_value' => $component['extra']['collapsed'],
+    '#description' => t('Collapsible fieldsets are "open" by default. Select this option to default the fieldset to "closed."'),
+    '#weight' => 3,
+    '#parents' => array('extra', 'collapsed'),
+  );
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_fieldset($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'fieldset',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : NULL,
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#collapsible' => $component['extra']['collapsible'],
+    '#collapsed' => $component['extra']['collapsed'],
+    '#attributes' => array('class' => array('webform-component-fieldset'), 'id' => 'webform-component-' . $component['form_key']),
+    '#pre_render' => array('webform_fieldset_prerender', 'webform_element_title_display'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  return $element;
+}
+
+/**
+ * Pre-render function to set a fieldset ID.
+ */
+function webform_fieldset_prerender($element) {
+  $element['#attributes']['id'] = 'webform-component-' . str_replace('_', '-', implode('--', array_slice($element['#parents'], 1)));
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_fieldset($component, $value, $format = 'html') {
+  if ($format == 'text') {
+    $element = array(
+      '#title' => $component['name'],
+      '#weight' => $component['weight'],
+      '#theme_wrappers' => array('webform_element_text'),
+      '#translatable' => array('title'),
+    );
+  }
+  else {
+    $element = _webform_render_fieldset($component, $value);
+  }
+
+  $element['#format'] = $format;
+
+  return $element;
+}
diff -Naurw webform_old/components/file.inc webform/components/file.inc
--- webform_old/components/file.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/file.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,532 @@
+<?php
+
+/**
+ * @file
+ * Webform module file component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_file() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'mandatory' => 0,
+    'pid' => 0,
+    'weight' => 0,
+    'extra' => array(
+      'filtering' => array(
+        'types' => array('gif', 'jpg', 'png'),
+        'addextensions' => '',
+        'size' => '2 MB',
+      ),
+      'scheme' => 'public',
+      'directory' => '',
+      'progress_indicator' => 'throbber',
+      'title_display' => 0,
+      'description' => '',
+      'attributes' => array(),
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_file() {
+  return array(
+    'webform_edit_file_extensions' => array(
+      'render element' => 'element',
+      'file' => 'components/file.inc',
+    ),
+    'webform_display_file' => array(
+      'render element' => 'element',
+      'file' => 'components/file.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_file($component) {
+    $form = array();
+    $form['#element_validate'] = array('_webform_edit_file_check_directory');
+    $form['#after_build'] = array('_webform_edit_file_check_directory');
+
+    $form['validation']['size'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Max upload size'),
+      '#default_value' => $component['extra']['filtering']['size'],
+      '#description' => t('Enter the max file size a user may upload such as 2 MB or 800 KB. Your server has a max upload size of @size.', array('@size' => format_size(file_upload_max_size()))),
+      '#size' => 10,
+      '#parents' => array('extra', 'filtering', 'size'),
+      '#element_validate' => array('_webform_edit_file_size_validate'),
+      '#weight' => 1,
+    );
+
+    $form['validation']['extensions'] = array(
+      '#element_validate' => array('_webform_edit_file_extensions_validate'),
+      '#parents' => array('extra', 'filtering'),
+      '#theme' => 'webform_edit_file_extensions',
+      '#theme_wrappers' => array('form_element'),
+      '#title' => t('Allowed file extensions'),
+      '#attached' => array(
+        'js' => array(drupal_get_path('module', 'webform') . '/js/webform-admin.js'),
+        'css' => array(drupal_get_path('module', 'webform') . '/css/webform-admin.css'),
+      ),
+      '#weight' => 2,
+    );
+
+    // Find the list of all currently valid extensions.
+    $current_types = isset($component['extra']['filtering']['types']) ? $component['extra']['filtering']['types'] : array();
+
+    $types = array('gif', 'jpg', 'png');
+    $form['validation']['extensions']['types']['webimages'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Web images'),
+      '#options' => drupal_map_assoc($types),
+      '#default_value' => array_intersect($current_types, $types),
+    );
+
+    $types = array('bmp', 'eps', 'tif', 'pict', 'psd');
+    $form['validation']['extensions']['types']['desktopimages'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Desktop images'),
+      '#options' => drupal_map_assoc($types),
+      '#default_value' => array_intersect($current_types, $types),
+    );
+
+    $types = array('txt', 'rtf', 'html', 'odf', 'pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'xml');
+    $form['validation']['extensions']['types']['documents'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Documents'),
+      '#options' => drupal_map_assoc($types),
+      '#default_value' => array_intersect($current_types, $types),
+    );
+
+    $types = array('avi', 'mov', 'mp3', 'ogg', 'wav');
+    $form['validation']['extensions']['types']['media'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Media'),
+      '#options' => drupal_map_assoc($types),
+      '#default_value' => array_intersect($current_types, $types),
+    );
+
+    $types = array('bz2', 'dmg', 'gz', 'jar', 'rar', 'sit', 'tar', 'zip');
+    $form['validation']['extensions']['types']['archives'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Archives'),
+      '#options' => drupal_map_assoc($types),
+      '#default_value' => array_intersect($current_types, $types),
+    );
+
+    $form['validation']['extensions']['addextensions'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Additional extensions'),
+      '#default_value' => $component['extra']['filtering']['addextensions'],
+      '#description' => t('Enter a list of additional file extensions for this upload field, separated by commas.<br /> Entered extensions will be appended to checked items above.'),
+      '#size' => 20,
+      '#weight' => 3,
+    );
+
+    $scheme_options = array();
+    foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
+      $scheme_options[$scheme] = $stream_wrapper['name'];
+    }
+    $form['extra']['scheme'] = array(
+      '#type' => 'radios',
+      '#title' => t('Upload destination'),
+      '#options' => $scheme_options,
+      '#default_value' => $component['extra']['scheme'],
+      '#description' => t('Private file storage has significantly more overhead than public files, but restricts file access to users who can view submissions.'),
+      '#weight' => 4,
+      '#access' => count($scheme_options) > 1,
+    );
+    $form['extra']['directory'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Upload directory'),
+      '#default_value' => $component['extra']['directory'],
+      '#description' => t('You may optionally specify a sub-directory to store your files.'),
+      '#weight' => 5,
+      '#field_prefix' => 'webform/',
+    );
+
+    $form['display']['progress_indicator'] = array(
+      '#type' => 'radios',
+      '#title' => t('Progress indicator'),
+      '#options' => array(
+        'throbber' => t('Throbber'),
+        'bar' => t('Bar with progress meter'),
+      ),
+      '#default_value' => $component['extra']['progress_indicator'],
+      '#description' => t('The throbber display does not show the status of uploads but takes up less space. The progress bar is helpful for monitoring progress on large uploads.'),
+      '#weight' => 16,
+      '#access' => file_progress_implementation(),
+      '#parents' => array('extra', 'progress_indicator'),
+    );
+
+    // TODO: Make managed_file respect the "size" parameter.
+    /*
+    $form['display']['width'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Width'),
+      '#default_value' => $component['extra']['width'],
+      '#description' => t('Width of the file field.') . ' ' . t('Leaving blank will use the default size.'),
+      '#size' => 5,
+      '#maxlength' => 10,
+      '#weight' => 4,
+      '#parents' => array('extra', 'width')
+    );
+    */
+
+    return $form;
+}
+
+/**
+ * A Form API element validate function to check filesize is valid.
+ */
+function _webform_edit_file_size_validate($element) {
+  if (!empty($element['#value'])) {
+    $set_filesize = parse_size($element['#value']);
+    if ($set_filesize == FALSE) {
+      form_error($element, t('File size @value is not a valid filesize. Use a value such as 2 MB or 800 KB.', array('@value' => $element['#value'])));
+    }
+    else {
+      $max_filesize = parse_size(file_upload_max_size());
+      if ($max_filesize < $set_filesize) {
+        form_error($element, t('An upload size of @value is too large, you are allow to upload files @max or less.', array('@value' => $element['#value'], '@max' => format_size($max_filesize))));
+      }
+    }
+  }
+}
+
+/**
+ * A Form API after build and validate function.
+ *
+ * Ensure that the destination directory exists and is writable.
+ */
+function _webform_edit_file_check_directory($element) {
+  $scheme = $element['extra']['scheme']['#value'];
+  $directory = $element['extra']['directory']['#value'];
+
+  $destination_dir = file_stream_wrapper_uri_normalize($scheme . '://' . $directory . '/webform');
+
+  // Sanity check input to prevent use parent (../) directories.
+  if (preg_match('/\.\.[\/\\\]/', $destination_dir . '/')) {
+    form_error($element['extra']['directory'], t('The save directory %directory is not valid.', array('%directory' => $directory)));
+  }
+  else {
+    $destination_success = file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY);
+    if (!$destination_success) {
+      form_error($element['extra']['directory'], t('The save directory %directory could not be created. Check that the webform files directory is writable.', array('%directory' => $directory)));
+    }
+  }
+
+  return $element;
+}
+
+/**
+ * A Form API element validate function.
+ *
+ * Change the submitted values of the component so that all filtering extensions
+ * are saved as a single array.
+ */
+function _webform_edit_file_extensions_validate($element, &$form_state) {
+  // Predefined types.
+  $extensions = array();
+  foreach (element_children($element['types']) as $category) {
+    foreach (array_keys($element['types'][$category]['#value']) as $extension) {
+      if ($element['types'][$category][$extension]['#value']) {
+        $extensions[] = $extension;
+      }
+    }
+  }
+
+  // Additional types.
+  $additional_extensions = explode(',', $element['addextensions']['#value']);
+  foreach ($additional_extensions as $extension) {
+    $clean_extension = drupal_strtolower(trim($extension));
+    if (!empty($clean_extension) && !in_array($clean_extension, $extensions)) {
+      $extensions[] = $clean_extension;
+    }
+  }
+
+  form_set_value($element['types'], $extensions, $form_state);
+}
+
+/**
+ * Output the list of allowed extensions as checkboxes.
+ */
+function theme_webform_edit_file_extensions($variables) {
+  $element = $variables['element'];
+
+  // Format the components into a table.
+  $rows = array();
+  foreach (element_children($element['types']) as $filtergroup) {
+    $row = array();
+    $first_row = count($rows);
+    if ($element['types'][$filtergroup]['#type'] == 'checkboxes') {
+      $select_link = ' <a href="#" class="webform-select-link webform-select-link-' . $filtergroup . '">(' . t('select') . ')</a>';
+      $row[] = $element['types'][$filtergroup]['#title'];
+      $row[] = array('data' => $select_link, 'width' => 40);
+      $row[] = array('data' => drupal_render_children($element['types'][$filtergroup]), 'class' => array('webform-file-extensions', 'webform-select-group-' . $filtergroup));
+      $rows[] = array('data' => $row);
+      unset($element['types'][$filtergroup]);
+    }
+  }
+
+  // Add the row for additional types.
+  $row = array();
+  $title = $element['addextensions']['#title'];
+  $element['addextensions']['#title'] = NULL;
+  $row[] = array('data' => $title, 'colspan' => 2);
+  $row[] = drupal_render($element['addextensions']);
+  $rows[] = $row;
+
+  $header = array(array('data' => t('Category'), 'colspan' => '2'), array('data' => t('Types')));
+
+  // Create the table inside the form.
+  $element['types']['table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+    '#attributes' => array('class' => array('webform-file-extensions')),
+  );
+
+  return drupal_render_children($element);
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_file($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  // Cap the upload size according to the PHP limit.
+  $max_filesize = parse_size(file_upload_max_size());
+  $set_filesize = $component['extra']['filtering']['size'];
+  if (!empty($set_filesize) && parse_size($set_filesize) < $max_filesize) {
+    $max_filesize = parse_size($set_filesize);
+  }
+
+  $element = array(
+    '#type' => 'managed_file',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#required' => $component['mandatory'],
+    '#default_value' => isset($value[0]) ? $value[0] : NULL,
+    '#attributes' => $component['extra']['attributes'],
+    '#upload_validators' => array(
+      'file_validate_size' => array($max_filesize),
+      'file_validate_extensions' => array(implode(' ', $component['extra']['filtering']['types'])),
+    ),
+    '#pre_render' => array_merge(element_info_property('managed_file', '#pre_render'), array('webform_file_allow_access')),
+    '#upload_location' => $component['extra']['scheme'] . '://webform/' . $component['extra']['directory'],
+    '#progress_indicator' => $component['extra']['progress_indicator'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#weight' => $component['weight'],
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  return $element;
+}
+
+/**
+ * Implements _webform_submit_component().
+ */
+function _webform_submit_file($component, $value) {
+  if (is_array($value)) {
+    return !empty($value['fid']) ? $value['fid'] : '';
+  }
+  else {
+    return !empty($value) ? $value : '';
+  }
+}
+
+/**
+ * Pre-render callback to allow access to uploaded files.
+ *
+ * Files that have not yet been saved into a submission must be accessible to
+ * the user who uploaded it, but no one else. After the submission is saved,
+ * access is granted through the file_usage table. Before then, we use a
+ * $_SESSION value to record a user's upload.
+ *
+ * @see webform_file_download()
+ */
+function webform_file_allow_access($element) {
+  if (!empty($element['#value']['fid'])) {
+    $fid = $element['#value']['fid'];
+    $_SESSION['webform_files'][$fid] = $fid;
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_file($component, $value, $format = 'html') {
+  $fid = isset($value[0]) ? $value[0] : NULL;
+  return array(
+    '#title' => $component['name'],
+    '#value' => $fid ? webform_get_file($fid) : NULL,
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_file',
+    '#theme_wrappers' => $format == 'text' ? array('webform_element_text') : array('webform_element'),
+    '#format' => $format,
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the output of text data for this component
+ */
+function theme_webform_display_file($variables) {
+  $element = $variables['element'];
+
+  $file = $element['#value'];
+  $url = !empty($file) ? webform_file_url($file->uri) : t('no upload');
+  return !empty($file) ? ($element['#format'] == 'text' ? $url : l($file->filename, $url)) : ' ';
+}
+
+/**
+ * Implements _webform_delete_component().
+ */
+function _webform_delete_file($component, $value) {
+  // Delete an individual submission file.
+  if (!empty($value[0]) && ($file = webform_get_file($value[0]))) {
+    file_usage_delete($file, 'webform');
+    file_delete($file);
+  }
+}
+
+/**
+ * Implements _webform_attachments_component().
+ */
+function _webform_attachments_file($component, $value) {
+  $file = (array) webform_get_file($value[0]);
+  //This is necessary until the next release of mimemail is out, see [#1388786]
+  $file['filepath'] = $file['uri'];
+  $files = array($file);
+  return $files;
+}
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_file($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $nonblanks = 0;
+  $sizetotal = 0;
+  $submissions = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    $file = webform_get_file($data['data']);
+    if (isset($file->filesize)) {
+      $nonblanks++;
+      $sizetotal += $file->filesize;
+    }
+    $submissions++;
+  }
+
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User uploaded file'), $nonblanks);
+  $rows[2] = array(t('Average uploaded file size'), ($sizetotal != 0 ? (int) (($sizetotal/$nonblanks)/1024) . ' KB' : '0'));
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_file($component, $value) {
+  $output = '';
+  $file = webform_get_file($value[0]);
+  if (!empty($file->fid)) {
+    $output = '<a href="' . webform_file_url($file->uri) . '">' . check_plain(webform_file_name($file->uri)) . '</a>';
+    $output .= ' (' . (int) ($file->filesize/1024) . ' KB)';
+  }
+  return $output;
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_file($component, $export_options) {
+  $header = array();
+  // Two columns in header.
+  $header[0] = array('', '');
+  $header[1] = array($component['name'], '');
+  $header[2] = array(t('Name'), t('Filesize (KB)'));
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_file($component, $export_options, $value) {
+  $file = webform_get_file($value[0]);
+  return empty($file->filename) ? array('', '') : array(webform_file_url($file->uri), (int) ($file->filesize/1024));
+}
+
+/**
+ * Helper function to create proper file names for uploaded file.
+ */
+function webform_file_name($filepath) {
+  if (!empty($filepath)) {
+    $info = pathinfo($filepath);
+    $file_name = $info['basename'];
+  }
+  return isset($file_name) ? $file_name : '';
+}
+
+/**
+ * Helper function to create proper URLs for uploaded file.
+ */
+function webform_file_url($uri) {
+  if (!empty($uri)) {
+    $file_url = file_create_url($uri);
+  }
+  return isset($file_url) ? $file_url : '';
+}
+
+/**
+ * Helper function to load a file from the database.
+ */
+function webform_get_file($fid) {
+  // Simple check to prevent loading of NULL values, which throws an entity
+  // system error.
+  return $fid ? file_load($fid) : FALSE;
+}
+
+/**
+ * Given a submission with file_usage set, add or remove file usage entries.
+ */
+function webform_file_usage_adjust($submission) {
+  if (isset($submission->file_usage)) {
+    $files = file_load_multiple($submission->file_usage['added_fids']);
+    foreach ($files as $file) {
+      $file->status = 1;
+      file_save($file);
+      file_usage_add($file, 'webform', 'submission', $submission->sid);
+    }
+
+    $files = file_load_multiple($submission->file_usage['deleted_fids']);
+    foreach ($files as $file) {
+      file_usage_delete($file, 'webform', 'submission', $submission->sid);
+      file_delete($file);
+    }
+  }
+}
+
diff -Naurw webform_old/components/grid.inc webform/components/grid.inc
--- webform_old/components/grid.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/grid.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,430 @@
+<?php
+
+/**
+ * @file
+ * Webform module grid component.
+ */
+
+// Grid depends on functions provided by select.
+webform_component_include('select');
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_grid() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'mandatory' => 0,
+    'pid' => 0,
+    'weight' => 0,
+    'extra' => array(
+      'options' => '',
+      'questions' => '',
+      'optrand' => 0,
+      'qrand' => 0,
+      'title_display' => 0,
+      'custom_option_keys' => 0,
+      'custom_question_keys' => 0,
+      'description' => '',
+      'private' => FALSE,
+    ),
+  );
+}
+
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_grid() {
+  return array(
+    'webform_grid' => array(
+      'render element' => 'element',
+      'file' => 'components/grid.inc',
+    ),
+    'webform_display_grid' => array(
+      'render element' => 'element',
+      'file' => 'components/grid.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_grid($component) {
+  $form = array();
+
+  if (module_exists('options_element')) {
+    $form['options'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Options'),
+      '#collapsible' => TRUE,
+      '#description' => t('Options to select across the top. Usually these are ratings such as "poor" through "excellent" or "strongly disagree" through "strongly agree".'),
+      '#attributes' => array('class' => array('webform-options-element')),
+      '#element_validate' => array('_webform_edit_validate_options'),
+    );
+    $form['options']['options'] = array(
+      '#type' => 'options',
+      '#options' => _webform_select_options_from_text($component['extra']['options'], TRUE),
+      '#optgroups' => FALSE,
+      '#default_value' => FALSE,
+      '#default_value_allowed' => FALSE,
+      '#optgroups' => FALSE,
+      '#key_type' => 'mixed',
+      '#key_type_toggle' => t('Customize option keys (Advanced)'),
+      '#key_type_toggled' => $component['extra']['custom_option_keys'],
+    );
+
+    $form['questions'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Questions'),
+      '#collapsible' => TRUE,
+      '#description' => t('Questions list down the side of the grid.'),
+      '#attributes' => array('class' => array('webform-options-element')),
+      '#element_validate' => array('_webform_edit_validate_options'),
+    );
+    $form['questions']['options'] = array(
+      '#type' => 'options',
+      '#options' => _webform_select_options_from_text($component['extra']['questions'], TRUE),
+      '#optgroups' => FALSE,
+      '#default_value' => FALSE,
+      '#default_value_allowed' => FALSE,
+      '#optgroups' => FALSE,
+      '#key_type' => 'mixed',
+      '#key_type_toggle' => t('Customize question keys (Advanced)'),
+      '#key_type_toggled' => $component['extra']['custom_question_keys'],
+    );
+  }
+  else {
+    $form['extra']['options'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Options'),
+      '#default_value' => $component['extra']['options'],
+      '#description' => t('Options to select across the top. One option per line. <strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Use of only alphanumeric characters and underscores is recommended in keys.') . theme('webform_token_help'),
+      '#cols' => 60,
+      '#rows' => 5,
+      '#weight' => -3,
+      '#required' => TRUE,
+      '#wysiwyg' => FALSE,
+      '#element_validate' => array('_webform_edit_validate_select'),
+    );
+    $form['extra']['questions'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Questions'),
+      '#default_value' => $component['extra']['questions'],
+      '#description' => t('Questions list down the side of the grid. One question per line. <strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Use of only alphanumeric characters and underscores is recommended in keys.') . theme('webform_token_help'),
+      '#cols' => 60,
+      '#rows' => 5,
+      '#weight' => -2,
+      '#required' => TRUE,
+      '#wysiwyg' => FALSE,
+      '#element_validate' => array('_webform_edit_validate_select'),
+    );
+  }
+
+  $form['display']['optrand'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Randomize Options'),
+    '#default_value' => $component['extra']['optrand'],
+    '#description' => t('Randomizes the order of options on the top when they are displayed in the form.'),
+    '#parents' => array('extra', 'optrand')
+  );
+  $form['display']['qrand'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Randomize Questions'),
+    '#default_value' => $component['extra']['qrand'],
+    '#description' => t('Randomize the order of the questions on the side when they are displayed in the form.'),
+    '#parents' => array('extra', 'qrand')
+  );
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_grid($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'webform_grid',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#grid_questions' => _webform_select_options_from_text($component['extra']['questions'], TRUE),
+    '#grid_options' => _webform_select_options_from_text($component['extra']['options'], TRUE),
+    '#optrand' => $component['extra']['optrand'],
+    '#qrand' => $component['extra']['qrand'],
+    '#theme' => 'webform_grid',
+    '#theme_wrappers' => array('webform_element'),
+    '#process' => array('webform_expand_grid'),
+    '#translatable' => array('title', 'description', 'grid_options', 'grid_questions'),
+  );
+
+  if ($value) {
+    $element['#default_value'] = $value;
+  }
+
+  return $element;
+}
+
+/**
+ * A Form API #process function for Webform grid fields.
+ */
+function webform_expand_grid($element) {
+  $options = $element['#grid_options'];
+  $questions = $element['#grid_questions'];
+
+  if (!empty($element['#optrand'])) {
+    _webform_shuffle_options($options);
+  }
+
+  if (!empty($element['#qrand'])) {
+    _webform_shuffle_options($questions);
+  }
+
+  foreach ($questions as $key => $question) {
+    if ($question != '') {
+      $element[$key] = array(
+        '#title' => $question,
+        '#required' => $element['#required'],
+        '#options' => $options,
+        '#type' => 'radios',
+        '#process' => array('form_process_radios', 'webform_expand_select_ids'),
+
+        // Webform handles validation manually.
+        '#validated' => TRUE,
+        '#webform_validated' => FALSE,
+        '#translatable' => array('title'),
+      );
+    }
+  }
+
+  $value = isset($element['#default_value']) ? $element['#default_value'] : array();
+  foreach (element_children($element) as $key) {
+    if (isset($value[$key])) {
+      $element[$key]['#default_value'] = ($value[$key] !== '') ? $value[$key] : NULL;
+    }
+    else {
+      $element[$key]['#default_value'] = NULL;
+    }
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_grid($component, $value, $format = 'html') {
+  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
+
+  $element = array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#format' => $format,
+    '#grid_questions' => $questions,
+    '#grid_options' => $options,
+    '#theme' => 'webform_display_grid',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#sorted' => TRUE,
+    '#translatable' => array('#title', '#grid_questions', '#grid_options'),
+  );
+
+  foreach ($questions as $key => $question) {
+    if ($question !== '') {
+      $element[$key] = array(
+        '#title' => $question,
+        '#value' => isset($value[$key]) ? $value[$key] : NULL,
+        '#translatable' => array('#title', '#value'),
+      );
+    }
+  }
+
+  return $element;
+}
+
+/**
+ * Format the text output for this component.
+ */
+function theme_webform_display_grid($variables) {
+  $element = $variables['element'];
+
+  $component = $element['#webform_component'];
+  $format = $element['#format'];
+
+  if ($format == 'html') {
+    $rows = array();
+    $header = array(array('data' => '', 'class' => array('webform-grid-question')));
+    foreach ($element['#grid_options'] as $option) {
+      $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option'));
+    }
+    foreach ($element['#grid_questions'] as $question_key => $question) {
+      $row = array();
+      $row[] = array('data' => _webform_filter_xss($question), 'class' => array('webform-grid-question'));
+      foreach ($element['#grid_options'] as $option_value => $option_label) {
+        if (strcmp($element[$question_key]['#value'], $option_value) == 0) {
+          $row[] = array('data' => '<strong>X</strong>', 'class' => array('checkbox', 'webform-grid-option'));
+        }
+        else {
+          $row[] = array('data' => '&nbsp;', 'class' => array('checkbox', 'webform-grid-option'));
+        }
+      }
+      $rows[] = $row;
+    }
+
+    $option_count = count($header) - 1;
+    $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-grid', 'webform-grid-' . $option_count))));
+  }
+  else {
+    $items = array();
+    foreach (element_children($element) as $key) {
+      $items[] = ' - ' . $element[$key]['#title'] . ': ' . (isset($element['#grid_options'][$element[$key]['#value']]) ? $element['#grid_options'][$element[$key]['#value']] : '');
+    }
+    $output = implode("\n", $items);
+  }
+
+  return $output;
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_grid($component, $sids = array()) {
+  // Generate the list of options and questions.
+  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
+  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+
+  // Generate a lookup table of results.
+  $query = db_select('webform_submitted_data', 'wsd')
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->condition('data', '', '<>')
+    ->groupBy('no')
+    ->groupBy('data');
+  $query->addExpression('COUNT(sid)', 'datacount');
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $result = $query->execute();
+  $counts = array();
+  foreach ($result as $data) {
+    $counts[$data->no][$data->data] = $data->datacount;
+  }
+
+  // Create an entire table to be put into the returned row.
+  $rows = array();
+  $header = array('');
+
+  // Add options as a header row.
+  foreach ($options as $option) {
+    $header[] = _webform_filter_xss($option);
+  }
+
+  // Add questions as each row.
+  foreach ($questions as $qkey => $question) {
+    $row = array(_webform_filter_xss($question));
+    foreach ($options as $okey => $option) {
+      $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
+    }
+    $rows[] = $row;
+  }
+  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-grid'))));
+
+
+  return array(array(array('data' => $output, 'colspan' => 2)));
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_grid($component, $value) {
+  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
+
+  $output = '';
+  // Set the value as a single string.
+  foreach ($questions as $key => $label) {
+    if (isset($value[$key]) && isset($options[$value[$key]])) {
+      $output .= _webform_filter_xss($label) . ': ' . _webform_filter_xss($options[$value[$key]]) . '<br />';
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_grid($component, $export_options) {
+  $header = array();
+  $header[0] = array('');
+  $header[1] = array($component['name']);
+  $items = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+  $count = 0;
+  foreach ($items as $key => $item) {
+    // Empty column per sub-field in main header.
+    if ($count != 0) {
+      $header[0][] = '';
+      $header[1][] = '';
+    }
+    // The value for this option.
+    $header[2][] = $item;
+    $count++;
+  }
+
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_grid($component, $export_options, $value) {
+  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
+  $return = array();
+  foreach ($questions as $key => $question) {
+    if (isset($value[$key]) && isset($options[$value[$key]])) {
+      $return[] = $export_options['select_keys'] ? $value[$key] : $options[$value[$key]];
+    }
+    else {
+      $return[] = '';
+    }
+  }
+  return $return;
+}
+
+function theme_webform_grid($variables) {
+  $element = $variables['element'];
+
+  $rows = array();
+  $header = array(array('data' => '', 'class' => array('webform-grid-question')));
+  // Set the header for the table.
+  foreach ($element['#grid_options'] as $option) {
+    $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option'));
+  }
+
+  foreach (element_children($element) as $key) {
+    $question_element = $element[$key];
+
+    // Create a row with the question title.
+    $row = array(array('data' => _webform_filter_xss($question_element['#title']), 'class' => array('webform-grid-question')));
+
+    // Render each radio button in the row.
+    $radios = form_process_radios($question_element);
+    foreach (element_children($radios) as $key) {
+      $radios[$key]['#title_display'] = 'invisible';
+      $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'));
+    }
+    $rows[] = $row;
+  }
+
+  $option_count = count($header) - 1;
+  return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-grid', 'webform-grid-' . $option_count))));
+}
diff -Naurw webform_old/components/hidden.inc webform/components/hidden.inc
--- webform_old/components/hidden.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/hidden.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @file
+ * Webform module hidden component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_hidden() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'extra' => array(
+      'private' => FALSE,
+      'hidden_type' => 'value',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_hidden() {
+  return array(
+    'webform_display_hidden' => array(
+      'render element' => 'element',
+      'file' => 'components/hidden.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_hidden($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . theme('webform_token_help'),
+    '#cols' => 60,
+    '#rows' => 5,
+    '#weight' => 0,
+  );
+
+  $form['display']['hidden_type'] = array(
+    '#type' => 'radios',
+    '#options' => array(
+      'value' => t('Secure value (allows use of all tokens)'),
+      'hidden' => t('Hidden element (less secure, changeable via JavaScript)'),
+    ),
+    '#title' => t('Hidden type'),
+    '#description' => t('Both types of hidden fields are not shown to end-users. Using a <em>Secure value</em> allows the use of <em>all tokens</em>, even for anonymous users.'),
+    '#default_value' => $component['extra']['hidden_type'],
+    '#parents' => array('extra', 'hidden_type'),
+  );
+
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_hidden($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  // Set filtering options for "value" types, which are not displayed to the
+  // end user so they do not need to be sanitized.
+  $strict = $component['extra']['hidden_type'] != 'value';
+  $allow_anonymous = $component['extra']['hidden_type'] == 'value';
+  $default_value = $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, $strict, $allow_anonymous) : $component['value'];
+  if (isset($value[0])) {
+    $default_value = $value[0];
+  }
+
+  $element = array(
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#weight' => $component['weight'],
+    '#translatable' => array('title'),
+  );
+
+  if ($component['extra']['hidden_type'] == 'value') {
+    $element['#type'] = 'value';
+    $element['#value'] = $default_value;
+  }
+  else {
+    $element['#type'] = 'hidden';
+    $element['#default_value'] = $default_value;
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_hidden($component, $value, $format = 'html') {
+  $element = array(
+    '#title' => $component['name'],
+    '#markup' => isset($value[0]) ? $value[0] : NULL,
+    '#weight' => $component['weight'],
+    '#format' => $format,
+    '#theme' => 'webform_display_hidden',
+    '#theme_wrappers' => $format == 'text' ? array('webform_element_text') : array('webform_element'),
+    '#translatable' => array('title'),
+  );
+
+  return $element;
+}
+
+function theme_webform_display_hidden($variables) {
+  $element = $variables['element'];
+
+  return $element['#format'] == 'html' ? check_plain($element['#markup']) : $element['#markup'];
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_hidden($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $nonblanks = 0;
+  $submissions = 0;
+  $wordcount = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    if (strlen(trim($data['data'])) > 0) {
+      $nonblanks++;
+      $wordcount += str_word_count(trim($data['data']));
+    }
+    $submissions++;
+  }
+
+  $rows[0] = array( t('Empty'), ($submissions - $nonblanks));
+  $rows[1] = array( t('Non-empty'), $nonblanks);
+  $rows[2] = array( t('Average submission length in words (ex blanks)'),
+                    ($nonblanks !=0 ? number_format($wordcount/$nonblanks, 2) : '0'));
+  return $rows;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_table_hidden($component, $value) {
+  return check_plain(empty($value[0]) ? '' : $value[0]);
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_headers_hidden($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_hidden($component, $export_options, $value) {
+  return isset($value[0]) ? $value[0] : '';
+}
diff -Naurw webform_old/components/markup.inc webform/components/markup.inc
--- webform_old/components/markup.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/markup.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Webform module markup component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_markup() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'extra' => array(
+      'format' => NULL,
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_markup($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'text_format',
+    '#title' => t('Value'),
+    '#default_value' => $component['value'],
+    '#description' => t('Markup allows you to enter custom HTML or PHP logic into your form.') . theme('webform_token_help'),
+    '#weight' => -1,
+    '#format' => $component['extra']['format'],
+    '#element_validate' => array('_webform_edit_markup_validate'),
+  );
+
+  return $form;
+}
+
+/**
+ * Element validate handler; Set the text format value.
+ */
+function _webform_edit_markup_validate($form, &$form_state) {
+  if (is_array($form_state['values']['value'])) {
+    $form_state['values']['extra']['format'] = $form_state['values']['value']['format'];
+    $form_state['values']['value'] = $form_state['values']['value']['value'];
+  }
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_markup($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'markup',
+    '#title' => $filter ? NULL : $component['name'],
+    '#weight' => $component['weight'],
+    '#markup' => $filter ? _webform_filter_values(check_markup($component['value'], $component['extra']['format'], '', TRUE), $node, NULL, NULL, FALSE) : $component['value'],
+    '#format' => $component['extra']['format'],
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'markup'),
+  );
+
+  // TODO: Remove when #markup becomes available in D7.
+  $element['#value'] = $element['#markup'];
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_markup($component, $value, $format = 'html') {
+  return array();
+}
diff -Naurw webform_old/components/number.inc webform/components/number.inc
--- webform_old/components/number.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/number.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,721 @@
+<?php
+
+/**
+ * @file
+ * Webform module number component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_number() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'type' => 'textfield',
+      'field_prefix' => '',
+      'field_suffix' => '',
+      'unique' => 0,
+      'title_display' => 0,
+      'description' => '',
+      'attributes' => array(),
+      'private' => FALSE,
+      'min' => '',
+      'max' => '',
+      'step' => '',
+      'decimals' => '',
+      'point' => '.',
+      'separator' => ',',
+      'integer' => 0,
+      'excludezero' => 0,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_number() {
+  return array(
+    'webform_number' => array(
+      'render element' => 'element',
+      'file' => 'components/number.inc',
+    ),
+    'webform_display_number' => array(
+      'render element' => 'element',
+      'file' => 'components/number.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_number($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . theme('webform_token_help'),
+    '#size' => 60,
+    '#maxlength' => 1024,
+    '#weight' => 0,
+  );
+  $form['display']['type'] = array(
+    '#type' => 'radios',
+    '#title' => t('Element type'),
+    '#options' => array(
+      'textfield' => t('Text field'),
+      'select' => t('Select list'),
+    ),
+    '#default_value' => $component['extra']['type'],
+    '#description' => t('A minimum and maximum value are required if displaying as a select.'),
+    '#weight' => -1,
+    '#parents' => array('extra', 'type'),
+  );
+  $form['display']['field_prefix'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Prefix text placed to the left of the field'),
+    '#default_value' => $component['extra']['field_prefix'],
+    '#description' => t('Examples: $, #, -.'),
+    '#size' => 20,
+    '#maxlength' => 127,
+    '#weight' => 1.1,
+    '#parents' => array('extra', 'field_prefix'),
+  );
+  $form['display']['field_suffix'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Postfix text placed to the right of the field'),
+    '#default_value' => $component['extra']['field_suffix'],
+    '#description' => t('Examples: lb, kg, %.'),
+    '#size' => 20,
+    '#maxlength' => 127,
+    '#weight' => 1.2,
+    '#parents' => array('extra', 'field_suffix'),
+  );
+  $form['display']['decimals'] = array(
+    '#type' => 'select',
+    '#title' => t('Decimal places'),
+    '#options' => array('' => t('Automatic')) + drupal_map_assoc(range(0, 10)),
+    '#description' => t('Automatic will display up to @count decimals places if needed. A value of "2" is common to format currency amounts.', array('@count' => '4')),
+    '#default_value' => $component['extra']['decimals'],
+    '#weight' => 2,
+    '#parents' => array('extra', 'decimals'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  $form['display']['separator'] = array(
+    '#type' => 'select',
+    '#title' => t('Thousands separator'),
+    '#options' => array(
+      ',' => t('Comma (,)'),
+      '.' => t('Period (.)'),
+      ' ' => t('Space ( )'),
+      '' => t('None'),
+    ),
+    '#default_value' => $component['extra']['separator'],
+    '#weight' => 3,
+    '#parents' => array('extra', 'separator'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  $form['display']['point'] = array(
+    '#type' => 'select',
+    '#title' => t('Decimal point'),
+    '#options' => array(
+      ',' => t('Comma (,)'),
+      '.' => t('Period (.)'),
+    ),
+    '#default_value' => $component['extra']['point'],
+    '#weight' => 4,
+    '#parents' => array('extra', 'point'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  $form['validation']['unique'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Unique'),
+    '#return_value' => 1,
+    '#description' => t('Check that all entered values for this field are unique. The same value is not allowed to be used twice.'),
+    '#weight' => 1,
+    '#default_value' => $component['extra']['unique'],
+    '#parents' => array('extra', 'unique'),
+  );
+  $form['validation']['integer'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Integer'),
+    '#return_value' => 1,
+    '#description' => t('Permit only integer values as input. e.g. 12.34 would be invalid.'),
+    '#weight' => 1.5,
+    '#default_value' => $component['extra']['integer'],
+    '#parents' => array('extra', 'integer'),
+  );
+  $form['validation']['min'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Minimum'),
+    '#default_value' => $component['extra']['min'],
+    '#description' => t('Minimum numeric value. e.g. 0 would ensure positive numbers.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#weight' => 2.1,
+    '#parents' => array('extra', 'min'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  $form['validation']['max'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Maximum'),
+    '#default_value' => $component['extra']['max'],
+    '#description' => t('Maximum numeric value. This may also determine the display width of your field.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#weight' => 2.2,
+    '#parents' => array('extra', 'max'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  $form['validation']['step'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Step'),
+    '#default_value' => $component['extra']['step'],
+    '#description' => t('Limit options to a specific increment. e.g. a step of "5" would allow values 5, 10, 15, etc.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#weight' => 3,
+    '#parents' => array('extra', 'step'),
+    '#element_validate' => array('_webform_edit_number_validate'),
+  );
+  // Analysis settings.
+  $form['analysis'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Analysis'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => 10,
+  );
+  $form['analysis']['excludezero'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Exclude zero'),
+    '#return_value' => 1,
+    '#description' => t('Exclude entries of zero (or blank) when counting submissions to calculate average and standard deviation.'),
+    '#weight' => 1.5,
+    '#default_value' => $component['extra']['excludezero'],
+    '#parents' => array('extra', 'excludezero'),
+  );
+  return $form;
+}
+
+/**
+ * Theme function to render a number component.
+ */
+function theme_webform_number($variables) {
+  $element = $variables['element'];
+
+  // This IF statement is mostly in place to allow our tests to set type="text"
+  // because SimpleTest does not support type="number".
+  if (!isset($element['#attributes']['type'])) {
+    $element['#attributes']['type'] = 'number';
+  }
+
+  // Step property *must* be a full number with 0 prefix if a decimal.
+  if (!empty($element['#step']) && !is_int($element['#step'] * 1)) {
+    $decimals = strlen($element['#step']) - strrpos($element['#step'], '.') - 1;
+    $element['#step'] = sprintf('%1.' . $decimals . 'F', $element['#step']);
+  }
+
+  // If the number is not an integer and step is undefined/empty, set the "any"
+  // value to allow any decimal.
+  if (empty($element['#integer']) && empty($element['#step'])) {
+    $element['#step'] = 'any';
+  }
+  elseif ($element['#integer'] && empty($element['#step'])) {
+    $element['#step'] = 1;
+  }
+
+  // Convert properties to attributes on the element if set.
+  foreach (array('id', 'name', 'value', 'size', 'min', 'max', 'step') as $property) {
+    if (isset($element['#' . $property]) && $element['#' . $property] !== '') {
+      $element['#attributes'][$property] = $element['#' . $property];
+    }
+  }
+  _form_set_class($element, array('form-text', 'form-number'));
+
+  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_number($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value'],
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#field_prefix' => empty($component['extra']['field_prefix']) ? NULL : ($filter ? _webform_filter_xss($component['extra']['field_prefix']) : $component['extra']['field_prefix']),
+    '#field_suffix' => empty($component['extra']['field_suffix']) ? NULL : ($filter ? _webform_filter_xss($component['extra']['field_suffix']) : $component['extra']['field_suffix']),
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#attributes' => $component['extra']['attributes'],
+    '#element_validate' => array('_webform_validate_number'),
+    '#theme_wrappers' => array('webform_element'),
+    '#min' => $component['extra']['min'],
+    '#max' => $component['extra']['max'],
+    '#step' => $component['extra']['step'] ? abs($component['extra']['step']) : '',
+    '#integer' => $component['extra']['integer'],
+    '#translatable' => array('title', 'description'),
+  );
+
+  // Flip the min and max properties to make min less than max if needed.
+  if ($element['#min'] !== '' && $element['#max'] !== '' && $element['#min'] > $element['#max']) {
+    $max = $element['#min'];
+    $element['#min'] = $element['#max'];
+    $element['#max'] = $max;
+  }
+
+  // Ensure #step starts with a zero if a decimal.
+  if (!is_int($element['#step'] * 1)) {
+    $decimals = strlen($element['#step']) - strrpos($element['#step'], '.') - 1;
+    $element['#step'] = sprintf('%1.' . $decimals . 'F', $element['#step']);
+  }
+
+  if ($component['extra']['type'] == 'textfield') {
+    // Render as textfield.
+    $element['#type'] = 'webform_number';
+
+    // Set the size property based on #max, to ensure consistent behavior for
+    // browsers that do not support type = number.
+    if ($element['#max']) {
+      $element['#size'] =  strlen($element['#max']) + 1;
+    }
+  }
+  else {
+    // Render as select.
+    $element['#type'] = 'select';
+
+    // Create user-specified options list as an array.
+    $element['#options'] = _webform_number_select_options($component);
+
+    // Add default options if using a select list with no default. This trigger's
+    // Drupal 7's adding of the option for us. See form_process_select().
+    if ($component['extra']['type'] == 'select' && $element['#default_value'] === '') {
+      $element['#empty_value'] = '';
+    }
+  }
+
+  // Set user-entered values.
+  if (isset($value[0])) {
+    $element['#default_value'] = $value[0];
+  }
+
+  // Enforce uniqueness.
+  if ($component['extra']['unique']) {
+    $element['#element_validate'][] = 'webform_validate_unique';
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_number($component, $value, $format = 'html') {
+  $empty = !isset($value[0]) || $value[0] === '';
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_number',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#field_prefix' => $empty ? '' : $component['extra']['field_prefix'],
+    '#field_suffix' => $empty ? '' : $component['extra']['field_suffix'],
+    '#format' => $format,
+    '#value' => $empty ? '' : _webform_number_format($component, $value[0]),
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the output of data for this component.
+ */
+function theme_webform_display_number($variables) {
+  $element = $variables['element'];
+  $prefix = $element['#format'] == 'html' ? filter_xss($element['#field_prefix']) : $element['#field_prefix'];
+  $suffix = $element['#format'] == 'html' ? filter_xss($element['#field_suffix']) : $element['#field_suffix'];
+  $value = $element['#format'] == 'html' ? check_plain($element['#value']) : $element['#value'];
+  return $value !== '' ? ($prefix . $value . $suffix) : ' ';
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_number($component, $sids = array(), $single = FALSE) {
+  $advanced_stats = $single;
+
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $population = array();
+  $submissions = 0;
+  $nonzero = 0;
+  $not_empty = 0;
+  $sum = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    $value = trim($data['data']);
+    if ($value == '') {
+      $number = 0.0;
+    }
+    else {
+      $number = $value * 1.0;
+    }
+
+    if ($value !== '') {
+      $not_empty++;
+    }
+
+    if ($number > 0) {
+      $nonzero++;
+      $sum += $number;
+    }
+    $population[] = $number;
+    $submissions++;
+  }
+  sort($population, SORT_NUMERIC);
+
+  // Average and population count.
+  if ($component['extra']['excludezero']) {
+    $average = $nonzero ? ($sum / $nonzero) : 0;
+    $average_title = t('Average !mu excluding zeros/blanks', array('!mu' => $advanced_stats ? '(&mu;)' : ''));
+    // Sample (sub-set of total population).
+    $population_count = $nonzero - 1;
+    $sigma = 'sd';
+    $description = t('sample');
+  }
+  else {
+    $average = $submissions ? ($sum / $submissions) : 0;
+    $average_title = t('Average !mu including zeros/blanks', array('!mu' => $advanced_stats ? '(&mu;)' : ''));
+    // Population.
+    $population_count = $submissions;
+    $sigma = '&sigma;';
+    $description = t('population');
+  }
+
+  // Formatting.
+  $average = _webform_number_format($component, $average);
+  $sum = _webform_number_format($component, $sum);
+
+  $rows[0] = array(t('Zero/blank'), ($submissions - $nonzero));
+  $rows[1] = array(t('User entered value'), $not_empty);
+  $rows[2] = array(t('Sum') . ($advanced_stats ? ' (&Sigma;)' : ''), $sum);
+  $rows[3] = array($average_title, $average);
+
+  if (!$advanced_stats && $sum != 0) {
+    $rows[4] = array('', l(t('More stats »'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']));
+  }
+
+  // Normal distribution information.
+  if ($advanced_stats && $population_count && $sum != 0) {
+    // Standard deviation.
+    $stddev = 0;
+    foreach($population as $value) {
+      // Obtain the total of squared variances.
+      $stddev += pow(($value - $average), 2);
+    }
+    if ($population_count > 0) {
+      $stddev = sqrt($stddev / $population_count);
+    }
+    else {
+      $stddev = sqrt($stddev);
+    }
+
+    // Build normal distribution table rows.
+    $count = array();
+    $percent = array();
+    $limit = array();
+    $index = 0;
+
+    $count[] = 0;
+    $limit[] = $average - ($stddev * 4);
+    foreach ($population as $value) {
+      while ($value >= $limit[$index]) {
+        $percent[] = number_format($count[$index] / $population_count * 100, 2, '.', '');
+        $limit[] = $limit[$index] + $stddev;
+        $index += 1;
+        if ($limit[$index] == $average) {
+          $limit[$index] = $limit[$index] + $stddev;
+        }
+        $count[$index] = 0;
+      }
+      $count[$index] += 1;
+    }
+    $percent[] = number_format($count[$index] / $population_count * 100, 2, '.', '');
+
+    // Format normal distribution table output.
+    $stddev = _webform_number_format($component, $stddev);
+    $low = _webform_number_format($component, $population[0]);
+    $high = _webform_number_format($component, end($population));
+    foreach($limit as $key => $value) {
+      $limit[$key] = _webform_number_format($component, $value);
+    }
+
+    // Column headings (override potential theme uppercase, e.g. Seven in D7).
+    $header = array(
+      t('Normal Distribution'),
+      array('data' => '-4' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '-3' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '-2' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '-1' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '+1' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '+2' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '+3' . $sigma, 'style' => 'text-transform: lowercase;',),
+      array('data' => '+4' . $sigma, 'style' => 'text-transform: lowercase;',),
+    );
+
+    // Insert row labels.
+    array_unshift($limit, t('Boundary'));
+    array_unshift($count, t('Count'));
+    array_unshift($percent, t('% of !description', array('!description' => $description)));
+
+    $output = theme('table', array('header' => $header, 'rows' => array($limit, $count, $percent)));
+
+    $rows[4] = array(t('Range'), t('!low to !high', array('!low' => $low, '!high' => $high)));
+    $rows[5] = array(t('Standard deviation (!sigma)', array('!sigma' => $sigma)), $stddev);
+    $rows[6] = array(array('data' => $output, 'colspan' => 2));
+  }
+
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_number($component, $value) {
+  return isset($value[0]) ? _webform_number_format($component, $value[0]) : '';
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_number($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_number($component, $export_options, $value) {
+  if (isset($value[0]) && is_numeric($value[0]) && $component['extra']['decimals'] !== '') {
+    $value[0] = number_format($value[0], $component['extra']['decimals'], '.', '');
+  }
+  return isset($value[0]) ? $value[0] : '';
+}
+
+/**
+ * A Drupal Form API Validation function. Validates the entered values from
+ * number components on the client-side form.
+ *
+ * @param $element
+ *   The form element. May either be a select or a webform_number element.
+ * @param $form_state
+ *   The full form state for the webform.
+ * @return
+ *   None. Calls a form_set_error if the number is not valid.
+ */
+function _webform_validate_number($element, &$form_state) {
+  $value = trim($element['#value']);
+  form_set_value($element, $value, $form_state);
+
+  if ($value != '') {
+    // Numeric test.
+    if (is_numeric($value)) {
+      // Range test.
+      if ($element['#min'] != '' && $element['#max'] != '') {
+        // Flip minimum and maximum if needed.
+        if ($element['#max'] > $element['#min']) {
+          $min = $element['#min'];
+          $max = $element['#max'];
+        }
+        else {
+          $min = $element['#max'];
+          $max = $element['#min'];
+        }
+        if ($value > $max || $value < $min) {
+          form_error($element, t('%name field value of @value should be in the range @min to @max.', array('%name' => $element['#title'], '@value' => $value, '@min' => $min, '@max' => $max)));
+        }
+      }
+      elseif ($element['#max'] != '' && $value > $element['#max']) {
+        form_error($element, t('%name field value must be less than @max.', array('%name' => $element['#title'], '@max' => $element['#max'])));
+      }
+      elseif ($element['#min'] != '' && $value < $element['#min']) {
+        form_error($element, t('%name field value must be greater than @min.', array('%name' => $element['#title'], '@min' => $element['#min'])));
+      }
+
+      // Integer test.
+      if ($element['#integer'] && !is_int($value * 1)) {
+        form_error($element, t('%name field value of @value must be an integer.', array('%name' => $element['#title'], '@value' => $value)));
+      }
+
+      // Step test.
+      $starting_number = $element['#min'] ? $element['#min'] : 0;
+      if ($element['#step'] != 0 && fmod($element['#value'] - $starting_number, $element['#step']) != 0) {
+        $samples = array(
+          $starting_number,
+          $starting_number + ($element['#step'] * 1),
+          $starting_number + ($element['#step'] * 2),
+          $starting_number + ($element['#step'] * 3),
+        );
+        if ($starting_number) {
+          form_error($element, t('%name field value must be @start plus a multiple of @step. i.e. @samples, etc.', array('%name' => $element['#title'], '@start' => $element['#min'], '@step' => $element['#step'], '@samples' => implode(', ', $samples))));
+        }
+        else {
+          form_error($element, t('%name field value must be a multiple of @step. i.e. @samples, etc.', array('%name' => $element['#title'], '@step' => $element['#step'], '@samples' => implode(', ', $samples))));
+        }
+      }
+    }
+    else {
+      form_error($element, t('%name field value of @value must be numeric.', array('%name' => $element['#title'], '@value' => $value)));
+    }
+  }
+
+}
+
+/**
+ * Validation of number edit form items.
+ */
+function _webform_edit_number_validate($element, &$form_state) {
+  // Find the value of all related fields to this element.
+  $parents = $element['#parents'];
+  $key = array_pop($parents);
+  $values = $form_state['values'];
+  foreach ($parents as $parent) {
+    $values = $values[$parent];
+  }
+
+  switch ($key) {
+    case 'min':
+      if ($values['min'] == '') {
+        if (isset($values['type']) && $values['type'] === 'select') {
+          form_error($element, t('Minimum is required when using a select list element.'));
+        }
+      }
+      else {
+        if (!is_numeric($values['min'])) {
+          form_error($element, t('Minimum must be numeric.'));
+        }
+        if ($values['integer'] && !is_int($values['min'] * 1)) {
+          form_error($element, t('Minimum must have an integer value.'));
+        }
+      }
+      break;
+    case 'max':
+      if ($values['max'] == '') {
+        if (isset($values['type']) && $values['type'] === 'select') {
+          form_error($element, t('Maximum is required when using a select list element.'));
+        }
+      }
+      else {
+        if (!is_numeric($values['max'])) {
+          form_error($element, t('Maximum must be numeric.'));
+        }
+        if ($values['integer'] && !is_int($values['max'] * 1)) {
+          form_error($element, t('Maximum must have an integer value.'));
+        }
+      }
+      break;
+    case 'step':
+      if ($values['step'] !== '') {
+        if (!is_numeric($values['step'])) {
+          form_error($element, t('Step must be numeric.'));
+        }
+        else {
+          if ($values['integer'] && !is_int($values['step'] * 1)) {
+            form_error($element, t('Step must have an integer value.'));
+          }
+        }
+      }
+      break;
+  }
+  return TRUE;
+}
+
+/**
+ * Generate select list options.
+ */
+function _webform_number_select_options($component) {
+  $options = array();
+  $step = abs($component['extra']['step']);
+
+  // Step is optional and defaults to 1.
+  $step = empty($step) ? 1 : $step;
+
+  // Generate list in correct direction.
+  $min = $component['extra']['min'];
+  $max = $component['extra']['max'];
+  $flipped = FALSE;
+  if ($max < $min) {
+    $min = $component['extra']['max'];
+    $max = $component['extra']['min'];
+    $flipped = TRUE;
+  }
+
+  for ($f = $min; $f <= $max; $f += $step) {
+    $options[$f . ''] = $f . '';
+  }
+
+  // TODO: HTML5 browsers apparently do not include the max value if it does
+  // not line up with step. Restore this if needed in the future.
+  // Add end limit if it's been skipped due to step.
+  //if (end($options) != $max) {
+  //  $options[$f] = $max;
+  //}
+
+  if ($flipped) {
+    $options = array_reverse($options, TRUE);
+  }
+
+  // Apply requisite number formatting.
+  foreach ($options as $key => $value) {
+    $options[$key] = _webform_number_format($component, $value);
+  }
+
+  return $options;
+}
+
+/**
+ * Apply number format.
+ */
+function _webform_number_format($component, $value) {
+  if (!is_numeric($value)) {
+    return '';
+  }
+  // If no decimal places are specified, do a best guess length of decimals.
+  $decimals = $component['extra']['decimals'];
+  if ($decimals === '') {
+    // If it's an integer, no decimals needed.
+    if (is_int(($value . '') * 1)) {
+      $decimals = 0;
+    }
+    else {
+      $decimals = strlen($value) - strrpos($value, '.') - 1;
+    }
+    if ($decimals > 4) {
+      $decimals = 4;
+    }
+  }
+
+  return number_format($value, $decimals, $component['extra']['point'], $component['extra']['separator']);
+}
diff -Naurw webform_old/components/pagebreak.inc webform/components/pagebreak.inc
--- webform_old/components/pagebreak.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/pagebreak.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Webform module page break component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_pagebreak() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'extra' => array(
+      'private' => FALSE,
+      'next_page_label' => '',
+      'prev_page_label' => '',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_pagebreak() {
+  return array(
+    'webform_display_pagebreak' => array(
+      'render element' => 'element',
+      'file' => 'components/pagebreak.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_pagebreak($component) {
+  $form = array();
+  // Force the parent to always be root.
+  $form['position']['pid'] = array(
+    '#type' => 'hidden',
+    '#value' => '0',
+  );
+
+  $form['display'] = array('#type' => 'markup'); // Hide the display options.
+
+  $form['extra']['next_page_label'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Next page button label'),
+    '#description' => t('This is used for the <em>Next Page</em> button on the page before this page break. Default: <em>Next Page &gt;</em>'),
+    '#default_value' => $component['extra']['next_page_label'],
+    '#size' => 30,
+  );
+  $form['extra']['prev_page_label'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Prev page button label'),
+    '#description' => t('This is used for the <em>Prev Page</em> button on the page after this page break. Default: <em>&lt; Prev Page</em>'),
+    '#default_value' => $component['extra']['prev_page_label'],
+    '#size' => 30,
+  );
+
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_pagebreak($component, $value = NULL, $filter = TRUE) {
+  $element = array(
+    '#type' => 'hidden',
+    '#value' => $component['name'],
+    '#weight' => $component['weight'],
+  );
+  return $element;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_display_pagebreak($component, $value = NULL, $format = 'html') {
+  $element = array(
+    '#theme' => 'webform_display_pagebreak',
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#format' => $format,
+    '#translatable' => array('title'),
+  );
+  return $element;
+}
+
+/**
+ * Format the text output data for this component.
+ */
+function theme_webform_display_pagebreak($variables) {
+  $element = $variables['element'];
+
+  return $element['#format'] == 'html' ? '<h2 class="webform-page">' . check_plain($element['#title']) . '</h2>' : "--" . $element['#title'] . "--\n";
+}
diff -Naurw webform_old/components/select.inc webform/components/select.inc
--- webform_old/components/select.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/select.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,981 @@
+<?php
+
+/**
+ * @file
+ * Webform module multiple select component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_select() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'mandatory' => 0,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'extra' => array(
+      'items' => '',
+      'multiple' => NULL,
+      'aslist' => NULL,
+      'optrand' => 0,
+      'other_option' => NULL,
+      'other_text' => t('Other...'),
+      'title_display' => 0,
+      'description' => '',
+      'custom_keys' => FALSE,
+      'options_source' => '',
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_select() {
+  return array(
+    'webform_display_select' => array(
+      'render element' => 'element',
+      'file' => 'components/select.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_select($component) {
+  $form = array(
+    '#attached' => array(
+      'js' => array(
+        drupal_get_path('module', 'webform') . '/js/select-admin.js' => array('preprocess' => FALSE),
+        array('data' => array('webform' => array('selectOptionsUrl' => url('webform/ajax/options/' . $component['nid']))), 'type' => 'setting'),
+      ),
+    ),
+  );
+
+  $other = array();
+  if ($info = _webform_select_options_info()) {
+    $options = array('' => t('None'));
+    foreach ($info as $name => $source) {
+      $options[$name] = $source['title'];
+    }
+
+    $other['options_source'] = array(
+      '#title' => t('Load a pre-built option list'),
+      '#type' => 'select',
+      '#options' => $options,
+      '#default_value' => $component['extra']['options_source'],
+      '#weight' => 1,
+      '#description' => t('Use a pre-built list of options rather than entering options manually. Options will not be editable if using pre-built list.'),
+      '#parents' => array('extra', 'options_source'),
+      '#weight' => 5,
+    );
+  }
+
+  if (module_exists('select_or_other')) {
+    $other['other_option'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Allow "Other..." option'),
+      '#default_value' => $component['extra']['other_option'],
+      '#description' => t('Check this option if you want to allow users to enter an option not on the list.'),
+      '#parents' => array('extra', 'other_option'),
+      '#weight' => 2,
+    );
+    $other['other_text'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Text for "Other..." option'),
+      '#default_value' => $component['extra']['other_text'],
+      '#description' => t('If allowing other options, enter text to be used for other-enabling option.'),
+      '#parents' => array('extra', 'other_text'),
+      '#weight' => 3,
+    );
+  }
+
+  if (module_exists('options_element')) {
+    $options = _webform_select_options($component, FALSE, FALSE);
+
+    $form['items'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Options'),
+      '#collapsible' => TRUE,
+      '#attributes' => array('class' => array('webform-options-element')),
+      '#element_validate' => array('_webform_edit_validate_options'),
+      '#weight' => 2,
+    );
+
+    $form['items']['options'] = array(
+      '#type' => 'options',
+      '#limit' => 500,
+      '#optgroups' => $component['extra']['aslist'],
+      '#multiple' => $component['extra']['multiple'],
+      '#multiple_toggle' => t('Multiple'),
+      '#default_value' => $component['value'],
+      '#options' => $options,
+      '#options_readonly' => !empty($component['extra']['options_source']),
+      '#key_type' => 'mixed',
+      '#key_type_toggle' => t('Customize keys (Advanced)'),
+      '#key_type_toggled' => $component['extra']['custom_keys'],
+      '#default_value_pattern' => '^%.+\[.+\]$',
+      '#weight' => 1,
+    );
+
+    $form['items']['options']['option_settings'] = $other;
+  }
+  else {
+    $form['extra']['items'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Options'),
+      '#default_value' => $component['extra']['items'],
+      '#description' => t('<strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Use of only alphanumeric characters and underscores is recommended in keys. One option per line. Option groups may be specified with &lt;Group Name&gt;. &lt;&gt; can be used to insert items at the root of the menu after specifying a group.') . theme('webform_token_help'),
+      '#cols' => 60,
+      '#rows' => 5,
+      '#weight' => 0,
+      '#required' => TRUE,
+      '#wysiwyg' => FALSE,
+      '#element_validate' => array('_webform_edit_validate_select'),
+    );
+
+    if (!empty($component['extra']['options_source'])) {
+      $form['extra']['items']['#attributes'] = array('readonly' => 'readonly');
+    }
+
+    $form['extra'] = array_merge($form['extra'], $other);
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Default value'),
+      '#default_value' => $component['value'],
+      '#description' => t('The default value of the field identified by its key. For multiple selects use commas to separate multiple defaults.') . theme('webform_token_help'),
+      '#size' => 60,
+      '#maxlength' => 1024,
+      '#weight' => 0,
+    );
+    $form['extra']['multiple'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Multiple'),
+      '#default_value' => $component['extra']['multiple'],
+      '#description' => t('Check this option if the user should be allowed to choose multiple values.'),
+      '#weight' => 0,
+    );
+  }
+
+  $form['display']['aslist'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Listbox'),
+    '#default_value' => $component['extra']['aslist'],
+    '#description' => t('Check this option if you want the select component to be displayed as a select list box instead of radio buttons or checkboxes. Option groups (nested options) are only supported with listbox components.'),
+    '#parents' => array('extra', 'aslist'),
+  );
+  $form['display']['optrand'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Randomize options'),
+    '#default_value' => $component['extra']['optrand'],
+    '#description' => t('Randomizes the order of the options when they are displayed in the form.'),
+    '#parents' => array('extra', 'optrand'),
+  );
+
+  return $form;
+}
+
+/**
+ * Element validation callback. Ensure keys are not duplicated.
+ */
+function _webform_edit_validate_select($element, &$form_state) {
+  // Check for duplicate key values to prevent unexpected data loss. Require
+  // all options to include a safe_key.
+  if (!empty($element['#value'])) {
+    $lines = explode("\n", trim($element['#value']));
+    $existing_keys = array();
+    $duplicate_keys = array();
+    $missing_keys = array();
+    $long_keys = array();
+    $group = '';
+    foreach ($lines as $line) {
+      $matches = array();
+      $line = trim($line);
+      if (preg_match('/^\<([^>]*)\>$/', $line, $matches)) {
+        $group = $matches[1];
+        $key = NULL; // No need to store group names.
+      }
+      elseif (preg_match('/^([^|]*)\|(.*)$/', $line, $matches)) {
+        $key = $matches[1];
+        if (strlen($key) > 128) {
+          $long_keys[] = $key;
+        }
+      }
+      else {
+        $missing_keys[] = $line;
+      }
+
+      if (isset($key)) {
+        if (isset($existing_keys[$group][$key])) {
+          $duplicate_keys[$key] = $key;
+        }
+        else {
+          $existing_keys[$group][$key] = $key;
+        }
+      }
+    }
+
+    if (!empty($missing_keys)) {
+      form_error($element, t('Every option must have a key specified. Specify each option as "safe_key|Some readable option".'));
+    }
+
+    if (!empty($long_keys)) {
+      form_error($element, t('Option keys must be less than 128 characters. The following keys exceed this limit:') . theme('item_list', $long_keys));
+    }
+
+    if (!empty($duplicate_keys)) {
+      form_error($element, t('Options within the select list must be unique. The following keys have been used multiple times:') . theme('item_list', array('items' => $duplicate_keys)));
+    }
+
+    // Set the listbox option if needed.
+    if (empty($missing_keys) && empty($long_keys) && empty($duplicate_keys)) {
+      $options = _webform_select_options_from_text($element['#value']);
+      _webform_edit_validate_set_aslist($options, $form_state);
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+ * Set the appropriate webform values when using the options element module.
+ */
+function _webform_edit_validate_options($element, &$form_state) {
+  $key = end($element['#parents']);
+  $element_options = $form_state['values'][$key]['options'];
+  unset($form_state['values'][$key]);
+
+  $form_state['values']['extra'][$key] = form_options_to_text($element_options['options'], 'custom');
+
+  // Options saved for select components.
+  if ($key == 'items') {
+    $form_state['values']['extra']['multiple'] = $element_options['multiple'];
+    $form_state['values']['extra']['custom_keys'] = $element_options['custom_keys'];
+    $form_state['values']['value'] = is_array($element_options['default_value']) ? implode(', ', $element_options['default_value']) : $element_options['default_value'];
+
+    // Set the listbox option if needed.
+    _webform_edit_validate_set_aslist($element_options['options'], $form_state);
+  }
+  // Options saved for grid components.
+  else {
+    $form_state['values']['extra']['custom_' . rtrim($key, 's') . '_keys'] = $element_options['custom_keys'];
+  }
+}
+
+/**
+ * Ensure "aslist" is used for option groups. Called from options validations.
+ */
+function _webform_edit_validate_set_aslist($options, &$form_state) {
+  if (empty($form_state['values']['extra']['aslist']) && !empty($options)) {
+    foreach ($options as $option) {
+      if (is_array($option)) {
+        $form_state['values']['extra']['aslist'] = 1;
+        drupal_set_message(t('The component %name has automatically been set to display as a listbox in order to support option groups.', array('%name' => $form_state['values']['name'])), 'warning');
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_select($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#theme_wrappers' => array('webform_element'),
+    '#pre_render' => array(), // Needed to disable double-wrapping of radios and checkboxes.
+    '#translatable' => array('title', 'description', 'options'),
+  );
+
+  // Convert the user-entered options list into an array.
+  $default_value = $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value'];
+  $options = _webform_select_options($component, !$component['extra']['aslist'], $filter);
+
+  if ($component['extra']['optrand']) {
+    _webform_shuffle_options($options);
+  }
+
+  // Add default options if using a select list with no default. This trigger's
+  // Drupal 7's adding of the option for us. See @form_process_select().
+  if ($component['extra']['aslist'] && !$component['extra']['multiple'] && $default_value === '') {
+    $element['#empty_value'] = '';
+  }
+
+  // Set the component options.
+  $element['#options'] = $options;
+
+  // Set the default value.
+  if (isset($value)) {
+    if ($component['extra']['multiple']) {
+      // Set the value as an array.
+      $element['#default_value'] = array();
+      foreach ((array) $value as $key => $option_value) {
+        $element['#default_value'][] = $option_value;
+      }
+    }
+    else {
+      // Set the value as a single string.
+      $element['#default_value'] = '';
+      foreach ((array) $value as $option_value) {
+        $element['#default_value'] = $option_value;
+      }
+    }
+  }
+  elseif ($default_value !== '') {
+    // Convert default value to a list if necessary.
+    if ($component['extra']['multiple']) {
+      $varray = explode(',', $default_value);
+      foreach ($varray as $key => $v) {
+        $v = trim($v);
+        if ($v !== '') {
+          $element['#default_value'][] = $v;
+        }
+      }
+    }
+    else {
+      $element['#default_value'] = $default_value;
+    }
+  }
+  elseif ($component['extra']['multiple']) {
+    $element['#default_value'] = array();
+  }
+
+  if ($component['extra']['other_option'] && module_exists('select_or_other')) {
+    // Set display as a select_or_other element:
+    $element['#type'] = 'select_or_other';
+    $element['#other'] = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
+    $element['#other_title'] = $element['#title'] . ' ' . $element['#other'];
+    $element['#other_title_display'] = 'invisible';
+    $element['#other_unknown_defaults'] = 'other';
+    $element['#other_delimiter'] = ', ';
+    // Merge in Webform's #process function for Select or other.
+    $element['#process'] = array_merge(element_info_property('select_or_other', '#process'), array('webform_expand_select_or_other'));
+
+    if ($component['extra']['multiple']) {
+      $element['#multiple'] = TRUE;
+      $element['#select_type'] = 'checkboxes';
+    }
+    else {
+      $element['#multiple'] = FALSE;
+      $element['#select_type'] = 'radios';
+    }
+    if ($component['extra']['aslist']) {
+      $element['#select_type'] = 'select';
+    }
+  }
+  elseif ($component['extra']['aslist']) {
+    // Set display as a select list:
+    $element['#type'] = 'select';
+    if ($component['extra']['multiple']) {
+      $element['#size'] = 4;
+      $element['#multiple'] = TRUE;
+    }
+  }
+  else {
+    if ($component['extra']['multiple']) {
+      // Set display as a checkbox set.
+      $element['#type'] = 'checkboxes';
+      $element['#theme_wrappers'] = array_merge(array('checkboxes'), $element['#theme_wrappers']);
+      $element['#process'] = array_merge(element_info_property('checkboxes', '#process'), array('webform_expand_select_ids'));
+
+      // Entirely replace the normal expand checkboxes with our custom version.
+      // This helps render checkboxes in multipage forms.
+      $process_key = array_search('form_process_checkboxes', $element['#process']);
+      $element['#process'][$process_key] = 'webform_expand_checkboxes';
+    }
+    else {
+      // Set display as a radio set.
+      $element['#type'] = 'radios';
+      $element['#theme_wrappers'] = array_merge(array('radios'), $element['#theme_wrappers']);
+      $element['#process'] = array_merge(element_info_property('radios', '#process'), array('webform_expand_select_ids'));
+    }
+  }
+
+  return $element;
+}
+
+/**
+ * Process function to ensure select_or_other elements validate properly.
+ */
+function webform_expand_select_or_other($element) {
+  // Disable validation for back-button and save draft.
+  $element['select']['#validated'] = TRUE;
+  $element['select']['#webform_validated'] = FALSE;
+
+  $element['other']['#validated'] = TRUE;
+  $element['other']['#webform_validated'] = FALSE;
+
+  // The Drupal FAPI does not support #title_display inline so we need to move
+  // to a supported value here to be compatible with select_or_other.
+  $element['select']['#title_display'] = $element['#title_display'] === 'inline' ? 'before' : $element['#title_display'];
+
+  // If the default value contains "select_or_other" (the key of the select
+  // element for the "other..." choice), discard it and set the "other" value.
+  if (is_array($element['#default_value']) && in_array('select_or_other', $element['#default_value'])) {
+    $key = array_search('select_or_other', $element['#default_value']);
+    unset($element['#default_value'][$key]);
+    $element['#default_value'] = array_values($element['#default_value']);
+    $element['other']['#default_value'] = implode(', ', $element['#default_value']);
+  }
+
+  // Sanitize the options in Select or other check boxes and radio buttons.
+  if ($element['#select_type'] == 'checkboxes' || $element['#select_type'] == 'radios') {
+    $element['select']['#process'] = array_merge(element_info_property($element['#select_type'], '#process'), array('webform_expand_select_ids'));
+  }
+
+  return $element;
+}
+
+/**
+ * Drupal 6 hack that properly *renders* checkboxes in multistep forms. This is
+ * different than the value hack needed in Drupal 5, which is no longer needed.
+ */
+function webform_expand_checkboxes($element) {
+  // Elements that have a value set are already in the form structure cause
+  // them not to be written when the expand_checkboxes function is called.
+  $default_value = array();
+  foreach (element_children($element) as $key) {
+    if (isset($element[$key]['#default_value'])) {
+      $default_value[$key] = $element[$key]['#default_value'];
+      unset($element[$key]);
+    }
+  }
+
+  $element = form_process_checkboxes($element);
+
+  // Escape the values of checkboxes.
+  foreach (element_children($element) as $key) {
+    $element[$key]['#return_value'] = check_plain($element[$key]['#return_value']);
+    $element[$key]['#name'] = $element['#name'] . '[' . $element[$key]['#return_value'] . ']';
+  }
+
+  foreach ($default_value as $key => $val) {
+    $element[$key]['#default_value'] = $val;
+  }
+  return $element;
+}
+
+/**
+ * FAPI process function to rename IDs attached to checkboxes and radios.
+ */
+function webform_expand_select_ids($element) {
+  $id = $element['#id'] = str_replace('_', '-', _webform_safe_name(strip_tags($element['#id'])));
+  $delta = 0;
+  foreach (element_children($element) as $key) {
+    $delta++;
+    // Convert the #id for each child to a safe name, regardless of key.
+    $element[$key]['#id'] = $id . '-' . $delta;
+
+    // Prevent scripts or CSS in the labels for each checkbox or radio.
+    $element[$key]['#title'] = _webform_filter_xss($element[$key]['#title']);
+  }
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_select($component, $value, $format = 'html') {
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#multiple' => $component['extra']['multiple'],
+    '#theme' => 'webform_display_select',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#format' => $format,
+    '#options' => _webform_select_options($component, !$component['extra']['aslist']),
+    '#value' => (array) $value,
+    '#translatable' => array('title', 'options'),
+  );
+}
+
+/**
+ * Implements _webform_submit_component().
+ *
+ * Convert FAPI 0/1 values into something saveable.
+ */
+function _webform_submit_select($component, $value) {
+  // Build a list of all valid keys expected to be submitted.
+  $options = _webform_select_options($component, TRUE);
+
+  $return = NULL;
+  if (is_array($value)) {
+    $return = array();
+    foreach ($value as $key => $option_value) {
+      // Handle options that are specified options.
+      if ($option_value !== '' && isset($options[$option_value])) {
+        // Checkboxes submit an integer value of 0 when unchecked. A checkbox
+        // with a value of '0' is valid, so we can't use empty() here.
+        if ($option_value === 0 && !$component['extra']['aslist'] && $component['extra']['multiple']) {
+          unset($value[$option_value]);
+        }
+        else {
+          $return[] = $option_value;
+        }
+      }
+      // Handle options that are added through the "other" field. Specifically
+      // exclude the "select_or_other" value, which is added by the select list.
+      elseif ($component['extra']['other_option'] && module_exists('select_or_other') && $option_value != 'select_or_other') {
+        $return[] = $option_value;
+      }
+    }
+  }
+  elseif (is_string($value)) {
+    $return = $value;
+  }
+
+  return $return;
+}
+
+/**
+ * Format the text output for this component.
+ */
+function theme_webform_display_select($variables) {
+  $element = $variables['element'];
+
+  // Flatten the list of options so we can get values easily. These options
+  // may be translated by hook_webform_display_component_alter().
+  $options = array();
+  foreach ($element['#options'] as $key => $value) {
+    if (is_array($value)) {
+      foreach ($value as $subkey => $subvalue) {
+        $options[$subkey] = $subvalue;
+      }
+    }
+    else {
+      $options[$key] = $value;
+    }
+  }
+
+  $items = array();
+  if ($element['#multiple']) {
+    foreach ((array) $element['#value'] as $option_value) {
+      if ($option_value !== '') {
+        // Administer provided values.
+        if (isset($options[$option_value])) {
+          $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$option_value]) : $options[$option_value];
+        }
+        // User-specified in the "other" field.
+        else {
+          $items[] = $element['#format'] == 'html' ? check_plain($option_value) : $option_value;
+        }
+      }
+    }
+  }
+  else {
+    if (isset($element['#value'][0]) && $element['#value'][0] !== '') {
+      // Administer provided values.
+      if (isset($options[$element['#value'][0]])) {
+        $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$element['#value'][0]]) : $options[$element['#value'][0]];
+      }
+      // User-specified in the "other" field.
+      else {
+        $items[] = $element['#format'] == 'html' ? check_plain($element['#value'][0]) : $element['#value'][0];
+      }
+    }
+  }
+
+  if ($element['#format'] == 'html') {
+    $output = count($items) > 1 ? theme('item_list', array('items' => $items)) : (isset($items[0]) ? $items[0] : ' ');
+  }
+  else {
+    if (count($items) > 1) {
+      foreach ($items as $key => $item) {
+        $items[$key] = ' - ' . $item;
+      }
+      $output = implode("\n", $items);
+    }
+    else {
+      $output = isset($items[0]) ? $items[0] : ' ';
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_select($component, $sids = array(), $single = FALSE) {
+  $options = _webform_select_options($component, TRUE);
+  $show_other_results = $single;
+
+  $sid_placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array();
+  $sid_filter = count($sids) ? " AND sid IN (" . implode(",", $sid_placeholders) . ")" : "";
+
+  $option_operator = $show_other_results ? 'NOT IN' : 'IN';
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->condition('data', '', '<>')
+    ->condition('data', array_keys($options), $option_operator)
+    ->groupBy('data');
+  $query->addExpression('COUNT(data)', 'datacount');
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $count_query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->condition('data', '', '<>');
+  $count_query->addExpression('COUNT(*)', 'datacount');
+  if (count($sids)) {
+    $count_query->condition('sid', $sids, 'IN');
+  }
+
+  $result = $query->execute();
+  $rows = array();
+  $normal_count = 0;
+  foreach ($result as $data) {
+    $display_option = $single ? $data['data'] : $options[$data['data']];
+    $rows[$data['data']] = array(_webform_filter_xss($display_option), $data['datacount']);
+    $normal_count += $data['datacount'];
+  }
+
+  if (!$show_other_results) {
+    // Order the results according to the normal options array.
+    $ordered_rows = array();
+    foreach (array_intersect_key($options, $rows) as $key => $label) {
+      $ordered_rows[] = $rows[$key];
+    }
+
+    // Add a row for any unknown or user-entered values.
+    if ($component['extra']['other_option']) {
+      $full_count = $count_query->execute()->fetchField();
+      $other_count = $full_count - $normal_count;
+      $display_option = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
+      $other_text = $other_count ? $other_count . ' (' . l(t('view'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']) . ')' : $other_count;
+      $ordered_rows[] = array($display_option, $other_text);
+    }
+
+    $rows = $ordered_rows;
+  }
+
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_select($component, $value) {
+  // Convert submitted 'safe' values to un-edited, original form.
+  $options = _webform_select_options($component, TRUE);
+
+  $value = (array) $value;
+  $items = array();
+  // Set the value as a single string.
+  foreach ($value as $option_value) {
+    if ($option_value !== '') {
+      if (isset($options[$option_value])) {
+        $items[] = _webform_filter_xss($options[$option_value]);
+      }
+      else {
+        $items[] = check_plain($option_value);
+      }
+    }
+  }
+
+  return implode('<br />', $items);
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_select($component, $export_options) {
+  $headers = array(
+    0 => array(),
+    1 => array(),
+    2 => array(),
+  );
+
+  if ($component['extra']['multiple'] && $export_options['select_format'] == 'separate') {
+    $headers[0][] = '';
+    $headers[1][] = $component['name'];
+    $items = _webform_select_options($component, TRUE, FALSE);
+    if ($component['extra']['other_option']) {
+      $other_label = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
+      $items[$other_label] = $other_label;
+    }
+    $count = 0;
+    foreach ($items as $key => $item) {
+      // Empty column per sub-field in main header.
+      if ($count != 0) {
+        $headers[0][] = '';
+        $headers[1][] = '';
+      }
+      if ($export_options['select_keys']) {
+        $headers[2][] = $key;
+      }
+      else {
+        $headers[2][] = $item;
+      }
+      $count++;
+    }
+  }
+  else {
+    $headers[0][] = '';
+    $headers[1][] = '';
+    $headers[2][] = $component['name'];
+  }
+  return $headers;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_select($component, $export_options, $value) {
+  $options = _webform_select_options($component, TRUE, FALSE);
+  $return = array();
+
+  if ($component['extra']['multiple']) {
+    foreach ($options as $key => $item) {
+      $index = array_search($key, (array) $value);
+      if ($index !== FALSE) {
+        if ($export_options['select_format'] == 'separate') {
+          $return[] = 'X';
+        }
+        else {
+          $return[] = $export_options['select_keys'] ? $key : $item;
+        }
+        unset($value[$index]);
+      }
+      elseif ($export_options['select_format'] == 'separate') {
+        $return[] = '';
+      }
+    }
+
+    // Any remaining items in the $value array will be user-added options.
+    if ($component['extra']['other_option']) {
+      $return[] = count($value) ? implode(',', $value) : '';
+    }
+  }
+  else {
+    $key = $value[0];
+    if ($export_options['select_keys']) {
+      $return = $key;
+    }
+    else {
+      $return = isset($options[$key]) ? $options[$key] : $key;
+    }
+  }
+
+  if ($component['extra']['multiple'] && $export_options['select_format'] == 'compact') {
+    $return = implode(',', (array) $return);
+  }
+
+  return $return;
+}
+
+/**
+ * Menu callback; Return a predefined list of select options as JSON.
+ */
+function webform_select_options_ajax($source_name = '') {
+  $info = _webform_select_options_info();
+
+  $component['extra']['options_source'] = $source_name;
+  if ($source_name && isset($info[$source_name])) {
+    $options = _webform_select_options_to_text(_webform_select_options($component, !$component['extra']['aslist'], FALSE));
+  }
+  else {
+    $options = '';
+  }
+
+  $return = array(
+    'elementId' => module_exists('options_element') ? 'edit-items-options-options-field-widget' : 'edit-extra-items',
+    'options' => $options,
+  );
+
+  drupal_json_output($return);
+}
+
+/**
+ * Generate a list of options for a select list.
+ */
+function _webform_select_options($component, $flat = FALSE, $filter = TRUE) {
+  if ($component['extra']['options_source']) {
+    $options = _webform_select_options_callback($component['extra']['options_source'], $component, $flat, $filter);
+  }
+  else {
+    $options = _webform_select_options_from_text($component['extra']['items'], $flat, $filter);
+  }
+
+  return isset($options) ? $options : array();
+}
+
+/**
+ * Load Webform select option info from 3rd party modules.
+ */
+function _webform_select_options_info() {
+  static $info;
+  if (!isset($info)) {
+    $info = array();
+
+    foreach (module_implements('webform_select_options_info') as $module) {
+      $additions = module_invoke($module, 'webform_select_options_info');
+      foreach ($additions as $key => $addition) {
+        $additions[$key]['module'] = $module;
+      }
+      $info = array_merge($info, $additions);
+    }
+    drupal_alter('webform_select_options_info', $info);
+  }
+  return $info;
+}
+
+/**
+ * Execute a select option callback.
+ *
+ * @param $name
+ *   The name of the options group.
+ * @param $component
+ *   The full Webform component.
+ * @param $flat
+ *   Whether the information returned should exclude any nested groups.
+ * @param $filter
+ *   Whether information returned should be sanitized. Defaults to TRUE.
+ */
+function _webform_select_options_callback($name, $component, $flat = FALSE, $filter = TRUE) {
+  $info = _webform_select_options_info();
+
+  // Include any necessary files.
+  if (isset($info[$name]['file'])) {
+    $pathinfo = pathinfo($info[$name]['file']);
+    $path = ($pathinfo['dirname'] ? $pathinfo['dirname'] . '/' : '') . basename($pathinfo['basename'], '.' . $pathinfo['extension']);
+    module_load_include($pathinfo['extension'], $info[$name]['module'], $path);
+  }
+
+  // Execute the callback function.
+  if (isset($info[$name]['options callback']) && function_exists($info[$name]['options callback'])) {
+    $function = $info[$name]['options callback'];
+
+    $arguments = array();
+    if (isset($info[$name]['options arguments'])) {
+      $arguments = $info[$name]['options arguments'];
+    }
+
+    return $function($component, $flat, $filter, $arguments);
+  }
+}
+
+/**
+ * Utility function to split user-entered values from new-line separated
+ * text into an array of options.
+ *
+ * @param $text
+ *   Text to be converted into a select option array.
+ * @param $flat
+ *   Optional. If specified, return the option array and exclude any optgroups.
+ * @param $filter
+ *   Optional. Whether or not to filter returned values.
+ */
+function _webform_select_options_from_text($text, $flat = FALSE, $filter = TRUE) {
+  static $option_cache = array();
+
+  // Keep each processed option block in an array indexed by the MD5 hash of
+  // the option text and the value of the $flat variable.
+  $md5 = md5($text);
+
+  // Check if this option block has been previously processed.
+  if (!isset($option_cache[$flat][$md5])) {
+    $options = array();
+    $rows = array_filter(explode("\n", trim($text)));
+    $group = NULL;
+    foreach ($rows as $option) {
+      $option = trim($option);
+      /**
+       * If the Key of the option is within < >, treat as an optgroup
+       *
+       * <Group 1>
+       *   creates an optgroup with the label "Group 1"
+       *
+       * <>
+       *   Unsets the current group, allowing items to be inserted at the root element.
+       */
+      if (preg_match('/^\<([^>]*)\>$/', $option, $matches)) {
+        if (empty($matches[1])) {
+          unset($group);
+        }
+        elseif (!$flat) {
+          $group = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
+        }
+      }
+      elseif (preg_match('/^([^|]+)\|(.*)$/', $option, $matches)) {
+        $key = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
+        $value = $filter ? _webform_filter_values($matches[2], NULL, NULL, NULL, FALSE) : $matches[2];
+        isset($group) ? $options[$group][$key] = $value : $options[$key] = $value;
+      }
+      else {
+        $filtered_option = $filter ? _webform_filter_values($option, NULL, NULL, NULL, FALSE) : $option;
+        isset($group) ? $options[$group][$filtered_option] = $filtered_option : $options[$filtered_option] = $filtered_option;
+      }
+    }
+
+    $option_cache[$flat][$md5] = $options;
+  }
+
+  // Return our options from the option_cache array.
+  return $option_cache[$flat][$md5];
+}
+
+/**
+ * Convert an array of options into text.
+ */
+function _webform_select_options_to_text($options) {
+  $output = '';
+  $previous_key = FALSE;
+
+  foreach ($options as $key => $value) {
+    // Convert groups.
+    if (is_array($value)) {
+      $output .= '<' . $key . '>' . "\n";
+      foreach ($value as $subkey => $subvalue) {
+        $output .= $subkey . '|' . $subvalue . "\n";
+      }
+      $previous_key = $key;
+    }
+    // Typical key|value pairs.
+    else {
+      // Exit out of any groups.
+      if (isset($options[$previous_key]) && is_array($options[$previous_key])) {
+        $output .= "<>\n";
+      }
+      // Skip empty rows.
+      if ($options[$key] !== '') {
+        $output .= $key . '|' . $value . "\n";
+      }
+      $previous_key = $key;
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Utility function to shuffle an array while preserving key-value pairs.
+ */
+function _webform_shuffle_options(&$array) {
+  // First shuffle the array keys, then use them as the basis for ordering
+  // the options.
+  $aux = array();
+  $keys = array_keys($array);
+  shuffle($keys);
+  foreach ($keys as $key) {
+    $aux[$key] = $array[$key];
+  }
+  $array = $aux;
+}
diff -Naurw webform_old/components/textarea.inc webform/components/textarea.inc
--- webform_old/components/textarea.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/textarea.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * @file
+ * Webform module textarea component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_textarea() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'cols' => '',
+      'rows' => '',
+      'title_display' => 0,
+      'resizable' => 1,
+      'disabled' => 0,
+      'description' => '',
+      'attributes' => array(),
+      'private' => FALSE,
+    ),
+  );
+}
+
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_textarea() {
+  return array(
+    'webform_display_textarea' => array(
+      'render element' => 'element',
+      'file' => 'components/textarea.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_textarea($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . theme('webform_token_help'),
+    '#cols' => 60,
+    '#rows' => 5,
+    '#weight' => 0,
+  );
+  $form['display']['cols'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Width'),
+    '#default_value' => $component['extra']['cols'],
+    '#description' => t('Width of the textarea in columns. This property might not have a visual impact depending on the CSS of your site.') . ' ' . t('Leaving blank will use the default size.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#parents' => array('extra', 'cols'),
+  );
+  $form['display']['rows'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Height'),
+    '#default_value' => $component['extra']['rows'],
+    '#description' => t('Height of the textarea in rows.') . ' ' . t('Leaving blank will use the default size.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#parents' => array('extra', 'rows'),
+  );
+  $form['display']['resizable'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Resizable'),
+    '#description' => t('Make this field resizable by the user.'),
+    '#weight' => 2,
+    '#default_value' => $component['extra']['resizable'],
+    '#parents' => array('extra', 'resizable'),
+  );
+  $form['display']['disabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Disabled'),
+    '#return_value' => 1,
+    '#description' => t('Make this field non-editable. Useful for setting an unchangeable default value.'),
+    '#weight' => 11,
+    '#default_value' => $component['extra']['disabled'],
+    '#parents' => array('extra', 'disabled'),
+  );
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_textarea($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'textarea',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node) : $component['value'],
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#rows' => !empty($component['extra']['rows']) ? $component['extra']['rows'] : 5,
+    '#cols' => !empty($component['extra']['cols']) ? $component['extra']['cols'] : 60,
+    '#attributes' => $component['extra']['attributes'],
+    '#resizable' => (bool) $component['extra']['resizable'], // MUST be FALSE to disable.
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  if ($component['extra']['disabled']) {
+    if ($filter) {
+      $element['#attributes']['readonly'] = 'readonly';
+    }
+    else {
+      $element['#disabled'] = TRUE;
+    }
+  }
+
+  if (isset($value)) {
+    $element['#default_value'] = $value[0];
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_textarea($component, $value, $format = 'html') {
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_textarea',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#format' => $format,
+    '#value' => isset($value[0]) ? $value[0] : '',
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the output of data for this component.
+ */
+function theme_webform_display_textarea($variables) {
+  $element = $variables['element'];
+  $output = $element['#format'] == 'html' ? nl2br(check_plain($element['#value'])) : $element['#value'];
+  if (drupal_strlen($output) > 80) {
+    $output = ($element['#format'] == 'html') ? '<div class="webform-long-answer">' . $output . '</div>' : $output;
+  }
+  return $output !== '' ? $output : ' ';
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_textarea($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $nonblanks = 0;
+  $submissions = 0;
+  $wordcount = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    if (drupal_strlen(trim($data['data'])) > 0) {
+      $nonblanks++;
+      $wordcount += str_word_count(trim($data['data']));
+    }
+    $submissions++;
+  }
+
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User entered value'), $nonblanks);
+  $rows[2] = array(t('Average submission length in words (ex blanks)'), ($nonblanks != 0 ? number_format($wordcount/$nonblanks, 2) : '0'));
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_textarea($component, $value) {
+  return empty($value[0]) ? '' : check_plain($value[0]);
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_textarea($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_textarea($component, $export_options, $value) {
+  return empty($value[0]) ? '' : $value[0];
+}
diff -Naurw webform_old/components/textfield.inc webform/components/textfield.inc
--- webform_old/components/textfield.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/textfield.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,254 @@
+<?php
+
+/**
+ * @file
+ * Webform module textfield component.
+ */
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_textfield() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'width' => '',
+      'maxlength' => '',
+      'field_prefix' => '',
+      'field_suffix' => '',
+      'disabled' => 0,
+      'unique' => 0,
+      'title_display' => 0,
+      'description' => '',
+      'attributes' => array(),
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_textfield() {
+  return array(
+    'webform_display_textfield' => array(
+      'render element' => 'element',
+      'file' => 'components/textfield.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_textfield($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . theme('webform_token_help'),
+    '#size' => 60,
+    '#maxlength' => 1024,
+    '#weight' => 0,
+  );
+  $form['display']['width'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Width'),
+    '#default_value' => $component['extra']['width'],
+    '#description' => t('Width of the textfield.') . ' ' . t('Leaving blank will use the default size.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#weight' => 0,
+    '#parents' => array('extra', 'width'),
+  );
+  $form['display']['field_prefix'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Prefix text placed to the left of the textfield'),
+    '#default_value' => $component['extra']['field_prefix'],
+    '#description' => t('Examples: $, #, -.'),
+    '#size' => 20,
+    '#maxlength' => 127,
+    '#weight' => 1.1,
+    '#parents' => array('extra', 'field_prefix'),
+  );
+  $form['display']['field_suffix'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Postfix text placed to the right of the textfield'),
+    '#default_value' => $component['extra']['field_suffix'],
+    '#description' => t('Examples: lb, kg, %.'),
+    '#size' => 20,
+    '#maxlength' => 127,
+    '#weight' => 1.2,
+    '#parents' => array('extra', 'field_suffix'),
+  );
+  $form['display']['disabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Disabled'),
+    '#return_value' => 1,
+    '#description' => t('Make this field non-editable. Useful for setting an unchangeable default value.'),
+    '#weight' => 11,
+    '#default_value' => $component['extra']['disabled'],
+    '#parents' => array('extra', 'disabled'),
+  );
+  $form['validation']['unique'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Unique'),
+    '#return_value' => 1,
+    '#description' => t('Check that all entered values for this field are unique. The same value is not allowed to be used twice.'),
+    '#weight' => 1,
+    '#default_value' => $component['extra']['unique'],
+    '#parents' => array('extra', 'unique'),
+  );
+  $form['validation']['maxlength'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Maxlength'),
+    '#default_value' => $component['extra']['maxlength'],
+    '#description' => t('Maximum length of the textfield value.'),
+    '#size' => 5,
+    '#maxlength' => 10,
+    '#weight' => 2,
+    '#parents' => array('extra', 'maxlength'),
+  );
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_textfield($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'textfield',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value'],
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#field_prefix' => empty($component['extra']['field_prefix']) ? NULL : ($filter ? _webform_filter_xss($component['extra']['field_prefix']) : $component['extra']['field_prefix']),
+    '#field_suffix' => empty($component['extra']['field_suffix']) ? NULL : ($filter ? _webform_filter_xss($component['extra']['field_suffix']) : $component['extra']['field_suffix']),
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#attributes' => $component['extra']['attributes'],
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'description', 'field_prefix', 'field_suffix'),
+  );
+
+  if ($component['extra']['disabled']) {
+    if ($filter) {
+      $element['#attributes']['readonly'] = 'readonly';
+    }
+    else {
+      $element['#disabled'] = TRUE;
+    }
+  }
+
+  // Enforce uniqueness.
+  if ($component['extra']['unique']) {
+    $element['#element_validate'][] = 'webform_validate_unique';
+  }
+
+  // Change the 'width' option to the correct 'size' option.
+  if ($component['extra']['width'] > 0) {
+    $element['#size'] = $component['extra']['width'];
+  }
+  if ($component['extra']['maxlength'] > 0) {
+    $element['#maxlength'] = $component['extra']['maxlength'];
+  }
+
+  if (isset($value)) {
+    $element['#default_value'] = $value[0];
+  }
+
+  return $element;
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_textfield($component, $value, $format = 'html') {
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_textfield',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#field_prefix' => $component['extra']['field_prefix'],
+    '#field_suffix' => $component['extra']['field_suffix'],
+    '#format' => $format,
+    '#value' => isset($value[0]) ? $value[0] : '',
+    '#translatable' => array('title', 'field_prefix', 'field_suffix'),
+  );
+}
+
+/**
+ * Format the output of data for this component.
+ */
+function theme_webform_display_textfield($variables) {
+  $element = $variables['element'];
+  $prefix = $element['#format'] == 'html' ? '' : $element['#field_prefix'];
+  $suffix = $element['#format'] == 'html' ? '' : $element['#field_suffix'];
+  $value = $element['#format'] == 'html' ? check_plain($element['#value']) : $element['#value'];
+  return $value !== '' ? ($prefix . $value . $suffix) : ' ';
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_textfield($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid']);
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $nonblanks = 0;
+  $submissions = 0;
+  $wordcount = 0;
+
+  $result = $query->execute();
+  foreach ($result as $data) {
+    if (drupal_strlen(trim($data['data'])) > 0) {
+      $nonblanks++;
+      $wordcount += str_word_count(trim($data['data']));
+    }
+    $submissions++;
+  }
+
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User entered value'), $nonblanks);
+  $rows[2] = array(t('Average submission length in words (ex blanks)'), ($nonblanks != 0 ? number_format($wordcount/$nonblanks, 2) : '0'));
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_textfield($component, $value) {
+  return check_plain(empty($value[0]) ? '' : $value[0]);
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_textfield($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_textfield($component, $export_options, $value) {
+  return !isset($value[0]) ? '' : $value[0];
+}
diff -Naurw webform_old/components/time.inc webform/components/time.inc
--- webform_old/components/time.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/components/time.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,433 @@
+<?php
+
+/**
+ * @file
+ * Webform module time component.
+ */
+
+// Time depends on functions provided by date.
+webform_component_include('date');
+
+/**
+ * Implements _webform_defaults_component().
+ */
+function _webform_defaults_time() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'pid' => 0,
+    'weight' => 0,
+    'value' => '',
+    'mandatory' => 0,
+    'extra' => array(
+      'timezone' => 'user',
+      'hourformat' => '12-hour',
+      'minuteincrements' => 1,
+      'title_display' => 0,
+      'description' => '',
+      'private' => FALSE,
+    ),
+  );
+}
+
+/**
+ * Implements _webform_theme_component().
+ */
+function _webform_theme_time() {
+  return array(
+    'webform_time' => array(
+      'render element' => 'element',
+      'file' => 'components/time.inc',
+    ),
+    'webform_display_time' => array(
+      'render element' => 'element',
+      'file' => 'components/time.inc',
+    ),
+  );
+}
+
+/**
+ * Implements _webform_edit_component().
+ */
+function _webform_edit_time($component) {
+  $form = array();
+  $form['value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default value'),
+    '#default_value' => $component['value'],
+    '#description' => t('The default value of the field.') . '<br />' . t('Accepts a time in any <a href="http://www.gnu.org/software/tar/manual/html_chapter/Date-input-formats.html">GNU Date Input Format</a>. Strings such as now, +2 hours, and 10:30pm are all valid.'),
+    '#size' => 60,
+    '#maxlength' => 127,
+    '#weight' => 0,
+  );
+  $form['extra']['timezone'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default value timezone'),
+    '#default_value' => $component['extra']['timezone'],
+    '#description' => t('If using relative dates for a default value (e.g. "now") base the current time on this timezone.'),
+    '#options' => array('user' => t('User timezone'), 'site' => t('Website timezone')),
+    '#weight' => 2,
+    '#access' => variable_get('configurable_timezones', 1),
+  );
+  $form['display']['hourformat'] = array(
+    '#type' => 'radios',
+    '#title' => t('Time format'),
+    '#default_value' => $component['extra']['hourformat'],
+    '#options' => array('12-hour' => t('12-hour (am/pm)'), '24-hour' => t('24-hour')),
+    '#weight' => 2,
+    '#parents' => array('extra', 'hourformat'),
+  );
+  $form['display']['minuteincrements'] = array(
+    '#type' => 'select',
+    '#title' => t('Minute increments'),
+    '#default_value' => $component['extra']['minuteincrements'],
+    '#options' => array(
+      1 => t('1 minute'),
+      5 => t('5 minute'),
+      10 => t('10 minute'),
+      15 => t('15 minute'),
+      30 => t('30 minute'),
+    ),
+    '#weight' => 3,
+    '#parents' => array('extra', 'minuteincrements'),
+  );
+  return $form;
+}
+
+/**
+ * Implements _webform_render_component().
+ */
+function _webform_render_time($component, $value = NULL, $filter = TRUE) {
+  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
+
+  $element = array(
+    '#type' => 'webform_time',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
+    '#element_validate' => array('webform_validate_time'),
+    '#hourformat' => $component['extra']['hourformat'],
+    '#minuteincrements' => $component['extra']['minuteincrements'],
+    '#default_value' => $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value'],
+    '#timezone' => $component['extra']['timezone'],
+    '#process' => array('webform_expand_time'),
+    '#theme' => 'webform_time',
+    '#theme_wrappers' => array('webform_element'),
+    '#translatable' => array('title', 'description'),
+  );
+
+  // Set the value from Webform if available.
+  if (!empty($value[0])) {
+    $element['#default_value'] = $value[0];
+  }
+
+  return $element;
+}
+
+/**
+ * Form API #process function for Webform time fields.
+ */
+function webform_expand_time($element) {
+  // Expand the default value from a string into an array.
+  if (!empty($element['#default_value'])) {
+    // Adjust the time based on the user or site timezone.
+    if (variable_get('configurable_timezones', 1) && $element['#timezone'] == 'user') {
+      $timezone_name = isset($GLOBALS['user']->timezone) ? $GLOBALS['user']->timezone : 'UTC';
+    }
+    else {
+      $timezone_name = variable_get('date_default_timezone', 'UTC');
+    }
+
+    $default_values = webform_date_array(webform_strtodate('c', $element['#default_value'], $timezone_name), 'time');
+  }
+  else {
+    $default_values = array(
+      'hour' => '',
+      'minute' => '',
+      'second' => '',
+    );
+  }
+
+  $first_hour = 0;
+  $last_hour = 23;
+  if ($element['#hourformat'] == '12-hour') {
+    $first_hour = 1;
+    $last_hour = 12;
+    $default_values = webform_time_convert($default_values, '12-hour');
+    $default_values['ampm'] = $default_values['ampm'] ? $default_values['ampm'] : 'am';
+  }
+
+  // Generate the choices for drop-down selects.
+  $hours[''] = t('hour');
+  $minutes[''] = t('minute');
+  for ($i = $first_hour; $i <= $last_hour; $i++) {
+    $hours[$i] = $i;
+  }
+  for ($i = 0; $i <= 59; $i += $element['#minuteincrements']) {
+    $minutes[$i] = $i < 10 ? "0$i" : $i;
+  }
+  $ampms = array('am' => t('am'), 'pm' => t('pm'));
+
+  // Adjust the default for minutes if needed, rounding up to the closest value.
+  if (!isset($minutes[$default_values['minute']])) {
+    foreach ($minutes as $minute => $padded_minute) {
+      if ($minute > $default_values['minute']) {
+        $default_values['minute'] = $minute;
+        break;
+      }
+    }
+  }
+
+  // If the above loop didn't set a value, it's because rounding up would go to
+  // the next hour. This gets quite a bit more complicated, since we need to
+  // deal with looping around on hours, as well as flipping am/pm.
+  if (!isset($minutes[$default_values['minute']])) {
+    $default_values['minute'] = 0;
+    $default_values['hour']++;
+    // If the hour rolls over also, set hour to the first hour in the list.
+    if (!isset($hours[$default_values['hour']])) {
+      $default_values['hour'] = $element['#hourformat'] == '12-hour' ? 1 : 0;
+    }
+    // If the hour has been incremented to 12:00 in 12-hour format, flip am/pm.
+    // Note that technically midnight and noon are neither am or pm, but common
+    // convention (and US standard) is to represent 12:00am as midnight.
+    // See http://en.wikipedia.org/wiki/Midnight#Start_and_end_of_day.
+    if ($element['#hourformat'] == '12-hour' && $default_values['hour'] == 12) {
+      $default_values['ampm'] = $default_values['ampm'] == 'am' ? 'pm' : 'am';
+    }
+  }
+
+  $element['hour'] = array(
+    '#prefix' => '',
+    '#type' => 'select',
+    '#default_value' => $default_values['hour'],
+    '#options' => $hours,
+  );
+  $element['minute'] = array(
+    '#prefix' => ':',
+    '#type' => 'select',
+    '#default_value' => $default_values['minute'],
+    '#options' => $minutes,
+  );
+  if (strcmp($element['#hourformat'], '12-hour') == 0) {
+    $element['ampm'] = array(
+      '#type' => 'radios',
+      '#default_value' => $default_values['ampm'],
+      '#options' => $ampms,
+    );
+  }
+
+  // Set the overall default value.
+  if ($default_values['hour'] !== '') {
+    $element['#default_value'] = webform_date_string($default_values);
+  }
+
+  return $element;
+}
+
+/**
+ * Theme a webform time element.
+ */
+function theme_webform_time($variables) {
+  $element = $variables['element'];
+
+  $element['hour']['#attributes']['class'] = array('hour');
+  $element['minute']['#attributes']['class'] = array('minute');
+
+  // Add error classes to all items within the element.
+  if (form_get_error($element)) {
+    $element['hour']['#attributes']['class'][] = 'error';
+    $element['minute']['#attributes']['class'][] = 'error';
+  }
+
+  $output = '<div class="webform-container-inline">' . drupal_render($element['hour']) . drupal_render($element['minute']) . drupal_render($element['ampm']) . '</div>';
+
+  return $output;
+}
+
+function webform_validate_time($element, $form_state) {
+  $form_key = $element['#webform_component']['form_key'];
+  $name = $element['#webform_component']['name'];
+
+  // Check if the user filled the required fields.
+  foreach ($element['#hourformat'] == '12-hour' ? array('hour', 'minute', 'ampm') : array('hour', 'minute') as $field_type) {
+    if ($element[$field_type]['#value'] === '' && $element['#required']) {
+      form_error($element, t('%field field is required.', array('%field' => $name)));
+      return;
+    }
+  }
+
+  // Check for a valid time.
+  if ($element['hour']['#value'] !== '' || $element['minute']['#value'] !== '') {
+    if (!is_numeric($element['hour']['#value']) || !is_numeric($element['minute']['#value']) || (isset($element['ampm']) && $element['ampm']['#value'] === '')) {
+      form_error($element, t('Entered %name is not a valid time.', array('%name' => $name)));
+      return;
+    }
+  }
+}
+
+/**
+ * Implements _webform_submit_component().
+ */
+function _webform_submit_time($component, $value) {
+  // Convert to 24-hour time before string conversion.
+  if ($component['extra']['hourformat'] == '12-hour') {
+    $value = webform_time_convert($value, '24-hour');
+  }
+
+  // Convert the value into a ISO 8601 string.
+  return $value['hour'] !== '' ? webform_date_string($value, 'time') : '';
+}
+
+/**
+ * Implements _webform_display_component().
+ */
+function _webform_display_time($component, $value, $format = 'html') {
+  $value = webform_date_array(isset($value[0]) ? $value[0] : '', 'time');
+  if ($component['extra']['hourformat'] == '12-hour') {
+    $value = webform_time_convert($value, '12-hour');
+  }
+
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_time',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#format' => $format,
+    '#hourformat' => $component['extra']['hourformat'],
+    '#value' => $value,
+    '#translatable' => array('title'),
+  );
+}
+
+/**
+ * Format the output of data for this component.
+ */
+function theme_webform_display_time($variables) {
+  $element = $variables['element'];
+  $output = ' ';
+  if (isset($element['#value']['hour']) && $element['#value']['hour'] !== '' && isset($element['#value']['minute']) && $element['#value']['minute'] !== '') {
+    if ($element['#hourformat'] == '24-hour') {
+      $output = sprintf('%02d', $element['#value']['hour']) . ':' . sprintf('%02d', $element['#value']['minute']);
+    }
+    else {
+      $output = $element['#value']['hour'] . ':' . sprintf('%02d', $element['#value']['minute']) . ' ' . $element['#value']['ampm'];
+    }
+  }
+  return $output;
+}
+
+/**
+ * Implements _webform_analysis_component().
+ */
+function _webform_analysis_time($component, $sids = array()) {
+  $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->orderBy('sid');
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $result = $query->execute();
+
+  $times = array();
+  $submissions = 0;
+  foreach ($result as $row) {
+    $submissions++;
+    if ($row['data']) {
+      $times[] = webform_date_array($row['data']);
+    }
+  }
+
+  // Display stats.
+  $nonblanks = count($times);
+  $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
+  $rows[1] = array(t('User entered value'), $nonblanks);
+  return $rows;
+}
+
+/**
+ * Implements _webform_table_component().
+ */
+function _webform_table_time($component, $value) {
+  if ($value[0]) {
+    $time = webform_date_array($value[0], 'time');
+    if ($component['extra']['hourformat'] == '24-hour') {
+      return sprintf('%02d', $time['hour']) . ':' . sprintf('%02d', $time['minute']);
+    }
+    else {
+      $time = webform_time_convert($time, '12-hour');
+      return $time['hour'] . ':' . sprintf('%02d', $time['minute']) . ' ' . $time['ampm'];
+    }
+  }
+  else {
+    return '';
+  }
+}
+
+/**
+ * Implements _webform_csv_headers_component().
+ */
+function _webform_csv_headers_time($component, $export_options) {
+  $header = array();
+  $header[0] = '';
+  $header[1] = '';
+  $header[2] = $component['name'];
+  return $header;
+}
+
+/**
+ * Implements _webform_csv_data_component().
+ */
+function _webform_csv_data_time($component, $export_options, $value) {
+  if ($value[0]) {
+    $time = webform_date_array($value[0], 'time');
+    if ($component['extra']['hourformat'] == '24-hour') {
+      return sprintf('%02d', $time['hour']) . ':' . sprintf('%02d', $time['minute']);
+    }
+    else {
+      $time = webform_time_convert($time, '12-hour');
+      return $time['hour'] . ':' . sprintf('%02d', $time['minute']) . ' ' . $time['ampm'];
+    }
+  }
+  else {
+    return '';
+  }
+}
+
+/**
+ * Convert a time between a 24-hour and a 12-hour value.
+ *
+ * @param $array
+ *   An array of hour, minute, second, and optionally ampm.
+ * @param $format
+ *   Either 12-hour or 24-hour.
+ * @return
+ *   An array with hour, minute, second, and ampm (if using "12-hour").
+ */
+function webform_time_convert($array, $format) {
+  if ($array['hour'] !== '') {
+    if ($format == '12-hour') {
+      $array['ampm'] = ($array['hour'] >= 12 && $array['hour'] < 24) ? 'pm' : 'am';
+      $array['hour'] = ($array['hour'] > 12 || $array['hour'] == 0) ? abs($array['hour'] - 12) : (int) $array['hour'];
+    }
+    elseif ($format == '24-hour' && isset($array['ampm'])) {
+      $array['hour'] = ($array['hour'] < 12 && $array['ampm'] == 'pm') ? $array['hour'] + 12 : (int) $array['hour'];
+      $array['hour'] = ($array['hour'] == 12 && $array['ampm'] == 'am') ? 0 : $array['hour'];
+    }
+  }
+
+  if ($format == '12-hour' && !isset($array['ampm'])) {
+    $array['ampm'] = '';
+  }
+  elseif ($format == '24-hour' && isset($array['ampm'])) {
+    unset($array['ampm']);
+  }
+
+  return $array;
+}
diff -Naurw webform_old/css/webform-admin.css webform/css/webform-admin.css
--- webform_old/css/webform-admin.css	1970-01-01 01:00:00.000000000 +0100
+++ webform/css/webform-admin.css	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,119 @@
+/**
+ * @file
+ * Styles that are used when viewing results or modifying webform settings.
+ */
+
+/* Submission view page */
+.webform-submission-info {
+  padding: 10px;
+}
+.webform-submission-info .user-picture {
+  float: right;
+  margin-left: 10px;
+}
+.webform-submission-info-text {
+  overflow: hidden;
+}
+.webform-submission-actions {
+  float: left;
+}
+.webform-submission-navigation {
+  text-align: right;
+}
+.webform-submission-next {
+  margin-left: 20px;
+}
+
+/* Element for selecting components, i.e. included components for e-mails. */
+.webform-component-select-wrapper {
+  max-height: 300px;
+  overflow: auto;
+}
+.webform-component-select-table table {
+  margin: 0;
+}
+.webform-component-select-table tr.selected td,
+.webform-component-select-table tr.selected td.active {
+  background: inherit;
+  color: inherit;
+}
+.form-item .webform-component-select-table .description {
+  font-size: inherit;
+}
+.form-item .webform-component-select-table input {
+  width: 12px;
+  height: 12px;
+  margin: 0 2px 2px;
+}
+.webform-select-list-format table {
+  border: 1px solid;
+  width: auto;
+  font-size: 90%;
+}
+.webform-select-list-format td,
+.webform-select-list-format th {
+  border: 1px solid;
+}
+tr.webform-pagebreak td {
+  border-top: 2px dotted #999;
+}
+td.webform-pagebreak {
+  font-weight: bold;
+}
+
+/* Special theming for the options element widget (if installed) */
+.webform-options-element thead {
+  display: none;
+}
+.webform-options-element fieldset {
+  border: none;
+  background: none;
+  margin: 0;
+  padding: 0;
+}
+.webform-options-element fieldset legend {
+  display: none;
+}
+
+/* Checkboxes for allowed file extensions */
+table.webform-file-extensions td {
+  vertical-align: top;
+}
+table.webform-file-extensions td .description {
+  white-space: normal;
+}
+table.webform-file-extensions .form-type-checkbox {
+  width: 5em;
+  float: left;
+}
+table.webform-file-extensions input.form-text {
+  width: 95%;
+}
+
+/* General styles */
+.webform-checkbox {
+  text-align: center;
+  width: 40px;
+}
+.webform-container-inline div,
+.webform-container-inline div.form-item {
+  display: inline;
+}
+.webform-default-value {
+  color: #999;
+}
+.webform-results-per-page a.selected {
+  font-weight: bold;
+}
+
+/* Display of adding/editing components */
+html.js fieldset.webform-position,
+html.js div.webform-position {
+  display: none;
+}
+tr.webform-add-form .tabledrag-changed {
+  display: none;
+}
+#webform-components tr.webform-add-form {
+  background-color: inherit;
+}
diff -Naurw webform_old/css/webform.css webform/css/webform.css
--- webform_old/css/webform.css	1970-01-01 01:00:00.000000000 +0100
+++ webform/css/webform.css	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,30 @@
+/**
+ * @file
+ * Front-end styling for the display of webforms.
+ */
+input.webform-calendar {
+  display: none;
+  padding: 3px;
+  vertical-align: top;
+}
+html.js input.webform-calendar {
+  display: inline;
+}
+.webform-container-inline label {
+  display: inline;
+  margin-right: 1em;
+}
+.webform-container-inline div,
+.webform-container-inline div.form-item {
+  display: inline;
+}
+.webform-container-inline div.description {
+  display: block;
+}
+.webform-container-inline div.messages {
+  display: block;
+  float: left;
+}
+.webform-container-inline div.ajax-progress-bar div {
+  display: inherit;
+}
diff -Naurw webform_old/images/calendar.png webform/images/calendar.png
--- webform_old/images/calendar.png	1970-01-01 01:00:00.000000000 +0100
+++ webform/images/calendar.png	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,3 @@
+PNG
+
+   IHDR         Xo    tEXtSoftware Adobe ImageReadyqe<   PLTEuN/1ɴqqq   s-   	tRNS SOx   8IDATxb lh $ L !F TY.!V@~   RJ>w#O    IENDB`
\ No newline at end of file
diff -Naurw webform_old/includes/webform.admin.inc webform/includes/webform.admin.inc
--- webform_old/includes/webform.admin.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.admin.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,297 @@
+<?php
+
+/**
+ * @file
+ * Administration pages provided by Webform module.
+ */
+
+/**
+ * Menu callback for admin/config/content/webform.
+ */
+function webform_admin_settings() {
+  module_load_include('inc', 'webform', 'includes/webform.export');
+
+  $node_types = node_type_get_names();
+  $form['node_types'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Webform-enabled content types'),
+    '#description' => t('Webform allows you to enable the webform components for any content type.  Choose the types on which you would like to associate webform components.'),
+    '#options' => $node_types,
+    '#default_value' => webform_variable_get('webform_node_types'),
+  );
+
+  $form['components'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Available components'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#description' => t('These are the available field types for your installation of Webform. You may disable any of these components by unchecking its corresponding box. Only checked components will be available in existing or new webforms.'),
+  );
+
+  // Add each component to the form:
+  $form['components'] = array('#tree' => TRUE);
+  $component_types = webform_components(TRUE);
+  foreach ($component_types as $key => $component) {
+    $form['components'][$key] = array(
+      '#title' => $component['label'],
+      '#description' => $component['description'],
+      '#type' => 'checkbox',
+      '#return_value' => 1,
+      '#default_value' => $component['enabled'],
+    );
+  }
+
+  $form['email'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Default e-mail values'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+
+  $form['email']['webform_default_from_address']  = array(
+    '#type' => 'textfield',
+    '#title' => t('From address'),
+    '#default_value' => variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from'))),
+    '#description' => t('The default sender address for emailed webform results; often the e-mail address of the maintainer of your forms.'),
+  );
+
+  $form['email']['webform_default_from_name']  = array(
+    '#type' => 'textfield',
+    '#title' => t('From name'),
+    '#default_value' => variable_get('webform_default_from_name', variable_get('site_name', '')),
+    '#description' => t('The default sender name which is used along with the default from address.'),
+  );
+
+  $form['email']['webform_default_subject']  = array(
+    '#type' => 'textfield',
+    '#title' => t('Default subject'),
+    '#default_value' => variable_get('webform_default_subject', t('Form submission from: %title')),
+    '#description' => t('The default subject line of any e-mailed results.'),
+  );
+
+  $form['email']['webform_default_format']  = array(
+    '#type' => 'radios',
+    '#title' => t('Format'),
+    '#options' => array(
+      0 => t('Plain text'),
+      1 => t('HTML'),
+    ),
+    '#default_value' => variable_get('webform_default_format', 0),
+    '#description' => t('The default format for new e-mail settings. Webform e-mail options take precedence over the settings for system-wide e-mails configured in MIME mail.'),
+    '#access' => webform_email_html_capable(),
+  );
+
+  $form['email']['webform_format_override']  = array(
+    '#type' => 'radios',
+    '#title' => t('Format override'),
+    '#options' => array(
+      0 => t('Per-webform configuration of e-mail format'),
+      1 => t('Send all e-mails in the default format'),
+    ),
+    '#default_value' => variable_get('webform_format_override', 0),
+    '#description' => t('Force all webform e-mails to be sent in the default format.'),
+    '#access' => webform_email_html_capable(),
+  );
+
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced options'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['advanced']['webform_use_cookies']  = array(
+    '#type' => 'checkbox',
+    '#checked_value' => 1,
+    '#title' => t('Allow cookies for tracking submissions'),
+    '#default_value' => variable_get('webform_use_cookies', 0),
+    '#description' => t('<a href="http://www.wikipedia.org/wiki/HTTP_cookie">Cookies</a> can be used to help prevent the same user from repeatedly submitting a webform. This feature is not needed for limiting submissions per user, though it can increase accuracy in some situations. Besides cookies, Webform also uses IP addresses and site usernames to prevent repeated submissions.'),
+  );
+
+  $form['advanced']['webform_search_index']  = array(
+    '#type' => 'checkbox',
+    '#checked_value' => 1,
+    '#title' => t('Include webform forms in search index'),
+    '#default_value' => variable_get('webform_search_index', 1),
+    '#description' => t('When selected, all Webform nodes will have their form components indexed by the search engine.'),
+    '#access' => module_exists('search'),
+  );
+
+  $form['advanced']['webform_email_address_format'] = array(
+    '#type' => 'radios',
+    '#title' => t('E-mail address format'),
+    '#options' => array(
+      'long' => t('Long format: "Example Name" &lt;name@example.com&gt;'),
+      'short' => t('Short format: name@example.com'),
+    ),
+    '#default_value' => variable_get('webform_email_address_format', 'long'),
+    '#description' => t('Most servers support the "long" format which will allow for more friendly From addresses in e-mails sent. However many Windows-based servers are unable to send in the long format. Change this option if experiencing problems sending e-mails with Webform.'),
+  );
+
+  $form['advanced']['webform_export_format'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default export format'),
+    '#options' => webform_export_list(),
+    '#default_value' => variable_get('webform_export_format', 'delimited'),
+  );
+
+  $form['advanced']['webform_csv_delimiter']  = array(
+    '#type' => 'select',
+    '#title' => t('Default export delimiter'),
+    '#description' => t('This is the delimiter used in the CSV/TSV file when downloading Webform results. Using tabs in the export is the most reliable method for preserving non-latin characters. You may want to change this to another character depending on the program with which you anticipate importing results.'),
+    '#default_value' => variable_get('webform_csv_delimiter', '\t'),
+    '#options' => array(
+      ','  => t('Comma (,)'),
+      '\t' => t('Tab (\t)'),
+      ';'  => t('Semicolon (;)'),
+      ':'  => t('Colon (:)'),
+      '|'  => t('Pipe (|)'),
+      '.'  => t('Period (.)'),
+      ' '  => t('Space ( )'),
+    ),
+  );
+
+  $form['advanced']['webform_submission_access_control']  = array(
+    '#type' => 'radios',
+    '#title' => t('Submission access control'),
+    '#options' => array(
+      '1' => t('Select the user roles that may submit each individual webform'),
+      '0' => t('Disable Webform submission access control'),
+    ),
+    '#default_value' => variable_get('webform_submission_access_control', 1),
+    '#description' => t('By default, the configuration form for each webform allows the administrator to choose which roles may submit the form. You may want to allow users to always submit the form if you are using a separate node access module to control access to webform nodes themselves.'),
+  );
+
+  $form = system_settings_form($form);
+  $form['#theme'] = 'webform_admin_settings';
+  array_unshift($form['#submit'], 'webform_admin_settings_submit');
+
+  return $form;
+}
+
+/**
+ * Submit handler for the webform_admin_settings() form.
+ */
+function webform_admin_settings_submit($form, &$form_state) {
+  $disabled_components = array();
+  foreach ($form_state['values']['components'] as $name => $enabled) {
+    if (!$enabled) {
+      $disabled_components[] = $name;
+    }
+  }
+  // Update $form_state and let system_settings_form_submit() handle saving.
+  $form_state['values']['webform_disabled_components'] = $disabled_components;
+  unset($form_state['values']['components']);
+
+  // Change the name of the node type variable and clean it up.
+  $form_state['values']['webform_node_types'] = array_keys(array_filter($form_state['values']['node_types']));
+  unset($form_state['values']['node_types']);
+}
+
+/**
+ * Theme the output of the webform_admin_settings() form.
+ */
+function theme_webform_admin_settings($variables) {
+  $form = $variables['form'];
+
+  // Format the components into a table.
+  foreach (element_children($form['components']) as $key) {
+    $row = array();
+    $row[] = $form['components'][$key]['#title'];
+    $row[] = $form['components'][$key]['#description'];
+    $form['components'][$key]['#title'] = NULL;
+    $form['components'][$key]['#description'] = NULL;
+    $row[] = array('data' => drupal_render($form['components'][$key]), 'align' => 'center');
+    $rows[] = $row;
+  }
+  $header = array(t('Name'), t('Description'), array('data' => t('Enabled'), 'class' => array('checkbox')));
+
+  // Create the table inside the form.
+  $form['components']['table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+  );
+
+  return drupal_render_children($form);
+}
+
+/**
+ * Menu callback for admin/content/webform. Displays all webforms on the site.
+ */
+function webform_admin_content() {
+  $webform_types = webform_variable_get('webform_node_types');
+
+  $nodes = array();
+  if ($webform_types) {
+    $nodes = db_select('node', 'n')
+      ->fields('n')
+      ->condition('n.type', $webform_types, 'IN')
+      ->execute()
+      ->fetchAllAssoc('nid');
+  }
+
+  return theme('webform_admin_content', array('nodes' => $nodes));
+}
+
+/**
+ * Create a comma-separate list of content types that are webform enabled.
+ */
+function webform_admin_type_list() {
+  $webform_types = webform_variable_get('webform_node_types');
+  $webform_type_list = '';
+  $webform_type_count = count($webform_types);
+  foreach ($webform_types as $n => $type) {
+    $webform_type_list .= l(node_type_get_name($type), 'node/add/' . $type);
+    if ($n + 1 < $webform_type_count) {
+      $webform_type_list .= $webform_type_count == 2 ? ' ' : ', ';
+    }
+    if ($n + 2 == $webform_type_count) {
+      $webform_type_list .= t('or') . ' ';
+    }
+  }
+
+  return $webform_type_list;
+}
+
+/**
+ * Generate a list of all webforms avaliable on this site.
+ */
+function theme_webform_admin_content($variables) {
+  $nodes = $variables['nodes'];
+  $header = array(
+    t('Title'),
+    array('data' => t('View'), 'colspan' => '4'),
+    array('data' => t('Operations'), 'colspan' => '2')
+  );
+
+  $rows = array();
+  foreach ($nodes as $node) {
+    $rows[] = array(
+      l($node->title, 'node/' . $node->nid),
+      l(t('Submissions'), 'node/' . $node->nid . '/webform-results'),
+      l(t('Analysis'), 'node/' . $node->nid . '/webform-results/analysis'),
+      l(t('Table'), 'node/' . $node->nid . '/webform-results/table'),
+      l(t('Download'), 'node/' . $node->nid . '/webform-results/download'),
+      node_access('update', $node) ? l(t('Edit'), 'node/' . $node->nid . '/edit') : '',
+      user_access('delete all webform submissions') ? l(t('Clear'), 'node/' . $node->nid . '/webform-results/clear') : '',
+    );
+  }
+
+  if (empty($rows)) {
+    $webform_types = webform_variable_get('webform_node_types');
+    if (empty($webform_types)) {
+      $message = t('Webform is currently not enabled on any content types.') . ' ' . t('Visit the <a href="!url">Webform settings</a> page and enable Webform on at least one content type.', array('!url' => url('admin/config/content/webform')));
+    }
+    else {
+      $webform_type_list = webform_admin_type_list();
+      $message = t('There are currently no webforms on your site. Create a !types piece of content.', array('!types' => $webform_type_list));
+    }
+
+    $rows[] = array(
+      array('data' => $message, 'colspan' => 7),
+    );
+  }
+
+  return theme('table', array('header' => $header, 'rows' => $rows));
+}
diff -Naurw webform_old/includes/webform.components.inc webform/includes/webform.components.inc
--- webform_old/includes/webform.components.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.components.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,1104 @@
+<?php
+
+/**
+ * @file
+ * Webform module component handling.
+ */
+
+/**
+ * Provides interface and database handling for editing components of a webform.
+ *
+ * @author Nathan Haug <nate@lullabot.com>
+ */
+
+/**
+ * Overview page of all components for this webform.
+ */
+function webform_components_page($node, $page_number = 1) {
+  $output = drupal_get_form('webform_components_form', $node);
+
+  return array(
+    '#theme' => 'webform_components_page',
+    '#node' => $node,
+    '#form' => $output,
+  );
+}
+
+/**
+ * Theme the output of the main components page.
+ *
+ * This theming provides a way to toggle between the editing modes if Form
+ * Builder module is available.
+ */
+function theme_webform_components_page($variables) {
+  $node = $variables['node'];
+  $form = $variables['form'];
+
+  return drupal_render($form);
+}
+
+/**
+ * The table-based listing of all components for this webform.
+ */
+function webform_components_form($form, $form_state, $node) {
+  $form = array(
+    '#tree' => TRUE,
+    '#node' => $node,
+    'components' => array(),
+  );
+
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+
+  $options = array();
+  foreach ($node->webform['components'] as $cid => $component) {
+    $options[$cid] = check_plain($component['name']);
+    $form['components'][$cid]['cid'] = array(
+      '#type' => 'hidden',
+      '#default_value' => $component['cid'],
+    );
+    $form['components'][$cid]['pid'] = array(
+      '#type' => 'hidden',
+      '#default_value' => $component['pid'],
+    );
+    $form['components'][$cid]['weight'] = array(
+      '#type' => 'textfield',
+      '#size' => 4,
+      '#title' => t('Weight'),
+      '#default_value' => $component['weight'],
+    );
+    $form['components'][$cid]['mandatory'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Mandatory'),
+      '#default_value' => $component['mandatory'],
+      '#access' => webform_component_feature($component['type'], 'required'),
+    );
+    if (!isset($max_weight) || $component['weight'] > $max_weight) {
+      $max_weight = $component['weight'];
+    }
+  }
+
+  $form['add']['name'] = array(
+    '#type' => 'textfield',
+    '#size' => 24,
+    '#maxlength' => 255,
+  );
+
+  $form['add']['type'] = array(
+    '#type' => 'select',
+    '#options' => webform_component_options(),
+    '#weight' => 3,
+    '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['type'] : 'textfield',
+  );
+  $form['add']['mandatory'] = array(
+    '#type' => 'checkbox',
+  );
+  $form['add']['cid'] = array(
+    '#type' => 'hidden',
+    '#default_value' => '',
+  );
+  $form['add']['pid'] = array(
+    '#type' => 'hidden',
+    '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['pid'] : 0,
+  );
+  $form['add']['weight'] = array(
+    '#type' => 'textfield',
+    '#size' => 4,
+    '#delta' => count($node->webform['components']) > 10 ? count($node->webform['components']) : 10,
+  );
+
+  if (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) {
+    // Make the new component appear by default directly after the one that was
+    // just added.
+    $form['add']['weight']['#default_value'] = $node->webform['components'][$_GET['cid']]['weight'] + 1;
+    foreach (array_keys($node->webform['components']) as $cid) {
+      // Adjust all later components also, to make sure none of them have the
+      // same weight as the new component.
+      if ($form['components'][$cid]['weight']['#default_value'] >= $form['add']['weight']['#default_value']) {
+        $form['components'][$cid]['weight']['#default_value']++;
+      }
+    }
+  }
+  else {
+    // If no component was just added, the new component should appear by
+    // default at the end of the list.
+    $form['add']['weight']['#default_value'] = isset($max_weight) ? $max_weight + 1 : 0;
+  }
+
+  $form['add']['add'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+    '#weight' => 45,
+    '#validate' => array('webform_components_form_add_validate', 'webform_components_form_validate'),
+    '#submit' => array('webform_components_form_add_submit'),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => 45,
+    '#access' => count($node->webform['components']) > 0,
+  );
+
+  return $form;
+}
+
+/**
+ * Theme the node components form. Use a table to organize the components.
+ *
+ * @param $form
+ *   The form array.
+ * @return
+ *   Formatted HTML form, ready for display.
+ */
+function theme_webform_components_form($variables) {
+  $form = $variables['form'];
+
+  $form['components']['#attached']['library'][] = array('webform', 'admin');
+
+  // TODO: Attach these. See http://drupal.org/node/732022.
+  drupal_add_tabledrag('webform-components', 'order', 'sibling', 'webform-weight');
+  drupal_add_tabledrag('webform-components', 'match', 'parent', 'webform-pid', 'webform-pid', 'webform-cid');
+
+  $node = $form['#node'];
+
+  $header = array(t('Label'), t('Type'), t('Value'), t('Mandatory'), t('Weight'), array('data' => t('Operations'), 'colspan' => 3));
+  $rows = array();
+
+  // Add a row containing form elements for a new item.
+  unset($form['add']['name']['#title'], $form['add_type']['#description']);
+  $form['add']['name']['#attributes']['rel'] = t('New component name');
+  $form['add']['name']['#attributes']['class'] = array('webform-default-value');
+  $form['add']['cid']['#attributes']['class'] = array('webform-cid');
+  $form['add']['pid']['#attributes']['class'] = array('webform-pid');
+  $form['add']['weight']['#attributes']['class'] = array('webform-weight');
+  $row_data = array(
+    drupal_render($form['add']['name']),
+    drupal_render($form['add']['type']),
+    '',
+    drupal_render($form['add']['mandatory']),
+    drupal_render($form['add']['cid']) . drupal_render($form['add']['pid']) . drupal_render($form['add']['weight']),
+    array('colspan' => 3, 'data' => drupal_render($form['add']['add'])),
+  );
+  $add_form = array('data' => $row_data, 'class' => array('draggable', 'webform-add-form'));
+  $form_rendered = FALSE;
+
+  if (!empty($node->webform['components'])) {
+    $component_tree = array();
+    $page_count = 1;
+    _webform_components_tree_build($node->webform['components'], $component_tree, 0, $page_count);
+    $component_tree = _webform_components_tree_sort($component_tree);
+    // Build the table rows.
+    function _webform_components_form_rows($node, $cid, $component, $level, &$form, &$rows, &$add_form) {
+      // Create presentable values.
+      if (drupal_strlen($component['value']) > 30) {
+        $component['value'] = drupal_substr($component['value'], 0, 30);
+        $component['value'] .= '...';
+      }
+      $component['value'] = check_plain($component['value']);
+
+      // Remove individual titles from the mandatory and weight fields.
+      unset($form['components'][$cid]['mandatory']['#title']);
+      unset($form['components'][$cid]['pid']['#title']);
+      unset($form['components'][$cid]['weight']['#title']);
+
+      // Add special classes for weight and parent fields.
+      $form['components'][$cid]['cid']['#attributes']['class'] = array('webform-cid');
+      $form['components'][$cid]['pid']['#attributes']['class'] = array('webform-pid');
+      $form['components'][$cid]['weight']['#attributes']['class'] = array('webform-weight');
+
+      // Build indentation for this row.
+      $indents = '';
+      for ($n = 1; $n <= $level; $n++) {
+        $indents .= '<div class="indentation">&nbsp;</div>';
+      }
+
+      // Add each component to a table row.
+      $row_data = array(
+        $indents . filter_xss($component['name']),
+        $form['add']['type']['#options'][$component['type']],
+        ($component['value'] == '') ? '-' : $component['value'],
+        drupal_render($form['components'][$cid]['mandatory']),
+        drupal_render($form['components'][$cid]['cid']) . drupal_render($form['components'][$cid]['pid']) . drupal_render($form['components'][$cid]['weight']),
+        l(t('Edit'), 'node/' . $node->nid . '/webform/components/' . $cid, array('query' => drupal_get_destination())),
+        l(t('Clone'), 'node/' . $node->nid . '/webform/components/' . $cid . '/clone', array('query' => drupal_get_destination())),
+        l(t('Delete'), 'node/' . $node->nid . '/webform/components/' . $cid . '/delete', array('query' => drupal_get_destination())),
+      );
+      $row_class = array('draggable');
+      if (!webform_component_feature($component['type'], 'group')) {
+        $row_class[] = 'tabledrag-leaf';
+      }
+      if ($component['type'] == 'pagebreak') {
+        $row_class[] = 'tabledrag-root';
+        $row_class[] = 'webform-pagebreak';
+        $row_data[0] = array('class' => array('webform-pagebreak'), 'data' => $row_data[0]);
+      }
+      $rows[] = array('data' => $row_data, 'class' => $row_class);
+      if (isset($component['children']) && is_array($component['children'])) {
+        foreach ($component['children'] as $cid => $component) {
+          _webform_components_form_rows($node, $cid, $component, $level + 1, $form, $rows, $add_form);
+        }
+      }
+
+      // Add the add form if this was the last edited component.
+      if (isset($_GET['cid']) && $component['cid'] == $_GET['cid'] && $add_form) {
+        $add_form['data'][0] = $indents . $add_form['data'][0];
+        $rows[] = $add_form;
+        $add_form = FALSE;
+      }
+    }
+    foreach ($component_tree['children'] as $cid => $component) {
+      _webform_components_form_rows($node, $cid, $component, 0, $form, $rows, $add_form);
+    }
+  }
+  else {
+    $rows[] = array(array('data' => t('No Components, add a component below.'), 'colspan' => 9));
+  }
+
+  // Append the add form if not already printed.
+  if ($add_form) {
+    $rows[] = $add_form;
+  }
+
+  $output = '';
+  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'webform-components')));
+  $output .= drupal_render_children($form);
+  return $output;
+}
+
+/**
+ * Validate handler for webform_components_form().
+ */
+function webform_components_form_validate($form, &$form_state) {
+  // Check that no two components end up with the same form key.
+  $duplicates = array();
+  $parents = array();
+  if (isset($form_state['values']['components'])) {
+    foreach ($form_state['values']['components'] as $cid => $component) {
+      $form_key = $form['#node']->webform['components'][$cid]['form_key'];
+      if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']])) && $existing !== FALSE) {
+        if (!isset($duplicates[$form_key])) {
+          $duplicates[$form_key] = array($existing);
+        }
+        $duplicates[$form_key][] = $cid;
+      }
+      $parents[$component['pid']][$cid] = $form_key;
+    }
+  }
+
+  if (!empty($duplicates)) {
+    $error = t('The form order failed to save because the following elements have same form keys and are under the same parent. Edit each component and give them a unique form key, then try moving them again.');
+    $items = array();
+    foreach ($duplicates as $form_key => $cids) {
+      foreach ($cids as $cid) {
+        $items[] = $form['#node']->webform['components'][$cid]['name'];
+      }
+    }
+
+    form_error($form['components'], $error . theme('item_list', array('items' => $items)));
+  }
+}
+
+/**
+ * Validate handler for webform_component_form() when adding a new component.
+ */
+function webform_components_form_add_validate($form, &$form_state) {
+  // Check that the entered component name is valid.
+  if (drupal_strlen(trim($form_state['values']['add']['name'])) <= 0) {
+    form_error($form['add']['name'], t('When adding a new component, the name field is required.'));
+  }
+}
+
+/**
+ * Submit handler for webform_components_form() to save component order.
+ */
+function webform_components_form_submit($form, &$form_state) {
+  $node = node_load($form_state['values']['nid']);
+
+  // Update all mandatory and weight values.
+  $changes = FALSE;
+  foreach ($node->webform['components'] as $cid => $component) {
+    if ($component['pid'] != $form_state['values']['components'][$cid]['pid'] || $component['weight'] != $form_state['values']['components'][$cid]['weight'] || $component['mandatory'] != $form_state['values']['components'][$cid]['mandatory']) {
+      $changes = TRUE;
+      $node->webform['components'][$cid]['weight'] = $form_state['values']['components'][$cid]['weight'];
+      $node->webform['components'][$cid]['mandatory'] = $form_state['values']['components'][$cid]['mandatory'];
+      $node->webform['components'][$cid]['pid'] = $form_state['values']['components'][$cid]['pid'];
+    }
+  }
+
+  if ($changes) {
+    node_save($node);
+  }
+
+  drupal_set_message(t('The component positions and mandatory values have been updated.'));
+}
+
+/**
+ * Submit handler for webform_components_form() that adds a new component.
+ */
+function webform_components_form_add_submit($form, &$form_state) {
+  $node = node_load($form_state['values']['nid']);
+
+  $component = $form_state['values']['add'];
+  $form_state['redirect'] = array('node/' . $node->nid . '/webform/components/new/' . $component['type'], array('query' => array('name' => $component['name'], 'mandatory' => $component['mandatory'], 'pid' => $component['pid'], 'weight' => $component['weight'])));
+}
+
+/**
+ * Form to configure a webform component.
+ */
+function webform_component_edit_form($form, $form_state, $node, $component, $clone = FALSE) {
+  drupal_set_title(t('Edit component: @name', array('@name' => $component['name'])), PASS_THROUGH);
+
+  $form['#attached']['library'][] = array('webform', 'admin');
+  $form['#tree'] = TRUE;
+
+  // Print the correct field type specification.
+  // We always need: name and description.
+  $form['type'] = array(
+    '#type' => 'value',
+    '#value' => $component['type'],
+  );
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+  $form['cid'] = array(
+    '#type' => 'value',
+    '#value' => isset($component['cid']) ? $component['cid'] : NULL,
+  );
+  $form['clone'] = array(
+    '#type' => 'value',
+    '#value' => $clone,
+  );
+
+  if (webform_component_feature($component['type'], 'title')) {
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#default_value' => $component['name'],
+      '#title' => t('Label'),
+      '#description' => t('This is used as a descriptive label when displaying this form element.'),
+      '#required' => TRUE,
+      '#weight' => -10,
+      '#maxlength' => 255,
+    );
+  }
+
+  $form['form_key'] = array(
+    '#type' => 'textfield',
+    '#default_value' => empty($component['form_key']) ? _webform_safe_name($component['name']) : $component['form_key'],
+    '#title' => t('Field Key'),
+    '#description' => t('Enter a machine readable key for this form element. May contain only alphanumeric characters and underscores. This key will be used as the name attribute of the form element. This value has no effect on the way data is saved, but may be helpful if doing custom form processing.'),
+    '#required' => TRUE,
+    '#weight' => -9,
+  );
+
+  $form['extra'] = array();
+  if (webform_component_feature($component['type'], 'description')) {
+    $form['extra']['description'] = array(
+      '#type' => 'textarea',
+      '#default_value' => isset($component['extra']['description']) ? $component['extra']['description'] : '',
+      '#title' => t('Description'),
+      '#description' => t('A short description of the field used as help for the user when he/she uses the form.') . theme('webform_token_help'),
+      '#weight' => -1,
+    );
+  }
+
+  // Display settings.
+  $form['display'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Display'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => 8,
+  );
+  if (webform_component_feature($component['type'], 'title_display')) {
+    if (webform_component_feature($component['type'], 'title_inline')) {
+      $form['display']['title_display'] = array(
+        '#type' => 'select',
+        '#title' => t('Label display'),
+        '#default_value' => !empty($component['extra']['title_display']) ? $component['extra']['title_display'] : 'before',
+        '#options' => array(
+          'before' => t('Above'),
+          'inline' => t('Inline'),
+          'none' => t('None'),
+        ),
+        '#description' => t('Determines the placement of the component\'s label.'),
+      );
+    }
+    else {
+      $form['display']['title_display'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Hide label'),
+        '#default_value' => strcmp($component['extra']['title_display'], 'none') === 0,
+        '#return_value' => 'none',
+        '#description' => t('Do not display the label of this component.'),
+      );
+    }
+    $form['display']['title_display']['#weight'] = 8;
+    $form['display']['title_display']['#parents'] = array('extra', 'title_display');
+  }
+
+  if (webform_component_feature($component['type'], 'private')) {
+    // May user mark fields as Private?
+    $form['display']['private'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Private'),
+      '#default_value' => ($component['extra']['private'] == '1' ? TRUE : FALSE),
+      '#description' => t('Private fields are shown only to users with results access.'),
+      '#weight' => 45,
+      '#parents' => array('extra', 'private'),
+      '#disabled' => empty($node->nid) ? FALSE : !webform_results_access($node),
+    );
+  }
+
+  // Validation settings.
+  $form['validation'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Validation'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => 5,
+  );
+  if (webform_component_feature($component['type'], 'required')) {
+    $form['validation']['mandatory'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Mandatory'),
+      '#default_value' => ($component['mandatory'] == '1' ? TRUE : FALSE),
+      '#description' => t('Check this option if the user must enter a value.'),
+      '#weight' => -1,
+      '#parents' => array('mandatory'),
+    );
+  }
+
+  // Position settings, only shown if JavaScript is disabled.
+  $form['position'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Position'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#tree' => FALSE,
+    '#weight' => 20,
+    '#attributes' => array('class' => array('webform-position')),
+  );
+
+  $options = array('0' => t('Root'));
+  foreach ($node->webform['components'] as $existing_cid => $value) {
+    if (webform_component_feature($value['type'], 'group') && (!isset($component['cid']) || $existing_cid != $component['cid'])) {
+      $options[$existing_cid] = $value['name'];
+    }
+  }
+  $form['position']['pid'] = array(
+    '#type' => 'select',
+    '#title' => t('Parent'),
+    '#default_value' => $component['pid'],
+    '#description' => t('Optional. You may organize your form by placing this component inside another fieldset.'),
+    '#options' => $options,
+    '#access' => count($options) > 1,
+    '#weight' => 3,
+  );
+  $form['position']['weight'] = array(
+    '#type' => 'textfield',
+    '#size' => 4,
+    '#title' => t('Weight'),
+    '#default_value' => $component['weight'],
+    '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
+    '#weight' => 4,
+  );
+
+  // Add conditional fields.
+  $conditional_components = array();
+  $counter = 0;
+  $last_pagebreak_slice = 0;
+  foreach ($node->webform['components'] as $cid => $test_component) {
+    // Only components before the pagebreak can be considered.
+    if ($test_component['type'] == 'pagebreak') {
+      $last_pagebreak_slice = $counter;
+    }
+    if (isset($component['cid']) && $cid == $component['cid']) {
+      break;
+    }
+    if (webform_component_feature($test_component['type'], 'conditional')) {
+      $conditional_components[$cid] = $test_component;
+      $counter++;
+    }
+  }
+  if ($component['type'] != 'pagebreak') {
+    $fieldset_description = t('Create a rule to control whether or not to skip this page.');
+  }
+  else {
+    $fieldset_description = t('Create a rule to control whether or not to show this form element.');
+  }
+  $conditional_components = array_slice($conditional_components, 0, $last_pagebreak_slice, TRUE);
+  $form['conditional'] = array(
+    '#weight' => 10,
+    '#type' => 'fieldset',
+    '#title' => t('Conditional rules'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('Create a rule to control whether or not to show this form element.'),
+    '#tree' => FALSE,
+  );
+  $form['conditional']['extra'] = array(
+    '#tree' => TRUE,
+  );
+  $form['conditional']['extra']['conditional_component'] = array(
+    '#type' => 'select',
+    '#title' => t('Component'),
+    '#options' => webform_component_list($node, $conditional_components, FALSE, TRUE),
+    '#description' => t('Select another component to decide whether to show or hide this component. You can only select components occurring before the most recent pagebreak.'),
+    '#default_value' => $component['extra']['conditional_component'],
+  );
+  $form['conditional']['extra']['conditional_operator'] = array(
+    '#type' => 'select',
+    '#title' => t('Operator'),
+    '#options' => array(
+      '=' => t('Is one of'),
+      '!=' => t('Is not one of')
+    ),
+    '#description' => t('Determines whether the list below is inclusive or exclusive.'),
+    '#default_value' => $component['extra']['conditional_operator'],
+  );
+  $form['conditional']['extra']['conditional_values'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Values'),
+    '#description' => t('List values, one per line, that will trigger this action. If you leave this blank, this component will always display.'),
+    '#default_value' => $component['extra']['conditional_values'],
+  );
+  if (empty($conditional_components)) {
+    $form['conditional']['#access'] = FALSE;
+  }
+
+  // Add the fields specific to this component type:
+  $additional_form_elements = (array) webform_component_invoke($component['type'], 'edit', $component);
+  if (empty($additional_form_elements)) {
+    drupal_set_message(t('The webform component of type @type does not have an edit function defined.', array('@type' => $component['type'])));
+  }
+
+  // Merge the additional fields with the current fields:
+  if (isset($additional_form_elements['extra'])) {
+    $form['extra'] = array_merge($form['extra'], $additional_form_elements['extra']);
+    unset($additional_form_elements['extra']);
+  }
+  if (isset($additional_form_elements['position'])) {
+    $form['position'] = array_merge($form['position'], $additional_form_elements['position']);
+    unset($additional_form_elements['position']);
+  }
+  if (isset($additional_form_elements['display'])) {
+    $form['display'] = array_merge($form['display'], $additional_form_elements['display']);
+    unset($additional_form_elements['display']);
+  }
+  if (isset($additional_form_elements['validation'])) {
+    $form['validation'] = array_merge($form['validation'], $additional_form_elements['validation']);
+    unset($additional_form_elements['validation']);
+  }
+  elseif (count(element_children($form['validation'])) == 0) {
+    unset($form['validation']);
+  }
+  $form = array_merge($form, $additional_form_elements);
+
+  // Add the submit button.
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save component'),
+    '#weight' => 50,
+  );
+
+  return $form;
+}
+
+/**
+ * Field name validation for the webform unique key. Must be alphanumeric.
+ */
+function webform_component_edit_form_validate($form, &$form_state) {
+  $node = node_load($form_state['values']['nid']);
+
+  if (!preg_match('/^[a-z0-9_]+$/i', $form_state['values']['form_key'])) {
+    form_set_error('form_key', t('The field key %field_key is invalid. Please include only lowercase alphanumeric characters and underscores.', array('%field_key' => $form_state['values']['form_key'])));
+  }
+
+  foreach ($node->webform['components'] as $cid => $component) {
+    if (($component['cid'] != $form_state['values']['cid'] || $form_state['values']['clone']) && ($component['pid'] == $form_state['values']['pid']) && (strcasecmp($component['form_key'], $form_state['values']['form_key']) == 0)) {
+      form_set_error('form_key', t('The field key %field_key is already in use by the field labeled %existing_field. Please use a unique key.', array('%field_key' => $form_state['values']['form_key'], '%existing_field' => $component['name'])));
+    }
+  }
+}
+
+/**
+ * Submit handler for webform_component_edit_form().
+ */
+function webform_component_edit_form_submit($form, &$form_state) {
+  // Ensure a webform record exists.
+  $node = node_load($form_state['values']['nid']);
+  webform_ensure_record($node);
+
+  // Remove empty extra values.
+  if (isset($form_state['values']['extra'])) {
+    foreach ($form_state['values']['extra'] as $key => $value) {
+      if ($value  === '' && !isset($form['display'][$key]['#options'][''])) {
+        unset($form_state['values']['extra'][$key]);
+      }
+    }
+  }
+
+  // Remove empty attribute values.
+  if (isset($form_state['values']['extra']['attributes'])) {
+    foreach ($form_state['values']['extra']['attributes'] as $key => $value) {
+      if ($value === '') {
+        unset($form_state['values']['extra']['attributes'][$key]);
+      }
+    }
+  }
+
+  if ($form_state['values']['clone']) {
+    webform_component_clone($node, $form_state['values']);
+    drupal_set_message(t('Component %name cloned.', array('%name' => $form_state['values']['name'])));
+  }
+  elseif (!empty($form_state['values']['cid'])) {
+    webform_component_update($form_state['values']);
+    drupal_set_message(t('Component %name updated.', array('%name' => $form_state['values']['name'])));
+  }
+  else {
+    $cid = webform_component_insert($form_state['values']);
+    drupal_set_message(t('New component %name added.', array('%name' => $form_state['values']['name'])));
+  }
+
+  // Since Webform components have been updated but the node itself has not
+  // been saved, it is necessary to explicitly clear the cache to make sure
+  // the updated webform is visible to anonymous users.
+  cache_clear_all();
+
+  // Clear the entity cache if Entity Cache module is installed.
+  if (module_exists('entitycache')) {
+    cache_clear_all($node->nid, 'cache_entity_node');
+  }
+
+  $form_state['redirect'] = array('node/' . $node->nid . '/webform/components', isset($cid) ? array('query' => array('cid' => $cid)) : array());
+}
+
+/**
+ * Form to confirm deletion of a component.
+ */
+function webform_component_delete_form($form, $form_state, $node, $component) {
+  $cid = $component['cid'];
+
+  $form = array();
+  $form['node'] = array(
+    '#type' => 'value',
+    '#value' => $node,
+  );
+  $form['component'] = array(
+    '#type' => 'value',
+    '#value' => $component,
+  );
+
+  if (webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
+    $question = t('Delete the %name fieldset?', array('%name' => $node->webform['components'][$cid]['name']));
+    $description = t('This will immediately delete the %name fieldset and all children elements within %name from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
+  }
+  else {
+    $question = t('Delete the %name component?', array('%name' => $node->webform['components'][$cid]['name']));
+    $description = t('This will immediately delete the %name component from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
+  }
+
+  return confirm_form($form, $question, 'node/' . $node->nid . '/webform/components', $description, t('Delete'));
+}
+
+/**
+ * Submit handler for webform_component_delete_form().
+ */
+function webform_component_delete_form_submit($form, &$form_state) {
+  // Delete the component.
+  $node = $form_state['values']['node'];
+  $component = $form_state['values']['component'];
+  webform_component_delete($node, $component);
+  drupal_set_message(t('Component %name deleted.', array('%name' => $component['name'])));
+
+  // Check if this webform still contains any information.
+  unset($node->webform['components'][$component['cid']]);
+  webform_check_record($node);
+
+  // Since Webform components have been updated but the node itself has not
+  // been saved, it is necessary to explicitly clear the cache to make sure
+  // the updated webform is visible to anonymous users.
+  cache_clear_all();
+
+  // Clear the entity cache if Entity Cache module is installed.
+  if (module_exists('entitycache')) {
+    cache_clear_all($node->nid, 'cache_entity_node');
+  }
+
+  $form_state['redirect'] = 'node/' . $node->nid . '/webform/components';
+}
+
+/**
+ * Insert a new component into the database.
+ *
+ * @param $component
+ *   A full component containing fields from the component form.
+ */
+function webform_component_insert(&$component) {
+  // Allow modules to modify the component before saving.
+  foreach (module_implements('webform_component_presave') as $module) {
+    $function = $module . '_webform_component_presave';
+    $function($component);
+  }
+
+  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
+  $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0;
+  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
+
+  if (!isset($component['cid'])) {
+    if (lock_acquire('webform_component_insert_' . $component['nid'], 5)) {
+      $next_id_query = db_select('webform_component')->condition('nid', $component['nid']);
+      $next_id_query->addExpression('MAX(cid) + 1', 'cid');
+      $component['cid'] = $next_id_query->execute()->fetchField();
+      if ($component['cid'] == NULL) {
+        $component['cid'] = 1;
+      }
+      lock_release('webform_component_insert_' . $component['nid']);
+    }
+    else {
+      watchdog('webform', 'A Webform component could not be saved because a timeout occurred while trying to acquire a lock for the node. Details: <pre>@component</pre>', array('@component' => print_r($component, TRUE)));
+      return FALSE;
+    }
+  }
+
+  $query = db_insert('webform_component')
+    ->fields(array(
+      'nid' => $component['nid'],
+      'cid' => $component['cid'],
+      'pid' => $component['pid'],
+      'form_key' => $component['form_key'],
+      'name' => $component['name'],
+      'type' => $component['type'],
+      'value' => (string) $component['value'],
+      'extra' => serialize($component['extra']),
+      'mandatory' => $component['mandatory'],
+      'weight' => $component['weight'],
+    ))
+    ->execute();
+
+  // Post-insert actions.
+  module_invoke_all('webform_component_insert', $component);
+
+  return $component['cid'];
+}
+
+/**
+ * Update an existing component with new values.
+ *
+ * @param $component
+ *   A full component containing a nid, cid, and all other fields from the
+ *   component form. Additional properties are stored in the extra array.
+ */
+function webform_component_update($component) {
+  // Allow modules to modify the component before saving.
+  foreach (module_implements('webform_component_presave') as $module) {
+    $function = $module . '_webform_component_presave';
+    $function($component);
+  }
+
+  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
+  $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0;
+  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
+  db_update('webform_component')
+    ->fields(array(
+      'pid' => $component['pid'],
+      'form_key' => $component['form_key'],
+      'name' => $component['name'],
+      'type' => $component['type'],
+      'value' => isset($component['value']) ? $component['value'] : '',
+      'extra' => serialize($component['extra']),
+      'mandatory' => $component['mandatory'],
+      'weight' => $component['weight']
+    ))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->execute();
+
+  // Post-update actions.
+  module_invoke_all('webform_component_update', $component);
+}
+
+function webform_component_delete($node, $component) {
+  // Check if a delete function is available for this component. If so,
+  // load all submissions and allow the component to delete each one.
+
+  webform_component_include($component['type']);
+  $delete_function = '_webform_delete_' . $component['type'];
+  if (function_exists($delete_function)) {
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+    $submissions = webform_get_submissions($node->nid);
+    foreach ($submissions as $submission) {
+      if (isset($submission->data[$component['cid']])) {
+        webform_component_invoke($component['type'], 'delete', $component, $submission->data[$component['cid']]['value']);
+      }
+    }
+  }
+
+  // Remove database entries.
+  db_delete('webform_component')
+    ->condition('nid', $node->nid)
+    ->condition('cid', $component['cid'])
+    ->execute();
+  db_delete('webform_submitted_data')
+    ->condition('nid', $node->nid)
+    ->condition('cid', $component['cid'])
+    ->execute();
+
+  // Delete all elements under this element.
+  $result = db_select('webform_component', 'c')
+    ->fields('c')
+    ->condition('nid', $node->nid)
+    ->condition('pid', $component['cid'])
+    ->execute();
+  foreach ($result as $row) {
+    $child_component = $node->webform['components'][$row->cid];
+    webform_component_delete($node, $child_component);
+  }
+
+  // Post-delete actions.
+  module_invoke_all('webform_component_delete', $component);
+}
+
+/**
+ * Recursively insert components into the database.
+ *
+ * @param $node
+ *   The node object containing the current webform.
+ * @param $component
+ *   A full component containing fields from the component form.
+ */
+function webform_component_clone(&$node, &$component) {
+  $original_cid = $component['cid'];
+  $component['cid'] = NULL;
+  $new_cid = webform_component_insert($component);
+  $component['cid'] = $new_cid;
+  if (webform_component_feature($component['type'], 'group')) {
+    foreach ($node->webform['components'] as $cid => $child_component) {
+      if ($child_component['pid'] == $original_cid) {
+        $child_component['pid'] = $new_cid;
+        webform_component_clone($node, $child_component);
+      }
+    }
+  }
+  return $new_cid;
+}
+
+/**
+ * Check if a component has a particular feature.
+ *
+ * @see hook_webform_component_info()
+ */
+function webform_component_feature($type, $feature) {
+  $components = webform_components();
+  $defaults = array(
+    'csv' => TRUE,
+    'default_value' => TRUE,
+    'description' => TRUE,
+    'email' => TRUE,
+    'email_address' => FALSE,
+    'email_name' => FALSE,
+    'required' => TRUE,
+    'title' => TRUE,
+    'title_display' => TRUE,
+    'title_inline' => TRUE,
+    'conditional' => TRUE,
+    'spam_analysis' => FALSE,
+    'group' => FALSE,
+    'attachment' => FALSE,
+    'private' => TRUE,
+  );
+  return isset($components[$type]['features'][$feature]) ? $components[$type]['features'][$feature] : !empty($defaults[$feature]);
+}
+
+/**
+ * Create a list of components suitable for a select list.
+ *
+ * @param $node
+ *   The webform node.
+ * @param $component_filter
+ *   Either an array of components, or a string containing a feature name (csv,
+ *   email, required, conditional) on which this list of components will be
+ *   restricted.
+ * @param $indent
+ *   Indent components placed under fieldsets with hyphens.
+ * @param $optgroups
+ *   Determine if pagebreaks should be converted to option groups in the
+ *   returned list of options.
+ */
+function webform_component_list($node, $component_filter = NULL, $indent = TRUE, $optgroups = FALSE) {
+  $options = array();
+  $page_names = array();
+
+  $components = is_array($component_filter) ? $component_filter : $node->webform['components'];
+  $feature = is_string($component_filter) ? $component_filter : NULL;
+
+  foreach ($components as $cid => $component) {
+    if (!isset($feature) || webform_component_feature($component['type'], $feature) || ($indent && webform_component_feature($component['type'], 'group'))) {
+      $prefix = '';
+      $page_num = $component['page_num'];
+      $page_index = 'p' . $page_num;
+      if ($indent && ($parent_count = count(webform_component_parent_keys($node, $component)) - 1)) {
+        $prefix = str_repeat('-', $parent_count);
+      }
+      if ($optgroups && $component['type'] == 'pagebreak') {
+        $page_names[$page_index] = $component['name'];
+      }
+      elseif ($optgroups && $page_num > 1) {
+        $options[$page_index][$cid] = $prefix . $component['name'];
+      }
+      else {
+        $options[$cid] = $prefix . $component['name'];
+      }
+    }
+  }
+
+  // Convert page breaks into optgroups.
+  if ($optgroups) {
+    $grouped_options = $options;
+    $options = array();
+    foreach ($grouped_options as $key => $values) {
+      if (is_array($values) && isset($page_names[$key])) {
+        $options[$page_names[$key]] = $values;
+      }
+      else {
+        $options[$key] = $values;
+      }
+    }
+  }
+
+  return $options;
+}
+
+/**
+ * A Form API process function to expand a component list into checkboxes.
+ */
+function webform_component_select($element) {
+  // Split the select list into checkboxes.
+  foreach ($element['#options'] as $key => $label) {
+    $label_length = strlen($label);
+    $label = preg_replace('/^(\-)+/', '', $label);
+    $indents = $label_length - strlen($label);
+    $element[$key] = array(
+      '#title' => $label,
+      '#type' => 'checkbox',
+      '#default_value' => array_search($key, $element['#value']) !== FALSE,
+      '#return_value' => $key,
+      '#parents' => array_merge($element['#parents'], array($key)),
+      '#indent' => $indents,
+    );
+  }
+
+  $element['#theme_wrappers'] = array();
+  $element['#type'] = 'webform_component_select';
+  $element['#theme'] = 'webform_component_select';
+  $element['#attached'] = array(
+    'library' => array(
+      array('webform', 'admin'),
+    ),
+    'js' => array(
+      'misc/tableselect.js' => array(),
+    ),
+  );
+
+  return $element;
+}
+
+/**
+ * Theme the contents of a Webform component select element.
+ */
+function theme_webform_component_select($variables) {
+  $element = $variables['element'];
+
+  $rows = array();
+  $header = array();
+  if (!isset($element['#all_checkbox']) || $element['#all_checkbox']) {
+    $header = array(array('class' => array('select-all'), 'data' => ' ' . t('Include all components')));
+  }
+  foreach (element_children($element) as $key) {
+    $rows[] = array(
+      theme('indentation', array('size' => $element[$key]['#indent'])) . drupal_render($element[$key]),
+    );
+  }
+
+  $element['#type'] = 'fieldset';
+  $element['#value'] = NULL;
+  $element['#attributes']['class'] = array('webform-component-select-table');
+  if (!isset($element['#collapsible']) || $element['#collapsible']) {
+    $element['#attributes']['class'][] = 'collapsible';
+  }
+  if (!isset($element['#collapsed']) || $element['#collapsed']) {
+    $element['#attributes']['class'][] = 'collapsed';
+  }
+
+  if (empty($rows)) {
+    $element['#children'] = t('No available components.');
+  }
+  else {
+    $element['#children'] = '<div class="webform-component-select-wrapper">' . theme('table', array('header' => $header, 'rows' => $rows)) . '</div>';
+  }
+
+  return theme('fieldset', array('element' => $element));
+}
+
+/**
+ * Find a components parents within a node.
+ */
+function webform_component_parent_keys($node, $component) {
+  $parents = array($component['form_key']);
+  $pid = $component['pid'];
+  while ($pid) {
+    $parents[] = $node->webform['components'][$pid]['form_key'];
+    $pid = $node->webform['components'][$pid]['pid'];
+  }
+  return array_reverse($parents);
+}
+
+/**
+ * Populate a component with the defaults for that type.
+ */
+function webform_component_defaults(&$component) {
+  if ($defaults = webform_component_invoke($component['type'], 'defaults')) {
+    foreach ($defaults as $key => $default) {
+      if (!isset($component[$key])) {
+        $component[$key] = $default;
+      }
+    }
+    foreach ($defaults['extra'] as $extra => $default) {
+      if (!isset($component['extra'][$extra])) {
+        $component['extra'][$extra] = $default;
+      }
+    }
+    $component['extra'] += array(
+      'conditional_component' => '',
+      'conditional_operator' => '=',
+      'conditional_values' => '',
+    );
+  }
+}
+
+/**
+ * Validate an element value is unique with no duplicates in the database.
+ */
+function webform_validate_unique($element, $form_state) {
+  if ($element['#value'] !== '') {
+    $nid = $form_state['values']['details']['nid'];
+    $sid = $form_state['values']['details']['sid'];
+    $query = db_select('webform_submitted_data')
+      ->fields('webform_submitted_data', array('sid'))
+      ->condition('nid', $nid)
+      ->condition('cid', $element['#webform_component']['cid'])
+      ->condition('data', $element['#value'])
+      ->range(0, 1); // More efficient than using countQuery() for data checks.
+    if ($sid) {
+      $query->condition('sid', $sid, '<>');
+    }
+    $count = $query->execute()->fetchField();
+    if ($count) {
+      form_error($element, t('The value %value has already been submitted once for the %title field. You may have already submitted this form, or you need to use a different value.', array('%value' => $element['#value'], '%title' => $element['#title'])));
+    }
+  }
+}
diff -Naurw webform_old/includes/webform.emails.inc webform/includes/webform.emails.inc
--- webform_old/includes/webform.emails.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.emails.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,578 @@
+<?php
+
+/**
+ * @file
+ * Provides interface and database handling for e-mail settings of a webform.
+ *
+ * @author Nathan Haug <nate@lullabot.com>
+ */
+
+/**
+ * Overview form of all components for this webform.
+ */
+function webform_emails_form($form, $form_state, $node) {
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  $form['#attached']['library'][] = array('webform', 'admin');
+
+  $form['#tree'] = TRUE;
+  $form['#node'] = $node;
+  $form['components'] = array();
+
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+
+  foreach ($node->webform['emails'] as $eid => $email) {
+    $email_addresses = array_filter(explode(',', check_plain($email['email'])));
+    foreach ($email_addresses as $key => $email_address) {
+      $email_addresses[$key] = webform_format_email_address($email_address, NULL, $node, NULL, FALSE);
+    }
+
+    $form['emails'][$eid]['email'] = array(
+      '#markup' => implode('<br />', $email_addresses),
+    );
+    $form['emails'][$eid]['subject'] = array(
+      '#markup' => check_plain(webform_format_email_subject($email['subject'], $node)),
+    );
+    $form['emails'][$eid]['from'] = array(
+      '#markup' => check_plain(webform_format_email_address($email['from_address'], $email['from_name'], $node, NULL, FALSE)),
+    );
+  }
+
+  $form['add'] = array(
+    '#theme' => 'webform_email_add_form',
+    '#tree' => FALSE,
+  );
+
+  $form['add']['email_option'] = array(
+    '#type' => 'radios',
+    '#options' => array(
+      'custom' => t('Address'),
+      'component' => t('Component value'),
+    ),
+    '#default_value' => 'custom',
+  );
+
+  $form['add']['email_custom'] = array(
+    '#type' => 'textfield',
+    '#size' => 24,
+    '#maxlength' => 500,
+  );
+
+  $form['add']['email_component'] = array(
+    '#type' => 'select',
+    '#options' => webform_component_list($node, 'email_address', FALSE),
+  );
+
+  if (empty($form['add']['email_component']['#options'])) {
+    $form['add']['email_component']['#options'][''] = t('No available components');
+    $form['add']['email_component']['#disabled'] = TRUE;
+  }
+
+  $form['add_button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+    '#weight' => 45,
+  );
+
+  $form['#validate'] = array('webform_email_address_validate');
+
+  return $form;
+}
+
+/**
+ * Theme the node components form. Use a table to organize the components.
+ *
+ * @param $form
+ *   The form array.
+ * @return
+ *   Formatted HTML form, ready for display.
+ */
+function theme_webform_emails_form($variables) {
+  $form = $variables['form'];
+  $node = $form['#node'];
+
+  $header = array(t('E-mail to'), t('Subject'), t('From'), array('data' => t('Operations'), 'colspan' => 2));
+  $rows = array();
+
+  if (!empty($form['emails'])) {
+    foreach (element_children($form['emails']) as $eid) {
+      // Add each component to a table row.
+      $rows[] = array(
+        drupal_render($form['emails'][$eid]['email']),
+        drupal_render($form['emails'][$eid]['subject']),
+        drupal_render($form['emails'][$eid]['from']),
+        l(t('Edit'), 'node/' . $node->nid . '/webform/emails/' . $eid),
+        l(t('Delete'), 'node/' . $node->nid . '/webform/emails/' . $eid . '/delete'),
+      );
+    }
+  }
+  else {
+    $rows[] = array(array('data' => t('Currently not sending e-mails, add an e-mail recipient below.'), 'colspan' => 5));
+  }
+
+  // Add a row containing form elements for a new item.
+  $row_data = array(
+    array('colspan' => 3, 'data' => drupal_render($form['add'])),
+    array('colspan' => 2, 'data' => drupal_render($form['add_button'])),
+  );
+  $rows[] = array('data' => $row_data, 'class' => array('webform-add-form'));
+
+  $output = '';
+  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'webform-emails')));
+  $output .= drupal_render_children($form);
+  return $output;
+}
+
+/**
+ * Theme the add new e-mail settings form on the node/x/webform/emails page.
+ */
+function theme_webform_email_add_form($variables) {
+  $form = $variables['form'];
+
+  // Add a default value to the custom e-mail textfield.
+  $form['email_custom']['#attributes']['rel'] = t('email@example.com');
+  $form['email_custom']['#attributes']['class'] = array('webform-set-active', 'webform-default-value');
+  $form['email_option']['custom']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['email_option']['custom']['#inline_element'] = drupal_render($form['email_custom']);
+
+  // Render the component value.
+  $form['email_component']['#attributes']['class'] = array('webform-set-active');
+  $form['email_option']['component']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['email_option']['component']['#inline_element'] = drupal_render($form['email_component']);
+
+  return drupal_render_children($form);
+}
+
+/**
+ * Submit handler for webform_emails_form().
+ */
+function webform_emails_form_submit($form, &$form_state) {
+  if ($form_state['values']['email_option'] == 'custom') {
+    $email = $form_state['values']['email_custom'];
+  }
+  else {
+    $email = $form_state['values']['email_component'];
+  }
+  $form_state['redirect'] = array('node/' . $form['#node']->nid . '/webform/emails/new', array('query' => array('option' => $form_state['values']['email_option'], 'email' => trim($email))));
+}
+
+/**
+ * Form for configuring an e-mail setting and template.
+ */
+function webform_email_edit_form($form, $form_state, $node, $email = array()) {
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  $form['#attached']['library'][] = array('webform', 'admin');
+  $form['#attached']['js'][] = array('data' => array('webform' => array('revertConfirm' => t('Are you sure you want to revert any changes to your template back to the default?'))), 'type' => 'setting');
+
+  $form['#tree'] = TRUE;
+  $form['node'] = array(
+    '#type' => 'value',
+    '#value' => $node,
+  );
+  $form['eid'] = array(
+    '#type' => 'value',
+    '#value' => isset($email['eid']) ? $email['eid'] : NULL,
+  );
+
+  // All these fields work essentially the same, with a radio button set,
+  // a textfield for custom values, and a select list for a component.
+  foreach (array('email', 'subject', 'from_address', 'from_name') as $field) {
+    switch ($field) {
+      case 'email':
+        $default_value = NULL;
+        $title = t('E-mail to address');
+        $description = t('Form submissions will be e-mailed to this address. Any email, select, or hidden form element may be selected as the recipient address. Multiple e-mail addresses may be separated by commas.');
+        break;
+      case 'subject':
+        $default_value = _webform_filter_values(webform_variable_get('webform_default_subject'), $node);
+        $title = t('E-mail subject');
+        $description = t('Any textfield, select, or hidden form element may be selected as the subject for e-mails.');
+        break;
+      case 'from_address':
+        $default_value = _webform_filter_values(webform_variable_get('webform_default_from_address'), $node);
+        $title = t('E-mail from address');
+        $description = t('Any email, select, or hidden form element may be selected as the sender\'s e-mail address.');
+        break;
+      case 'from_name':
+        $default_value = _webform_filter_values(webform_variable_get('webform_default_from_name'), $node);
+        $title = t('E-mail from name');
+        $description = t('Any textfield, select, or hidden form element may be selected as the sender\'s name for e-mails.');
+        break;
+    }
+
+    $form[$field . '_option'] = array(
+      '#title' => $title,
+      '#type' => 'radios',
+      '#default_value' => is_numeric($email[$field]) ? 'component' : ((empty($default_value) || ($email[$field] != 'default' && isset($email[$field]))) ? 'custom' : 'default'),
+      '#description' => $description,
+    );
+    if (!empty($default_value)) {
+      $form[$field . '_option']['#options']['default'] = t('Default: %value', array('%value' => $default_value));
+    }
+    $form[$field . '_option']['#options']['custom'] = t('Custom');
+    $form[$field . '_option']['#options']['component'] = t('Component');
+
+    $form[$field . '_custom'] = array(
+      '#type' => 'textfield',
+      '#size' => 40,
+      '#default_value' => (!is_numeric($email[$field]) && $email[$field] != 'default') ? $email[$field] : NULL,
+      '#maxlength' => $field == 'email' ? 500 : 255,
+    );
+    $options = webform_component_list($node, $field == 'from_address' || $field == 'email' ? 'email_address' : 'email_name', FALSE);
+    $form[$field . '_component'] = array(
+      '#type' => 'select',
+      '#default_value' =>  is_numeric($email[$field]) ? $email[$field] : NULL,
+      '#options' => empty($options) ? array('' => t('No available components')) : $options,
+      '#disabled' => empty($options) ? TRUE : FALSE,
+      '#weight' => 6,
+    );
+  }
+
+  // Do not show the "E-mail from name" if using the short e-mail format.
+  if (variable_get('webform_email_address_format', 'long') == 'short') {
+    $form['from_name_option']['#access'] = FALSE;
+    $form['from_name_custom']['#access'] = FALSE;
+    $form['from_name_component']['#access'] = FALSE;
+  }
+
+  // Add the template fieldset.
+  $form['template'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('E-mail template'),
+    '#collapsible' => TRUE,
+    '#collapsed' => !empty($email['cid']) && empty($email['template']),
+    '#description' => t('An e-mail template can customize the display of e-mails.'),
+    '#weight' => 15,
+    '#tree' => FALSE,
+    '#attributes' => array('id' => 'webform-template-fieldset'),
+  );
+
+  $form['template']['template_option'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      'default' => t('Default template'),
+      'custom' => t('Custom template'),
+    ),
+    '#default_value' => $email['template'] == 'default' ? 'default' : 'custom',
+  );
+
+  $default_template = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), array('node' => $node, 'email' => $email));
+  $template = $email['template'] == 'default' ? $default_template : $email['template'];
+  $form['template']['template'] = array(
+    '#type' => 'textarea',
+    '#rows' => max(10, min(20, count(explode("\n", $template)))),
+    '#default_value' => $template,
+    '#wysiwyg' => webform_email_html_capable() ? NULL : FALSE,
+  );
+
+  $form['template']['html'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Send e-mail as HTML'),
+    '#default_value' => $email['html'],
+    '#access' => webform_email_html_capable() && !variable_get('webform_format_override', 0),
+  );
+
+  $form['template']['attachments'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Include files as attachments'),
+    '#default_value' => $email['attachments'],
+    '#access' => webform_email_html_capable(),
+  );
+
+  $form['template']['tokens'] = array(
+    '#markup' => theme('webform_token_help', array('groups' => 'all')),
+  );
+
+  $form['template']['components'] = array(
+    '#type' => 'select',
+    '#title' => t('Included e-mail values'),
+    '#options' => webform_component_list($node, 'email', TRUE),
+    '#default_value' => array_diff(array_keys($node->webform['components']), $email['excluded_components']),
+    '#multiple' => TRUE,
+    '#size' => 10,
+    '#description' => t('The selected components will be included in the %email_values token. Individual values may still be printed if explicitly specified as a %email[key] in the template.'),
+    '#process' => array('webform_component_select'),
+  );
+
+  // TODO: Allow easy re-use of existing templates.
+  $form['templates']['#tree'] = TRUE;
+  $form['templates']['default'] = array(
+    '#type' => 'textarea',
+    '#value' => $default_template,
+    '#resizable' => FALSE,
+    '#weight' => 19,
+    '#wysiwyg' => FALSE,
+  );
+
+  // Add the submit button.
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save e-mail settings'),
+    '#weight' => 20,
+  );
+
+  $form['#validate'] = array('webform_email_address_validate', 'webform_email_edit_form_validate');
+
+  return $form;
+}
+
+/**
+ * Theme the Webform mail settings section of the node form.
+ */
+function theme_webform_email_edit_form($variables) {
+  $form = $variables['form'];
+
+  // Loop through fields, rendering them into radio button options.
+  foreach (array('email', 'subject', 'from_address', 'from_name') as $field) {
+    foreach (array('custom', 'component') as $option) {
+      $form[$field . '_' . $option]['#attributes']['class'] = array('webform-set-active');
+      $form[$field . '_option'][$option]['#theme_wrappers'] = array('webform_inline_radio');
+      $form[$field . '_option'][$option]['#inline_element'] = drupal_render($form[$field . '_' . $option]);
+    }
+    if (isset($form[$field . '_option']['#options']['default'])) {
+      $form[$field . '_option']['default']['#theme_wrappers'] = array('webform_inline_radio');
+    }
+  }
+
+  $details = '';
+  $details .= drupal_render($form['subject_option']);
+  $details .= drupal_render($form['from_address_option']);
+  $details .= drupal_render($form['from_name_option']);
+  $form['details'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('E-mail header details'),
+    '#weight' => 10,
+    '#children' => $details,
+    '#collapsible' => FALSE,
+    '#parents' => array('details'),
+    '#groups' => array('details' => array()),
+    '#attributes' => array(),
+  );
+
+  // Ensure templates are completely hidden.
+  $form['templates']['#prefix'] = '<div id="webform-email-templates" style="display: none">';
+  $form['templates']['#suffix'] = '</div>';
+
+  // Re-sort the elements since we added the details fieldset.
+  $form['#sorted'] = FALSE;
+  $children = element_children($form, TRUE);
+  return drupal_render_children($form, $children);
+}
+
+/**
+ * Validate handler for webform_email_edit_form() and webform_emails_form().
+ */
+function webform_email_address_validate($form, &$form_state) {
+  if ($form_state['values']['email_option'] == 'custom') {
+    $email = trim($form_state['values']['email_custom']);
+    if (empty($email)) {
+      form_set_error('email_custom', t('When adding a new custom e-mail, the e-mail field is required.'));
+    }
+    else {
+      $emails = array_filter(explode(',', $email));
+      foreach ($emails as $email) {
+        if (!valid_email_address(trim($email))) {
+          form_set_error('email_custom', t('The entered e-mail address "@email" does not appear valid.', array('@email' => $email)));
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Validate handler for webform_email_edit_form().
+ */
+function webform_email_edit_form_validate($form, &$form_state) {
+  if ($form_state['values']['from_address_option'] == 'custom' && !valid_email_address($form_state['values']['from_address_custom'])) {
+    form_set_error('from_address_custom', t('The entered e-mail address "@email" does not appear valid.', array('@email' => $form_state['values']['from_address_custom'])));
+  }
+}
+
+/**
+ * Submit handler for webform_email_edit_form().
+ */
+function webform_email_edit_form_submit($form, &$form_state) {
+  // Ensure a webform record exists.
+  $node = $form_state['values']['node'];
+  webform_ensure_record($node);
+
+  // Merge the e-mail, name, address, and subject options into single values.
+  $email = array(
+    'eid' => $form_state['values']['eid'],
+    'nid' => $node->nid,
+  );
+
+  foreach (array('email', 'from_name', 'from_address', 'subject') as $field) {
+    $option = $form_state['values'][$field . '_option'];
+    if ($option == 'default') {
+      $email[$field] = 'default';
+    }
+    else {
+      $email[$field] = $form_state['values'][$field . '_' . $option];
+    }
+  }
+
+  // Ensure templates are unaffected by differences in line breaks.
+  $form_state['values']['template'] = str_replace(array("\r", "\n"), array('', "\n"), $form_state['values']['template']);
+  $form_state['values']['templates']['default'] = str_replace(array("\r", "\n"), array('', "\n"), $form_state['values']['templates']['default']);
+
+  // Set the template value.
+  // TODO: Support reuse of templates.
+  if (strcmp(trim($form_state['values']['templates']['default']), trim($form_state['values']['template'])) == 0) {
+    $email['template'] = 'default';
+  }
+  else {
+    $email['template'] = $form_state['values']['template'];
+  }
+
+  // Save the attachment and HTML options provided by MIME mail.
+  $email['html'] = empty($form_state['values']['html']) ? 0 : 1;
+  $email['attachments'] = empty($form_state['values']['attachments']) ? 0 : 1;
+
+  // Save the list of included components.
+  // We actually maintain an *exclusion* list, so any new components will
+  // default to being included in the %email_values token until unchecked.
+  $included = array_keys(array_filter((array) $form_state['values']['components']));
+  $excluded = array_diff(array_keys($node->webform['components']), $included);
+  $email['excluded_components'] = $excluded;
+
+  if (empty($form_state['values']['eid'])) {
+    drupal_set_message(t('Email settings added.'));
+    $form_state['values']['eid'] = webform_email_insert($email);
+  }
+  else {
+    drupal_set_message(t('Email settings updated.'));
+    webform_email_update($email);
+  }
+
+  // Clear the entity cache if Entity Cache module is installed.
+  if (module_exists('entitycache')) {
+    cache_clear_all($node->nid, 'cache_entity_node');
+  }
+
+  $form_state['redirect'] = array('node/' . $node->nid . '/webform/emails');
+}
+
+/**
+ * Form for deleting an e-mail setting.
+ */
+function webform_email_delete_form($form, $form_state, $node, $email) {
+  $eid = $email['eid'];
+
+  $form['node'] = array(
+    '#type' => 'value',
+    '#value' => $node,
+  );
+  $form['email'] = array(
+    '#type' => 'value',
+    '#value' => $email,
+  );
+
+  $question = t('Delete e-mail settings?');
+  if (is_numeric($email['email'])) {
+    $description = t('This will immediately delete the e-mail settings based on the @component component.', array('@component' => $email['email']));
+  }
+  else {
+    $description = t('This will immediately delete the e-mail settings sending to the @address address.', array('@address' => $email['email']));
+  }
+
+  return confirm_form($form, $question, 'node/' . $node->nid . '/webform/emails', $description, t('Delete'));
+}
+
+/**
+ * Submit handler for webform_email_delete_form().
+ */
+function webform_email_delete_form_submit($form, &$form_state) {
+  // Delete the e-mail settings.
+  $node = $form_state['values']['node'];
+  $email = $form_state['values']['email'];
+  webform_email_delete($node, $email);
+  drupal_set_message(t('E-mail settings deleted.'));
+
+  // Check if this webform still contains any information.
+  unset($node->webform['emails'][$email['eid']]);
+  webform_check_record($node);
+
+  // Clear the entity cache if Entity Cache module is installed.
+  if (module_exists('entitycache')) {
+    cache_clear_all($node->nid, 'cache_entity_node');
+  }
+
+  $form_state['redirect'] = 'node/' . $node->nid . '/webform/emails';
+}
+
+/**
+ * Load an e-mail setting from the database or initialize a new e-mail.
+ */
+function webform_email_load($eid, $nid) {
+  $node = node_load($nid);
+  if ($eid == 'new') {
+    $email = array(
+      'email' => '',
+      'subject' => 'default',
+      'from_name' => 'default',
+      'from_address' => 'default',
+      'template' => 'default',
+      'excluded_components' => array(),
+      'html' => variable_get('webform_default_format', 0),
+      'attachments' => 0,
+    );
+  }
+  else {
+    $email = isset($node->webform['emails'][$eid]) ? $node->webform['emails'][$eid] : FALSE;
+    if (variable_get('webform_format_override', 0)) {
+      $email['html'] = variable_get('webform_default_format', 0);
+    }
+  }
+
+  return $email;
+}
+
+/**
+ * Insert a new e-mail setting into the database.
+ *
+ * @param $email
+ *   An array of settings for sending an e-mail.
+ */
+function webform_email_insert($email) {
+  // TODO: This is not race-condition safe. Switch to using transactions?
+  if (!isset($email['eid'])) {
+    $next_id_query = db_select('webform_emails')->condition('nid', $email['nid']);
+    $next_id_query->addExpression('MAX(eid) + 1', 'eid');
+    $email['eid'] = $next_id_query->execute()->fetchField();
+    if ($email['eid'] == NULL) {
+      $email['eid'] = 1;
+    }
+  }
+
+  $email['excluded_components'] = implode(',', $email['excluded_components']);
+  $success = drupal_write_record('webform_emails', $email);
+
+  return $success ? $email['eid'] : FALSE;
+}
+
+/**
+ * Update an existing e-mail setting with new values.
+ *
+ * @param $email
+ *   An array of settings for sending an e-mail containing a nid, eid, and all
+ *   other fields from the e-mail form.
+ */
+function webform_email_update($email) {
+  $email['excluded_components'] = implode(',', $email['excluded_components']);
+  return drupal_write_record('webform_emails', $email, array('nid', 'eid'));
+}
+
+/**
+ * Delete an e-mail setting.
+ */
+function webform_email_delete($node, $email) {
+  db_delete('webform_emails')
+    ->condition('nid', $node->nid)
+    ->condition('eid', $email['eid'])
+    ->execute();
+}
diff -Naurw webform_old/includes/webform.export.inc webform/includes/webform.export.inc
--- webform_old/includes/webform.export.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.export.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ *   Provides several different handlers for exporting webform results.
+ */
+
+/**
+ * Implements hook_webform_exporters().
+ *
+ * Defines the exporters this module implements.
+ *
+ * @return
+ *   An "array of arrays", keyed by content-types. The 'handler' slot
+ *   should point to the PHP class implementing this flag.
+ */
+function webform_webform_exporters() {
+  return array(
+    'delimited' => array(
+      'title' => t('Delimited text'),
+      'description' => t('A plain text file delimited by commas, tabs, or other characters.'),
+      'handler' => 'webform_exporter_delimited',
+    ),
+    'excel' => array(
+      'title' => t('Microsoft Excel'),
+      'description' => t('A file readable by Microsoft Excel.'),
+      'handler' => 'webform_exporter_excel',
+    ),
+  );
+}
+
+/**
+ * Return a list of exporters suitable for display in a select list.
+ */
+function webform_export_list() {
+  $exporters = webform_export_fetch_definition();
+  $list = array();
+  foreach ($exporters as $name => $exporter) {
+    $list[$name] = $exporter['title'];
+  }
+  return $list;
+}
+
+/**
+ * Returns a Webform exporter definition.
+ */
+function webform_export_fetch_definition($format = NULL) {
+  static $cache;
+  if (!isset($cache)) {
+    $cache = module_invoke_all('webform_exporters');
+  }
+
+  if (isset($format)) {
+    if (isset($cache[$format])) {
+      return $cache[$format];
+    }
+  }
+  else {
+    return $cache;
+  }
+}
+
+/**
+ * Instantiates a new Webform handler based on the format.
+ */
+function webform_export_create_handler($format, $options) {
+  $definition = webform_export_fetch_definition($format);
+  if (isset($definition) && class_exists($definition['handler'])) {
+    $handler = new $definition['handler']($options);
+  }
+  else  {
+    // TODO: Create a default broken exporter.
+    $handler = new webform_exporter_broken($options);
+  }
+
+  return $handler;
+}
+
+class webform_exporter {
+  function add_row(&$file_handle, $data) {
+  }
+
+  function set_headers($filename) {
+    drupal_add_http_header('Content-Type', 'application/force-download');
+    drupal_add_http_header('Pragma', 'public');
+    drupal_add_http_header('Cache-Control', 'max-age=0');
+  }
+
+  function bof(&$file_handle) {
+  }
+
+  function eof(&$file_handle) {
+  }
+}
+
+class webform_exporter_delimited extends webform_exporter {
+  var $delimiter;
+
+  function webform_exporter_delimited($options) {
+    $this->delimiter = isset($options['delimiter']) ? $options['delimiter'] : ',';
+    // Convert tabs.
+    if ($this->delimiter == '\t') {
+      $this->delimiter = "\t";
+    }
+  }
+
+  function bof(&$file_handle) {
+    $output = '';
+
+    // Include at BOM at the beginning of the file for Little Endian.
+    // This makes tab-separated imports work correctly in MS Excel.
+    if (function_exists('mb_convert_encoding') && $this->delimiter == "\t") {
+      $output = chr(255) . chr(254);
+    }
+    @fwrite($file_handle, $output);
+  }
+
+  function add_row(&$file_handle, $data) {
+    foreach ($data as $key => $value) {
+      // Escape inner quotes and wrap all contents in new quotes.
+      $data[$key] = '"' . str_replace('"', '""', $data[$key]) . '"';
+
+      // Remove <script> tags, which mysteriously cause Excel not to import.
+      $data[$key] = preg_replace('!<(/?script.*?)>!', '[$1]', $data[$key]);
+    }
+    $row = implode($this->delimiter, $data) . "\n";
+
+    if (function_exists('mb_convert_encoding')) {
+      $row = mb_convert_encoding($row, 'UTF-16LE', 'UTF-8');
+    }
+
+    @fwrite($file_handle, $row);
+  }
+
+  function set_headers($filename) {
+    parent::set_headers($filename);
+
+    // Convert tabs.
+    if ($this->delimiter == "\t") {
+      $extension = 'tsv';
+      $content_type = 'text/tab-separated-values';
+    }
+    else {
+      $extension = 'csv';
+      $content_type = 'text/csv';
+    }
+
+    drupal_add_http_header('Content-Type', $content_type);
+    drupal_add_http_header('Content-Disposition', "attachment; filename=$filename.$extension");
+  }
+}
+
+/**
+ * The Excel exporter currently is just a tab-delimited export.
+ */
+class webform_exporter_excel extends webform_exporter_delimited {
+  var $delimiter;
+
+  function webform_exporter_excel($options) {
+    $options['delimiter'] = '\t';
+    parent::webform_exporter_delimited($options);
+  }
+
+  function set_headers($filename) {
+    drupal_add_http_header('Content-Type', 'application/x-msexcel');
+    drupal_add_http_header('Content-Disposition', "attachment; filename=$filename.xls");
+    drupal_add_http_header('Pragma', 'public');
+    drupal_add_http_header('Cache-Control', 'max-age=0');
+  }
+}
diff -Naurw webform_old/includes/webform.options.inc webform/includes/webform.options.inc
--- webform_old/includes/webform.options.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.options.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * A collection of built-in select list options for Webform.
+ */
+
+/**
+ * Private implementation of hook_webform_select_options_info().
+ *
+ * @see webform_webform_select_options_info()
+ */
+function _webform_options_info() {
+  $items = array();
+
+  $items['days'] = array(
+    'title' => t('Days of the week'),
+    'options callback' => 'webform_options_days',
+    'file' => 'includes/webform.options.inc',
+  );
+
+  $items['countries'] = array(
+    'title' => t('Countries'),
+    'options callback' => 'webform_options_countries',
+    'file' => 'includes/webform.options.inc',
+  );
+
+  $items['united_states'] = array(
+    'title' => t('US states'),
+    'options callback' => 'webform_options_united_states',
+    'file' => 'includes/webform.options.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Option list containing the days of the week.
+ */
+function webform_options_days($component, $flat, $filter, $arguments) {
+  $days = array(
+    'sunday' => t('Sunday'),
+    'monday' => t('Monday'),
+    'tuesday' => t('Tuesday'),
+    'wednesday' => t('Wednesday'),
+    'thursday' => t('Thursday'),
+    'friday' => t('Friday'),
+    'saturday' => t('Saturday'),
+  );
+
+  // Order according to site settings for first day.
+  if ($first_day = variable_get('date_first_day', 0)) {
+    $week = array_splice($days, $first_day);
+    $days = array_merge($week, $days);
+  }
+
+  return $days;
+}
+
+/**
+ * Options list containing country names.
+ */
+function webform_options_countries($component, $flat, $filter, $arguments) {
+  include_once DRUPAL_ROOT . '/includes/locale.inc';
+  return country_get_list();
+}
+
+/**
+ * Options list containing United States states and territories.
+ */
+function webform_options_united_states($component, $flat, $filter, $arguments) {
+  return array(
+    'AL' => t('Alabama'),
+    'AK' => t('Alaska'),
+    'AS' => t('American Samoa'),
+    'AZ' => t('Arizona'),
+    'AR' => t('Arkansas'),
+    'CA' => t('California'),
+    'CO' => t('Colorado'),
+    'CT' => t('Connecticut'),
+    'DE' => t('Delaware'),
+    'DC' => t('District of Columbia'),
+    'FL' => t('Florida'),
+    'GA' => t('Georgia'),
+    'GU' => t('Guam'),
+    'HI' => t('Hawaii'),
+    'ID' => t('Idaho'),
+    'IL' => t('Illinois'),
+    'IN' => t('Indiana'),
+    'IA' => t('Iowa'),
+    'KS' => t('Kansas'),
+    'KY' => t('Kentucky'),
+    'LA' => t('Louisiana'),
+    'ME' => t('Maine'),
+    'MH' => t('Marshall Islands'),
+    'MD' => t('Maryland'),
+    'MA' => t('Massachusetts'),
+    'MI' => t('Michigan'),
+    'MN' => t('Minnesota'),
+    'MS' => t('Mississippi'),
+    'MO' => t('Missouri'),
+    'MT' => t('Montana'),
+    'NE' => t('Nebraska'),
+    'NV' => t('Nevada'),
+    'NH' => t('New Hampshire'),
+    'NJ' => t('New Jersey'),
+    'NM' => t('New Mexico'),
+    'NY' => t('New York'),
+    'NC' => t('North Carolina'),
+    'ND' => t('North Dakota'),
+    'MP' => t('Northern Marianas Islands'),
+    'OH' => t('Ohio'),
+    'OK' => t('Oklahoma'),
+    'OR' => t('Oregon'),
+    'PW' => t('Palau'),
+    'PA' => t('Pennsylvania'),
+    'PR' => t('Puerto Rico'),
+    'RI' => t('Rhode Island'),
+    'SC' => t('South Carolina'),
+    'SD' => t('South Dakota'),
+    'TN' => t('Tennessee'),
+    'TX' => t('Texas'),
+    'UT' => t('Utah'),
+    'VT' => t('Vermont'),
+    'VI' => t('Virgin Islands'),
+    'VA' => t('Virginia'),
+    'WA' => t('Washington'),
+    'WV' => t('West Virginia'),
+    'WI' => t('Wisconsin'),
+    'WY' => t('Wyoming'),
+  );
+}
diff -Naurw webform_old/includes/webform.pages.inc webform/includes/webform.pages.inc
--- webform_old/includes/webform.pages.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.pages.inc	2013-07-22 17:30:26.000000000 +0100
@@ -0,0 +1,414 @@
+<?php
+
+/**
+ * @file
+ *
+ * Menu callbacks and functions for configuring and editing webforms.
+ */
+
+/**
+ * Main configuration form for editing a webform node.
+ */
+function webform_configure_form($form, &$form_state, $node) {
+  $form['#attached']['library'][] = array('webform', 'admin');
+
+  $form['#node'] = $node;
+
+  $form['#submit'] = array(
+    'webform_configure_form_submit',
+    'webform_configure_form_submit_save',
+  );
+
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+
+  /* Start Edit Form */
+  $form['submission'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Submission settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => -4,
+  );
+
+  $form['submission']['confirmation'] = array(
+    '#type' => 'text_format',
+    '#title' => t('Confirmation message'),
+    '#description' => t('Message to be shown upon successful submission. If the redirection location is set to <em>Confirmation page</em> it will be shown on its own page, otherwise this displays as a message.'),
+    '#default_value' => $node->webform['confirmation'],
+    '#cols' => 40,
+    '#rows' => 10,
+    '#format' => $node->webform['confirmation_format'],
+    '#parents' => array('confirmation'),
+  );
+
+  // Redirection settings.
+  if (strpos($node->webform['redirect_url'], '<') === 0) {
+    $redirect = trim($node->webform['redirect_url'], '<>');
+    // Redirection is set to front page.
+    if ($redirect == 'front') {
+      $redirect = 'url';
+      $redirect_url = $node->webform['redirect_url'];
+    }
+    else {
+      $redirect_url = '';
+    }
+  }
+  else {
+    $redirect = 'url';
+    $redirect_url = $node->webform['redirect_url'];
+  }
+  $form['submission']['redirection'] = array(
+    '#type' => 'item',
+    '#title' => t('Redirection location'),
+    '#theme' => 'webform_advanced_redirection_form',
+    '#description' => t('Choose where to redirect the user upon successful submission.') . ' ' . t('The <em>Custom URL</em> option supports Webform token replacements.') . theme('webform_token_help', array('groups' => array('basic', 'node', 'special', 'submission'))),
+  );
+  $form['submission']['redirection']['redirect']= array(
+    '#type' => 'radios',
+    '#default_value' => $redirect,
+    '#options' => array(
+      'confirmation' => t('Confirmation page'),
+      'url' => t('Custom URL'),
+      'none' => t('No redirect (reload current page)'),
+    ),
+  );
+  $form['submission']['redirection']['redirect_url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Redirect URL'),
+    '#description' => t('URL to redirect the user to upon successful submission.'),
+    '#default_value' => $redirect_url,
+    '#maxlength' => 255,
+  );
+
+  // Submission limit settings for all submissions.
+  $form['submission']['total_submit_limit'] = array(
+    '#type' => 'item',
+    '#title' => t('Total submissions limit'),
+    '#theme' => 'webform_advanced_total_submit_limit_form',
+    '#description' => t('Limit the total number of allowed submissions.'),
+  );
+  $form['submission']['total_submit_limit']['enforce_total_limit'] = array(
+    '#type' => 'radios',
+    '#options' => array('no' => t('Unlimited'), 'yes' => 'Limit to !count total submission(s) !timespan'),
+    '#default_value' => $node->webform['total_submit_limit'] == -1 ? 'no' : 'yes',
+    '#parents' => array('enforce_total_limit'),
+  );
+  $form['submission']['total_submit_limit']['total_submit_limit'] = array(
+    '#type' => 'textfield',
+    '#maxlength' => 8,
+    '#size' => 8,
+    '#default_value' => $node->webform['total_submit_limit'] != -1 ? $node->webform['total_submit_limit'] : '',
+    '#parents' => array('total_submit_limit'),
+  );
+  $form['submission']['total_submit_limit']['total_submit_interval'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      '-1' => t('ever'),
+      '3600' => t('every hour'),
+      '86400' => t('every day'),
+      '604800' => t('every week'),
+    ),
+    '#default_value' => $node->webform['total_submit_interval'],
+    '#parents' => array('total_submit_interval'),
+  );
+
+  // Submission limit per user settings.
+  $form['submission']['submit_limit'] = array(
+    '#type' => 'item',
+    '#title' => t('Per user submission limit'),
+    '#theme' => 'webform_advanced_submit_limit_form',
+    '#description' => t('Limit the number of submissions <em>per user</em>. A user is identified by their user login if logged-in, or by their IP Address and Cookie if anonymous. Use of cookies may be modified in the global <a href="!url">Webform settings</a>.', array('!url' => url('admin/config/content/webform'))),
+  );
+  $form['submission']['submit_limit']['enforce_limit'] = array(
+    '#type' => 'radios',
+    '#options' => array('no' => t('Unlimited'), 'yes' => 'Limit each user to !count submission(s) !timespan'),
+    '#default_value' => $node->webform['submit_limit'] == -1 ? 'no' : 'yes',
+    '#parents' => array('enforce_limit'),
+  );
+  $form['submission']['submit_limit']['submit_limit'] = array(
+    '#type' => 'textfield',
+    '#maxlength' => 2,
+    '#size' => 2,
+    '#default_value' => $node->webform['submit_limit'] != -1 ? $node->webform['submit_limit'] : '',
+    '#parents' => array('submit_limit'),
+  );
+  $form['submission']['submit_limit']['submit_interval'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      '-1' => t('ever'),
+      '3600' => t('every hour'),
+      '86400' => t('every day'),
+      '604800' => t('every week'),
+    ),
+    '#default_value' => $node->webform['submit_interval'],
+    '#parents' => array('submit_interval'),
+  );
+
+  $form['submission']['status'] = array(
+    '#type' => 'radios',
+    '#title' => t('Status of this form'),
+    '#default_value' => $node->webform['status'] == 0 ? 0 : 1,
+    '#description' => t('Closing a form prevents any further submissions by any users.'),
+    '#parents' => array('status'),
+    '#options' => array(1 => t('Open'), 0 => t('Closed')),
+  );
+  /* End Edit Form */
+
+  /* Start per-role submission control */
+  $form['role_control'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Submission access'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => -3,
+    '#description' => t('These permissions affect which roles can submit this webform. It does not prevent access to the webform page. If needing to prevent access to the webform page entirely, use a content access module such as <a href="http://drupal.org/project/taxonomy_access">Taxonomy Access</a> or <a href="http://drupal.org/project/node_privacy_byrole">Node Privacy by Role</a>.'),
+    '#access' => variable_get('webform_submission_access_control', 1),
+  );
+  $user_roles = user_roles();
+  foreach ($user_roles as $rid => $rname) {
+    if ($rid == DRUPAL_ANONYMOUS_RID || $rid == DRUPAL_AUTHENTICATED_RID) {
+      continue;
+    }
+    $user_roles[$rid] = webform_tt("user:rid:$rid:name", $rname);
+  }
+  $form['role_control']['roles'] = array(
+    '#default_value' => $node->webform['roles'],
+    '#options' => $user_roles,
+    '#type' => 'checkboxes',
+    '#title' => t('Roles that can submit this webform'),
+    '#description' => t('The %authenticated role applies to any user signed into the site, regardless of other assigned roles.', array('%authenticated' => $user_roles[2])),
+  );
+  /* End per-role submission control */
+
+  /* Start advanced settings form */
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#weight' => -1,
+  );
+  $form['advanced']['block'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Available as block'),
+    '#default_value' => $node->webform['block'],
+    '#description' => t('If enabled this webform will be available as a block.'),
+    '#access' => user_access('administer blocks') || user_access('administer site configuration') || user_access('use panels dashboard'),
+  );
+  $form['advanced']['teaser'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show complete form in teaser'),
+    '#default_value' => $node->webform['teaser'],
+    '#description' => t('Display the entire form in the teaser display of this node.'),
+  );
+  $form['advanced']['allow_draft'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show "Save draft" button'),
+    '#default_value' => $node->webform['allow_draft'],
+    '#description' => t('Allow your users to save and finish the form later. This option is available only for authenticated users.'),
+  );
+  $form['advanced']['auto_save'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Automatically save as draft between pages'),
+    '#default_value' => $node->webform['auto_save'],
+    '#description' => t('Automatically save partial submissions when users click the "Next" or "Previous" buttons in a multipage form.'),
+  );
+  $form['advanced']['submit_notice'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show the notification about previous submissions.'),
+    '#default_value' => $node->webform['submit_notice'],
+    '#description' => t('Show the previous submissions notification that appears when users have previously submitted this form.'),
+  );
+  $form['advanced']['submit_text'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Submit button text'),
+    '#default_value' => $node->webform['submit_text'],
+    '#description' => t('By default the submit button on this form will have the label <em>Submit</em>. Enter a new title here to override the default.'),
+  );
+  /* End Advanced Settings Form */
+
+  $form['actions'] = array(
+    '#type' => 'actions',
+    '#weight' => 300,
+  );
+
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save configuration'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validate handler for webform_configure_form().
+ */
+function webform_configure_form_validate($form, &$form_state) {
+  // Ensure the entered e-mail addresses are valid.
+  if (!empty($form_state['values']['email'])) {
+    $emails = explode(',', $form_state['values']['email']);
+    foreach ($emails as $email) {
+      if (!valid_email_address(trim($email))) {
+        form_error($form['submission']['redirect_url'], t('The entered email address %address is not a valid address.', array('%address' => $email)));
+        break;
+      }
+    }
+  }
+
+  // Ensure the entered redirect URL is valid.
+  if ($form_state['values']['redirect'] == 'url') {
+    $redirect_url = trim($form_state['values']['redirect_url']);
+    if (empty($redirect_url)) {
+      form_error($form['submission']['redirection']['redirect_url'], t('A valid URL is required for custom redirection.'));
+    }
+    else {
+      form_set_value($form['submission']['redirection']['redirect_url'], $redirect_url, $form_state);
+    }
+  }
+  elseif ($form_state['values']['redirect'] == 'confirmation') {
+    form_set_value($form['submission']['redirection']['redirect_url'], '<confirmation>', $form_state);
+  }
+  else {
+    form_set_value($form['submission']['redirection']['redirect_url'], '<none>', $form_state);
+  }
+}
+
+/**
+ * Submit handler for webform_configure_form().
+ */
+function webform_configure_form_submit($form, &$form_state) {
+  // Edit the node by reference just to shorten it up.
+  $node = &$form['#node'];
+
+  // Save the confirmation.
+  $node->webform['confirmation'] = $form_state['values']['confirmation']['value'];
+  $node->webform['confirmation_format'] = $form_state['values']['confirmation']['format'];
+
+  // Save the redirect URL
+  $node->webform['redirect_url'] = $form_state['values']['redirect_url'];
+
+  // Overall form status.
+  $node->webform['status'] = $form_state['values']['status'];
+
+  // Save roles.
+  $node->webform['roles'] = array_keys(array_filter($form_state['values']['roles']));
+
+  // Set the block option.
+  $node->webform['block'] = $form_state['values']['block'];
+
+  // Set the Show complete form in teaser setting.
+  $node->webform['teaser'] = $form_state['values']['teaser'];
+
+  // Set the draft option.
+  $node->webform['allow_draft'] = $form_state['values']['allow_draft'];
+
+  // Set the auto-save draft option.
+  $node->webform['auto_save'] = $form_state['values']['auto_save'];
+
+  // Set the submit limit to -1 if set to unlimited.
+  if ($form_state['values']['enforce_limit'] == 'no') {
+    $node->webform['submit_limit'] = -1;
+    $node->webform['submit_interval'] = -1;
+  }
+  else {
+    $node->webform['submit_limit'] = $form_state['values']['submit_limit'];
+    $node->webform['submit_interval'] = $form_state['values']['submit_interval'];
+  }
+
+  // Set the total submit limit to -1 if set to unlimited.
+  if ($form_state['values']['enforce_total_limit'] == 'no') {
+    $node->webform['total_submit_limit'] = -1;
+    $node->webform['total_submit_interval'] = -1;
+  }
+  else {
+    $node->webform['total_submit_limit'] = $form_state['values']['total_submit_limit'];
+    $node->webform['total_submit_interval'] = $form_state['values']['total_submit_interval'];
+  }
+
+  // Set submit notice.
+  $node->webform['submit_notice'] = $form_state['values']['submit_notice'];
+
+  // Set submit button text.
+  $node->webform['submit_text'] = $form_state['values']['submit_text'];
+}
+
+/**
+ * Submit handler for webform_configure_form() that saves the node.
+ *
+ * This is separate from webform_configure_form_submit() to allow other modules
+ * to add properties if needed into the $form['#node'] object before save.
+ */
+function webform_configure_form_submit_save($form, &$form_state) {
+  node_save($form['#node']);
+  drupal_set_message(t('The form settings have been updated.'));
+}
+
+/**
+ * Theme the redirection setting on the webform node form.
+ */
+function theme_webform_advanced_redirection_form($variables) {
+  $form = $variables['form'];
+  // Add special class for setting the active radio button.
+  $form['redirect_url']['#attributes']['class'] = array('webform-set-active');
+
+  // Remove title and description for Redirect URL field.
+  $form['redirect_url']['#title'] = NULL;
+  $form['redirect_url']['#description'] = NULL;
+
+  $form['redirect']['confirmation']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['redirect']['url']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['redirect']['none']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['redirect']['url']['#inline_element'] = $form['redirect']['url']['#title'] . ': ' . drupal_render($form['redirect_url']);
+  $form['redirect']['url']['#title'] = NULL;
+
+  return drupal_render_children($form);
+}
+
+/**
+ * Theme the submit limit fieldset on the webform node form.
+ */
+function theme_webform_advanced_submit_limit_form($variables) {
+  $form = $variables['form'];
+  $form['submit_limit']['#attributes']['class'] = array('webform-set-active');
+  $form['submit_interval']['#attributes']['class'] = array('webform-set-active');
+  // Remove div wrappers around limit options.
+  $form['submit_limit']['#theme_wrappers'] = array();
+  $form['submit_interval']['#theme_wrappers'] = array();
+  $replacements = array(
+    '!count' => drupal_render($form['submit_limit']),
+    '!timespan' => drupal_render($form['submit_interval']),
+  );
+
+  $form['enforce_limit']['no']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['enforce_limit']['yes']['#title'] = NULL;
+  $form['enforce_limit']['yes']['#inline_element'] = t('Limit each user to !count submission(s) !timespan', $replacements);
+  $form['enforce_limit']['yes']['#theme_wrappers'] = array('webform_inline_radio');
+
+  return drupal_render_children($form);
+}
+
+/**
+ * Theme the total submit limit fieldset on the webform node form.
+ */
+function theme_webform_advanced_total_submit_limit_form($variables) {
+  $form = $variables['form'];
+  $form['total_submit_limit']['#attributes']['class'] = array('webform-set-active');
+  $form['total_submit_interval']['#attributes']['class'] = array('webform-set-active');
+  // Remove div wrappers around limit options.
+  $form['total_submit_limit']['#theme_wrappers'] = array();
+  $form['total_submit_interval']['#theme_wrappers'] = array();
+  $replacements = array(
+    '!count' => drupal_render($form['total_submit_limit']),
+    '!timespan' => drupal_render($form['total_submit_interval']),
+  );
+
+  $form['enforce_total_limit']['no']['#theme_wrappers'] = array('webform_inline_radio');
+  $form['enforce_total_limit']['yes']['#title'] = NULL;
+  $form['enforce_total_limit']['yes']['#inline_element'] = t('Limit to !count total submission(s) !timespan', $replacements);
+  $form['enforce_total_limit']['yes']['#theme_wrappers'] = array('webform_inline_radio');
+
+  return drupal_render_children($form);
+}
diff -Naurw webform_old/includes/webform.report.inc webform/includes/webform.report.inc
--- webform_old/includes/webform.report.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.report.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,1006 @@
+<?php
+
+/**
+ * @file
+ * This file includes helper functions for creating reports for webform.module
+ *
+ * @author Nathan Haug <nate@lullabot.com>
+ */
+
+// All functions within this file need the webform.submissions.inc.
+module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+/**
+ * Retrieve lists of submissions for a given webform.
+ */
+function webform_results_submissions($node, $user_filter, $pager_count) {
+  global $user;
+
+  if (isset($_GET['results']) && is_numeric($_GET['results'])) {
+    $pager_count = $_GET['results'];
+  }
+
+  $header = theme('webform_results_submissions_header', array('node' => $node));
+  if ($user_filter) {
+    if ($user->uid) {
+      drupal_set_title(t('Submissions for %user', array('%user' => $user->name)), PASS_THROUGH);
+    }
+    else {
+      drupal_set_title(t('Your submissions'));
+      webform_disable_page_cache();
+    }
+    webform_set_breadcrumb($node);
+    $submissions = webform_get_submissions(array('nid' => $node->nid, 'uid' => $user->uid), $header, $pager_count);
+    $count = webform_get_submission_count($node->nid, $user->uid);
+  }
+  else {
+    $submissions = webform_get_submissions($node->nid, $header, $pager_count);
+    $count = webform_get_submission_count($node->nid);
+  }
+
+  $operation_column = end($header);
+  $operation_total = $operation_column['colspan'];
+
+  $rows = array();
+  foreach ($submissions as $sid => $submission) {
+    $row = array(
+      $submission->is_draft ? t('@sid (draft)', array('@sid' => $sid)) : $sid,
+      format_date($submission->submitted, 'short'),
+    );
+    if (webform_results_access($node, $user)) {
+      $row[] = theme('username', array('account' => $submission));
+      $row[] = $submission->remote_addr;
+    }
+    $row[] = l(t('View'), "node/$node->nid/submission/$sid");
+    $operation_count = 1;
+    // No need to call this multiple times, just reference this in a variable.
+    $destination = drupal_get_destination();
+    if (webform_submission_access($node, $submission, 'edit', $user)) {
+      $row[] = l(t('Edit'), "node/$node->nid/submission/$sid/edit", array('query' => $destination));
+      $operation_count++;
+    }
+    if (webform_submission_access($node, $submission, 'delete', $user)) {
+      $row[] = l(t('Delete'), "node/$node->nid/submission/$sid/delete", array('query' => $destination));
+      $operation_count++;
+    }
+    if ($operation_count < $operation_total) {
+      $row[count($row) - 1] = array('data' => $row[count($row) - 1], 'colspan' => $operation_total - $operation_count + 1);
+    }
+    $rows[] = $row;
+  }
+
+  $element['#theme'] = 'webform_results_submissions';
+  $element['#node'] = $node;
+  $element['#submissions'] = $submissions;
+  $element['#total_count'] = $count;
+  $element['#pager_count'] = $pager_count;
+  $element['#attached']['library'][] = array('webform', 'admin');
+
+  $element['table']['#theme'] = 'table';
+  $element['table']['#header'] = $header;
+  $element['table']['#rows'] = $rows;
+  $element['table']['#operation_total'] = $operation_total;
+
+  return drupal_render($element);
+
+}
+
+/**
+ * Theme the list of links for selecting the number of results per page.
+ *
+ * @param $total_count
+ *   The total number of results available.
+ * @param $pager_count
+ *   The current number of results displayed per page.
+ */
+function theme_webform_results_per_page($variables) {
+  $total_count = $variables['total_count'];
+  $pager_count = $variables['pager_count'];
+  $output = '';
+
+  // Create a list of results-per-page options.
+  $counts = array(
+    '20' => '20',
+    '50' => '50',
+    '100' => '100',
+    '200' => '200',
+    '500' => '500',
+    '1000' => '1000',
+    '0' => t('All'),
+  );
+
+  $count_links = array();
+
+  foreach ($counts as $number => $text) {
+    if ($number < $total_count) {
+      $count_links[] = l($text, $_GET['q'], array('query' => array('results' => $number), 'attributes' => array('class' => array($pager_count == $number ? 'selected' : ''))));
+    }
+  }
+
+  $output .= '<div class="webform-results-per-page">';
+  if (count($count_links) > 1) {
+    $output .= t('Show !count results per page.', array('!count' => implode(' | ', $count_links)));
+  }
+  else {
+    $output .= t('Showing all results.');
+  }
+  if ($total_count > 1) {
+    $output .= ' ' . t('@total results total.', array('@total' => $total_count));
+  }
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * Theme the header of the submissions table.
+ *
+ * This is done in it's own function so that webform can retrieve the header and
+ * use it for sorting the results.
+ */
+function theme_webform_results_submissions_header($variables) {
+  $node = $variables['node'];
+
+  $columns = array(
+    array('data' => t('#'), 'field' => 'sid', 'sort' => 'desc'),
+    array('data' => t('Submitted'), 'field' => 'submitted'),
+  );
+  if (webform_results_access($node)) {
+    $columns[] = array('data' => t('User'), 'field' => 'name');
+    $columns[] = array('data' => t('IP Address'), 'field' => 'remote_addr');
+  }
+  $columns[] = array('data' => t('Operations'), 'colspan' => module_exists('print') ? 5 : 3);
+
+  return $columns;
+}
+
+/**
+ * Preprocess function for webform-results-submissions.tpl.php
+ */
+function template_preprocess_webform_results_submissions(&$vars) {
+  $vars['node'] = $vars['element']['#node'];
+  $vars['submissions'] = $vars['element']['#submissions'];
+  $vars['table'] = $vars['element']['table'];
+  $vars['total_count'] = $vars['element']['#total_count'];
+  $vars['pager_count'] = $vars['element']['#pager_count'];
+  $vars['is_submissions'] = (arg(2) == 'submissions')? 1 : 0;
+
+  unset($vars['element']);
+}
+
+/**
+ * Create a table containing all submitted values for a webform node.
+ */
+function webform_results_table($node, $pager_count = 0) {
+  if (isset($_GET['results']) && is_numeric($_GET['results'])) {
+    $pager_count = $_GET['results'];
+  }
+
+  // Get all the submissions for the node.
+  $header = theme('webform_results_table_header', array('node' => $node));
+  $submissions = webform_get_submissions($node->nid, $header, $pager_count);
+  $total_count = webform_get_submission_count($node->nid);
+
+  $output = theme('webform_results_table', array('node' => $node, 'components' => $node->webform['components'], 'submissions' => $submissions, 'total_count' => $total_count, 'pager_count' => $pager_count));
+  if ($pager_count) {
+    $output .= theme('pager');
+  }
+  return $output;
+}
+
+function theme_webform_results_table_header($variables) {
+  return array(
+    array('data' => t('#'), 'field' => 'sid', 'sort' => 'desc'),
+    array('data' => t('Submitted'), 'field' => 'submitted'),
+    array('data' => t('User'), 'field' => 'name'),
+    array('data' => t('IP Address'), 'field' => 'remote_addr'),
+  );
+}
+
+/**
+ * Theme the results table displaying all the submissions for a particular node.
+ *
+ * @param $node
+ *   The node whose results are being displayed.
+ * @param $components
+ *   An associative array of the components for this webform.
+ * @param $submissions
+ *   An array of all submissions for this webform.
+ * @param $total_count
+ *   The total number of submissions to this webform.
+ * @param $pager_count
+ *   The number of results to be shown per page.
+ */
+function theme_webform_results_table($variables) {
+  drupal_add_library('webform', 'admin');
+
+  $node = $variables['node'];
+  $components = $variables['components'];
+  $submissions = $variables['submissions'];
+  $total_count = $variables['total_count'];
+  $pager_count = $variables['pager_count'];
+
+  $header = array();
+  $rows = array();
+  $cell = array();
+
+  // This header has to be generated separately so we can add the SQL necessary.
+  // to sort the results.
+  $header = theme('webform_results_table_header', array('node' => $node));
+
+  // Generate a row for each submission.
+  foreach ($submissions as $sid => $submission) {
+    $cell[] = l($sid, 'node/' . $node->nid . '/submission/' . $sid);
+    $cell[] = format_date($submission->submitted, 'short');
+    $cell[] = theme('username', array('account' => $submission));
+    $cell[] = $submission->remote_addr;
+    $component_headers = array();
+
+    // Generate a cell for each component.
+    foreach ($node->webform['components'] as $component) {
+      $data = isset($submission->data[$component['cid']]['value']) ? $submission->data[$component['cid']]['value'] : NULL;
+      $submission_output = webform_component_invoke($component['type'], 'table', $component, $data);
+      if ($submission_output !== NULL) {
+        $component_headers[] = check_plain($component['name']);
+        $cell[] = $submission_output;
+      }
+    }
+
+    $rows[] = $cell;
+    unset($cell);
+  }
+  if (!empty($component_headers)) {
+    $header = array_merge($header, $component_headers);
+  }
+
+  if (count($rows) == 0) {
+    $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 4));
+  }
+
+
+  $output = '';
+  $output .= theme('webform_results_per_page', array('total_count' => $total_count, 'pager_count' => $pager_count));
+  $output .= theme('table', array('header' => $header, 'rows' => $rows));
+  return $output;
+}
+
+/**
+ * Delete all submissions for a node.
+ *
+ * @param $nid
+ *   The node id whose submissions will be deleted.
+ */
+function webform_results_clear($nid) {
+  $node = node_load($nid);
+  $submissions = webform_get_submissions($nid);
+  foreach ($submissions as $submission) {
+    webform_submission_delete($node, $submission);
+  }
+}
+
+/**
+ * Confirmation form to delete all submissions for a node.
+ *
+ * @param $nid
+ *   ID of node for which to clear submissions.
+ */
+function webform_results_clear_form($form, $form_state, $node) {
+  drupal_set_title(t('Clear Form Submissions'));
+
+  $form = array();
+  $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
+  $question = t('Are you sure you want to delete all submissions for this form?');
+
+  return confirm_form($form, $question, 'node/' . $node->nid . '/webform-results', NULL, t('Clear'), t('Cancel'));
+}
+
+function webform_results_clear_form_submit($form, &$form_state) {
+  webform_results_clear($form_state['values']['nid']);
+  $node = node_load($form_state['values']['nid']);
+  $title = $node->title;
+  $message = t('Webform %title entries cleared.', array('%title' => $title));
+  drupal_set_message($message);
+  watchdog('webform', $message);
+  $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/webform-results';
+}
+
+/**
+ * Form to configure the download of CSV files.
+ */
+function webform_results_download_form($form, &$form_state, $node) {
+  module_load_include('inc', 'webform', 'includes/webform.export');
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  $form = array();
+
+  $form['node'] = array(
+    '#type' => 'value',
+    '#value' => $node,
+  );
+
+  $form['format'] = array(
+    '#type' => 'radios',
+    '#title' => t('Export format'),
+    '#options' => webform_export_list(),
+    '#default_value' => isset($form_state['values']['format']) ? $form_state['values']['format'] : variable_get('webform_export_format', 'delimited'),
+  );
+
+  $form['delimiter'] = array(
+    '#type' => 'select',
+    '#title' => t('Delimited text format'),
+    '#description' => t('This is the delimiter used in the CSV/TSV file when downloading Webform results. Using tabs in the export is the most reliable method for preserving non-latin characters. You may want to change this to another character depending on the program with which you anticipate importing results.'),
+    '#default_value' => isset($form_state['values']['delimiter']) ? $form_state['values']['delimiter'] : variable_get('webform_csv_delimiter', '\t'),
+    '#options' => array(
+      ','  => t('Comma (,)'),
+      '\t' => t('Tab (\t)'),
+      ';'  => t('Semicolon (;)'),
+      ':'  => t('Colon (:)'),
+      '|'  => t('Pipe (|)'),
+      '.'  => t('Period (.)'),
+      ' '  => t('Space ( )'),
+    ),
+  );
+
+  $form['select_options'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Select list options'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['select_options']['select_keys'] = array(
+    '#type' => 'radios',
+    '#title' => t('Select keys'),
+    '#options' => array(
+      0 => t('Full, human-readable options (values)'),
+      1 => t('Short, raw options (keys)'),
+    ),
+    '#default_value' => isset($form_state['values']['select_options']['select_keys']) ? $form_state['values']['select_options']['select_keys'] : 0,
+    '#description' => t('Choose which part of options should be displayed from key|value pairs.'),
+  );
+
+  $form['select_options']['select_format'] = array(
+    '#type' => 'radios',
+    '#title' => t('Select list format'),
+    '#options' => array(
+      'separate' => t('Separate'),
+      'compact' => t('Compact'),
+    ),
+    '#default_value' => isset($form_state['values']['select_options']['select_format']) ? $form_state['values']['select_options']['select_format'] : 'separate',
+    '#attributes' => array('class' => array('webform-select-list-format')),
+    '#theme' => 'webform_results_download_select_format',
+  );
+
+  $csv_components = array(
+    'info' => t('Submission information'),
+    'serial' => '-' . t('Submission Number'),
+    'sid' => '-' . t('Submission ID'),
+    'time' => '-' . t('Time'),
+    'draft' => '-' . t('Draft'),
+    'ip_address' => '-' . t('IP Address'),
+    'uid' => '-' . t('User ID'),
+    'username' => '-' . t('Username'),
+  );
+  $csv_components += webform_component_list($node, 'csv', TRUE);
+
+  $form['components'] = array(
+    '#type' => 'select',
+    '#title' => t('Included export components'),
+    '#options' => $csv_components,
+    '#default_value' => isset($form_state['values']['components']) ? $form_state['values']['components'] : array_keys($csv_components),
+    '#multiple' => TRUE,
+    '#size' => 10,
+    '#description' => t('The selected components will be included in the export.'),
+    '#process' => array('webform_component_select'),
+  );
+
+  $form['range'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Download range options'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#tree' => TRUE,
+    '#theme' => 'webform_results_download_range',
+    '#element_validate' => array('webform_results_download_range_validate'),
+    '#after_build' => array('webform_results_download_range_after_build'),
+  );
+
+  $form['range']['range_type'] = array(
+    '#type' => 'radios',
+    '#options' => array(
+      'all' => t('All submissions'),
+      'new' => t('Only new submissions since your last download'),
+      'latest' => t('Only the latest'),
+      'range' => t('All submissions starting from'),
+    ),
+    '#default_value' => 'all',
+  );
+  $form['range']['latest'] = array(
+    '#type' => 'textfield',
+    '#size' => 5,
+    '#maxlength' => 8,
+    '#default_value' => isset($form_state['values']['latest']) ? $form_state['values']['latest'] : '',
+  );
+  $form['range']['start'] = array(
+    '#type' => 'textfield',
+    '#size' => 5,
+    '#maxlength' => 8,
+    '#default_value' => '',
+  );
+  $form['range']['end'] = array(
+    '#type' => 'textfield',
+    '#size' => 5,
+    '#maxlength' => 8,
+    '#default_value' => '',
+    '#description' => '',
+  );
+
+  // By default results are downloaded. User can override this value if
+  // programmatically submitting this form.
+  $form['download'] = array(
+    '#type' => 'value',
+    '#default_value' => TRUE
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Download'),
+  );
+
+  return $form;
+}
+
+/**
+ * FormAPI element validate function for the range fieldset.
+ */
+function webform_results_download_range_validate($element, $form_state) {
+  switch ($element['range_type']['#value']) {
+    case 'latest':
+      // Download latest x submissions.
+      if ($element['latest']['#value'] == '') {
+        form_error($element['latest'], t('Latest number of submissions field is required.'));
+      }
+      else{
+        if (!is_numeric($element['latest']['#value'])) {
+          form_error($element['latest'], t('Latest number of submissions must be numeric.'));
+        }
+        else{
+          if ($element['latest']['#value'] <= 0) {
+            form_error($element['latest'], t('Latest number of submissions must be greater than 0.'));
+          }
+        }
+      }
+      break;
+    case 'range':
+      // Download Start-End range of submissions.
+      // Start submission number.
+      if ($element['start']['#value'] == '') {
+        form_error($element['start'], t('Start submission number is required.'));
+      }
+      else{
+        if (!is_numeric($element['start']['#value'])) {
+          form_error($element['start'], t('Start submission number must be numeric.'));
+        }
+        else{
+          if ($element['start']['#value'] <= 0) {
+            form_error($element['start'], t('Start submission number must be greater than 0.'));
+          }
+        }
+      }
+      // End submission number.
+      if ($element['end']['#value'] != '') {
+        if (!is_numeric($element['end']['#value'])) {
+          form_error($element['end'], t('End submission number must be numeric.'));
+        }
+        else{
+          if ($element['end']['#value'] <= 0) {
+            form_error($element['end'], t('End submission number must be greater than 0.'));
+          }
+          else{
+            if ($element['end']['#value'] < $element['start']['#value']) {
+              form_error($element['end'], t('End submission number may not be less than Start submission number.'));
+            }
+          }
+        }
+      }
+      break;
+  }
+
+}
+
+/**
+ * Validate handler for webform_results_download_form().
+ */
+function webform_results_download_form_submit(&$form, &$form_state) {
+  $options = array(
+    'delimiter' => $form_state['values']['delimiter'],
+    'components' => array_keys(array_filter($form_state['values']['components'])),
+    'select_keys' => $form_state['values']['select_keys'],
+    'select_format' => $form_state['values']['select_format'],
+    'range_type' => $form_state['values']['range']['range_type'],
+    'download' => $form_state['values']['download'],
+  );
+
+  // Retrieve the list of required SIDs.
+  if ($options['range_type'] != 'all') {
+    $options['sids'] = webform_download_sids($form_state['values']['node']->nid, $form_state['values']['range']);
+  }
+
+  $export_info = webform_results_export($form_state['values']['node'], $form_state['values']['format'], $options);
+
+  // If webform result file should be downloaded, send the file to the browser,
+  // otherwise save information about the created file in $form_state.
+  if ($options['download']) {
+    webform_results_download($form_state['values']['node'], $export_info);
+  }
+  else {
+    $form_state['export_info'] = $export_info;
+  }
+}
+
+/**
+ * FormAPI after build function for the download range fieldset.
+ */
+function webform_results_download_range_after_build($element, &$form_state) {
+  $node = $form_state['values']['node'];
+
+  // Build a list of counts of new and total submissions.
+  $count = webform_get_submission_count($node->nid);
+  $sids = webform_download_sids($node->nid, array('range_type' => 'new'));
+
+  $last_download = webform_download_last_download_info($node->nid);
+
+  $element['#webform_download_info']['sid'] = $last_download ? $last_download['sid'] : 0;
+  $element['#webform_download_info']['requested'] = $last_download ? $last_download['requested'] : $node->created;
+  $element['#webform_download_info']['total'] = $count;
+  $element['#webform_download_info']['new'] = count($sids);
+
+  return $element;
+}
+
+/**
+ * Theme the output of the export range fieldset.
+ */
+function theme_webform_results_download_range($variables) {
+  drupal_add_library('webform', 'admin');
+
+  $element = $variables['element'];
+  $download_info = $element['#webform_download_info'];
+
+  // Set description for total of all submissions.
+  $element['range_type']['all']['#theme_wrappers'] = array('webform_inline_radio');
+  $element['range_type']['all']['#description'] = '(' . t('@count total', array('@count' => $download_info['total'])) . ')';
+
+  // Set description for "New submissions since last download".
+  $format = webform_date_format('short');
+  $requested_date = format_date($download_info['requested'], 'custom', $format);
+  $element['range_type']['new']['#theme_wrappers'] = array('webform_inline_radio');
+  $element['range_type']['new']['#description'] = '(' . t('@count new since @date', array('@count' => $download_info['new'], '@date' => $requested_date)) . ')';
+
+
+  // Disable option if there are no new submissions.
+  if ($download_info['new'] == 0) {
+    $element['range_type']['new']['#attributes']['disabled'] = 'disabled';
+  }
+
+  // Render latest x submissions option.
+  $element['latest']['#attributes']['class'] = array('webform-set-active');
+  $element['range_type']['latest']['#theme_wrappers'] = array('webform_inline_radio');
+  $element['range_type']['latest']['#inline_element'] = t('Only the latest !number submissions', array('!number' => drupal_render($element['latest'])));
+  $element['range_type']['latest']['#title'] = NULL;
+
+  // Render Start-End submissions option.
+  $element['start']['#attributes']['class'] = array('webform-set-active');
+  $element['end']['#attributes']['class'] = array('webform-set-active');
+  $element['range_type']['range']['#theme_wrappers'] = array('webform_inline_radio');
+  $element['range_type']['range']['#inline_element'] = t('All submissions starting from: !start and optionally to: !end', array('!start' => drupal_render($element['start']), '!end' => drupal_render($element['end'])));
+  $element['range_type']['range']['#title'] = NULL;
+
+  $last_sid = $download_info['sid'] ? $download_info['sid'] : drupal_placeholder(t('none'));
+  $element['range_type']['range']['#description'] = '(' . t('Use submission IDs for the range. Last downloaded end SID: !sid.', array('!sid' => $last_sid)) . ')';
+
+  return drupal_render_children($element);
+}
+
+/**
+ * Theme the output of the select list format radio buttons.
+ */
+function theme_webform_results_download_select_format($variables) {
+  drupal_add_library('webform', 'admin');
+
+  $element = $variables['element'];
+  $output = '';
+
+  // Build an example table for the separate option.
+  $header = array(t('Option A'), t('Option B'), t('Option C'));
+  $rows = array(
+    array('X', '', ''),
+    array('X', '', 'X'),
+    array('', 'X', 'X'),
+  );
+
+  $element['separate']['#attributes']['class'] = array();
+  $element['separate']['#description'] = theme('table', array('header' => $header, 'rows' => $rows));
+  $element['separate']['#description'] .= t('Separate options are more suitable for building reports, graphs, and statistics in a spreadsheet application.');
+  $output .= drupal_render($element['separate']);
+
+  // Build an example table for the compact option.
+  $header = array(t('My select list'));
+  $rows = array(
+    array('Option A'),
+    array('Option A,Option C'),
+    array('Option B,Option C'),
+  );
+
+  $element['compact']['#attributes']['class'] = array();
+  $element['compact']['#description'] = theme('table', array('header' => $header, 'rows' => $rows));
+  $element['compact']['#description'] .= t('Compact options are more suitable for importing data into other systems.');
+  $output .= drupal_render($element['compact']);
+
+  return $output;
+}
+
+/**
+ * Generate a Excel-readable CSV file containing all submissions for a Webform.
+ *
+ * The CSV requires that the data be presented in a flat file.  In order
+ * to maximize usability to the Excel community and minimize subsequent
+ * stats or spreadsheet programming this program extracts data from the
+ * various records for a given session and presents them as a single file
+ * where each row represents a single record.
+ * The structure of the file is:
+ *   Heading Line 1: Gives group overviews padded by empty cells to the
+ *                   next group.  A group may be a question and corresponds
+ *                   to a component in the webform philosophy. Each group
+ *                   overview will have a fixed number of columns beneath it.
+ *   Heading line 2: gives column headings
+ *   Data line 1 .....
+ *   Data line 2 .....
+ *
+ * An example of this format is given below.  Note the columns have had spaces
+ * added so the columns line up.  This is not the case with actual file where
+ * a column may be null.  Note also, that multiple choice questions as produced
+ * by checkboxes or radio buttons have been presented as "yes" or "no" and the
+ * actual choice text is retained only in the header line 2.
+ * Data from text boxes and input fields are written out in the body of the table.
+ *
+ *   Submission Details,    ,   ,      ,Question 1,        ,        ,..,        ,Question 2,        ,        ,..,        ,Question n
+ *   timestamp         ,time,SID,userid,Choice 1  ,Choice 2,Choice 3,..,Choice n,Choice 1  ,Choice 2,Choice 3,..,Choice n,Comment
+ *   21 Feb 2005       ,1835,23 ,34    ,X         ,        ,        ,..,       ,X          ,X       ,X       ,..,X       ,My comment
+ *   23 Feb 2005       ,1125,24 ,89    ,X         ,X       ,        ,..,       ,X          ,X       ,X       ,..,X       ,Hello
+ *   .................................................................................................................................
+ *   27 Feb 2005       ,1035,56 ,212   ,X         ,        ,        ,..,       ,X          ,X       ,X       ,..,X       ,How is this?
+ *
+ */
+function webform_results_export($node, $format = 'delimited', $options = array()) {
+  global $user;
+  module_load_include('inc', 'webform', 'includes/webform.export');
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  $submission_information = array(
+    'serial' => t('Serial'),
+    'sid' => t('SID'),
+    'time' => t('Time'),
+    'draft' => t('Draft'),
+    'ip_address' => t('IP Address'),
+    'uid' => t('UID'),
+    'username' => t('Username'),
+  );
+
+  if (empty($options)) {
+    $options = array(
+      'delimiter' => variable_get('webform_csv_delimiter', '\t'),
+      'components' => array_merge(array_keys($submission_information), array_keys(webform_component_list($node, 'csv', TRUE))),
+      'select_keys' => 0,
+      'select_format' => 'separate',
+      'range_type' => 'all',
+    );
+  }
+  else {
+    foreach ($submission_information as $key => $label) {
+      if (!in_array($key, $options['components'])) {
+        unset($submission_information[$key]);
+      }
+    }
+  }
+
+  // Open a new Webform exporter object.
+  $exporter = webform_export_create_handler($format, $options);
+
+  $file_name = drupal_tempnam('temporary://', 'webform_');
+  $handle = @fopen($file_name, 'w'); // The @ suppresses errors.
+  $exporter->bof($handle);
+
+  // Fill in the header for the submission information (if any).
+  $header[2] = $header[1] = $header[0] = count($submission_information) ? array_fill(0, count($submission_information), '') : array();
+  if (count($submission_information)) {
+    $header[0][0] = $node->title;
+    $header[1][0] = t('Submission Details');
+    foreach (array_values($submission_information) as $column => $label) {
+      $header[2][$column] = $label;
+    }
+  }
+
+  // Compile header information for components.
+  foreach ($options['components'] as $cid) {
+    if (isset($node->webform['components'][$cid])) {
+      $component = $node->webform['components'][$cid];
+
+      // Let each component determine its headers.
+      if (webform_component_feature($component['type'], 'csv')) {
+        $component_header = (array) webform_component_invoke($component['type'], 'csv_headers', $component, $options);
+        $header[0] = array_merge($header[0], (array) $component_header[0]);
+        $header[1] = array_merge($header[1], (array) $component_header[1]);
+        $header[2] = array_merge($header[2], (array) $component_header[2]);
+      }
+    }
+  }
+
+  // Add headers to the file.
+  foreach ($header as $row) {
+    $exporter->add_row($handle, $row);
+  }
+
+  // Get all the required submissions for the download.
+  $filters['nid'] = $node->nid;
+  if (!empty($options['sids'])){
+    $filters['sid'] = $options['sids'];
+  }
+  $submissions = webform_get_submissions($filters);
+
+  // Generate a row for each submission.
+  $row_count = 0;
+  foreach ($submissions as $sid => $submission) {
+    $row_count++;
+
+    $row = array();
+    if (isset($submission_information['serial'])) {
+      $row[] = $row_count;
+    }
+    if (isset($submission_information['sid'])) {
+      $row[] = $sid;
+    }
+    if (isset($submission_information['time'])) {
+      $row[] = format_date($submission->submitted, 'short');
+    }
+    if (isset($submission_information['draft'])) {
+      $row[] = $submission->is_draft;
+    }
+    if (isset($submission_information['ip_address'])) {
+      $row[] =  $submission->remote_addr;
+    }
+    if (isset($submission_information['uid'])) {
+      $row[] = $submission->uid;
+    }
+    if (isset($submission_information['username'])) {
+      $row[] = $submission->name;
+    }
+
+    foreach ($options['components'] as $cid) {
+      if (isset($node->webform['components'][$cid])) {
+        $component = $node->webform['components'][$cid];
+        // Let each component add its data.
+        $raw_data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
+        if (webform_component_feature($component['type'], 'csv')) {
+          $data = webform_component_invoke($component['type'], 'csv_data', $component, $options, $raw_data);
+          if (is_array($data)) {
+            $row = array_merge($row, array_values($data));
+          }
+          else {
+            $row[] = isset($data) ? $data : '';
+          }
+        }
+      }
+    }
+
+    // Write data from submissions.
+    $data = $exporter->add_row($handle, $row);
+  }
+
+  // Add the closing bytes.
+  $exporter->eof($handle);
+
+  // Close the file.
+  @fclose($handle);
+
+  $export_info['options'] = $options;
+  $export_info['file_name'] = $file_name;
+  $export_info['exporter'] = $exporter;
+  $export_info['row_count'] = $row_count;
+  $export_info['last_sid'] = $sid;
+
+  return $export_info;
+}
+
+/**
+ * Send a generated webform results file to the user's browser.
+ *
+ * @param $node
+ *   The webform node.
+ * @param $export_info
+ *   Export information array retrieved from webform_results_export().
+ */
+function webform_results_download($node, $export_info) {
+  global $user;
+
+  // $exporter, $file_name, $row_count
+  $export_name = _webform_safe_name($node->title);
+  $export_info['exporter']->set_headers($export_name);
+  @readfile($export_info['file_name']);  // The @ makes it silent.
+  @unlink($export_info['file_name']);  // Clean up, the @ makes it silent.
+
+  // Update user last downloaded sid if required.
+  if ($export_info['options']['range_type'] != 'range' && $export_info['row_count'] > 0) {
+    // Delete existing record.
+    db_delete('webform_last_download')
+      ->condition('nid', $node->nid)
+      ->condition('uid', $user->uid)
+      ->execute();
+    // Write new record.
+    db_insert('webform_last_download')
+      ->fields(array(
+        'nid' => $node->nid,
+        'uid' => $user->uid,
+        'sid' => $export_info['last_sid'],
+        'requested' => REQUEST_TIME,
+      ))
+      ->execute();
+  }
+
+  exit();
+}
+
+/**
+ * Provides a simple analysis of all submissions to a webform.
+ *
+ * @param $node
+ *   The webform node on which to generate the analysis.
+ * @param $sids
+ *   An array of submission IDs to which this analysis may be filtered. May be
+ *   used to generate results that are per-user or other groups of submissions.
+ * @param $analysis_component
+ *   A webform component. If passed in, additional information may be returned
+ *   relating specifically to that component's analysis, such as a list of
+ *   "Other" values within a select list.
+ */
+function webform_results_analysis($node, $sids = array(), $analysis_component = NULL) {
+  if (!is_array($sids)) {
+    $sids = array();
+  }
+
+  // If showing a component's details, we don't want to loose the menu tabs.
+  if ($analysis_component) {
+    $item = menu_get_item('node/' . $node->nid . '/webform-results/analysis');
+    menu_set_item(NULL, $item);
+  }
+
+  $components = isset($analysis_component) ? array($analysis_component['cid'] => $analysis_component) : $node->webform['components'];
+  $data = array();
+  foreach ($components as $cid => $component) {
+    // Do component specific call.
+    if ($row_data = webform_component_invoke($component['type'], 'analysis', $component, $sids, isset($analysis_component))) {
+      $data[$cid] = $row_data;
+    }
+  }
+
+  return theme('webform_results_analysis', array('node' => $node, 'data' => $data, 'sids' => $sids, 'component' => $analysis_component));
+}
+
+/**
+ * Output the content of the Analysis page.
+ *
+ * @see webform_results_analysis()
+ */
+function theme_webform_results_analysis($variables) {
+  $node = $variables['node'];
+  $data = $variables['data'];
+  $sids = $variables['sids'];
+  $analysis_component = $variables['component'];
+
+  $rows = array();
+  $question_number = 0;
+  $single = isset($analysis_component);
+
+  $header = array(
+    $single ? $analysis_component['name'] : t('Q'),
+    array('data' => $single ? '&nbsp;' : t('responses'), 'colspan' => '10')
+  );
+
+  foreach ($data as $cid => $row_data) {
+    $question_number++;
+
+    if (is_array($row_data)) {
+      $row = array();
+      if (!$single) {
+        $row['data'][] = array('data' => '<strong>' . $question_number . '</strong>', 'rowspan' => count($row_data) + 1, 'valign' => 'top');
+        $row['data'][] = array('data' => '<strong>' . check_plain($node->webform['components'][$cid]['name']) . '</strong>', 'colspan' => '10');
+        $row['class'][] = 'webform-results-question';
+      }
+      $rows = array_merge($rows, array_merge(array($row), $row_data));
+    }
+  }
+
+  if (count($rows) == 0) {
+    $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 20));
+  }
+
+  return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-results-analysis'))));
+}
+
+/**
+ * Given a set of range options, retrieve a set of SIDs for a webform node.
+ */
+function webform_download_sids($nid, $range_options, $uid = NULL) {
+  $query = db_select('webform_submissions', 'ws')
+    ->fields('ws', array('sid'))
+    ->condition('nid', $nid);
+
+  switch ($range_options['range_type']) {
+    case 'all':
+      // All Submissions.
+      $query->orderBy('sid', 'ASC');
+      break;
+    case 'new':
+      // All Since Last Download.
+      $download_info = webform_download_last_download_info($nid, $uid);
+      $last_sid = $download_info ? $download_info['sid'] : 0;
+      $query
+        ->condition('sid', $last_sid, '>')
+        ->orderBy('sid', 'ASC');
+      break;
+    case 'latest':
+      // Last x Submissions.
+      $query
+        ->orderBy('sid', 'DESC')
+        ->range(0, $range_options['latest']);
+      break;
+    case 'range':
+      // Submissions Start-End.
+      $query->condition('sid', $range_options['start'], '>=');
+      if ($range_options['end']){
+        $query->condition('sid', $range_options['end'], '<=');
+      }
+      $query->orderBy('sid', 'ASC');
+      break;
+  }
+
+  $sids = $query->execute()->fetchCol();
+
+  // The last x submissions option has SIDs that are in reverse order.
+  if ($range_options['range_type'] == 'latest') {
+    $sids = array_reverse($sids);
+  }
+
+  return $sids;
+}
+
+/**
+ * Get this user's last download information, including the SID and timestamp.
+ *
+ * This function provides an array of information about the last download that
+ * a user had for a particular Webform node. Currently it only returns an array
+ * with two keys:
+ *  - sid: The last submission ID that was downloaded.
+ *  - requested: The timestamp of the last download request.
+ *
+ * @param $nid
+ *   The Webform NID.
+ * @param $uid
+ *   The user account ID for which to retrieve download information.
+ * @return
+ *   An array of download information or FALSE if this user has never downloaded
+ *   results for this particular node.
+ */
+function webform_download_last_download_info($nid, $uid = NULL) {
+  $uid = isset($uid) ? $uid : $GLOBALS['user']->uid;
+
+  $info = db_select('webform_last_download', 'wld')
+    ->fields('wld')
+    ->condition('nid', $nid)
+    ->condition('uid', $uid)
+    ->execute()
+    ->fetchAssoc();
+
+  return $info;
+}
diff -Naurw webform_old/includes/webform.submissions.inc webform/includes/webform.submissions.inc
--- webform_old/includes/webform.submissions.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/includes/webform.submissions.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,925 @@
+<?php
+
+/**
+ * @file
+ * This file is loaded when handling submissions, either submitting new,
+ * editing, or viewing. It also contains all CRUD functions for submissions.
+ *
+ * @author Nathan Haug <nate@lullabot.com>
+ */
+
+/**
+ * Given an array of submitted values, flatten it into data for a submission.
+ *
+ * @param $node
+ *   The node object containing the current webform.
+ * @param $submitted
+ *   The submitted user values from the webform.
+ * @return
+ *   An array suitable for use in the 'data' property of a $submission object.
+ */
+function webform_submission_data($node, $submitted) {
+  $data = array();
+
+  foreach ($submitted as $cid => $values) {
+    // Don't save pagebreaks as submitted data.
+    if ($node->webform['components'][$cid]['type'] == 'pagebreak') {
+      continue;
+    }
+
+    if (is_array($values)) {
+      $data[$cid]['value'] = $values;
+    }
+    else {
+      $data[$cid]['value'][0] = $values;
+    }
+  }
+
+  return $data;
+}
+
+/**
+ * Update a webform submission entry in the database.
+ *
+ * @param $node
+ *   The node object containing the current webform.
+ * @param $submission
+ *   The webform submission object to be saved into the database.
+ * @return
+ *   The existing submission SID.
+ */
+function webform_submission_update($node, $submission) {
+  // Allow other modules to modify the submission before saving.
+  foreach (module_implements('webform_submission_presave') as $module) {
+    $function = $module . '_webform_submission_presave';
+    $function($node, $submission);
+  }
+
+  // Update the main submission info.
+  drupal_write_record('webform_submissions', $submission, 'sid');
+
+  // If is draft, only delete data for components submitted, to
+  // preserve any data from form pages not visited in this submission.
+  if ($submission->is_draft) {
+    $submitted_cids = array_keys($submission->data);
+    if ($submitted_cids) {
+      db_delete('webform_submitted_data')
+        ->condition('sid', $submission->sid)
+        ->condition('cid', $submitted_cids, 'IN')
+        ->execute();
+    }
+  }
+  else {
+    db_delete('webform_submitted_data')
+      ->condition('sid', $submission->sid)
+      ->execute();
+  }
+
+  // Then re-add submission data to the database.
+  $submission->is_new = FALSE;
+  webform_submission_insert($node, $submission);
+
+  module_invoke_all('webform_submission_update', $node, $submission);
+
+  return $submission->sid;
+}
+
+/**
+ * Insert a webform submission entry in the database.
+ *
+ * @param $node
+ *   The node object containing the current webform.
+ * @param $submission
+ *   The webform submission object to be saved into the database.
+ * @return
+ *   The new submission SID.
+ */
+function webform_submission_insert($node, $submission) {
+  // The submission ID may already be set if being called as an update.
+  if (!isset($submission->sid) && (!isset($submission->is_new) || $submission->is_new == FALSE)) {
+    // Allow other modules to modify the submission before saving.
+    foreach (module_implements('webform_submission_presave') as $module) {
+      $function = $module . '_webform_submission_presave';
+      $function($node, $submission);
+    }
+    $submission->nid = $node->webform['nid'];
+    drupal_write_record('webform_submissions', $submission);
+    $is_new = TRUE;
+  }
+
+  foreach ($submission->data as $cid => $values) {
+    foreach ($values['value'] as $delta => $value) {
+      $data = array(
+        'nid' => $node->webform['nid'],
+        'sid' => $submission->sid,
+        'cid' => $cid,
+        'no' => $delta,
+        'data' => is_null($value) ? '' : $value,
+      );
+      drupal_write_record('webform_submitted_data', $data);
+    }
+  }
+
+  // Invoke the insert hook after saving all the data.
+  if (isset($is_new)) {
+    module_invoke_all('webform_submission_insert', $node, $submission);
+  }
+
+  return $submission->sid;
+}
+
+/**
+ * Delete a single submission.
+ *
+ * @param $nid
+ *   ID of node for which this webform was submitted.
+ * @param $sid
+ *   ID of submission to be deleted (from webform_submitted_data).
+ */
+function webform_submission_delete($node, $submission) {
+  // Iterate through all components and let each do cleanup if necessary.
+  foreach ($node->webform['components'] as $cid => $component) {
+    if (isset($submission->data[$cid])) {
+      webform_component_invoke($component['type'], 'delete', $component, $submission->data[$cid]['value']);
+    }
+  }
+
+  // Delete any anonymous session information.
+  if (isset($_SESSION['webform_submission'][$submission->sid])) {
+    unset($_SESSION['webform_submission'][$submission->sid]);
+  }
+
+  db_delete('webform_submitted_data')
+    ->condition('nid', $node->nid)
+    ->condition('sid', $submission->sid)
+    ->execute();
+  db_delete('webform_submissions')
+    ->condition('nid', $node->nid)
+    ->condition('sid', $submission->sid)
+    ->execute();
+
+  module_invoke_all('webform_submission_delete', $node, $submission);
+}
+
+/**
+ * Send related e-mails related to a submission.
+ *
+ * This function is usually invoked when a submission is completed, but may be
+ * called any time e-mails should be redelivered.
+ *
+ * @param $node
+ *   The node object containing the current webform.
+ * @param $submission
+ *   The webform submission object to be used in sending e-mails.
+ * @param $emails
+ *   (optional) An array of specific e-mail settings to be used. If omitted, all
+ *   e-mails in $node->webform['emails'] will be sent.
+ */
+function webform_submission_send_mail($node, $submission, $emails = NULL) {
+  global $user;
+
+  // Get the list of e-mails we'll be sending.
+  $emails = isset($emails) ? $emails : $node->webform['emails'];
+
+  // Create a themed message for mailing.
+  $send_count = 0;
+  foreach ($emails as $eid => $email) {
+    // Set the HTML property based on availablity of MIME Mail.
+    $email['html'] = ($email['html'] && webform_email_html_capable());
+
+    // Pass through the theme layer if using the default template.
+    if ($email['template'] == 'default') {
+      $email['message'] = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), array('node' => $node, 'submission' => $submission, 'email' => $email));
+    }
+    else {
+      $email['message'] = $email['template'];
+    }
+
+    // Replace tokens in the message.
+    $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE, TRUE);
+
+    // Build the e-mail headers.
+    $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), array('node' => $node, 'submission' => $submission, 'email' => $email));
+
+    // Assemble the From string.
+    if (isset($email['headers']['From'])) {
+      // If a header From is already set, don't override it.
+      $email['from'] = $email['headers']['From'];
+      unset($email['headers']['From']);
+    }
+    else {
+      $email['from'] = webform_format_email_address($email['from_address'], $email['from_name'], $node, $submission);
+    }
+
+    // Update the subject if set in the themed headers.
+    if (isset($email['headers']['Subject'])) {
+      $email['subject'] = $email['headers']['Subject'];
+      unset($email['headers']['Subject']);
+    }
+    else {
+      $email['subject'] = webform_format_email_subject($email['subject'], $node, $submission);
+    }
+
+    // Update the to e-mail if set in the themed headers.
+    if (isset($email['headers']['To'])) {
+      $email['email'] = $email['headers']['To'];
+      unset($email['headers']['To']);
+    }
+
+    // Generate the list of addresses that this e-mail will be sent to.
+    $addresses = array_filter(explode(',', $email['email']));
+    $addresses_final = array();
+    foreach ($addresses as $address) {
+      $address = trim($address);
+
+      // After filtering e-mail addresses with component values, a single value
+      // might contain multiple addresses (such as from checkboxes or selects).
+      $address = webform_format_email_address($address, NULL, $node, $submission, TRUE, FALSE, 'short');
+
+      if (is_array($address)) {
+        foreach ($address as $new_address) {
+          $new_address = trim($new_address);
+          if (valid_email_address($new_address)) {
+            $addresses_final[] = $new_address;
+          }
+        }
+      }
+      elseif (valid_email_address($address)) {
+        $addresses_final[] = $address;
+      }
+    }
+
+    // Mail the webform results.
+    foreach ($addresses_final as $address) {
+      // Verify that this submission is not attempting to send any spam hacks.
+      if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers'])) {
+        watchdog('webform', 'Possible spam attempt from @remote_addr' . "<br />\n" . nl2br(htmlentities($email['message'])), array('@remote_add' => ip_address()));
+        drupal_set_message(t('Illegal information. Data not submitted.'), 'error');
+        return FALSE;
+      }
+
+      $language = $user->uid ? user_preferred_language($user) : language_default();
+      $mail_params = array(
+        'message' => $email['message'],
+        'subject' => $email['subject'],
+        'headers' => $email['headers'],
+        'node' => $node,
+        'submission' => $submission,
+        'email' => $email,
+      );
+
+      if (webform_email_html_capable()) {
+        // Load attachments for the e-mail.
+        $attachments = array();
+        if ($email['attachments']) {
+          webform_component_include('file');
+          foreach ($node->webform['components'] as $component) {
+            if (webform_component_feature($component['type'], 'attachment') && !empty($submission->data[$component['cid']]['value'][0])) {
+              if (webform_component_implements($component['type'], 'attachments')) {
+                $files = webform_component_invoke($component['type'], 'attachments', $component, $submission->data[$component['cid']]['value']);
+                if ($files) {
+                  $attachments = array_merge($attachments, $files);
+                }
+              }
+            }
+          }
+        }
+
+        // Add the attachments to the mail parameters.
+        $mail_params['attachments'] = $attachments;
+
+        // Set all other properties for HTML e-mail handling.
+        $mail_params['plain'] = !$email['html'];
+        $mail_params['plaintext'] = $email['html'] ? NULL : $email['message'];
+        $mail_params['headers'] = $email['headers'];
+        if ($email['html']) {
+          // MIME Mail requires this header or it will filter all text.
+          $mail_params['headers']['Content-Type'] = 'text/html; charset=UTF-8';
+        }
+      }
+
+      // Mail the submission.
+      $message = drupal_mail('webform', 'submission', $address, $language, $mail_params, $email['from']);
+      if ($message['result']) {
+        $send_count++;
+      }
+    }
+  }
+
+  return $send_count;
+}
+
+/**
+ * Confirm form to delete a single form submission.
+ *
+ * @param $form
+ *   The new form array.
+ * @param $form_state
+ *   The current form state.
+ * @param $node
+ *   The node for which this webform was submitted.
+ * @param $submission
+ *   The submission to be deleted (from webform_submitted_data).
+ */
+function webform_submission_delete_form($form, $form_state, $node, $submission) {
+  webform_set_breadcrumb($node, $submission);
+
+  // Set the correct page title.
+  drupal_set_title(webform_submission_title($node, $submission));
+
+  // Keep the NID and SID in the same location as the webform_client_form().
+  // This helps mollom identify the same fields when deleting a submission.
+  $form['#tree'] = TRUE;
+  $form['details']['nid'] = array(
+    '#type' => 'value',
+    '#value' => $node->nid,
+  );
+  $form['details']['sid'] = array(
+    '#type' => 'value',
+    '#value' => $submission->sid,
+  );
+
+  $question = t('Are you sure you want to delete this submission?');
+
+  if (isset($_GET['destination'])) {
+    $destination = $_GET['destination'];
+  }
+  elseif (webform_results_access($node)) {
+    $destination = 'node/' . $node->nid . '/webform-results';
+  }
+  else {
+    $destination = 'node/' . $node->nid . '/submissions';
+  }
+
+  return confirm_form($form, NULL, $destination, $question, t('Delete'), t('Cancel'));
+}
+
+function webform_submission_delete_form_submit($form, &$form_state) {
+  $node = node_load($form_state['values']['details']['nid']);
+  $submission = webform_get_submission($form_state['values']['details']['nid'], $form_state['values']['details']['sid']);
+  webform_submission_delete($node, $submission);
+  drupal_set_message(t('Submission deleted.'));
+
+  $form_state['redirect'] = 'node/' . $node->nid . '/webform-results';
+}
+
+/**
+ * Menu title callback; Return the submission number as a title.
+ */
+function webform_submission_title($node, $submission) {
+  return t('Submission #@sid', array('@sid' => $submission->sid));
+}
+
+/**
+ * Menu callback; Present a Webform submission page for display or editing.
+ */
+function webform_submission_page($node, $submission, $format) {
+  global $user;
+
+  // Render the admin UI breadcrumb.
+  webform_set_breadcrumb($node, $submission);
+
+  // Set the correct page title.
+  drupal_set_title(webform_submission_title($node, $submission));
+
+  if ($format == 'form') {
+    $output = drupal_get_form('webform_client_form_' . $node->nid, $node, $submission);
+  }
+  else {
+    $output = webform_submission_render($node, $submission, NULL, $format);
+  }
+
+  // Determine the mode in which we're displaying this submission.
+  $mode = ($format != 'form') ? 'display' : 'form';
+  if (strpos(request_uri(), 'print/') !== FALSE) {
+    $mode = 'print';
+  }
+  if (strpos(request_uri(), 'printpdf/') !== FALSE) {
+    $mode = 'pdf';
+  }
+
+  // Add navigation for administrators.
+  if (webform_results_access($node)) {
+    $navigation = theme('webform_submission_navigation', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
+    $information = theme('webform_submission_information', array('node' => $node, 'submission' => $submission, 'mode' => $mode));
+  }
+  else {
+    $navigation = NULL;
+    $information = NULL;
+  }
+
+  // Actions may be shown to all users.
+  $actions = theme('links', array('links' => module_invoke_all('webform_submission_actions', $node, $submission), 'attributes' => array('class' => array('links', 'inline', 'webform-submission-actions'))));
+
+  // Disable the page cache for anonymous users viewing or editing submissions.
+  if (!$user->uid) {
+    webform_disable_page_cache();
+  }
+
+  $page = array(
+    '#theme' => 'webform_submission_page',
+    '#node' => $node,
+    '#mode' => $mode,
+    '#submission' => $submission,
+    '#submission_content' => $output,
+    '#submission_navigation' => $navigation,
+    '#submission_information' => $information,
+    '#submission_actions' => $actions,
+  );
+  $page['#attached']['library'][] = array('webform', 'admin');
+  return $page;
+}
+
+/**
+ * Form to resend specific e-mails associated with a submission.
+ */
+function webform_submission_resend($form, $form_state, $node, $submission) {
+  // Render the admin UI breadcrumb.
+  webform_set_breadcrumb($node, $submission);
+
+  $form['#tree'] = TRUE;
+  $form['#node'] = $node;
+  $form['#submission'] = $submission;
+
+  foreach ($node->webform['emails'] as $eid => $email) {
+    $email_addresses = array_filter(explode(',', check_plain($email['email'])));
+    foreach ($email_addresses as $key => $email_address) {
+      $email_addresses[$key] = webform_format_email_address($email_address, NULL, $node, $submission, FALSE);
+    }
+    $valid_email = !empty($email_addresses[0]) && valid_email_address($email_addresses[0]);
+    $form['resend'][$eid] = array(
+      '#type' => 'checkbox',
+      '#default_value' => $valid_email ? TRUE : FALSE,
+      '#disabled' => $valid_email ? FALSE : TRUE,
+    );
+    $form['emails'][$eid]['email'] = array(
+      '#markup' => implode('<br />', $email_addresses),
+    );
+    if (!$valid_email) {
+      $form['emails'][$eid]['email']['#value'] .= ' (' . t('empty') . ')';
+    }
+    $form['emails'][$eid]['subject'] = array(
+      '#markup' => check_plain(webform_format_email_subject($email['subject'], $node, $submission)),
+    );
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['resend'] = array(
+      '#type' => 'submit',
+      '#value' => t('Resend e-mails'),
+    );
+    $form['actions']['cancel'] = array(
+      '#type' => 'markup',
+      '#markup' => l(t('Cancel'), isset($_GET['destination']) ? $_GET['destination'] : 'node/' . $node->nid . '/submission/' . $submission->sid),
+    );
+  }
+  return $form;
+}
+
+/**
+ * Validate handler for webform_submission_resend().
+ */
+function webform_submission_resend_validate($form, &$form_state) {
+  if (count(array_filter($form_state['values']['resend'])) == 0) {
+    form_set_error('emails', t('You must select at least one email address to resend submission.'));
+  }
+}
+
+/**
+ * Submit handler for webform_submission_resend().
+ */
+function webform_submission_resend_submit($form, &$form_state) {
+  $node = $form['#node'];
+  $submission = $form['#submission'];
+
+  $emails = array();
+  foreach ($form_state['values']['resend'] as $eid => $checked) {
+    if ($checked) {
+      $emails[] = $form['#node']->webform['emails'][$eid];
+    }
+  }
+  $sent_count = webform_submission_send_mail($node, $submission, $emails);
+  if ($sent_count) {
+    drupal_set_message(format_plural($sent_count,
+      'Successfully re-sent submission #@sid to 1 recipient.',
+      'Successfully re-sent submission #@sid to @count recipients.',
+      array('@sid' => $submission->sid)
+    ));
+  }
+  else {
+    drupal_set_message(t('No e-mails were able to be sent due to a server error.'), 'error');
+  }
+}
+
+/**
+ * Theme the node components form. Use a table to organize the components.
+ *
+ * @param $form
+ *   The form array.
+ * @return
+ *   Formatted HTML form, ready for display.
+ */
+function theme_webform_submission_resend($variables) {
+  $form = $variables['form'];
+
+  $header = array('', t('E-mail to'), t('Subject'));
+  $rows = array();
+  if (!empty($form['emails'])) {
+    foreach (element_children($form['emails']) as $eid) {
+      // Add each component to a table row.
+      $rows[] = array(
+        drupal_render($form['resend'][$eid]),
+        drupal_render($form['emails'][$eid]['email']),
+        drupal_render($form['emails'][$eid]['subject']),
+      );
+    }
+  }
+  else {
+    $rows[] = array(array('data' => t('This webform is currently not setup to send emails.'), 'colspan' => 3));
+  }
+  $output = '';
+  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'webform-emails')));
+  $output .= drupal_render_children($form);
+  return $output;
+}
+
+/**
+ * Print a Webform submission for display on a page or in an e-mail.
+ */
+function webform_submission_render($node, $submission, $email, $format) {
+  $component_tree = array();
+  $renderable = array();
+  $page_count = 1;
+  $excluded_components = isset($email) ? $email['excluded_components'] : array();
+
+  // Meta data that may be useful for modules implementing
+  // hook_webform_submission_render_alter().
+  $renderable['#node'] = $node;
+  $renderable['#submission'] = $submission;
+  $renderable['#email'] = $email;
+  $renderable['#format'] = $format;
+
+  // Set the theme function for submissions.
+  $renderable['#theme'] = array('webform_submission_' . $node->nid, 'webform_submission');
+
+  // Remove excluded components.
+  $components = $node->webform['components'];
+  foreach ($excluded_components as $cid) {
+    unset($components[$cid]);
+  }
+
+  _webform_components_tree_build($components, $component_tree, 0, $page_count);
+
+  // Make sure at least one field is available
+  if (isset($component_tree['children'])) {
+    // Recursively add components to the form.
+    foreach ($component_tree['children'] as $cid => $component) {
+      if (_webform_client_form_rule_check($node, $component, $component['page_num'], NULL, $submission)) {
+        _webform_client_form_add_component($node, $component, NULL, $renderable, $renderable, NULL, $submission, $format);
+      }
+    }
+  }
+
+  drupal_alter('webform_submission_render', $renderable);
+  return drupal_render($renderable);
+}
+
+/**
+ * Return all the submissions for a particular node.
+ *
+ * @param $filters
+ *   An array of filters to apply to this query. Usually in the format
+ *   array('nid' => $nid, 'uid' => $uid). A single integer may also be passed
+ *   in, which will be equivalent to specifying a $nid filter.
+ * @param $header
+ *   If the results of this fetch will be used in a sortable
+ *   table, pass the array header of the table.
+ * @param $pager_count
+ *   Optional. The number of submissions to include in the results.
+ */
+function webform_get_submissions($filters = array(), $header = NULL, $pager_count = 0) {
+  $submissions = array();
+
+  if (!is_array($filters)) {
+    $filters = array('nid' => $filters);
+  }
+
+  // UID filters need to be against a specific table.
+  if (isset($filters['uid'])) {
+    $filters['u.uid'] = $filters['uid'];
+    unset($filters['uid']);
+  }
+
+  // No need to find SIDs if it was given to us.
+  if (isset($filters['sid'])) {
+    $sids = array($filters['sid']);
+  }
+  // Build the list of SIDs that need to be retrieved.
+  else {
+    $pager_query = db_select('webform_submissions', 'ws')->fields('ws', array('sid'));
+    foreach ($filters as $column => $value) {
+      $pager_query->condition($column, $value);
+    }
+
+    if (isset($filters['u.uid']) || !empty($header)) {
+      // Join to the users table for sorting by user name.
+      $pager_query->leftJoin('users', 'u', 'u.uid = ws.uid');
+    }
+
+    if (isset($filters['u.uid']) && $filters['u.uid'] === 0) {
+      if (!empty($_SESSION['webform_submission'])) {
+        $anonymous_sids = array_keys($_SESSION['webform_submission']);
+        $pager_query->condition('sid', $anonymous_sids, 'IN');
+      }
+      else {
+        $pager_query->condition('sid', 0);
+      }
+    }
+
+    if (is_array($header)) {
+      // Extending the query instatiates a new query object.
+      $pager_query = $pager_query->extend('TableSort');
+      $pager_query->orderByHeader($header);
+    }
+    else {
+      $pager_query->orderBy('sid', 'ASC');
+    }
+
+    if ($pager_count) {
+      // Extending the query instatiates a new query object.
+      $pager_query = $pager_query->extend('PagerDefault');
+      $pager_query->limit($pager_count);
+    }
+    $result = $pager_query->execute();
+
+    $sids = array();
+    foreach ($result as $row) {
+      $sids[] = $row->sid;
+      $submissions[$row->sid] = FALSE;
+    }
+  }
+
+  // If there are no submissions being retrieved, return an empty array.
+  if (empty($sids)) {
+    return $submissions;
+  }
+
+  // Query the required submission data.
+  $query = db_select('webform_submitted_data', 'sd');
+  $query->leftJoin('webform_submissions', 's', 's.sid = sd.sid');
+  $query->leftJoin('users', 'u', 'u.uid = s.uid');
+  $query
+    ->fields('s')
+    ->fields('sd', array('cid', 'no', 'data'))
+    ->fields('u', array('name'))
+    ->condition('sd.sid', $sids, 'IN')
+    ->orderBy('sd.sid', 'ASC')
+    ->orderBy('sd.cid', 'ASC')
+    ->orderBy('sd.no', 'ASC');
+
+  // By adding the NID to this query we allow MySQL to use the primary key on
+  // in webform_submitted_data for sorting (nid_sid_cid_no).
+  if (isset($filters['nid'])) {
+    $query->condition('sd.nid', $filters['nid']);
+  }
+
+  $result = $query->execute();
+
+  // Convert the queried rows into submissions.
+  $previous = 0;
+  foreach ($result as $row) {
+    if ($row->sid != $previous) {
+      $submissions[$row->sid] = new stdClass();
+      $submissions[$row->sid]->sid = $row->sid;
+      $submissions[$row->sid]->nid = $row->nid;
+      $submissions[$row->sid]->submitted = $row->submitted;
+      $submissions[$row->sid]->remote_addr = $row->remote_addr;
+      $submissions[$row->sid]->uid = $row->uid;
+      $submissions[$row->sid]->name = $row->name;
+      $submissions[$row->sid]->is_draft = $row->is_draft;
+      $submissions[$row->sid]->data = array();
+    }
+    // CID may be NULL if this submission does not actually contain any data.
+    if ($row->cid) {
+      $submissions[$row->sid]->data[$row->cid]['value'][$row->no] = $row->data;
+    }
+    $previous = $row->sid;
+  }
+
+  foreach (module_implements('webform_submission_load') as $module) {
+    $function = $module . '_webform_submission_load';
+    $function($submissions);
+  }
+
+  return $submissions;
+}
+
+/**
+ * Return a count of the total number of submissions for a node.
+ *
+ * @param $nid
+ *   The node ID for which submissions are being fetched.
+ * @param $uid
+ *   Optional; the user ID to filter the submissions by.
+ * @return
+ *   An integer value of the number of submissions.
+ */
+function webform_get_submission_count($nid, $uid = NULL, $reset = FALSE) {
+  static $counts;
+
+  if (!isset($counts[$nid][$uid]) || $reset) {
+    $query = db_select('webform_submissions', 'ws')
+      ->addTag('webform_get_submission_count')
+      ->condition('ws.nid', $nid)
+      ->condition('ws.is_draft', 0);
+    $arguments = array($nid);
+    if ($uid !== NULL) {
+      $query->condition('ws.uid', $uid);
+    }
+    if ($uid === 0) {
+      $submissions = isset($_SESSION['webform_submission']) ? $_SESSION['webform_submission'] : NULL;
+      if ($submissions) {
+        $query->condition('ws.sid', $submissions, 'IN');
+      }
+      else {
+        // Intentionally never match anything if the anonymous user has no
+        // submissions.
+        $query->condition('ws.sid', 0);
+      }
+    }
+
+    $counts[$nid][$uid] = $query->countQuery()->execute()->fetchField();
+  }
+  return $counts[$nid][$uid];
+}
+
+/**
+ * Fetch a specified submission for a webform node.
+ */
+function webform_get_submission($nid, $sid, $reset = FALSE) {
+  static $submissions = array();
+
+  if ($reset) {
+    $submissions = array();
+    if (!isset($sid)) {
+      return;
+    }
+  }
+
+  // Load the submission if needed.
+  if (!isset($submissions[$sid])) {
+    $new_submissions = webform_get_submissions(array('nid' => $nid, 'sid' => $sid));
+    $submissions[$sid] = isset($new_submissions[$sid]) ? $new_submissions[$sid] : FALSE;
+  }
+
+  return $submissions[$sid];
+}
+
+function _webform_submission_spam_check($to, $subject, $from, $headers = array()) {
+  $headers = implode('\n', (array)$headers);
+  // Check if they are attempting to spam using a bcc or content type hack.
+  if (preg_match('/(b?cc\s?:)|(content\-type:)/i', $to . "\n" . $subject . "\n" . $from . "\n" . $headers)) {
+    return TRUE; // Possible spam attempt.
+  }
+  return FALSE; // Not spam.
+}
+
+/**
+ * Check if the current user has exceeded the limit on this form.
+ *
+ * @param $node
+ *   The webform node to be checked.
+ * @return
+ *   Boolean TRUE if the user has exceeded their limit. FALSE otherwise.
+ */
+function _webform_submission_user_limit_check($node) {
+  global $user;
+
+  // Check if submission limiting is enabled.
+  if ($node->webform['submit_limit'] == '-1') {
+    return FALSE; // No check enabled.
+  }
+
+  // Retrieve submission data for this IP address or username from the database.
+  $query = db_select('webform_submissions')
+    ->condition('nid', $node->nid)
+    ->condition('is_draft', 0);
+
+  if ($node->webform['submit_interval'] != -1) {
+    $query->condition('submitted', REQUEST_TIME - $node->webform['submit_interval'], '>');
+  }
+
+  if ($user->uid) {
+    $query->condition('uid', $user->uid);
+  }
+  else {
+    $query->condition('remote_addr', ip_address());
+  }
+
+  // Fetch all the entries from the database within the submit interval with this username and IP.
+  $num_submissions_database = $query->countQuery()->execute()->fetchField();
+
+  // Double check the submission history from the users machine using cookies.
+  $num_submissions_cookie = 0;
+  if ($user->uid == 0 && variable_get('webform_use_cookies', 0)) {
+    $cookie_name = 'webform-' . $node->nid;
+
+    if (isset($_COOKIE[$cookie_name]) && is_array($_COOKIE[$cookie_name])) {
+      foreach ($_COOKIE[$cookie_name] as $key => $timestamp) {
+        if ($node->webform['submit_interval'] != -1 && $timestamp <= REQUEST_TIME - $node->webform['submit_interval']) {
+          // Remove the cookie if past the required time interval.
+          $params = session_get_cookie_params();
+          setcookie($cookie_name . '[' . $key . ']', '', 0, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
+        }
+      }
+      // Count the number of submissions recorded in cookies.
+      $num_submissions_cookie = count($_COOKIE[$cookie_name]);
+    }
+    else {
+      $num_submissions_cookie = 0;
+    }
+  }
+
+  if ($num_submissions_database >= $node->webform['submit_limit'] || $num_submissions_cookie >= $node->webform['submit_limit']) {
+    // Limit exceeded.
+    return TRUE;
+  }
+
+  // Limit not exceeded.
+  return FALSE;
+}
+
+/**
+ * Check if the total number of submissions has exceeded the limit on this form.
+ *
+ * @param $node
+ *   The webform node to be checked.
+ * @return
+ *   Boolean TRUE if the form has exceeded it's limit. FALSE otherwise.
+ */
+function _webform_submission_total_limit_check($node) {
+
+  // Check if submission limiting is enabled.
+  if ($node->webform['total_submit_limit'] == '-1') {
+    return FALSE; // No check enabled.
+  }
+
+  // Retrieve submission data from the database.
+  $query = db_select('webform_submissions')
+    ->condition('nid', $node->nid)
+    ->condition('is_draft', 0);
+
+  if ($node->webform['total_submit_interval'] != -1) {
+    $query->condition('submitted', REQUEST_TIME - $node->webform['total_submit_interval'], '>');
+  }
+
+  // Fetch all the entries from the database within the submit interval.
+  $num_submissions_database = $query->countQuery()->execute()->fetchField();
+
+  if ($num_submissions_database >= $node->webform['total_submit_limit']) {
+    // Limit exceeded.
+    return TRUE;
+  }
+
+  // Limit not exceeded.
+  return FALSE;
+}
+
+/**
+ * Preprocess function for webform-submission.tpl.php.
+ */
+function template_preprocess_webform_submission(&$vars) {
+  $vars['node'] = $vars['renderable']['#node'];
+  $vars['submission'] = $vars['renderable']['#submission'];
+  $vars['email'] = $vars['renderable']['#email'];
+  $vars['format'] = $vars['renderable']['#format'];
+}
+
+/**
+ * Preprocess function for webform-submission-navigation.tpl.php.
+ */
+function template_preprocess_webform_submission_navigation(&$vars) {
+  $start_path = ($vars['mode'] == 'print') ? 'print/' : 'node/';
+
+  $previous_query = db_select('webform_submissions')
+    ->condition('nid', $vars['node']->nid)
+    ->condition('sid', $vars['submission']->sid, '<');
+  $previous_query->addExpression('MAX(sid)');
+
+  $next_query = db_select('webform_submissions')
+    ->condition('nid', $vars['node']->nid)
+    ->condition('sid', $vars['submission']->sid, '>');
+  $next_query->addExpression('MIN(sid)');
+
+  $vars['previous'] = $previous_query->execute()->fetchField();
+  $vars['next'] = $next_query->execute()->fetchField();
+  $vars['previous_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['previous'] . ($vars['mode'] == 'form' ? '/edit' : '');
+  $vars['next_url'] = $start_path . $vars['node']->nid . '/submission/' . $vars['next'] . ($vars['mode'] == 'form' ? '/edit' : '');
+}
+
+/**
+ * Preprocess function for webform-submission-navigation.tpl.php.
+ */
+function template_preprocess_webform_submission_information(&$vars) {
+  $vars['account'] = user_load($vars['submission']->uid);
+  $vars['actions'] = theme('links', module_invoke_all('webform_submission_actions', $vars['node'], $vars['submission']));
+}
diff -Naurw webform_old/js/select-admin.js webform/js/select-admin.js
--- webform_old/js/select-admin.js	1970-01-01 01:00:00.000000000 +0100
+++ webform/js/select-admin.js	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,56 @@
+
+/**
+ * @file
+ * Enhancements for select list configuration options.
+ */
+
+(function ($) {
+
+Drupal.behaviors.webformSelectLoadOptions = {};
+Drupal.behaviors.webformSelectLoadOptions.attach = function(context) {
+  settings = Drupal.settings;
+
+  $('#edit-extra-options-source', context).change(function() {
+    var url = settings.webform.selectOptionsUrl + '/' + this.value;
+    $.ajax({
+      url: url,
+      success: Drupal.webform.selectOptionsLoad,
+      dataType: 'json'
+    });
+  });
+}
+
+Drupal.webform = Drupal.webform || {};
+
+Drupal.webform.selectOptionsOriginal = false;
+Drupal.webform.selectOptionsLoad = function(result) {
+  if (Drupal.optionsElement) {
+    if (result.options) {
+      // Save the current select options the first time a new list is chosen.
+      if (Drupal.webform.selectOptionsOriginal === false) {
+        Drupal.webform.selectOptionsOriginal = $(Drupal.optionElements[result.elementId].manualOptionsElement).val();
+      }
+      $(Drupal.optionElements[result.elementId].manualOptionsElement).val(result.options);
+      Drupal.optionElements[result.elementId].disable();
+      Drupal.optionElements[result.elementId].updateWidgetElements();
+    }
+    else {
+      Drupal.optionElements[result.elementId].enable();
+      if (Drupal.webform.selectOptionsOriginal) {
+        $(Drupal.optionElements[result.elementId].manualOptionsElement).val(Drupal.webform.selectOptionsOriginal);
+        Drupal.optionElements[result.elementId].updateWidgetElements();
+        Drupal.webform.selectOptionsOriginal = false;
+      }
+    }
+  }
+  else {
+    if (result.options) {
+      $('#' + result.elementId).val(result.options).attr('readonly', 'readonly');
+    }
+    else {
+      $('#' + result.elementId).attr('readonly', '');
+    }
+  }
+}
+
+})(jQuery);
diff -Naurw webform_old/js/webform-admin.js webform/js/webform-admin.js
--- webform_old/js/webform-admin.js	1970-01-01 01:00:00.000000000 +0100
+++ webform/js/webform-admin.js	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,119 @@
+(function ($) {
+
+/**
+ * Webform node form interface enhancments.
+ */
+
+Drupal.behaviors.webformAdmin = {};
+Drupal.behaviors.webformAdmin.attach = function(context) {
+  // Apply special behaviors to fields with default values.
+  Drupal.webform.defaultValues(context);
+  // On click or change, make a parent radio button selected.
+  Drupal.webform.setActive(context);
+  // Update the template select list upon changing a template.
+  Drupal.webform.updateTemplate(context);
+  // Select all link for file extensions.
+  Drupal.webform.selectCheckboxesLink(context);
+  // Enhance the normal tableselect.js file to support indentations.
+  Drupal.webform.tableSelectIndentation(context);
+}
+
+Drupal.webform = Drupal.webform || {};
+
+Drupal.webform.defaultValues = function(context) {
+  var $fields = $('.webform-default-value:not(.error)', context);
+  var $forms = $fields.parents('form:first');
+  $fields.each(function() {
+    this.defaultValue = $(this).attr('rel');
+    if (this.value != this.defaultValue) {
+      $(this).removeClass('webform-default-value');
+    }
+    $(this).focus(function() {
+      if (this.value == this.defaultValue) {
+        this.value = '';
+        $(this).removeClass('webform-default-value');
+      }
+    });
+    $(this).blur(function() {
+      if (this.value == '') {
+        $(this).addClass('webform-default-value');
+        this.value = this.defaultValue;
+      }
+    });
+  });
+
+  // Clear all the form elements before submission.
+  $forms.submit(function() {
+    $fields.focus();
+  });
+};
+
+Drupal.webform.setActive = function(context) {
+  var setActive = function(e) {
+    $('.form-radio', $(this).parent().parent()).attr('checked', true);
+    e.preventDefault();
+  };
+  $('.webform-set-active', context).click(setActive).change(setActive);
+};
+
+Drupal.webform.updateTemplate = function(context) {
+  var defaultTemplate = $('#edit-templates-default').val();
+  var $templateSelect = $('#webform-template-fieldset select#edit-template-option', context);
+  var $templateTextarea = $('#webform-template-fieldset textarea:visible', context);
+
+  var updateTemplateSelect = function() {
+    if ($(this).val() == defaultTemplate) {
+      $templateSelect.val('default');
+    }
+    else {
+      $templateSelect.val('custom');
+    }
+  }
+
+  var updateTemplateText = function() {
+    if ($(this).val() == 'default' && $templateTextarea.val() != defaultTemplate) {
+      if (confirm(Drupal.settings.webform.revertConfirm)) {
+        $templateTextarea.val(defaultTemplate);
+      }
+      else {
+        $(this).val('custom');
+      }
+    }
+  }
+
+  $templateTextarea.keyup(updateTemplateSelect);
+  $templateSelect.change(updateTemplateText);
+}
+
+Drupal.webform.selectCheckboxesLink = function(context) {
+  function selectCheckboxes() {
+    var group = this.className.replace(/.*?webform-select-link-([^ ]*).*/, '$1');
+    var $checkboxes = $('.webform-select-group-' + group + ' input[type=checkbox]');
+    var reverseCheck = !$checkboxes[0].checked;
+    $checkboxes.each(function() {
+      this.checked = reverseCheck;
+    });
+    $checkboxes.trigger('change');
+    return false;
+  }
+  $('a.webform-select-link', context).click(selectCheckboxes);
+}
+
+Drupal.webform.tableSelectIndentation = function(context) {
+  var $tables = $('th.select-all', context).parents('table');
+  $tables.find('input.form-checkbox').change(function() {
+    var $rows = $(this).parents('table:first').find('tr');
+    var row = $(this).parents('tr:first').get(0);
+    var rowNumber = $rows.index(row);
+    var rowTotal = $rows.size();
+    var indentLevel = $(row).find('div.indentation').size();
+    for (var n = rowNumber + 1; n < rowTotal; n++) {
+      if ($rows.eq(n).find('div.indentation').size() <= indentLevel) {
+        break;
+      }
+      $rows.eq(n).find('input.form-checkbox').attr('checked', this.checked);
+    }
+  });
+}
+
+})(jQuery);
diff -Naurw webform_old/js/webform.js webform/js/webform.js
--- webform_old/js/webform.js	1970-01-01 01:00:00.000000000 +0100
+++ webform/js/webform.js	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,86 @@
+
+/**
+ * JavaScript behaviors for the front-end display of webforms.
+ */
+
+(function ($) {
+
+Drupal.behaviors.webform = Drupal.behaviors.webform || {};
+
+Drupal.behaviors.webform.attach = function(context) {
+  // Calendar datepicker behavior.
+  Drupal.webform.datepicker(context);
+};
+
+Drupal.webform = Drupal.webform || {};
+
+Drupal.webform.datepicker = function(context) {
+  $('div.webform-datepicker').each(function() {
+    var $webformDatepicker = $(this);
+    var $calendar = $webformDatepicker.find('input.webform-calendar');
+
+    // Ensure the page we're on actually contains a datepicker.
+    if ($calendar.length == 0) { 
+      return;
+    }
+
+    var startDate = $calendar[0].className.replace(/.*webform-calendar-start-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-');
+    var endDate = $calendar[0].className.replace(/.*webform-calendar-end-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-');
+    var firstDay = $calendar[0].className.replace(/.*webform-calendar-day-(\d).*/, '$1');
+    // Convert date strings into actual Date objects.
+    startDate = new Date(startDate[0], startDate[1] - 1, startDate[2]);
+    endDate = new Date(endDate[0], endDate[1] - 1, endDate[2]);
+
+    // Ensure that start comes before end for datepicker.
+    if (startDate > endDate) {
+      var laterDate = startDate;
+      startDate = endDate;
+      endDate = laterDate;
+    }
+
+    var startYear = startDate.getFullYear();
+    var endYear = endDate.getFullYear();
+
+    // Set up the jQuery datepicker element.
+    $calendar.datepicker({
+      dateFormat: 'yy-mm-dd',
+      yearRange: startYear + ':' + endYear,
+      firstDay: parseInt(firstDay),
+      minDate: startDate,
+      maxDate: endDate,
+      onSelect: function(dateText, inst) {
+        var date = dateText.split('-');
+        $webformDatepicker.find('select.year, input.year').val(+date[0]);
+        $webformDatepicker.find('select.month').val(+date[1]);
+        $webformDatepicker.find('select.day').val(+date[2]);
+      },
+      beforeShow: function(input, inst) {
+        // Get the select list values.
+        var year = $webformDatepicker.find('select.year, input.year').val();
+        var month = $webformDatepicker.find('select.month').val();
+        var day = $webformDatepicker.find('select.day').val();
+
+        // If empty, default to the current year/month/day in the popup.
+        var today = new Date();
+        year = year ? year : today.getFullYear();
+        month = month ? month : today.getMonth() + 1;
+        day = day ? day : today.getDate();
+
+        // Make sure that the default year fits in the available options.
+        year = (year < startYear || year > endYear) ? startYear : year;
+
+        // jQuery UI Datepicker will read the input field and base its date off
+        // of that, even though in our case the input field is a button.
+        $(input).val(year + '-' + month + '-' + day);
+      }
+    });
+
+    // Prevent the calendar button from submitting the form.
+    $calendar.click(function(event) {
+      $(this).focus();
+      event.preventDefault();
+    });
+  });
+}
+
+})(jQuery);
diff -Naurw webform_old/templates/webform-calendar.tpl.php webform/templates/webform-calendar.tpl.php
--- webform_old/templates/webform-calendar.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-calendar.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,8 @@
+<?php
+
+/**
+ * @file
+ * Theme the button for the date component date popup.
+ */
+?>
+<input type="image" src="<?php print base_path() . drupal_get_path('module', 'webform') . '/images/calendar.png'; ?>" class="<?php print implode(' ', $calendar_classes); ?>" alt="<?php print t('Open popup calendar'); ?>" title="<?php print t('Open popup calendar'); ?>" />
diff -Naurw webform_old/templates/webform-confirmation.tpl.php webform/templates/webform-confirmation.tpl.php
--- webform_old/templates/webform-confirmation.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-confirmation.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Customize confirmation screen after successful submission.
+ *
+ * This file may be renamed "webform-confirmation-[nid].tpl.php" to target a
+ * specific webform e-mail on your site. Or you can leave it
+ * "webform-confirmation.tpl.php" to affect all webform confirmations on your
+ * site.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $confirmation_message: The confirmation message input by the webform author.
+ * - $sid: The unique submission ID of this submission.
+ */
+?>
+
+<div class="webform-confirmation">
+  <?php if ($confirmation_message): ?>
+    <?php print $confirmation_message ?>
+  <?php else: ?>
+    <p><?php print t('Thank you, your submission has been received.'); ?></p>
+  <?php endif; ?>
+</div>
+
+<div class="links">
+  <a href="<?php print url('node/'. $node->nid) ?>"><?php print t('Go back to the form') ?></a>
+</div>
diff -Naurw webform_old/templates/webform-form.tpl.php webform/templates/webform-form.tpl.php
--- webform_old/templates/webform-form.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-form.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Customize the display of a complete webform.
+ *
+ * This file may be renamed "webform-form-[nid].tpl.php" to target a specific
+ * webform on your site. Or you can leave it "webform-form.tpl.php" to affect
+ * all webforms on your site.
+ *
+ * Available variables:
+ * - $form: The complete form array.
+ * - $nid: The node ID of the Webform.
+ *
+ * The $form array contains two main pieces:
+ * - $form['submitted']: The main content of the user-created form.
+ * - $form['details']: Internal information stored by Webform.
+ */
+?>
+<?php
+  // Print out the main part of the form.
+  // Feel free to break this up and move the pieces within the array.
+  print drupal_render($form['submitted']);
+
+  // Always print out the entire $form. This renders the remaining pieces of the
+  // form that haven't yet been rendered above.
+  print drupal_render_children($form);
diff -Naurw webform_old/templates/webform-mail.tpl.php webform/templates/webform-mail.tpl.php
--- webform_old/templates/webform-mail.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-mail.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Customize the e-mails sent by Webform after successful submission.
+ *
+ * This file may be renamed "webform-mail-[nid].tpl.php" to target a
+ * specific webform e-mail on your site. Or you can leave it
+ * "webform-mail.tpl.php" to affect all webform e-mails on your site.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $submission: The webform submission.
+ * - $email: The entire e-mail configuration settings.
+ * - $user: The current user submitting the form.
+ * - $ip_address: The IP address of the user submitting the form.
+ *
+ * The $email['email'] variable can be used to send different e-mails to different users
+ * when using the "default" e-mail template.
+ */
+?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted on %date'). ($email['html'] ? '</p>' : ''); ?>
+
+<?php if ($user->uid): ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted by user: %username') . ($email['html'] ? '</p>' : ''); ?>
+<?php else: ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted by anonymous user: [%ip_address]') . ($email['html'] ? '</p>' : ''); ?>
+<?php endif; ?>
+
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted values are') . ':' . ($email['html'] ? '</p>' : ''); ?>
+
+%email_values
+
+<?php print ($email['html'] ? '<p>' : '') . t('The results of this submission may be viewed at:') . ($email['html'] ? '</p>' : '') ?>
+
+<?php print ($email['html'] ? '<p>' : ''); ?>%submission_url<?php print ($email['html'] ? '</p>' : ''); ?>
diff -Naurw webform_old/templates/webform-results-submissions.tpl.php webform/templates/webform-results-submissions.tpl.php
--- webform_old/templates/webform-results-submissions.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-results-submissions.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,35 @@
+<?php
+// $Id:
+
+/**
+ * @file
+ * Result submissions page.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $submissions: The Webform submissions array.
+ * - $total_count: The total number of submissions to this webform.
+ * - $pager_count: The number of results to be shown per page.
+ * - is_submissions: The user is viewing the node/NID/submissions page.
+ * - $table: The table[] array consists of three keys:
+ * - $table['#header']: Table header.
+ * - $table['#rows']: Table rows.
+ * - $table['#operation_total']: Maximum number of operations in the operation column.
+ */
+?>
+
+<?php if (count($table['#rows'])): ?>
+  <?php print theme('webform_results_per_page', array('total_count' => $total_count, 'pager_count' => $pager_count)); ?>
+  <?php print render($table); ?>
+<?php else: ?>
+  <?php print t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))); ?>
+<?php endif; ?>
+
+
+<?php if ($is_submissions): ?>
+  <?php print theme('links', array('links' => array('webform' => array('title' => t('Go back to the form'), 'href' => 'node/' . $node->nid)))); ?>
+<?php endif; ?>
+
+<?php if ($pager_count): ?>
+  <?php print theme('pager', array('limit' => $pager_count)); ?>
+<?php endif; ?>
diff -Naurw webform_old/templates/webform-submission-information.tpl.php webform/templates/webform-submission-information.tpl.php
--- webform_old/templates/webform-submission-information.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-submission-information.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Customize the header information shown when editing or viewing submissions.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $mode: Either "form" or "display". May be other modes provided by other
+ *          modules, such as "print" or "pdf".
+ * - $submission: The contents of the webform submission.
+ * - $account: The user that submitted the form.
+ */
+?>
+<fieldset class="webform-submission-info clearfix">
+  <legend><?php print t('Submission information'); ?></legend>
+  <?php print theme('user_picture', array('account' => $account)); ?>
+  <div class="webform-submission-info-text">
+    <div><?php print t('Form: !form', array('!form' => l($node->title, 'node/' . $node->nid))); ?></div>
+    <div><?php print t('Submitted by !name', array('!name' => theme('username', array('account' => $account)))); ?></div>
+    <div><?php print format_date($submission->submitted, 'long'); ?></div>
+    <div><?php print $submission->remote_addr; ?></div>
+  </div>
+</fieldset>
diff -Naurw webform_old/templates/webform-submission-navigation.tpl.php webform/templates/webform-submission-navigation.tpl.php
--- webform_old/templates/webform-submission-navigation.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-submission-navigation.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Customize the navigation shown when editing or viewing submissions.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $mode: Either "form" or "display". May be other modes provided by other
+ *          modules, such as "print" or "pdf".
+ * - $submission: The contents of the webform submission.
+ * - $previous: The previous submission ID.
+ * - $next: The next submission ID.
+ * - $previous_url: The URL of the previous submission.
+ * - $next_url: The URL of the next submission.
+ */
+?>
+<div class="webform-submission-navigation">
+  <?php if ($previous): ?>
+    <?php print l(t('Previous submission'), $previous_url, array('attributes' => array('class' => array('webform-submission-previous')), 'query' => ($mode == 'form' ? array('destination' => $previous_url) : NULL))); ?>
+  <?php else: ?>
+    <span class="webform-submission-previous"><?php print t('Previous submission'); ?></span>
+  <?php endif; ?>
+
+  <?php if ($next): ?>
+    <?php print l(t('Next submission'), $next_url, array('attributes' => array('class' => array('webform-submission-next')), 'query' => ($mode == 'form' ? array('destination' => $next_url) : NULL))); ?>
+  <?php else: ?>
+    <span class="webform-submission-next"><?php print t('Next submission'); ?></span>
+  <?php endif; ?>
+</div>
diff -Naurw webform_old/templates/webform-submission-page.tpl.php webform/templates/webform-submission-page.tpl.php
--- webform_old/templates/webform-submission-page.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-submission-page.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Customize the navigation shown when editing or viewing submissions.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $mode: Either "form" or "display". May be other modes provided by other
+ *          modules, such as "print" or "pdf".
+ * - $submission: The Webform submission array.
+ * - $submission_content: The contents of the webform submission.
+ * - $submission_navigation: The previous submission ID.
+ * - $submission_information: The next submission ID.
+ */
+?>
+
+<?php if ($mode == 'display' || $mode == 'form'): ?>
+  <div class="clearfix">
+    <?php print $submission_actions; ?>
+    <?php print $submission_navigation; ?>
+  </div>
+<?php endif; ?>
+
+<?php print $submission_information; ?>
+
+<div class="webform-submission">
+  <?php print render($submission_content); ?>
+</div>
+
+<?php if ($mode == 'display' || $mode == 'form'): ?>
+  <div class="clearfix">
+    <?php print $submission_navigation; ?>
+  </div>
+<?php endif; ?>
diff -Naurw webform_old/templates/webform-submission.tpl.php webform/templates/webform-submission.tpl.php
--- webform_old/templates/webform-submission.tpl.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/templates/webform-submission.tpl.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Customize the display of a webform submission.
+ *
+ * Available variables:
+ * - $node: The node object for this webform.
+ * - $submission: The Webform submission array.
+ * - $email: If sending this submission in an e-mail, the e-mail configuration
+ *   options.
+ * - $format: The format of the submission being printed, either "html" or
+ *   "text".
+ * - $renderable: The renderable submission array, used to print out individual
+ *   parts of the submission, just like a $form array.
+ */
+?>
+
+<?php print drupal_render_children($renderable); ?>
diff -Naurw webform_old/tests/components.test webform/tests/components.test
--- webform_old/tests/components.test	1970-01-01 01:00:00.000000000 +0100
+++ webform/tests/components.test	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Webform module component tests.
+ */
+
+include_once(dirname(__FILE__) . '/webform.test');
+
+class WebformComponentsTestCase extends WebformTestCase {
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Webform components'),
+      'description' => t('Add and remove components from a webform.'),
+      'group' => t('Webform'),
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Implements testWebformDummy(). if it is not present,
+   * then the test runs fine, but when combined with other tests
+   * the whole block fails, since there would be no output.
+   */
+  function testWebformDummy() {
+    $this->pass = t('WebformComponentsTest pass.');
+  }
+}
diff -Naurw webform_old/tests/permissions.test webform/tests/permissions.test
--- webform_old/tests/permissions.test	1970-01-01 01:00:00.000000000 +0100
+++ webform/tests/permissions.test	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Webform module permission tests.
+ */
+
+include_once(dirname(__FILE__) . '/webform.test');
+
+class WebformPermissionsTestCase extends WebformTestCase {
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Webform permissions'),
+      'description' => t('Create webforms and check editing and access permissions.'),
+      'group' => t('Webform'),
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Implements tearDown().
+   */
+  function tearDown() {
+    parent::tearDown();
+  }
+
+  /**
+   * Create a webform node in which authenticated users have access to submit.
+  */
+  function testWebformSubmitAccess() {
+    $this->webformReset();
+    $node = $this->testWebformForm();
+    $node->webform['roles'] = array(2);
+    node_save($node);
+
+    // Test that the authenticated user is able to access.
+    $this->drupalLogin($this->webform_users['userAccess']);
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertText($node->body[LANGUAGE_NONE][0]['value'], t('Webform node created and accessible to authenticated users at !url', array('!url' => 'node/' . $node->nid)), t('Webform'));
+
+    // Confirm that the submission has been created.
+    $this->drupalPost(NULL, array(), 'Submit');
+    $this->assertText($node->webform['confirmation'], t('Confirmation message "@confirmation" received.', array('@confirmation' => $node->webform['confirmation'])), t('Webform'));
+    $this->drupalLogout();
+
+    // The anonymous user should not be able to submit.
+    $this->drupalGet('node/' . $node->nid);
+
+    // Note: Should be: You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.
+    // Something in SimpleTest isn't handling the string correctly.
+    $this->assertText('to view this form.', t('Anonymous user is not allowed to submit form.'), t('Webform'));
+  }
+
+  /**
+   * Create webform
+   */
+
+}
diff -Naurw webform_old/tests/submission.test webform/tests/submission.test
--- webform_old/tests/submission.test	1970-01-01 01:00:00.000000000 +0100
+++ webform/tests/submission.test	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * @file
+ * Webform module submission tests.
+ */
+
+include_once(dirname(__FILE__) . '/webform.test');
+
+class WebformSubmissionTestCase extends WebformTestCase {
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Webform submission'),
+      'description' => t('Submits a sample webform and checks the database integrity.'),
+      'group' => t('Webform'),
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Implements tearDown().
+   */
+  function tearDown() {
+    parent::tearDown();
+  }
+
+  /**
+   * Test sending a submission and check database integrity.
+   */
+  function testWebformSubmission() {
+    $this->drupalLogin($this->webform_users['admin']);
+    $this->webformReset();
+    $this->webformSubmissionExecute('sample');
+    $this->drupalLogout();
+  }
+
+  /**
+   * Test a submission that uses default values, and check database integrity.
+   */
+  function testWebformSubmissionDefault() {
+    $this->drupalLogin($this->webform_users['admin']);
+    $this->webformReset();
+    $this->webformSubmissionExecute('default');
+    $this->drupalLogout();
+  }
+
+  /**
+   * Test validation errors on each component that has specialized validation.
+   */
+  function testWebformSubmissionValidate() {
+    $this->drupalLogin($this->webform_users['admin']);
+    $this->webformReset();
+    $this->webformSubmissionValidateExecute();
+    $this->drupalLogout();
+  }
+
+  /**
+   * Test that required fields with no default value can't be submitted as-is.
+   */
+  function testWebformSubmissionRequiredComponents() {
+    $this->drupalLogin($this->webform_users['admin']);
+    $this->webformReset();
+
+    // Create the Webform test node, and set all components to be mandatory
+    // with no default value.
+    $node = $this->testWebformForm();
+    $node = node_load($node->nid);
+    foreach ($node->webform['components'] as &$component) {
+      $component['value'] = '';
+      $component['mandatory'] = '1';
+    }
+    node_save($node);
+
+    // Submit the webform with no data. We should get a message that all the
+    // components are required. (The exceptions are hidden fields, which can't
+    // be made mandatory, and date fields, which default to the current date
+    // when no default value is provided; therefore, we don't expect a message
+    // for those.)
+    $this->drupalPost('node/' . $node->nid, array(), 'Submit', array(), array(), 'webform-client-form-' . $node->nid);
+    foreach ($node->webform['components'] as $component) {
+      if ($component['type'] != 'hidden' && $component['type'] != 'date') {
+        $this->assertText(t('!name field is required.', array('!name' => $component['name'])));
+      }
+    }
+
+    $this->drupalLogout();
+  }
+
+  /**
+   * Execute the submission test.
+   *
+   * @param $value_type
+   *   The values to be submitted to the webform. Either "sample" or "default".
+   */
+  function webformSubmissionExecute($value_type = 'sample') {
+    $path = drupal_get_path('module', 'webform');
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+    // Create a new Webform test node.
+    $node = $this->testWebformForm();
+    $submission_values = $value_type == 'sample' ? $this->testWebformPost() : array();
+
+    // Visit the node page with the "foo=bar" query, to test %get[] default values.
+    $this->drupalGet('node/' . $node->nid, array('query' => array('foo' => 'bar')));
+    $this->assertText($node->body[LANGUAGE_NONE][0]['value'], t('Webform node created and accessible at !url', array('!url' => 'node/' . $node->nid)), t('Webform'));
+
+    // Submit our test data.
+    $this->drupalPost(NULL, $submission_values, 'Submit', array(), array(), 'webform-client-form-' . $node->nid);
+
+    // Confirm that the submission has been created.
+    $this->assertText($node->webform['confirmation'], t('Confirmation message "@confirmation" received.', array('@confirmation' => $node->webform['confirmation'])), t('Webform'));
+
+    // Get the SID of the new submission.
+    $matches = array();
+    preg_match('/sid=([0-9]+)/', $this->getUrl(), $matches);
+    $sid = $matches[1];
+
+    // Pull in the database submission and check the values.
+    $actual_submission = webform_get_submission($node->nid, $sid, TRUE);
+
+    $component_info = $this->testWebformComponents();
+    foreach ($node->webform['components'] as $cid => $component) {
+      $stable_value = $value_type == 'sample' ? $component_info[$component['form_key']]['database values'] : $component_info[$component['form_key']]['database default values'];
+      $actual_value = $actual_submission->data[$cid]['value'];
+      $result = $this->assertEqual($stable_value, $actual_value, t('Component @form_key data integrity check', array('@form_key' => $component['form_key'])), t('Webform'));
+      if (!$result || $result === 'fail') {
+        $this->fail(t('Expected !expected', array('!expected' => print_r($stable_value, TRUE))) . "\n\n" . t('Recieved !recieved', array('!recieved' => print_r($actual_value, TRUE))), t('Webform'));
+      }
+    }
+  }
+
+  /**
+   * Execute a validation check for a single component.
+   *
+   * @param $value_type
+   *   The values to be submitted to the webform. Either "sample" or "default".
+   */
+  function webformSubmissionValidateExecute() {
+    $path = drupal_get_path('module', 'webform');
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+    // Create a new Webform test node.
+    $node = $this->testWebformForm();
+
+    // Visit the node page.
+    $this->drupalGet('node/' . $node->nid);
+
+    foreach ($this->testWebformComponents() as $key => $component_info) {
+      if (isset($component_info['error values'])) {
+        foreach ($component_info['error values'] as $value => $error_message) {
+          $submission_values = array();
+          $submission_values["submitted[$key]"] = $value;
+
+          // Submit our test data.
+          $this->drupalPost('node/' . $node->nid, $submission_values, 'Submit', array(), array(), 'webform-client-form-' . $node->nid);
+
+          // Confirm that the validation error occurred and the submission did not save.
+          $this->assertRaw($error_message, t('Validation message properly thrown: "%message".', array('%message' => $error_message)), t('Webform'));
+
+          $this->assertFalse(preg_match('/sid=([0-9]+)/', $this->getUrl()), t('Submission not saved.'));
+        }
+      }
+    }
+  }
+}
diff -Naurw webform_old/tests/webform.test webform/tests/webform.test
--- webform_old/tests/webform.test	1970-01-01 01:00:00.000000000 +0100
+++ webform/tests/webform.test	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,808 @@
+<?php
+
+/**
+ * @file
+ * Webform module tests.
+ */
+
+class WebformTestCase extends DrupalWebTestCase {
+  private $_webform_node;
+  private $_webform_components;
+  public $webform_users;
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    // Enable Webform.
+    parent::setUp('webform', 'profile');
+
+    // Create a profile field to test %profile tokens.
+    db_query("INSERT INTO {profile_field} (title, name, explanation, category, type, weight, required, register, visibility, autocomplete, options, page) VALUES ('Gender', 'profile_gender', '', 'Profile', 'textfield', 0, 0, 0, 2, 0, '', '')");
+
+    // Create a normal user that can view their own submissions.
+    $permissions['userAccess'] = array(
+      'access content',
+      'access own webform submissions',
+    );
+
+    // Create a normal user than can edit their own submissions.
+    $permissions['userEdit'] = array(
+      'access content',
+      'edit own webform submissions',
+    );
+
+    // Create a webform editor to test creating and editing own content.
+    $permissions['editor'] = array(
+      'access content',
+      'create webform content',
+      'edit own webform content',
+      'access all webform results',
+    );
+
+    // Create a webform admin that will do all node creation.
+    $permissions['admin'] = array(
+      'access content',
+      'administer nodes',
+      'create webform content',
+      'edit any webform content',
+      'access all webform results',
+      'edit all webform submissions',
+      'delete all webform submissions',
+    );
+
+    foreach ($permissions as $user_key => $role_permissions) {
+      $this->webform_users[$user_key] = $this->drupalCreateUser($role_permissions);
+      $profile = array('profile_gender' => 'Female');
+      $this->webform_users[$user_key]->profile_gender = 'Female';
+      profile_save_profile($profile, $this->webform_users[$user_key], 'Profile');
+    }
+  }
+
+  /**
+   * Implemenation of tearDown().
+   */
+  function tearDown() {
+    // Delete the webform admin and any created nodes.
+    foreach ($this->webform_users as $account) {
+      $uid = $account->uid;
+      $result = db_select('node')
+        ->fields('node')
+        ->condition('uid', $uid)
+        ->execute();
+      foreach ($result as $node) {
+        node_delete($node->nid);
+      }
+      user_cancel(array(), $uid, 'user_cancel_delete');
+    }
+
+    parent::tearDown();
+  }
+
+  /**
+   *
+   */
+  function webformReset() {
+    $this->_webform_node = NULL;
+    $this->_webform_components = NULL;
+  }
+
+  /**
+   * Provide a list of components to test throughout the suite.
+   *
+   * Each component provides:
+   *   - A default configuration for the component.
+   *   - Values to try setting via POST
+   *   - Values that should match the database storage when set via POST
+   *   - Values that should match the database storage when using the default values.
+   *
+   * @return array
+   *   An array of each component settings.
+   */
+  function testWebformComponents() {
+    if (isset($this->_webform_components)) {
+      return $this->_webform_components;
+    }
+
+    $this->_webform_components = array(
+      // Test date components.
+      'date' => array(
+        'component' => array(
+          'form_key' => 'date',
+          'name' => 'Date',
+          'type' => 'date',
+          'value' => '19 Nov 1978',
+          'extra' => array(
+            'timezone' => 'site',
+            'start_date' => '-100 years',
+            'end_date' => '+2 years',
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => array('day' => '30', 'month' => '9', 'year' => '1982'),
+        'database values' => array('1982-09-30'),
+        'database default values' => array('1978-11-19'),
+      ),
+
+      // Test grid components.
+      'grid' => array(
+        'component' => array(
+          'form_key' => 'grid',
+          'name' => 'Grid',
+          'type' => 'grid',
+          'value' => '',
+          'extra' => array(
+            'questions' => "0|Ålphå\n1|ıé†å\n2|Îé¬†å", // Left side
+            'options' => "0|øne\n1|twö\n2|ǼBƇ\n3|€Euro", // Top
+          ),
+          'mandatory' => '0',
+          'pid' => '2',
+          'weight' => '-19',
+        ),
+        'sample values' => array('0' => '0', '1' => '1', '2' => '2'),
+        'database values' => array('0' => '0', '1' => '1', '2' => '2'),
+        'database default values' => array('', '', ''),
+      ),
+      'grid_keyed' => array(
+        'component' => array(
+          'form_key' => 'grid_keyed',
+          'name' => 'Grid Keyed',
+          'type' => 'grid',
+          'value' => '',
+          'extra' => array(
+            'questions' => "one|What's your option?\ntwo|Agåin?\nthree|One more time!", // Left side.
+            'options' => "one|Option one\ntwo|Option 2\nthree| Three is me", // Top
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => array('one' => 'one', 'two' => 'two', 'three' => 'three'),
+        'database values' => array('one' => 'one', 'two' => 'two', 'three' => 'three'),
+        'database default values' => array('one' => '', 'two' => '', 'three' => ''),
+      ),
+
+      // Test select components.
+      'checkboxes' => array(
+        'component' => array(
+          'form_key' => 'checkboxes',
+          'name' => 'Checkboxes',
+          'type' => 'select',
+          'value' => 'two',
+          'extra' => array(
+            'items' => "one|one\ntwo|two\nthree|three",
+            'multiple' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => array('one' => TRUE, 'two' => FALSE, 'three' => TRUE),
+        'database values' => array('one', 'three'),
+        'database default values' => array('two'),
+      ),
+      'checkboxes_zero' => array(
+        'component' => array(
+          'form_key' => 'checkboxes_zero',
+          'name' => 'Checkboxes zero',
+          'type' => 'select',
+          'value' => '0',
+          'extra' => array(
+            'items' => "0|zero\n1|one\n2|two",
+            'multiple' => 1,
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-9',
+        ),
+        'sample values' => array('0' => TRUE),
+        'database values' => array('0'),
+        'database default values' => array('0'),
+      ),
+      'radios' => array(
+        'component' => array(
+          'form_key' => 'radios',
+          'name' => 'Radios',
+          'type' => 'select',
+          'value' => 'two',
+          'extra' => array(
+            'items' => "one|one\ntwo|two\nthree|three",
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-9',
+        ),
+        'sample values' => 'one',
+        'database values' => array('one'),
+        'database default values' => array('two'),
+      ),
+      'radios_zero' => array(
+        'component' => array(
+          'form_key' => 'radios_zero',
+          'name' => 'Radios zero',
+          'type' => 'select',
+          'value' => '0',
+          'extra' => array(
+            'items' => "0|zero\n1|one\n2|two",
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-9',
+        ),
+        'sample values' => '0',
+        'database values' => array('0'),
+        'database default values' => array('0'),
+      ),
+      'select' => array(
+        'component' => array(
+          'form_key' => 'select',
+          'name' => 'Select',
+          'type' => 'select',
+          'value' => 'one',
+          'extra' => array(
+            'description' => 'Description here',
+            'items' => "one|one\ntwo|two\nthree|three\nfour|four\nfive|five\nsix|six",
+            'aslist' => 1,
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => 'two',
+        'database values' => array('two'),
+        'database default values' => array('one'),
+      ),
+      'select_zero' => array(
+        'component' => array(
+          'form_key' => 'select_zero',
+          'name' => 'Select zero',
+          'type' => 'select',
+          'value' => '0',
+          'extra' => array(
+            'description' => 'Tests saving zero as a value.',
+            'items' => "0|zero\n1|one\n2|two",
+            'aslist' => 1,
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => '0',
+        'database values' => array('0'),
+        'database default values' => array('0'),
+      ),
+      'select_no_default' => array(
+        'component' => array(
+          'form_key' => 'select_no_default',
+          'name' => 'Select no default',
+          'type' => 'select',
+          'value' => '',
+          'extra' => array(
+            'description' => 'Description here',
+            'items' => "one|one\ntwo|two\nthree|three\nfour|four\nfive|five\nsix|six",
+            'aslist' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => 'two',
+        'database values' => array('two'),
+        'database default values' => array(''),
+      ),
+      'select_no_default_zero' => array(
+        'component' => array(
+          'form_key' => 'select_no_default_zero',
+          'name' => 'Select no default zero',
+          'type' => 'select',
+          'value' => '',
+          'extra' => array(
+            'description' => 'Tests saving zero as a value.',
+            'items' => "0|zero\n1|one\n2|two",
+            'aslist' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => '0',
+        'database values' => array('0'),
+        'database default values' => array(''),
+      ),
+      'select_optgroup' => array(
+        'component' => array(
+          'form_key' => 'select_optgroup',
+          'name' => 'Select Optgroup',
+          'type' => 'select',
+          'value' => 'option 1-2',
+          'extra' => array(
+            'description' => 'Tests saving zero as a value.',
+            'items' => "<Group 1>\noption 1-1|option 1-1\noption 1-2|option 1-2\noption 1-3|option 1-3\n<Group 2>\noption 2-1|option 2-1\noption 2-2|option 2-2\noption 2-3|option 2-3",
+            'aslist' => 1,
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => 'option 2-2',
+        'database values' => array('option 2-2'),
+        'database default values' => array('option 1-2'),
+      ),
+      'select_email' => array(
+        'component' => array(
+          'form_key' => 'select_email',
+          'name' => 'Select e-mails',
+          'type' => 'select',
+          'value' => 'nate@localhost.localhost',
+          'extra' => array(
+            'items' => "nate@localhost.localhost|one\nadmin@localhost.localhost|two",
+          ),
+          'mandatory' => '0',
+          'pid' => '2',
+          'weight' => '-17',
+        ),
+        'sample values' => 'admin@localhost.localhost',
+        'database values' => array('admin@localhost.localhost'),
+        'database default values' => array('nate@localhost.localhost'),
+      ),
+      'select_multiple' => array(
+        'component' => array(
+          'form_key' => 'select_multiple',
+          'name' => 'Select Multiple',
+          'type' => 'select',
+          'value' => 'one,two',
+          'extra' => array(
+            'items' => "one|one\ntwo|two\nthree|three",
+            'multiple' => 1,
+            'aslist' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-10',
+        ),
+        // TODO: I'd like to test a value, but SimpleTest can't set multiple values.
+        'sample values' => NULL,
+        'database values' => array('one', 'two'),
+        'database default values' => array('one', 'two'),
+      ),
+
+      // Test date components.
+      'date_textfield' => array(
+        'component' => array(
+          'form_key' => 'date_textfield',
+          'name' => 'Date Textfield',
+          'type' => 'date',
+          'value' => 'Nov 19 1978',
+          'extra' => array(
+            'timezone' => 'site',
+            'start_date' => '-100 years',
+            'end_date' => '+2 years',
+            'year_textfield' => 1,
+          ),
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-7',
+        ),
+        'sample values' => array('day' => '30', 'month' => '9', 'year' => '1982'),
+        'database values' => array('1982-09-30'),
+        'database default values' => array('1978-11-19'),
+      ),
+
+      // Test email components.
+      'email' => array(
+        'component' => array(
+          'form_key' => 'email',
+          'name' => 'E-mail',
+          'type' => 'email',
+          'value' => '%useremail',
+          'mandatory' => '0',
+          'extra' => array(
+            // SimpleTest does not support type="email" input fields.
+            'attributes' => array('type' => 'text'),
+          ),
+          'pid' => '0',
+          'weight' => '-5',
+        ),
+        'sample values' => 'admin@localhost.localhost',
+        'database values' => array('admin@localhost.localhost'),
+        'database default values' => array($this->webform_users['admin']->mail),
+      ),
+
+      // Test hidden components.
+      'hidden' => array(
+        'component' => array(
+          'form_key' => 'hidden',
+          'name' => 'Hidden',
+          'type' => 'hidden',
+          'value' => 'default hidden value',
+          'mandatory' => '1',
+          'pid' => '0',
+          'weight' => '-4',
+        ),
+        'sample values' => NULL,
+        'database values' => array('default hidden value'),
+        'database default values' => array('default hidden value'),
+      ),
+
+      // Test textarea components.
+      'textarea' => array(
+        'component' => array(
+          'form_key' => 'textarea',
+          'name' => 'Textarea',
+          'type' => 'textarea',
+          'value' => 'sample textarea default value',
+          'extra' => array(),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '15',
+        ),
+        'sample values' => 'sample textarea value',
+        'database values' => array('sample textarea value'),
+        'database default values' => array('sample textarea default value'),
+      ),
+
+      // Test textfield components.
+      'textfield_disabled' => array(
+        'component' => array(
+          'form_key' => 'textfield_disabled',
+          'name' => 'Textfield Disabled',
+          'type' => 'textfield',
+          'value' => '%get[foo]',
+          'extra' => array(
+            'disabled' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-15',
+        ),
+        'sample values' => NULL,
+        'database values' => array('bar'),
+        'database default values' => array('bar'),
+      ),
+      'textfield_profile' => array(
+        'component' => array(
+          'form_key' => 'textfield_profile',
+          'name' => 'Textfield Profile',
+          'type' => 'textfield',
+          'value' => '%profile[profile_gender]',
+          'extra' => array(
+            'width' => '20',
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '-6',
+        ),
+        'sample values' => 'Female',
+        'database values' => array('Female'),
+        'database default values' => array($this->webform_users['admin']->profile_gender),
+      ),
+
+      // Test time components.
+      'time' => array(
+        'component' => array(
+          'form_key' => 'time',
+          'name' => 'Time',
+          'type' => 'time',
+          'value' => '10:30pm',
+          'extra' => array(
+            'timezone' => 'site',
+            'hourformat' => '12-hour',
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '16',
+        ),
+        'sample values' => array('hour' => '5', 'minute' => '0', 'ampm' => 'am'),
+        'database values' => array('05:00:00'),
+        'database default values' => array('22:30:00'),
+      ),
+      'time_24h' => array(
+        'component' => array(
+          'form_key' => 'time_24h',
+          'name' => 'Time 24H',
+          'type' => 'time',
+          'value' => '10:30pm',
+          'extra' => array(
+            'timezone' => 'site',
+            'hourformat' => '24-hour',
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '17',
+        ),
+        'sample values' => array('hour' => '5', 'minute' => '0'),
+        'database values' => array('05:00:00'),
+        'database default values' => array('22:30:00'),
+      ),
+
+      // Test number components.
+      'integer' => array(
+        'component' => array(
+          'form_key' => 'integer',
+          'name' => 'Integer',
+          'type' => 'number',
+          'value' => '1',
+          'extra' => array(
+            'type' => 'textfield',
+            'integer' => 1,
+            'max' => '100',
+            // SimpleTest does not support type="number" input fields.
+            'attributes' => array('type' => 'text'),
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '18',
+        ),
+        'sample values' => '2',
+        'database values' => array('2'),
+        'database default values' => array('1'),
+        'error values' => array(
+          '1.5' => t('%name field value of @value must be an integer.', array('%name' => 'Integer', '@value' => '1.5')),
+          '101' => t('%name field value must be less than @max.', array('%name' => 'Integer', '@max' => '100')),
+        ),
+      ),
+      'integer_range' => array(
+        'component' => array(
+          'form_key' => 'integer_range',
+          'name' => 'Integer Range',
+          'type' => 'number',
+          'value' => '50',
+          'extra' => array(
+            'type' => 'select',
+            'min' => '10',
+            'max' => '50',
+            'step' => 5,
+            'integer' => 1,
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '19',
+        ),
+        'sample values' => '10',
+        'database values' => array('10'),
+        'database default values' => array('50'),
+      ),
+      'decimal_positive' => array(
+        'component' => array(
+          'form_key' => 'decimal_positive',
+          'name' => 'Decimal positive',
+          'type' => 'number',
+          'value' => '1',
+          'extra' => array(
+            'type' => 'textfield',
+            'field_prefix' => '$',
+            'field_suffix' => 'lbs',
+            'min' => '0',
+            'decimals' => '2',
+            'point' => '.',
+            'separator' => ',',
+            // SimpleTest does not support type="number" input fields.
+            'attributes' => array('type' => 'text'),
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '20',
+        ),
+        'sample values' => '2.00',
+        'database values' => array('2.00'),
+        'database default values' => array('1'),
+        'error values' => array(
+          '-1' => t('%name field value must be greater than @min.', array('%name' => 'Decimal positive', '@min' => '0')),
+        ),
+      ),
+      'decimal_range' => array(
+        'component' => array(
+          'form_key' => 'decimal_range',
+          'name' => 'Decimal range',
+          'type' => 'number',
+          'value' => '1',
+          'extra' => array(
+            'type' => 'textfield',
+            'field_prefix' => '$',
+            'field_suffix' => 'lbs',
+            'min' => '1',
+            'max' => '12',
+            'step' => '1.5',
+            // SimpleTest does not support type="number" input fields.
+            'attributes' => array('type' => 'text'),
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '21',
+        ),
+        'sample values' => '11.5',
+        'database values' => array('11.5'),
+        'database default values' => array('1'),
+        'error values' => array(
+          '2' => t('%name field value must be @start plus a multiple of @step.', array('%name' => 'Decimal range', '@start' => '1', '@step' => '1.5')),
+          '13' => t('%name field value of @value should be in the range @min to @max.', array('%name' => 'Decimal range', '@value' => '13', '@min' => '1', '@max' => '12')),
+        ),
+      ),
+      'decimal_range_select' => array(
+        'component' => array(
+          'form_key' => 'decimal_range_select',
+          'name' => 'Decimal range select',
+          'type' => 'number',
+          'value' => '1',
+          'extra' => array(
+            'type' => 'select',
+            'field_prefix' => '$',
+            'field_suffix' => 'lbs',
+            'min' => '1',
+            'max' => '12',
+            'step' => '1.5',
+          ),
+          'mandatory' => '0',
+          'pid' => '0',
+          'weight' => '21',
+        ),
+        'sample values' => '11.5',
+        'database values' => array('11.5'),
+        'database default values' => array('1'),
+      ),
+    );
+
+    return $this->_webform_components;
+  }
+
+  function testWebformForm() {
+    if (isset($this->_webform_node)) {
+      return $this->_webform_node;
+    }
+
+    $settings = array(
+     'type' => 'webform',
+     'language'  => LANGUAGE_NONE,
+     'uid' => '1',
+     'status' => '1',
+     'promote' => '1',
+     'moderate' => '0',
+     'sticky' => '0',
+     'tnid' => '0',
+     'translate' => '0',
+     'title' => 'Test Webform',
+     'body' => array(LANGUAGE_NONE => array(array('value' => 'Donec placerat. Nullam nibh dolor, blandit sed, fermentum id, imperdiet sit amet, neque. Nam mollis ultrices justo. Sed tempor. Sed vitae tellus. Etiam sem arcu, eleifend sit amet, gravida eget, porta at, wisi. Nam non lacus vitae ipsum viverra pretium. Phasellus massa. Fusce magna sem, gravida in, feugiat ac, molestie eget, wisi. Fusce consectetuer luctus ipsum. Vestibulum nunc. Suspendisse dignissim adipiscing libero. Integer leo. Sed pharetra ligula a dui. Quisque ipsum nibh, ullamcorper eget, pulvinar sed, posuere vitae, nulla. Sed varius nibh ut lacus. Curabitur fringilla. Nunc est ipsum, pretium quis, dapibus sed, varius non, lectus. Proin a quam. Praesent lacinia, eros quis aliquam porttitor, urna lacus volutpat urna, ut fermentum neque mi egestas dolor.'))),
+     'teaser' => array(LANGUAGE_NONE => array(array('value' => 'Donec placerat. Nullam nibh dolor, blandit sed, fermentum id, imperdiet sit amet, neque. Nam mollis ultrices justo. Sed tempor. Sed vitae tellus. Etiam sem arcu, eleifend sit amet, gravida eget, porta at, wisi. Nam non lacus vitae ipsum viverra pretium. Phasellus massa. Fusce magna sem, gravida in, feugiat ac, molestie eget, wisi. Fusce consectetuer luctus ipsum. Vestibulum nunc. Suspendisse dignissim adipiscing libero. Integer leo. Sed pharetra ligula a dui. Quisque ipsum nibh, ullamcorper eget, pulvinar sed, posuere vitae, nulla. Sed varius nibh ut lacus. Curabitur fringilla.'))),
+     'log' => '',
+     'format' => '1',
+     'webform' => array(
+        'confirmation' => 'Thanks!',
+        'confirmation_format' => filter_default_format(),
+        'redirect_url' => '<confirmation>',
+        'teaser' => '0',
+        'allow_draft' => '1',
+        'submit_text' => '',
+        'submit_limit' => '-1',
+        'submit_interval' => '-1',
+        'submit_notice' => '1',
+        'roles' => array('1', '2'),
+        'components' => array(),
+        'emails' => array(),
+      ),
+    );
+
+    $cid = 0;
+    foreach ($this->testWebformComponents() as $key => $component_info) {
+      $cid++;
+      $settings['webform']['components'][$cid] = $component_info['component'];
+      $settings['webform']['components'][$cid]['cid'] = $cid;
+      $settings['webform']['components'][$cid]['pid'] = 0;
+    }
+
+    $this->_webform_node = $this->drupalCreateNode($settings);
+
+    return $this->_webform_node;
+  }
+
+  /**
+   * Generate a list of all values that would result in a valid submission.
+   */
+  function testWebformPost() {
+    $edit = array();
+    foreach ($this->testWebformComponents() as $key => $component_info) {
+      if (is_array($component_info['sample values'])) {
+        foreach ($component_info['sample values'] as $subkey => $value) {
+          $edit["submitted[$key][$subkey]"] = $value;
+        }
+      }
+      elseif ($component_info['sample values'] != NULL) {
+        $value = $component_info['sample values'];
+        // Multiple selects have a funky extra empty bracket in the name.
+        $extra = $key == 'select_multiple' ? '[]' : '';
+        $edit["submitted[$key]$extra"] = $value;
+      }
+    }
+    return $edit;
+  }
+
+  /**
+   * Utility function to print out the current page being tested.
+   */
+  function webformPrintPage() {
+    $this->verbose($this->drupalGetContent());
+  }
+}
+
+/**
+ * Test general functionality of Webform.
+ */
+class WebformGeneralTestCase extends WebformTestCase {
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Webform'),
+      'description' => t('Checks global Webform settings and content types.'),
+      'group' => t('Webform'),
+    );
+  }
+
+  /**
+   * Test creating a new Webform node.
+   */
+  function testWebformCreate() {
+    $settings = array(
+      'title' => 'Test webform, no components',
+      'type' => 'webform',
+    );
+    $node = $this->drupalCreateNode($settings);
+
+    // Because this is a "webform" type node, it should have an entry in the
+    // database even though it's using the default settings.
+    $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record made in the database for the new webform node.'));
+
+    // Make a change to the node, ensure that the record stays intact.
+    $node->title .= '!';
+    node_save($node);
+    $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record still in the database after modifying webform node.'));
+  }
+
+  /**
+   * Test webform-enabling a different node type and testing behavior.
+   */
+  function testWebformCreateNewType() {
+    // Enable webforms on the page content type.
+    variable_set('webform_node_types', array('webform', 'page'));
+
+    $settings = array(
+      'title' => 'Test webform-enabled page',
+      'type' => 'page',
+    );
+    $node = $this->drupalCreateNode($settings);
+
+    // Because this is a webform-enabled type node but does not yet have any
+    // components, it should not have an entry in the database because it is
+    // using the default settings.
+    $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record not in the database for the new page node.'));
+
+    // Make a change to the node, ensure that the record stays empty.
+    $node->title .= '!';
+    node_save($node);
+    $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record still not in the database after modifying page node.'));
+
+    // Add a new component to the node and check that a record is made in the
+    // webform table.
+    $components = $this->testWebformComponents();
+    $textarea = $components['textarea'];
+    $textarea['type'] = 'textarea';
+    $textarea['form_key'] = 'textarea';
+    $textarea['cid'] = 1;
+    $textarea['pid'] = 0;
+    $textarea = array_merge(webform_component_invoke('textarea', 'defaults'), $textarea);
+    $node->webform['components'][1] = $textarea;
+    node_save($node);
+    $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record now exists after adding a new component.'));
+
+    // Remove the new component and ensure that the record is deleted.
+    $node->webform['components'] = array();
+    node_save($node);
+    $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record deleted after deleting last component.'));
+  }
+
+  function webformRecordExists($nid) {
+    return (bool) db_query("SELECT nid FROM {webform} WHERE nid = :nid", array(':nid' => $nid))->fetchField();
+  }
+}
diff -Naurw webform_old/views/webform.views.inc webform/views/webform.views.inc
--- webform_old/views/webform.views.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform.views.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,308 @@
+<?php
+
+/**
+ * @file
+ * Views hooks implemented for the Webform module.
+ */
+
+function webform_views_data() {
+
+  /**
+   * Webform table definitions.
+   */
+  $data['webform']['table']['group'] = t('Webform');
+  $data['webform']['table']['join'] = array(
+    'node' => array(
+      'left_field' => 'nid',
+      'field' => 'nid',
+      'type' => 'INNER',
+    ),
+  );
+
+  // status
+  $data['webform']['status'] = array(
+    'title' => t('Status'),
+    'help' => t('The open or closed status of a webform.'),
+    'field' => array(
+      'handler' => 'webform_handler_field_webform_status',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'label' => t('Status'),
+      'handler' => 'webform_handler_filter_webform_status',
+      'type' => 'open-closed',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  /**
+   * Submissions table definitions.
+   */
+  $data['webform_submissions']['table']['group'] = t('Webform submissions');
+  $data['webform_submissions']['table']['base'] = array(
+    'field' => 'sid',
+    'title' => t('Webform submissions'),
+    'help' => t('Submissions generated from Webform forms.'),
+  );
+
+  // sid
+  $data['webform_submissions']['sid'] = array(
+    'title' => t('Sid'),
+    'help' => t('The submission ID of the submission.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'title' => t('Sid'),
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_numeric',
+    ),
+  );
+
+  // nid
+  $data['webform_submissions']['nid'] = array(
+    'title' => t('Node'),
+    'help' => t('The webform node this submission was generated from.'),
+    'relationship' => array(
+      'base' => 'node',
+      'field' => 'nid',
+      'handler' => 'views_handler_relationship',
+      'label' => t('Webform Node'),
+    ),
+  );
+
+  // uid
+  $data['webform_submissions']['uid'] = array(
+    'title' => t('User'),
+    'help' => t('The user who sent the webform submission.'),
+    'relationship' => array(
+      'base' => 'users',
+      'field' => 'uid',
+      'handler' => 'views_handler_relationship',
+      'label' => t('Webform Submission User'),
+    ),
+  );
+
+  // is_draft
+  $data['webform_submissions']['is_draft'] = array(
+    'title' => t('Draft'),
+    'help' => t('Whether or not the submission is a draft.'),
+    'field' => array(
+      'handler' => 'webform_handler_field_is_draft',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'webform_handler_filter_is_draft',
+    ),
+    'sort' => array(
+       'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // submitted
+  $data['webform_submissions']['submitted'] = array(
+    'title' => t('Submitted'),
+    'help' => t('The date this submission was submitted.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'title' => t('Submitted'),
+      'handler' => 'views_handler_filter_date',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
+  // remote_addr
+  $data['webform_submissions']['remote_addr'] = array(
+    'title' => t('Remote address'),
+    'help' => t('The remote IP address of the user that submitted this submission.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'title' => t('Remote address'),
+      'handler' => 'views_handler_filter_string',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // view link
+  $data['webform_submissions']['view_submission'] = array(
+    'field' => array(
+      'title' => t('View link'),
+      'help' => t('Provide a simple link to view the submission.'),
+      'handler' => 'webform_handler_field_submission_link',
+      'link_type' => 'view',
+    ),
+  );
+
+  // edit link
+  $data['webform_submissions']['edit_submission'] = array(
+    'field' => array(
+      'title' => t('Edit link'),
+      'help' => t('Provide a simple link to edit the submission.'),
+      'handler' => 'webform_handler_field_submission_link',
+      'link_type' => 'edit',
+    ),
+  );
+
+  // delete link
+  $data['webform_submissions']['delete_submission'] = array(
+    'field' => array(
+      'title' => t('Delete link'),
+      'help' => t('Provide a simple link to delete the submission.'),
+      'handler' => 'webform_handler_field_submission_link',
+      'link_type' => 'delete',
+    ),
+  );
+
+  return $data;
+}
+
+/**
+ * Implements hook_views_data_alter().
+ */
+function webform_views_data_alter(&$data) {
+  // Webform submission from node.
+  $data['node']['webform_submission'] = array(
+    'title' => t('Webform submission'),
+    'help' => t('Webform submissions of the given Webform node.'),
+    'real field' => 'nid',
+    'relationship' => array(
+      'base' => 'webform_submissions',
+      'base field' => 'nid',
+      'handler' => 'views_handler_relationship',
+      'label' => t('Webform Submission'),
+    ),
+  );
+  $data['node']['table']['join']['webform_submissions'] = array(
+    'field' => 'nid',
+    'left_field' => 'nid',
+    'left_table' => 'webform_submissions',
+  );
+  // Submission count (node).
+  $data['node']['webform_submission_count_node'] = array(
+    'group' => t('Webform'),
+    'field' => array(
+      'title' => t('Webform submission count'),
+      'help' => t('The number of webform submissions on this node.'),
+      'handler' => 'webform_handler_field_submission_count',
+      'count_type' => 'node',
+    ),
+  );
+
+
+  // Webform submission of user.
+  $data['users']['webform_submission'] = array(
+    'title' => t('Webform submission'),
+    'help' => t('Webform submissions of the given user.'),
+    'real field' => 'uid',
+    'relationship' => array(
+      'base' => 'webform_submissions',
+      'base field' => 'uid',
+      'handler' => 'views_handler_relationship',
+      'label' => t('Webform Submission'),
+    ),
+  );
+  // Submission count (user).
+  $data['users']['webform_submission_count_user'] = array(
+    'field' => array(
+      'title' => t('Webform submission count'),
+      'help' => t('The number of webform submissions for this user.'),
+      'handler' => 'webform_handler_field_submission_count',
+      'count_type' => 'users',
+    ),
+  );
+
+  // Link for editing the webform.
+  $data['node']['webform_edit'] = array(
+    'group' => t('Webform'),
+    'field' => array(
+      'title' => t('Webform edit link'),
+      'help' => t('Provide a simple link to edit the webform components and configuration.'),
+      'handler' => 'webform_handler_field_node_link_edit',
+    ),
+  );
+
+  // Link for viewing webform results.
+  $data['node']['webform_results'] = array(
+    'group' => t('Webform'),
+    'field' => array(
+      'title' => t('Webform results link'),
+      'help' => t('Provide a simple link to view the results of a webform.'),
+      'handler' => 'webform_handler_field_node_link_results',
+    ),
+  );
+
+  // Webform form content.
+  $data['node']['webform_form_body'] = array(
+    'group' => t('Webform'),
+    'field' => array(
+      'title' => t('Webform form body'),
+      'help' => t('The Webform form body display for this node.'),
+      'handler' => 'webform_handler_field_form_body',
+    ),
+  );
+}
+
+/**
+ * Implements hook_views_handlers().
+ */
+function webform_views_handlers() {
+  return array(
+    'info' => array(
+      'path' => drupal_get_path('module', 'webform') . '/views',
+    ),
+    'handlers' => array(
+      'webform_handler_field_submission_link' => array(
+        'parent' => 'views_handler_field',
+        'file' => 'webform_handler_field_submission_link.inc',
+      ),
+      'webform_handler_field_submission_count' => array(
+        'parent' => 'views_handler_field',
+        'file' => 'webform_handler_field_submission_count.inc',
+      ),
+      'webform_handler_field_node_link_edit' => array(
+        'parent' => 'views_handler_field_node_link',
+      ),
+      'webform_handler_field_node_link_results' => array(
+        'parent' => 'views_handler_field_node_link',
+      ),
+      'webform_handler_field_form_body' => array(
+        'parent' => 'views_handler_field',
+        'file' => 'webform_handler_field_form_body.inc',
+      ),
+      'webform_handler_field_is_draft' => array(
+        'parent' => 'views_handler_field',
+        'file' => 'webform_handler_field_is_draft.inc',
+      ),
+      'webform_handler_filter_is_draft' => array(
+        'parent' => 'views_handler_filter_in_operator',
+        'file' => 'webform_handler_filter_is_draft.inc',
+      ),
+      'webform_handler_field_webform_status' => array(
+        'parent' => 'views_handler_field_boolean',
+        'file' => 'webform_handler_field_webform_status.inc',
+      ),
+      'webform_handler_filter_webform_status' => array(
+        'parent' => 'views_handler_filter_boolean_operator',
+        'file' => 'webform_handler_filter_webform_status.inc',
+      ),
+    ),
+  );
+}
\ No newline at end of file
diff -Naurw webform_old/views/webform_handler_field_form_body.inc webform/views/webform_handler_field_form_body.inc
--- webform_old/views/webform_handler_field_form_body.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_form_body.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display the content of a webform form.
+ */
+
+/**
+ * Field handler to present the Webform form body to the user.
+ */
+class webform_handler_field_form_body extends views_handler_field {
+  function construct() {
+    parent::construct();
+    $this->additional_fields['nid'] = 'nid';
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['label'] = array('default' => 'Form', 'translatable' => TRUE);
+    return $options;
+  }
+
+  function query() {
+    $this->ensure_my_table();
+    $this->add_additional_fields();
+  }
+
+  function render($values) {
+    $node = node_load($values->{$this->aliases['nid']});
+
+    if (node_access('view', $node)) {
+      // Populate $node->content['webform'] by reference.
+      webform_node_view($node, 'full');
+      $form_body = isset($node->content['webform']) ? drupal_render($node->content['webform']) : NULL;
+    }
+    else {
+      return;
+    }
+
+    return $form_body;
+  }
+}
diff -Naurw webform_old/views/webform_handler_field_is_draft.inc webform/views/webform_handler_field_is_draft.inc
--- webform_old/views/webform_handler_field_is_draft.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_is_draft.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display the draft status of a submission.
+ */
+
+/**
+ * Field handler to show if submission is draft or not.
+ *
+ * @ingroup views_field_handlers
+ */
+class webform_handler_field_is_draft extends views_handler_field {
+  function render($values) {
+    $is_draft = $values->{$this->field_alias};
+    return $is_draft ? t('draft') : t('completed');
+  }
+}
\ No newline at end of file
diff -Naurw webform_old/views/webform_handler_field_node_link_edit.inc webform/views/webform_handler_field_node_link_edit.inc
--- webform_old/views/webform_handler_field_node_link_edit.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_node_link_edit.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display an edit link for Webform configuration.
+ */
+
+/**
+ * Field handler to present a link node edit.
+ */
+class webform_handler_field_node_link_edit extends views_handler_field_node_link {
+
+  /**
+   * Renders the link.
+   */
+  function render_link($node, $values) {
+    // Ensure user has access to edit this node.
+    if (!node_access('update', $node)) {
+      return;
+    }
+
+    $this->options['alter']['make_link'] = TRUE;
+    $this->options['alter']['path'] = "node/$node->nid/webform";
+
+    $text = !empty($this->options['text']) ? $this->options['text'] : t('edit webform');
+    return $text;
+  }
+
+}
+
diff -Naurw webform_old/views/webform_handler_field_node_link_results.inc webform/views/webform_handler_field_node_link_results.inc
--- webform_old/views/webform_handler_field_node_link_results.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_node_link_results.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display a results link for Webform submissions.
+ */
+
+/**
+ * Field handler to present a link node edit.
+ */
+class webform_handler_field_node_link_results extends views_handler_field_node_link {
+
+  /**
+   * Renders the link.
+   */
+  function render_link($node, $values) {
+    // Ensure user has access to edit this node.
+    if (!webform_results_access($node)) {
+      return;
+    }
+
+    $this->options['alter']['make_link'] = TRUE;
+    $this->options['alter']['path'] = "node/$node->nid/webform-results";
+
+    $text = !empty($this->options['text']) ? $this->options['text'] : t('results');
+    return $text;
+  }
+
+}
+
diff -Naurw webform_old/views/webform_handler_field_submission_count.inc webform/views/webform_handler_field_submission_count.inc
--- webform_old/views/webform_handler_field_submission_count.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_submission_count.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display the number of submissions in a webform.
+ */
+
+/**
+ * Field handler to present the submission count of a node to the user.
+ */
+class webform_handler_field_submission_count extends views_handler_field {
+  function construct() {
+    parent::construct();
+    $this->count_type = $this->definition['count_type'];
+
+    if ($this->count_type == 'node') {
+      $this->additional_fields['nid'] = 'nid';
+      $this->additional_fields['type'] = 'type';
+    }
+    elseif ($this->count_type == 'users') {
+      $this->additional_fields['uid'] = 'uid';
+    }
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['label'] = array('default' => '# of Submissions', 'translatable' => TRUE);
+    return $options;
+  }
+
+  function query() {
+    $this->ensure_my_table();
+    $this->add_additional_fields();
+  }
+
+  function render($values) {
+    global $user;
+
+    $output = NULL;
+    if ($this->count_type == 'node' && in_array($values->{$this->aliases['type']}, webform_variable_get('webform_node_types'))) {
+      module_load_include('inc', 'webform', 'includes/webform.submissions');
+      $node = node_load($values->{$this->aliases['nid']});
+      if (webform_results_access($node, $user)) {
+        $count = webform_get_submission_count($node->nid);
+        $output = l($count, "node/$node->nid/webform-results");
+      }
+      else {
+        $count = webform_get_submission_count($node->nid, $user->uid);
+        $output = l($count, "node/$node->nid/submissions");
+      }
+    }
+    elseif ($this->count_type == 'users') {
+      $output = db_select('webform_submissions')
+        ->condition('uid', $values->{$this->aliases['uid']})
+        ->countQuery->execute()->fetchField();
+    }
+
+    return $output;
+  }
+}
diff -Naurw webform_old/views/webform_handler_field_submission_link.inc webform/views/webform_handler_field_submission_link.inc
--- webform_old/views/webform_handler_field_submission_link.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_submission_link.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display links to a submission.
+ */
+
+/**
+ * Field handler to present a link to the user.
+ */
+class webform_handler_field_submission_link extends views_handler_field {
+  var $link_type;
+
+  function construct() {
+    // We need to set this property before calling the construct() chain
+    // as we use it in the option_definintion() call.
+    $this->link_type = $this->definition['link_type'];
+
+    parent::construct();
+    $this->additional_fields['sid'] = 'sid';
+    $this->additional_fields['nid'] = 'nid';
+    $this->additional_fields['uid'] = 'uid';
+
+    $this->additional_fields['node_uid'] = array(
+      'table' => 'node',
+      'field' => 'uid',
+    );
+  }
+
+  function allow_advanced_render() {
+    return FALSE;
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['label'] = array('default' => '', 'translatable' => TRUE);
+    $options['text'] = array('default' => $this->link_type, 'translatable' => TRUE);
+    return $options;
+  }
+
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['text'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Text to display'),
+      '#default_value' => $this->options['text'],
+    );
+  }
+
+  function query() {
+    $this->ensure_my_table();
+
+    // Join to the node table to retrieve the node UID.
+    $join = new views_join();
+    $join->construct('node', $this->table_alias, 'nid', 'nid');
+    $this->query->ensure_table('node', $this->relationship, $join);
+
+    $this->add_additional_fields();
+  }
+
+  function render($values) {
+    $submission = new stdClass();
+    $submission->sid = $values->{$this->aliases['sid']};
+    $submission->nid = $values->{$this->aliases['nid']};
+    $submission->uid = $values->{$this->aliases['uid']};
+    $node = (object) array(
+      'nid' => $submission->nid,
+      'uid' => $values->{$this->aliases['node_uid']},
+    );
+
+    switch ($this->link_type) {
+      case 'view':
+        $text = !empty($this->options['text']) ? $this->options['text'] : t('view');
+        $link = l($text, "node/$submission->nid/submission/$submission->sid");
+        $access = webform_submission_access($node, $submission, 'view');
+        break;
+      case 'edit':
+        $text = !empty($this->options['text']) ? $this->options['text'] : t('edit');
+        $link = l($text, "node/$submission->nid/submission/$submission->sid/edit");
+        $access = webform_submission_access($node, $submission, 'edit');
+        break;
+      case 'delete':
+        $text = !empty($this->options['text']) ? $this->options['text'] : t('delete');
+        $path = drupal_get_path_alias($_GET['q']);
+        $link = l($text, "node/$submission->nid/submission/$submission->sid/delete", array('query' => array('destination' => $path)));
+        $access = webform_submission_access($node, $submission, 'delete');
+        break;
+      default:
+        $text = '';
+        $link = NULL;
+        $access = FALSE;
+    }
+
+    if (!$access) {
+      return;
+    }
+
+    return $link;
+  }
+}
diff -Naurw webform_old/views/webform_handler_field_webform_status.inc webform/views/webform_handler_field_webform_status.inc
--- webform_old/views/webform_handler_field_webform_status.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_field_webform_status.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Views handler to display the open or closed status of a webform.
+ */
+class webform_handler_field_webform_status extends views_handler_field_boolean {
+
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type']['#options'] = array('open-closed' => t('Open/Closed')) + $form['type']['#options'];
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['type']['default'] = 'open-closed';
+    return $options;
+  }
+
+  function render($values) {
+    $value = $values->{$this->field_alias};
+    if (!empty($this->options['not'])) {
+      $value = !$value;
+    }
+    switch ($this->options['type']) {
+      case 'yes-no':
+        return $value ? t('Yes') : t('No');
+      case 'true-false':
+        return $value ? t('True') : t('False');
+      case 'on-off':
+        return $value ? t('On') : t('Off');
+      case 'open-closed':
+      default:
+        return $value ? t('Open') : t('Closed');
+    }
+  }
+}
+
diff -Naurw webform_old/views/webform_handler_filter_is_draft.inc webform/views/webform_handler_filter_is_draft.inc
--- webform_old/views/webform_handler_filter_is_draft.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_filter_is_draft.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Views handler to filter submissions by draft state.
+ */
+
+/**
+ * Filter by submission status
+ */
+class webform_handler_filter_is_draft extends views_handler_filter_in_operator {
+  function get_value_options() {
+    if (!isset($this->value_options)) {
+      $this->value_title = t('Status');
+      $options = array('0' => t('Completed'), '1' => t('Draft'));
+      $this->value_options = $options;
+    }
+  }
+
+  // '0' won't work as a key for checkboxes.
+  function value_form(&$form, &$form_state) {
+    parent::value_form($form, $form_state);
+    $form['value']['#type'] = 'select';
+  }
+}
diff -Naurw webform_old/views/webform_handler_filter_webform_status.inc webform/views/webform_handler_filter_webform_status.inc
--- webform_old/views/webform_handler_filter_webform_status.inc	1970-01-01 01:00:00.000000000 +0100
+++ webform/views/webform_handler_filter_webform_status.inc	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Views handler to filter webforms by open or closed status.
+ */
+class webform_handler_filter_webform_status extends views_handler_filter_boolean_operator {
+  function get_value_options() {
+    if (!isset($this->value_options)) {
+      $this->value_title = t('Status');
+      $options = array('1' => t('Open'), '0' => t('Closed'));
+      $this->value_options = $options;
+    }
+  }
+}
diff -Naurw webform_old/webform.api.php webform/webform.api.php
--- webform_old/webform.api.php	1970-01-01 01:00:00.000000000 +0100
+++ webform/webform.api.php	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,923 @@
+<?php
+
+/**
+ * @file
+ * Sample hooks demonstrating usage in Webform.
+ */
+
+/**
+ * @defgroup webform_hooks Webform Module Hooks
+ * @{
+ * Webform's hooks enable other modules to intercept events within Webform, such
+ * as the completion of a submission or adding validation. Webform's hooks also
+ * allow other modules to provide additional components for use within forms.
+ */
+
+/**
+ * Define callbacks that can be used as select list options.
+ *
+ * When users create a select component, they may select a pre-built list of
+ * certain options. Webform core provides a few of these lists such as the
+ * United States, countries of the world, and days of the week. This hook
+ * provides additional lists that may be utilized.
+ *
+ * @see webform_options_example()
+ * @see hook_webform_select_options_info_alter()
+ *
+ * @return
+ *   An array of callbacks that can be used for select list options. This array
+ *   should be keyed by the "name" of the pre-defined list. The values should
+ *   be an array with the following additional keys:
+ *     - title: The translated title for this list.
+ *     - options callback: The name of the function that will return the list.
+ *     - options arguments: Any additional arguments to send to the callback.
+ *     - file: Optional. The file containing the options callback, relative to
+ *       the module root.
+ */
+function hook_webform_select_options_info() {
+  $items = array();
+
+  $items['days'] = array(
+    'title' => t('Days of the week'),
+    'options callback' => 'webform_options_days',
+    'file' => 'includes/webform.options.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Alter the list of select list options provided by Webform and other modules.
+ *
+ * @see hook_webform_select_options_info().
+ */
+function hook_webform_select_options_info_alter(&$items) {
+  // Remove the days of the week options.
+  unset($items['days']);
+}
+
+/**
+ * This is an example function to demonstrate a webform options callback.
+ *
+ * This function returns a list of options that Webform may use in a select
+ * component. In order to be called, the function name
+ * ("webform_options_example" in this case), needs to be specified as a callback
+ * in hook_webform_select_options_info().
+ *
+ * @param $component
+ *   The Webform component array for the select component being displayed.
+ * @param $flat
+ *   Boolean value indicating whether the returned list needs to be a flat array
+ *   of key => value pairs. Select components support up to one level of
+ *   nesting, but when results are displayed, the list needs to be returned
+ *   without the nesting.
+ * @param $filter
+ *   Boolean value indicating whether the included options should be passed
+ *   through the _webform_filter_values() function for token replacement (only)
+ *   needed if your list contains tokens).
+ * @param $arguments
+ *   The "options arguments" specified in hook_webform_select_options_info().
+ * @return
+ *   An array of key => value pairs suitable for a select list's #options
+ *   FormAPI property.
+ */
+function webform_options_example($component, $flat, $filter, $arguments) {
+  $options = array(
+    'one' => t('Pre-built option one'),
+    'two' => t('Pre-built option two'),
+    'three' => t('Pre-built option three'),
+  );
+
+  return $options;
+}
+
+/**
+ * Respond to the loading of Webform submissions.
+ *
+ * @param $submissions
+ *   An array of Webform submissions that are being loaded, keyed by the
+ *   submission ID. Modifications to the submissions are done by reference.
+ */
+function hook_webform_submission_load(&$submissions) {
+  foreach ($submissions as $sid => $submission) {
+    $submissions[$sid]->new_property = 'foo';
+  }
+}
+
+/**
+ * Modify a Webform submission, prior to saving it in the database.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that is about to be saved to the database.
+ */
+function hook_webform_submission_presave($node, &$submission) {
+  // Update some component's value before it is saved.
+  $component_id = 4;
+  $submission->data[$component_id]['value'][0] = 'foo';
+}
+
+/**
+ * Respond to a Webform submission being inserted.
+ *
+ * Note that this hook is called after a submission has already been saved to
+ * the database. If needing to modify the submission prior to insertion, use
+ * hook_webform_submission_presave().
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just inserted into the database.
+ */
+function hook_webform_submission_insert($node, $submission) {
+  // Insert a record into a 3rd-party module table when a submission is added.
+  db_insert('mymodule_table')
+    ->fields(array(
+      'nid' => $node->nid,
+      'sid' => $submission->sid,
+      'foo' => 'foo_data',
+    ))
+    ->execute();
+}
+
+/**
+ * Respond to a Webform submission being updated.
+ *
+ * Note that this hook is called after a submission has already been saved to
+ * the database. If needing to modify the submission prior to updating, use
+ * hook_webform_submission_presave().
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just updated in the database.
+ */
+function hook_webform_submission_update($node, $submission) {
+  // Update a record in a 3rd-party module table when a submission is updated.
+  db_update('mymodule_table')
+    ->fields(array(
+      'foo' => 'foo_data',
+    ))
+    ->condition('nid', $node->nid)
+    ->condition('sid', $submission->sid)
+    ->execute();
+}
+
+/**
+ * Respond to a Webform submission being deleted.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just deleted from the database.
+ */
+function hook_webform_submission_delete($node, $submission) {
+  // Delete a record from a 3rd-party module table when a submission is deleted.
+  db_delete('mymodule_table')
+    ->condition('nid', $node->nid)
+    ->condition('sid', $submission->sid)
+    ->execute();
+}
+
+/**
+ * Provide a list of actions that can be executed on a submission.
+ *
+ * Some actions are displayed in the list of submissions such as edit, view, and
+ * delete. All other actions are displayed only when viewing the submission.
+ * These additional actions may be specified in this hook. Examples included
+ * directly in the Webform module include PDF, print, and resend e-mails. Other
+ * modules may extend this list by using this hook.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission on which the actions may be performed.
+ */
+function hook_webform_submission_actions($node, $submission) {
+  if (webform_results_access($node)) {
+    $actions['myaction'] = array(
+      'title' => t('Do my action'),
+      'href' => 'node/' . $node->nid . '/submission/' . $submission->sid . '/myaction',
+      'query' => drupal_get_destination(),
+    );
+  }
+
+  return $actions;
+}
+
+/**
+ * Alter the display of a Webform submission.
+ *
+ * This function applies to both e-mails sent by Webform and normal display of
+ * submissions when viewing through the adminsitrative interface.
+ *
+ * @param $renderable
+ *   The Webform submission in a renderable array, similar to FormAPI's
+ *   structure. This variable must be passed in by-reference. Important
+ *   properties of this array include #node, #submission, #email, and #format,
+ *   which can be used to find the context of the submission that is being
+ *   rendered.
+ */
+function hook_webform_submission_render_alter(&$renderable) {
+  // Remove page breaks from sent e-mails.
+  if (isset($renderable['#email'])) {
+    foreach (element_children($renderable) as $key) {
+      if ($renderable[$key]['#component']['type'] == 'pagebreak') {
+        unset($renderable[$key]);
+      }
+    }
+  }
+}
+
+/**
+ * Modify a loaded Webform component.
+ *
+ * IMPORTANT: This hook does not actually exist because components are loaded
+ * in bulk as part of webform_node_load(). Use hook_node_load() to modify loaded
+ * components when the node is loaded. This example is provided merely to point
+ * to hook_node_load().
+ *
+ * @see hook_nodeapi()
+ * @see webform_node_load()
+ */
+function hook_webform_component_load() {
+  // This hook does not exist. Instead use hook_node_load().
+}
+
+/**
+ * Modify a Webform component before it is saved to the database.
+ *
+ * Note that most of the time this hook is not necessary, because Webform will
+ * automatically add data to the component based on the component form. Using
+ * hook_form_alter() will be sufficient in most cases.
+ *
+ * @see hook_form_alter()
+ * @see webform_component_edit_form()
+ *
+ * @param $component
+ *   The Webform component being saved.
+ */
+function hook_webform_component_presave(&$component) {
+  $component['extra']['new_option'] = 'foo';
+}
+
+/**
+ * Respond to a Webform component being inserted into the database.
+ */
+function hook_webform_component_insert($component) {
+  // Insert a record into a 3rd-party module table when a component is inserted.
+  db_insert('mymodule_table')
+    ->fields(array(
+      'nid' => $component['nid'],
+      'cid' => $component['cid'],
+      'foo' => 'foo_data',
+    ))
+    ->execute();
+}
+
+/**
+ * Respond to a Webform component being updated in the database.
+ */
+function hook_webform_component_update($component) {
+  // Update a record in a 3rd-party module table when a component is updated.
+  db_update('mymodule_table')
+    ->fields(array(
+      'foo' => 'foo_data',
+    ))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->execute();
+}
+
+/**
+ * Respond to a Webform component being deleted.
+ */
+function hook_webform_component_delete($component) {
+  // Delete a record in a 3rd-party module table when a component is deleted.
+  db_delete('mymodule_table')
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->execute();
+}
+
+/**
+ * Define components to Webform.
+ *
+ * @return
+ *   An array of components, keyed by machine name. Required properties are
+ *   "label" and "description". The "features" array defines which capabilities
+ *   the component has, such as being displayed in e-mails or csv downloads.
+ *   A component like "markup" for example would not show in these locations.
+ *   The possible features of a component include:
+ *
+ *     - csv
+ *     - email
+ *     - email_address
+ *     - email_name
+ *     - required
+ *     - conditional
+ *     - spam_analysis
+ *     - group
+ *
+ *   Note that most of these features do not indicate the default state, but
+ *   determine if the component can have this property at all. Setting
+ *   "required" to TRUE does not mean that a component's fields will always be
+ *   required, but instead give the option to the administrator to choose the
+ *   requiredness. See the example implementation for details on how these
+ *   features may be set.
+ *
+ *   An optional "file" may be specified to be loaded when the component is
+ *   needed. A set of callbacks will be established based on the name of the
+ *   component. All components follow the pattern:
+ *
+ *   _webform_[callback]_[component]
+ *
+ *   Where [component] is the name of the key of the component and [callback] is
+ *   any of the following:
+ *
+ *     - defaults
+ *     - edit
+ *     - render
+ *     - display
+ *     - submit
+ *     - delete
+ *     - help
+ *     - theme
+ *     - analysis
+ *     - table
+ *     - csv_headers
+ *     - csv_data
+ *
+ * See the sample component implementation for details on each one of these
+ * callbacks.
+ *
+ * @see webform_components()
+ */
+function hook_webform_component_info() {
+  $components = array();
+
+  $components['textfield'] = array(
+    'label' => t('Textfield'),
+    'description' => t('Basic textfield type.'),
+    'features' => array(
+      // Add content to CSV downloads. Defaults to TRUE.
+      'csv' => TRUE,
+
+      // This component supports default values. Defaults to TRUE.
+      'default_value' => FALSE,
+
+      // This component supports a description field. Defaults to TRUE.
+      'description' => FALSE,
+
+      // Show this component in e-mailed submissions. Defaults to TRUE.
+      'email' => TRUE,
+
+      // Allow this component to be used as an e-mail FROM or TO address.
+      // Defaults to FALSE.
+      'email_address' => FALSE,
+
+      // Allow this component to be used as an e-mail SUBJECT or FROM name.
+      // Defaults to FALSE.
+      'email_name' => TRUE,
+
+      // This component may be toggled as required or not. Defaults to TRUE.
+      'required' => TRUE,
+
+      // This component supports a title attribute. Defaults to TRUE.
+      'title' => FALSE,
+
+      // This component has a title that can be toggled as displayed or not.
+      'title_display' => TRUE,
+
+      // This component has a title that can be displayed inline.
+      'title_inline' => TRUE,
+
+      // If this component can be used as a conditional SOURCE. All components
+      // may always be displayed conditionally, regardless of this setting.
+      // Defaults to TRUE.
+      'conditional' => TRUE,
+
+      // If this component allows other components to be grouped within it
+      // (like a fieldset or tabs). Defaults to FALSE.
+      'group' => FALSE,
+
+      // If this component can be used for SPAM analysis, usually with Mollom.
+      'spam_analysis' => FALSE,
+
+      // If this component saves a file that can be used as an e-mail
+      // attachment. Defaults to FALSE.
+      'attachment' => FALSE,
+    ),
+    'file' => 'components/textfield.inc',
+  );
+
+  return $components;
+}
+
+/**
+ * Alter the list of available Webform components.
+ *
+ * @param $components
+ *   A list of existing components as defined by hook_webform_component_info().
+ *
+ * @see hook_webform_component_info()
+ */
+function hook_webform_component_info_alter(&$components) {
+  // Completely remove a component.
+  unset($components['grid']);
+
+  // Change the name of a component.
+  $components['textarea']['label'] = t('Text box');
+}
+
+/**
+ * Alter access to a Webform submission.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission.
+ * @param $op
+ *   The operation to be performed on the submission. Possible values are:
+ *   - "view"
+ *   - "edit"
+ *   - "delete"
+ *   - "list"
+ * @param $account
+ *   A user account object.
+ * @return
+ *   TRUE if the current user has access to submission,
+ *   or FALSE otherwise.
+ */
+function hook_webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
+  switch ($op) {
+    case 'view':
+      return TRUE;
+      break;
+    case 'edit':
+      return FALSE;
+      break;
+    case 'delete':
+      return TRUE;
+      break;
+    case 'list':
+      return TRUE;
+      break;
+  }
+}
+
+/**
+ * Determine if a user has access to see the results of a webform.
+ *
+ * Note in addition to the view access to the results granted here, the $account
+ * must also have view access to the Webform node in order to see results.
+ *
+ * @see webform_results_access().
+ *
+ * @param $node
+ *   The Webform node to check access on.
+ * @param $account
+ *   The user account to check access on.
+ * @return
+ *   TRUE or FALSE if the user can access the webform results.
+ */
+function hook_webform_results_access($node, $account) {
+  // Let editors view results of unpublished webforms.
+  if ($node->status == 0 && in_array('editor', $account->roles)) {
+    return TRUE;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Return an array of files associated with the component.
+ *
+ * The output of this function will be used to attach files to e-mail messages.
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   An array of information containing the submission result, directly
+ *   correlating to the webform_submitted_data database schema.
+ * @return
+ *   An array of files, each file is an array with following keys:
+ *     - filepath: The relative path to the file.
+ *     - filename: The name of the file including the extension.
+ *     - filemime: The mimetype of the file.
+ *   This will result in an array looking something like this:
+ *   @code
+ *   array[0] => array(
+ *     'filepath' => '/sites/default/files/attachment.txt',
+ *     'filename' => 'attachment.txt',
+ *     'filemime' => 'text/plain',
+ *   );
+ *   @endcode
+ */
+function _webform_attachments_component($component, $value) {
+  $files = array();
+  $files[] = (array) file_load($value[0]);
+  return $files;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup webform_component Sample Webform Component
+ * @{
+ * In each of these examples, the word "component" should be replaced with the,
+ * name of the component type (such as textfield or select). These are not
+ * actual hooks, but instead samples of how Webform integrates with its own
+ * built-in components.
+ */
+
+/**
+ * Specify the default properties of a component.
+ *
+ * @return
+ *   An array defining the default structure of a component.
+ */
+function _webform_defaults_component() {
+  return array(
+    'name' => '',
+    'form_key' => NULL,
+    'mandatory' => 0,
+    'pid' => 0,
+    'weight' => 0,
+    'extra' => array(
+      'options' => '',
+      'questions' => '',
+      'optrand' => 0,
+      'qrand' => 0,
+      'description' => '',
+    ),
+  );
+}
+
+/**
+ * Generate the form for editing a component.
+ *
+ * Create a set of form elements to be displayed on the form for editing this
+ * component. Use care naming the form items, as this correlates directly to the
+ * database schema. The component "Name" and "Description" fields are added to
+ * every component type and are not necessary to specify here (although they
+ * may be overridden if desired).
+ *
+ * @param $component
+ *   A Webform component array.
+ * @return
+ *   An array of form items to be displayed on the edit component page
+ */
+function _webform_edit_component($component) {
+  $form = array();
+
+  // Disabling the description if not wanted.
+  $form['description'] = array();
+
+  // Most options are stored in the "extra" array, which stores any settings
+  // unique to a particular component type.
+  $form['extra']['options'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Options'),
+    '#default_value' => $component['extra']['options'],
+    '#description' => t('Key-value pairs may be entered separated by pipes. i.e. safe_key|Some readable option') . theme('webform_token_help'),
+    '#cols' => 60,
+    '#rows' => 5,
+    '#weight' => -3,
+    '#required' => TRUE,
+  );
+  return $form;
+}
+
+/**
+ * Render a Webform component to be part of a form.
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   If editing an existing submission or resuming a draft, this will contain
+ *   an array of values to be shown instead of the default in the component
+ *   configuration. This value will always be an array, keyed numerically for
+ *   each value saved in this field.
+ * @param $filter
+ *   Whether or not to filter the contents of descriptions and values when
+ *   rendering the component. Values need to be unfiltered to be editable by
+ *   Form Builder.
+ *
+ * @see _webform_client_form_add_component()
+ */
+function _webform_render_component($component, $value = NULL, $filter = TRUE) {
+  $form_item = array(
+    '#type' => 'textfield',
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description'   => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
+    '#default_value' => $filter ? _webform_filter_values($component['value']) : $component['value'],
+    '#prefix' => '<div class="webform-component-textfield" id="webform-component-' . $component['form_key'] . '">',
+    '#suffix' => '</div>',
+  );
+
+  if (isset($value)) {
+    $form_item['#default_value'] = $value[0];
+  }
+
+  return $form_item;
+}
+
+/**
+ * Display the result of a submission for a component.
+ *
+ * The output of this function will be displayed under the "Results" tab then
+ * "Submissions". This should output the saved data in some reasonable manner.
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   An array of information containing the submission result, directly
+ *   correlating to the webform_submitted_data database table schema.
+ * @param $format
+ *   Either 'html' or 'text'. Defines the format that the content should be
+ *   returned as. Make sure that returned content is run through check_plain()
+ *   or other filtering functions when returning HTML.
+ * @return
+ *   A renderable element containing at the very least these properties:
+ *    - #title
+ *    - #weight
+ *    - #component
+ *    - #format
+ *    - #value
+ *   Webform also uses #theme_wrappers to output the end result to the user,
+ *   which will properly format the label and content for use within an e-mail
+ *   (such as wrapping the text) or as HTML (ensuring consistent output).
+ */
+function _webform_display_component($component, $value, $format = 'html') {
+  return array(
+    '#title' => $component['name'],
+    '#weight' => $component['weight'],
+    '#theme' => 'webform_display_textfield',
+    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
+    '#post_render' => array('webform_element_wrapper'),
+    '#field_prefix' => $component['extra']['field_prefix'],
+    '#field_suffix' => $component['extra']['field_suffix'],
+    '#component' => $component,
+    '#format' => $format,
+    '#value' => isset($value[0]) ? $value[0] : '',
+  );
+}
+
+/**
+ * A hook for changing the input values before saving to the database.
+ *
+ * Webform expects a component to consist of a single field, or a single array
+ * of fields. If you have a component that requires a deeper form tree
+ * you must flatten the data into a single array using this callback
+ * or by setting #parents on each field to avoid data loss and/or unexpected
+ * behavior.
+ *
+ * Note that Webform will save the result of this function directly into the
+ * database.
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   The POST data associated with the user input.
+ * @return
+ *   An array of values to be saved into the database. Note that this should be
+ *   a numerically keyed array.
+ */
+function _webform_submit_component($component, $value) {
+  // Clean up a phone number into 123-456-7890 format.
+  if ($component['extra']['phone_number']) {
+    $matches = array();
+    $number = preg_replace('[^0-9]', $value[0]);
+    if (strlen($number) == 7) {
+      $number = substr($number, 0, 3) . '-' . substr($number, 3, 4);
+    }
+    else {
+      $number = substr($number, 0, 3) . '-' . substr($number, 3, 3) . '-' . substr($number, 6, 4);
+    }
+  }
+
+  $value[0] = $number;
+  return $value;
+}
+
+/**
+ * Delete operation for a component or submission.
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   An array of information containing the submission result, directly
+ *   correlating to the webform_submitted_data database schema.
+ */
+function _webform_delete_component($component, $value) {
+  // Delete corresponding files when a submission is deleted.
+  $filedata = unserialize($value['0']);
+  if (isset($filedata['filepath']) && is_file($filedata['filepath'])) {
+    unlink($filedata['filepath']);
+    db_query("DELETE FROM {files} WHERE filepath = '%s'", $filedata['filepath']);
+  }
+}
+
+/**
+ * Module specific instance of hook_help().
+ *
+ * This allows each Webform component to add information into hook_help().
+ */
+function _webform_help_component($section) {
+  switch ($section) {
+    case 'admin/config/content/webform#grid_description':
+      return t('Allows creation of grid questions, denoted by radio buttons.');
+  }
+}
+
+/**
+ * Module specific instance of hook_theme().
+ *
+ * This allows each Webform component to add information into hook_theme(). If
+ * you specify a file to include, you must define the path to the module that
+ * this file belongs to.
+ */
+function _webform_theme_component() {
+  return array(
+    'webform_grid' => array(
+      'render element' => 'element',
+      'file' => 'components/grid.inc',
+      'path' => drupal_get_path('module', 'webform'),
+    ),
+    'webform_display_grid' => array(
+      'render element' => 'element',
+      'file' => 'components/grid.inc',
+      'path' => drupal_get_path('module', 'webform'),
+    ),
+  );
+}
+
+/**
+ * Calculate and returns statistics about results for this component.
+ *
+ * This takes into account all submissions to this webform. The output of this
+ * function will be displayed under the "Results" tab then "Analysis".
+ *
+ * @param $component
+ *   An array of information describing the component, directly correlating to
+ *   the webform_component database schema.
+ * @param $sids
+ *   An optional array of submission IDs (sid). If supplied, the analysis will
+ *   be limited to these sids.
+ * @param $single
+ *   Boolean flag determining if the details about a single component are being
+ *   shown. May be used to provided detailed information about a single
+ *   component's analysis, such as showing "Other" options within a select list.
+ * @return
+ *   An array of data rows, each containing a statistic for this component's
+ *   submissions.
+ */
+function _webform_analysis_component($component, $sids = array(), $single = FALSE) {
+  // Generate the list of options and questions.
+  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
+  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
+
+  // Generate a lookup table of results.
+  $query = db_select('webform_submitted_data', 'wsd')
+    ->fields('wsd', array('no', 'data'))
+    ->condition('nid', $component['nid'])
+    ->condition('cid', $component['cid'])
+    ->condition('data', '', '<>')
+    ->groupBy('no')
+    ->groupBy('data');
+  $query->addExpression('COUNT(sid)', 'datacount');
+
+  if (count($sids)) {
+    $query->condition('sid', $sids, 'IN');
+  }
+
+  $result = $query->execute();
+  $counts = array();
+  foreach ($result as $data) {
+    $counts[$data->no][$data->data] = $data->datacount;
+  }
+
+  // Create an entire table to be put into the returned row.
+  $rows = array();
+  $header = array('');
+
+  // Add options as a header row.
+  foreach ($options as $option) {
+    $header[] = $option;
+  }
+
+  // Add questions as each row.
+  foreach ($questions as $qkey => $question) {
+    $row = array($question);
+    foreach ($options as $okey => $option) {
+      $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
+    }
+    $rows[] = $row;
+  }
+  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('webform-grid'))));
+
+  return array(array(array('data' => $output, 'colspan' => 2)));
+}
+
+/**
+ * Return the result of a component value for display in a table.
+ *
+ * The output of this function will be displayed under the "Results" tab then
+ * "Table".
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $value
+ *   An array of information containing the submission result, directly
+ *   correlating to the webform_submitted_data database schema.
+ * @return
+ *   Textual output formatted for human reading.
+ */
+function _webform_table_component($component, $value) {
+  $questions = array_values(_webform_component_options($component['extra']['questions']));
+  $output = '';
+  // Set the value as a single string.
+  if (is_array($value)) {
+    foreach ($value as $item => $value) {
+      if ($value !== '') {
+        $output .= $questions[$item] . ': ' . check_plain($value) . '<br />';
+      }
+    }
+  }
+  else {
+    $output = check_plain(!isset($value['0']) ? '' : $value['0']);
+  }
+  return $output;
+}
+
+/**
+ * Return the header for this component to be displayed in a CSV file.
+ *
+ * The output of this function will be displayed under the "Results" tab then
+ * "Download".
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $export_options
+ *   An array of options that may configure export of this field.
+ * @return
+ *   An array of data to be displayed in the first three rows of a CSV file, not
+ *   including either prefixed or trailing commas.
+ */
+function _webform_csv_headers_component($component, $export_options) {
+  $header = array();
+  $header[0] = array('');
+  $header[1] = array($component['name']);
+  $items = _webform_component_options($component['extra']['questions']);
+  $count = 0;
+  foreach ($items as $key => $item) {
+    // Empty column per sub-field in main header.
+    if ($count != 0) {
+      $header[0][] = '';
+      $header[1][] = '';
+    }
+    // The value for this option.
+    $header[2][] = $item;
+    $count++;
+  }
+
+  return $header;
+}
+
+/**
+ * Format the submitted data of a component for CSV downloading.
+ *
+ * The output of this function will be displayed under the "Results" tab then
+ * "Download".
+ *
+ * @param $component
+ *   A Webform component array.
+ * @param $export_options
+ *   An array of options that may configure export of this field.
+ * @param $value
+ *   An array of information containing the submission result, directly
+ *   correlating to the webform_submitted_data database schema.
+ * @return
+ *   An array of items to be added to the CSV file. Each value within the array
+ *   will be another column within the file. This function is called once for
+ *   every row of data.
+ */
+function _webform_csv_data_component($component, $export_options, $value) {
+  $questions = array_keys(_webform_select_options($component['extra']['questions']));
+  $return = array();
+  foreach ($questions as $key => $question) {
+    $return[] = isset($value[$key]) ? $value[$key] : '';
+  }
+  return $return;
+}
+
+/**
+ * @}
+ */
diff -Naurw webform_old/webform.info webform/webform.info
--- webform_old/webform.info	1970-01-01 01:00:00.000000000 +0100
+++ webform/webform.info	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,32 @@
+; $Id: $
+name = Webform
+description = Enables the creation of forms and questionnaires.
+core = 7.x
+package = Webform
+configure = admin/config/content/webform
+
+; Files that contain classes:
+files[] = includes/webform.export.inc
+
+files[] = views/webform_handler_field_form_body.inc
+files[] = views/webform_handler_field_is_draft.inc
+files[] = views/webform_handler_field_node_link_edit.inc
+files[] = views/webform_handler_field_node_link_results.inc
+files[] = views/webform_handler_field_submission_count.inc
+files[] = views/webform_handler_field_submission_link.inc
+files[] = views/webform_handler_field_webform_status.inc
+files[] = views/webform_handler_filter_is_draft.inc
+files[] = views/webform_handler_filter_webform_status.inc
+files[] = views/webform.views.inc
+
+files[] = tests/components.test
+files[] = tests/permissions.test
+files[] = tests/submission.test
+files[] = tests/webform.test
+
+; Information added by drupal.org packaging script on 2013-05-29
+version = "7.x-3.19"
+core = "7.x"
+project = "webform"
+datestamp = "1369860079"
+
diff -Naurw webform_old/webform.install webform/webform.install
--- webform_old/webform.install	1970-01-01 01:00:00.000000000 +0100
+++ webform/webform.install	2013-07-22 17:25:57.000000000 +0100
@@ -0,0 +1,845 @@
+<?php
+
+/**
+ * @file
+ *   Webform module install/schema hooks.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function webform_schema() {
+  $schema = array();
+
+  $schema['webform'] = array(
+    'description' => 'Table for storing additional properties for webform nodes.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'confirmation' => array(
+        'description' => 'The confirmation message or URL displayed to the user after submitting a form.',
+        'type' => 'text',
+        'not null' => TRUE,
+      ),
+      'confirmation_format' => array(
+        'description' => 'The {filter_format}.format of the confirmation message.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+      ),
+      'redirect_url' => array(
+        'description' => 'The URL a user is redirected to after submitting a form.',
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => '<confirmation>',
+      ),
+      'status' => array(
+        'description' => 'Boolean value of a webform for open (1) or closed (0).',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'block' => array(
+         'description' => 'Boolean value for whether this form be available as a block.',
+         'type' => 'int',
+         'size' => 'tiny',
+         'not null' => TRUE,
+         'default' => 0,
+      ),
+      'teaser' => array(
+        'description' => 'Boolean value for whether the entire form should be displayed on the teaser.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'allow_draft' => array(
+         'description' => 'Boolean value for whether submissions to this form be saved as a draft.',
+         'type' => 'int',
+         'size' => 'tiny',
+         'not null' => TRUE,
+         'default' => 0,
+      ),
+      'auto_save' => array(
+         'description' => 'Boolean value for whether submissions to this form should be auto-saved between pages.',
+         'type' => 'int',
+         'size' => 'tiny',
+         'not null' => TRUE,
+         'default' => 0,
+      ),
+      'submit_notice' => array(
+        'description' => 'Boolean value for whether to show or hide the previous submissions notification.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'submit_text' => array(
+        'description' => 'The title of the submit button on the form.',
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'submit_limit' => array(
+        'description' => 'The number of submissions a single user is allowed to submit within an interval. -1 is unlimited.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => -1,
+      ),
+      'submit_interval' => array(
+        'description' => 'The amount of time in seconds that must pass before a user can submit another submission within the set limit.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => -1,
+      ),
+      'total_submit_limit' => array(
+        'description' => 'The total number of submissions allowed within an interval. -1 is unlimited.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => -1,
+      ),
+      'total_submit_interval' => array(
+        'description' => 'The amount of time in seconds that must pass before another submission can be submitted within the set limit.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => -1,
+      ),
+    ),
+    'primary key' => array('nid'),
+  );
+
+  $schema['webform_component'] = array(
+    'description' => 'Stores information about components for webform nodes.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'cid' => array(
+        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
+        'type' => 'int',
+        'size' => 'small',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'pid' => array(
+        'description' => 'If this component has a parent fieldset, the cid of that component.',
+        'type' => 'int',
+        'size' => 'small',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'form_key' => array(
+        'description' => 'When the form is displayed and processed, this key can be used to reference the results.',
+        'type' => 'varchar',
+        'length' => 128,
+      ),
+      'name' => array(
+        'description' => 'The label for this component.',
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'type' => array(
+        'description' => 'The field type of this component (textfield, select, hidden, etc.).',
+        'type' => 'varchar',
+        'length' => 16,
+      ),
+      'value' => array(
+        'description' => 'The default value of the component when displayed to the end-user.',
+        'type' => 'text',
+        'not null' => TRUE,
+      ),
+      'extra' => array(
+        'description' => 'Additional information unique to the display or processing of this component.',
+        'type' => 'text',
+        'not null' => TRUE,
+      ),
+      'mandatory' => array(
+        'description' => 'Boolean flag for if this component is required.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'weight' => array(
+        'description' => 'Determines the position of this component in the form.',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'cid'),
+  );
+
+  $schema['webform_emails'] = array(
+    'description' => 'Holds information regarding e-mails that should be sent upon submitting a webform',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'eid' => array(
+        'description' => 'The e-mail identifier for this row\'s settings.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'email' => array(
+        'description' => 'The e-mail address that will be sent to upon submission. This may be an e-mail address, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'subject' => array(
+        'description' => 'The e-mail subject that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => FALSE,
+      ),
+      'from_name' => array(
+        'description' => 'The e-mail "from" name that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => FALSE,
+      ),
+      'from_address' => array(
+        'description' => 'The e-mail "from" e-mail address that will be used. This may be a string, the special key "default" or a numeric value. If a numeric value is used, the value of a component will be substituted on submission.',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => FALSE,
+      ),
+      'template' => array(
+        'description' => 'A template that will be used for the sent e-mail. This may be a string or the special key "default", which will use the template provided by the theming layer.',
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'excluded_components' => array(
+        'description' => 'A list of components that will not be included in the %email_values token. A list of CIDs separated by commas.',
+        'type' => 'text',
+        'not null' => TRUE,
+      ),
+      'html' => array(
+        'description' => 'Determines if the e-mail will be sent in an HTML format. Requires Mime Mail module.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'attachments' => array(
+        'description' => 'Determines if the e-mail will include file attachments. Requires Mime Mail module.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'eid'),
+  );
+
+  $schema['webform_roles'] = array(
+    'description' => 'Holds access information regarding which roles are allowed to submit which webform nodes. Does not prevent access to the webform node entirely, use the {node_access} table for that purpose.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'rid' => array(
+        'description' => 'The role identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'rid'),
+  );
+
+  $schema['webform_submissions'] = array(
+    'description' => 'Holds general information about submissions outside of field values.',
+    'fields' => array(
+      'sid' => array(
+        'description' => 'The unique identifier for this submission.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => 'The id of the user that completed this submission.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'is_draft' => array(
+         'description' => 'Is this a draft of the submission?',
+         'type' => 'int',
+         'size' => 'tiny',
+         'not null' => TRUE,
+         'default' => 0,
+      ),
+      'submitted' => array(
+        'description' => 'Timestamp of when the form was submitted.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'remote_addr' => array(
+        'description' => 'The IP address of the user that submitted the form.',
+        'type' => 'varchar',
+        'length' => 128,
+      ),
+    ),
+    'primary key' => array('sid'),
+    'unique keys' => array(
+      'sid_nid' => array('sid', 'nid'),
+    ),
+    'indexes' => array(
+      'nid_uid_sid' => array('nid', 'uid', 'sid'),
+      'nid_sid' => array('nid', 'sid'),
+    ),
+  );
+
+  $schema['webform_submitted_data'] = array(
+    'description' => 'Stores all submitted field data for webform submissions.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'sid' => array(
+        'description' => 'The unique identifier for this submission.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'cid' => array(
+        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
+        'type' => 'int',
+        'size' => 'small',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'no' => array(
+        'description' => 'Usually this value is 0, but if a field has multiple values (such as a time or date), it may require multiple rows in the database.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '0',
+      ),
+      'data' => array(
+        'description' => 'The submitted value of this field, may be serialized for some components.',
+        'type' => 'text',
+        'size' => 'medium',
+        'not null' => TRUE,
+      ),
+    ),
+    'primary key' => array('nid', 'sid', 'cid', 'no'),
+    'indexes' => array(
+      'nid' => array('nid'),
+      'sid_nid' => array('sid', 'nid'),
+    ),
+  );
+
+  $schema['webform_last_download'] = array(
+   'description' => 'Stores last submission number per user download.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+       'description' => 'The user identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+     'sid' => array(
+        'description' => 'The last downloaded submission number.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+     'requested' => array(
+        'description' => 'Timestamp of last download request.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'uid'),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function webform_install() {
+  module_load_include('inc', 'node', 'content_types');
+  db_update('system')
+    ->condition('name', 'webform')
+    ->condition('type', 'module')
+    ->fields(array('weight' => -1))
+    ->execute();
+
+  // Optionally create the default webform type.
+  if (variable_get('webform_install_create_content_type', TRUE)) {
+    $webform_type = array(
+      'type' => 'webform',
+      'name' => st('Webform'),
+      'base' => 'node_content',
+      'description' => st('Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.'),
+      'custom' => TRUE,
+      'modified' => TRUE,
+      'locked' => FALSE,
+    );
+    $webform_type = node_type_set_defaults($webform_type);
+    node_type_save($webform_type);
+    node_add_body_field($webform_type);
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function webform_uninstall() {
+  // Unset webform variables.
+  variable_del('webform_node_types');
+  variable_del('webform_node_types_primary');
+  variable_del('webform_use_cookies');
+  variable_del('webform_default_from_address');
+  variable_del('webform_default_from_name');
+  variable_del('webform_default_subject');
+  variable_del('webform_default_format');
+  variable_del('webform_format_override');
+  variable_del('webform_csv_delimiter');
+  variable_del('webform_allowed_tags');
+  variable_del('webform_blocks');
+
+  $component_list = array();
+  $path = drupal_get_path('module', 'webform') . '/components';
+  $files = file_scan_directory($path, '/^.*\.inc$/');
+  foreach ($files as $filename => $file) {
+    variable_del('webform_enable_' . $file->name, 1);
+  }
+
+  // Delete uploaded files.
+  $filepath = file_build_uri('webform');
+  file_unmanaged_delete_recursive($filepath);
+}
+
+/**
+ * Set the minimum upgrade version.
+ *
+ * Currently you cannot upgrade from 2.x in Drupal 6 to 3.x in Drupal 7. However
+ * there are no database changes between the 3.x versions, so no update is
+ * needed at all to move from 3.x in Drupal 6 to Drupal 7.
+ */
+function webform_update_last_removed() {
+  return 6313;
+}
+
+/**
+ * Allow the confirmation format column to have a NULL value.
+ */
+function webform_update_7301() {
+  // These changes are modeled after user_update_7010().
+  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
+    'description' => 'The {filter_format}.format of the confirmation message.',
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => FALSE,
+  ));
+  db_update('webform')
+    ->fields(array('confirmation_format' => NULL))
+    ->condition('confirmation', '')
+    ->condition('confirmation_format', 0)
+    ->execute();
+  $existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
+  $default_format = variable_get('filter_default_format', 1);
+
+  // Since Webform may be updated separately from Drupal core, not all format
+  // names may be numbers when running this update.
+  $numeric_formats = array();
+  foreach ($existing_formats as $format_name) {
+    if (is_numeric($format_name)) {
+      $numeric_formats[] = (int) $format_name;
+    }
+  }
+
+  $query = db_update('webform')
+    ->fields(array('confirmation_format' => $default_format))
+    ->isNotNull('confirmation_format');
+
+  if (!empty($numeric_formats)) {
+    $query->condition('confirmation_format', $numeric_formats, 'NOT IN');
+  }
+
+  $query->execute();
+}
+
+/**
+ * Add columns for e-mail HTML and attachment settings.
+ */
+function webform_update_7302() {
+  if (!db_field_exists('webform_emails', 'html')) {
+    db_add_field('webform_emails', 'html', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
+    db_add_field('webform_emails', 'attachments', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'default' => 0, 'not null' => TRUE));
+  }
+}
+
+/**
+ * Set the default for the "submit_notice" column to 1.
+ */
+function webform_update_7303() {
+  db_change_field('webform', 'submit_notice', 'submit_notice', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
+}
+
+/**
+ * Add field for block feature and redirection setting.
+ */
+function webform_update_7304() {
+  if (!db_field_exists('webform', 'block')) {
+    db_add_field('webform', 'block', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
+    db_change_field('webform', 'redirect_url', 'redirect_url', array('type' => 'varchar', 'length' => 255, 'default' => '<confirmation>'));
+    db_update('webform')
+      ->fields(array('redirect_url' => 'confirmation'))
+      ->condition('redirect_url', '')
+      ->execute();
+  }
+}
+
+/**
+ * Set additional_validate and additional_submit columns to allow NULL.
+ */
+function webform_update_7305() {
+  if (db_field_exists('webform', 'additional_validate')) {
+    db_change_field('webform', 'additional_validate', 'additional_validate', array('type' => 'text', 'not null' => FALSE));
+    db_change_field('webform', 'additional_submit', 'additional_submit', array('type' => 'text', 'not null' => FALSE));
+  }
+}
+
+/**
+ * Add column for webform status (open or closed).
+ */
+function webform_update_7306() {
+  if (!db_field_exists('webform', 'status')) {
+    db_add_field('webform', 'status', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1));
+  }
+}
+
+/**
+ * Update the confirmation_format column for default text format changes.
+ */
+function webform_update_7307() {
+  // Update removed and moved to webform_update_7301().
+  // See http://drupal.org/node/976102.
+}
+
+/**
+ * Update the confirmation_format column to allow it to store strings.
+ */
+function webform_update_7308() {
+  db_change_field('webform', 'confirmation_format', 'confirmation_format', array(
+    'description' => 'The {filter_format}.format of the confirmation message.',
+    'type' => 'varchar',
+    'length' => 255,
+    'not null' => FALSE,
+  ));
+}
+
+/**
+ * Add the ability to auto-save as draft between pages.
+ */
+function webform_update_7309() {
+  if (!db_field_exists('webform', 'auto_save')) {
+    db_add_field('webform', 'auto_save', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
+  }
+}
+
+/**
+ * Remove orphaned and unnecessary rows in the webform table.
+ */
+function webform_update_7310() {
+  $result = db_query("SELECT nid FROM {webform} WHERE
+    nid NOT IN
+    (SELECT DISTINCT(w1.nid) FROM {webform} w1 INNER JOIN {webform_component} wc ON w1.nid = wc.nid)
+    AND nid NOT IN
+    (SELECT w2.nid FROM {webform} w2 INNER JOIN {node} n ON w2.nid = n.nid WHERE n.type = 'webform')"
+  );
+  $empty_nids = array();
+  foreach ($result as $row) {
+    $empty_nids[] = $row->nid;
+  }
+  if (!empty($empty_nids)) {
+    db_delete('webform')->condition('nid', $empty_nids, 'IN')->execute();
+  }
+}
+
+/**
+ * Add an index for nid_uid_sid to webform_submissions.
+ */
+function webform_update_7311() {
+  if (!db_index_exists('webform_submissions', 'nid_uid_sid')) {
+    db_add_index('webform_submissions', 'nid_uid_sid', array('nid', 'uid', 'sid'));
+  }
+}
+
+/**
+ * Remove unused Webform variables.
+ */
+function webform_update_7312() {
+  variable_del('node_types');
+  variable_del('components');
+}
+
+/**
+ * Convert the Date component start and end year options to start and end date.
+ */
+function webform_update_7313() {
+  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wc')
+    ->condition('type', 'date')
+    ->execute();
+  foreach ($result as $component) {
+    $component['extra'] = unserialize($component['extra']);
+    if (!isset($component['extra']['start_date']) && !isset($component['end_date'])) {
+      foreach (array('year_start' => 'start_date', 'year_end' => 'end_date') as $key => $replacement) {
+        $value = isset($component['extra'][$key]) ? trim($component['extra'][$key]) : '';
+        // Relative years.
+        if (preg_match('/[-+][ ]*[0-9]+/', $value)) {
+          $component['extra'][$replacement] = ($value == 1) ? ($value . ' year') : ($value . ' years');
+        }
+        // Absolute years.
+        elseif (is_numeric($value)) {
+          $component['extra'][$replacement] = 'Dec 31 ' . $value;
+        }
+        unset($component['extra'][$key]);
+      }
+      $component['extra'] = serialize($component['extra']);
+      drupal_write_record('webform_component', $component, array('nid', 'cid'));
+    }
+  }
+}
+
+/**
+ * Add webform_last_download table to store last downloaded sid per user.
+ */
+function webform_update_7314() {
+  // Safety check to prevent recreating the webform_last_download table.
+  if (db_table_exists('webform_last_download')) {
+    return;
+  }
+
+  $schema['webform_last_download'] = array(
+    'description' => 'Stores last submission number per user download.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The node identifier of a webform.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => 'The user identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'sid' => array(
+        'description' => 'The last downloaded submission number.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid', 'uid'),
+  );
+  db_create_table('webform_last_download', $schema['webform_last_download']);
+}
+
+/**
+ * Add column for timestamp of last requested CSV download.
+ */
+function webform_update_7315() {
+  if (!db_field_exists('webform_last_download', 'requested')) {
+    db_add_field('webform_last_download', 'requested', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0,));
+  }
+}
+
+/**
+ * Add additional columns for total submission limit.
+ */
+function webform_update_7316() {
+  if (!db_field_exists('webform', 'total_submit_limit')) {
+    db_add_field('webform', 'total_submit_limit', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
+  }
+
+  if (!db_field_exists('webform', 'total_submit_interval')) {
+    db_add_field('webform', 'total_submit_interval', array('type' => 'int', 'not null' => TRUE, 'default' => -1));
+  }
+}
+
+/**
+ * Add an index for 'nid_sid' to webform_submissions.
+ */
+function webform_update_7317() {
+  // Even though we already have an index 'nid_uid_sid', adding the index for
+  // 'nid_sid' saves us a tablesort on the node/x/webform-results page.
+  if (!db_index_exists('webform_submissions', 'nid_sid')) {
+    db_add_index('webform_submissions', 'nid_sid', array('nid', 'sid'));
+  }
+}
+
+/**
+ * Upgrade file components to support the new AJAX-upload element.
+ */
+function webform_update_7318() {
+  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wc')
+    ->condition('type', 'file')
+    ->execute();
+  foreach ($result as $component) {
+    $component['extra'] = unserialize($component['extra']);
+    if (!isset($component['extra']['directory'])) {
+      $component['extra']['directory'] = $component['extra']['savelocation'];
+      $component['extra']['scheme'] = file_default_scheme();
+      $component['extra']['filtering']['size'] = $component['extra']['filtering']['size'] . ' KB';
+      unset($component['extra']['savelocation']);
+      $component['extra'] = serialize($component['extra']);
+      drupal_write_record('webform_component', $component, array('nid', 'cid'));
+    }
+  }
+
+  return t('File components updated to support AJAX uploading.');
+}
+
+/**
+ * Add file usage entries for all files uploaded through Webform.
+ */
+function webform_update_7319(&$sandbox) {
+  if (!isset($sandbox['progress'])) {
+    // Initialize batch update information.
+    $sandbox['progress'] = 0;
+    $sandbox['last_fid_processed'] = -1;
+    $sandbox['max'] = db_select('file_managed')
+      ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+  }
+
+  // Process all files attached to a given revision during the same batch.
+  $limit = variable_get('webform_update_batch_size', 100);
+  $files = db_select('file_managed', 'f')
+    ->fields('f')
+    ->condition('uri', '%' . db_like('://webform/') . '%', 'LIKE')
+    ->condition('fid', $sandbox['last_fid_processed'], '>')
+    ->orderBy('fid', 'ASC')
+    ->range(0, $limit)
+    ->execute()
+    ->fetchAllAssoc('fid', PDO::FETCH_ASSOC);
+
+  // Determine each submission with which a file is associated.
+  if (!empty($files)) {
+    foreach ($files as $fid => $file) {
+      $file = (object) $file;
+      $sids = db_query('SELECT wsd.sid FROM {webform_component} wc INNER JOIN {webform_submitted_data} wsd ON wc.nid = wsd.nid AND wc.type = :file WHERE data = :fid', array(':file' => 'file', ':fid' => $file->fid))->fetchAllAssoc('sid', PDO::FETCH_ASSOC);
+      foreach ($sids as $sid => $row) {
+        // We use a db_merge() instead of file_usage_add() to prevent problems
+        // in the event this update was run twice. No file provided by Webform
+        // should ever be in use more than once at this point.
+        db_merge('file_usage')
+          ->key(array(
+            'fid' => $file->fid,
+            'type' => 'submission',
+            'module' => 'webform',
+            'id' => $sid,
+          ))
+          ->fields(array(
+            'count' => 1,
+          ))
+          ->execute();
+      }
+
+      // Update our progress information for the batch update.
+      $sandbox['progress']++;
+      $sandbox['last_fid_processed'] = $file->fid;
+    }
+  }
+
+  // If less than limit was processed, the update process is finished.
+  if (count($files) < $limit || $sandbox['progress'] == $sandbox['max']) {
+    $finished = TRUE;
+  }
+
+  // If there's no max value then there's nothing to update and we're finished.
+  if (empty($sandbox['max']) || isset($finished)) {
+    return t('Webform file entries created in the file_usage table.');
+  }
+  else {
+    // Indicate our current progress to the batch update system.
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
+}
+
+/**
+ * Mark files uploaded through Webform that report active usage permanent.
+ */
+function webform_update_7320() {
+  db_query("UPDATE {file_managed} SET status = 1 WHERE fid IN (SELECT fid FROM {file_usage} WHERE module = :module_name)", array(':module_name' => 'webform'));
+}
+
+/**
+ * Remove files left over from deleted submissions. Such files are now deleted
+ * automatically.
+ */
+function webform_update_7321() {
+  module_load_include('inc', 'webform', 'components/file');
+  $fids = db_query("SELECT fid FROM {file_usage} WHERE module = 'webform' AND type = 'submission' AND NOT id IN(SELECT sid FROM {webform_submissions})")->fetchCol();
+  foreach ($fids as $fid) {
+    _webform_delete_file(NULL, array($fid));
+  }
+}
diff -Naurw webform_old/webform.module webform/webform.module
--- webform_old/webform.module	1970-01-01 01:00:00.000000000 +0100
+++ webform/webform.module	2013-07-22 17:38:13.000000000 +0100
@@ -0,0 +1,3929 @@
+<?php
+
+/**
+ * This module provides a simple way to create forms and questionnaires.
+ *
+ * The initial development of this module was sponsered by ÅF Industri AB, Open
+ * Source City and Karlstad University Library. Continued development sponsored
+ * by Lullabot.
+ *
+ * @author Nathan Haug <nate@lullabot.com>
+ */
+
+/**
+ * Implements hook_help().
+ */
+function webform_help($section = 'admin/help#webform', $arg = NULL) {
+  $output = '';
+  switch ($section) {
+    case 'admin/config/content/webform':
+      module_load_include('inc', 'webform', 'includes/webform.admin');
+      $type_list = webform_admin_type_list();
+      $output = t('Webform enables nodes to have attached forms and questionnaires.');
+      if ($type_list) {
+        $output .= ' ' . t('To add one, create a !types piece of content.', array('!types' => $type_list));
+      }
+      else {
+        $output .= ' <strong>' . t('Webform is currently not enabled on any content types.') . '</strong> ' . t('To use Webform, please enable it on at least one content type on this page.');
+      }
+      $output = '<p>' . $output . '</p>';
+      break;
+    case 'admin/content/webform':
+      $output = '<p>' . t('This page lists all of the content on the site that may have a webform attached to it.') . '</p>';
+      break;
+    case 'admin/help#webform':
+      module_load_include('inc', 'webform', 'includes/webform.admin');
+      $types = webform_admin_type_list();
+      if (empty($types)) {
+        $types = t('Webform-enabled piece of content');
+        $types_message = t('Webform is currently not enabled on any content types.') . ' ' . t('Visit the <a href="!url">Webform settings</a> page and enable Webform on at least one content type.', array('!url' => url('admin/config/content/webform')));
+      }
+      else {
+        $types_message = t('Optional: Enable Webform on multiple types by visiting the <a href="!url">Webform settings</a> page.', array('!url' => url('admin/config/content/webform')));
+      }
+      $output = t("<p>This module lets you create forms or questionnaires and define their content. Submissions from these forms are stored in the database and optionally also sent by e-mail to a predefined address.</p>
+      <p>Here is how to create one:</p>
+      <ul>
+        <li>!webform-types-message</li>
+        <li>Go to <a href=\"!create-content\">Create content</a> and add a !types piece of content.</li>
+        <li>After saving the new content, you will be redirected to the main field list of the form that will be created. Add the fields you would like on your form.</li>
+        <li>Once finished adding fields, you may want to send e-mails to administrators or back to the user who filled out the form. Click on the <em>Emails</em> sub-tab underneath the <em>Webform</em> tab on the piece of content.</li>
+        <li>Finally, visit the <em>Form settings</em> sub-tab under the <em>Webform</em> tab to configure remaining configurations options for your form.
+          <ul>
+          <li>Add a confirmation message and/or redirect URL that is to be displayed after successful submission.</li>
+          <li>Set a submission limit.</li>
+          <li>Determine which roles may submit the form.</li>
+          <li>Advanced configuration options such as allowing drafts or show users a message indicating how they can edit their submissions.</li>
+          </ul>
+        </li>
+        <li>Your form is now ready for viewing. After receiving submissions, you can check the results users have submitted by visiting the <em>Results</em> tab on the piece of content.</li>
+      </ul>
+      <p>Help on adding and configuring the components will be shown after you add your first component.</p>
+      ", array('!webform-types-message' => $types_message, '!create-content' => url('node/add'), '!types' => $types));
+      break;
+    case 'node/%/webform/components':
+      $output .= '<p>' . t('This page displays all the components currently configured for this webform node. You may add any number of components to the form, even multiple of the same type. To add a new component, fill in a name and select a type from the fields at the bottom of the table. Submit the form to create the new component or update any changed form values.') . '</p>';
+      $output .= '<p>' . t('Click on any existing component\'s name to edit its settings.') . '</p>';
+      break;
+    case 'node/%/submission/%/resend':
+      $output .= '<p>' . t('This form may be used to resend e-mails configured for this webform. Check the e-mails that need to be sent and click <em>Resend e-mails</em> to send these e-mails again.') . '</p>';
+      break;
+  }
+
+  return $output;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function webform_menu() {
+  $items = array();
+
+  // Submissions listing.
+  $items['admin/content/webform'] = array(
+    'title' => 'Webforms',
+    'page callback' => 'webform_admin_content',
+    'access callback' => 'user_access',
+    'access arguments' => array('access all webform results'),
+    'description' => 'View and edit all the available webforms on your site.',
+    'file' => 'includes/webform.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  // Admin Settings.
+  $items['admin/config/content/webform'] = array(
+    'title' => 'Webform settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_admin_settings'),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer site configuration'),
+    'description' => 'Global configuration of webform functionality.',
+    'file' => 'includes/webform.admin.inc',
+    'type' => MENU_NORMAL_ITEM,
+  );
+
+  // Node page tabs.
+  $items['node/%webform_menu/done'] = array(
+    'title' => 'Webform confirmation',
+    'page callback' => '_webform_confirmation',
+    'page arguments' => array(1),
+    'access callback' => 'node_access',
+    'access arguments' => array('view', 1),
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%webform_menu/webform'] = array(
+    'title' => 'Webform',
+    'page callback' => 'webform_components_page',
+    'page arguments' => array(1),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.components.inc',
+    'weight' => 1,
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+  );
+  $items['node/%webform_menu/webform/components'] = array(
+    'title' => 'Form components',
+    'page callback' => 'webform_components_page',
+    'page arguments' => array(1),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.components.inc',
+    'weight' => 0,
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform/configure'] = array(
+    'title' => 'Form settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_configure_form', 1),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.pages.inc',
+    'weight' => 2,
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  // Node e-mail forms.
+  $items['node/%webform_menu/webform/emails'] = array(
+    'title' => 'E-mails',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_emails_form', 1),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.emails.inc',
+    'weight' => 1,
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform/emails/%webform_menu_email'] = array(
+    'title' => 'Edit e-mail settings',
+    'load arguments' => array(1),
+    'page arguments' => array('webform_email_edit_form', 1, 4),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.emails.inc',
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%webform_menu/webform/emails/%webform_menu_email/delete'] = array(
+    'title' => 'Delete e-mail settings',
+    'load arguments' => array(1),
+    'page arguments' => array('webform_email_delete_form', 1, 4),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'type' => MENU_CALLBACK,
+  );
+
+  // Node component forms.
+  $items['node/%webform_menu/webform/components/%webform_menu_component'] = array(
+    'load arguments' => array(1, 5),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_component_edit_form', 1, 4, FALSE),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.components.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform/components/%webform_menu_component/clone'] = array(
+    'load arguments' => array(1, 5),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_component_edit_form', 1, 4, TRUE),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.components.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform/components/%webform_menu_component/delete'] = array(
+    'load arguments' => array(1, 5),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_component_delete_form', 1, 4),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'file' => 'includes/webform.components.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  // AJAX callback for loading select list options.
+  $items['webform/ajax/options/%webform_menu'] = array(
+    'load arguments' => array(3),
+    'page callback' => 'webform_select_options_ajax',
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 3),
+    'file' => 'components/select.inc',
+    'type' => MENU_CALLBACK,
+  );
+
+  // Node webform results.
+  $items['node/%webform_menu/webform-results'] = array(
+    'title' => 'Results',
+    'page callback' => 'webform_results_submissions',
+    'page arguments' => array(1, FALSE, '50'),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 2,
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+  );
+  $items['node/%webform_menu/webform-results/submissions'] = array(
+    'title' => 'Submissions',
+    'page callback' => 'webform_results_submissions',
+    'page arguments' => array(1, FALSE, '50'),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 4,
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform-results/analysis'] = array(
+    'title' => 'Analysis',
+    'page callback' => 'webform_results_analysis',
+    'page arguments' => array(1),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 5,
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform-results/analysis/%webform_menu_component'] = array(
+    'title' => 'Analysis',
+    'load arguments' => array(1, 4),
+    'page callback' => 'webform_results_analysis',
+    'page arguments' => array(1, array(), 4),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%webform_menu/webform-results/table'] = array(
+    'title' => 'Table',
+    'page callback' => 'webform_results_table',
+    'page arguments' => array(1, '50'),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 6,
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform-results/download'] = array(
+    'title' => 'Download',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_results_download_form', 1),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 7,
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/webform-results/clear'] = array(
+    'title' => 'Clear',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_results_clear_form', 1),
+    'access callback' => 'webform_results_clear_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.report.inc',
+    'weight' => 8,
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  // Node submissions.
+  $items['node/%webform_menu/submissions'] = array(
+    'title' => 'Submissions',
+    'page callback' => 'webform_results_submissions',
+    'page arguments' => array(1, TRUE, '50'),
+    'access callback' => 'webform_submission_access',
+    'access arguments' => array(1, NULL, 'list'),
+    'file' => 'includes/webform.report.inc',
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%webform_menu/submission/%webform_menu_submission'] = array(
+    'title' => 'Webform submission',
+    'load arguments' => array(1),
+    'page callback' => 'webform_submission_page',
+    'page arguments' => array(1, 3, 'html'),
+    'title callback' => 'webform_submission_title',
+    'title arguments' => array(1, 3),
+    'access callback' => 'webform_submission_access',
+    'access arguments' => array(1, 3, 'view'),
+    'file' => 'includes/webform.submissions.inc',
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%webform_menu/submission/%webform_menu_submission/view'] = array(
+    'title' => 'View',
+    'load arguments' => array(1),
+    'page callback' => 'webform_submission_page',
+    'page arguments' => array(1, 3, 'html'),
+    'access callback' => 'webform_submission_access',
+    'access arguments' => array(1, 3, 'view'),
+    'weight' => 0,
+    'file' => 'includes/webform.submissions.inc',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/submission/%webform_menu_submission/edit'] = array(
+    'title' => 'Edit',
+    'load arguments' => array(1),
+    'page callback' => 'webform_submission_page',
+    'page arguments' => array(1, 3, 'form'),
+    'access callback' => 'webform_submission_access',
+    'access arguments' => array(1, 3, 'edit'),
+    'weight' => 1,
+    'file' => 'includes/webform.submissions.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/submission/%webform_menu_submission/delete'] = array(
+    'title' => 'Delete',
+    'load arguments' => array(1),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_submission_delete_form', 1, 3),
+    'access callback' => 'webform_submission_access',
+    'access arguments' => array(1, 3, 'delete'),
+    'weight' => 2,
+    'file' => 'includes/webform.submissions.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['node/%webform_menu/submission/%webform_menu_submission/resend'] = array(
+    'title' => 'Resend e-mails',
+    'load arguments' => array(1),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('webform_submission_resend', 1, 3),
+    'access callback' => 'webform_results_access',
+    'access arguments' => array(1),
+    'file' => 'includes/webform.submissions.inc',
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Menu loader callback. Load a webform node if the given nid is a webform.
+ */
+function webform_menu_load($nid) {
+  if (!is_numeric($nid)) {
+    return FALSE;
+  }
+  $node = node_load($nid);
+  if (!isset($node->type) || !in_array($node->type, webform_variable_get('webform_node_types'))) {
+    return FALSE;
+  }
+  return $node;
+}
+
+/**
+ * Menu loader callback. Load a webform submission if the given sid is a valid.
+ */
+function webform_menu_submission_load($sid, $nid) {
+  module_load_include('inc', 'webform', 'includes/webform.submissions');
+  $submission = webform_get_submission($nid, $sid);
+  return empty($submission) ? FALSE : $submission;
+}
+
+/**
+ * Menu loader callback. Load a webform component if the given cid is a valid.
+ */
+function webform_menu_component_load($cid, $nid, $type) {
+  module_load_include('inc', 'webform', 'includes/webform.components');
+  if ($cid == 'new') {
+    $components = webform_components();
+    $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE;
+  }
+  else {
+    $node = node_load($nid);
+    $component = isset($node->webform['components'][$cid]) ? $node->webform['components'][$cid] : FALSE;
+  }
+  if ($component) {
+    webform_component_defaults($component);
+  }
+  return $component;
+}
+
+
+/**
+ * Menu loader callback. Load a webform e-mail if the given eid is a valid.
+ */
+function webform_menu_email_load($eid, $nid) {
+  module_load_include('inc', 'webform', 'includes/webform.emails');
+  $node = node_load($nid);
+  $email = webform_email_load($eid, $nid);
+  if ($eid == 'new') {
+    if (isset($_GET['option']) && isset($_GET['email'])) {
+      $type = $_GET['option'];
+      if ($type == 'custom') {
+        $email['email'] = $_GET['email'];
+      }
+      elseif ($type == 'component' && isset($node->webform['components'][$_GET['email']])) {
+        $email['email'] = $_GET['email'];
+      }
+    }
+  }
+
+  return $email;
+}
+
+function webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
+  global $user;
+  $account = isset($account) ? $account : $user;
+
+  $access_all = user_access('access all webform results', $account);
+  $access_own_submission = isset($submission) && user_access('access own webform submissions', $account) && (($account->uid && $account->uid == $submission->uid) || isset($_SESSION['webform_submission'][$submission->sid]));
+  $access_node_submissions = user_access('access own webform results', $account) && $account->uid == $node->uid;
+
+  $general_access = $access_all || $access_own_submission || $access_node_submissions;
+
+  // Disable the page cache for anonymous users in this access callback,
+  // otherwise the "Access denied" page gets cached.
+  if (!$account->uid && user_access('access own webform submissions', $account)) {
+    webform_disable_page_cache();
+  }
+
+  $module_access = count(array_filter(module_invoke_all('webform_submission_access', $node, $submission, $op, $account))) > 0;
+
+  switch ($op) {
+    case 'view':
+      return $module_access || $general_access;
+    case 'edit':
+      return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid)));
+    case 'delete':
+      return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid)));
+    case 'list':
+      return $module_access || user_access('access all webform results', $account) || (user_access('access own webform submissions', $account) && ($account->uid || isset($_SESSION['webform_submission']))) || (user_access('access own webform results', $account) && $account->uid == $node->uid);
+  }
+}
+
+/**
+ * Menu access callback. Ensure a user both access and node 'view' permission.
+ */
+function webform_results_access($node, $account = NULL) {
+  global $user;
+  $account = isset($account) ? $account : $user;
+
+  $module_access = count(array_filter(module_invoke_all('webform_results_access', $node, $account))) > 0;
+
+  return node_access('view', $node, $account) && ($module_access || user_access('access all webform results', $account) || (user_access('access own webform results', $account) && $account->uid == $node->uid));
+}
+
+function webform_results_clear_access($node, $account = NULL) {
+  global $user;
+  $account = isset($account) ? $account : $user;
+
+  $module_access = count(array_filter(module_invoke_all('webform_results_clear_access', $node, $account))) > 0;
+
+  return webform_results_access($node, $account) && ($module_access || user_access('delete all webform submissions', $account));
+}
+
+/**
+ * Implements hook_admin_paths().
+ */
+function webform_admin_paths() {
+  if (variable_get('node_admin_theme')) {
+    return array(
+      'node/*/webform' => TRUE,
+      'node/*/webform/*' => TRUE,
+      'node/*/webform-results' => TRUE,
+      'node/*/webform-results/*' => TRUE,
+      'node/*/submission/*' => TRUE,
+    );
+  }
+}
+
+/**
+ * Implements hook_perm().
+ */
+function webform_permission() {
+  return array(
+    'access all webform results' => array(
+      'title' => t('Access all webform results'),
+      'description' => t('Grants access to the "Results" tab on all webform content. Generally an administrative permission.'),
+    ),
+    'access own webform results' => array(
+      'title' => t('Access own webform results'),
+      'description' => t('Grants access to the "Results" tab to the author of webform content they have created.'),
+    ),
+    'edit all webform submissions' => array(
+      'title' => t('Edit all webform submissions'),
+      'description' => t('Allows editing of any webform submission by any user. Generally an administrative permission.'),
+    ),
+    'delete all webform submissions' => array(
+      'title' => t('Delete all webform submissions'),
+      'description' => t('Allows deleting of any webform submission by any user. Generally an administrative permission.'),
+    ),
+    'access own webform submissions' => array(
+      'title' => t('Access own webform submissions'),
+    ),
+    'edit own webform submissions' => array(
+      'title' => t('Edit own webform submissions'),
+    ),
+    'delete own webform submissions' => array(
+      'title' => t('Delete own webform submissions'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_theme().
+ */
+function webform_theme() {
+  $theme = array(
+    // webform.module.
+    'webform_view' => array(
+      'render element' => 'webform',
+    ),
+    'webform_view_messages' => array(
+      'variables' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'user_limit_exceeded' => NULL, 'total_limit_exceeded' => NULL, 'allowed_roles' => NULL, 'closed' => NULL, 'cached' => NULL),
+    ),
+    'webform_form' => array(
+      'render element' => 'form',
+      'template' => 'templates/webform-form',
+      'pattern' => 'webform_form_[0-9]+',
+    ),
+    'webform_confirmation' => array(
+      'variables' => array('node' => NULL, 'sid' => NULL),
+      'template' => 'templates/webform-confirmation',
+      'pattern' => 'webform_confirmation_[0-9]+',
+    ),
+    'webform_element' => array(
+      'render element' => 'element',
+    ),
+    'webform_element_text' => array(
+      'render element' => 'element',
+    ),
+    'webform_inline_radio' => array(
+      'render element' => 'element',
+    ),
+    'webform_mail_message' => array(
+      'variables' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
+      'template' => 'templates/webform-mail',
+      'pattern' => 'webform_mail(_[0-9]+)?',
+    ),
+    'webform_mail_headers' => array(
+      'variables' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
+      'pattern' => 'webform_mail_headers_[0-9]+',
+    ),
+    'webform_token_help' => array(
+      'variables' => array('groups' => array()),
+    ),
+    // webform.admin.inc.
+    'webform_admin_settings' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.admin.inc',
+    ),
+    'webform_admin_content' => array(
+      'variables' => array('nodes' => NULL),
+      'file' => 'includes/webform.admin.inc',
+    ),
+    // webform.emails.inc.
+    'webform_emails_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.emails.inc',
+    ),
+    'webform_email_add_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.emails.inc',
+    ),
+    'webform_email_edit_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.emails.inc',
+    ),
+    // webform.components.inc.
+    'webform_components_page' => array(
+      'variables' => array('node' => NULL, 'form' => NULL),
+      'file' => 'includes/webform.components.inc',
+    ),
+    'webform_components_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.components.inc',
+    ),
+    'webform_component_select' => array(
+      'render element' => 'element',
+      'file' => 'includes/webform.components.inc',
+    ),
+    // webform.pages.inc.
+    'webform_advanced_redirection_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.pages.inc',
+    ),
+    'webform_advanced_submit_limit_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.pages.inc',
+    ),
+    'webform_advanced_total_submit_limit_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.pages.inc',
+    ),
+    // webform.report.inc.
+    'webform_results_per_page' => array(
+      'variables' => array('total_count' => NULL, 'pager_count' => NULL),
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_submissions_header' => array(
+      'variables' => array('node' => NULL),
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_submissions' => array(
+      'render element' => 'element',
+      'template' => 'templates/webform-results-submissions',
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_table_header' => array(
+      'variables' => array('node' => NULL),
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_table' => array(
+      'variables' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL, 'total_count' => NULL, 'pager_count' => NULL),
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_download_range' => array(
+      'render element' => 'element',
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_download_select_format' => array(
+      'render element' => 'element',
+      'file' => 'includes/webform.report.inc',
+    ),
+    'webform_results_analysis' => array(
+      'variables' => array('node' => NULL, 'data' => NULL, 'sids' => array(), 'component' => NULL),
+      'file' => 'includes/webform.report.inc',
+    ),
+    // webform.submissions.inc
+    'webform_submission' => array(
+      'render element' => 'renderable',
+      'template' => 'templates/webform-submission',
+      'pattern' => 'webform_submission_[0-9]+',
+      'file' => 'includes/webform.submissions.inc',
+    ),
+    'webform_submission_page' => array(
+      'variables' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL, 'submission_actions' => NULL, 'mode' => NULL),
+      'template' => 'templates/webform-submission-page',
+      'file' => 'includes/webform.submissions.inc',
+    ),
+    'webform_submission_information' => array(
+      'variables' => array('node' => NULL, 'submission' => NULL, 'mode' => 'display'),
+      'template' => 'templates/webform-submission-information',
+      'file' => 'includes/webform.submissions.inc',
+    ),
+    'webform_submission_navigation' => array(
+      'variables' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL),
+      'template' => 'templates/webform-submission-navigation',
+      'file' => 'includes/webform.submissions.inc',
+    ),
+    'webform_submission_resend' => array(
+      'render element' => 'form',
+      'file' => 'includes/webform.submissions.inc',
+    ),
+  );
+
+  // Theme functions in all components.
+  $components = webform_components(TRUE);
+  foreach ($components as $type => $component) {
+    if ($theme_additions = webform_component_invoke($type, 'theme')) {
+      $theme = array_merge($theme, $theme_additions);
+    }
+  }
+  return $theme;
+}
+
+/**
+ * Implements hook_library().
+ */
+function webform_library() {
+  $module_path = drupal_get_path('module', 'webform');
+
+  // Webform administration.
+  $libraries['admin'] = array(
+    'title' => 'Webform: Administration',
+    'website' => 'http://drupal.org/project/webform',
+    'version' => '1.0',
+    'js' => array(
+      $module_path . '/js/webform-admin.js' => array('group' => JS_DEFAULT),
+    ),
+    'css' => array(
+      $module_path . '/css/webform-admin.css' => array('group' => CSS_DEFAULT, 'weight' => 1),
+    ),
+  );
+
+  return $libraries;
+}
+
+/**
+ * Implements hook_element_info().
+ */
+function webform_element_info() {
+  // A few of our components need to be defined here because Drupal does not
+  // provide these components natively. Because this hook fires on every page
+  // load (even on non-webform pages), we don't put this in the component .inc
+  // files because of the unnecessary loading that it would require.
+  $elements['webform_time'] = array('#input' => 'TRUE');
+  $elements['webform_grid'] = array('#input' => 'TRUE');
+
+  $elements['webform_email'] = array(
+    '#input' => TRUE,
+    '#theme' => 'webform_email',
+    '#size' => 60,
+  );
+  $elements['webform_number'] = array(
+    '#input' => TRUE,
+    '#theme' => 'webform_number',
+    '#min' => NULL,
+    '#max' => NULL,
+    '#step' => NULL,
+  );
+
+  return $elements;
+}
+
+/**
+ * Implements hook_webform_component_info().
+ */
+function webform_webform_component_info() {
+  $component_info = array(
+    'date' => array(
+      'label' => t('Date'),
+      'description' => t('Presents month, day, and year fields.'),
+      'features' => array(
+        'conditional' => FALSE,
+      ),
+      'file' => 'components/date.inc',
+    ),
+    'email' => array(
+      'label' => t('E-mail'),
+      'description' => t('A special textfield that accepts e-mail addresses.'),
+      'file' => 'components/email.inc',
+      'features' => array(
+        'email_address' => TRUE,
+        'spam_analysis' => TRUE,
+      ),
+    ),
+    'fieldset' => array(
+      'label' => t('Fieldset'),
+      'description' => t('Fieldsets allow you to organize multiple fields into groups.'),
+      'features' => array(
+        'csv' => FALSE,
+        'default_value' => FALSE,
+        'required' => FALSE,
+        'conditional' => FALSE,
+        'group' => TRUE,
+        'title_inline' => FALSE,
+      ),
+      'file' => 'components/fieldset.inc',
+    ),
+    'grid' => array(
+      'label' => t('Grid'),
+      'description' => t('Allows creation of grid questions, denoted by radio buttons.'),
+      'features' => array(
+        'conditional' => FALSE,
+        'default_value' => FALSE,
+        'title_inline' => FALSE,
+      ),
+      'file' => 'components/grid.inc',
+    ),
+    'hidden' => array(
+      'label' => t('Hidden'),
+      'description' => t('A field which is not visible to the user, but is recorded with the submission.'),
+      'file' => 'components/hidden.inc',
+      'features' => array(
+        'required' => FALSE,
+        'description' => FALSE,
+        'email_address' => TRUE,
+        'email_name' => TRUE,
+        'title_display' => FALSE,
+        'private' => FALSE,
+      ),
+    ),
+    'markup' => array(
+      'label' => t('Markup'),
+      'description' => t('Displays text as HTML in the form; does not render a field.'),
+      'features' => array(
+        'csv' => FALSE,
+        'default_value' => FALSE,
+        'description' => FALSE,
+        'email' => FALSE,
+        'required' => FALSE,
+        'conditional' => FALSE,
+        'title_display' => FALSE,
+        'private' => FALSE,
+      ),
+      'file' => 'components/markup.inc',
+    ),
+    'number' => array(
+      'label' => t('Number'),
+      'description' => t('A numeric input field (either as textfield or select list).'),
+      'features' => array(
+      ),
+      'file' => 'components/number.inc',
+    ),
+    'pagebreak' => array(
+      'label' => t('Page break'),
+      'description' => t('Organize forms into multiple pages.'),
+      'features' => array(
+        'csv' => FALSE,
+        'default_value' => FALSE,
+        'description' => FALSE,
+        'private' => FALSE,
+        'required' => FALSE,
+        'title_display' => FALSE,
+      ),
+      'file' => 'components/pagebreak.inc',
+    ),
+    'select' => array(
+      'label' => t('Select options'),
+      'description' => t('Allows creation of checkboxes, radio buttons, or select menus.'),
+      'file' => 'components/select.inc',
+      'features' => array(
+        'default_value' => FALSE,
+        'email_address' => TRUE,
+        'email_name' => TRUE,
+      ),
+    ),
+    'textarea' => array(
+      'label' => t('Textarea'),
+      'description' => t('A large text area that allows for multiple lines of input.'),
+      'file' => 'components/textarea.inc',
+      'features' => array(
+        'spam_analysis' => TRUE,
+        'title_inline' => FALSE,
+      ),
+    ),
+    'textfield' => array(
+      'label' => t('Textfield'),
+      'description' => t('Basic textfield type.'),
+      'file' => 'components/textfield.inc',
+      'features' => array(
+        'email_name' => TRUE,
+        'spam_analysis' => TRUE,
+      ),
+    ),
+    'time' => array(
+      'label' => t('Time'),
+      'description' => t('Presents the user with hour and minute fields. Optional am/pm fields.'),
+      'features' => array(
+        'conditional' => FALSE,
+      ),
+      'file' => 'components/time.inc',
+    ),
+  );
+
+  if (module_exists('file')) {
+    $component_info['file'] = array(
+      'label' => t('File'),
+      'description' => t('Allow users to upload files of configurable types.'),
+      'features' => array(
+        'conditional' => FALSE,
+        'default_value' => FALSE,
+        'attachment' => TRUE,
+      ),
+      'file' => 'components/file.inc',
+    );
+  }
+
+  return $component_info;
+}
+
+/**
+ * Implements hook_forms().
+ *
+ * All webform_client_form forms share the same form handler
+ */
+function webform_forms($form_id) {
+  $forms = array();
+  if (strpos($form_id, 'webform_client_form_') === 0) {
+    $forms[$form_id]['callback'] = 'webform_client_form';
+  }
+  return $forms;
+}
+
+/**
+ * Implements hook_webform_select_options_info().
+ */
+function webform_webform_select_options_info() {
+  module_load_include('inc', 'webform', 'includes/webform.options');
+  return _webform_options_info();
+}
+
+/**
+ * Implements hook_webform_webform_submission_actions().
+ */
+function webform_webform_submission_actions($node, $submission) {
+  $actions = array();
+  $destination = drupal_get_destination();
+
+  if (module_exists('print_pdf') && user_access('access PDF version')) {
+    $actions['printpdf'] = array(
+      'title' => t('Download PDF'),
+      'href' => 'printpdf/' . $node->nid . '/submission/' . $submission->sid,
+      'query' => $destination,
+    );
+  }
+
+  if (module_exists('print') && user_access('access print')) {
+    $actions['print'] = array(
+      'title' => t('Print'),
+      'href' => 'print/' . $node->nid . '/submission/' . $submission->sid,
+    );
+  }
+
+  if (webform_results_access($node) && count($node->webform['emails'])) {
+    $actions['resend'] = array(
+      'title' => t('Resend e-mails'),
+      'href' => 'node/' . $node->nid . '/submission/' . $submission->sid . '/resend',
+      'query' => drupal_get_destination(),
+    );
+  }
+
+  return $actions;
+}
+
+/**
+ * Implements hook_webform_submission_update().
+ *
+ * We implement our own hook here to facilitate the File component, which needs
+ * to clean up manage file usage records and delete files from submissions that
+ * have been edited if necessary.
+ */
+function webform_webform_submission_presave($node, &$submission) {
+  // Check if there are any file components in this submission and if any of
+  // them currently contain files.
+  $has_file_components = FALSE;
+  $new_fids = array();
+  $old_fids = array();
+
+  foreach ($node->webform['components'] as $cid => $component) {
+    if ($component['type'] == 'file') {
+      $has_file_components = TRUE;
+      if (!empty($submission->data[$cid]['value'])) {
+        $new_fids = array_merge($new_fids, $submission->data[$cid]['value']);
+      }
+    }
+  }
+
+  if ($has_file_components) {
+    // If we're updating a submission, build a list of previous files.
+    if (isset($submission->sid)) {
+      $old_submission = webform_get_submission($node->nid, $submission->sid, TRUE);
+
+      foreach ($node->webform['components'] as $cid => $component) {
+        if ($component['type'] == 'file') {
+          if (!empty($old_submission->data[$cid]['value'])) {
+            $old_fids = array_merge($old_fids, $old_submission->data[$cid]['value']);
+          }
+        }
+      }
+    }
+
+    // Save the list of added or removed files so we can add usage in
+    // hook_webform_submission_insert() or _update().
+    $submission->file_usage = array(
+      // Diff the old against new to determine what files were deleted.
+      'deleted_fids' => array_diff($old_fids, $new_fids),
+      // Diff the new files against old to determine new uploads.
+      'added_fids' => array_diff($new_fids, $old_fids)
+    );
+  }
+}
+
+/**
+ * Implements hook_webform_submission_insert().
+ */
+function webform_webform_submission_insert($node, $submission) {
+  if (isset($submission->file_usage)) {
+    webform_component_include('file');
+    webform_file_usage_adjust($submission);
+  }
+}
+
+/**
+ * Implements hook_webform_submission_update().
+ */
+function webform_webform_submission_update($node, $submission) {
+  if (isset($submission->file_usage)) {
+    webform_component_include('file');
+    webform_file_usage_adjust($submission);
+  }
+}
+
+/**
+ * Implements hook_webform_submission_render_alter().
+ */
+function webform_webform_submission_render_alter(&$renderable) {
+  // If displaying a submission to end-users who are viewing their own
+  // submissions (and not through an e-mail), do not show hidden values.
+  // This needs to be implemented at the level of the entire submission, since
+  // individual components do not get contextual information about where they
+  // are being displayed.
+  $node = $renderable['#node'];
+  $is_admin = webform_results_access($node);
+  if (empty($renderable['#email']) && !$is_admin) {
+    // Find and hide the display of all hidden components.
+    foreach ($node->webform['components'] as $cid => $component) {
+      if ($component['type'] == 'hidden') {
+        $parents = webform_component_parent_keys($node, $component);
+        $element = &$renderable;
+        foreach ($parents as $pid) {
+          $element = &$element[$pid];
+        }
+        $element['#access'] = FALSE;
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_file_download().
+ *
+ * Only allow users with view webform submissions to download files.
+ */
+function webform_file_download($uri) {
+  module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+  // Determine whether this file was a webform upload.
+  $row = db_query("SELECT fu.id as sid, f.fid FROM {file_managed} f LEFT JOIN {file_usage} fu ON f.fid = fu.fid AND fu.module = :webform AND fu.type = :submission WHERE f.uri = :uri", array('uri' => $uri, ':webform' => 'webform', ':submission' => 'submission'))->fetchObject();
+  if ($row) {
+    $file = file_load($row->fid);
+  }
+  if (!empty($row->sid)) {
+    $submissions = webform_get_submissions(array('sid' => $row->sid));
+    $submission = reset($submissions);
+  }
+
+  // Grant access based on access to the submission.
+  if (!empty($submission)) {
+    $node = node_load($submission->nid);
+    if (webform_submission_access($node, $submission)) {
+      return file_get_content_headers($file);
+    }
+  }
+  // Grant access to files uploaded by a user before the submission is saved.
+  elseif (!empty($file) && !empty($_SESSION['webform_files'][$file->fid])) {
+    return file_get_content_headers($file);
+  }
+}
+
+/**
+ * Implements hook_node_type().
+ *
+ * Not a real hook in Drupal 7. Re-used for consistency with the D6 version.
+ */
+function webform_node_type($op, $info) {
+  $webform_types = webform_variable_get('webform_node_types');
+  $affected_type = isset($info->old_type) ? $info->old_type : $info->type;
+  $key = array_search($affected_type, $webform_types);
+  if ($key !== FALSE) {
+    if ($op == 'update') {
+      $webform_types[$key] = $info->type;
+    }
+    if ($op == 'delete') {
+      unset($webform_types[$key]);
+    }
+    variable_set('webform_node_types', $webform_types);
+  }
+}
+
+/**
+ * Implements hook_node_type_update().
+ */
+function webform_node_type_update($info) {
+  webform_node_type('update', $info);
+}
+
+/**
+ * Implements hook_node_type_delete().
+ */
+function webform_node_type_delete($info) {
+  webform_node_type('delete', $info);
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function webform_node_insert($node) {
+  if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
+    return;
+  }
+
+  // If added directly through node_save(), set defaults for the node.
+  if (!isset($node->webform)) {
+    $node->webform = webform_node_defaults();
+  }
+
+  // Do not make an entry if this node does not have any Webform settings.
+  if ($node->webform == webform_node_defaults() && !in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
+    return;
+  }
+
+  module_load_include('inc', 'webform', 'includes/webform.components');
+  module_load_include('inc', 'webform', 'includes/webform.emails');
+
+  // Insert the webform.
+  $node->webform['nid'] = $node->nid;
+  $node->webform['record_exists'] = (bool) drupal_write_record('webform', $node->webform);
+
+  // Insert the components into the database. Used with clone.module.
+  if (isset($node->webform['components']) && !empty($node->webform['components'])) {
+    foreach ($node->webform['components'] as $cid => $component) {
+      $component['nid'] = $node->nid; // Required for clone.module.
+      webform_component_insert($component);
+    }
+  }
+
+  // Insert emails. Also used with clone.module.
+  if (isset($node->webform['emails']) && !empty($node->webform['emails'])) {
+    foreach ($node->webform['emails'] as $eid => $email) {
+      $email['nid'] = $node->nid;
+      webform_email_insert($email);
+    }
+  }
+
+  // Set the per-role submission access control.
+  foreach (array_filter($node->webform['roles']) as $rid) {
+    db_insert('webform_roles')->fields(array('nid' => $node->nid, 'rid' => $rid))->execute();
+  }
+}
+
+/**
+ * Implements hook_node_update().
+ */
+function webform_node_update($node) {
+  if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
+    return;
+  }
+
+  // Check if this node needs a webform record at all. If it matches the
+  // defaults, any existing record will be deleted.
+  webform_check_record($node);
+
+  // If a webform row doesn't even exist, we can assume it needs to be inserted.
+  // If the the webform matches the defaults, no row will be inserted.
+  if (!$node->webform['record_exists']) {
+    webform_node_insert($node);
+    return;
+  }
+
+  // Update the webform entry.
+  $node->webform['nid'] = $node->nid;
+  drupal_write_record('webform', $node->webform, array('nid'));
+
+  // Compare the webform components and don't do anything if it's not needed.
+  // The internal cache needs to be reset here so that the cached node entity
+  // does not get loaded and invalidate the comparisons.
+  $original = node_load($node->nid, NULL, TRUE);
+
+  if ($original->webform['components'] != $node->webform['components']) {
+    module_load_include('inc', 'webform', 'includes/webform.components');
+
+    $original_cids = array_keys($original->webform['components']);
+    $current_cids = array_keys($node->webform['components']);
+
+    $all_cids = array_unique(array_merge($original_cids, $current_cids));
+    $deleted_cids = array_diff($original_cids, $current_cids);
+    $inserted_cids = array_diff($current_cids, $original_cids);
+
+    foreach ($all_cids as $cid) {
+      if (in_array($cid, $inserted_cids)) {
+        webform_component_insert($node->webform['components'][$cid]);
+      }
+      elseif (in_array($cid, $deleted_cids)) {
+        webform_component_delete($node, $original->webform['components'][$cid]);
+      }
+      elseif ($node->webform['components'][$cid] != $original->webform['components'][$cid]) {
+        $node->webform['components'][$cid]['nid'] = $node->nid;
+        webform_component_update($node->webform['components'][$cid]);
+      }
+    }
+  }
+
+  // Compare the webform e-mails and don't do anything if it's not needed.
+  if ($original->webform['emails'] != $node->webform['emails']) {
+    module_load_include('inc', 'webform', 'includes/webform.emails');
+
+    $original_eids = array_keys($original->webform['emails']);
+    $current_eids = array_keys($node->webform['emails']);
+
+    $all_eids = array_unique(array_merge($original_eids, $current_eids));
+    $deleted_eids = array_diff($original_eids, $current_eids);
+    $inserted_eids = array_diff($current_eids, $original_eids);
+
+    foreach ($all_eids as $eid) {
+      if (in_array($eid, $inserted_eids)) {
+        webform_email_insert($node->webform['emails'][$eid]);
+      }
+      elseif (in_array($eid, $deleted_eids)) {
+        webform_email_delete($node, $original->webform['emails'][$eid]);
+      }
+      elseif ($node->webform['emails'][$eid] != $original->webform['emails'][$eid]) {
+        $node->webform['emails'][$eid]['nid'] = $node->nid;
+        webform_email_update($node->webform['emails'][$eid]);
+      }
+    }
+  }
+
+  // Just delete and re-insert roles if they've changed.
+  if ($original->webform['roles'] != $node->webform['roles']) {
+    db_delete('webform_roles')->condition('nid', $node->nid)->execute();
+    foreach (array_filter($node->webform['roles']) as $rid) {
+      db_insert('webform_roles')->fields(array('nid' => $node->nid, 'rid' => $rid))->execute();
+    }
+  }
+}
+
+/**
+ * Implements hook_delete().
+ */
+function webform_node_delete($node) {
+  if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
+    return;
+  }
+
+  // Allow components clean up extra data, such as uploaded files.
+  module_load_include('inc', 'webform', 'includes/webform.components');
+  foreach ($node->webform['components'] as $cid => $component) {
+    webform_component_delete($node, $component);
+  }
+
+  // Remove any trace of webform data from the database.
+  db_delete('webform')->condition('nid', $node->nid)->execute();
+  db_delete('webform_component')->condition('nid', $node->nid)->execute();
+  db_delete('webform_emails')->condition('nid', $node->nid)->execute();
+  db_delete('webform_roles')->condition('nid', $node->nid)->execute();
+  db_delete('webform_submissions')->condition('nid', $node->nid)->execute();
+  db_delete('webform_submitted_data')->condition('nid', $node->nid)->execute();
+  db_delete('webform_last_download')->condition('nid', $node->nid)->execute();
+}
+
+/**
+ * Default settings for a newly created webform node.
+ */
+function webform_node_defaults() {
+  $defaults = array(
+    'confirmation' => '',
+    'confirmation_format' => NULL,
+    'redirect_url' => '<confirmation>',
+    'teaser' => '0',
+    'block' => '0',
+    'allow_draft' => '0',
+    'auto_save' => '0',
+    'submit_notice' => '1',
+    'submit_text' => '',
+    'submit_limit' => '-1',
+    'submit_interval' => '-1',
+    'total_submit_limit' => '-1',
+    'total_submit_interval' => '-1',
+    'status' => '1',
+    'record_exists' => FALSE,
+    'roles' => array('1', '2'),
+    'emails' => array(),
+    'components' => array(),
+  );
+  drupal_alter('webform_node_defaults', $defaults);
+  return $defaults;
+}
+
+/**
+ * Implements hook_node_prepare().
+ */
+function webform_node_prepare($node) {
+  $webform_types = webform_variable_get('webform_node_types');
+  if (in_array($node->type, $webform_types) && !isset($node->webform)) {
+    $node->webform = webform_node_defaults();
+  }
+}
+
+
+/**
+ * Implements hook_node_load().
+ */
+function webform_node_load($nodes, $types) {
+  // Quick check to see if we need to do anything at all for these nodes.
+  $webform_types = webform_variable_get('webform_node_types');
+  if (count(array_intersect($types, $webform_types)) == 0) {
+    return;
+  }
+
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  // Select all webforms that match these node IDs.
+  $result = db_select('webform')
+    ->fields('webform')
+    ->condition('nid', array_keys($nodes), 'IN')
+    ->execute()
+    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
+
+  foreach ($result as $nid => $webform) {
+    // Load the basic information for each node.
+    $nodes[$nid]->webform = $webform;
+    $nodes[$nid]->webform['record_exists'] = TRUE;
+  }
+
+  // Load the components, emails, and defaults for all webform-enabled nodes.
+  // TODO: Increase efficiency here by pulling in all information all at once
+  // instead of individual queries.
+  foreach ($nodes as $nid => $node) {
+    if (!in_array($node->type, $webform_types)) {
+      continue;
+    }
+
+    // If a webform record doesn't exist, just return the defaults.
+    if (!isset($nodes[$nid]->webform)) {
+      $nodes[$nid]->webform = webform_node_defaults();
+      continue;
+    }
+
+    $nodes[$nid]->webform['roles'] = db_select('webform_roles')
+      ->fields('webform_roles', array('rid'))
+      ->condition('nid', $nid)
+      ->execute()
+      ->fetchCol();
+    $nodes[$nid]->webform['emails'] = db_select('webform_emails')
+      ->fields('webform_emails')
+      ->condition('nid', $nid)
+      ->execute()
+      ->fetchAllAssoc('eid', PDO::FETCH_ASSOC);
+
+    // Unserialize the exclude component list for e-mails.
+    foreach ($nodes[$nid]->webform['emails'] as $eid => $email) {
+      $nodes[$nid]->webform['emails'][$eid]['excluded_components'] = array_filter(explode(',', $email['excluded_components']));
+      if (variable_get('webform_format_override', 0)) {
+        $nodes[$nid]->webform['emails'][$eid]['html'] = variable_get('webform_default_format', 0);
+      }
+    }
+
+    // Load components for each node.
+    $nodes[$nid]->webform['components'] = db_select('webform_component')
+      ->fields('webform_component')
+      ->condition('nid', $nid)
+      ->orderBy('weight')
+      ->orderBy('name')
+      ->execute()
+      ->fetchAllAssoc('cid', PDO::FETCH_ASSOC);
+
+    // Do a little cleanup on each component.
+    foreach ($nodes[$nid]->webform['components'] as $cid => $component) {
+      $nodes[$nid]->webform['components'][$cid]['nid'] = $nid;
+      $nodes[$nid]->webform['components'][$cid]['extra'] = unserialize($component['extra']);
+      webform_component_defaults($nodes[$nid]->webform['components'][$cid]);
+    }
+
+    // Organize the components into a fieldset-based order.
+    if (!empty($nodes[$nid]->webform['components'])) {
+      $component_tree = array();
+      $page_count = 1;
+      _webform_components_tree_build($nodes[$nid]->webform['components'], $component_tree, 0, $page_count);
+      $nodes[$nid]->webform['components'] = _webform_components_tree_flatten($component_tree['children']);
+    }
+  }
+}
+
+/**
+ * Implements hook_link().
+ * Always add a "view form" link.
+ */
+function webform_link($type, $node = NULL, $teaser = FALSE) {
+  $links = array();
+  if (isset($node->type) && $node->type === 'webform') {
+    if ($teaser && !$node->webform['teaser']) {
+      $links['webform_goto'] = array(
+        'title' => t('Go to form'),
+        'href' => 'node/' . $node->nid,
+        'attributes' => array('title' => t('View this form.'), 'class' => array('read-more'))
+      );
+    }
+  }
+  return $links;
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function webform_form_alter(&$form, $form_state, $form_id) {
+  $matches = array();
+  if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types'))) {
+    $node = $form['#node'];
+    // Preserve all Webform options currently set on the node.
+    $form['webform'] = array(
+      '#type' => 'value',
+      '#value' => $node->webform,
+    );
+
+    // If a new node, redirect the user to the components form after save.
+    if (empty($node->nid) && in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
+      $form['actions']['submit']['#submit'][] = 'webform_form_submit';
+    }
+  }
+}
+
+/**
+ * Submit handler for the webform node form.
+ *
+ * Redirect the user to the components form on new node inserts. Note that this
+ * fires after the hook_submit() function above.
+ */
+function webform_form_submit($form, &$form_state) {
+  drupal_set_message(t('The new webform %title has been created. Add new fields to your webform with the form below.', array('%title' => $form_state['values']['title'])));
+  $form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/components';
+}
+
+/**
+ * Implements hook_node_view().
+ */
+function webform_node_view($node, $view_mode) {
+  global $user;
+
+  if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
+    return;
+  }
+
+  // Set teaser and page variables a la Drupal 6.
+  $teaser = $view_mode == 'teaser';
+  $page = arg(0) == 'node' && arg(1) == $node->nid;
+
+  // If empty, a teaser, or a new node (during preview) do not display.
+  if (empty($node->webform['components']) || ($teaser && !$node->webform['teaser']) || empty($node->nid)) {
+    return;
+  }
+
+  // Do not include the form in the search index if indexing is disabled.
+  if (module_exists('search') && $view_mode == 'search_index' && !variable_get('webform_search_index', 1)) {
+    return;
+  }
+
+  $info = array();
+  $submission = array();
+  $submission_count = 0;
+  $enabled = TRUE;
+  $logging_in = FALSE;
+  $total_limit_exceeded = FALSE;
+  $user_limit_exceeded = FALSE;
+  $closed = FALSE;
+  $allowed_roles = array();
+
+  // If a teaser, tell the form to load subsequent pages on the node page.
+  if ($teaser && !isset($node->webform['action'])) {
+    $query = array_diff_key($_GET, array('q' => ''));
+    $node->webform['action'] = url('node/' . $node->nid, array('query' => $query));
+  }
+
+  // When logging in using a form on the same page as a webform node, suppress
+  // output messages so that they don't show up after the user has logged in.
+  // See http://drupal.org/node/239343.
+  if (isset($_POST['op']) && isset($_POST['name']) && isset($_POST['pass'])) {
+    $logging_in = TRUE;
+  }
+
+  if ($node->webform['status'] == 0) {
+    $closed = TRUE;
+    $enabled = FALSE;
+  }
+  else {
+    // Check if the user's role can submit this webform.
+    if (variable_get('webform_submission_access_control', 1)) {
+      foreach ($node->webform['roles'] as $rid) {
+        $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE;
+      }
+      if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
+        $enabled = FALSE;
+      }
+    }
+    else {
+      // If not using Webform submission access control, allow for all roles.
+      $allowed_roles = array_keys(user_roles());
+    }
+  }
+
+  // Get a count of previous submissions by this user. Note that the
+  // webform_submission_access() function may disable the page cache for
+  // anonymous users if they are allowed to edit their own submissions!
+  if ($page && webform_submission_access($node, NULL, 'list')) {
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+    $submission_count = webform_get_submission_count($node->nid, $user->uid);
+  }
+
+  // Check if this page is cached or not.
+  $cached = $user->uid == 0 && (variable_get('cache', 0) || drupal_page_is_cacheable() === FALSE);
+
+  // Check if the user can add another submission.
+  if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+    // Disable the form if the limit is exceeded and page cache is not active.
+    if (($user_limit_exceeded = _webform_submission_user_limit_check($node)) && !$cached) {
+      $enabled = FALSE;
+    }
+  }
+
+  // Check if the user can add another submission if there is a limit on total
+  // submissions.
+  if ($node->webform['total_submit_limit'] != -1) { // -1: Submissions are never throttled.
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+    // Disable the form if the limit is exceeded and page cache is not active.
+    if (($total_limit_exceeded = _webform_submission_total_limit_check($node)) && !$cached) {
+      $enabled = FALSE;
+    }
+  }
+
+  // Check if this user has a draft for this webform.
+  $is_draft = FALSE;
+  if (($node->webform['allow_draft'] || $node->webform['auto_save']) && $user->uid != 0) {
+    // Draft found - display form with draft data for further editing.
+    if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
+      module_load_include('inc', 'webform', 'includes/webform.submissions');
+      $submission = webform_get_submission($node->nid, $draft_sid);
+      $enabled = TRUE;
+      $is_draft = TRUE;
+    }
+  }
+
+  // Render the form and generate the output.
+  $form = !empty($node->webform['components']) ? drupal_get_form('webform_client_form_' . $node->nid, $node, $submission, $is_draft) : '';
+
+  // Remove the surrounding <form> tag if this is a preview.
+  if (!empty($node->in_preview)) {
+    $form['#type'] = 'markup';
+  }
+
+  // Print out messages for the webform.
+  if (empty($node->in_preview) && !isset($node->webform_block) && !$logging_in) {
+    theme('webform_view_messages', array('node' => $node, 'teaser' => $teaser, 'page' => $page, 'submission_count' => $submission_count, 'user_limit_exceeded' => $user_limit_exceeded, 'total_limit_exceeded' => $total_limit_exceeded, 'allowed_roles' => $allowed_roles, 'closed' => $closed, 'cached' => $cached));
+  }
+
+  // Add the output to the node.
+  $node->content['webform'] = array(
+    '#theme' => 'webform_view',
+    '#node' => $node,
+    '#teaser' => $teaser,
+    '#page' => $page,
+    '#form' => $form,
+    '#enabled' => $enabled,
+    '#weight' => 10,
+  );
+}
+
+/**
+ * Output the Webform into the node content.
+ *
+ * @param $node
+ *   The webform node object.
+ * @param $teaser
+ *   If this webform is being displayed as the teaser view of the node.
+ * @param $page
+ *   If this webform node is being viewed as the main content of the page.
+ * @param $form
+ *   The rendered form.
+ * @param $enabled
+ *   If the form allowed to be completed by the current user.
+ */
+function theme_webform_view($variables) {
+  // Only show the form if this user is allowed access.
+  if ($variables['webform']['#enabled']) {
+    return drupal_render($variables['webform']['#form']);
+  }
+}
+
+/**
+ * Display a message to a user if they are not allowed to fill out a form.
+ *
+ * @param $node
+ *   The webform node object.
+ * @param $teaser
+ *   If this webform is being displayed as the teaser view of the node.
+ * @param $page
+ *   If this webform node is being viewed as the main content of the page.
+ * @param $submission_count
+ *   The number of submissions this user has already submitted. Not calculated
+ *   for anonymous users.
+ * @param $user_limit_exceeded
+ *   Boolean value if the submission limit for this user has been exceeded.
+ * @param $total_limit_exceeded
+ *   Boolean value if the total submission limit has been exceeded.
+ * @param $allowed_roles
+ *   A list of user roles that are allowed to submit this webform.
+ * @param $closed
+ *   Boolean value if submissions are closed.
+ */
+function theme_webform_view_messages($variables) {
+  global $user;
+
+  $node = $variables['node'];
+  $teaser = $variables['teaser'];
+  $page = $variables['page'];
+  $submission_count = $variables['submission_count'];
+  $user_limit_exceeded = $variables['user_limit_exceeded'];
+  $total_limit_exceeded = $variables['total_limit_exceeded'];
+  $allowed_roles = $variables['allowed_roles'];
+  $closed = $variables['closed'];
+  $cached = $variables['cached'];
+
+  $type = 'status';
+
+  if ($closed) {
+    $message = t('Submissions for this form are closed.');
+  }
+  // If open and not allowed to submit the form, give an explanation.
+  elseif (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
+    if (empty($allowed_roles)) {
+      // No roles are allowed to submit the form.
+      $message = t('Submissions for this form are closed.');
+    }
+    elseif (isset($allowed_roles[2])) {
+      // The "authenticated user" role is allowed to submit and the user is currently logged-out.
+      $login = url('user/login', array('query' => drupal_get_destination()));
+      $register = url('user/register', array('query' => drupal_get_destination()));
+      if (variable_get('user_register', 1) == 0) {
+        $message = t('You must <a href="!login">login</a> to view this form.', array('!login' => $login));
+      }
+      else {
+        $message = t('You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.', array('!login' => $login, '!register' => $register));
+      }
+    }
+    else {
+      // The user must be some other role to submit.
+      $message = t('You do not have permission to view this form.');
+    }
+  }
+
+  // If the user has exceeded the limit of submissions, explain the limit.
+  elseif ($user_limit_exceeded && !$cached) {
+    if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1) {
+      $message = t('You have submitted this form the maximum number of times (@count).', array('@count' => $node->webform['submit_limit']));
+    }
+    elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) {
+      $message = t('You have already submitted this form.');
+    }
+    else {
+      $message = t('You may not submit another entry at this time.');
+    }
+    $type = 'error';
+  }
+  elseif ($total_limit_exceeded && !$cached) {
+    if ($node->webform['total_submit_interval'] == -1 && $node->webform['total_submit_limit'] > 1) {
+      $message = t('This form has received the maximum number of entries.');
+    }
+    else {
+      $message = t('You may not submit another entry at this time.');
+    }
+  }
+
+  // If the user has submitted before, give them a link to their submissions.
+  if ($submission_count > 0 && $node->webform['submit_notice'] == 1 && !$cached) {
+    if (empty($message)) {
+      $message = t('You have already submitted this form.') . ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
+    }
+    else {
+      $message .= ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
+    }
+  }
+
+  if ($page && isset($message)) {
+    drupal_set_message($message, $type, FALSE);
+  }
+}
+
+/**
+ * Implements hook_mail().
+ */
+function webform_mail($key, &$message, $params) {
+  $message['headers'] = array_merge($message['headers'], $params['headers']);
+  $message['subject'] = $params['subject'];
+  $message['body'][] = $params['message'];
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function webform_block_info() {
+  $blocks = array();
+  $webform_node_types = webform_variable_get('webform_node_types');
+  if (!empty($webform_node_types)) {
+    $query = db_select('webform', 'w')->fields('w')->fields('n', array('title'));
+    $query->leftJoin('node', 'n', 'w.nid = n.nid');
+    $query->condition('w.block', 1);
+    $query->condition('n.type', $webform_node_types, 'IN');
+    $result = $query->execute();
+    foreach ($result as $data) {
+      $blocks['client-block-' . $data->nid] = array(
+        'info' => t('Webform: !title', array('!title' => $data->title)),
+        'cache' => DRUPAL_NO_CACHE,
+      );
+    }
+  }
+  return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function webform_block_view($delta = '') {
+  global $user;
+
+  // Load the block-specific configuration settings.
+  $webform_blocks = variable_get('webform_blocks', array());
+  $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
+  $settings += array(
+    'display' => 'form',
+    'pages_block' => 0,
+  );
+
+  // Get the node ID from delta.
+  $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
+
+  // Load node in current language.
+  if (module_exists('translation')) {
+    global $language;
+    if (($translations = translation_node_get_translations($nid)) && (isset($translations[$language->language]))) {
+      $nid = $translations[$language->language]->nid;
+    }
+  }
+
+  // The webform node to display in the block.
+  $node = node_load($nid);
+
+  // Return if user has no access to the webform node.
+  if (!node_access('view', $node)) {
+    return;
+  }
+
+  // This is a webform node block.
+  $node->webform_block = TRUE;
+
+  // Use the node title for the block title.
+  $subject = $node->title;
+
+  // If not displaying pages in the block, set the #action property on the form.
+  if ($settings['pages_block']) {
+    $node->webform['action'] = FALSE;
+  }
+  else {
+    $query = array_diff_key($_GET, array('q' => ''));
+    $node->webform['action'] = url('node/' . $node->nid, array('query' => $query));
+  }
+
+  // Generate the content of the block based on display settings.
+  if ($settings['display'] == 'form') {
+    webform_node_view($node, 'full');
+    $content = $node->content['webform'];
+  }
+  else {
+    $teaser = ($settings['display'] == 'teaser') ? 'teaser' : 'full';
+    $content = node_view($node, $teaser);
+  }
+
+  // Add contextual links for the webform node if they aren't already there.
+  if (!isset($content['#contextual_links']['node'])) {
+    $content['#contextual_links']['node'] = array('node', array($node->nid));
+  }
+
+  // Create the block.
+  // Note that we render the content immediately here rather than passing back
+  // a renderable so that if the block is empty it is hidden.
+  $block = array(
+    'subject' => $subject,
+    'content' => drupal_render($content),
+  );
+  return $block;
+}
+
+/**
+ * Implements hook_block_configure().
+ */
+function webform_block_configure($delta = '') {
+  // Load the block-specific configuration settings.
+  $webform_blocks = variable_get('webform_blocks', array());
+  $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
+  $settings += array(
+    'display' => 'form',
+    'pages_block' => 0,
+  );
+
+  $form = array();
+  $form['display'] = array(
+    '#type' => 'radios',
+    '#title' => t('Display mode'),
+    '#default_value' => $settings['display'],
+    '#options' => array(
+      'form' => t('Form only'),
+      'full' => t('Full node'),
+      'teaser' => t('Teaser'),
+    ),
+    '#description' => t('The display mode determines how much of the webform to show within the block.'),
+  );
+
+  $form['pages_block'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show all webform pages in block'),
+    '#default_value' => $settings['pages_block'],
+    '#description' => t('By default multi-page webforms redirect to the node page for all pages after the first one. If checked, all pages will be shown in the block instead.'),
+  );
+
+  return $form;
+}
+
+/**
+ * Implements hook_block_save().
+ */
+function webform_block_save($delta = '', $edit = array()) {
+  // Load the previously defined block-specific configuration settings.
+  $settings = variable_get('webform_blocks', array());
+  // Build the settings array.
+  $new_settings[$delta] = array(
+    'display' => $edit['display'],
+    'pages_block' => $edit['pages_block'],
+  );
+  // We store settings for multiple blocks in just one variable
+  // so we merge the existing settings with the new ones before save.
+  variable_set('webform_blocks', array_merge($settings, $new_settings));
+}
+
+/**
+ * Client form generation function. If this is displaying an existing
+ * submission, pass in the $submission variable with the contents of the
+ * submission to be displayed.
+ *
+ * @param $form
+ *   The current form array (always empty).
+ * @param $form_state
+ *   The current form values of a submission, used in multipage webforms.
+ * @param $node
+ *   The current webform node.
+ * @param $submission
+ *   An object containing information about the form submission if we're
+ *   displaying a result.
+ * @param $is_draft
+ *   Optional. Set to TRUE if displaying a draft.
+ * @param $filter
+ *   Whether or not to filter the contents of descriptions and values when
+ *   building the form. Values need to be unfiltered to be editable by
+ *   Form Builder.
+ */
+function webform_client_form($form, &$form_state, $node, $submission, $is_draft = FALSE, $filter = TRUE) {
+  global $user;
+
+  // Attach necessary JavaScript and CSS.
+  $form['#attached'] = array(
+    'css' => array(drupal_get_path('module', 'webform') . '/css/webform.css'),
+    'js' => array(drupal_get_path('module', 'webform') . '/js/webform.js'),
+  );
+  form_load_include($form_state, 'inc', 'webform', 'includes/webform.components');
+  form_load_include($form_state, 'inc', 'webform', 'includes/webform.submissions');
+
+  $form['#process'] = array(
+    'webform_client_form_includes',
+  );
+
+  // If in a multi-step form, a submission ID may be specified in form state.
+  // Load this submission. This allows anonymous users to use auto-save.
+  if (empty($submission) && !empty($form_state['values']['details']['sid'])) {
+    $submission = webform_get_submission($node->nid, $form_state['values']['details']['sid']);
+    $is_draft = $submission->is_draft;
+  }
+
+  // Bind arguments to $form to make them available in theming and form_alter.
+  $form['#node'] = $node;
+  $form['#submission'] = $submission;
+  $form['#is_draft'] = $is_draft;
+  $form['#filter'] = $filter;
+
+  // Add a theme function for this form.
+  $form['#theme'] = array('webform_form_' . $node->nid, 'webform_form');
+
+  // Add a css class for all client forms.
+  $form['#attributes'] = array('class' => array('webform-client-form'));
+
+  // Set the encoding type (necessary for file uploads).
+  $form['#attributes']['enctype'] = 'multipart/form-data';
+
+  // Sometimes when displaying a webform as a teaser or block, a custom action
+  // property is set to direct the user to the node page.
+  if (!empty($node->webform['action'])) {
+    $form['#action'] = $node->webform['action'];
+  }
+
+  $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit');
+  $form['#validate'] = array('webform_client_form_validate');
+
+  if (is_array($node->webform['components']) && !empty($node->webform['components'])) {
+    // Prepare a new form array.
+    $form['submitted'] = array(
+      '#tree' => TRUE
+    );
+    $form['details'] = array(
+      '#tree' => TRUE,
+    );
+
+    // Put the components into a tree structure.
+    if (!isset($form_state['storage']['component_tree'])) {
+      $form_state['webform']['component_tree'] = array();
+      $form_state['webform']['page_count'] = 1;
+      $form_state['webform']['page_num'] = 1;
+      _webform_components_tree_build($node->webform['components'], $form_state['webform']['component_tree'], 0, $form_state['webform']['page_count']);
+    }
+    else {
+      $form_state['webform']['component_tree'] = $form_state['storage']['component_tree'];
+      $form_state['webform']['page_count'] = $form_state['storage']['page_count'];
+      $form_state['webform']['page_num'] = $form_state['storage']['page_num'];
+    }
+
+    // Shorten up our variable names.
+    $component_tree = $form_state['webform']['component_tree'];
+    $page_count = $form_state['webform']['page_count'];
+    $page_num = $form_state['webform']['page_num'];
+
+    if ($page_count > 1) {
+      $next_page_labels = array();
+      $prev_page_labels = array();
+    }
+
+    // Recursively add components to the form. The unfiltered version of the
+    // form (typically used in Form Builder), includes all components.
+    foreach ($component_tree['children'] as $cid => $component) {
+      $component_value = isset($form_state['values']['submitted'][$cid]) ? $form_state['values']['submitted'][$cid] : NULL;
+      if ($filter == FALSE || _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
+        if ($component['type'] == 'pagebreak') {
+          $next_page_labels[$component['page_num'] - 1] = !empty($component['extra']['next_page_label']) ? $component['extra']['next_page_label'] : t('Next Page >');
+          $prev_page_labels[$component['page_num']] = !empty($component['extra']['prev_page_label']) ? $component['extra']['prev_page_label'] : t('< Previous Page');
+        }
+        _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
+      }
+    }
+
+    // These form details help managing data upon submission.
+    $form['details']['nid'] = array(
+      '#type' => 'value',
+      '#value' => $node->nid,
+    );
+    $form['details']['sid'] = array(
+      '#type' => 'hidden',
+      '#value' => isset($submission->sid) ? $submission->sid : '',
+    );
+    $form['details']['uid'] = array(
+      '#type' => 'value',
+      '#value' => isset($submission->uid) ? $submission->uid : $user->uid,
+    );
+    $form['details']['page_num'] = array(
+      '#type'  => 'hidden',
+      '#value' => $page_num,
+    );
+    $form['details']['page_count'] = array(
+      '#type'  => 'hidden',
+      '#value' => $page_count,
+    );
+    $form['details']['finished'] = array(
+      '#type' => 'hidden',
+      '#value' => isset($submission->is_draft) ? (!$submission->is_draft) : 0,
+    );
+
+    // Add buttons for pages, drafts, and submissions.
+    $form['actions'] = array(
+      '#type' => 'actions',
+      '#weight' => 1000,
+    );
+
+    // Add the draft button.
+    if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
+      $form['actions']['draft'] = array(
+        '#type' => 'submit',
+        '#value' => t('Save Draft'),
+        '#weight' => -2,
+        '#validate' => array(),
+        '#attributes' => array('formnovalidate' => 'formnovalidate'),
+      );
+    }
+
+    if ($page_count > 1) {
+      // Add the submit button(s).
+      if ($page_num > 1) {
+        $form['actions']['previous'] = array(
+          '#type' => 'submit',
+          '#value' => $prev_page_labels[$page_num],
+          '#weight' => 5,
+          '#validate' => array(),
+          '#attributes' => array('formnovalidate' => 'formnovalidate'),
+        );
+      }
+      if ($page_num == $page_count) {
+        $form['actions']['submit'] = array(
+          '#type' => 'submit',
+          '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
+          '#weight' => 10,
+        );
+      }
+      elseif ($page_num < $page_count) {
+        $form['actions']['next'] = array(
+          '#type' => 'submit',
+          '#value' => $next_page_labels[$page_num],
+          '#weight' => 10,
+        );
+      }
+    }
+    else {
+      // Add the submit button.
+      $form['actions']['submit'] = array(
+        '#type' => 'submit',
+        '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
+        '#weight' => 10,
+      );
+    }
+  }
+
+  return $form;
+}
+
+/**
+ * Process function for webform_client_form().
+ *
+ * Include all the enabled components for this form to ensure availability.
+ */
+function webform_client_form_includes($form, $form_state) {
+  $components = webform_components();
+  foreach ($components as $component_type => $component) {
+    webform_component_include($component_type);
+  }
+  return $form;
+}
+
+/**
+ * Check if a component should be displayed on the current page.
+ */
+function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL) {
+  $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL;
+  $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL;
+  $conditional_cid = $conditional_component['cid'];
+
+  // Check the rules for this entire page. Note individual page breaks are
+  // checked down below in the individual component rule checks.
+  $show_page = TRUE;
+  if ($component['page_num'] > 1 && $component['type'] != 'pagebreak') {
+    foreach ($node->webform['components'] as $cid => $page_component) {
+      if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num) {
+        $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission);
+        break;
+      }
+    }
+  }
+
+  // Check any parents' visibility rules.
+  $show_parent = $show_page;
+  if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']])) {
+    $parent_component = $node->webform['components'][$component['pid']];
+    $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission);
+  }
+
+  // Check the individual component rules.
+  $show_component = $show_parent;
+  if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values))) {
+    $input_values = array();
+    if (isset($form_state)) {
+      $input_value = isset($form_state['values']['submitted'][$conditional_cid]) ? $form_state['values']['submitted'][$conditional_cid] : NULL;
+      $input_values = is_array($input_value) ? $input_value : array($input_value);
+    }
+    elseif (isset($submission)) {
+      $input_values = isset($submission->data[$conditional_cid]['value']) ? $submission->data[$conditional_cid]['value'] : array();
+    }
+
+    $test_values = array_map('trim', explode("\n", $conditional_values));
+    if (empty($input_values) && !empty($test_values)) {
+      $show_component = FALSE;
+    }
+    else {
+      foreach ($input_values as $input_value) {
+        if ($show_component = in_array($input_value, $test_values)) {
+          break;
+        }
+      }
+    }
+
+    if ($component['extra']['conditional_operator'] == '!=') {
+      $show_component = !$show_component;
+    }
+  }
+
+  // Private component?
+  if ($component['extra']['private']) {
+    $show_component = webform_results_access($node);
+  }
+
+  return $show_component;
+}
+
+/**
+ * Add a component to a renderable array. Called recursively for fieldsets.
+ *
+ * This function assists in the building of the client form, as well as the
+ * display of results, and the text of e-mails.
+ *
+ * @param $component
+ *   The component to be added to the form.
+ * @param $component_value
+ *   The components current value if known.
+ * @param $parent_fieldset
+ *   The fieldset to which this element will be added.
+ * @param $form
+ *   The entire form array.
+ * @param $form_state
+ *   The form state.
+ * @param $submission
+ *   The Webform submission as retrieved from the database.
+ * @param $format
+ *   The format the form should be displayed as. May be one of the following:
+ *   - form: Show as an editable form.
+ *   - html: Show as HTML results.
+ *   - text: Show as plain text.
+ * @param $filter
+ *   Whether the form element properties should be filtered. Only set to FALSE
+ *   if needing the raw properties for editing.
+ *
+ * @see webform_client_form()
+ * @see webform_submission_render()
+ */
+function _webform_client_form_add_component($node, $component, $component_value, &$parent_fieldset, &$form, $form_state, $submission, $format = 'form', $page_num = 0, $filter = TRUE) {
+  $cid = $component['cid'];
+
+  // Load with submission information if necessary.
+  if ($format != 'form') {
+    // This component is display only.
+    $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
+    if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) {
+      // Ensure the component is added as a property.
+      $display_element['#webform_component'] = $component;
+
+      // Allow modules to modify a "display only" webform component.
+      drupal_alter('webform_component_display', $display_element, $component);
+
+      // The form_builder() function usually adds #parents and #id for us, but
+      // because these are not marked for #input, we need to add them manually.
+      if (!isset($display_element['#parents'])) {
+        $parents = isset($parent_fieldset['#parents']) ? $parent_fieldset['#parents'] : array('submitted');
+        $parents[] = $component['form_key'];
+        $display_element['#parents'] = $parents;
+      }
+      if (!isset($display_element['#id'])) {
+        $display_element['#id'] = drupal_clean_css_identifier('edit-' . implode('-', $display_element['#parents']));
+      }
+
+      // Add the element into the proper parent in the display.
+      $parent_fieldset[$component['form_key']] = $display_element;
+    }
+  }
+  // Show the component only on its form page, or if building an unfiltered
+  // version of the form (such as for Form Builder).
+  elseif ($component['page_num'] == $page_num || $filter == FALSE) {
+    // Add this user-defined field to the form (with all the values that are always available).
+    $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
+    if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) {
+      // Ensure the component is added as a property.
+      $element['#webform_component'] = $component;
+
+      // The 'private' option is in most components, but it's not a real
+      // property. Add it for Form Builder compatibility.
+      if (webform_component_feature($component['type'], 'private')) {
+        $element['#webform_private'] = $component['extra']['private'];
+      }
+
+      // Allow modules to modify a webform component that is going to be render in a form.
+      drupal_alter('webform_component_render', $element, $component);
+
+      // Add the element into the proper parent in the form.
+      $parent_fieldset[$component['form_key']] = $element;
+
+      // Override the value if one already exists in the form state.
+      if (isset($component_value)) {
+        $parent_fieldset[$component['form_key']]['#default_value'] = $component_value;
+        if (is_array($component_value)) {
+          foreach ($component_value as $key => $value) {
+            if (isset($parent_fieldset[$component['form_key']][$key])) {
+              $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value;
+            }
+          }
+        }
+      }
+    }
+    else {
+      drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type'])));
+    }
+  }
+
+  // Disable validation initially on all elements. We manually validate
+  // all webform elements in webform_client_form_validate().
+  if (isset($parent_fieldset[$component['form_key']])) {
+    $parent_fieldset[$component['form_key']]['#validated'] = TRUE;
+    $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE;
+  }
+
+  if (isset($component['children']) && is_array($component['children'])) {
+    foreach ($component['children'] as $scid => $subcomponent) {
+      $subcomponent_value = isset($form_state['values']['submitted'][$scid]) ? $form_state['values']['submitted'][$scid] : NULL;
+      if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission)) {
+        _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
+      }
+    }
+  }
+}
+
+function webform_client_form_validate($form, &$form_state) {
+  $node = node_load($form_state['values']['details']['nid']);
+  $finished = $form_state['values']['details']['finished'];
+
+  // Check that the submissions have not exceeded the total submission limit.
+  if ($node->webform['total_submit_limit'] != -1) {
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+    // Check if the total number of entries was reached before the user submitted
+    // the form.
+    if (!$finished && $total_limit_exceeded = _webform_submission_total_limit_check($node)) {
+      // Show the user the limit has exceeded.
+      theme('webform_view_messages', array('node' => $node, 'teaser' => 0, 'page' => 1, 'submission_count' => 0, 'total_limit_exceeded' => $total_limit_exceeded, 'allowed_roles' => array_keys(user_roles()), 'closed' => FALSE, 'cached' => FALSE));
+      form_set_error('', NULL);
+      return;
+    }
+  }
+
+  // Check that the user has not exceeded the submission limit.
+  // This usually will only apply to anonymous users when the page cache is
+  // enabled, because they may submit the form even if they do not have access.
+  if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
+    module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+    if (!$finished && $user_limit_exceeded = _webform_submission_user_limit_check($node)) {
+      // Assume that webform_view_messages will print out the necessary message,
+      // then stop the processing of the form with an empty form error.
+      theme('webform_view_messages', array('node' => $node, 'teaser' => 0, 'page' => 1, 'submission_count' => 0, 'user_limit_exceeded' => $user_limit_exceeded, 'allowed_roles' => array_keys(user_roles()), 'closed' => FALSE, 'cached' => FALSE));
+      form_set_error('', NULL);
+      return;
+    }
+  }
+
+  // Run all #element_validate and #required checks. These are skipped initially
+  // by setting #validated = TRUE on all components when they are added.
+  _webform_client_form_validate($form, $form_state);
+}
+
+/**
+ * Recursive validation function to trigger normal Drupal validation.
+ *
+ * This function imitates _form_validate in Drupal's form.inc, only it sets
+ * a different property to ensure that validation has occurred.
+ */
+function _webform_client_form_validate($elements, &$form_state, $first_run = TRUE) {
+  static $form;
+  if ($first_run) {
+    $form = $elements;
+  }
+
+  // Recurse through all children.
+  foreach (element_children($elements) as $key) {
+    if (isset($elements[$key]) && $elements[$key]) {
+      _webform_client_form_validate($elements[$key], $form_state, FALSE);
+    }
+  }
+  // Validate the current input.
+  if (isset($elements['#webform_validated']) && $elements['#webform_validated'] == FALSE) {
+    if (isset($elements['#needs_validation'])) {
+      // Make sure a value is passed when the field is required.
+      // A simple call to empty() will not cut it here as some fields, like
+      // checkboxes, can return a valid value of '0'. Instead, check the
+      // length if it's a string, and the item count if it's an array. For
+      // radios, FALSE means that no value was submitted, so check that too.
+      if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === FALSE)) {
+        form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
+      }
+
+      // Verify that the value is not longer than #maxlength.
+      if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
+        form_error($elements, t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
+      }
+
+      if (isset($elements['#options']) && isset($elements['#value'])) {
+        if ($elements['#type'] == 'select') {
+          $options = form_options_flatten($elements['#options']);
+        }
+        else {
+          $options = $elements['#options'];
+        }
+        if (is_array($elements['#value'])) {
+          $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
+          foreach ($value as $v) {
+            if (!isset($options[$v])) {
+              form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
+              watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
+            }
+          }
+        }
+        elseif ($elements['#value'] !== '' && !isset($options[$elements['#value']])) {
+          form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
+          watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
+        }
+      }
+    }
+
+    // Call any element-specific validators. These must act on the element
+    // #value data.
+    if (isset($elements['#element_validate'])) {
+      foreach ($elements['#element_validate'] as $function) {
+        if (function_exists($function))  {
+          $function($elements, $form_state, $form);
+        }
+      }
+    }
+    $elements['#webform_validated'] = TRUE;
+  }
+}
+
+/**
+ * Handle the processing of pages and conditional logic.
+ */
+function webform_client_form_pages($form, &$form_state) {
+  $node = node_load($form_state['values']['details']['nid']);
+
+  // Multistep forms may not have any components on the first page.
+  if (!isset($form_state['values']['submitted'])) {
+    $form_state['values']['submitted'] = array();
+  }
+
+  // Move special settings to storage.
+  if (isset($form_state['webform']['component_tree'])) {
+    $form_state['storage']['component_tree'] = $form_state['webform']['component_tree'];
+    $form_state['storage']['page_count'] = $form_state['webform']['page_count'];
+    $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
+  }
+
+  // Perform post processing by components.
+  _webform_client_form_submit_process($node, $form_state['values']['submitted']);
+
+  // Flatten trees within the submission.
+  $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
+  $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
+
+  // Assume the form is completed unless the page logic says otherwise.
+  $form_state['webform_completed'] = TRUE;
+
+  // Check for a multi-page form that is not yet complete.
+  $submit_op = !empty($form['actions']['submit']['#value']) ? $form['actions']['submit']['#value'] : t('Submit');
+  $draft_op = !empty($form['actions']['draft']['#value']) ? $form['actions']['draft']['#value'] : t('Save Draft');
+  if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
+    // Store values from the current page in the form state storage.
+    if (is_array($form_state['values']['submitted'])) {
+      foreach ($form_state['values']['submitted'] as $key => $val) {
+        $form_state['storage']['submitted'][$key] = $val;
+      }
+    }
+
+    // Update form state values with those from storage.
+    if (isset($form_state['storage']['submitted'])) {
+      foreach ($form_state['storage']['submitted'] as $key => $val) {
+        $form_state['values']['submitted'][$key] = $val;
+      }
+    }
+
+    // Set the page number.
+    if (!isset($form_state['storage']['page_num'])) {
+      $form_state['storage']['page_num'] = 1;
+    }
+    if (end($form_state['clicked_button']['#parents']) == 'next') {
+      $direction = 1;
+    }
+    else {
+      $direction = 0;
+    }
+
+    // If the next page has no components that need to be displayed, skip it.
+    if (isset($direction)) {
+      $components = $direction ? $node->webform['components'] : array_reverse($node->webform['components'], TRUE);
+      $last_component = end($node->webform['components']);
+      foreach ($components as $component) {
+        if ($component['type'] == 'pagebreak' && (
+            $direction == 1 && $component['page_num'] > $form_state['storage']['page_num'] ||
+            $direction == 0 && $component['page_num'] <= $form_state['storage']['page_num'])) {
+          $previous_pagebreak = $component;
+          continue;
+        }
+        if (isset($previous_pagebreak)) {
+          $page_num = $previous_pagebreak['page_num'] + $direction - 1;
+          // If we've found an component on this page, advance to that page.
+          if ($component['page_num'] == $page_num && _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
+            $form_state['storage']['page_num'] = $page_num;
+            break;
+          }
+          // If we've gotten to the end of the form without finding any more
+          // components, set the page number more than the max, ending the form.
+          elseif ($direction && $component['cid'] == $last_component['cid']) {
+            $form_state['storage']['page_num'] = $page_num + 1;
+          }
+        }
+      }
+    }
+
+    // The form is done if the page number is greater than the page count.
+    $form_state['webform_completed'] = $form_state['storage']['page_num'] > $form_state['storage']['page_count'];
+  }
+
+  // Merge any stored submission data for multistep forms.
+  if (isset($form_state['storage']['submitted'])) {
+    $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
+    unset($form_state['values']['submitted']);
+
+    foreach ($form_state['storage']['submitted'] as $key => $val) {
+      $form_state['values']['submitted'][$key] = $val;
+    }
+    foreach ($original_values as $key => $val) {
+      $form_state['values']['submitted'][$key] = $val;
+    }
+
+    // Remove the variable so it doesn't show up in the additional processing.
+    unset($original_values);
+  }
+
+  // Inform the submit handlers that a draft will be saved.
+  $form_state['save_draft'] = $form_state['values']['op'] == $draft_op || ($node->webform['auto_save'] && !$form_state['webform_completed']);
+
+  // Determine what we need to do on the next page.
+  if (!empty($form_state['save_draft']) || !$form_state['webform_completed']) {
+    // Rebuild the form and display the current (on drafts) or next page.
+    $form_state['rebuild'] = TRUE;
+  }
+  else {
+    // Remove the form state storage now that we're done with the pages.
+    $form_state['rebuild'] = FALSE;
+    unset($form_state['storage']);
+  }
+}
+
+/**
+ * Submit handler for saving the form values and sending e-mails.
+ */
+function webform_client_form_submit($form, &$form_state) {
+  module_load_include('inc', 'webform', 'includes/webform.submissions');
+  module_load_include('inc', 'webform', 'includes/webform.components');
+  global $user;
+
+  if (empty($form_state['save_draft']) && empty($form_state['webform_completed'])) {
+    return;
+  }
+
+  $node = $form['#node'];
+  $sid = $form_state['values']['details']['sid'] ? (int) $form_state['values']['details']['sid'] : NULL;
+
+  // Check if user is submitting as a draft.
+  $is_draft = (int) !empty($form_state['save_draft']);
+
+  if (!$sid) {
+    // Create a new submission object.
+    $submission = (object) array(
+      'nid' => $node->nid,
+      'uid' => $form_state['values']['details']['uid'],
+      'submitted' => REQUEST_TIME,
+      'remote_addr' => ip_address(),
+      'is_draft' => $is_draft,
+      'data' => webform_submission_data($node, $form_state['values']['submitted']),
+    );
+  }
+  else {
+    // To maintain time and user information, load the existing submission.
+    $submission = webform_get_submission($node->webform['nid'], $sid);
+    $submission->is_draft = $is_draft;
+
+    // Merge with new submission data. The + operator maintains numeric keys.
+    // This maintains existing data with just-submitted data when a user resumes
+    // a submission previously saved as a draft.
+    $new_data = webform_submission_data($node, $form_state['values']['submitted']);
+    $submission->data = $new_data + $submission->data;
+  }
+
+  // If there is no data to be saved (such as on a multipage form with no fields
+  // on the first page), process no further. Submissions with no data cannot
+  // be loaded from the database as efficiently, so we don't save them at all.
+  if (empty($submission->data)) {
+    return;
+  }
+
+  // Save the submission to the database.
+  if (!$sid) {
+    // No sid was found thus insert it in the dataabase.
+    $form_state['values']['details']['sid'] = $sid = webform_submission_insert($node, $submission);
+    $form_state['values']['details']['is_new'] = TRUE;
+
+    // Set a cookie including the server's submission time.
+    // The cookie expires in the length of the interval plus a day to compensate for different timezones.
+    if (variable_get('webform_use_cookies', 0)) {
+      $cookie_name = 'webform-' . $node->nid;
+      $time = REQUEST_TIME;
+      $params = session_get_cookie_params();
+      setcookie($cookie_name . '[' . $time . ']', $time, $time + $node->webform['submit_interval'] + 86400, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
+    }
+
+    // Save session information about this submission for anonymous users,
+    // allowing them to access or edit their submissions.
+    if (!$user->uid && user_access('access own webform submissions')) {
+      $_SESSION['webform_submission'][$form_state['values']['details']['sid']] = $node->nid;
+    }
+  }
+  else {
+    // Sid was found thus update the existing sid in the database.
+    webform_submission_update($node, $submission);
+    $form_state['values']['details']['is_new'] = FALSE;
+  }
+
+  // Check if this form is sending an email.
+  if (!$is_draft && !$form_state['values']['details']['finished']) {
+    $submission = webform_get_submission($node->webform['nid'], $sid, TRUE);
+    webform_submission_send_mail($node, $submission);
+  }
+
+  // Strip out empty tags added by WYSIWYG editors if needed.
+  $confirmation = strlen(trim(strip_tags($node->webform['confirmation']))) ? $node->webform['confirmation'] : '';
+
+  // Clean up the redirect URL and filter it for webform tokens.
+  $redirect_url = trim($node->webform['redirect_url']);
+  $redirect_url = _webform_filter_values($redirect_url, $node, $submission, NULL, FALSE, TRUE);
+
+
+  // Remove the domain name from the redirect.
+  $redirect_url = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $redirect_url);
+
+  // Check confirmation and redirect_url fields.
+  $message = NULL;
+  $redirect = NULL;
+  $external_url = FALSE;
+  if (isset($form['actions']['draft']['#value']) && $form_state['values']['op'] == $form['actions']['draft']['#value']) {
+    $message = t('Submission saved. You may return to this form later and it will restore the current values.');
+  }
+  elseif ($is_draft) {
+    $redirect = NULL;
+  }
+  elseif (!empty($form_state['values']['details']['finished'])) {
+    $message = t('Submission updated.');
+  }
+  elseif ($redirect_url == '<none>') {
+    $redirect = NULL;
+  }
+  elseif ($redirect_url == '<confirmation>') {
+    $redirect = array('node/' . $node->nid . '/done', array('query' => array('sid' => $sid)));
+  }
+  elseif ($redirect_url && strpos($redirect_url, 'http') !== 0) {
+    $parts = drupal_parse_url($redirect_url);
+    $parts['query'] ? ($parts['query']['sid'] = $sid) : ($parts['query'] = array('sid' => $sid));
+    $query = $parts['query'];
+    $redirect = array($parts['path'], array('query' => $query, 'fragment' => $parts['fragment']));
+  }
+  elseif ($redirect_url) {
+      $aOldUrl = parse_url($redirect_url);
+      //Assemble url components
+      $sBaseUrl = (!empty($aOldUrl['scheme'])) ? $aOldUrl['scheme'] . '://' : '';
+      $sBaseUrl .= (!empty($aOldUrl['host'])) ? $aOldUrl['host'] : '';
+      $sBaseUrl .= (!empty($aOldUrl['path'])) ? urldecode($aOldUrl['path']) : '';
+      $sFragment = (!empty($aOldUrl['fragment'])) ? $aOldUrl['fragment'] : '';
+      $aQuery = array();
+      if (!empty($aOldUrl['query'])) {
+           $aOldUrl['query'] = urldecode ($aOldUrl['query']);
+           parse_str($aOldUrl['query'], $aQuery);
+      }
+
+      $aNewUrl = array(
+          $sBaseUrl,
+          array(
+            'query' => $aQuery,
+            'fragment' => $sFragment,
+              ),
+      );
+    if(valid_url($sBaseUrl, TRUE)) {
+      $redirect = $aNewUrl;
+    }
+  }
+
+  // Show a message if manually set.
+  if (isset($message)) {
+    drupal_set_message($message);
+  }
+  // If redirecting and we have a confirmation message, show it as a message.
+  elseif (!$is_draft && !$external_url && (!empty($redirect_url) && $redirect_url != '<confirmation>') && !empty($confirmation)) {
+    drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], '', TRUE));
+  }
+
+  $form_state['redirect'] = $redirect;
+}
+
+
+/**
+ * Post processes the submission tree with any updates from components.
+ *
+ * @param $node
+ *   The full webform node.
+ * @param $form_values
+ *   The form values for the form.
+ * @param $types
+ *   Optional. Specific types to perform processing.
+ * @param $parent
+ *   Internal use. The current parent CID whose children are being processed.
+ */
+function _webform_client_form_submit_process($node, &$form_values, $types = NULL, $parent = 0) {
+  if (is_array($form_values)) {
+    foreach ($form_values as $form_key => $value) {
+      $cid = webform_get_cid($node, $form_key, $parent);
+      if (is_array($value) && isset($node->webform['components'][$cid]['type']) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
+        _webform_client_form_submit_process($node, $form_values[$form_key], $types, $cid);
+      }
+
+      if (isset($node->webform['components'][$cid])) {
+        // Call the component process submission function.
+        $component = $node->webform['components'][$cid];
+        if ((!isset($types) || in_array($component['type'], $types)) && webform_component_implements($component['type'], 'submit')) {
+          $form_values[$component['form_key']] = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Flattens a submitted form back into a single array representation (rather than nested fields)
+ */
+function _webform_client_form_submit_flatten($node, $fieldset, $parent = 0) {
+  $values = array();
+
+  if (is_array($fieldset)) {
+    foreach ($fieldset as $form_key => $value) {
+      $cid = webform_get_cid($node, $form_key, $parent);
+
+      if (is_array($value) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
+        $values += _webform_client_form_submit_flatten($node, $value, $cid);
+      }
+      else {
+        $values[$cid] = $value;
+      }
+    }
+  }
+
+  return $values;
+}
+
+/**
+ * Prints the confirmation message after a successful submission.
+ */
+function _webform_confirmation($node) {
+  drupal_set_title($node->title);
+  webform_set_breadcrumb($node);
+  $sid = isset($_GET['sid']) ? $_GET['sid'] : NULL;
+  return theme(array('webform_confirmation_' . $node->nid, 'webform_confirmation'), array('node' => $node, 'sid' => $sid));
+}
+
+/**
+ * Prepare for theming of the webform form.
+ */
+function template_preprocess_webform_form(&$vars) {
+  if (isset($vars['form']['details']['nid']['#value'])) {
+    $vars['nid'] = $vars['form']['details']['nid']['#value'];
+  }
+  elseif (isset($vars['form']['submission']['#value'])) {
+    $vars['nid'] = $vars['form']['submission']['#value']->nid;
+  }
+}
+
+/**
+ * Prepare for theming of the webform submission confirmation.
+ */
+function template_preprocess_webform_confirmation(&$vars) {
+  $confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], '', TRUE);
+  // Strip out empty tags added by WYSIWYG editors if needed.
+  $vars['confirmation_message'] = strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
+}
+
+/**
+ * Prepare to theme the contents of e-mails sent by webform.
+ */
+function template_preprocess_webform_mail_message(&$vars) {
+  global $user;
+
+  $vars['user'] = $user;
+  $vars['ip_address'] = ip_address();
+}
+
+/**
+ * A Form API #pre_render function. Sets display based on #title_display.
+ *
+ * This function is used regularly in D6 for all elements, but specifically for
+ * fieldsets in D7, which don't support #title_display natively.
+ */
+function webform_element_title_display($element) {
+  if (isset($element['#title_display']) && strcmp($element['#title_display'], 'none') === 0) {
+    $element['#title'] = NULL;
+  }
+  return $element;
+}
+
+/**
+ * Replacement for theme_form_element().
+ */
+function theme_webform_element($variables) {
+  // Ensure defaults.
+  $variables['element'] += array(
+    '#title_display' => 'before',
+  );
+
+  $element = $variables['element'];
+
+  // All elements using this for display only are given the "display" type.
+  if (isset($element['#format']) && $element['#format'] == 'html') {
+    $type = 'display';
+  }
+  else {
+    $type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield', 'webform_email', 'webform_number'))) ? $element['#type'] : $element['#webform_component']['type'];
+  }
+
+  // Convert the parents array into a string, excluding the "submitted" wrapper.
+  $nested_level = $element['#parents'][0] == 'submitted' ? 1 : 0;
+  $parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], $nested_level)));
+
+  $wrapper_classes = array(
+   'form-item',
+   'webform-component',
+   'webform-component-' . $type,
+  );
+  if (isset($element['#title_display']) && strcmp($element['#title_display'], 'inline') === 0) {
+    $wrapper_classes[] = 'webform-container-inline';
+  }
+  $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="webform-component-' . $parents . '">' . "\n";
+
+  // If #title is not set, we don't display any label or required marker.
+  if (!isset($element['#title'])) {
+    $element['#title_display'] = 'none';
+  }
+  $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . _webform_filter_xss($element['#field_prefix']) . '</span> ' : '';
+  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . _webform_filter_xss($element['#field_suffix']) . '</span>' : '';
+
+  switch ($element['#title_display']) {
+    case 'inline':
+    case 'before':
+    case 'invisible':
+      $output .= ' ' . theme('form_element_label', $variables);
+      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
+      break;
+
+    case 'after':
+      $output .= ' ' . $prefix . $element['#children'] . $suffix;
+      $output .= ' ' . theme('form_element_label', $variables) . "\n";
+      break;
+
+    case 'none':
+    case 'attribute':
+      // Output no label and no required marker, only the children.
+      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
+      break;
+  }
+
+  if (!empty($element['#description'])) {
+    $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
+  }
+
+  $output .= "</div>\n";
+
+  return $output;
+}
+
+/**
+ * Output a form element in plain text format.
+ */
+function theme_webform_element_text($variables) {
+  $element = $variables['element'];
+  $value = $variables['element']['#children'];
+
+  $output = '';
+  $is_group = webform_component_feature($element['#webform_component']['type'], 'group');
+
+  // Output the element title.
+  if (isset($element['#title'])) {
+    if ($is_group) {
+      $output .= '--' . $element['#title'] . '--';
+    }
+    elseif (!in_array(drupal_substr($element['#title'], -1), array('?', ':', '!', '%', ';', '@'))) {
+      $output .= $element['#title'] . ':';
+    }
+    else {
+      $output .= $element['#title'];
+    }
+  }
+
+  // Wrap long values at 65 characters, allowing for a few fieldset indents.
+  // It's common courtesy to wrap at 75 characters in e-mails.
+  if ($is_group && drupal_strlen($value) > 65) {
+    $value = wordwrap($value, 65, "\n");
+    $lines = explode("\n", $value);
+    foreach ($lines as $key => $line) {
+      $lines[$key] = '  ' . $line;
+    }
+    $value = implode("\n", $lines);
+  }
+
+  // Add the value to the output.
+  if ($value) {
+    $output .= (strpos($value, "\n") === FALSE ? ' ' : "\n") . $value;
+  }
+
+  // Indent fieldsets.
+  if ($is_group) {
+    $lines = explode("\n", $output);
+    foreach ($lines as $number => $line) {
+      if (strlen($line)) {
+        $lines[$number] = '  ' . $line;
+      }
+    }
+    $output = implode("\n", $lines);
+    $output .= "\n";
+  }
+
+  if ($output) {
+    $output .= "\n";
+  }
+
+  return $output;
+}
+
+/**
+ * Theme a radio button and another element together.
+ *
+ * This is used in the e-mail configuration to show a radio button and a text
+ * field or select list on the same line.
+ */
+function theme_webform_inline_radio($variables) {
+  $element = $variables['element'];
+
+  // Add element's #type and #name as class to aid with JS/CSS selectors.
+  $class = array('form-item');
+  if (!empty($element['#type'])) {
+    $class[] = 'form-type-' . strtr($element['#type'], '_', '-');
+  }
+  if (!empty($element['#name'])) {
+    $class[] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
+  }
+
+  // Add container-inline to all elements.
+  $class[] = 'webform-container-inline';
+  if (isset($element['#inline_element']) && isset($variables['element']['#title'])) {
+    $variables['element']['#title'] .= ': ';
+  }
+
+  $output = '<div class="' . implode(' ', $class) . '">' . "\n";
+  $output .= ' ' . $element['#children'];
+  if (!empty($element['#title'])) {
+    $output .= ' ' . theme('form_element_label', $variables) . "\n";
+  }
+  if (isset($element['#inline_element'])) {
+    $output .= ' ' . $element['#inline_element'] . "\n";
+  }
+
+  if (!empty($element['#description'])) {
+    $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
+  }
+
+  $output .= "</div>\n";
+
+  return $output;
+}
+
+/**
+ * Theme the headers when sending an email from webform.
+ *
+ * @param $node
+ *   The complete node object for the webform.
+ * @param $submission
+ *   The webform submission of the user.
+ * @param $email
+ *   If you desire to make different e-mail headers depending on the recipient,
+ *   you can check the $email['email'] property to output different content.
+ *   This will be the ID of the component that is a conditional e-mail
+ *   recipient. For the normal e-mails, it will have the value of 'default'.
+ * @return
+ *   An array of headers to be used when sending a webform email. If headers
+ *   for "From", "To", or "Subject" are set, they will take precedence over
+ *   the values set in the webform configuration.
+ */
+function theme_webform_mail_headers($variables) {
+  $headers = array(
+    'X-Mailer' => 'Drupal Webform (PHP/' . phpversion() . ')',
+  );
+  return $headers;
+}
+
+/**
+ * Check if current user has a draft of this webform, and return the sid.
+ */
+function _webform_fetch_draft_sid($nid, $uid) {
+  return db_select('webform_submissions')
+    ->fields('webform_submissions', array('sid'))
+    ->condition('nid', $nid)
+    ->condition('uid', $uid)
+    ->condition('is_draft', 1)
+    ->orderBy('submitted', 'DESC')
+    ->execute()
+    ->fetchField();
+}
+
+/**
+ * Filters all special tokens provided by webform, such as %post and %profile.
+ *
+ * @param $string
+ *   The string to have its tokens replaced.
+ * @param $node
+ *   If replacing node-level tokens, the node for which tokens will be created.
+ * @param $submission
+ *   If replacing submission-level tokens, the submission for which tokens will
+ *   be created.
+ * @param $email
+ *   If replacing tokens within the context of an e-mail, the Webform e-mail
+ *   settings array.
+ * @param $strict
+ *   Boolean value indicating if the results should be run through check_plain.
+ *   This is used any time the values will be output as HTML, but not in
+ *   default values or e-mails.
+ * @param $allow_anonymous
+ *   Boolean value indicating if all tokens should be replaced for anonymous
+ *   users, even if they contain sensitive user information such as %session or
+ *   %ip_address. This is disabled by default to prevent user data from being
+ *   preserved in the anonymous page cache and should only be used in
+ *   non-cached situations, such as e-mails.
+ */
+function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE) {
+  global $user;
+  static $replacements;
+
+  // Don't do any filtering if the string is empty.
+  if (strlen(trim($string)) == 0) {
+    return $string;
+  }
+
+  // Setup default token replacements.
+  if (!isset($replacements)) {
+    $replacements['unsafe'] = array();
+    $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
+    $replacements['safe']['%date'] = format_date(REQUEST_TIME, 'long');
+  }
+
+  // Node replacements.
+  if (isset($node) && !array_key_exists('%nid', $replacements['safe'])) {
+    $replacements['safe']['%nid'] = $node->nid;
+    $replacements['safe']['%title'] = $node->title;
+  }
+
+  // Determine the display format.
+  $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
+
+  // Submission replacements.
+  if (isset($submission) && !isset($replacements['email'][$format])) {
+    module_load_include('inc', 'webform', 'includes/webform.components');
+
+    // Set the submission ID.
+    $replacements['unsafe']['%sid'] = $submission->sid;
+
+    // E-mails may be sent in two formats, keep tokens separate for each one.
+    $replacements['email'][$format] = array();
+
+    // Populate token values for each component.
+    foreach ($submission->data as $cid => $value) {
+      $component = $node->webform['components'][$cid];
+
+      // Find by form key.
+      $parents = webform_component_parent_keys($node, $component);
+      $form_key = implode('][', $parents);
+      $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
+
+      // Ensure the component is added as a property.
+      $display_element['#webform_component'] = $component;
+
+      if (empty($display_element['#parents'])) {
+        $display_element['#parents'] = array_merge(array('submitted'), $parents);
+      }
+      if (empty($display_element['#id'])) {
+        $display_element['#id'] = drupal_html_id('edit-' . implode('-', $display_element['#parents']));
+      }
+      $replacements['email'][$format]['%email[' . $form_key . ']'] = render($display_element);
+      $display_element['#theme_wrappers'] = array(); // Remove label and wrappers.
+      $replacements['email'][$format]['%value[' . $form_key . ']'] = render($display_element);
+    }
+
+    // Provide blanks for components in the webform but not in the submission.
+    $missing_components = array_diff_key($node->webform['components'], $submission->data);
+    foreach ($missing_components as $component) {
+      $parents = webform_component_parent_keys($node, $component);
+      $form_key = implode('][', $parents);
+      $replacements['email'][$format]['%email[' . $form_key . ']'] = '';
+      $replacements['email'][$format]['%value[' . $form_key . ']'] = '';
+    }
+
+    // Submission edit URL.
+    $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
+  }
+
+  // Token for the entire form tree for e-mails.
+  if (isset($submission) && isset($email)) {
+    $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
+  }
+
+  // Provide a list of candidates for token replacement.
+  $special_tokens = array(
+    'safe' => array(
+      '%get' => $_GET,
+      '%post' => $_POST,
+    ),
+    'unsafe' => array(
+      '%cookie' => $_COOKIE,
+      '%session' => isset($_SESSION) ? $_SESSION : array(),
+      '%request' => $_REQUEST,
+      '%server' => $_SERVER,
+      '%profile' => (array) $user,
+    ),
+  );
+
+  // Replacements of global variable tokens.
+  if (!isset($replacements['specials_set'])) {
+    $replacements['specials_set'] = TRUE;
+
+    // Load profile information if available.
+    if ($user->uid) {
+      $account = user_load($user->uid);
+      $special_tokens['unsafe']['%profile'] = (array) $account;
+    }
+
+    // User replacements.
+    if (!array_key_exists('%uid', $replacements['unsafe'])) {
+      $replacements['unsafe']['%uid'] = !empty($user->uid) ? $user->uid : '';
+      $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
+      $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
+      $replacements['unsafe']['%ip_address'] = ip_address();
+    }
+
+    // Populate the replacements array with special variables.
+    foreach ($special_tokens as $safe_state => $tokens) {
+      foreach ($tokens as $token => $variable) {
+        // Safety check in case $_POST or some other global has been removed
+        // by a naughty module, in which case $variable may be NULL.
+        if (!is_array($variable)) {
+          continue;
+        }
+
+        foreach ($variable as $key => $value) {
+          // This special case for profile module dates.
+          if ($token == '%profile' && is_array($value) && isset($value['year'])) {
+            $replacement = webform_strtodate(webform_date_format(), $value['month'] . '/' . $value['day'] . '/' . $value['year'], 'UTC');
+          }
+          else {
+            // Checking for complex types (arrays and objects) fails here with
+            // incomplete objects (see http://php.net/is_object), so we check
+            // for simple types instead.
+            $replacement = (is_string($value) || is_bool($value) || is_numeric($value)) ? $value : '';
+          }
+          $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
+        }
+      }
+    }
+  }
+
+  // Make a copy of the replacements so we don't affect the static version.
+  $safe_replacements = $replacements['safe'];
+
+  // Restrict replacements for anonymous users. Not all tokens can be used
+  // because they may expose session or other private data to other users when
+  // anonymous page caching is enabled.
+  if ($user->uid || $allow_anonymous) {
+    $safe_replacements += $replacements['unsafe'];
+    if (isset($replacements['email'][$format])) {
+      $safe_replacements += $replacements['email'][$format];
+    }
+  }
+  else {
+    foreach ($replacements['unsafe'] as $key => $value) {
+      $safe_replacements[$key] = '';
+    }
+  }
+  $find = array_keys($safe_replacements);
+  $replace = array_values($safe_replacements);
+  $string = str_replace($find, $replace, $string);
+
+  // Clean up any unused tokens.
+  foreach ($special_tokens as $safe_state => $tokens) {
+    foreach (array_keys($tokens) as $token) {
+      $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
+    }
+  }
+
+  return $strict ? _webform_filter_xss($string) : $string;
+}
+
+/**
+ * Filters all special tokens provided by webform, and allows basic layout in descriptions.
+ */
+function _webform_filter_descriptions($string, $node = NULL, $submission = NULL) {
+  return strlen($string) == 0 ? '' : _webform_filter_xss(_webform_filter_values($string, $node, $submission, NULL, FALSE));
+}
+
+/**
+ * Filter labels for display by running through XSS checks.
+ */
+function _webform_filter_xss($string) {
+  static $allowed_tags;
+  $allowed_tags = isset($allowed_tags) ? $allowed_tags : webform_variable_get('webform_allowed_tags');
+  return filter_xss($string, $allowed_tags);
+}
+
+
+/**
+ * Utility function to ensure that a webform record exists in the database.
+ *
+ * @param $node
+ *   The node object to check if a database entry exists.
+ * @return
+ *   This function should always return TRUE if no errors were encountered,
+ *   ensuring that a webform table row has been created. Will return FALSE if
+ *   a record does not exist and a new one could not be created.
+ */
+function webform_ensure_record(&$node) {
+  if (!$node->webform['record_exists']) {
+    // Even though webform_node_insert() would set this property to TRUE,
+    // we set record_exists to trigger a difference from the defaults.
+    $node->webform['record_exists'] = TRUE;
+    webform_node_insert($node);
+  }
+  return $node->webform['record_exists'];
+}
+
+/**
+ * Utility function to check if a webform record is necessary in the database.
+ *
+ * If the node is no longer using any webform settings, this function will
+ * delete the settings from the webform table. Note that this function will NOT
+ * delete rows from the webform table if the node-type is exclusively used for
+ * webforms (per the "webform_node_types_primary" variable).
+ *
+ * @param $node
+ *   The node object to check if a database entry is still required.
+ * @return
+ *   Returns TRUE if the webform still has a record in the database. Returns
+ *   FALSE if the webform does not have a record or if the previously existing
+ *   record was just deleted.
+ */
+function webform_check_record(&$node) {
+  $webform = $node->webform;
+  $webform['record_exists'] = FALSE;
+  unset($webform['nid']);
+
+  // Don't include empty values in the comparison, this makes it so modules that
+  // extend Webform with empty defaults won't affect cleanup of rows.
+  $webform = array_filter($webform);
+  $defaults = array_filter(webform_node_defaults());
+  if ($webform == $defaults && !in_array($node->type, webform_variable_get('webform_node_types_primary'))) {
+    webform_node_delete($node);
+    $node->webform = webform_node_defaults();
+  }
+  return $node->webform['record_exists'];
+}
+
+/**
+ * Given a form_key and a list of form_key parents, determine the cid.
+ *
+ * @param $node
+ *   A fully loaded node object.
+ * @param $form_key
+ *   The form key for which we're finding a cid.
+ * @param $parent
+ *   The cid of the parent component.
+ */
+function webform_get_cid(&$node, $form_key, $pid) {
+  foreach ($node->webform['components'] as $cid => $component) {
+    if ($component['form_key'] == $form_key && $component['pid'] == $pid) {
+      return $cid;
+    }
+  }
+}
+
+/**
+ * Retreive a Drupal variable with the appropriate default value.
+ */
+function webform_variable_get($variable) {
+  switch ($variable) {
+    case 'webform_allowed_tags':
+      $result = variable_get('webform_allowed_tags', array('a', 'em', 'strong', 'code', 'img'));
+      break;
+    case 'webform_default_from_name':
+      $result = variable_get('webform_default_from_name', variable_get('site_name', ''));
+      break;
+    case 'webform_default_from_address':
+      $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
+      break;
+    case 'webform_default_subject':
+      $result = variable_get('webform_default_subject', t('Form submission from: %title'));
+      break;
+    case 'webform_node_types':
+      $result = variable_get('webform_node_types', array('webform'));
+      break;
+    case 'webform_node_types_primary':
+      $result = variable_get('webform_node_types_primary', array('webform'));
+      break;
+  }
+  return $result;
+}
+
+function theme_webform_token_help($variables) {
+  $groups = $variables['groups'];
+  $groups = empty($groups) ? array('basic', 'node', 'special') : $groups;
+
+  static $tokens = array();
+
+  if (empty($tokens)) {
+    $tokens['basic'] = array(
+      'title' => t('Basic tokens'),
+      'tokens' => array(
+        '%username' => t('The name of the user if logged in. Blank for anonymous users.'),
+        '%useremail' => t('The e-mail address of the user if logged in. Blank for anonymous users.'),
+        '%ip_address' => t('The IP address of the user.'),
+        '%site' => t('The name of the site (i.e. %site_name)', array('%site_name' => variable_get('site_name', ''))),
+        '%date' => t('The current date, formatted according to the site settings.'),
+      ),
+    );
+
+    $tokens['node'] = array(
+      'title' => t('Node tokens'),
+      'tokens' => array(
+        '%nid' => t('The node ID.'),
+        '%title' => t('The node title.'),
+      ),
+    );
+
+    $tokens['special'] = array(
+      'title' => t('Special tokens'),
+      'tokens' => array(
+        '%profile[' . t('key') . ']' => t('Any user profile field or value, such as %profile[name] or %profile[profile_first_name]'),
+        '%get[' . t('key') . ']' => t('Tokens may be populated from the URL by creating URLs of the form http://example.com/my-form?foo=bar. Using the token %get[foo] would print "bar".'),
+        '%post[' . t('key') . ']' => t('Tokens may also be populated from POST values that are submitted by forms.'),
+      ),
+      'description' => t('In addition to %get and %post, the following super tokens may be used, though only with logged-in users: %server, %cookie, and %request. For example %server[HTTP_USER_AGENT] or %session[id].'),
+    );
+
+    $tokens['email'] = array(
+      'title' => t('E-mail tokens'),
+      'tokens' => array(
+        '%email_values' => t('All included components in a hierarchical structure.'),
+        '%email[' . t('key') . '] ' => t('A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.'),
+        '%submission_url' => t('The URL for viewing the completed submission.'),
+      ),
+    );
+
+    $tokens['submission'] = array(
+      'title' => t('Submission tokens'),
+      'tokens' => array(
+        '%sid' => t('The unique submission ID.'),
+        '%value[key]' => t('A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.'),
+      ),
+    );
+  }
+
+  $output = '';
+  $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
+
+  foreach ($tokens as $group_name => $group) {
+    if (!is_array($groups) || in_array($group_name, $groups)) {
+      $items = array();
+      foreach ($group['tokens'] as $token => $token_description) {
+        $items[] = $token . ' - ' . $token_description;
+      }
+      $output .= theme('item_list', array('items' => $items, 'title' => $group['title']));
+      $output .= isset($group['description']) ? '<p>' . $group['description']  . '</p>' : '';
+    }
+  }
+
+  $fieldset = array(
+    '#title' => t('Token values'),
+    '#type' => 'fieldset',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#children' => '<div>' . $output . '</div>',
+    '#attributes' => array('class' => array('collapsible', 'collapsed')),
+  );
+  return theme('fieldset', array('element' => $fieldset));
+}
+
+function _webform_safe_name($name) {
+  $new = trim($name);
+
+  // If transliteration is available, use it to convert names to ASCII.
+  if (function_exists('transliteration_get')) {
+    $new = transliteration_get($new, '');
+    $new = str_replace(array(' ', '-', '/'), array('_', '_', '_'), $new);
+  }
+  else {
+    $new = str_replace(
+      array(' ', '-', '/', '€', 'ƒ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', '¢', '¥', 'µ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Œ',  'œ',  'Æ',  'Ð',  'Þ',  'ß',  'æ',  'ð',  'þ'),
+      array('_', '_', '_', 'E', 'f', 'S', 'Z', 's', 'z', 'Y', 'c', 'Y', 'u', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'),
+      $new);
+  }
+
+  $new = drupal_strtolower($new);
+  $new = preg_replace('/[^a-z0-9_]/', '', $new);
+  return $new;
+}
+
+/**
+ * Given an email address and a name, format an e-mail address.
+ *
+ * @param $address
+ *   The e-mail address.
+ * @param $name
+ *   The name to be used in the formatted address.
+ * @param $node
+ *   The webform node if replacements will be done.
+ * @param $submission
+ *   The webform submission values if replacements will be done.
+ * @param $encode
+ *   Encode the text for use in an e-mail.
+ * @param $single
+ *   Force a single value to be returned, even if a component expands to
+ *   multiple addresses. This is useful to ensure a single e-mail will be
+ *   returned for the "From" address.
+ * @param $format
+ *   The e-mail format, defaults to the site-wide setting. May be either "short"
+ *   or "long".
+ */
+function webform_format_email_address($address, $name, $node = NULL, $submission = NULL, $encode = TRUE, $single = TRUE, $format = NULL) {
+  if (!isset($format)) {
+    $format = variable_get('webform_email_address_format', 'long');
+  }
+
+  if ($name == 'default') {
+    $name = webform_variable_get('webform_default_from_name');
+  }
+  elseif (is_numeric($name) && isset($node->webform['components'][$name])) {
+    if (isset($submission->data[$name]['value'])) {
+      $name = $submission->data[$name]['value'];
+    }
+    else {
+      $name = t('Value of !component', array('!component' => $node->webform['components'][$name]['name']));
+    }
+  }
+
+  if ($address == 'default') {
+    $address = webform_variable_get('webform_default_from_address');
+  }
+  elseif (is_numeric($address) && isset($node->webform['components'][$address])) {
+    if (isset($submission->data[$address]['value'])) {
+      $values = $submission->data[$address]['value'];;
+      $address = array();
+      foreach ($values as $value) {
+        $address = array_merge($address, explode(',', $value));
+      }
+    }
+    else {
+      $address = t('Value of "!component"', array('!component' => $node->webform['components'][$address]['name']));
+    }
+  }
+
+  // Convert arrays into a single value for From values.
+  if ($single) {
+    $address = is_array($address) ? reset($address) : $address;
+    $name = is_array($name) ? reset($name) : $name;
+  }
+
+  // Address may be an array if a component value was used on checkboxes.
+  if (is_array($address)) {
+    foreach ($address as $key => $individual_address) {
+      $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
+    }
+  }
+  else {
+    $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
+  }
+
+  if ($format == 'long' && !empty($name)) {
+    $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
+    if ($encode) {
+      $name = mime_header_encode($name);
+    }
+    return '"' . $name . '" <' . $address . '>';
+  }
+  else {
+    return $address;
+  }
+}
+
+/**
+ * Given an email subject, format it with any needed replacements.
+ */
+function webform_format_email_subject($subject, $node = NULL, $submission = NULL) {
+  if ($subject == 'default') {
+    $subject = webform_variable_get('webform_default_subject');
+  }
+  elseif (is_numeric($subject) && isset($node->webform['components'][$subject])) {
+    $component = $node->webform['components'][$subject];
+    if (isset($submission->data[$subject]['value'])) {
+      $display_function = '_webform_display_' . $component['type'];
+      $value = $submission->data[$subject]['value'];
+
+      // Convert the value to a clean text representation if possible.
+      if (function_exists($display_function)) {
+        $display = $display_function($component, $value, 'text');
+        $display['#theme_wrappers'] = array();
+        $display['#webform_component'] = $component;
+        $subject = str_replace("\n", ' ', drupal_render($display));
+      }
+      else {
+        $subject = $value;
+      }
+    }
+    else {
+      $subject = t('Value of "!component"', array('!component' => $component['name']));
+    }
+  }
+
+  // Convert arrays to strings (may happen if checkboxes are used as the value).
+  if (is_array($subject)) {
+    $subject = reset($subject);
+  }
+
+  return _webform_filter_values($subject, $node, $submission, NULL, FALSE, TRUE);
+}
+
+/**
+ * Convert an array of components into a tree
+ */
+function _webform_components_tree_build($src, &$tree, $parent, &$page_count) {
+  foreach ($src as $cid => $component) {
+    if ($component['pid'] == $parent) {
+      _webform_components_tree_build($src, $component, $cid, $page_count);
+      if ($component['type'] == 'pagebreak') {
+        $page_count++;
+      }
+      $tree['children'][$cid] = $component;
+      $tree['children'][$cid]['page_num'] = $page_count;
+    }
+  }
+  return $tree;
+}
+
+/**
+ * Flatten a component tree into a flat list.
+ */
+function _webform_components_tree_flatten($tree) {
+  $components = array();
+  foreach ($tree as $cid => $component) {
+    if (isset($component['children'])) {
+      unset($component['children']);
+      $components[$cid] = $component;
+      // array_merge() can't be used here because the keys are numeric.
+      $children = _webform_components_tree_flatten($tree[$cid]['children']);
+      foreach ($children as $ccid => $ccomponent) {
+        $components[$ccid] = $ccomponent;
+      }
+    }
+    else {
+      $components[$cid] = $component;
+    }
+  }
+  return $components;
+}
+
+/**
+ * Helper for the uasort in webform_tree_sort()
+ */
+function _webform_components_sort($a, $b) {
+  if ($a['weight'] == $b['weight']) {
+    return strcasecmp($a['name'], $b['name']);
+  }
+  return ($a['weight'] < $b['weight']) ? -1 : 1;
+}
+
+/**
+ * Sort each level of a component tree by weight and name
+ */
+function _webform_components_tree_sort($tree) {
+  if (isset($tree['children']) && is_array($tree['children'])) {
+    $children = array();
+    uasort($tree['children'], '_webform_components_sort');
+    foreach ($tree['children'] as $cid => $component) {
+      $children[$cid] = _webform_components_tree_sort($component);
+    }
+    $tree['children'] = $children;
+  }
+  return $tree;
+}
+
+/**
+ * Get a list of all available component definitions.
+ */
+function webform_components($include_disabled = FALSE, $reset = FALSE) {
+  static $components, $disabled;
+
+  if (!isset($components) || $reset) {
+    $components = array();
+    $disabled = array_flip(variable_get('webform_disabled_components', array()));
+    foreach (module_implements('webform_component_info') as $module) {
+      $module_components = module_invoke($module, 'webform_component_info');
+      foreach ($module_components as $type => $info) {
+        $module_components[$type]['module'] = $module;
+        $module_components[$type]['enabled'] = !array_key_exists($type, $disabled);
+      }
+      $components += $module_components;
+    }
+    drupal_alter('webform_component_info', $components);
+    ksort($components);
+  }
+
+  return $include_disabled ? $components : array_diff_key($components, $disabled);
+}
+
+/**
+ * Build a list of components suitable for use as select list options.
+ */
+function webform_component_options($include_disabled = FALSE) {
+  $component_info = webform_components($include_disabled);
+  $options = array();
+  foreach ($component_info as $type => $info) {
+    $options[$type] = $info['label'];
+  }
+  return $options;
+}
+
+/**
+ * Load a component file into memory.
+ *
+ * @param $component_type
+ *   The string machine name of a component.
+ */
+function webform_component_include($component_type) {
+  static $included = array();
+
+  // No need to load components that have already been added once.
+  if (!isset($included[$component_type])) {
+    $components = webform_components(TRUE);
+    $included[$component_type] = TRUE;
+
+    if (($info = $components[$component_type]) && isset($info['file'])) {
+      $pathinfo = pathinfo($info['file']);
+      $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']);
+      $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename;
+      module_load_include($pathinfo['extension'], $info['module'], $path);
+    }
+  }
+}
+
+/**
+ * Invoke a component callback.
+ *
+ * @param $type
+ *   The component type as a string.
+ * @param $callback
+ *   The callback to execute.
+ * @param ...
+ *   Any additional parameters required by the $callback.
+ */
+function webform_component_invoke($type, $callback) {
+  $args = func_get_args();
+  $type = array_shift($args);
+  $callback = array_shift($args);
+  $function = '_webform_' . $callback . '_' . $type;
+  webform_component_include($type);
+  if (function_exists($function)) {
+    return call_user_func_array($function, $args);
+  }
+}
+
+/**
+ * Check if a component implements a particular hook.
+ *
+ * @param $type
+ *   The component type as a string.
+ * @param $callback
+ *   The callback to check.
+ */
+function webform_component_implements($type, $callback) {
+  $function = '_webform_' . $callback . '_' . $type;
+  webform_component_include($type);
+  return function_exists($function);
+}
+
+/**
+ * Disable the Drupal page cache.
+ */
+function webform_disable_page_cache() {
+  drupal_page_is_cacheable(FALSE);
+}
+
+/**
+ * Set the necessary breadcrumb for the page we are on.
+ */
+function webform_set_breadcrumb($node, $submission = NULL) {
+  $breadcrumb = drupal_get_breadcrumb();
+
+  if (isset($node)) {
+    $webform_breadcrumb = array();
+    $webform_breadcrumb[] = array_shift($breadcrumb);
+    $webform_breadcrumb[] = l($node->title, 'node/' . $node->nid);
+    if (isset($submission)) {
+      $last_link = array_shift($breadcrumb);
+      if (webform_results_access($node)) {
+        $webform_breadcrumb[] = l(t('Webform results'), 'node/' . $node->nid . '/webform-results');
+      }
+      elseif (user_access('access own webform results', $account)) {
+        $webform_breadcrumb[] = l(t('Submissions'), 'node/' . $node->nid . '/submissions');
+      }
+      if (isset($last_link)) {
+        $webform_breadcrumb[] = $last_link;
+      }
+    }
+    $breadcrumb = $webform_breadcrumb;
+  }
+
+  drupal_set_breadcrumb($breadcrumb);
+}
+
+/**
+ * Convert an ISO 8601 date or time into an array.
+ *
+ * This converts full format dates or times. Either a date or time may be
+ * provided, in which case only those portions will be returned. Dashes and
+ * colons must be used, never implied.
+ *
+ * Formats:
+ * Dates: YYYY-MM-DD
+ * Times: HH:MM:SS
+ * Datetimes: YYYY-MM-DDTHH:MM:SS
+ *
+ * @param $string
+ *   An ISO 8601 date, time, or datetime.
+ * @param $type
+ *   If wanting only specific fields back, specify either "date" or "time".
+ *   Leaving empty will return an array with both date and time keys, even if
+ *   some are empty. Returns an array with the following keys:
+ *   - year
+ *   - month
+ *   - day
+ *   - hour (in 24hr notation)
+ *   - minute
+ *   - second
+ */
+function webform_date_array($string, $type = NULL) {
+  $pattern = '/((\d{4}?)-(\d{2}?)-(\d{2}?))?(T?(\d{2}?):(\d{2}?):(\d{2}?))?/';
+  $matches = array();
+  preg_match($pattern, $string, $matches);
+  $matches += array_fill(0, 9, '');
+
+  $return = array();
+
+  // Check for a date string.
+  if ($type == 'date' || !isset($type)) {
+    $return['year'] = $matches[2] !== '' ? (int) $matches[2] : '';
+    $return['month'] = $matches[3] !== '' ? (int) $matches[3] : '';
+    $return['day'] = $matches[4] !== '' ? (int) $matches[4] : '';
+  }
+
+  // Check for a time string.
+  if ($type == 'time' || !isset($type)) {
+    $return['hour'] = $matches[6] !== '' ? (int) $matches[6] : '';
+    $return['minute'] = $matches[7] !== '' ? (int) $matches[7] : '';
+    $return['second'] = $matches[8] !== '' ? (int) $matches[8] : '';
+  }
+
+  return $return;
+}
+
+/**
+ * Convert an array of a date or time into an ISO 8601 compatible string.
+ *
+ * @param $array
+ *   The array to convert to a date or time string.
+ * @param $type
+ *   If wanting a specific string format back specify either "date" or "time".
+ *   Otherwise a full ISO 8601 date and time string will be returned.
+ */
+function webform_date_string($array, $type = NULL) {
+  $string = '';
+
+  if ($type == 'date' || !isset($type)) {
+    $string .= empty($array['year']) ? '0000' : sprintf('%04d', $array['year']);
+    $string .= '-';
+    $string .= empty($array['month']) ? '00' : sprintf('%02d', $array['month']);
+    $string .= '-';
+    $string .= empty($array['day']) ? '00' : sprintf('%02d', $array['day']);
+  }
+
+  if (!isset($type)) {
+    $string .= 'T';
+  }
+
+  if ($type == 'time' || !isset($type)) {
+    $string .= empty($array['hour']) ? '00' :  sprintf('%02d', $array['hour']);
+    $string .= ':';
+    $string .= empty($array['minute']) ? '00' :  sprintf('%02d', $array['minute']);
+    $string .= ':';
+    $string .= empty($array['second']) ? '00' :  sprintf('%02d', $array['second']);
+  }
+
+  return $string;
+}
+
+/**
+ * Get a date format according to the site settings.
+ *
+ * @param $size
+ *   A choice of 'short', 'medium', or 'long' date formats.
+ */
+function webform_date_format($size = 'medium') {
+    // Format date according to site's given format.
+    $format = variable_get('date_format_' . $size, 'D, m/d/Y - H:i');
+    $time = 'aABgGhHisueIOPTZ';
+    $day_of_week = 'Dlw';
+    $special = ',-: ';
+    $date_format = trim($format, $time . $day_of_week . $special);
+
+    // Ensure that a day, month, and year value are present. Use a default
+    // format if all the values are not found.
+    if (!preg_match('/[dj]/', $date_format) || !preg_match('/[FmMn]/', $date_format) || !preg_match('/[oYy]/', $date_format)) {
+      $date_format = 'm/d/Y';
+    }
+
+    return $date_format;
+}
+
+/**
+ * Return a date in the desired format taking into consideration user timezones.
+ */
+function webform_strtodate($format, $string, $timezone_name = NULL) {
+  global $user;
+
+  // Adjust the time based on the user or site timezone.
+  if (variable_get('configurable_timezones', 1) && $timezone_name == 'user' && $user->uid) {
+    $timezone_name = isset($GLOBALS['user']->timezone) ? $GLOBALS['user']->timezone : 'UTC';
+  }
+  // If the timezone is still empty or not set, use the site timezone.
+  if (empty($timezone_name) || $timezone_name == 'user') {
+    $timezone_name = variable_get('date_default_timezone', 'UTC');
+  }
+
+  if (!empty($timezone_name) && class_exists('DateTimeZone')) {
+    // Suppress errors if encountered during string conversion. Exceptions are
+    // only supported for DateTime in PHP 5.3 and higher.
+    try {
+      @$timezone = new DateTimeZone($timezone_name);
+      @$datetime = new DateTime($string, $timezone);
+      return @$datetime->format($format);
+    }
+    catch (Exception $e) {
+      return '';
+    }
+  }
+  else {
+    return date($format, strtotime($string));
+  }
+}
+
+/**
+ * Get a timestamp in GMT time, ensuring timezone accuracy.
+ */
+function webform_strtotime($date) {
+  $current_tz = date_default_timezone_get();
+  date_default_timezone_set('UTC');
+  $timestamp = strtotime($date);
+  date_default_timezone_set($current_tz);
+  return $timestamp;
+}
+
+/**
+ * Wrapper function for i18n_string() if i18nstrings enabled.
+ */
+function webform_tt($name, $string, $langcode = NULL, $update = FALSE) {
+  if (function_exists('i18n_string')) {
+    $options = array(
+      'langcode' => $langcode,
+      'update' => $update,
+    );
+    return i18n_string($name, $string, $options);
+  }
+  else {
+    return $string;
+  }
+}
+
+/**
+ * Check if any available HTML mail handlers are available for Webform to use.
+ */
+function webform_email_html_capable() {
+  // TODO: Right now we only support MIME Mail. Support others if available
+  // through a hook?
+  if (module_exists('mimemail')) {
+    $mail_systems = variable_get('mail_system', array('default-system' => 'DefaultMailSystem'));
+    $enable = !isset($mail_systems['webform']) || $mail_systems['webform'] == 'MimeMailSystem';
+
+    // We assume that if a solution exists even if it's not specified we should
+    // use it. Webform will specify if e-mails sent with the system are plain-
+    // text or not when sending each e-mail.
+    if ($enable) {
+      $GLOBALS['conf']['mail_system']['webform'] = 'MimeMailSystem';
+      return TRUE;
+    }
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function webform_views_api() {
+  return array(
+    'api' => 2.0,
+    'path' => drupal_get_path('module', 'webform') . '/views',
+  );
+}
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function webform_field_extra_fields() {
+  $extra = array();
+  foreach (webform_variable_get('webform_node_types') as $type) {
+    $extra['node'][$type]['display']['webform'] = array(
+      'label' => t('Webform'),
+      'description' => t('Webform client form.'),
+      'weight' => 10,
+    );
+  }
+  return $extra;
+}
+
+/**
+ * Implements hook_mollom_form_list().
+ */
+function webform_mollom_form_list() {
+  $forms = array();
+  $webform_types = webform_variable_get('webform_node_types');
+  if (empty($webform_types)) {
+    return $forms;
+  }
+
+  $result = db_select('node', 'n')
+    ->fields('n', array('nid', 'title'))
+    ->condition('n.type', $webform_types, 'IN')
+    ->execute();
+
+  foreach ($result as $node) {
+    $form_id = 'webform_client_form_' . $node->nid;
+    $forms[$form_id] = array(
+      'title' => t('@name form', array('@name' => $node->title)),
+      'entity' => 'webform',
+      'delete form' => 'webform_submission_delete_form',
+    );
+  }
+  return $forms;
+}
+
+/**
+ * Implements hook_mollom_form_info().
+ */
+function webform_mollom_form_info($form_id) {
+  module_load_include('inc', 'webform', 'includes/webform.components');
+
+  $nid = drupal_substr($form_id, 20);
+  $node = node_load($nid);
+  $form_info = array(
+    'title' => t('@name form', array('@name' => $node->title)),
+    'mode' => MOLLOM_MODE_ANALYSIS,
+    'bypass access' => array('edit all webform submissions', 'edit any webform content'),
+    'entity' => 'webform',
+    'elements' => array(),
+    'mapping' => array(
+      'post_id' => 'details][sid',
+      'author_id' => 'details][uid',
+    ),
+  );
+  // Add components as elements.
+  // These components can be enabled for textual analysis (when not using a
+  // CAPTCHA-only protection) in Mollom's form configuration.
+  foreach ($node->webform['components'] as $cid => $component) {
+    if (webform_component_feature($component['type'], 'spam_analysis')) {
+      $parents = implode('][', webform_component_parent_keys($node, $component));
+      $form_info['elements']['submitted][' . $parents] = check_plain(t($component['name']));
+    }
+  }
+  // Assign field mappings based on webform configuration.
+  // Since multiple emails can be configured, we iterate over all and take
+  // over the assigned component for the field mapping in any email, unless
+  // we already assigned one. We are not interested in administratively
+  // configured static strings, only user-submitted values.
+  foreach ($node->webform['emails'] as $email) {
+    // Subject (post_title).
+    if (!isset($form_info['mapping']['post_title'])) {
+      $cid = $email['subject'];
+      if (is_numeric($cid)) {
+        $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
+        $form_info['mapping']['post_title'] = 'submitted][' . $parents;
+      }
+    }
+    // From name (author_name).
+    if (!isset($form_info['mapping']['author_name'])) {
+      $cid = $email['from_name'];
+      if (is_numeric($cid)) {
+        $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
+        $form_info['mapping']['author_name'] = 'submitted][' . $parents;
+      }
+    }
+    // From address (author_mail).
+    if (!isset($form_info['mapping']['author_mail'])) {
+      $cid = $email['from_address'];
+      if (is_numeric($cid)) {
+        $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
+        $form_info['mapping']['author_mail'] = 'submitted][' . $parents;
+      }
+    }
+  }
+
+  return $form_info;
+}
