FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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 '';
}
}

View File

@@ -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];
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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))));
}

View File

@@ -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] : '';
}

View File

@@ -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();
}

View File

@@ -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']);
}

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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];
}

View File

@@ -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];
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

View File

@@ -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));
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}

View File

@@ -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');
}
}

View File

@@ -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'),
);
}

View File

@@ -0,0 +1,417 @@
<?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.'));
}
elseif (strpos($redirect_url, 'http') === 0 && !valid_url($redirect_url, TRUE)) {
form_error($form['submission']['redirection']['redirect_url'], t('The entered URL is not a valid address.'));
}
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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -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']));
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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'); ?>" />

View File

@@ -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>

View File

@@ -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);

View File

@@ -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>' : ''); ?>

View File

@@ -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; ?>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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; ?>

View File

@@ -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); ?>

View File

@@ -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.');
}
}

View File

@@ -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
*/
}

View File

@@ -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.'));
}
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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',
),
),
);
}

View File

@@ -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;
}
}

View File

@@ -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');
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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');
}
}
}

View File

@@ -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';
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
/**
* @}
*/

View File

@@ -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"

View File

@@ -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));
}
}

File diff suppressed because it is too large Load Diff