FoxPoll
Questions answered by this recipe
- How can I build forms for voting and polling and accumulating the results?
- How can I create dynamically updated displays of results and in a number of ways and different pages?
Description
Gather voting results via a Fox form using chiefly radio and checkboxes and adding the choices made hex-encoded to a csv table on a data store page section, as well as PTVs of the sums of each of the voting options.
Installation
Download foxpoll.phpΔ, copy to cookbook folder and include in config.php.
Configuration
The script is an add-on to Fox, so Fox needs to be installed and enabled for posting vote submission to a target page. The script runs as a foxfilter function prior to any processing of the submitted data as part of Fox form processing. The form needs to be set up carefully, read more below, with example.
You can use variable $FoxPollConfig in config.php to enable a few settings:
$FoxPollConfig['date_log'] = 1;//0 disables date/time logging to vote records.$FoxPollConfig['date_fmt'] = 'j M H:i';//'d/m/Y H:i:s' //change the default date format used by the date logging.$FoxPollConfig['IP_log'] = 1;// 0 disables IP address logging to vote records.$FoxPollConfig['IP_block'] = 0;// 1 enables blocking of repeated voting by IP.$FoxPollConfig['ip_max_repeat'] = 3;//max number vote repeats before a block can be activated on the ip
Fox Poll Form Setup
A Fox form needs to be set up with some additional specific requirements:
foxfilter=foxpollneeds to be added as parameter.- A target parameter is needed to specify the page for storing the data and the Sum PTVs. If just with a page name then an anchor section named #data for the vote records and one named #datasums will be used. You can change these defaults by postfixing a section name to the target, and also by setting a specific ptvtarget parameter, if you want the sum-PTVs being stored on a different page. Examples:
(:fox pollform foxfilter=foxpoll target=Polls.PollResults :)(:fox pollform foxfilter=foxpoll target=Polls.PollResultStore#store ptvtarget=PollResults#sums :) - A hidden input named vote-inputs with a string of all the ID names of the inputs used for the voting, comma-separated. Example:
(:input hidden poll-inputs "aa,bb,cc" :)
- Only the inputs named in the string values of poll-inputs and, optional, poll-options will be handled as data input for the data store csv table. The input naming and values needs to correspond with the comma-separated string parameters of poll-inputs.
Optional:
- A template or foxtemplate parameter is optional. If not provided the script will construct a foxtemplate parameter from the poll-options provided in a hidden field.
- A hidden input named poll-options with a string of all the polling choice options, comma-seperated, is optional. If not specified the script will construct the options automatically from the form markup at submission. Example if set in the form:
(:input hidden poll-options "aa_Yes,aa_No,bb_5,bb_4,bb_3,bb_2,bb_1,cc_A,cc_B,cc_C,cc_D" :)
Each item in this comma-separated list is a combination of input name and input value joined by an underscore "_" character, for the inputs used for the poll, as specified with the poll-inputs parameter.
For this example (see box below) we have two radio button controls named aa and bb, one checkbox control named cc and a text box named comment. This corresponds to the string in poll-inputs, but excluding the comment text input. Here is the form as an example:
(:foxmessages:)
(:fox pollform foxfilter=foxpoll target=Polls.PollResults :)
(:input hidden poll-inputs "aa,bb,cc" :)
!!!Question A: Do you regularly buy fruit from us?
(:input radio aa Yes label=Yes:)
(:input radio aa No label=No:)
!!!Question B: How do you rate the quality of our fruit?
(:input radio bb 5 label=excellent:)
(:input radio bb 4 label=good:)
(:input radio bb 3 label=acceptable:)
(:input radio bb 2 label="not so good":)
(:input radio bb 1 label="very bad":)
!!!Question C: What kinds of fruit would you buy?
(:input checkbox cc[] A label=apples:)
(:input checkbox cc[] B label=bananas:)
(:input checkbox cc[] C label=cherries:)
(:input checkbox cc[] P label=plums:)
!!!Would tou like to add a comment?
(:input text comment size="60" :)
(:input submit post "Submit Vote":)
(:foxadd target=Polls.PollComments#comments foxtemplate="{$$comment}\n----" :)
(:input hidden foxsuccess "%red%Your vote has been submitted. Thank you for taking part in this poll!" :)
(:input hidden foxfailure "%red%An error occurred when submitting your vote!Please try again later!" :)
(:foxend pollform:)
|
(:foxmessages:) (:fox pollform foxfilter=foxpoll target=Polls.PollResults :) Question A: Do you regularly buy fruit from us?
Question B: How do you rate the quality of our fruit?
Question C: What kinds of fruit would you buy?
Would tou like to add a comment?
(:foxadd target=Polls.PollComments#comments foxtemplate="{$$comment}\n----" :) (:foxend pollform:) |
Notes to the example:
- In the example parameters foxsuccess and foxfailure are added in hidden input fields, to provide a suitable message on form submission, vie the
(:foxmessages:)markup. - A template string or location is not set (it could be), as the script can add it automatically.
- A directive
(:foxadd .... :)is used to add processing of any comments submitted to a different location than the data store. - By naming the text input comment or comment<suffix> the script will check if a text entry was made, and if not prevent posting an empty comment to that particular target, but still posting the voting choices made with the radio inputs and checkboxes.
- The script offers a possibility for admins to more easily display voting data as an echoed table with binary, hex and row numbers. For this set
polldatacheck=1. I recommend to use a checkbox input in the form for this purpose, and have it only for admins:
(:if auth admin:)(:input checkbox polldatacheck 1 label="check data":)(:ifend:)
- Another option for admins is a parameter
pollupdatesums=1, which will cause a PTV update (overwrite) using sum values from the data store csv. You can add another checkbox input like:
(:if auth admin:)(:input checkbox pollupdatasums 1 label="update PTV sums":)(:ifend:)
- A third option for admins is a parameter
polldatadump=1, which will cause a display of the data table in form of binary and hexadecimal numbers. You can add another checkbox input like:
(:if auth admin:)(:input checkbox polldatadump 1 label="update PTV sums":)(:ifend:)
Automatic Setup of Results Page Store
Once the poll form is created and ready for the first time to use, but before a target page is created, the first Vote click will create the target page automatically (if Fox is allowed to post to that page). With the first click the script will check if the target page exists, and if it does not, then it creates it, from the info given in the form (the target given and the poll-inputs strings provided, and, optionally, a poll-options string. The script builds a special template for this occasion only. This first-time use will not count as a poll vote, no values will be posted. All the PTVs for the result sums will be created with 0 as values. They will appear in an anchored target page section [[#datasums]]. Another anchored target page section called [[#data]] will be created with a first line as header for the csv lines, which will be added there by subsequent poll submissions. The csv separator used is a semi-colon ';'.
The automatic store creation will also add, at the top of the data store page, a section displaying the current PTV values, arranged with the poll option id's. This is just a crude way to display the sums. Scrap it or change as wanted.
The first column in this csv table, under header vote, contains each vote record as a hexadecimal encoded number. This number is an encoding of the binary sequence of vote choices (with an added leading 1), which consists of 1's for a choice made and 0's for the alternatives not made. The hex-coding of vote results saves a lot of space. It is not an encryption of each vote record, but it makes it very hard to read what a voter has actually voted for.
The script reads the data csv table when a vote gets submitted. It uses the first column with the hex values, creates internally a table as an array with all the choices made, and then determines how many columns there are, and calculates the sums of votes for each column, and the total votes recorded for each poll question captured with radio or checkbox controls. This is then used to calculate values for the SUM_<id> and TOTL_<ids> PTVs to be updated with the current vote in progress.
A poll results page could look like this, after 6 votes, based on the example poll form (copied from my test page):
[[#datasums]] (:VOTES: 6:) (:TOTL_aa: 6:) (:SUM_aa_Yes: 4:) (:SUM_aa_No: 2:) (:TOTL_bb: 4:) (:SUM_bb_1: 0:) (:SUM_bb_2: 1:) (:SUM_bb_3: 2:) (:SUM_bb_4: 1:) (:SUM_bb_5: 0:) (:TOTL_cc: 4:) (:SUM_cc_A: 1:) (:SUM_cc_B: 3:) (:SUM_cc_C: 0:) (:SUM_cc_P: 0:) (:Poll_Options: aa_Yes,aa_No,bb_5,bb_4,bb_3,bb_2,bb_1,cc_A,cc_B,cc_C,cc_P:) (:if false:) [[#data]] vote; date; ip; c2c; 9 Dec 15:14; 127.0.0.1; c44; 9 Dec 15:21; 127.0.0.1; c80; 9 Dec 15:22; 127.0.0.1; a00; 9 Dec 15:23; 127.0.0.1; a00; 9 Dec 15:26; 127.0.0.1; c44; 9 Dec 15:51; 127.0.0.1;@] |
The first entry is "c2c" in hexadecimal base, which is in binary "110000101100". By discounting the leading 1 we get "10 00010 1100", grouped of first two for aa, next five for bb. and last four for cc. That maps precisely to the series of values of the poll-options parameter of the form, if set, or the values of PTV 'Poll-Options', ("aa_Yes,aa_No,bb_5,bb_4,bb_3,bb_2,bb_1,cc_A,cc_B,cc_C,cc_P"), and you can work out which choices were made from that!
Some Ideas for Poll Result Dynamic Displays
- The PTVs are created as hidden PTVs. See also PageTextVariables
- They can then be accessed and displayed in many ways and on different pages.
- In a most simple way it could be like this, using the PTV display syntax and some text:
!!Poll Results from the Fruit Survey
!!!Question A
'''Do you regularly buy fruit from us?'''
{$:TOTL_aa} votes
{$:SUM_aa_Yes} voted Yes
{$:SUM_aa_No} voted No
!!!Question B
'''How do you rate the quality of our fruit?'''
{$:TOTL_bb} votes
{$:SUM_bb_5} find it excellent
{$:SUM_bb_4} find it good
{$:SUM_bb_3} find it acceptable
{$:SUM_bb_2} find it not so good
{$:SUM_bb_1} find it bad
!!!Question C
'''What kinds of fruit would you buy?'''
{$:TOTL_cc} votes
{$:SUM_cc_A} would buy apples
{$:SUM_cc_B} would buy bananas
{$:SUM_cc_C} would buy cherries
{$:SUM_cc_P} would buy plums
|
For a result display with a bar chart, which dynamically changes with poll data entering, you can try Method 3 - simple bar chart with markup only in conjunction with using a Percent Markup Expression. Example provided on the barchart page.
Usage
Mainly aimed at use for community polling, never for serious political voting, or voting in the thousands of submissions.
If abuse of anonymous polling is suspected or encountered you could try IP logging and blocking. Use of Captcha might help as well.
If anonymous polling is not so important the poll form can be set up to require users to enter name and email address. If the text inputs for this are named voter-name and voter-email, then template variables for these fields are added automatically to the template, and entries become part of the vote record in the store.
Notes
To do / some day / maybe
Add an admin form for dta checks and maintenance?
Change log / Release notes
- 2025-12-17: Added more error reporting, especially for data integrity errors. Added parameter polldatadump.
- 2025-12-13: changed so sums are read from PTVs primarily, but cross-checked with sums from data store. Added PTV 'Poll-Options' as part of intial store page creation. Added parameter for updating sum PTVs in case their values do not match the sums from data store.
- 2025-12-12: added as default an underscore character "_" to join input name and value in PTV names.Refactored code for PTV posting. Added some checks on input data integrity. Added parameter for displaying vote data as binary table for easier visual checks.
- 2025-12-11: added function and possibility to get all poll options from markup.
- 2025-12-10: revised handling of target parameters for easier setup.
- 2025-12-09: first release.
See also
Contributors
Comments
See discussion at FoxPoll-Talk
User notes : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.