From da4930009d0b0a4984c39d06ada300f39def6fdb Mon Sep 17 00:00:00 2001 From: Eric Rasmussen <eric@unl.edu> Date: Tue, 8 Nov 2011 22:59:20 +0000 Subject: [PATCH] [gh-215] Merging test into staging -c1181 -c1041 git-svn-id: file:///tmp/wdn_thm_drupal/branches/drupal-7.x/staging@1267 20a16fea-79d4-4915-8869-1ea9d5ebf173 --- sites/all/modules/webform_alt_ui/LICENSE.txt | 274 +++ .../css/webform_alt_ui.client.css | 8 + .../webform_alt_ui/css/webform_alt_ui.css | 559 ++++++ .../all/modules/webform_alt_ui/images/add.png | Bin 0 -> 51624 bytes .../images/draggable-states.png | Bin 0 -> 268 bytes .../webform_alt_ui/images/draggable.png | Bin 0 -> 281 bytes .../webform_alt_ui/images/green_arrow.png | Bin 0 -> 219 bytes .../webform_alt_ui/images/yellow_arrow.png | Bin 0 -> 216 bytes .../includes/webform_alt_ui.components.inc | 175 ++ .../webform_alt_ui/js/webform_alt_ui.file.js | 37 + .../webform_alt_ui/webform_alt_ui.info | 15 + .../webform_alt_ui/webform_alt_ui.install | 26 + .../modules/webform_alt_ui/webform_alt_ui.js | 190 ++ .../webform_alt_ui/webform_alt_ui.module | 1680 +++++++++++++++++ 14 files changed, 2964 insertions(+) create mode 100644 sites/all/modules/webform_alt_ui/LICENSE.txt create mode 100644 sites/all/modules/webform_alt_ui/css/webform_alt_ui.client.css create mode 100644 sites/all/modules/webform_alt_ui/css/webform_alt_ui.css create mode 100644 sites/all/modules/webform_alt_ui/images/add.png create mode 100644 sites/all/modules/webform_alt_ui/images/draggable-states.png create mode 100644 sites/all/modules/webform_alt_ui/images/draggable.png create mode 100644 sites/all/modules/webform_alt_ui/images/green_arrow.png create mode 100644 sites/all/modules/webform_alt_ui/images/yellow_arrow.png create mode 100644 sites/all/modules/webform_alt_ui/includes/webform_alt_ui.components.inc create mode 100644 sites/all/modules/webform_alt_ui/js/webform_alt_ui.file.js create mode 100644 sites/all/modules/webform_alt_ui/webform_alt_ui.info create mode 100644 sites/all/modules/webform_alt_ui/webform_alt_ui.install create mode 100644 sites/all/modules/webform_alt_ui/webform_alt_ui.js create mode 100644 sites/all/modules/webform_alt_ui/webform_alt_ui.module diff --git a/sites/all/modules/webform_alt_ui/LICENSE.txt b/sites/all/modules/webform_alt_ui/LICENSE.txt new file mode 100644 index 00000000..2c095c8d --- /dev/null +++ b/sites/all/modules/webform_alt_ui/LICENSE.txt @@ -0,0 +1,274 @@ +GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, +Cambridge, MA 02139, 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 Library 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 diff --git a/sites/all/modules/webform_alt_ui/css/webform_alt_ui.client.css b/sites/all/modules/webform_alt_ui/css/webform_alt_ui.client.css new file mode 100644 index 00000000..034499ec --- /dev/null +++ b/sites/all/modules/webform_alt_ui/css/webform_alt_ui.client.css @@ -0,0 +1,8 @@ + +.webform-client-form .form-actions input + input { + margin-left: 10px; +} + +.webform-client-form input { + max-width: 98%; +} diff --git a/sites/all/modules/webform_alt_ui/css/webform_alt_ui.css b/sites/all/modules/webform_alt_ui/css/webform_alt_ui.css new file mode 100644 index 00000000..2d4b6de8 --- /dev/null +++ b/sites/all/modules/webform_alt_ui/css/webform_alt_ui.css @@ -0,0 +1,559 @@ + +/* @group Form Settings */ + +#form-builder > h3 { + display: none; +} + +#edit-ui-wrapper .horizontal-tabs { + float: left; +} + +#edit-ui-wrapper .horizontal-tabs-pane > .fieldset-wrapper { + padding: 1em 0 0; +} + +#edit-ui-wrapper .horizontal-tabs-pane > .fieldset-wrapper .pane-header, +#edit-ui-wrapper .horizontal-tabs-pane > .fieldset-wrapper .pane { + border-left: medium none; + border-right: medium none; +} + +#edit-ui-wrapper .horizontal-tabs-pane > .fieldset-wrapper > .pane-header:last-child { + border-bottom: medium none; +} + +#edit-ui-wrapper { + border: 1px solid #ccc; + display: inline-block; + margin-top: 45px; + padding: 0; + width: 100%; +} + +#edit-ui-wrapper .horizontal-tabs-pane input.form-text { + width: 100%; +} + +#edit-ui-wrapper .horizontal-tabs { + margin: -27px -1px -1px; + width: 242px; +} + +#edit-settings-container { + padding-left: 1.2em; +} + +/* @end */ + +/* @group Form builder layout */ + +#form-builder-wrapper { + border-left: 1px solid #ccc; + padding: 0; +} + +#form-builder { + padding: 8px 22px 11px 0; + position: relative; +} + +.horizontal-tabs-processed .horizontal-tabs-pane { + border: none; + border-left: 1px solid #ccc; + margin-bottom: 0; +} + +.horizontal-tabs-panes.horizontal-tabs-processed { + border-right: 1px solid #ccc; + margin-left: 1px; + margin-top: 26px; +} + +.horizontal-tabs fieldset.form-wrapper { + border: none; + margin: 0; + padding: 0; +} + +.horizontal-tabs fieldset.form-wrapper .fieldset-wrapper { + padding: 0; +} + +#form-builder-field-remove, +.field-settings-message { + padding: 0 11px +} + +/* @end */ + +/* @group Form builder palette */ + +#form-builder-field-palette { + padding: 0 11px; +} + +#form-builder-field-palette h3 { + margin-bottom: 0.5em; +} + +#form-builder-field-palette ul.form-builder-fields { + margin: 0; + width: 100%; +} + +#form-builder-field-palette ul li, +#form-builder-wrapper > li { + background-color: #dddddd; + border: medium none; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + float: none; + margin-top: 8px; + width: auto; +} + +#form-builder-field-palette ul li:hover { + background-color: #f2efc3; +} + +#form-builder-field-palette ul .first { + margin-top: 0; +} + +#form-builder-field-palette ul li.ui-draggable, +#form-builder-wrapper > li.ui-draggable { + background-image: none; + list-style: none; + padding: 3px; +} + +#form-builder-field-palette ul li.ui-draggable a, +#form-builder-wrapper > li.ui-draggable a { + background: url(../images/draggable-states.png) no-repeat scroll left 6px transparent; + color: #000; + display: block; + font-size: 13px; + margin-left: 3px; + padding: 2px 10px 2px 19px; + white-space: nowrap; +} + +#form-builder-field-palette ul li.ui-draggable-dragging:hover, +#form-builder-wrapper > li.ui-draggable-dragging:hover { + background-color: #dddddd; +} + +#form-builder-field-palette ul li.ui-draggable:hover a, +#form-builder-field-palette ul li.ui-draggable-dragging a, +#form-builder-wrapper > li.ui-draggable:hover a, +#form-builder-wrapper > li.ui-draggable-dragging a{ + background-position: left -14px; +} + +/* @end */ + +/* @group Form builder field configure */ + +#form-builder-field-configure .confirmation input[type=submit] { + display: inline-block; +} + +#form-builder-field-configure .options-widget + fieldset > legend { + display: none; +} + +#form-builder-field-configure .option-actions-cell { + width: 20px; +} + +/* @end */ + +/* @group Form builder preview */ + +#form-builder h3 { + margin: 0 0 20px; +} + +#form-builder .form-item label, +#form-builder .form-item label.option { + font-size: 13px; +} + +#form-builder-wrapper .form-builder-element .form-builder-preview-hidden-webform-element, +#form-builder-wrapper .form-builder-element .form-builder-preview-pagebreak-webform-element { + padding-top: 4px; +} + +#form-builder-wrapper .form-builder-preview-pagebreak-webform-element { + text-align: center; +} + +#form-builder-wrapper .form-builder-preview-pagebreak-webform-element * { + margin: 0; + white-space: nowrap; +} + +/* @group Form elements */ + +.form-builder-title-bar { + display: block !important; +} + +#form-builder .form-builder-wrapper .form-builder-wrapper { + margin: 0; +} + +#form-builder .form-builder-element { + margin: 0; + padding-left: 21px; +} + +#form-builder .form-builder-element .form-item { + padding: 10px 10px 30px 35px; +} + +#form-builder .form-builder-element .form-type-checkbox, +#form-builder .form-builder-element .form-type-radio { + margin-left: 0; + padding: 5px 0; +} + +#form-builder .form-builder-element .fieldset-color-wrapper { + background: #fff; + padding: 10px 10px 10px 20px; +} + +.fieldset-color-wrapper > fieldset { + padding-left: 15px; +} + +#form-builder .form-builder-wrapper, +#form-builder .form-builder-active { + border: none; + padding: 0; +} + +#form-builder .form-builder-active > .form-builder-element { + background: url(../images/yellow_arrow.png) no-repeat left 14px transparent; +} + +#form-builder .form-builder-active > .form-builder-element > .form-item { + background: #F4ECC7; +} + +#form-builder .form-builder-active > .form-builder-element-fieldset { + background: url(../images/green_arrow.png) no-repeat left 14px transparent; +} + +#form-builder .form-builder-active > .form-builder-element-fieldset > .fieldset-color-wrapper { + background: #E0EFCC; +} + +#form-builder .webform-component { + padding: 0; +} + +#form-builder label { + font-weight: normal; +} + +#form-builder .form-builder-placeholder.over { + min-height: 30px; + padding: 4px; +} + +#form-builder .form-builder-placeholder.over + .form-builder-wrapper .form-builder-placeholder { + height: 0px !important; +} + +div.form-builder-hover { + border: none; +} + +.form-builder-placeholder.over, +#form-builder .form-builder-empty-placeholder.over { + background: #f0f0f0; +} + +#form-builder .form-builder-placeholder.over + .form-builder-wrapper .form-builder-empty-placeholder.over, +#form-builder .form-builder-placeholder.over + .original + .form-builder-wrapper .form-builder-empty-placeholder.over, +#form-builder .form-builder-placeholder.over + .form-builder-wrapper .form-builder-placeholder.over, +#form-builder .form-builder-placeholder.over + .original + .form-builder-wrapper .form-builder-placeholder.over { + background: none; +} + +.form-builder-placeholder { + margin-left: 25px; +} + +#form-builder-wrapper > .form-builder-wrapper { + max-height: 80px; +} + +#form-builder-wrapper > .form-builder-wrapper .fieldset-wrapper { + display: none; +} + +/* @end */ + +/* @group Fieldsets */ + +#form-builder .form-builder-wrapper.form-builder-empty-placeholder { + padding: 40px; +} + +#form-builder .ui-sortable-placeholder + .form-builder-wrapper.form-builder-empty-placeholder, +#form-builder .form-builder-wrapper.form-builder-empty-placeholder + .form-builder-wrapper.form-builder-empty-placeholder { + padding: 0 0 40px; +} + +#form-builder .ui-sortable-placeholder + .form-builder-wrapper.form-builder-empty-placeholder span { + display: none; +} + +#form-builder .form-builder-wrapper.form-builder-empty-placeholder span { + margin: 0; +} + +#form-builder fieldset { + border: 2px solid #ccc; + padding-bottom: 20px; + padding-right: 11px; +} + +#form-builder fieldset legend { + margin-left: -7px; +} + +#form-builder fieldset .fieldset-legend { + font-weight: normal; + padding: 0 5px; + position: static; + margin: 0; + text-transform: none; +} + +#form-builder fieldset.collapsible .fieldset-legend { + padding-left: 15px; +} + +#form-builder #webform-component-live-preview--submit-preview { + background-image: none; +} + +#form-builder .button { + background-color: #666666; + background-image: none; + border: medium none; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + color: #FFFFFF; + display: inline-block; + font-size: 0.9286em; + font-weight: normal; + padding: 0.6667em 2em; +} + +#form-builder .button:hover { + cursor: default; +} + +#form-builder > .empty-form + .form-builder-wrapper { + display: none; +} + +#form-builder > .empty-form { + margin: -1em 0 0; + position: absolute; + text-align: center; + top: 50%; + width: 100%; +} + +/* @end */ + +/* @group Links */ + +#form-builder-wrapper .form-builder-links { + display: none; + position: absolute; + text-align: right; + left: auto; + top: auto; + bottom: 2px; + right: 11px; + z-index: 1; +} + +#form-builder-wrapper .form-builder-wrapper:hover > * > .webform-component, +#form-builder-wrapper .form-builder-wrapper:hover > * > .fieldset-color-wrapper > .webform-component { + background-image: url('../images/draggable.png'); + background-position: 4px 4px; + background-repeat: no-repeat; + border: 1px solid #ccc; + margin: -1px; + position: relative; + z-index: 1; +} + +#form-builder-wrapper .form-builder-active:hover > * > .webform-component { + border: none; + margin: 0; +} + +#form-builder-wrapper .form-builder-wrapper:hover > * > .form-builder-links, +#form-builder-wrapper .form-builder-wrapper:hover > * > .fieldset-color-wrapper > .form-builder-links { + display: block; +} + +#form-builder .fieldset-color-wrapper > .form-builder-links { + position: static; +} + +#form-builder .form-builder-active > .form-builder-element > .form-builder-links, +#form-builder .form-builder-active > .form-builder-element > .fieldset-color-wrapper > .form-builder-links { + display: block; +} + +.form-builder-links li { + display: inline-block; +} + +.form-builder-links li + li { + margin-left: 1em; +} + +/* @end */ + +/* @end */ + +.disabled > label { + color: #ddd; +} + +#edit-ui-wrapper .vertical-accordion label { + display: inline; + font-size: 13px; + font-weight: bold; +} + +.webform-response td + td { + text-align: right; +} + +.webform-upload-file-types { + outline: 1px solid red; + overflow: hidden; +} + +#edit-webform-file-filtering-types-type-groups, +#edit-webform-file-filtering-types-file-types { + margin-bottom: 10px; + overflow: hidden; +} + +#edit-webform-file-filtering-types-type-groups .form-item, +#edit-webform-file-filtering-types-file-types .form-item { + float: left; + margin: 1px 0; + padding: 0; + width: 25%; +} + +#edit-webform-file-filtering-types-type-groups .form-item { + width: 50%; +} + +#edit-webform-file-filtering-types-type-groups .form-item:first-child { + width: 100%; +} + +/* @group Vertical tabs */ + +div.vertical-tabs fieldset { + border: 1px solid #ccc; + margin: 1em 0; + padding: 2.5em 1em 0; + position: relative; +} + +div.vertical-tabs .vertical-tabs-panes legend { + display: block; +} + +div.vertical-tabs-panes > fieldset { + border: none; + margin: 0; + padding: 0; +} + +div.vertical-tabs .vertical-tabs-panes > fieldset > legend { + display: none; +} + +.vertical-tabs-panes .limit-wrapper { + font-size: 13px; +} + +.vertical-tabs-panes .limit-wrapper .form-item { + display: inline-block; +} + +.vertical-tabs-panes .limit-wrapper .form-item .form-text { + width: auto; +} + +.vertical-tabs-panes .horizontal-tabs-pane .form-item-redirect-url .form-text { + width: 70%; +} + +/* @end Vertical tabs */ + +.webform-submission-navigation { + color: #ccc; +} + +.webform-user { + border: 1px solid #CCCCCC; + margin-bottom: 20px; + padding: 0 12px 6px; +} + +.webform-user .user-picture { + float: left; + margin-right: 10px; +} + +.webform-user .label { + font-weight: bold; +} + +.webform-submission .webform-component label, +.webform-submission .webform-component input { + display: inline-block; + font-size: 1em; +} + +.webform-submission .webform-component label { + font-weight: bold; + vertical-align: top; + width: 12em; +} + +.webform-alt-ui-simplified-text-format .form-type-select { + text-align: right; +} + +div.options-widget a.add:hover, +div.options-widget a.remove:hover, +div.form-option-add a:hover { + background-position: 0 0; +} + +div.options-widget a.add, +div.form-option-add a { + background-image: url(../images/add.png); +} diff --git a/sites/all/modules/webform_alt_ui/images/add.png b/sites/all/modules/webform_alt_ui/images/add.png new file mode 100644 index 0000000000000000000000000000000000000000..e96107242ecc48e713486ef5390aeb8b25f0fde5 GIT binary patch literal 51624 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 TyDCWzv<Jo0)z4*}Q$iB}-{b3M literal 0 HcmV?d00001 diff --git a/sites/all/modules/webform_alt_ui/images/draggable-states.png b/sites/all/modules/webform_alt_ui/images/draggable-states.png new file mode 100644 index 0000000000000000000000000000000000000000..93d20d60d504041b9841a35d285d4ba5eaaf6167 GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0y~yVBlw9V6fp}W?*3GdnQoLz`&pz;1lA?z`*by3>X*~ z1Ox<%ii)bMt6N)JySuw5Po6w;=FCHf4&A$V@9ER0A3l5tyPw_0z`(#<666=m;JDx8 zlt?=R14Fl`i(^Q|t)qU7e9Vd*vo|!BYyGc(cjkdpgnWXq)2&;lP8%lKZ|Qf^zo?`W z>A5XAsoK8(`K(RP<>FKu``ZNe@tjHC?{3rm{V8wy!l~c3i%s9jH97rkQCUgcw%X`5 z^L$$-*ge~5Z?%Ps>$^Pb-@wIQYq`!w332&3uHE`gx@eZd2SxtBKkV;4zp4C#>4glN V>eNuxP6h@B22WQ%mvv4FO#lXdZb|?E literal 0 HcmV?d00001 diff --git a/sites/all/modules/webform_alt_ui/images/draggable.png b/sites/all/modules/webform_alt_ui/images/draggable.png new file mode 100644 index 0000000000000000000000000000000000000000..de9724df8fc80275a767e76a2c99aebe5ae42a68 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4wWz`#)S`biA~1A}CVYeY$Kep*R+Vo@rCV@iHf zs)A>3VtQ&&YGO)d;mK4R1_p-Ro-U3d6}M`F4Eb6V1Y$BYG%9;Ux7jqP|KPdzD1FPK zEt@tgmmE1Jq4n<C6P}7gCc-n$ygSF#%C#)h%e40z&$U~tChn7*mfBc*KXKmkwBt<Y zcBb@ro;d4Q>3Xj6p#R)w%DK18LirVAl#J$mtc$&V+oNuwp)>dM$|eD(B~u(e<z}vJ zG-NY$lQ=HemZ)*gOY_C=d3(<Nd0VB+v~w2c#Bhtg#SfPIW%0k>yWdzudGE}k<m;vN iZE8{W50wuv{8&Hrar5ne9~l@J7(8A5T-G@yGywo<ZgmL& literal 0 HcmV?d00001 diff --git a/sites/all/modules/webform_alt_ui/images/green_arrow.png b/sites/all/modules/webform_alt_ui/images/green_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..4b3622ffc1c29ef97a970c024601e4148921fc07 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0y~yU=U+qU=ZP8V_;zDtT}7Iz`!6`;u=vBoS#-wo>-L1 z;Fyx1l&avFo0y&&l$w}QS$HzlhJk^h*3-o?q~g|=t7kb67;v;D>Pjj|mvp?_@7Vr= zP4eKQg%=#^dg?wtds^wpp(v>M#OA4(sj$$W_nf?K59MC8r9TkLIeKOTyUZcM4V=mc zxZIlV%WTPvm@jYa>|gk-TU(;^wVn*GPh$Q7p7m}q8&n0F+!;CUaB2AL-nT5gfsui~ XGkaxZ-A{W41_lOCS3j3^P6<r_U~f$n literal 0 HcmV?d00001 diff --git a/sites/all/modules/webform_alt_ui/images/yellow_arrow.png b/sites/all/modules/webform_alt_ui/images/yellow_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a47d8afa478aa5660dc204ef5b3509eaaed0422f GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0y~yU=U+qU=ZP8V_;zDtT}7Iz`!6`;u=vBoS#-wo>-L1 z;Fyx1l&avFo0y&&l$w}QS$HzlhJk^h%G1R$q~g|=tF~MR3^>{nb#EGdGur$A(&>^j zd4(D#6XX}i--($g-Xb6*=;T+K7ig?_@c+CE2@>xb^D5Z%4u+lKsXm}6!#LZ3X-$I4 zkF$=m+rIuZH2xy;bI*>Z!0qo2T|6N2jqS{btRt+^Oo|mE4F}dX9NO|ayNXqUVbY}> VUA5pt;S3B644$rjF6*2UngC@3QJ4S# literal 0 HcmV?d00001 diff --git a/sites/all/modules/webform_alt_ui/includes/webform_alt_ui.components.inc b/sites/all/modules/webform_alt_ui/includes/webform_alt_ui.components.inc new file mode 100644 index 00000000..ecebaadf --- /dev/null +++ b/sites/all/modules/webform_alt_ui/includes/webform_alt_ui.components.inc @@ -0,0 +1,175 @@ +<?php + +/** + * @file Alters the configuration forms for webform components + */ + +/** + * Alters the configuration form for textfields + */ +function _webform_alt_ui_textfield_configure($form) { + + return $form; +} + +/** + * Alters the configuration form for textareas + */ +function _webform_alt_ui_textarea_configure($form) { + + return $form; +} + +/** + * Alters the configuration form for radios + */ +function _webform_alt_ui_radios_configure($form) { + $form = _webform_alt_ui_option_configure($form); + + return $form; +} + +/** + * Alters the configuration form for checkboxes + */ +function _webform_alt_ui_checkboxes_configure($form) { + $form = _webform_alt_ui_option_configure($form); + + return $form; +} + +/** + * Alters the configuration form for selects + */ +function _webform_alt_ui_select_configure($form) { + $form = _webform_alt_ui_option_configure($form); + + return $form; +} + +/** + * Alters the configuration form for emails + */ +function _webform_alt_ui_email_configure($form) { + + return $form; +} + +/** + * Alters the configuration form for file uploads + */ +function _webform_alt_ui_file_configure($form) { + // Deny access to fields we don't want configured + $form['webform_file_savelocation']['#access'] = FALSE; + $form['webform_file_filtering']['addextensions']['#access'] = FALSE; + + // Alterations for the webform_file_width field + $form['webform_file_width']['#title'] = 'Size'; + $form['webform_file_width']['#description'] = NULL; + + $groups = element_children($form['webform_file_filtering']['types']); + + $form['webform_file_filtering']['types']['#prefix'] = '<label>Allow the following types:</label>'; + + $options = array(); + $defaults = array(); + $settings = array('all' => array()); + + $form['webform_file_filtering']['types']['type_groups'] = array( + '#type' => 'checkboxes', + '#options' => array('all' => 'All'), + '#attributes' => array('class' => array('type-groups')), + ); + + foreach($groups as $key) { + // Remove disallowed extensions from the options. + // @todo Is it appropriate for this code to be in webform_alt_ui module, or + // is it really more of a product/hosting decision? + foreach($form['webform_file_filtering']['types'][$key]['#options'] as $extension) { + if (in_array($extension, array('jar', 'dmg'))) { + unset($form['webform_file_filtering']['types'][$key]['#options'][$extension]); + } + } + $options = array_merge($options, $form['webform_file_filtering']['types'][$key]['#options']); + $defaults = array_merge($defaults, $form['webform_file_filtering']['types'][$key]['#default_value']); + + $form['webform_file_filtering']['types']['type_groups']['#options'][$key] = 'All ' . str_replace('image', ' image', $key); + $settings[$key] = $form['webform_file_filtering']['types'][$key]['#options']; + $settings['all'] = array_merge($settings['all'], $form['webform_file_filtering']['types'][$key]['#options']); + unset($form['webform_file_filtering']['types'][$key]); + } + + $form['webform_file_filtering']['types']['type_groups']['#default_value'] = 'webimages'; + + $form['webform_file_filtering']['types']['file_types'] = array( + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $defaults, + '#attributes' => array('class' => array('file-types')), + ); + + // Set an absolute size limit and a new default. + // @todo It's possible that the #default_value line below is no longer needed + // due to webform_alt_ui_form_builder_types_alter(), but needs more testing + // before it can be removed. The default must be altered in the other + // function, because by the time we get here, we can't distinguish between + // the webform module default and a prior saved configuration value for this + // component. + $form['webform_file_filtering']['size']['#default_value'] = isset($form['#_edit_element']['#webform_file_filtering']['size']) ? $form['#_edit_element']['#webform_file_filtering']['size'] : 1000; + $form['webform_file_filtering']['size']['#element_validate'][] = 'webform_alt_ui_file_size_validate'; + $form['webform_file_filtering']['size']['#description'] = t('Enter the max file size (in KB) a user may upload (limit 20,000KB).'); + + drupal_add_js(array('webform_alt_ui' => $settings), 'setting'); + + $form['#attached']['js'][] = drupal_get_path('module', 'webform_alt_ui') . '/js/webform_alt_ui.file.js'; + + return $form; +} + +/** + * Alters the configuration form for markup + */ +function _webform_alt_ui_markup_configure($form) { + // Hide the title options. + $form['title']['#access'] = FALSE; + $form['title_display']['#access'] = FALSE; + + // Modify the title of the markup field. + $form['markup']['#title'] = t('Formatted content'); + + // Prevent the markup field from being hidden behind a checkbox. + $form['markup']['#checkbox'] = FALSE; + + // Hide parts of the text format selector. + _webform_alt_ui_add_text_format_simplification($form['markup']); + + return $form; +} + +/** + * Alters the configuration form for fieldsets + */ +function _webform_alt_ui_fieldset_configure($form) { + $form['description']['#access'] = FALSE; + + return $form; +} + +/** + * Alters the configuration form for hidden fields + */ +function _webform_alt_ui_hidden_configure($form) { + + return $form; +} + +/* + * Alters the configuration form for all option elements. + * + * Applies to select, checkboxes, and radios elements. + */ +function _webform_alt_ui_option_configure($form) { + $form['options']['#process'] = array('form_options_expand', 'webform_alt_ui_option_process'); + + return $form; +} diff --git a/sites/all/modules/webform_alt_ui/js/webform_alt_ui.file.js b/sites/all/modules/webform_alt_ui/js/webform_alt_ui.file.js new file mode 100644 index 00000000..e71cd3bc --- /dev/null +++ b/sites/all/modules/webform_alt_ui/js/webform_alt_ui.file.js @@ -0,0 +1,37 @@ + +(function ($) { + +/** + * Control the visibility of additional configuration options + */ +Drupal.behaviors.webformAltUI_file_field = { + attach: function (context) { + $('.type-groups', context).once('type-groups', function () { + $(this).find('input[type=checkbox]').bind('click', Drupal.behaviors.webformAltUI_file_field.groupToggle); + }); + $('.file-types', context).once('file-types', function () { + $(this).find('input[type=checkbox]').bind('click', Drupal.behaviors.webformAltUI_file_field.checkToggle); + }); + }, + groupToggle: function (event) { + var $this = $(this); + $.each(Drupal.settings.webform_alt_ui[$this.val()], function (index, value) { + $('input[value=' + value + ']').attr('checked', $this.attr('checked')); + }); + Drupal.behaviors.webformAltUI_file_field.checkToggle(); + }, + checkToggle: function () { + var $this = $(this); + $.each(Drupal.settings.webform_alt_ui, function(index, value) { + var checked = true; + $.each(value, function(index2, value2) { + if ($('.file-types input[value=' + value2 + ']').attr('checked') == false) { + checked = false; + } + }); + $('.type-groups input[value=' + index + ']').attr('checked', checked); + }); + } +}; + +})(jQuery); diff --git a/sites/all/modules/webform_alt_ui/webform_alt_ui.info b/sites/all/modules/webform_alt_ui/webform_alt_ui.info new file mode 100644 index 00000000..ed132d89 --- /dev/null +++ b/sites/all/modules/webform_alt_ui/webform_alt_ui.info @@ -0,0 +1,15 @@ +name = Webform Alternate UI +description = Provides an alternate user experience for the Webform module. +package = Webform +core = 7.x +dependencies[] = webform +dependencies[] = form_builder +dependencies[] = form_builder_webform +dependencies[] = ux_elements + +; Information added by drupal.org packaging script on 2011-07-21 +version = "7.x-1.x-dev" +core = "7.x" +project = "webform_alt_ui" +datestamp = "1311208906" + diff --git a/sites/all/modules/webform_alt_ui/webform_alt_ui.install b/sites/all/modules/webform_alt_ui/webform_alt_ui.install new file mode 100644 index 00000000..02212bc8 --- /dev/null +++ b/sites/all/modules/webform_alt_ui/webform_alt_ui.install @@ -0,0 +1,26 @@ +<?php + +/** + * @file + * Install file for webform_alt_ui. + */ + +/** + * This sets the default delimiter for webform export to be a comma. + */ +function webform_alt_ui_enable() { + variable_set('webform_csv_delimiter', ','); + + $type = node_type_load('webform'); + if ($type) { + $type->description = 'Create a new form or questionnaire. Submission results and statistics are recorded and accessible to privileged users.'; + node_type_save($type); + } + + // Turn off comments by default for webform nodes. + variable_set('comment_webform', COMMENT_NODE_CLOSED); + // Disable the AddThis block by default. + variable_set('addthis_nodetype_webform', 0); + // Hide the submitted by text by default + variable_set('node_submitted_webform', FALSE); +} diff --git a/sites/all/modules/webform_alt_ui/webform_alt_ui.js b/sites/all/modules/webform_alt_ui/webform_alt_ui.js new file mode 100644 index 00000000..bff6e325 --- /dev/null +++ b/sites/all/modules/webform_alt_ui/webform_alt_ui.js @@ -0,0 +1,190 @@ + +(function ($) { + +/** + * Control the visibility of additional configuration options + */ +Drupal.behaviors.webformAltUI = { + attach: function (context) { + $('.check-expand', context).children().not(':first-child').hide(); + $('.check-expander', context) + .bind('click', Drupal.behaviors.webformAltUI.toggleInputs) + .each(Drupal.behaviors.webformAltUI.toggleInputs); + if (Drupal.formBuilder) { + Drupal.formBuilder.fieldConfigureForm = $('#form-builder-field-configure'); + } + + // Add checkboxes before the labels for fields in the field configure form + Drupal.behaviors.webformAltUI.fieldSettingsCheckboxes(context); + + // Because runs all behaviors on every AJAX callback unset and set clicks. + $('#form-builder .form-builder-links a') + .unbind('click') + .bind('click', Drupal.formBuilder.editField) + .bind('click', Drupal.behaviors.webformAltUI.showFieldSettings); + $('.form-builder-element') + .unbind('click') + .bind('click', Drupal.behaviors.webformAltUI.showFieldSettings) + .bind('click', Drupal.formBuilder.clickField); + $('.form-builder-element label').unbind('click'); + $('#form-builder .form-builder-links a.delete') + .unbind('click') + .bind('click', Drupal.behaviors.webformAltUI.removeField); + + // Enable the scroll for the active tabs. + $('.horizontal-tabs-pane').bind('horizontalTabActivate', Drupal.behaviors.webformAltUI.tabActivated); + $(window).bind('scroll', Drupal.behaviors.webformAltUI.windowScroll); + if (!Drupal.behaviors.webformAltUI.activeTab) { + var tab = {}; + tab.fieldset = $('.horizontal-tabs-pane:visible'); + if (tab.fieldset.length) { + Drupal.behaviors.webformAltUI.activeTab = tab; + Drupal.behaviors.webformAltUI.tabActivated({}, tab); + } + } + + // Bind event handlers to update the submit button text. + $('input[name=submit_button]').bind('blur keyup', Drupal.behaviors.webformAltUI.submitTextChange); + + // Check to see if the form is empty and need explanatory text + Drupal.behaviors.webformAltUI.checkForm(true); + + // Remove useless links from the options_element configuration + $('#edit-options-options-field-widget a.add').remove(); + $('.form-options-manual').remove(); + + // Disable dragging on the submit button + $('a.configure.button').closest('.form-builder-wrapper').draggable('destroy'); + + // Remove the field settings form from inside the node form before + // submitting the node form. + $('#webform-node-form').bind('submit', Drupal.behaviors.webformAltUI.removeNestedForms); + }, + toggleInputs: function (event) { + if ($(this).attr('checked')) { + $(this).closest('.check-expand').children().not(':first-child').slideDown(); + } else { + $(this).closest('.check-expand').children().not(':first-child').slideUp(); + } + }, + fieldSettingsCheckboxes: function (context) { + $('.check-toggle:visible', context).once('check-toggle', function() { + // Keep track of, and hide the input itself. + var input = $(this).is('textarea') ? $(this).parent() : $(this); + + var wrapper = input.parent(); + + // Add the checkbox and bind its behavior. + var checkbox = $('<input type="checkbox" />'); + checkbox + .attr('id', $(this).attr('name') + '-checkbox') + .bind('click', input, Drupal.behaviors.webformAltUI.fieldSettingsCheckToggle) + .prependTo(wrapper); + + // If there is already a value here, check the box, otherwise hide the input + if ($(this).val()) { + checkbox.attr('checked', true); + } else { + wrapper.children().not(checkbox).not('label').hide(); + } + // Link the label to the new checkbox so that clicking on it selects the + // checkbox. + wrapper.find('label').attr('for', checkbox.attr('id')); + }); + }, + fieldSettingsCheckToggle: function (event) { + var input = event.data; + if ($(event.target).attr('checked') == true) { + // Show the field. + input.parent().children().not(event.target).not('label').slideDown(); + } else { + // Clear the field and hide it. + input.parent().find('input, textarea').val('').text('').trigger('change'); + input.parent().children().not(event.target).not('label').slideUp(); + } + }, + showFieldSettings: function (event) { + // Trigger a click on the field settings tab + $('#edit-ui-wrapper .horizontal-tabs-list > .last:not(.selected) a').click(); + }, + tabActivated: function (event, tab) { + tab.offset = tab.fieldset.offset().top; + if (typeof Drupal.toolbar != 'undefined') { + tab.offset = tab.offset - Drupal.toolbar.height(); + } + Drupal.behaviors.webformAltUI.activeTab = tab; + Drupal.behaviors.webformAltUI.scrollFieldset(); + + $('#form-builder').css('min-height', tab.fieldset.height()); + }, + windowScroll: function (event) { + if (Drupal.behaviors.webformAltUI.timeout) { + clearTimeout(Drupal.behaviors.webformAltUI.timeout); + } + Drupal.behaviors.webformAltUI.timeout = setTimeout(Drupal.behaviors.webformAltUI.scrollFieldset, 100); + }, + scrollFieldset: function () { + var fieldset = Drupal.behaviors.webformAltUI.activeTab.fieldset; + var offset = Drupal.behaviors.webformAltUI.activeTab.offset; + // Do not move the palette while dragging a field. + if (Drupal.formBuilder.activeDragUi) { + return; + } + + var windowOffset = $(window).scrollTop(); + var blockHeight = fieldset.height(); + var formBuilderHeight = $('#form-builder').height(); + if (windowOffset - offset > 0) { + // Do not scroll beyond the bottom of the editing area. + var toolbarOffset = parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + 10; + var newTop = Math.min(windowOffset - offset + toolbarOffset, formBuilderHeight - blockHeight); + fieldset.animate({'margin-top': (newTop + 'px')}, 'fast'); + } + else { + fieldset.animate({'margin-top': '0px'}, 'fast'); + } + }, + removeField: function (event) { + event.preventDefault(); + event.stopPropagation(); + Drupal.formBuilder.setActive($(event.target).parents('.form-builder-wrapper').get(0), event.target); + Drupal.formBuilder.ajaxOptions = { + url: $(this).attr('href'), + type: 'POST', + dataType: 'json', + data: 'js=1', + success: Drupal.behaviors.webformAltUI.removeFieldCallback, + error: Drupal.formBuilder.ajaxError, + errorMessage: Drupal.t('Field could not be deleted at this time. Please try again later.'), + maxTry: 3, + tryCount: 0 + }; + // Submit the form via ajax + $.ajax(Drupal.formBuilder.ajaxOptions); + }, + removeFieldCallback: function (response) { + Drupal.formBuilder.deleteField(Drupal.behaviors.webformAltUI.checkForm); + }, + submitTextChange: function (e) { + // Put the value of this field into the submit button. + var text = $(this).val() ? $(this).val() : Drupal.t('Submit'); + $('#form-builder .button').text(text); + // Put the value into the hidden field in the form settings advanced tab + $('input[name=submit_text]').val($(this).val()); + }, + checkForm: function () { + var fb = $('#form-builder'); + if (fb.children().length <= 1) { + $('<div class="form-builder-placeholder empty-form">' + Drupal.t('Drag a field from the Add field tab to add it to your webform') + '</div>').prependTo(fb); + } else { + // Ensure that the submit button is always at the bottom of the form + fb.find('.button').closest('.form-builder-wrapper').appendTo(fb); + } + }, + removeNestedForms: function (submitEvent) { + $(submitEvent.target).find('form').remove(); + return true; + } +}; + +})(jQuery); diff --git a/sites/all/modules/webform_alt_ui/webform_alt_ui.module b/sites/all/modules/webform_alt_ui/webform_alt_ui.module new file mode 100644 index 00000000..cdcc409b --- /dev/null +++ b/sites/all/modules/webform_alt_ui/webform_alt_ui.module @@ -0,0 +1,1680 @@ +<?php + +/** + * @file + * Improves the UX of the webform module by adding the form building tools to + * the node edit screen. + */ + +define('WEBFORM_ALT_UI_FILE_WIDTH', 30); + +/** + * Implements hook_init() + */ +function webform_alt_ui_init() { + drupal_add_css(drupal_get_path('module', 'webform_alt_ui') . '/css/webform_alt_ui.client.css', array('every_page' => TRUE)); +} + +/** + * Implements hook_menu(). + */ +function webform_alt_ui_menu() { + $items = array(); + + $items['admin/webform_alt_ui/submit_button_form'] = array( + 'title' => 'Webform submit button form', + 'description' => 'Callback for the webform submit button form.', + 'page callback' => 'webform_alt_ui_submit_button_page', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Implements hook_menu_alter(). + */ +function webform_alt_ui_menu_alter(&$items) { + // Remove these items from the local tasks. We make these actions available + // as buttons at the bottom of the page. + $items['node/%webform_menu/webform-results/download']['type'] = MENU_CALLBACK; + $items['node/%webform_menu/webform-results/clear']['type'] = MENU_CALLBACK; + + // Override result listing callback so we can add form elements. + $items['node/%webform_menu/webform-results']['page callback'] = 'webform_alt_ui_results_submissions'; + unset($items['node/%webform_menu/webform-results']['file']); + + // Remove access to the webform tab. + $items['node/%webform_menu/webform']['access callback'] = FALSE; + + // Change the callback for removing a form element. + $items['admin/build/form-builder/remove'] = array( + 'title' => 'Remove field', + 'description' => 'Remove a field from a form.', + 'page callback' => 'webform_alt_ui_remove_element', + 'access callback' => 'form_builder_menu_field_access', + 'access arguments' => array('remove', 4, 5, 6), + 'type' => MENU_CALLBACK, + ); + + // Hide the Table view from the results page + $items['node/%webform_menu/webform-results/table']['access callback'] = FALSE; + + // Alter the link description on the admin/config page. + $items['admin/config/content/webform']['description'] = 'Configure webform settings and defaults.'; + + // Alter the confirmation page. + $items['node/%webform_menu/done']['page callback'] = '_webform_alt_ui_confirmation'; +} + +/* + * The form_builder_positions form needs to be on the page but not within the + * node edit form. + */ +function webform_alt_ui_page_alter(&$page) { + if (isset($page['content']['system_main']['#node_edit_form']) && isset($page['content']['system_main']['webform'])) { + // If this is a new node, there will be no nid, so a temporary form_builder id + // is created from the form token. + $nid = isset($page['content']['system_main']['nid']['#value']) ? $page['content']['system_main']['nid']['#value'] : $page['content']['system_main']['#form_builder_token']; + + // Set the active form + form_builder_active_form('webform', $nid); + // Load the current state of the form, or create a new cache if needed. + $form_structure = form_builder_cache_load('webform', $nid); + if (!$form_structure) { + $form_structure = form_builder_load_form('webform', $nid); + form_builder_cache_save('webform', $nid, $form_structure); + } + + $page['content'][] = drupal_get_form('form_builder_positions', $form_structure, 'webform', $nid); + } +} + +/** + * Implements hook_modules_installed(). + */ +function webform_alt_ui_modules_installed($modules) { + // The Webform module creates a body field for the webform content type, but + // we don't want that in our UI by default since we put the form for adding + // components on the main node edit page, and it gets in the way of that. + if (in_array('webform', $modules) && ($instance = field_info_instance('node', 'body', 'webform'))) { + field_delete_instance($instance); + } +} + +/** + * Implements hook_theme(). + */ +function webform_alt_ui_theme() { + return array( + 'webform_alt_ui_field_configure' => array( + 'render element' => 'form', + ), + 'webform_alt_ui_field_palette' => array( + 'variables' => array('fields' => NULL, 'groups' => NULL, 'form_type' => NULL, 'form_id' => NULL), + ), + 'webform_alt_ui_element_links' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_fieldset_wrapper' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_element_markup' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_check_expand_wrapper' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_confirmation' => array( + 'variables' => array('node' => NULL, 'sid' => NULL, 'link_to_form' => TRUE), + ), + 'webform_alt_ui_text_format_wrapper' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_admin_settings' => array( + 'render element' => 'element', + ), + 'webform_alt_ui_submission_navigation' => array( + 'variables' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL), + ), + 'webform_alt_ui_submission_information' => array( + 'variables' => array('node' => NULL, 'submission' => NULL), + ), + ); +} + +/* + * Implements hook_element_info_alter(); + */ +function webform_alt_ui_element_info_alter(&$type) { + $type['options']['#pre_render'][] = 'webform_alt_ui_pre_render_option'; +} + +/* + * Implements hook_form_alter(). + * + * This form alter adds the fields and handling for the separate webform_configure_form + * to the node edit form + */ +function webform_alt_ui_form_alter(&$form, &$form_state, $form_id) { + // Perform the same check as webform_form_alter(). + if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types'))) { + + form_load_include($form_state, 'inc', 'form_builder', 'includes/form_builder.cache'); + form_load_include($form_state, 'inc', 'form_builder', 'includes/form_builder.admin'); + form_load_include($form_state, 'inc', 'form_builder', 'includes/form_builder.api'); + form_load_include($form_state, 'inc', 'webform', 'includes/webform.pages'); + form_load_include($form_state, 'inc', 'webform', 'includes/webform.components'); + + // Modify the form. + _webform_alt_ui_horizontal_tabs($form, $form_state); + _webform_alt_ui_webform_settings($form, $form_state); + _webform_alt_ui_webform_email($form, $form_state); + _webform_alt_ui_webform_form($form, $form_state); + + // Add validation and submission handlers. (Our custom validation must go + // at the beginning, since it is responsible for adding some elements to + // the $form_state['values'] array based on other form values which were + // submitted.) + $form['#validate'][] = 'webform_configure_form_validate'; + $form['#validate'] = array_merge(array('webform_alt_ui_pre_validate'), $form['#validate']); + $form['actions']['submit']['#submit'][] = 'webform_alt_ui_prepare_configure_form_submit'; + $form['actions']['submit']['#submit'][] = 'webform_configure_form_submit'; + $form['actions']['submit']['#submit'][] = 'webform_alt_ui_submit'; + + // Add a pre-render function to reorganize the form before it is displayed. + $form['#pre_render'][] = 'webform_alt_ui_pre_render_node_form'; + + // Attach our custom CSS and JavaScript. + $css_path = drupal_get_path('module', 'webform_alt_ui') . '/css/webform_alt_ui.css'; + $form['#attached']['css'][$css_path] = array('preprocess' => FALSE, 'group' => CSS_DEFAULT, 'weight' => 2); + $form['#attached']['css'][] = drupal_get_path('module', 'webform') . '/css/webform.css'; + $form['#attached']['js'][] = drupal_get_path('module', 'webform_alt_ui') . '/webform_alt_ui.js'; + $form['#attached']['js'][] = drupal_get_path('module', 'webform_alt_ui') . '/js/webform_alt_ui.file.js'; + + // Need to add the vertical accordion files here because form_builder + // does not use D7's AJAX framework. + drupal_add_js(drupal_get_path('module', 'ux_elements') . '/js/vertical_accordion.js', array('group' => 'JS_DEFAULT', 'weight' => 50)); + drupal_add_css(drupal_get_path('module', 'ux_elements') . '/css/vertical_accordion.css'); + drupal_add_library('system', 'ui.accordion'); + + // Remove the preview button and add a cancel button + unset($form['actions']['preview']); + $form['actions']['cancel'] = array( + '#type' => 'button', + '#value' => t('Cancel'), + '#weight' => 6, + '#submit' => array('webform_alt_ui_click_cancel'), + '#limit_validation_errors' => array(), + '#executes_submit_callback' => TRUE, + ); + + // Add the form_builder cache clear to the delete action + $form['actions']['delete']['#submit'][] = 'webform_alt_ui_clear_cache'; + } +} + +/** + * Validation handler for webform_node_form. + * + * This function puts the appropriate values into $form_state['values'] based + * on our custom controls, so that they can be appropriately handled by + * validation and submission handlers that run later. + * + * @todo Should this be moved to an #after_build callback instead? + */ +function webform_alt_ui_pre_validate($form, &$form_state) { + // If the "customize confirmation" checkbox was unchecked, revert to default + // values of everything inside it. + if (empty($form_state['values']['confirm_redirect_toggle'])) { + $form_state['values']['confirmation']['value'] = webform_alt_ui_default_confirmation_text(); + $form_state['values']['redirect'] = 'confirmation'; + // Keep the text format the same as whatever was passed in; it doesn't make + // sense to revert that to filter_default_format() since the default format + // can be different for different users who might be editing this form. + // (Given that our default text doesn't have any formatting, this should + // work fine anyway.) + } + else { + // If we are redirecting to a confirmation page, store the message that was + // entered for the content of that page. + if ($form_state['values']['redirect'] == 'confirmation') { + $form_state['values']['confirmation'] = $form_state['values']['confirmation_page_content']; + } + // Otherwise, if the confirmation was toggled off, do not save it so as to + // force there to be no confirmation. + elseif (empty($form_state['values']['confirmation_toggle'])) { + $form_state['values']['confirmation']['value'] = ''; + } + } + + // Use the values of the submit limit toggle to set the value of the "enforce + // limit" setting. + $form_state['values']['enforce_limit'] = $form_state['values']['submit_limit_toggle'] ? 'yes' : 'no'; +} + +/** + * Submit handler that prepares for the webform configuration submit handler. + * + * This submit handler runs before webform_configure_form_submit() and prepares + * the submitted form values for the expectations of that function. + */ +function webform_alt_ui_prepare_configure_form_submit($form, &$form_state) { + // There is a conflict in the expected form values, since 'status' is used by + // the node form and also by the webform configuration form which we attach + // to it. So we use a different key in the form, and therefore need to switch + // the submitted form values here so that webform's submit handler receives + // what it expects. It is OK to overload 'status' in the submitted form + // values at this point, since the node form submit handler has already run. + // See _webform_alt_ui_webform_settings(). + $form_state['values']['status'] = $form_state['values']['allow_submissions']; + unset($form_state['values']['allow_submissions']); +} + +/** + * Submit handler for webform_node_form. + */ +function webform_alt_ui_submit($form, &$form_state) { + // Save the form builder form associated with the node. + $nid = $form_state['values']['nid']; + $node = node_load($nid); + + // Custom implementation of form_builder_webform_save_node(). This is necessary + // because form_builder presumes the node to already exist when it is saving. + // The cache key has already been set as $form['#form_builder_token'] during + // the form alter process, so we can use that to properly get the cached form + // for the save process. + $form_cache = form_builder_cache_load('webform', $form['#form_builder_token']); + $element_ids = form_builder_preview_prepare($form_cache, 'webform', $form['#form_builder_token']); + + // Save modified or created components. + foreach ($element_ids as $id) { + form_builder_webform_save_component($node, $id, $form_cache); + } + + // Delete components that have been removed. + foreach ($node->webform['components'] as $component) { + $element_id = 'cid_' . $component['cid']; + if (!in_array($element_id, $element_ids)) { + webform_component_delete($node, $component); + } + } + + // Reset the node cache, since $node->webform['components'] has changed. + entity_get_controller('node')->resetCache(array($node->nid)); + + // Save submission email settings. + _webform_alt_ui_save_submission_email($form, $form_state, $node); + + // Handle Mollom spam protection. + if (module_exists('mollom')) { + $mollom_form_id = _webform_alt_ui_mollom_form_id($nid); + $mollom_form = mollom_form_load($mollom_form_id); + // Turn spam protection on when requested to do so and it's currently off. + if ($form_state['values']['spam_protection'] && !$mollom_form) { + $mollom_form = mollom_form_new($mollom_form_id); + mollom_form_save($mollom_form); + } + // Turn spam protection off when requested to do so and it's currently on. + elseif (!$form_state['values']['spam_protection'] && $mollom_form) { + mollom_form_delete($mollom_form_id); + } + // If the webform already has spam protection and is keeping it, update the + // protection based on the components that were added or removed. + elseif ($form_state['values']['spam_protection']) { + // New Mollom forms protect all available fields that are allowed to have + // spam protection, so use that to determine what's available to be + // enabled. + $new_form = mollom_form_new($mollom_form_id); + $mollom_fields_not_yet_enabled = array_diff($new_form['enabled_fields'], $mollom_form['enabled_fields']); + if (!empty($mollom_fields_not_yet_enabled)) { + // Enable the available fields only if they correspond to a webform + // component that was just added (otherwise the administrator turned + // Mollom off for this field on purpose, so we don't want to turn it + // back on). Note that the original $node was loaded before the new + // components were saved, so we can use that for comparison. + $new_node = node_load($nid); + $components_added = array_diff_key($new_node->webform['components'], $node->webform['components']); + foreach ($mollom_fields_not_yet_enabled as $field) { + $field_keys = explode('][', $field); + $form_key = end($field_keys); + foreach ($components_added as $component) { + if ($form_key == $component['form_key']) { + // If this is the first field we are adding Mollom protection to + // and there aren't any analysis checks saved for the form, add + // the default spam checking. + if (empty($mollom_form['enabled_fields']) && empty($mollom_form['checks'])) { + $mollom_form['checks'] = array('spam'); + } + // Add the new field and move on to the next available one that + // isn't enabled. + $mollom_form['enabled_fields'][] = $field; + break 1; + } + } + } + } + // Remove protection for any fields that are no longer able to be + // protected (most likely because the webform component associated with + // it was removed). + $mollom_form['enabled_fields'] = array_values(array_intersect($mollom_form['enabled_fields'], $new_form['enabled_fields'])); + // Save the updated form. + mollom_form_save($mollom_form); + } + } + + // Remove the cached form_builder form. + form_builder_cache_delete('webform', $form['#form_builder_token']); + + $form_state['redirect'] = 'node/' . $nid; + // The message set by node_form_submit() is first and most relevant so we + // reduce the messages to that. + $_SESSION['messages']['status'] = array(array_shift($_SESSION['messages']['status'])); +} + +/** + * Helper function to save submission email settings. + */ +function _webform_alt_ui_save_submission_email(&$form, &$form_state, &$node) { + module_load_include('inc', 'webform', 'includes/webform.emails'); + $email = $form_state['values']['submission_email']; + $email['nid'] = $node->nid; + $email['from_name'] = 'default'; + $email['from_address'] = 'default'; + $email['excluded_components'] = array(); + $email['html'] = FALSE; + $email['attachments'] = 0; + + $send_email = $form_state['values']['submission_email_checkbox']; + if ($send_email) { + empty($email['eid']) ? webform_email_insert($email) : webform_email_update($email); + } + else { + if (isset($email['eid'])) { + webform_email_delete($node, $email); + } + } +} + +/** + * Helper function to determine the Mollom form ID for a webform client form. + * + * This needs to be kept in sync with the value used by the Webform module + * itself in webform_mollom_form_list(). + * + * @param $nid + * The node ID of the form. + * + * @return + * The Mollom form ID associated with the form. + */ +function _webform_alt_ui_mollom_form_id($nid) { + return "webform_client_form_$nid"; +} + +/** + * Adds an initial set of horizontal tabs to the node edit form. + */ +function _webform_alt_ui_horizontal_tabs(&$form, &$form_state) { + $form['ui-wrapper'] = array( + '#type' => 'item', + ); + $form['ui-wrapper']['h-tabs'] = array( + '#type' => 'horizontal_tabs', + ); +} + +/** + * Adds the configuration settings tab to the node edit form. + */ +function _webform_alt_ui_webform_settings(&$form, &$form_state) { + // Add a tab to hold the form settings. + $form['form_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Form settings'), + '#group' => 'additional_settings', + '#weight' => $form['menu']['#weight'] -1, + ); + + // Add the default webform configuration form to the node edit form. We will + // alter what we just added in the rest of this function below. + $node = $form['#node']; + $form += webform_configure_form($form, $form_state, $node); + + // Collapse the submission settings fieldset by default and remove the submit + // button, since this section will be transformed into a secondary item + // attached to the overall node form. + $form['submission']['#collapsed'] = TRUE; + unset($form['submit']); + + // Perform adjustments to each section of the form. + _webform_alt_ui_alter_confirm_redirect_settings($form, $form_state, $node); + _webform_alt_ui_alter_submit_limit_settings($form, $form_state, $node); + _webform_alt_ui_add_custom_submission_settings($form, $form_state, $node); + _webform_alt_ui_alter_role_control_settings($form, $form_state, $node); + _webform_alt_ui_alter_advanced_settings($form, $form_state, $node); + + // Avoid a conflict between the 'status' field on the node form and the one + // added by the webform configuration form which we are attaching. See + // webform_alt_ui_prepare_configure_form_submit(). + $form['submission']['allow_submissions'] = $form['submission']['status']; + unset($form['submission']['allow_submissions']['#parents']); + unset($form['submission']['status']); + // @todo: Show this (somewhere) once we have a plan to integrate it into this + // module's UI. + $form['submission']['allow_submissions']['#access'] = FALSE; +} + +/** + * Helper function for altering part of the webform settings form. + */ +function _webform_alt_ui_alter_confirm_redirect_settings(&$form, &$form_state, $node) { + // Remove all theming from the redirection settings; for us it is just an + // array container to hold other form elements that will be displayed. + foreach (element_properties($form['submission']['redirection']) as $property) { + unset($form['submission']['redirection'][$property]); + } + + // Transform the radio buttons for the redirect options into a select list, + // with our custom labels. + $element = &$form['submission']['redirection']['redirect']; + $element['#type'] = 'select'; + $element['#options']['confirmation'] = t('Show standard confirmation page'); + $element['#options']['url'] = t('Redirect to a different page'); + $element['#options']['none'] = t('Stay on the same page'); + + // Make cosmetic adjustments to the "Redirect URL" textfield. + $element = &$form['submission']['redirection']['redirect_url']; + $element['#title_display'] = 'invisible'; + $element['#description'] = NULL; + $element['#field_prefix'] = t('Path:'); + // Make it appear only when the appropriate redirect option is chosen. + $element['#states'] = array( + 'visible' => array( + ':input[name="redirect"]' => array('value' => 'url'), + ), + ); + + // Make cosmetic adjustments to the "Confirmation message" textrea. + $confirmation = &$form['submission']['confirmation']; + $confirmation['#title_display'] = 'invisible'; + $confirmation['#description'] = NULL; + $confirmation['#rows'] = 3; + + // For a new webform, default both the confirmation message and confirmation + // page content elements to a standard message. + $confirmation_default = webform_alt_ui_default_confirmation_text(); + if (_webform_alt_ui_node_webform_is_new($node)) { + $confirmation['#default_value'] = $confirmation_default; + $confirmation_page_default = $confirmation_default; + } + else { + // Otherwise, inherit the same text for the confirmation page content as + // the confirmation message itself (since they share a database field). + $confirmation_page_default = $confirmation['#default_value']; + } + + // Create a container for the redirection and confirmation settings to be + // displayed together. They will actually be moved into the container during + // the pre-render stage; see webform_alt_ui_pre_render_node_form(). + $container_info = element_info('container'); + $form['submission']['confirm_redirect'] = array( + '#type' => 'container', + '#weight' => -10, + // Add a checkbox at the top level of this container. This will toggle the + // entire set of confirmation and redirection settings. + '#theme_wrappers' => array_merge(array('webform_alt_ui_check_expand_wrapper'), $container_info['#theme_wrappers']), + 'confirm_redirect_toggle' => array( + '#type' => 'checkbox', + '#title' => t('Customize confirmation'), + '#default_value' => !_webform_alt_ui_node_webform_is_new($node) && ($node->webform['confirmation'] != $confirmation_default || $node->webform['redirect_url'] != '<confirmation>'), + '#attributes' => array('class' => array('check-expander')), + ), + // Add a container for the settings themselves. Since this is the only + // other element in the top level of the container (besides the toggle + // itself), everything in here will be automatically hidden when the + // checkbox is toggled. + 'settings_container' => array( + '#type' => 'container', + // The redirect options will be moved in here during the pre-render + // stage. + 'redirection' => array(), + // Add a container for the confirmation message. + 'confirmation_message_container' => array( + '#type' => 'container', + // Hide this container when the appropriate redirect option is chosen. + '#states' => array( + 'invisible' => array( + ':input[name="redirect"]' => array('value' => 'confirmation'), + ), + ), + // Add a checkbox that toggles the "Confirmation message" textarea. + '#theme_wrappers' => array_merge(array('webform_alt_ui_check_expand_wrapper'), $container_info['#theme_wrappers']), + 'confirmation_toggle' => array( + '#type' => 'checkbox', + '#title' => t('Show a confirmation message'), + '#default_value' => !empty($confirmation['#default_value']), + '#attributes' => array('class' => array('check-expander')), + ), + // The confirmation textarea will be moved in here during the + // pre-render stage. + 'confirmation' => array(), + ), + // Add a container for the confirmation message. + 'confirmation_page_container' => array( + '#type' => 'container', + // Hide this container when the appropriate redirect option is chosen. + '#states' => array( + 'visible' => array( + ':input[name="redirect"]' => array('value' => 'confirmation'), + ), + ), + // Duplicate the above textarea into one that will store the page body + // (for the case where we are redirecting to a standard confirmation + // page). + 'confirmation_page_content' => array_merge($form['submission']['confirmation'], array( + '#title_display' => 'before', + '#title' => t('Page body'), + '#default_value' => $confirmation_page_default, + '#parents' => array('confirmation_page_content'), + )), + ), + ), + ); +} + +/** + * Helper function for altering part of the webform settings form. + */ +function _webform_alt_ui_alter_submit_limit_settings(&$form, &$form_state, $node) { + // Remove all theming from the submission limit settings; for us it is just + // an array container to hold other form elements that will be displayed. + foreach (element_properties($form['submission']['submit_limit']) as $property) { + unset($form['submission']['submit_limit'][$property]); + } + + // Add a checkbox that toggles the submissions limit, and hide the existing + // radio selector. + $form['submission']['submit_limit']['#theme_wrappers'][] = 'webform_alt_ui_check_expand_wrapper'; + $form['submission']['submit_limit']['submit_limit_toggle'] = array( + '#type' => 'checkbox', + '#title' => t('Limit submissions'), + '#default_value' => $node->webform['submit_limit'] != -1, + '#weight' => -5, + '#attributes' => array('class' => array('check-expander')), + ); + $form['submission']['submit_limit']['enforce_limit']['#access'] = FALSE; + + // Have the default limit be 1 + $form['submission']['submit_limit']['submit_limit']['#default_value'] = $node->webform['submit_limit'] != -1 ? $node->webform['submit_limit'] : 1; + + // Other cosmetic adjustments. + $form['submission']['submit_limit']['submit_limit']['#prefix'] = '<div class="limit-wrapper">' . t('Limit to: '); + $form['submission']['submit_limit']['submit_interval']['#prefix'] = ' ' . t('submission(s)') . ' '; + $form['submission']['submit_limit']['submit_interval']['#suffix'] = '</div>'; +} + +/** + * Helper function for adding custom submission settings to the webform form. + */ +function _webform_alt_ui_add_custom_submission_settings(&$form, &$form_state, $node) { + $mollom_enabled = module_exists('mollom'); + $form['submission']['spam_protection'] = array( + '#type' => 'checkbox', + '#title' => t('Enable spam protection (<a href="@url">Mollom</a>)', array('@url' => url('admin/help/mollom'))), + // New webforms have spam protection enabled by default. + '#default_value' => _webform_alt_ui_node_webform_is_new($node) || ($mollom_enabled && mollom_form_load(_webform_alt_ui_mollom_form_id($node->nid))), + // If Mollom is off it doesn't matter what this checkbox is set to, since + // the form submit handler won't do anything with it anyway. So just hide + // it. + '#access' => $mollom_enabled, + '#weight' => -8, + ); + + _webform_alt_ui_add_submission_email_settings($form, $form_state, $node); + + $form['submission']['#collapsible'] = FALSE; + $form['submission']['#collapsed'] = FALSE; +} + +/** + * Helper function to add basic submission email settings to the form. + */ +function _webform_alt_ui_add_submission_email_settings(&$form, &$form_state, &$node) { + $existing_email = reset($node->webform['emails']); + $form['submission']['submission_email_checkbox'] = array( + '#type' => 'checkbox', + '#title' => t('Send a confirmation e-mail'), + '#default_value' => !empty($existing_email), + ); + $dependent_on_checkbox = array( + 'visible' => array( + ':input[name="submission_email_checkbox"]' => array('checked' => TRUE), + ), + ); + + $form['submission']['submission_email']['#tree'] = TRUE; + $form['submission']['submission_email']['eid'] = array( + '#type' => 'value', + '#value' => ($existing_email ? $existing_email['eid'] : NULL), + ); + $form['submission']['submission_email']['email'] = array( + '#type' => 'textfield', + '#title' => t('To'), + '#default_value' => $existing_email ? $existing_email['email'] : variable_get('site_mail', ''), + '#states' => $dependent_on_checkbox, + ); + $form['submission']['submission_email']['subject'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#default_value' => $existing_email ? $existing_email['subject']: t('Webform submission received'), + '#states' => $dependent_on_checkbox, + ); + $form['submission']['submission_email']['template'] = array( + '#type' => 'textarea', + '#title' => t('Body'), + '#default_value' => $existing_email ? $existing_email['template'] : t('A visitor completed a webform on your site. Please visit your website and click the webform\'s "Results" tab to review the submission.'), + '#states' => $dependent_on_checkbox, + ); + $form['#validate'][] = '_webform_alt_ui_validate_submission_email'; + +} + +/** + * Validation function for the email address in the submission settings section. + */ +function _webform_alt_ui_validate_submission_email(&$form, &$form_state) { + if ($form_state['values']['submission_email_checkbox'] && !valid_email_address($form_state['values']['submission_email']['email'])) { + form_set_error('submission_email][email', t('The email address %mail is not valid.', array('%mail' => $form_state['values']['submission_email']['email']))); + } +} + +/** + * Helper function for altering part of the webform settings form. + */ +function _webform_alt_ui_alter_role_control_settings(&$form, &$form_state, $node) { + // Cosmetic adjustments. + $form['role_control']['#collapsed'] = TRUE; + $form['role_control']['#description'] = NULL; + $form['role_control']['roles']['#title'] = t('Permit submission from:'); + $form['role_control']['roles']['#description'] = NULL; +} + +/** + * Helper function for altering part of the webform settings form. + */ +function _webform_alt_ui_alter_advanced_settings(&$form, &$form_state, $node) { + // Hide descriptions from all checkboxes within the advanced settings + // fieldset. + foreach (element_children($form['advanced']) as $key) { + $form['advanced'][$key]['#description'] = NULL; + } + + // Alter titles + $form['advanced']['submit_notice']['#title'] = t("Display a link to previous submissions"); + $form['advanced']['block']['#title'] = t("Create a block"); + + // Hide options + $form['advanced']['allow_draft']['#access'] = FALSE; + $form['advanced']['auto_save']['#access'] = FALSE; + + // Make cosmetic adjustments to the "Submit button text" textfield. + $form['advanced']['submit_text']['#default_value'] = !empty($node->webform['submit_text']) ? $node->webform['submit_text'] : t('Submit'); + $form['advanced']['submit_text']['#type'] = 'hidden'; +} + +/** + * Adds the email form to the Form settings tab + */ +function _webform_alt_ui_webform_email(&$form, &$form_state) { + // This is just a stub function. The email form has not been implemented yet. +} + +/** + * Adds the Add field and Field settings tabs and the live preview + */ +function _webform_alt_ui_webform_form(&$form, &$form_state) { + // If this is a new node, there will be no nid, so a temporary form_builder id + // is created for the user. This does mean that a user can only create 1 webform + // concurrently. + $nid = isset($form['#node']->nid) ? $form['#node']->nid : substr($form['form_token']['#default_value'], 0 , 32); + $form['#form_builder_token'] = $nid; + + // Load the current state of the form, or create a new cache if needed. + $form_structure = form_builder_cache_load('webform', $nid); + if (!$form_structure) { + $form_structure = form_builder_load_form('webform', $nid); + form_builder_cache_save('webform', $nid, $form_structure); + } + + // Add the preview to the form. We need to manually call our alter hook on it + // since we are not using drupal_get_form() to load it (we are already inside + // a different form). + // @todo: Refactor this code to remove that restriction. + $form_builder_preview_form = form_builder_preview($form, $form_state, $form_structure, 'webform', $nid); + $form_id = 'form_builder_preview'; + drupal_alter(array('form', "form_{$form_id}"), $form_builder_preview_form, $form_state, $form_id); + $form['ui-wrapper']['live_preview'] = $form_builder_preview_form; + + // Prevent all form elements in the "live preview" from being validated when + // this form is submitted. + webform_alt_ui_prevent_validation($form['ui-wrapper']['live_preview']); + + // Add a submit button to the preview. + $button_text = $form['webform']['#value']['submit_text'] ? check_plain($form['webform']['#value']['submit_text']) : t('Submit'); + $path = 'admin/webform_alt_ui/submit_button_form'; + if (isset($form['#node']->nid)) { + $path .= '/' . $form['#node']->nid; + } + $form['ui-wrapper']['live_preview']['submit-preview'] = array( + '#type' => 'markup', + '#markup' => l($button_text, $path, array('attributes' => array('class' => array('configure', 'button')))), + '#weight' => count(element_children($form['ui-wrapper']['live_preview'])) + 1000, + '#theme_wrappers' => array('webform_element', 'form_builder_element_wrapper'), + '#form_builder' => array( + 'configurable' => FALSE, + 'removable' => FALSE, + ), + ); + + // Add a tab for Add field + $form['add_field'] = array( + '#type' => 'fieldset', + '#title' => 'Add field', + '#weight' => 1, + '#group' => 'h-tabs', + ); + + // Load the form_builder fields into the tab + $fields = form_builder_get_form_type('webform'); + $groups = module_invoke_all('form_builder_palette_groups'); + // TODO: We shouldn't have to clear the cache here. + $active_fields = form_builder_get_element_ids($form_structure); + foreach ($fields as $key => $field) { + if ($field['unique'] && in_array($key, $active_fields)) { + $fields[$key]['in_use'] = TRUE; + } + if ($field['addable'] == FALSE) { + unset($fields[$key]); + } + } + + $form['add_field']['fields'] = array( + '#markup' => theme('webform_alt_ui_field_palette', array('fields' => $fields, 'groups' => $groups, 'form_type' => 'webform', 'form_id' => $nid)), + ); + + // Add a tab for Field settings + $form['field_settings'] = array( + '#type' => 'fieldset', + '#title' => 'Field settings', + '#weight' => 2, + '#group' => 'h-tabs', + ); + + $form['field_settings']['placeholder'] = array( + '#markup' => '<div id="form-builder-field-configure" class="form-builder-field-configure"><div class="field-settings-message">' . t('No field selected') . '</div></div>', + ); +} + +/** + * Pre-render function to reorganize the webform node form before theming it. + * + * This module significantly alters the structure of the webform node form, but + * we do not want to alter the structure while the form is being built (since + * that would invalidate the code that runs within existing webform submit + * handlers, etc). So we only alter the structure immediately before the form + * is rendered. + */ +function webform_alt_ui_pre_render_node_form($form) { + // Put the redirection and confirmation settings in the correct place within + // their overall container. + $form['submission']['confirm_redirect']['settings_container']['redirection'] = $form['submission']['redirection']; + unset($form['submission']['redirection']); + $form['submission']['confirm_redirect']['settings_container']['confirmation_message_container']['confirmation'] = $form['submission']['confirmation']; + unset($form['submission']['confirmation']); + + // Put the submission settings, submission access settings, and advanced + // settings inside the form settings horizontal tab. + foreach (array('submission', 'role_control', 'advanced') as $key) { + $form['form_settings'][$key] = $form[$key]; + $form[$key]['#printed'] = TRUE; + } + + return $form; +} + +/** + * Returns default text for the webform confirmation message/page content. + */ +function webform_alt_ui_default_confirmation_text() { + return t('Thank you, your submission has been received.'); +} + +/** + * Determines if a node's webform is being edited for the first time. + * + * This function is intended to be called from within the webform alternate UI + * to determine if the node has been saved from this UI previously. + * + * @param $node + * The fully-loaded node object. + * + * @return + * TRUE for new nodes, or nodes that are being edited for the first time + * after their node type has become webform-enabled. + */ +function _webform_alt_ui_node_webform_is_new($node) { + // For now, the best "flag" we have to check for an existing node which is + // being edited for the first time after it was webform-enabled is to check + // the confirmation format, since this is null by default for new webforms + // but non-null after the user has saved the form and made a choice. This + // means the function will (usually) return false if the node has only been + // saved programmatically since becoming webform-enabled (and never saved via + // the UI), which is what we want. + // + // @todo Come up with a cleaner and more foolproof way to detect this + // condition. + return empty($node->nid) || !isset($node->webform['confirmation_format']); +} + +/** + * Traverse a form array and set all elements within it to skip validation. + * + * This is done recursively, so that, for example, elements within fieldsets + * are also found. + */ +function webform_alt_ui_prevent_validation(&$form) { + $form['#validated'] = TRUE; + foreach (element_children($form) as $key) { + webform_alt_ui_prevent_validation($form[$key]); + } +} + +/** + * Helper function; adds a process function to simplify text format selectors. + * + * @param $element + * The 'text_format' element to which the process function will be attached. + */ +function _webform_alt_ui_add_text_format_simplification(&$element) { + $element['#process'][] = 'webform_alt_ui_simplify_text_format_selector'; + $info = element_info('text_format'); + if (!empty($info['#process'])) { + $element['#process'] = array_merge($info['#process'], $element['#process']); + } +} + +/** + * Form process function to deny access to parts of a text format selector. + * + * This runs after an element of type '#text_format' is processed, and prevents + * parts of the format selector from actually displaying (except those that we + * want to display). + */ +function webform_alt_ui_simplify_text_format_selector($element) { + // Simplify the format selector (e.g., remove the formatting guidelines). + $element['format'] = $element['format']['format']; + + // Allow the format selector to inherit JavaScript behaviors from the overall + // element. + // @todo: Determine if this should be in core (as a bugfix)? + if (isset($element['#states'])) { + $element['format'] += array('#states' => array()); + $element['format']['#states'] = array_merge($element['#states'], $element['format']['#states']); + } + + // Remove custom theming and rendering from the overall element. + unset($element['#theme_wrappers']); + unset($element['#pre_render']); + + // Add a custom theme wrapper. + $element['#theme_wrappers'][] = 'webform_alt_ui_text_format_wrapper'; + + return $element; +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Reorganize the Field settings tab. + * + * This adds a pre_render callback to display the form as a vertical_accordion. + * Grouping and strings for properties are changed as well as adding checkboxes + * to toggle the display of the inputs for the appropriate properties. + */ +function webform_alt_ui_form_form_builder_field_configure_alter(&$form, &$form_state, $form_id) { + // Restring labels. + $form['title']['#title'] = t('Label'); + $form['title_display']['#title'] = t('Label alignment'); + $form['description']['#title'] = t('Help text'); + // Alter the groupings. + $form['weight']['#form_builder']['property_group'] = 'default'; + // Add a pre_render callback that will convert the property groups into + // vertical_accordions. + $form['#pre_render'][] = 'webform_alt_ui_pre_render_field_configure'; + + // Set a default of showing a checkbox for the properties in this form. + $holder = array(); + $keys = element_children($form); + foreach($keys as $key) { + if (!in_array($form[$key]['#type'], array('checkbox', 'hidden', 'button', 'submit')) && !$form[$key]['#required']) { + $form[$key]['#checkbox'] = TRUE; + } + } + + // Call functions that alter the field configuration form specific to the field type + form_load_include($form_state, 'inc', 'webform_alt_ui', 'includes/webform_alt_ui.components'); + $func = '_webform_alt_ui_' . $form['#_edit_element']['#type'] . '_configure'; + if (function_exists($func)) { + $form = call_user_func($func, $form); + } + + // Add a class to mark the appropriate properties to receive a checkbox + foreach($keys as $key) { + if (!empty($form[$key]['#checkbox'])) { + $form[$key]['#attributes']['class'][] = 'check-toggle'; + } + } +} + +/** + * Add the property groups in the field configure form to a vertical accordion. + */ +function webform_alt_ui_pre_render_field_configure($form) { + $keys = element_children($form); + + // Hide the form_key value + $form['default_property_group']['key']['#access'] = FALSE; + + // Create the vertical accordion. + $form['vertical_accordion'] = array( + '#type' => 'vertical_accordion', + ); + + // Iterate through the form's children. + foreach($keys as $key) { + $element = $form[$key]; + // If the child is a fieldset, add it to the vertical_accordion. + if (isset($element['#type']) && $element['#type'] == 'fieldset') { + $form['vertical_accordion'][$key] = $element; + // Remove the original element this duplicates. + unset($form[$key]); + } + } + + return $form; +} + +/** + * Theme function to wrap our simplified text format selector with a class. + */ +function theme_webform_alt_ui_text_format_wrapper($variables) { + return '<div class="webform-alt-ui-simplified-text-format">' . $variables['element']['#children'] . '</div>'; +} + +/** + * In order to restring "Add a field" to "Fields" we need to re-implement this + * theme function. + * + * @todo: We should eventually patch the form_builder version of this theme + * function to call theme('item_list__form_builder_field_palette') instead of + * the generic theme('item_list'); then we can delete the function below and + * instead implement a preprocess hook for theme_item_list() which modifies + * $variables['title'] for this item list only. However, currently it is not + * possible to do that effectively, so we should revisit this issue once + * http://drupal.org/node/956520 is resolved. + */ +function theme_webform_alt_ui_field_palette($variables) { + extract($variables); + + $output = ''; + $lists = array(); + foreach ($fields as $field_name => $field) { + $class = array('field-' . $field_name); + $style = ''; + + // If a field is unique, add a special class to identify it. + if ($field['unique']) { + $class[] = 'form-builder-unique'; + $class[] = 'form-builder-element-' . $field_name; + + // If already in use, do not display it in the list. + if (!empty($field['in_use'])) { + $style = 'display: none;'; + } + } + + $lists[$field['palette_group']]['#weight'] = $groups[$field['palette_group']]['weight']; + $lists[$field['palette_group']][] = array( + 'data' => l($field['title'], 'admin/build/form-builder/add/' . $form_type . '/' . $form_id . '/' . $field_name, array('query' => drupal_get_destination())), + 'class' => $class, + 'style' => $style, + ); + } + + // Sort the lists by weight. + uasort($lists, 'element_sort'); + + $output .= '<div id="form-builder-field-palette">'; + foreach ($lists as $group => $list) { + unset($list['#weight']); + $output .= theme('item_list', array('items' => $list, 'title' => (count($lists) > 1) ? $groups[$group]['title'] : t('Fields'), 'type' => 'ul', 'attributes' => array('class' => array('form-builder-fields', 'clearfix')))); + } + $output .= '</div>'; + + return $output; +} + +/* + * Disable the form_builder default links and add theme wrappers to customize + * the markup of the form_builder preview elements + */ +function webform_alt_ui_form_builder_preview_alter(&$element, $form_type, $form_id) { + // Disable the remove and configure links. + $element['#form_builder']['removable'] = FALSE; + $element['#form_builder']['configurable'] = FALSE; + + // Adjust the theme wrappers depending on the item type. + if ($element['#type'] == 'fieldset') { + $key = array_search('fieldset', $element['#theme_wrappers']); + array_splice($element['#theme_wrappers'], $key+1, 0, 'webform_alt_ui_element_links'); + array_splice($element['#theme_wrappers'], $key+2, 0, 'webform_alt_ui_fieldset_wrapper'); + } + else { + // If there is a special theme wrapper for this element type, add it. + if (function_exists('theme_webform_alt_ui_element_' . $element['#type'])) { + array_unshift($element['#theme_wrappers'], 'webform_alt_ui_element_' . $element['#type']); + } + $key = array_search('webform_element', $element['#theme_wrappers']); + array_splice($element['#theme_wrappers'], $key+1, 0, 'webform_alt_ui_element_links'); + } + + if ($element['#type'] == 'file') { + $element['#size'] = $element['#webform_file_width']; + } +} + +/** + * Adds links after the contents of an element to trigger actions + * + * The duplicate link is currently inactive. + */ +function theme_webform_alt_ui_element_links(&$vars) { + $element = $vars['element']; + + // Add a list of links after the content of this element + $output = $element['#children']; + $output .= '<ul class="form-builder-links">'; + + // @todo: Add duplicate functionality + //$output .= '<li>'. l(t('Duplicate'), '#', array('html' => TRUE, 'attributes' => array('class' => array('duplicate'), 'title' => t('Duplicate')))) .'</li>'; + + $output .= '<li>'. l(t('Delete'), 'admin/build/form-builder/remove/' . $element['#form_builder']['form_type'] . '/' . $element['#form_builder']['form_id'] . '/' . $element['#form_builder']['element_id'], array('html' => TRUE, 'attributes' => array('class' => array('delete'), 'title' => t('Delete')))) . '</li>'; + $output .= '<li class="element-invisible">'. l(t('Configure'), 'admin/build/form-builder/configure/' . $element['#form_builder']['form_type'] . '/' . $element['#form_builder']['form_id'] . '/' . $element['#form_builder']['element_id'], array('html' => TRUE, 'attributes' => array('class' => array('configure'), 'title' => t('Configure')))) .'</li>'; + $output .= '</ul>'; + + return $output; +} + +/** + * Themes markup webform elements. + */ +function theme_webform_alt_ui_element_markup($vars) { + return '<div class="markup-wrapper">' . $vars['element']['#children'] . '</div>'; +} + +/** + * Adds a wrapper around fieldsets to create the highlight color but not affect the border + */ +function theme_webform_alt_ui_fieldset_wrapper($vars) { + return '<div class="fieldset-color-wrapper">' . $vars['element']['#children'] . '</div>'; +} + +/** + * Adds a wrapper to make an element expand based on the status of its first checkbox. + */ +function theme_webform_alt_ui_check_expand_wrapper($variables) { + $element = $variables['element']; + $output = '<div class="check-expand">' . $element['#children'] . '</div>'; + return $output; +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function webform_alt_ui_form_form_builder_preview_alter(&$form, &$form_state) { + $form['#pre_render'][] = 'webform_alt_ui_pre_process_preview'; + + // The Webform module uses webform_component_include() to lazy-load component + // functions when they're needed, but it fails to ensure that those files are + // also included when a cached form is processed (e.g., during + // form_builder_field_render()). + // @todo This should be fixed within Webform module, but is non-trivial, + // because webform_component_include() gets called from form and non-form + // contexts. When time permits, submit an appropriate patch to the Webform + // project. + if ($form_state['build_info']['args'][1] === 'webform') { + // @todo When the fix is moved to the Webform module, it should be possible + // to lazy-load files for only the component types used by the current + // form. But from within here, it's hard to determine what those are, so + // include all of them. + foreach (webform_components() as $component_info) { + // Copied from webform_component_include(), but replacing + // module_load_include() with form_load_include(). + if (isset($component_info['file'])) { + $pathinfo = pathinfo($component_info['file']); + $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']); + $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename; + form_load_include($form_state, $pathinfo['extension'], $component_info['module'], $path); + } + } + } +} + +function webform_alt_ui_pre_process_preview($form) { + // Hide the title + unset($form['#title']); + + // Disable the field palette we've already added it to the Add field tab. + $form['#block_enabled'] = TRUE; + + return $form; +} + +// == Page overrides =========================================================== + +/** + * Overrides webform_results_submissions() from webform.report.inc. + * + * This lets us tie in a form in the page generation process. + */ +function webform_alt_ui_results_submissions($node, $user_filter, $pager_count) { + // This callback is invoked by node/NID/webform-results and by + // node/NID/submissions. The latter is available to people who may not have + // access to view all submissions for this webform, so protect the CSV file + // accordingly. + // @todo Might be a UI improvement for the form generated from + // node/NID/submissions to trigger a CSV file that contains the current + // user's submissions only, so that it matches what is displayed on the + // page. However, webform_results_download() does not currently support + // filters. + $form = webform_results_access($node) ? drupal_get_form('webform_alt_ui_result_buttons', $node) : array(); + + module_load_include('inc', 'webform', 'includes/webform.report'); + $page = webform_results_submissions($node, $user_filter, $pager_count); + + // Line up the page components in order. + return array(array('#type' => 'markup', '#markup' => $page), $form); +} + +/** + * Form for our overriden webforms result page. + */ +function webform_alt_ui_result_buttons($form, &$form_state, $node) { + return array( + '#node' => $node, + 'buttons' => array( + 'download' => array( + '#type' => 'submit', + '#value' => t('Download file'), + ), + 'clear' => array( + '#type' => 'submit', + '#value' => t('Clear'), + ), + ) + ); +} + +/** + * Form submission for our overriden webforms result page. + */ +function webform_alt_ui_result_buttons_submit($form, &$form_state) { + if ($form_state['values']['op'] == $form_state['values']['clear']) { + $form_state['redirect'] = 'node/' . $form['#node']->nid . '/webform-results/clear'; + } + elseif ($form_state['values']['op'] == $form_state['values']['download']) { + form_load_include($form_state, 'inc', 'webform', 'includes/webform.report'); + webform_results_download($form['#node']); + } +} + +/** + * Menu callback for removing a field. + */ +function webform_alt_ui_remove_element($form_type, $form_id, $element_id) { + module_load_include('inc', 'form_builder', 'includes/form_builder.admin'); + + // Remove the field from the cache. + form_builder_cache_field_delete($form_type, $form_id, $element_id); + + // Attempt to load the field. If it has been successfully deleted this returns false. + $error = form_builder_cache_field_load($form_type, $form_id, $element_id) ? TRUE : FALSE; + + // Create the JSON data to be sent back to the caller. + $data = array( + 'formType' => $form_type, + 'formId' => $form_id, + 'elementId' => $element_id, + 'html' => form_builder_field_render($form_type, $form_id, $element_id), + 'time' => time(), + 'error' => $error, + ); + + drupal_json_output($data); + exit(); + + return ''; +} + +/* + * Function for clicking cancel on the form. + */ +function webform_alt_ui_click_cancel(&$form, &$form_state) { + // Delete the form cache. + webform_alt_ui_clear_cache($form, $form_state); + + // Set a default destination of the Add content page. + $destination = 'node/add'; + // If the node exists set the destination to the view tab. + if (isset($form_state['node']->nid)) { + drupal_set_message(t('Changes to your webform have been cancelled.')); + $destination = 'node/' . $form_state['node']->nid; + } + + // The presence of a destination in the query string will supercede the previous + // desitination value. + if (!$_GET['destination']) { + drupal_goto($destination); + } +} + +/** + * Clears the webform cache + */ +function webform_alt_ui_clear_cache(&$form, &$form_state) { + form_builder_cache_delete('webform', $form['#form_builder_token']); +} + +/** + * Alter the render array for the option_element config form. + */ +function webform_alt_ui_pre_render_option($element, $form_state) { + $keys = element_children($element['settings']); + + // Remove the settings from their fieldset and eliminate their description text. + foreach($keys as $key) { + $element[$key] = $element['settings'][$key]; + unset($element[$key]['#description']); + } + unset($element['settings']); + + return $element; +} + + +/** + * Implements hook_form_builder_types_alter(). + * + * Function to re-order the form elements and change their titles. + */ +function webform_alt_ui_form_builder_types_alter(&$types) { + $fields = $types['webform']; + + // Build an array of titles keyed by element type. + $new_order = _webform_alt_ui_component_names(); + + // Create the new array of form elements altering the title when needed. + $order = array(); + foreach($new_order as $field_name => $title) { + $order[$field_name] = $fields[$field_name]; + if ($title) { + $order[$field_name]['title'] = $title; + } + unset($fields[$field_name]); + } + + // Merge and store the form element arrays. This means that any fields that + // were omitted from the $new_order list will still be included. + $types['webform'] = array_merge($order, $fields); + + // Alter _webform_defaults_COMPONENT() defaults. + if (isset($types['webform']['file'])) { + $types['webform']['file']['default']['#webform_file_filtering']['size'] = 1000; + } +} + +/* + * Define the new order and titles for form elements. + */ +function _webform_alt_ui_component_names() { + return array( + 'textfield' => t('Text field'), + 'textarea' => t('Multi-line text field'), + 'radios' => t('Radio buttons'), + 'checkboxes' => t('Check boxes'), + 'select' => t('Drop-down list'), + 'email' => t('E-mail'), + 'file' => t('File upload'), + 'pagebreak' => t('Page break'), + 'markup' => t('Formatted content'), + 'fieldset' => t('Fieldset'), + 'hidden' => t('Hidden field'), + ); +} + +/** + * This retitles the form components to match the names in the field palette. + */ +function webform_alt_ui_form_builder_add_element_alter(&$element, $form_type, $form_id) { + $titles = _webform_alt_ui_component_names(); + $type = $element['#form_builder']['element_type']; + + if ($titles[$type]) { + $element['#title'] = $titles[$type]; + } + else { + $element['#title'] = ucfirst($type); + } +} + +/** + * Implements hook_block_view_DELTA_alter(). + */ +function webform_alt_ui_block_view_system_help_alter(&$data, $block) { + // Replace the system help text for the current page, if applicable. + $item = menu_get_item(); + if ($item['path'] == 'admin/config/content/webform') { + $data['content'] = t('Webforms are forms or questionnaires that can optionally be attached to other content types. <a href="@url">Create a webform</a>.', array('@url' => url('node/add/webform'))); + } +} + +/** + * This function alters access and text strings to some of the options on the + * webform admin configuration page + */ +function webform_alt_ui_form_webform_admin_settings_alter(&$form, &$form_state, $form_id) { + $form['advanced']['#title'] = t('Advanced'); + + $form['advanced']['webform_use_cookies']['#title'] = t('Allow cookies to track submissions'); + + // Hide some options from the UI + $form['advanced']['webform_email_address_format']['#access'] = FALSE; + $form['advanced']['webform_submission_access_control']['#access'] = FALSE; + + $form['advanced']['webform_export_format']['#title'] = t('Default format for downloading webform results'); + + $form['advanced']['webform_csv_delimiter']['#title'] = t('Character to use for delimited text export format'); + $form['advanced']['webform_csv_delimiter']['#description'] = t('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.'); + $form['advanced']['webform_csv_delimiter']['#states'] = array( + 'invisible' => array( + 'input[name=webform_export_format]' => array('checked' => FALSE), + ), + ); + + $form['components']['file']['#title'] = t('File upload'); + $form['components']['hidden']['#title'] = t('Hidden field'); + $form['components']['textarea']['#title'] = t('Multi-line textfield'); + $form['components']['textarea']['#description'] = t('A large textfield that allows for multiple lines of input.'); + $form['components']['email']['#description'] = t('A special textfield that accepts an e-mail address.'); + $form['components']['select']['#description'] = t('Allows creation of checkboxes, radio buttons, or dropdown menus.'); + + // Disable options that don't work with form_builder + $form['components']['grid']['#printed'] = TRUE; + $form['components']['grid']['#default_value'] = FALSE; + $form['components']['date']['#printed'] = TRUE; + $form['components']['date']['#default_value'] = FALSE; + $form['components']['time']['#printed'] = TRUE; + $form['components']['time']['#default_value'] = FALSE; + + // Hide the email settings + $form['email']['#access'] = FALSE; + + // Retheme the table + $form['#theme'] = array('webform_alt_ui_admin_settings'); +} + +function theme_webform_alt_ui_admin_settings($variables) { + $form = $variables['element']; + + // Format the components into a table. + foreach (element_children($form['components']) as $key) { + if (!$form['components'][$key]['#printed']) { + $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('Field'), 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); +} + +/** + * Prints the confirmation message after a successful submission. + */ +function _webform_alt_ui_confirmation($node) { + drupal_set_title($node->title); + webform_set_breadcrumb($node); + return theme('webform_alt_ui_confirmation', array('node' => $node, 'sid' => $_GET['sid'])); +} + +/** + * Themes the output of the default confirmation page. + */ +function theme_webform_alt_ui_confirmation($vars) { + $confirmation_message = $vars['confirmation_message']; + $link_to_form = $vars['link_to_form']; + $node = $vars['node']; + + // Because we put a default confirmation message in the textbox in our UI, if + // someone leaves it blank they probably meant to do so, so we do not output + // a default confirmation here, just use whatever we were provided with. + $output = '<div class="webform-confirmation">' . $confirmation_message . '</div>'; + + if ($link_to_form) { + $output .= '<div class="links">' . l(t('Go back to the form'), 'node/' . $node->nid) . '</div>'; + } + + return $output; +} + +/** + * Preprocess function for the webform confirmation; repeat webform's steps. + */ +function webform_alt_ui_preprocess_webform_alt_ui_confirmation(&$vars) { + template_preprocess_webform_confirmation($vars); +} + +function webform_alt_ui_preprocess_webform_submission_page(&$vars) { + if (isset($vars['submission_content']['#type']) && $vars['submission_content']['#type'] == 'form') { + foreach(element_children($vars['submission_content']['submitted']) as $key) { + $vars['submission_content']['submitted'][$key]['#title'] .= ':'; + } + } +} + +/** + * Implements module_preprocess_hook(). + * + * Alter the theme_hook_suggestions for the navigation so we can retheme it on + * the module layer. + */ +function webform_alt_ui_preprocess_webform_submission_navigation(&$vars) { + $vars['theme_hook_suggestions'][] = 'webform_alt_ui_submission_navigation'; +} + +/** + * Themes the previous | next links on individual webform submissions + */ +function theme_webform_alt_ui_submission_navigation($vars) { + $next = $vars['next']; + $previous = $vars['previous']; + $previous_url = $vars['previous_url']; + $next_url = $vars['next_url']; + + $output = '<div class="webform-submission-navigation">'; + + drupal_add_css(drupal_get_path('module', 'webform_alt_ui') . '/css/webform_alt_ui.css'); + + if ($previous) { + $output .= l(t('Previous'), $previous_url, array('attributes' => array('class' => array('webform-submission-previous')), 'query' => ($mode == 'form' ? array('destination' => $previous_url) : NULL))); + } else { + $output .= '<span class="webform-submission-previous">' . t('Previous') . '</span>'; + } + + if ($next) { + $output .= l(t('Next'), $next_url, array('attributes' => array('class' => array('webform-submission-next')), 'query' => ($mode == 'form' ? array('destination' => $next_url) : NULL))); + } else { + $output .= '<span class="webform-submission-next">' . t('Next') . '</span>'; + } + + $output .= '</div>'; + + return $output; +} + +function webform_alt_ui_preprocess_webform_submission_information(&$vars) { + $vars['theme_hook_suggestions'][] = 'webform_alt_ui_submission_information'; + + $vars['count'] = array(); + $query = db_select('webform_submissions') + ->condition('nid', $vars['node']->nid) + ->condition('sid', $vars['submission']->sid, '<='); + $vars['count']['current'] = $query->countQuery()->execute()->fetchField(); + + $vars['count']['total'] = webform_get_submission_count($vars['node']->nid); +} + +function theme_webform_alt_ui_submission_information(&$vars) { + $node = $vars['node']; + $account = $vars['account']; + $submission = $vars['submission']; + $count = $vars['count']; + + $output = '<div class="webform-user clearfix">'; + $output .= '<h5>' . t('Submission information') . '</h5>'; + $output .= theme('user_picture', array('account' => $account)); + $output .= ' + <div class="bd"> + <div><span class="label">' . t('Form:') . '</span> ' . l($node->title, 'node/' . $node->nid) . '</div> + <div><span class="label">' . t('Submitted by:') . '</span> '. theme('username', array('account' => $account)) . '</div> + <div><span class="label">' . t('Date:') . '</span> '. format_date($submission->submitted, 'large') . '</div> + <div><span class="label">' . t('IP address:') . '</span> ' . $submission->remote_addr . '</div> + <div>' . t('!current of !total', array('!current' => $count['current'], '!total' => $count['total'])) . '</div> + </div>'; + $output .= '</div>'; + + return $output; +} + +function webform_alt_ui_webform_submission_render_alter(&$renderable) { + // Recursively append a colon to each element's title + webform_alt_ui_append_colon($renderable); +} + +function webform_alt_ui_append_colon(&$element) { + foreach(element_children($element) as $key) { + if ($element[$key]['#type'] != 'fieldset') { + $element[$key]['#title'] .= ':'; + } + webform_alt_ui_append_colon($element[$key]); + } +} + +/* + * Hides the custom keys option for checkboxes, select, and radios + */ +function webform_alt_ui_option_process(&$vars, $hook) { + $vars['custom_keys']['#access'] = FALSE; + + return $vars; +} + +/** + * Additional file size validation for file upload fields. + */ +function webform_alt_ui_file_size_validate($element, &$form_state) { + if ($element['#value'] > 20000) { + form_error($element, t('The maximum size must be less than 20,000 KB.')); + } + elseif ($element['#value'] <= 0) { + form_error($element, t('The maximum upload size must be a positive number.')); + } +} + +/** + * Preprocess function for webform file components + * + * Sets the #size attribute to a custom default + */ +function webform_alt_ui_preprocess_webform_render_file(&$vars) { + $keys = element_children($vars['element']); + $key = array_shift($keys); + + if (!$vars['element']['#webform_component']['extra']['width']) { + $vars['element'][$key]['#size'] = WEBFORM_ALT_UI_FILE_WIDTH; + } +} + +function webform_alt_ui_submit_button_page($nid = NULL) { + $form = drupal_get_form('webform_alt_ui_submit_button_form', $nid); + + $output = render($form); + + if ($_REQUEST['js']) { + $data = array( + 'html' => $output, + ); + + drupal_json_output($data); + return; + } + + return $output; +} + + +function webform_alt_ui_submit_button_form($form, &$form_state, $nid) { + // Handle the case where the node we are configuring doesn't exist, in + // addition to the case where it does. + if (!empty($nid)) { + $node = node_load($nid); + } + if (empty($node)) { + $node = new stdClass(); + } + + $form['vertical_wrapper'] = array( + '#type' => 'vertical_accordion', + ); + + $form['vertical_wrapper']['wrapper'] = array( + '#type' => 'fieldset', + '#title' => t('Properties'), + ); + + $form['vertical_wrapper']['wrapper']['submit_button'] = array( + '#type' => 'textfield', + '#title' => t('Submit button text'), + '#default_value' => !empty($node->webform['submit_text']) ? $node->webform['submit_text'] : t('Submit'), + ); + + // Note: This form deliberately has no submit button! It has no access + // control (so it would not be safe to allow it to be submitted); it only + // exists so that it can be displayed and used by JavaScript. + + return $form; +} -- GitLab