Commit Graph

2779 Commits

Author SHA1 Message Date
taitus
018b00cd6e Allow managing versions of cookies consent
This can be useful when adding a new cookie or making
modifications that require asking the user again.
2025-01-23 17:16:57 +01:00
taitus
6753505e7c Allow administrators to define the cookies vendors the application uses 2025-01-23 17:03:30 +01:00
taitus
d7f701cc9a Add an optional setting with the link to the cookies policy page 2025-01-23 16:48:54 +01:00
taitus
4c0b6455f6 Add cookies consent banner
Allow enabling from settings admin section.

Note that we set the z-index to 20 in order to will be greater than
the others z-index elements in the application like <header> on
mobile devices.
2025-01-23 16:05:40 +01:00
Javi Martín
0eb62f9b9c Merge pull request #5616 from coslajohn/maps
Render Geozone Maps Collections and Multipolygons
2024-12-23 18:01:12 +01:00
Javi Martín
1f627d34f1 Make sure polygons contain valid rings
According to the GeoJSON specification [1]:

> * A linear ring is a closed LineString with four or more positions.
> * The first and last positions are equivalent, and they MUST contain
>   identical values; their representation SHOULD also be identical.
> (...)
> * For type "Polygon", the "coordinates" member MUST be an array of
>   linear ring coordinate arrays.

Note that, for simplicity, right now we aren't checking whether the
coordinates are defined counterclockwise for exterior rings and
clockwise for interior rings, which is what the specification expects.

[1] https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6
2024-12-23 17:35:33 +01:00
Javi Martín
c3bda443a6 Make sure all lines in a MultiLineString are valid
Note we're starting to use hashes in tests because the objects here are
complex and using hashes makes the tests easier to read.
2024-12-23 17:35:33 +01:00
Javi Martín
9ef68f863a Make sure a LineString has at least two points
According to the GeoJSON specification [1]:

> For type "LineString", the "coordinates" member is an array of two or
> more positions.

Note that the same doesn't seem to apply to a MultiPoint [2]:

> For type "MultiPoint", the "coordinates" member is an array of
> positions.

[1] https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.4
[2] https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.3
2024-12-23 17:35:33 +01:00
CoslaJohn
cb8b0ad6ff Support different colors and headings on each feature
We're making sure each feature contains properties in order to avoid
possible JavaScript errors.

We're also adding a default color to a geozone.
2024-12-23 17:35:33 +01:00
CoslaJohn
5dbe2cbf24 Support FeatureCollection and MultiPolygon in geozones
We're reworking the format validation to correctly interpret feature
collection, feature, and geometry, according to RFC 7946 [1].

Since Leaflet interprets GeoJSON format, we're rendering the GeoJSON as
a layer instead of as a set of points. For that, we're normalizing the
GeoJSON to make sure it contains either a Feature or a
FeatureCollection. We're also adding the Leaflet images to the assets
path so the markers used for point geometries are rendered correctly.

Note we no longer allow a GeoJSON containing a geometry but not a
defined type. Since there might be invalid GeoJSON in existing Consul
Democracy databases, we're normalizing these existing geometry objects
to be part of a feature object.

We're also wrapping the outline points in a FeatureCollection object
because most of the large GIS systems eg ArcGIS, QGIS export geojson as
a complete FeatureCollection.

[1] https://datatracker.ietf.org/doc/html/rfc7946

Co-authored-by: Javi Martín <javim@elretirao.net>
2024-12-23 17:35:33 +01:00
Javi Martín
6ab51b0367 Remove the ahoy_events table
We stopped using ahoy events in commit f7e2d724d.
2024-11-13 15:55:20 +01:00
Javi Martín
3b7948a139 Use a date field to select the date of birth
The default `date_select` used in fields presents an accessibility
issue, because in generates three select controls but only one label.
That means that there are two controls without a label.

So we're using a date field instead. This type is field is supported by
about 99% of the browsers, and we've already got JavaScript code
converting this field to a jQuery UI datepicker in case the browser
doesn't support date fields.

Note that, since we no longer need to parse the three date fields into
one, we can simplify the code in both the models and the tests.

Another slight improvement is that, previously, we couldn't restrict the
month and day controls in order to set the minimum date, so the maximum
selectable date was always the 31st of December of the year set by the
minimum age setting. As seen in the component test, now that we use only
one field, we can set a specific date as the maximum one.
2024-11-12 15:15:34 +01:00
Javi Martín
07202fea10 Add and apply Style/RedundantBegin rubocop rule
We're about to add code which might fall into the `RedundantBegin`
category, so we're adding the rule in order to prevent that.
2024-10-30 15:57:44 +01:00
Javi Martín
54a48d63e1 Use separate actions to select/deselect investments
This is consistent to what we usually do. Also, we're applying the same
criteria mentioned in commit 72704d776:

> We're also making these actions idempotent, so sending many requests
> to the same action will get the same result, which wasn't the case
> with the `toggle` action. Although it's a low probability case, the
> `toggle` action could result in [selecting an investment] when trying
> to [deselect] it if someone else has [deselected it] it between the
> time the page loaded and the time the admin clicked on the
> "[Selected]" button.
2024-10-28 13:41:50 +01:00
Javi Martín
cf0d8258ed Use abilities to allow toggling investment selection
We were checking it in the view, meaning that it was possible to toggle
the selection by sending a custom request even when the investment
wasn't feasible.
2024-10-28 13:39:49 +01:00
taitus
93189d3ecd Allow use embedded_video_component in legislation proposals
Since the PR "Do not use third-party cookies in embedded videos #5548", the logic from
"embed_videos_helper" was extracted to the "embedded_video_component" and the
"videoable" model concern.

However, during this refactor, the "regex" method, which uses record.class:: to handle
video embeds, was left inaccessible for Legislation Proposals.

This commit fixes the issue by including the concern in the Legislation Proposal model.
2024-10-14 15:24:29 +02:00
Sebastia
d3a039040c Merge pull request #5722 from consuldemocracy/dependabot/bundler/rubocop-rails-2.26.2
Bump rubocop-rails from 2.25.1 to 2.26.2
2024-10-10 15:03:08 +02:00
taitus
c50452aec6 Add and apply Rails/EnumHash rubocop rule
In rubocop-rails 2.26.0, support was added for Rails 7 syntax in the
Rails/EnumHash rule. We took this opportunity to ensure consistency
by converting all enums to hash with integer values. This format minimizes
the risk of data consistency issues in the database when adding new values.
2024-10-10 09:56:44 +02:00
taitus
3d4f78a424 Add an apply Rails/EnumSyntax rubocop rule
This rule was added in rubocop-rails 2.26.0. Applying it allows
us to anticipate the deprecation of the current enum syntax
using keyword arguments, which is set to be removed in Rails
8.0, as mentioned in the rule's own documentation:

https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsenumsyntax
2024-10-10 09:55:48 +02:00
taitus
4dcac5bed5 Add and apply Naming/RescuedExceptionsVariableName rubocop rule
This rule was introduced in RuboCop 0.67.2, but now after seeing a fix in version 1.65.1,
we have decided to add it. The reason for adding it is to ensure consistency in how we
reference exceptions throughout the project, by following a standard naming convention
for exception variables.
2024-10-10 09:47:47 +02:00
taitus
6b15a073a2 Add and apply Style/RedundantRegexpArgument RuboCop rule
This rule was introduced in RuboCop 1.53.0. After adding the
Style/RedundantRegexpCharacterClass rule in the previous commit,
RuboCop started detecting redundant regular expression arguments.
Therefore, we apply this rule to remove them and prevent future
occurrences.
2024-10-10 09:47:47 +02:00
taitus
d94eed8628 Add and apply Style/RedundantRegexpCharacterClass rubocop rule
This rule was introduced in RuboCop 0.93.0, but now after seeing a fix in version 1.65,
we have decided to add it. The reason for adding it is to simplify our regular
expressions. This enforcement will help us maintain better regular expression
practices across the project.
2024-10-09 09:33:35 +02:00
cyrillefr
18323a36c3 Add new GraphQL type for milestones
- added the milestone type to be displayed with investments
- the corresponding spec
2024-09-30 11:14:01 +02:00
cyrillefr
5ec6337d47 Add new GraphQL types for budget investments
- added 2 new types
- modified the models to get data through graphQL
- modified the corresponding spec
- also testing that hidden comments do not show up
- modified comments specs bc now it returns comments on budget
  investments
2024-09-30 11:14:01 +02:00
Javi Martín
00c97ad587 Split polls date range validation
It was a bit strange to leave the end date blank and have a message
associated with the start date, so we're using presence validations
instead.

For the range validation, we're using the comparison validator included
in Rails 7.0.
2024-07-22 18:35:35 +02:00
Javi Martín
da86254fe5 Use comparison validation to validate dates
The `validates_comparison_of` method was added in Rails 7.0.

We aren't changing the `date_range` validation in polls yet because it's
a bit complex; we'll do it in the next commit.
2024-07-22 18:35:35 +02:00
Javi Martín
38ad65605e Use excluding instead of where.not(id:
This method was added in Rails 7.0 and makes the code slihgtly more
readable.

The downside is that it generates two queries instead of one, so it
might generate some confusion when debugging SQL queries. Its impact on
performance is probably negligible.
2024-07-22 18:35:35 +02:00
Javi Martín
9841a9b03a Use in_order_of to sort translations by fallback
This method was introduced in Rails 7.0, and thanks to it we can
simplify the code that gets the translations in order.

We tried to use this method to simplify the `Randomizable` concern as
well. However, we found out that, when ordering tens of thousands of
records, the query could take several minutes, so we aren't using it in
this case. Using it for translation fallbacks is OK, since there's a
good chance we're never going to have tens of thousands of available
locales.

Note that automated security tools reported a false positive related to
SQL Injection due to the way we used `LEFT JOIN`, so now we get one less
false positive in these reports.
2024-07-22 17:50:40 +02:00
Javi Martín
46dc4a3163 Add and apply Style/MapIntoArray rubocop rule
This rule was added in rubocop 1.63.0.
2024-07-09 11:23:02 +02:00
Javi Martín
2abe9f27b5 Use ranges instead of comparisons in SQL queries
These cases aren't covered by the `Rails/WhereRange` rubocop rule, but
IMHO using ranges makes them more consistent. Besides, they generate SQL
which is more consistent with what Rails usually generates. For example,
`Poll.where("starts_at <= :time and ends_at >= :time", time:
Time.current)` generates:

```
SELECT \"polls\".\"id\", (...) WHERE \"polls\".\"hidden_at\" IS NULL AND
(starts_at <= '2024-07-(...)' and ends_at >= '2024-07-(...)')
```

And `Poll.where(starts_at: ..Time.current, ends_at: Time.current..)`
generates:

```
SELECT \"polls\".\"id\", (...) WHERE \"polls\".\"hidden_at\" IS NULL AND
\"polls\".\"starts_at\" <= '2024-07-(...)' AND \"polls\".\"ends_at\" >=
'2024-07-(...)'"
```

Note that the `not_archived` scope in proposals slightly changes, since
we were using `>` and now we use the equivalent of `>=`. However, since
the `created_at` field is a time, this will only mean that a proposal
will be archived about one microsecond later.

For consistency, we're also changing the `archived` scope, so a proposal
is never archived and not archived at the same time (not even for a
microsecond).
2024-07-05 17:24:56 +02:00
Javi Martín
fb0c087f95 Add and apply Rails/WhereRange rubocop rule
This rule was added in rubocop-rails 2.25.0. Applying it allows us to
simplify the code a little bit. For example, now there's no need to
specify the `proposals` table in proposal scopes, which was actually
causing a bug in the `Legislation::Proposal` model, which was using the
`proposals` table instead of the `legislation_proposals` table (but,
since we don't use this scope, it didn't affect the application).
2024-07-05 17:11:29 +02:00
Javi Martín
5033691666 Avoid duplicate records in poll answers
Until now, we've stored the text of the answer somebody replied to. The
idea was to handle the scenarios where the user voters for an option but
then that option is deleted and restored, or the texts of the options
are accidentally edited and so the option "Yes" is now "Now" and vice
versa.

However, since commit 3a6e99cb8, options can no longer be edited once
the poll starts, so there's no risk of the option changing once somebody
has voted.

This means we can now store the ID of the option that has been voted.
That'll also help us deal with a bug introduced int 673ec075e, since
answers in different locales are not counted as the same answer. Note we
aren't dealing with this bug right now.

We're still keeping (and storing) the answer as well. There are two
reasons for that.

First, we might add an "open answer" type of questions in the future and
use this column for it.

Second, we've still got logic depending on the answer, and we need to be
careful when changing it because there are existing installations where
the answer is present but the option_id is not.

Note that we're using `dependent: nullify`. The reasoning is that, since
we're storing both the option_id and the answer text, we can still use
the answer text when removing the option. In practice, this won't matter
much, though, since we've got a validation rule that makes it impossible
to destroy options once the poll has started.

Also note we're still allowing duplicate records when the option is nil.
We need to do that until we've removed every duplicate record in the
database.
2024-06-26 20:20:24 +02:00
Javi Martín
9a8bfac5bd Prevent creation of duplicate poll voters
Note that, when taking votes from an erased user, since poll answers
don't belong to poll voters, we were not migrating them in the
`take_votes_from` method (and we aren't migrating them now either).
2024-06-26 15:41:44 +02:00
Javi Martín
5f12db899f Remove no longer needed call to Poll::Answer#touch
This call was added in commit 81f65f1ac, and the test for its need was
added in commit cb1542874. However, both the test and the helper method
relying on the `touch` call were removed in commit f90d0d9c4.
2024-06-26 15:41:44 +02:00
Javi Martín
fb9156f9b8 Use with_lock instead of lock!
That way the record is only locked while necessary.
2024-06-26 15:41:44 +02:00
Javi Martín
a54d424aed Add missing validation rule to poll answers
We were checking we didn't have more votes than allowed in the case of
questions with multiple answers, but we weren't checking it in the case
of questions with a single answer. This made it possible to create more
than one answer to the same question. This could happen because the
method `find_or_initialize_user_answer` might initialize two answers in
different threads, due to a race condition.
2024-06-26 15:41:44 +02:00
Javi Martín
6aafc107ae Remove Questionable concern
Since we were only using it in one place, it made the code harder to
follow.

We'll extract it again if we ever find a way to reuse it.
2024-06-26 15:41:44 +02:00
Javi Martín
0c650c423d Fix exception creating an answer without an author
We were getting `undefined method `lock!' for nil:NilClass` when the
question allowed multiple answers.
2024-06-26 15:41:44 +02:00
Javi Martín
ca8329d5f2 Remove unused association between user and pair answers
The code for Poll::PairAnswer was removed in commit af7c37634.
2024-06-13 19:38:29 +02:00
Javi Martín
38b38d1fcc Rename Poll::Question::Answer to Poll::Question::Option
Having a class named `Poll::Question::Answer` and another class named
`Poll::Answer` was so confusing that no developer working on the project
has ever been capable of remembering which is which for more than a few
seconds.

Furthermore, we're planning to add open answers to polls, and we might
add a reference from the `poll_answers` table to the
`poll_question_answers` table to property differentiate between open
answers and closed answers. Having yet another thing named answer would
be more than what our brains can handle (we know it because we did this
once in a prototype).

So we're renaming `Poll::Question::Answer` to `Poll::Question::Option`.
Hopefully that'll make it easier to remember. The name is also (more or
less) consistent with the `Legislation::QuestionOption` class, which is
similar.

We aren't changing the table or columns names for now in order to avoid
possible issues when upgrading (old code running with the new database
tables/columns after running the migrations but before deployment has
finished, for instance). We might do it in the future.

I've tried not to change the internationalization keys either so
existing translations would still be valid. However, since we have to
change the keys in `activerecord.yml` so methods like
`human_attribute_name` keep working, I'm also changing them in places
where similar keys were used (like `poll_question_answer` or
`poll/question/answer`).

Note that it isn't clear whether we should use `option` or
`question_option` in some cases. In order to keep things simple, we're
using `option` where we were using `answer` and `question_option` where
we were using `question_answer`.

Also note we're adding tests for the admin menu component, since at
first I forgot to change the `answers` reference there and all tests
passed.
2024-06-13 19:13:01 +02:00
Javi Martín
bfee1b0ecb Simplify query to get possible answers
We added the code indicating the table in commit 673ec075e because back
then we had a `title` column in both the `poll_question_answers` table
and the `poll_question_answer_translations` table.

Since that's no longer the case since commit 7a7877656, we can simplify
the code.
2024-06-12 15:16:14 +02:00
Javi Martín
5a0fa28189 Remove no longer needed calls to require_dependency
Not sure about when we stopped needing them, but all usages of
require_dependency definitely become obsolete after we started using
Zeitwerk in commit 5f24ee912.

We're also going to rename `Poll::Question::Answer` to
`Poll::Question::Option`, so the conflict of having two `Answer`
classes, that made us add this code in the first place, will not even
exist.
2024-06-12 15:16:14 +02:00
Javi Martín
236b58ab01 Remove duplication in code to validate video URL
We were using the same code, and the same regular expressions, in two
places. To do so, we were including a helper inside a model, which is
something we don't usually do.
2024-06-06 17:35:27 +02:00
Javi Martín
0c59c2dfb4 Extract model to handle locales settings
This way we can simplify the view, particularly the form. However, we're
still adding some complexity to the form so inputs are inside labels and
so the collection is easier to style with CSS.
2024-06-05 16:10:56 +02:00
Javi Martín
6de4737b70 Allow different default locales per tenant
Note that, for everything to work consistently, we need to make sure
that the default locale is one of the available locales.

Also note that we aren't overwriting the `#save ` method set by
globalize. I didn't feel too comfortable changing a monkey-patch which
ideally shouldn't be there in the first place, I haven't found a case
where `Globalize.locale` is `nil` (since it defaults to `I18n.locale`,
which should never be `nil`), so using `I18n.default_locale` probably
doesn't affect us.
2024-06-05 16:10:56 +02:00
Javi Martín
722e50a669 Simplify code to find a content block
We're only calling the `block_for` method from one place: the
`content_block` helper, and we're never passing the locale parameter to
that helper, which means we're always calling `block_for` using
`I18n.locale`.

So I'm not sure why we were doing the `locale ||=` assignment, since
`I18n.locale` doesn't return nil.

In any case, since this was added in commit c1de2dced, Ruby has added
support for arguments forwarding, so we can use it here to simplify the
code a little bit.
2024-06-05 16:10:56 +02:00
Javi Martín
647121d13e Allow different locales per tenant
Note that, currently, we take these settings from the database but we
don't provide a way to edit them through the admin interface, so the
locales must be manually introduced through a Rails console.

While we did consider using a comma-separated list, we're using spaces
in order to be consistent with the way we store the allowed content
types settings.

The `enabled_locales` nomenclature, which contrasts with
`available_locales`, is probably subconsciously based on similar
patterns like the one Nginx uses to enable sites.

Note that we aren't using `Setting.enabled_locales` in the globalize
initializer when setting the fallbacks. This means the following test
(which we could add to the shared globalizable examples) would fail:

```
it "Falls back to an enabled locale if the fallback is not enabled" do
  Setting["locales.default"] = "en"
  Setting["locales.enabled"] = "fr en"
  allow(I18n.fallbacks).to receive(:[]).and_return([:fr, :es])
  Globalize.set_fallbacks_to_all_available_locales

  I18n.with_locale(:fr) do
    expect(record.send(attribute)).to eq "In English"
  end
end
```

The reason is that the code making this test pass could be:

```
def Globalize.set_fallbacks_to_all_available_locales
  Globalize.fallbacks = I18n.available_locales.index_with do |locale|
    ((I18n.fallbacks[locale] & Setting.enabled_locales) + Setting.enabled_locales).uniq
  end
end
```

However, this would make it impossible to run `rake db:migrate` on new
applications because the initializer would try to load the `Setting`
model but the `settings` table wouldn't exist at that point.

Besides, this is a really rare case that IMHO we don't need to support.
For this scenario, an installation would have to enable a locale, create
records with contents in that locale, then disable that locale and have
that locale as a fallback for a language where content for that record
wasn't created. If that happened, it would be solved by creating content
for that record in every enabled language.
2024-06-05 16:10:56 +02:00
Javi Martín
790d515afc Remove unused code in globalizable concern
This code was added in commit 041abe904, but it was never used.
2024-06-05 16:10:56 +02:00
Javi Martín
b188c5cae0 Simplify method to get existing locales
We can get the same results using `pluck`.
2024-06-05 16:10:56 +02:00
Javi Martín
b451a251fc Rename methods that returns an array of locales
Having `_languages` in the method name made it harder to know what that
method was returning.
2024-06-05 16:10:55 +02:00