Commit Graph

6175 Commits

Author SHA1 Message Date
taitus
b1cb6f8372 Exclude open-ended questions from managing physical votes
Also make the :yes_no factory trait create a votation_type_unique
by default, since yes/no questions should always be unique.
2025-10-16 14:31:16 +02:00
taitus
f3050a1aa5 Manage correctly results and stats for open-ended questions
Note that we are not including Poll::PartialResults for open-ended
questions resutls. The reason is that we do not contemplate the
possibility of there being open questions in booths. Manually
counting and introducing the votes in the system is not feasible.
2025-10-16 14:26:30 +02:00
taitus
2a2edd17d1 Move results specs to Polls::ResultsComponent
Running tests at the component level is faster than at the system level,
so we move tests from system/polls/results_spec.rb to the component.

Note that moving these tests removes vote_for_poll_via_web and the visit
to results_poll_path, but both are already covered in other tests. We
also take the opportunity to reuse the method in another test where
it makes sense.

Additionally, the spec title has been reverted from "Results for polls
with questions but without options" to "renders results for polls with
questions but without answers", as it was before commit 8997ed316c.
2025-10-16 11:09:36 +02:00
taitus
83b206f0b7 Enable voting for open-ended questions in public section 2025-10-16 11:09:36 +02:00
taitus
62e1c13e7e Use option instead of answer text to find multiple answers 2025-10-16 11:09:36 +02:00
taitus
b4b00487cc Add validations for changing votation type 2025-10-16 11:09:34 +02:00
taitus
b3f8ba819b Adapt 'show' view for open questions without options
- Prevent creating options for open questions
- Skip rendering the options table when none exist
2025-10-15 15:52:14 +02:00
taitus
d3f32978c8 Hide "Maximum number of votes" message for unique and open-ended questions
The "Maximum number of votes" text in poll question show was unnecessary.
It appeared for both unique and open-ended questions, but it only makes
sense for questions that allow multiple answers.
2025-10-15 15:52:14 +02:00
taitus
69eaf66b93 Remove redundant max_votes validation from Poll::Answer
Since commit 8deb1964b, the `WebVote` class enforces the maximum vote
validation, making the `max_votes` method in `Poll::Answer` redundant.
2025-10-15 15:52:14 +02:00
taitus
4e57e311dc Add support for open-ended questions in admin section
Introduce a new "open" votation type for poll questions in the admin
interface. This type allows open answers provided by the user.
2025-10-15 15:52:12 +02:00
taitus
2a5985f6ef Update tests for votation type form behavior
Ensure the form toggles descriptions and fields correctly depending on the selected
votation type.
2025-09-26 15:41:09 +02:00
taitus
3a9f761476 Count total_votes by option_id instead of answer title
This makes Option#total_votes independent of translations
and resilient to title changes.
2025-09-26 15:25:20 +02:00
taitus
24239c98e3 Delete duplicate records in different languages
Also logs a message when duplicates have different amounts, keeping the
first partial result and deleting the others.
2025-09-26 15:05:40 +02:00
taitus
ed2a25663b Add task to add option_id to existing partial results 2025-09-26 15:05:40 +02:00
taitus
e286ee6943 Add task to delete duplicate poll partial results
Adds rake task "polls:remove_duplicate_partial_results" to delete duplicated
rows in "poll_partial_results" made before the DB was strict about duplicates.

Duplicates are considered only for records without "option_id", grouping by:
(question_id, booth_assignment_id, date, answer). We keep the first one and
delete the rest, per tenant.

The controller use:
  Poll::PartialResult.find_or_initialize_by(booth_assignment_id, date, question_id, answer)
which is not a strong protection against race conditions. Without a unique
index at the DB level, duplicates could be created. This task cleans up any
existing duplicates.
2025-09-26 15:05:40 +02:00
taitus
a29eeaf2e2 Add option_id to partial results and unique index
Similar to what we did in PR "Avoid duplicate records in poll answers" 5539,
specifically in commit 503369166, we want to stop relying on the plain text
"answer" and start using "option_id" to avoid issues with counts across
translations and to add consistency to the poll_partial_results table.

Note that we also moved the `possible_answers` method from Poll::Question to
Poll::Question::Option, since the list of valid answers really comes from the
options of a question and not from the question itself. Tests were updated
to validate answers against the translations of the assigned option.

Additionally, we renamed lambda parameters in validations to improve clarity.
2025-09-26 15:05:34 +02:00
taitus
7f376c3005 Extract admin poll results to component
Note that we have the same code in the officing section.
Then we can use the same component.

Note also that we are removing the parts of the system specs that are now
covered by the component itself, and taking the chance to unify tests.
In these removals and unifications we take into account that there are
other specs which already cover user interaction in this section.
2025-09-26 09:58:17 +02:00
Javi Martín
3cf6e9b1ca Merge pull request #6046 from Anamika1608/oidc_auth
Add support for OIDC authentication
2025-09-01 19:55:10 +02:00
Anamika Aggarwal
5e263baed2 Add OIDC section for sign in and sign up page
- name: :oidc → Identifier for this login provider in the app.
- scope: [:openid, :email, :profile] → Tells the provider we want the user’s ID (openid), their email, and basic profile info (name, picture, etc.).
- response_type: :code → Uses Authorization Code Flow, which is more secure because tokens are not exposed in the URL.
- issuer: Rails.application.secrets.oidc_issuer → The base URL of the OIDC provider (e.g., Auth0). Used to find its config.
- discovery: true → Automatically fetches the provider’s endpoints from its discovery document instead of manually setting them.
- client_auth_method: :basic → Sends client ID and secret using HTTP Basic Auth when exchanging the code for tokens.

Add system tests for OIDC Auth

Edit the oauth docs to support OIDC auth
2025-08-29 12:20:16 +02:00
Javi Martín
6da53b5716 Add unique index to poll voters table
Note that Rails 7.1 changes `find_or_create_by!` so it calls
`create_or_find_by!` when no record is found, meaning we'll rarely get
`RecordNotUnique` exceptions when using this method during a race
condition.

Adding this index means we need to remove the uniqueness validation.
According to the `create_or_find_by` documentation [1]:

> Columns with unique database constraints should not have uniqueness
> validations defined, otherwise create will fail due to validation
> errors and find_by will never be called.

We're adding a test that checks what happens when using
`create_or_find_by!`.

Note that we're creating voters combining `create_with` with
`find_or_create_by!`. Using `find_or_create_by!(...)` with all
attributes (including non-key ones like `origin`) fails when a voter
already exists with different values, e.g. an existing `origin: "web"`
and an incoming `"booth"`. In this situation the existing record is not
matched and the unique index raises an exception.

`create_with(...).find_or_create_by!(user: ..., poll: ...)` searches by
the unique key only and applies the extra attributes only on creation.
Existing voters are returned unchanged, which is the intended behavior.

For the `take_votes_from` method, we're handling a (highly unlikely, but
theoretically possible) scenario where a user votes at the same time as
taking voters from another user. For that, we're doing something similar
to what `create_or_find_by!` does: we're updating the `user_id` column
inside a new transaction (using a new transactions avoids a
`PG::InFailedSqlTransaction` exception when there are duplicate
records), and deleting the existing voter when we get a
`RecordNotUnique` exception.

On `Poll::WebVote` we're simply raising an exception when there's
already a user who's voted via booth, because the `Poll::WebVote#update`
method should never be called in this case.

We still need to use `with_lock` in `Poll::WebVote`, but not due to
duplicate voters (`find_or_create_by!` method will now handle the unique
record scenario, even in the case of simultaneous transactions), but
because we use a uniqueness validation in `Poll::Answer`; this
validation would cause an error in simultaneous transactions.

[1] https://api.rubyonrails.org/v7.1/classes/ActiveRecord/Relation.html#method-i-create_or_find_by
2025-08-28 14:42:30 +02:00
Javi Martín
03c5533cf0 Don't allow users who voted in a booth to vote via web
For the longest time, we've disabled the buttons to vote via web when
people had already voted in a booth. However, we were still allowing
HTTP requests to the actions to vote via web.

So we're adding a condition to prevent it.

The reason why we're changing the controller instead of the abilities
model (which is what we usually do) is that there might be side-effects
to the change. For instance, in the `Polls::PollComponent` class,
there's an `elsif cannot?(:answer, poll)` condition which would have a
different behavior if we changed the abilities model.
2025-08-28 14:42:30 +02:00
Javi Martín
de303aa1df Extract component to render dashboard polls
Just like we usually do when reorganizing code.
2025-08-27 17:40:45 +02:00
taitus
fc0d79b47b Move dashboard poll partial to component 2025-08-27 17:40:45 +02:00
Javi Martín
150af75e3e Make resources component tests more readable
We're testing things from the user's point of view by finding elements
given their texts, instead of checking for elements with a certain ID.
2025-08-27 17:40:45 +02:00
taitus
1e06e676a4 Move dashboard system tests to resources component specs
We're making the `new_actions_since_last_login` parameter optional in
order to simplify the tests.
2025-08-27 17:40:45 +02:00
taitus
048c8ce917 Move dashboard resources partial to a component
We're renaming the HTML class (and removing the id attribute, which was
only used in tests) in order to follow our naming conventions.
2025-08-27 17:40:45 +02:00
Javi Martín
1b9d321c4e Extract methods in poll status component
This way we remove duplication in the HTML.

We're also adding a test checking what happens when users can vote in
order to test the `render?` method we've added.
2025-08-22 13:09:49 +02:00
Javi Martín
b2a49cd291 Extract component to render the status of a poll
We're renaming the existing HTML class in order to be consistent with
the name of the component.
2025-08-22 12:13:41 +02:00
Javi Martín
8deb1964bd Show errors when submitting too many answers
This could be the case when JavaScript is disabled.

Note that, in `Poll/WebVote` we're calling `given_answers` inside a
transaction. Putting this code before the transaction resulted in a test
failing sometimes, probably because of a bug that might be possible to
reproduce by doing simultaneous requests.
2025-08-14 13:06:43 +02:00
werdenktwas-gmbh
abf02808bf Disable other answers when reaching maximum votes
This is similar to the way we were disabling buttons in the old design.

Co-authored-by: Javi Martín <javim@elretirao.net>
2025-08-14 13:06:43 +02:00
Javi Martín
7ea4f63b07 Allow blank votes in polls via web
With the old interface, there wasn't a clear way to send a blank ballot.
But now that we've got a form, there's an easy way: clicking on "Vote"
while leaving the form blank.
2025-08-14 13:06:43 +02:00
Javi Martín
5402cb6042 Move poll callout partial to a component
This way it'll be easier to refactor it.

Note there was a system test which tested both the callout and the form
when unverified users visit a poll. We've split this system test in two
component tests.
2025-08-14 13:06:43 +02:00
Javi Martín
a7e1b42b6c Use checkboxes and radio buttons on poll forms
Our original interface to vote in a poll had a few issues:

* Since there was no button to send the form, it wasn't clear that
  selecting an option would automatically store it in the database.
* The interface was almost identical for single-choice questions and
  multiple-choice questions, which made it hard to know which type of
  question we were answering.
* Adding other type of questions, like open answers, was hard since we
  would have to add a different submit button for each answer.

So we're now using radio buttons for single-choice questions and
checkboxes for multiple-choice questions, which are the native controls
designed for these purposes, and a button to send the whole form.

Since we don't have a database table for poll ballots like we have for
budget ballots, we're adding a new `Poll::WebVote` model to manage poll
ballots. We're using WebVote instead of Ballot or Vote because they
could be mistaken with other vote classes.

Note that browsers don't allow removing answers with radio buttons, so
once somebody has voted in a single-choice question, they can't remove
the vote unless they manually edit their HTML. This is the same behavior
we had before commit 7df0e9a96.

As mentioned in c2010f975, we're now adding the `ChangeByZero` rubocop
rule, since we've removed the test that used `and change`.
2025-08-14 13:06:37 +02:00
Javi Martín
fd14c55615 Make answer depend on the option in poll answer factory
Until now, when writing `create(:poll_answer, option: option)`, the
`answer` field would take the title of a random option instead of taking
the title from the `option` variable.

So now, if we're given the option, the `answer` field will be taken from
the option itself.

Note that writing something like:

```
option { question.question_options.find_by(title: answer) }
answer { option.title }
```

Would create an infinite loop when creating a poll answer if we don't
pass the `option` and/or the `answer` attributes.

So, instead, we're making the `option` depend on the `answer` attribute
exclusively when we pass the `answer` attribute to the factory.
2025-08-14 12:58:51 +02:00
Javi Martín
a5054089b8 Fix typo in poll voter test 2025-08-12 12:45:12 +02:00
Javi Martín
6c5b908ef5 Use a loop instead of with_collection to render questions
This is what we usually do in components.
2025-08-12 12:45:12 +02:00
Anamika Aggarwal
c9bf7797a0 Add multi-tenancy support for SAML 2025-07-23 14:43:45 +02:00
taitus
a4709f9da0 Add omniauth saml section for sign in and sign up page
Co-authored-by: Anamika Aggarwal <anamikaagg18@gmail.com>
2025-07-23 14:43:44 +02:00
Sebastia
f4189365ea Merge pull request #5955 from cyrillefr/ReplaceLinkWithButtonInVariousComponentsPartI
Replace link with button in various components part i
2025-07-09 15:26:38 +02:00
cyrillefr
f0e696b972 Replace link with button in letter verification 2025-07-09 13:48:58 +02:00
cyrillefr
c989210e74 Replace link with button in finish signup in registrations 2025-07-09 13:48:58 +02:00
cyrillefr
3eba2d27a4 Replace link with button in Topics show 2025-07-09 13:48:58 +02:00
cyrillefr
50076504b5 Replace link with button in flag unflag actions 2025-07-09 13:48:58 +02:00
cyrillefr
a6046155a2 Replace link with button in relationable scores 2025-07-09 13:48:58 +02:00
cyrillefr
d4b7939d2c Replace link with button in Proposal created 2025-07-09 13:48:58 +02:00
cyrillefr
ddd34d5cd0 Replace link with button in index notifications 2025-07-09 13:48:58 +02:00
cyrillefr
75ed7ae3ef Replace link with button in nofications 2025-07-09 13:48:58 +02:00
cyrillefr
267dd931d8 Replace link with button in change user link 2025-07-09 13:48:58 +02:00
cyrillefr
e370a52650 Replace link with buttons in follow button 2025-07-09 13:48:58 +02:00
cyrillefr
31ceb31256 Replace link with button in Dashboard show 2025-07-03 11:14:53 +02:00