Skip to content
This repository
  • 16 commits
  • 11 files changed
  • 0 comments
  • 6 contributors
Sep 17, 2012
Mateusz U mateusz BUGFIX: prevent form from expiring on users - ping regularly.
The form throws a CSRF error if left for too long by itself. This is
especially important for long forms.

Use local ping function, as the CMS ping returns 403 when BasicAuth is
enabled.
e11ab5a
Mateusz U mateusz BUG Fix the edge cases for radio button conditionals.
The specific situation this is fixing is that when server-side
validation fails and results in form having non-default values. In this
case the visibility might not be correct and will need to be updated.

Also, we cannot rely on onclick events on radio group to find out the
current value when we are loading - instead we need to actually find
out the currently selected radio button.

Fixes the IsBlank and IsNotBlank conditions as well.
12e8c67
Mateusz U mateusz BUG Fix the date field conditional to work with dropdown.
In this case they keyup will not be triggered at all.
4cc811d
Mateusz U mateusz ADD Add report visibility setting to EditableFormHeading
Same as already existing setting on EditableLiteralField.

Conflicts:
	code/model/formfields/EditableFormHeading.php
b572b79
Mateusz U mateusz BUG Popup to apear on a date field. 9b24ace
Sep 18, 2012
Will Rossiter wilr Merge pull request #56 from mateusz/minor-test-fixes-0.5
Minor fixes to the tests for 0.5.
af67984
Sep 19, 2012
Mateusz U mateusz Minor fixes to the tests for 0.5. 7b64dc2
Jan 03, 2013
Ingo Schommer chillu Create composer.json 71e648f
Mar 29, 2013
Ingo Schommer chillu Travis support 8426851
Jul 18, 2013
☃ Stephen Shkardoon ☃ ss23 Cherrypick 5c9a962 5792159
Will Rossiter wilr Fix license on packagist 07a2d10
Sep 04, 2013
Will Rossiter wilr FIX: Check security ID on admin non form links (#158) f9bcf88
Feb 14, 2014
Sam Minnée sminnee Add CMS as a requirement for UserForms 767a83e
Jun 19, 2014
Jeremy Shipman jedateach FIX: incorrect jquery wrapping for EditableDateField e8f843a
Jeremy Shipman jedateach FIX: Make stored data unique for dependent logic calls. i.e. don't ov…
…erride the same data value.
e2a761a
Jul 05, 2014
Will Rossiter wilr Merge pull request #216 from burnbright/0.5
Some small fixes for 0.5
9a18f73
17 .travis.yml
... ... @@ -0,0 +1,17 @@
  1 +# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
  2 +
  3 +language: php
  4 +php:
  5 + - 5.3
  6 +
  7 +env:
  8 + - DB=MYSQL CORE_RELEASE=2.4
  9 + - DB=PGSQL CORE_RELEASE=2.4
  10 +
  11 +before_script:
  12 + - git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
  13 + - php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
  14 + - cd ~/builds/ss
  15 +
  16 +script:
  17 + - phpunit userforms/tests/
2  README.md
Source Rendered
... ... @@ -1,5 +1,7 @@
1 1 # UserForms
2 2
  3 +[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-userforms.png?branch=0.5)](http://travis-ci.org/silverstripe/silverstripe-userforms)
  4 +
3 5 ## Introduction
4 6
5 7 UserForms enables CMS users to create dynamic forms via a drag and drop interface
13 code/formfields/FieldEditor.php
@@ -108,6 +108,7 @@ function CreatableFields() {
108 108 }
109 109 return $output;
110 110 }
  111 +
111 112 return false;
112 113 }
113 114
@@ -173,11 +174,15 @@ function saveInto(DataObject $record) {
173 174 * @return bool|html
174 175 */
175 176 public function addfield() {
  177 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  178 + return $this->httpError(400);
  179 + }
  180 +
176 181 // get the last field in this form editor
177 182 $parentID = $this->form->getRecord()->ID;
178 183
179 184 if($parentID) {
180   - $parentID = Convert::raw2sql($parentID);
  185 + $parentID = (int) $parentID;
181 186
182 187 $highestSort = DB::query("SELECT MAX(\"Sort\") FROM \"EditableFormField\" WHERE \"ParentID\" = '$parentID'");
183 188
@@ -206,12 +211,16 @@ public function addfield() {
206 211 * @return bool|html
207 212 */
208 213 public function addoptionfield() {
  214 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  215 + return $this->httpError(400);
  216 + }
  217 +
209 218 // passed via the ajax
210 219 $parent = (isset($_REQUEST['Parent'])) ? $_REQUEST['Parent'] : false;
211 220
212 221 // work out the sort by getting the sort of the last field in the form +1
213 222 if($parent) {
214   - $sql_parent = Convert::raw2sql($parent);
  223 + $sql_parent = (int) $parent;
215 224
216 225 $highestSort = DB::query("SELECT MAX(\"Sort\") FROM \"EditableOption\" WHERE \"ParentID\" = '$sql_parent'");
217 226
20 code/formfields/SubmittedFormReportField.php
@@ -38,6 +38,10 @@ function Submissions() {
38 38 }
39 39
40 40 function getSubmissions() {
  41 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  42 + return $this->httpError(400);
  43 + }
  44 +
41 45 return $this->customise(array(
42 46 'Submissions' => $this->Submissions()
43 47 ))->renderWith(array('SubmittedFormReportField'));
@@ -62,6 +66,11 @@ function RecordID() {
62 66 * @return HTTPResponse / bool
63 67 */
64 68 public function export($id = false) {
  69 + // check the security ID
  70 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  71 + return $this->httpError(400);
  72 + }
  73 +
65 74 if($id && is_int($id)) {
66 75 $SQL_ID = $id;
67 76 }
@@ -93,6 +102,7 @@ public function export($id = false) {
93 102 foreach($submissions as $submission) {
94 103 $inClause[] = $submission->ID;
95 104 }
  105 +
96 106 $csvHeaders = DB::query("SELECT \"Name\" , \"Title\" FROM \"SubmittedFormField\"
97 107 LEFT JOIN \"SubmittedForm\" ON \"SubmittedForm\".\"ID\" = \"SubmittedFormField\".\"ParentID\"
98 108 WHERE \"SubmittedFormField\".\"ParentID\" IN (" . implode(',', $inClause) . ")
@@ -153,6 +163,10 @@ public function export($id = false) {
153 163 * @return Redirect|Boolean
154 164 */
155 165 public function deletesubmissions($id = false) {
  166 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  167 + return $this->httpError(400);
  168 + }
  169 +
156 170 $isRunningTests = (class_exists('SapphireTest', false) && SapphireTest::is_running_test());
157 171
158 172 if($id && is_int($id)) {
@@ -175,6 +189,7 @@ public function deletesubmissions($id = false) {
175 189 return (Director::is_ajax() || $isRunningTests) ? true : Director::redirectBack();
176 190 }
177 191 }
  192 +
178 193 return (Director::is_ajax() || $isRunningTests) ? false : Director::redirectBack();
179 194 }
180 195
@@ -184,6 +199,10 @@ public function deletesubmissions($id = false) {
184 199 * @return Redirect|Boolean
185 200 */
186 201 public function deletesubmission($id = false) {
  202 + if(!SecurityToken::inst()->checkRequest($this->request)) {
  203 + return $this->httpError(400);
  204 + }
  205 +
187 206 $isRunningTests = (class_exists('SapphireTest', false) && SapphireTest::is_running_test());
188 207
189 208 if($id && is_int($id)) {
@@ -203,6 +222,7 @@ public function deletesubmission($id = false) {
203 222 return (Director::is_ajax() || $isRunningTests) ? true : Director::redirectBack();
204 223 }
205 224 }
  225 +
206 226 return (Director::is_ajax() || $isRunningTests) ? false : Director::redirectBack();
207 227 }
208 228 }
91 code/model/UserDefinedForm.php
@@ -304,6 +304,7 @@ public function init() {
304 304 // load the jquery
305 305 Requirements::javascript(SAPPHIRE_DIR .'/thirdparty/jquery/jquery.js');
306 306 Requirements::javascript('userforms/thirdparty/jquery-validate/jquery.validate.min.js');
  307 + Requirements::javascript('userforms/javascript/UserForm_frontend.js');
307 308 }
308 309
309 310 /**
@@ -332,6 +333,13 @@ public function index() {
332 333 }
333 334
334 335 /**
  336 + * Keep the session alive for the user.
  337 + */
  338 + function ping() {
  339 + return 1;
  340 + }
  341 +
  342 + /**
335 343 * Get the form for the page. Form can be modified by calling {@link updateForm()}
336 344 * on a UserDefinedForm extension
337 345 *
@@ -339,7 +347,7 @@ public function index() {
339 347 */
340 348 function Form() {
341 349 $fields = $this->getFormFields();
342   - if(!$fields) return false;
  350 + if(!$fields || !$fields->exists()) return false;
343 351
344 352 $actions = $this->getFormActions();
345 353
@@ -403,7 +411,7 @@ function getFormFields() {
403 411
404 412 // set the values passed by the url to the field
405 413 $request = $this->getRequest();
406   - if($var = $request->getVar($field->name)) {
  414 + if(isset($request) && $var = $request->getVar($field->name)) {
407 415 $field->value = Convert::raw2att($var);
408 416 }
409 417
@@ -533,14 +541,21 @@ function generateConditionalJavascript() {
533 541 // watch out for multiselect options - radios and check boxes
534 542 if(is_a($formFieldWatch, 'EditableDropdown')) {
535 543 $fieldToWatch = "$(\"select[name='".$dependency['ConditionField']."']\")";
  544 + $fieldToWatchOnLoad = $fieldToWatch;
536 545 }
537   -
538 546 // watch out for checkboxs as the inputs don't have values but are 'checked
539 547 else if(is_a($formFieldWatch, 'EditableCheckboxGroupField')) {
540 548 $fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."[".$dependency['Value']."]']\")";
  549 + $fieldToWatchOnLoad = $fieldToWatch;
  550 + }
  551 + else if(is_a($formFieldWatch, 'EditableRadioField')) {
  552 + $fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."']\")";
  553 + // We only want to trigger on load once for the radio group - hence we focus on the first option only.
  554 + $fieldToWatchOnLoad = "$(\"input[name='".$dependency['ConditionField']."']:first\")";
541 555 }
542 556 else {
543   - $fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."']\")";
  557 + $fieldToWatch = "$(\"input[name='".$dependency['ConditionField']."']\")";
  558 + $fieldToWatchOnLoad = $fieldToWatch;
544 559 }
545 560
546 561 // show or hide?
@@ -551,29 +566,43 @@ function generateConditionalJavascript() {
551 566 // @todo encapulsation
552 567 $action = "change";
553 568
554   - if($formFieldWatch->ClassName == "EditableTextField" || $formFieldWatch->ClassName == "EditableDateField") {
  569 + if($formFieldWatch->ClassName == "EditableTextField") {
555 570 $action = "keyup";
556 571 }
557 572
558 573 // is this field a special option field
559 574 $checkboxField = false;
  575 + $radioField = false;
560 576 if(in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) {
561 577 $action = "click";
562 578 $checkboxField = true;
563 579 }
  580 + else if ($formFieldWatch->ClassName == "EditableRadioField") {
  581 + $radioField = true;
  582 + }
564 583
  584 + // Escape the values.
  585 + $dependency['Value'] = str_replace('"', '\"', $dependency['Value']);
  586 +
565 587 // and what should we evaluate
566 588 switch($dependency['ConditionOption']) {
567 589 case 'IsNotBlank':
568   - $expression = ($checkboxField) ? '$(this).attr("checked")' :'$(this).val() != ""';
  590 + $expression = ($checkboxField || $radioField) ? '$(this).attr("checked")' :'$(this).val() != ""';
569 591
570 592 break;
571 593 case 'IsBlank':
572   - $expression = ($checkboxField) ? '!($(this).attr("checked"))' : '$(this).val() == ""';
  594 + $expression = ($checkboxField || $radioField) ? '!($(this).attr("checked"))' : '$(this).val() == ""';
573 595
574 596 break;
575 597 case 'HasValue':
576   - $expression = ($checkboxField) ? '$(this).attr("checked")' : '$(this).val() == "'. $dependency['Value'] .'"';
  598 + if ($checkboxField) {
  599 + $expression = '$(this).attr("checked")';
  600 + } else if ($radioField) {
  601 + // We cannot simply get the value of the radio group, we need to find the checked option first.
  602 + $expression = '$(this).parents(".field").find("input:checked").val()=="'. $dependency['Value'] .'"';
  603 + } else {
  604 + $expression = '$(this).val() == "'. $dependency['Value'] .'"';
  605 + }
577 606
578 607 break;
579 608 case 'ValueLessThan':
@@ -592,20 +621,40 @@ function generateConditionalJavascript() {
592 621 $expression = '$(this).val() >= parseFloat("'. $dependency['Value'] .'")';
593 622
594 623 break;
595   - default:
596   - $expression = ($checkboxField) ? '!($(this).attr("checked"))' : '$(this).val() != "'. $dependency['Value'] .'"';
  624 + default: // ==HasNotValue
  625 + if ($checkboxField) {
  626 + $expression = '!$(this).attr("checked")';
  627 + } else if ($radioField) {
  628 + // We cannot simply get the value of the radio group, we need to find the checked option first.
  629 + $expression = '$(this).parents(".field").find("input:checked").val()!="'. $dependency['Value'] .'"';
  630 + } else {
  631 + $expression = '$(this).val() != "'. $dependency['Value'] .'"';
  632 + }
597 633
598 634 break;
599 635 }
600   - // put it all together
601   - $rules .= $fieldToWatch.".$action(function() {
602   - if(". $expression ." ) {
603   - $(\"#". $fieldId ."\").".$view."();
604   - }
605   - else {
606   - $(\"#". $fieldId ."\").".$opposite."();
607   - }
  636 +
  637 + // Register conditional behaviour with an element, so it can be triggered from many places.
  638 + $rules .= $fieldToWatch.".each(function() {
  639 + $(this).data('userformConditions_$fieldId', function() {
  640 + if(". $expression ." ) {
  641 + $(\"#". $fieldId ."\").".$view."();
  642 + }
  643 + else {
  644 + $(\"#". $fieldId ."\").".$opposite."();
  645 + }
  646 + });
608 647 });";
  648 +
  649 + // Trigger update on element changes.
  650 + $rules .= $fieldToWatch.".$action(function() {
  651 + $(this).data('userformConditions_$fieldId').call(this);
  652 + });\n";
  653 +
  654 + // Trigger update on load (if server-side validation fails some fields will have different values than defaults).
  655 + $rules .= $fieldToWatchOnLoad.".each(function() {
  656 + $(this).data('userformConditions_$fieldId').call(this);
  657 + });\n";
609 658 }
610 659 }
611 660 }
@@ -615,9 +664,9 @@ function generateConditionalJavascript() {
615 664 Requirements::customScript(<<<JS
616 665 (function($) {
617 666 $(document).ready(function() {
618   - $rules
619   -
620 667 $default
  668 +
  669 + $rules
621 670 })
622 671 })(jQuery);
623 672 JS
@@ -904,4 +953,4 @@ class UserDefinedForm_SubmittedFormEmail extends Email {
904 953 function __construct() {
905 954 parent::__construct();
906 955 }
907   -}
  956 +}
8 code/model/formfields/EditableDateField.php
@@ -46,7 +46,7 @@ public function getFormField() {
46 46 $dateFormat = DateField_View_JQuery::convert_iso_to_jquery_format(i18n::get_date_format());
47 47
48 48 Requirements::customScript(<<<JS
49   - (function(jQuery) {
  49 + (function($) {
50 50 $(document).ready(function() {
51 51 $('input[name^=EditableDateField]').attr('autocomplete', 'off').datepicker({ dateFormat: '$dateFormat' });
52 52 });
@@ -59,7 +59,9 @@ public function getFormField() {
59 59
60 60 $default = ($this->getSetting('DefaultToToday')) ? date('d/m/Y') : $this->Default;
61 61
62   - return new DateField( $this->Name, $this->Title, $default);
  62 + $dateField = new DateField( $this->Name, $this->Title, $default);
  63 + $dateField->setConfig('showcalendar', true);
  64 + return $dateField;
63 65 }
64 66
65 67 /**
@@ -75,4 +77,4 @@ public function getValidation() {
75 77 'date' => true
76 78 );
77 79 }
78   -}
  80 +}
14 code/model/formfields/EditableFormHeading.php
@@ -25,7 +25,7 @@ function getFieldConfiguration() {
25 25 $label = _t('EditableFormHeading.LEVEL', 'Select Heading Level');
26 26
27 27 $options = parent::getFieldConfiguration();
28   -
  28 +
29 29 $options->push(
30 30 new DropdownField($this->getSettingName("Level"), $label, $levels, $level)
31 31 );
@@ -33,6 +33,14 @@ function getFieldConfiguration() {
33 33 if($this->readonly) {
34 34 $extraFields = $options->makeReadonly();
35 35 }
  36 +
  37 + $options->push(
  38 + new CheckboxField(
  39 + $this->getSettingName('HideFromReports'),
  40 + _t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?'),
  41 + $this->getSetting('HideFromReports')
  42 + )
  43 + );
36 44
37 45 return $options;
38 46 }
@@ -45,10 +53,10 @@ function getFormField() {
45 53 }
46 54
47 55 function showInReports() {
48   - return false;
  56 + return (!$this->getSetting('HideFromReports'));
49 57 }
50 58
51 59 function getFieldValidationOptions() {
52 60 return false;
53 61 }
54   -}
  62 +}
19 composer.json
... ... @@ -0,0 +1,19 @@
  1 +{
  2 + "name": "silverstripe/userforms",
  3 + "description": "UserForms enables CMS users to create dynamic forms via a drag and drop interface and without getting involved in any PHP code",
  4 + "type": "silverstripe-module",
  5 + "license": "BSD-3-Clause",
  6 + "keywords": ["silverstripe", "userforms"],
  7 + "authors": [
  8 + {
  9 + "name": "Will Rossiter",
  10 + "email": "will@fullscreen.io"
  11 + }
  12 + ],
  13 +
  14 + "require":
  15 + {
  16 + "silverstripe/framework": "~2.3",
  17 + "silverstripe/cms": "~2.3"
  18 + }
  19 +}
11 javascript/UserForm.js
@@ -154,9 +154,9 @@
154 154 // variables
155 155 var action = $("#Form_EditForm").attr("action") + '/field/Fields/addfield';
156 156 var length = $(".FieldInfo").length + 1;
157   - var securityID = ($("#SecurityID").length > 0) ? '&SecurityID='+$("#SecurityID").attr("value") : '';
  157 + var securityID = ($("input[name=SecurityID]").length > 0) ? '&SecurityID='+$("input[name=SecurityID]").first().attr("value") : '';
158 158 var type = $(this).siblings("select").val();
159   -
  159 +
160 160 // send ajax request to the page
161 161 $.ajax({
162 162 type: "GET",
@@ -264,13 +264,16 @@
264 264 var options = $(this).parent("li");
265 265 var action = $("#Form_EditForm").attr("action") + '/field/Fields/addoptionfield';
266 266 var parent = $(this).attr("rel");
  267 + var securityID = ($("input[name=SecurityID]").length > 0) ? $("input[name=SecurityID]").first().attr("value") : '';
267 268
268 269 // send ajax request to the page
269 270 $.ajax({
270 271 type: "GET",
271 272 url: action,
272   - data: 'Parent='+ parent,
273   -
  273 + data: {
  274 + 'Parent': parent,
  275 + 'SecurityID': securityID
  276 + },
274 277 // create a new field
275 278 success: function(msg){
276 279 options.before(msg);
9 javascript/UserForm_frontend.js
... ... @@ -0,0 +1,9 @@
  1 +jQuery(function($) {
  2 + /**
  3 + * Make sure the form does not expire on the user.
  4 + */
  5 + setInterval(function() {
  6 + // Ping every 3 mins.
  7 + $.ajax({url: "UserDefinedForm_Controller/ping"});
  8 + }, 180*1000);
  9 +});
10 templates/SubmittedFormReportField.ss
@@ -2,13 +2,13 @@
2 2
3 3 <% if Submissions %>
4 4 <ul class="userforms-submission-actions">
5   - <li><a href="{$Top.Link}/export/?id={$RecordID}"><% _t('EXPORTSUBMISSIONS', 'Export submissions to CSV') %></a></li>
6   - <li><a href="{$Top.Link}/deletesubmissions/?id={$RecordID}" class="deleteAllSubmissions"><% _t('DELETEALLSUBMISSIONS', 'Delete All Submissions') %></a></li>
  5 + <li><a href="{$Top.Link}/export/?id={$RecordID}&amp;SecurityID=$Form.SecurityID"><% _t('EXPORTSUBMISSIONS', 'Export submissions to CSV') %></a></li>
  6 + <li><a href="{$Top.Link}/deletesubmissions/?id={$RecordID}&amp;SecurityID=$Form.SecurityID" class="deleteAllSubmissions"><% _t('DELETEALLSUBMISSIONS', 'Delete All Submissions') %></a></li>
7 7 </ul>
8 8
9 9 <% control Submissions %>
10 10 <div class="userform-submission">
11   - <h4><% _t('SUBMITTED', 'Submitted at') %> $Created.Nice. <a href="{$Top.Link}/deletesubmission/?id={$ID}" class="deleteSubmission"><% _t('DELETESUBMISSION', 'Delete Submission') %></a></h4>
  11 + <h4><% _t('SUBMITTED', 'Submitted at') %> $Created.Nice. <a href="{$Top.Link}/deletesubmission/?id={$ID}&amp;SecurityID={$Form.SecurityID}" class="deleteSubmission"><% _t('DELETESUBMISSION', 'Delete Submission') %></a></h4>
12 12
13 13 <table>
14 14 <% control Values %>
@@ -25,13 +25,13 @@
25 25 <div class="userforms-submissions-pagination">
26 26
27 27 <% if Submissions.NotFirstPage %>
28   - <a class="prev" href="javascript:void(0)" onclick="jQuery('#userforms-submissions').parent().load(jQuery('base').get(0).href+'/{$Top.Link}/getSubmissions/?start={$Submissions.PrevStart}');" title="View the previous page">Previous page</a>
  28 + <a class="prev" href="javascript:void(0)" onclick="jQuery('#userforms-submissions').parent().load(jQuery('base').get(0).href+'{$Top.Link}/getSubmissions/?start={$Submissions.PrevStart}&amp;SecurityID={$Form.SecurityID}');" title="View the previous page">Previous page</a>
29 29 <% end_if %>
30 30
31 31 <span>Viewing rows $Submissions.Start - $Submissions.StartPlusOffset of $Submissions.TotalCount rows</span>
32 32
33 33 <% if Submissions.NotLastPage %>
34   - <a class="next" href="javascript:void(0)" onclick="jQuery('#userforms-submissions').parent().load(jQuery('base').get(0).href+'/{$Top.Link}/getSubmissions/?start={$Submissions.NextStart}');" title="View the next page">Next page</a>
  34 + <a class="next" href="javascript:void(0)" onclick="jQuery('#userforms-submissions').parent().load(jQuery('base').get(0).href+'{$Top.Link}/getSubmissions/?start={$Submissions.NextStart}&amp;SecurityID={$Form.SecurityID}');" title="View the next page">Next page</a>
35 35 <% end_if %>
36 36 </div>
37 37 <% end_if %>

No commit comments for this range

Something went wrong with that request. Please try again.