Skip to content

Commit c29f47e

Browse files
committed
Docs for the views
1 parent 523f01a commit c29f47e

9 files changed

Lines changed: 342 additions & 160 deletions

File tree

docs/docs/how-to-guides/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ This part of the documentation contains how-to guides, including installation an
1818
https://diataxis.fr/how-to-guides/
1919
```
2020

21+
## Polls
22+
23+
```{toctree}
24+
:maxdepth: 1
25+
26+
vote-on-a-poll
27+
```
2128

2229
## Authors
2330

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
myst:
3+
html_meta:
4+
"description": "How to vote on a poll and read the results in experimental.doodle"
5+
"property=og:description": "How to vote on a poll and read the results"
6+
"property=og:title": "Vote on a poll"
7+
"keywords": "Plone, experimental.doodle, poll, vote, results"
8+
---
9+
10+
# Vote on a poll
11+
12+
This guide explains how a visitor casts a vote on a published Poll and
13+
reads the aggregated results.
14+
15+
## Prerequisites
16+
17+
- A Plone site with `experimental.doodle` installed.
18+
- At least one published Poll with two or more time slots.
19+
20+
## Cast a vote
21+
22+
1. Open the Poll in your browser. The default view shows the vote form.
23+
2. Enter your name in the **Your name** field. Any non-empty string is
24+
accepted; no account is needed.
25+
3. For each proposed time slot, select **Yes** or **No**. Every slot
26+
defaults to **No** when the page loads.
27+
4. Click **Cast vote**. The page reloads and confirms your vote was
28+
recorded. You can vote again at any time. Casting a second vote with
29+
the same name replaces your earlier response.
30+
31+
## Read the results
32+
33+
Click **View results** below the vote form, or navigate to the Poll's
34+
`@@results` view directly (append `/@@results` to the Poll address). The
35+
results page shows:
36+
37+
- A table with each time slot and the number of **Yes** votes it received.
38+
- A proportional bar for each slot so you can see relative support at a
39+
glance.
40+
- A list of participants who have voted.
41+
42+
Click **Back to poll** to return to the vote form.
43+
44+
## Create a poll (for editors)
45+
46+
1. Navigate to the folder where you want to create the poll.
47+
2. Add a new **Poll** content item.
48+
3. Enter a **title** and, optionally, a **description**.
49+
4. Add at least two **time slots** using the date/time picker for each
50+
entry in the **Proposed time slots** field.
51+
5. Save the item and publish it so visitors can reach it without logging
52+
in.
53+
54+
## Related references
55+
56+
- {doc}`/reference/poll-content-type`: the Poll schema and FTI details.
57+
- {doc}`/reference/vote-storage`: the Python API behind the vote form.

docs/docs/reference/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ poll-content-type
3434
vote-storage
3535
```
3636

37+
## Views
38+
39+
```{toctree}
40+
:maxdepth: 1
41+
42+
poll-views
43+
```
44+
3745
## Configuration
3846

3947
- {doc}`plone:contributing/documentation/themes-and-extensions`

docs/docs/reference/poll-views.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
myst:
3+
html_meta:
4+
"description": "Reference for the Poll browser views in experimental.doodle"
5+
"property=og:description": "Reference for the Poll browser views"
6+
"property=og:title": "Poll views"
7+
"keywords": "Plone, experimental.doodle, poll, view, results, browser"
8+
---
9+
10+
# Poll views
11+
12+
`experimental.doodle` ships two browser views for `Poll` objects, both
13+
registered against `IPoll` on `IBrowserLayer`.
14+
15+
## Vote form (`@@poll_view`)
16+
17+
**Class:** `experimental.doodle.browser.poll.PollView`
18+
**Permission:** `zope2.View` (no login required)
19+
**Default view:** yes (`default_view` on the FTI since profile version `1002`)
20+
21+
The vote form renders the poll title, description, and a `<form>` with:
22+
23+
- a text field for the participant's name;
24+
- one `yes`/`no` radio pair per proposed time slot;
25+
- a submit button.
26+
27+
### Form submission
28+
29+
On submit the view:
30+
31+
1. Strips the `name` field; returns a form error if the result is empty.
32+
2. Reads `votes_0`, `votes_1`, … from the request; absent values default
33+
to `"false"`.
34+
3. Calls `IVoteStorage(context).cast_vote(name, votes)`.
35+
4. Redirects to `@@poll_view` (Post/Redirect/Get pattern).
36+
37+
Any `ValueError` from `cast_vote` is caught and shown as an inline error
38+
without a redirect.
39+
40+
### Template helpers
41+
42+
| Property | Returns |
43+
|---|---|
44+
| `options` | `[(index, label), ...]` where `label` is a human-readable date/time string. |
45+
| `error` | The current error string, or `None`. |
46+
| `participant_count` | Number of participants who have already voted. |
47+
48+
## Results (`@@results`)
49+
50+
**Class:** `experimental.doodle.browser.poll.PollResultsView`
51+
**Permission:** `zope2.View`
52+
53+
The results view shows the aggregated tally and a participant list. It
54+
is read-only; there is no form or POST handling.
55+
56+
### Template helpers
57+
58+
| Property | Returns |
59+
|---|---|
60+
| `rows` | `[(label, yes_count, max_count), ...]`, one row per slot. `max_count` is the highest yes-count across all slots, used to compute proportional bar widths. |
61+
| `participants` | `list[str]`, participant names sorted alphabetically. |
62+
63+
A `@@results` link appears on the vote form. A "Back to poll" link
64+
returns to `@@poll_view` from the results.
65+
66+
## Styles
67+
68+
Both views include `++plone++experimental.doodle/poll.css` from
69+
`src/experimental/doodle/browser/static/`. The stylesheet covers only
70+
poll-specific elements and makes no global overrides.
71+
72+
## Profile version
73+
74+
The FTI change (`default_view` set to `poll_view`, `results` added to
75+
`view_methods`) ships at profile version `1002`. Sites on `1001` can
76+
upgrade through the {guilabel}`Add-ons` control panel or
77+
programmatically:
78+
79+
```python
80+
from plone import api
81+
82+
api.portal.get_tool("portal_setup").upgradeProfile(
83+
"experimental.doodle:default"
84+
)
85+
```
86+
87+
## Related
88+
89+
- {doc}`vote-storage`: the adapter the views delegate to.
90+
- {doc}`poll-content-type`: the content type the views render.
91+
- {doc}`/how-to-guides/vote-on-a-poll`: step-by-step voting guide.

src/experimental/doodle/browser/poll.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ def __call__(self):
4242
def options(self):
4343
"""Return ``[(index, label), ...]`` for every proposed slot."""
4444
return [
45-
(i, _format_slot(dt))
46-
for i, dt in enumerate(self.context.options or [])
45+
(i, _format_slot(dt)) for i, dt in enumerate(self.context.options or [])
4746
]
4847

4948
@property

0 commit comments

Comments
 (0)