NEW: A new version that supports multiple questions is in development. Check it out!
This template is discussed in this forum thread.
The SimplePoll template generates a simple, one question multiple choice poll directly on the wiki. One vote is accepted for each registered user, and users may change their vote. The poll response data is stored in a page property; because it is not possible to secure this data under Lyons, for now this poll is recommended for entertainment purposes only. Usage instructions below will give recommendations on how to obscure the data, so it at least won't be trivially easy for evildoers to mess with it.
| Version | Date | Author | Description |
| 1.00 | 21-Aug-2009 | neilw |
|
| 1.01 | 26-August-2009 | neilw |
|
| 1.02 | 22-October-2009 | carles.coll |
|
| 1.03 | 23-October-2009 | neilw |
|
| 1.04 | 15-December-2009 | neilw |
|
| 1.05 | 16-December-2009 | neilw |
|
| 1.06 | 13-January-2010 | neilw |
|
| 1.07 | 18-March-2010 | varosi |
|
| 1.08 | 29-August-2010 | neilw |
|
For all the examples on this page, the code is shown before the working example. The code is shown with the syntax extension, and looks like this:
SimplePoll("Is it Friday yet?", [ "yes", "no" ])
This means that the actual code on the page should be enclosed in a Dekiscript block. If you want to copy the code from this page, then use the same procedure as described in steps 2-4 above.
| Name | Type | Default | Description |
| question | str? | blank | The question to be answered for the poll |
| answers | list of str | required | List of possible answers to the poll question |
| name | str? | "polldata" | Name of the poll. This defines the name of the property that will be used to store the data; by changing this value you have the ability to store the data from multiple polls on the same page, each in its own property. |
| path | str? | page.path | Path of page where poll data will be stored. Usage will frequently dictate that the poll is presented on a restricted page, which means that the poll data will need to be elsewhere, on a read/write page. By default, however, it is stored on the same page as the poll. |
| closed | (date or bool)? | false | Specifies when/if the poll is closed. May either be a valid date string (either GMT, or fully specified with GMT offset), which specifies when the poll is closed, or a boolean, which specifies if the poll is closed. Once a poll is closed, the user will not be allowed to change anything through this front end (the page property will still be fair game, though, unless you go protect that page manually). |
| color_bar | str? | "#B00000" | Javascript-compatible color value for the foreground bars in the poll results |
| fixed_behavior | str? | Automatic |
|
| language | str? |
| Language poll's standard texts. Supported language codes:
|
| halign | str? | center | Different horizontal alignment for the poll. |
There's not much to this. Here are some examples. I've forged the data for some to show some interesting results.
SimplePoll("Is it Friday yet?", [ "yes", "no" ])
| Is this template useful? | ||||||||
19 votes counted
|
SimplePoll {
question: "Is this template useful?",
answers: [ "Very", "Somewhat", "Not so much", "I'm losing brain cells just reading this" ],
name: "sample1",
closed: "1-Jan-2099",
fixed_behavior: "view_details"
}
| Is this template useful? | ||||||||
|
Note how the closing time for the poll is specified as a GMT time, and is converted to your local time for display in the "poll closes..." message.
Here's what it looks like when the poll is closed:
SimplePoll {
question: "How's the weather today in your neck of the woods?",
answers: [ "Cold", "Hot", "Sunny", "Rainy", "Snowy", "I live underground" ],
name: "sample2",
closed: true,
bar: "#0000B0"
}
| How's the weather today in your neck of the woods? | ||||||||||||
16 votes counted
|
By "securing the poll", we mean protecting it from tampering. Simply put, it is not possible to secure the poll using the features available in Lyons, since the page property is available for editing by anyone who is also empowered to answer the poll. This section will describe what you can do to make it a bit less easy to tamper with, but in any case the lack of security means that this poll should be used for entertainment purposes only.
You can view an example here: Example 1 - Protected Poll, without results
First, protect the page that contains the call to SimplePoll(). This will accomplish two things:
If you want the poll to reside on an unprotected page, then simply put the poll all by itself on a protected page, and pull it into the unprotected page via wiki.page().
In fact, once you've protected the calling page, you must store the data elsewhere, or else no one but you will have permission to answer the poll. By specifying the "path" argument, you can direct the page where the poll data is stored. You may pick any unrestricted page for this, but I would recommend one that would not normally be visited by users, to prevent "accidental" corruption of the data. For instance, you could create a page under your user page to store this data.
Want to make it a bit harder to find? Create a protected page, and then put the data store page underneath it (you must unprotect it of course). This way users will not be able to browse to the page, though they'll still be able to go straight there if they know where it is.
Fundamentals of this code were derived/borrowed/stolen from SteveB's Forms Demo.
// SimplePoll
// originally by neilw, 2009
// translated by carles.coll, 2009
//
// Version history:
// 1.00 20-August-2009 neilw First published version
// 1.01 26-August-2009 neilw Each poll uses a different channel and __request arg so
// multiple polls can coexist on the same page
// Changed display style of poll results a bit
// Added "bar" argument
// 1.02 21-October-2009 charles.coll Get the result detail (who)
// Added Fixed Behaviour parameter
// Now the parameter path works
// Multilanguage support
// 1.03 23-October-2009 neilw Cleaned up "View details" display and other minor code tweaks
// 1.04 15-December-2009 neilw Fixed a timezone-processing bug
// 1.05 16-December-2009 neilw Added Italian translation, thanks Giulio!
// 1.06 13-January-2010 neilw Fixed language processing problem
// 1.07 18-March-2010 varosi Added option to align poll differently
// 1.08 29-August-2010 neilw Fixed HTML entity syntax for 10.0
//
// Usage: SimplePoll(question:str, answers:list of str, name:str?, path:str?, closed:str? or bool?, color_bar:str?, fixed_behavior: str?, language: str?)
// question: The poll question
// answers: List of possible answers
// name: (optional) Unique name for the poll, in case you want to store multiple poll results
// on the same page properties
// path: (optional) Path to page where properties will be stored. Must be readable/writable
// by voters!
// closed: (optional) Either "true" to close the poll, or a valid date (GMT!!!!) string indicating
// when the poll should close
// color_bar: (optional) color of results bars (default: "#B00000")
// fixed_behavior: (optional) default -> Automatic.
// edit -> Edit Poll
// view -> View Poll Statistics
// view_details -> View who answers what
// language: (optional) default ->
// 1st -> Page language
// 2nd -> Site language
// 3rd -> 'en-us'
// halign: (optional) Horizontal poll alignment
// default -> center
//
//
// get initialized
//
var question = $0 ?? $question ?? "";
var answers = $1 ?? $answers ?? [];
var fixed_behavior = $fixed_behavior ?? "";
var language = __request.args.language ?? args.language ??
( page.language..'' != '' ? page.language : (site.language..'' != '' ? site.language : 'en-us') );
var halign = $halign ?? "center";
// -- START LANGUAGE STRINGS
var LANGUAGE_ES = {
warning_question: "CUIDADO: la pregunta tiene que ser una cadena de carecteres o un XML",
warning_no_answers: "CUIDADO: No has pasado respuestas. Así no podremos hacer un encuesta!",
error_no_data_page: "ERROR: no puedo encontrar la pagina con los datos de la encuesta",
error_invalid_close_date: "ERROR: 'cerrada' no es una fecha valida; asumo que la encuesta esta abierta",
error_invalid_data: "ERROR: los datos de la encuesta existen pero tienen un formato erroneo (tiene que ser de tipo 'map')",
error_updating_vote: "ERROR: actualizando el voto ",
error_creating_vote: "ERROR: creando el voto ",
error_reading_vote: "ERROR: leyendo el voto ",
txt_your_answer: "Tu respuesta: ",
txt_change_my_vote: "cambiar mi voto",
txt_vote: "votar en esta encuesta",
txt_view_results: "ver resultados",
txt_poll_closes_in: "La encuesta se cierra en ",
txt_votes: " votos contados",
txt_view_details: "ver detalles",
txt_poll_closed: "esta encuesta esta cerrada",
msg_poll_closed: "Ahora la encuesta esta cerrada, lo siento!",
button_submit: "guardar"
};
var LANGUAGE_EN = {
warning_question: "WARNING: question must be string or xml",
warning_no_answers: "WARNING: You have provided no answers. Not much of a poll!",
error_no_data_page: "ERROR: can't find page with data store",
error_invalid_close_date: "ERROR: 'closed' is not a valid date; assuming poll is open",
error_invalid_data: "ERROR: poll data store exists but has the wrong type (instead of 'map')",
error_updating_vote: "ERROR: updating poll ",
error_creating_vote: "ERROR: creating poll ",
error_reading_vote: "ERROR: reding poll ",
txt_your_answer: "Your answer: ",
txt_change_my_vote: "change my vote",
txt_vote: "vote in this poll",
txt_view_results: "view results",
txt_poll_closes_in: "Poll closes on ",
txt_votes: " votes counted",
txt_view_details: "view details",
txt_poll_closed: "this poll is closed",
msg_poll_closed: "Poll is now closed, sorry!",
button_submit: "submit"
};
var LANGUAGE_IT = {
warning_question: "ATTENZIONE: la domanda deve essere una stringa o xml",
warning_no_answers: "ATTENZIONE: non hai risposto. Così non riusciamo a fare un sondaggio!!",
error_no_data_page: "ERRORE: non trovo la pagina con i dati del sondaggio",
error_invalid_close_date: "ERRORE: 'chiuso' non è una data valida; si presume che il sondaggio sia aperto",
error_invalid_data: "ERRORE: lo store per i dati del sondaggio esiste ma ha un formato errato (deve essere di tipo 'map')",
error_updating_vote: "ERRORE: aggiornamento sondaggio ",
error_creating_vote: "ERRORE: creazione sondaggio ",
error_reading_vote: "ERRORE: lettura sondaggio ",
txt_your_answer: "La tua risposta: ",
txt_change_my_vote: "Cambia il mio voto",
txt_vote: "Vota il sondaggio",
txt_view_results: "Risultati",
txt_poll_closes_in: "Il sondaggio chiude il ",
txt_votes: " voti",
txt_view_details: "Dettaglio dei voti",
txt_poll_closed: "Questo sondaggio è finito",
msg_poll_closed: "Il sondaggio adesso è chiuso, spiacenti!",
button_submit: "submit"
};
var TXTS = {
en: LANGUAGE_EN, 'en-us': LANGUAGE_EN,
es: LANGUAGE_ES, 'es-es': LANGUAGE_ES,
it: LANGUAGE_IT, 'it-it': LANGUAGE_IT
};
var lg = language;
// -- END LANGUAGE STRINGS
if (question is not str && question is not xml) <p>TXTS[lg].warning_question;</p>;
if (#answers == 0) <p>TXTS[lg].warning_no_answers;</p>;
var CONST_POLLDATA_NAME = "poll_default_name";
var poll_name = $2 ?? $name?? CONST_POLLDATA_NAME;
if (poll_name is not str) {
/* <p>"ERROR: el nombre tiene que ser una cadena de caracteres. Le assignamos automáticamente el nombre '"..CONST_POLLDATA_NAME.."'"</p>; */
let poll_name = CONST_POLLDATA_NAME;
}
var poll_arg = "poll_"..poll_name;
var path = $3 ?? $path;
var p = (path == nil ? page : wiki.getpage(path));
var p_api = null;
if (p == nil) { <p>TXTS[lg].error_no_data_page;</p>;}
else { let p_api = p.api; }
var closed = $4 ?? $closed ?? false;
var closing_time = false;
if (closed is not bool) {
if (!date.isvalid(closed)) {
<p>TXTS[lg].error_invalid_close_date;</p>;
let closed = false;
}
else { // convert to local time
let closing_time = date.format(closed,"r");
let closed = date.compare(date.now, closing_time) > 0;
}
}
var bar = $5 ?? $color_bar ?? "#B00000";
var viewURI = page.uri & { (poll_arg):"view" };
var viewDetailsURI = page.uri & { (poll_arg):"view_details" };
var editURI = page.uri & { (poll_arg):"edit" };
// Fetch the data store
var data = json.parse(p.properties[poll_name].text ?? '{}');
if (data is not map)
<p>TXTS[lg].error_invalid_data</p>;
// Now figure out what to show
var vote = (data[user.name] ?? {}).poll;
var can_vote = !closed && !user.anonymous && wiki.pagepermissions(path).update;
var has_voted = (vote != nil);
var showform = (fixed_behavior=='edit') ||
(
(fixed_behavior=='') &&
(can_vote && (!has_voted || __request.args[poll_arg] == "edit")
&& __request.args[poll_arg] != "view"
&& __request.args[poll_arg] != "view_details"
)
);
<table align=(halign) cellpadding="5" style="background-color:#F4F4F4; border:2px solid #808080">
<tr><td align="center" style="font-weight:bold; padding-bottom:10px"> question </td></tr>;
if (showform) { // Allow user to enter or edit poll response(s)
<tr><td align="center"><form id=(@form)>
<ul style="text-align:left">
foreach (var opt in answers) {
if ((fixed_behavior=='edit') && (__request.args[poll_arg]!='edit') && (has_voted)) {
if(has_voted && vote==__index) { <B>TXTS[lg].txt_your_answer;</B>opt; }
}
else
{
<li style="list-style:none">
<input type="radio" name="poll"
value=(__index) checked=(has_voted && vote==__index ? 'checked' : nil)> " "..opt.." " </input>
</li>;
}
}
</ul>
<span style="text-align:center; padding-top:10px">
if ((fixed_behavior=='edit')&& (__request.args[poll_arg]!='edit') && (has_voted)) {
if (can_vote) <a href=(editURI)> has_voted ? TXTS[lg].txt_change_my_vote : TXTS[lg].txt_vote </a>;
}
else
{
<input type="button" value=(TXTS[lg].button_submit) ctor="when($this.click) {
var m = { };
Deki.$('form#' + {{@form}} + ' input').each(function() {
if ($(this).attr('name') == 'poll' && $(this).attr('checked')) m['poll'] = Deki.$(this).val();
});
Deki.publish({{@channel}}, { {{user.name}}:m }); }"/>; " ";
}
if(fixed_behavior=='') { <a href=(viewURI)> <span style="font-size:smaller">TXTS[lg].txt_view_results</span> </a> }
</span>
if (closing_time) {
<br />;
var ct = date.changetimezone(closing_time, user.timezone);
let ct = date.format(string.substr(ct,0,string.lastindexof(ct," ")),"MMM d, yyyy, h:mm tt");
<span style="font-size:smaller"> TXTS[lg].txt_poll_closes_in .. ct </span>;
}
</form></td></tr>;
}
else { // Display poll results
if ((fixed_behavior=='view') ||
( (fixed_behavior=='') &&
(__request.args[poll_arg] != "view_details")
)) {
// Calculate results
var total = #data;
<tr><td align="center">
<span style="font-size:smaller"> total .. TXTS[lg].txt_votes </span>;
<table>
foreach (var i in num.series(0,#answers-1)) {
var result = #map.select(data, "$.value.poll == "..i);
var pct = num.round(100*result/num.max(total,1),1);
var width = num.round(2 * pct, 0);
<tr>
<td> answers[i] </td>
<td>
<img src="/skins/common/icons/icon-trans.gif" style=("background-color:"..bar) height="10" width=(width) />
<img src="/skins/common/icons/icon-trans.gif" style="background-color:#C0C0C0" height="10" width=(200-width) />
" " .. pct .. "% (" .. result .. ")"
</td>
</tr>;
}
</table>
if (fixed_behavior=='') {
<span style="text-align:center; padding-top:10px; font-size:smaller">
<a href=(viewDetailsURI)>TXTS[lg].txt_view_details</a>;
; ;
if (can_vote) <a href=(editURI)> has_voted ? TXTS[lg].txt_change_my_vote : TXTS[lg].txt_vote </a>;
else if (closed) TXTS[lg].txt_poll_closed;
</span>;
}
</td></tr>;
}
else {
// Show details
<tr><td align="center">
<table>
<tr>
foreach (var i in num.series(0,#answers-1)) {
<th style="text-align:center; border:1px solid #606060"> answers[i] </th>
}
</tr>;
<tr>
foreach (var i in num.series(0,#answers-1)) {
var result = Map.Keys(map.select(data, "$.value.poll == "..i));
<td valign="top" style="text-align:center">
if (#result) {
<span style="font-size:.8em">"("..#result.." votes)" </span>;
<br />;
}
foreach (var who in result) {
if (__index) <br />;
who;
}
</td>
}
</tr>;
</table>
if(fixed_behavior=='') {
<span style="text-align:center; padding-top:10px; font-size:smaller">
<a href=(viewURI)> <span style="font-size:smaller">TXTS[lg].txt_view_results</span> </a>
; ;
if (can_vote) <a href=(editURI)> has_voted ? TXTS[lg].txt_change_my_vote : TXTS[lg].txt_vote </a>;
else if (closed) "esta encuesta esta cerrada";
</span>;
}
</td></tr>;
}
}
</table>;
// Code to update the page properties, largely cribbed from SteveB's "UpdateStore" template
dekiapi();
var store = poll_name;
<script type="text/javascript"> "
Deki.subscribe('"..@channel.."', null, function(c, m, d) {
var closed = " .. json.emit(closed) .. ";
var closing_time = " .. json.emit(closing_time) .. ";
var d = new Date();
if (closed || (closing_time && d.getTime() > Date.parse(closing_time))) {
alert('"..TXTS[lg].msg_poll_closed.."');
window.location.href = '" .. page.uri .. "';
return;
}
var prop = 'urn:custom.mindtouch.com#' + '"..store.."';
Deki.Api.ReadPageProperty('"..p_api.."', prop, function(result) {
var data = eval('(' + (result.value || '{}') + ')');
for (var k in m) data[k] = m[k];
if(result.etag)
Deki.Api.UpdatePageProperty(result.href, YAHOO.lang.JSON.stringify(data), result.etag,
function() { window.location.href = '" .. page.uri .. "'; },
function(result) { alert('"..TXTS[lg].error_updating_vote.." (esatdo: ' +
result.status + ' - ' + result.text + ')'); }
);
else
Deki.Api.CreatePageProperty('"..p_api.."', prop, YAHOO.lang.JSON.stringify(data),
function() { window.location.href = '" .. page.uri .. "'; },
function(result) { alert('"..TXTS[lg].error_creating_vote.." (esatdo: ' +
result.status + ' - ' + result.text + ')'); }
);
},
function(result) { alert('"..TXTS[lg].error_reading_vote.." (esatdo: ' +
result.status + ' - ' + result.text + ')'); }
);
}, null);
" </script>
None.
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by
I tried voting while not logged in and got a popup asking me to login. Hitting Cancel leads me to a 401 - Unable to update poll.
I understand this is an expected behaviour but...Is there a way to update properties even if the voter isn't logged?
What browser and OS are you using?
Can you create an example on your personal page on this wiki that shows the problem?
I saw your posting but haven't had a chance to look into it. You can try filing a bug.
Sure, it's possible. I'm not sure exactly when I'll get around to it, but I'll see what I can do. The results would have to be presented differently, since percentages don't make sense when multiple answers are possible. How would you like the results to look?