Set cookie duration to 365 days based on the AEPD's cookie usage guidelines.
Note from the document: "Cookies with a duration of up to 24 months are
considered acceptable as long as they are periodically updated."
Reference: https://www.aepd.es/guias/guia-cookies.pdf
Create cookie consent "all" when accept all cookies
Set cookie duration to 365 days based on the AEPD's cookie usage guidelines.
Note from the document: "Cookies with a duration of up to 24 months are
considered acceptable as long as they are periodically updated."
Reference: https://www.aepd.es/guias/guia-cookies.pdf
Set cookie duration to 365 days based on the AEPD's cookie usage guidelines.
Note from the document: "Cookies with a duration of up to 24 months are
considered acceptable as long as they are periodically updated."
Reference: https://www.aepd.es/guias/guia-cookies.pdf
Note we're adding a `name` property to the geozones investments sidebar
map even if we don't render the geozones in the map, in order to
simplify the JavaScript function `geozoneLayers`.
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>
We were using one label for both date selectors, but it wasn't
associated with any of them.
So we're now rendering one label per control and, just like we only show
one of these date selectors at a time, we're only showing one label at a
time.
In commit 96ae69fe9, we stopped using cookies to track Ahoy visits and
started using a combination of the IP and the browser agent instead.
However, since we're still using the legacy logic from Ahoy 1.x to track
visits (which we had to add in commit b5220effd), this way of tracking
visits doesn't work and counts every page visited by a user as an
independent visit.
Maybe we could migrate existing data, which uses the `visitor_id` column
so it uses the new `visit_token` and `visitor_token` columns, but
there's no mention in the Ahoy documentation regarding how to do so.
While deciding what to do about this, we found something interesting.
For two years, we've been seeing random failures in the
`system/admin/tenants_spec.rb` tests, with messages like:
```
1) Tenants Create Tenant with subdomain
Failure/Error:
raise TenantNotFound, <<~EXCEPTION_MESSAGE
Could not set search path to schemas, they may be invalid:
"#{tenant}" #{full_search_path}.
Original error: #{exception.class}: #{exception}
EXCEPTION_MESSAGE
Apartment::TenantNotFound:
Could not set search path to schemas, they may be invalid:
"earth" "public", "shared_extensions".
Original error:
ActiveRecord::StatementInvalid: Could not find schema earth
```
And we've found one of the causes: the AJAX requests done by Ahoy to
track visits. Sometimes a test that creates or updates a tenant finishes
but the Ahoy AJAX request to, say, `earth.lvh.me/ahoy/visits`, is
handled by the next test, when the `earth` schema no longer exists, thus
raising an `Apartment::TenantNotFound` exception.
So by disabling these AJAX requests and tracking the visits in the
server instead, we're killing two birds in one stone: we're fixing the
bug regarding the visits count and we're reducing the flakiness in our
test suite. It looks like we're also removing the "phantom ahoy cookie"
we were getting since the mentioned commit b5220effd: an ahoy cookie was
quickly set and unset in the browser.
Note that, even though we aren't migrating any data, we're still adding
the new fields, because some tests started to fail because, when
tracking visits in the server without cookies, Ahoy expects the Visit
model to have a `visit_token` field.
We don't need to replace the whole row, since the changes only affect
the button. Therefore, we don't need to depend on an `inserted` event to
decide which columns to render in that row.
Note that, while it doesn't really affect the way the application
behaves (as long as the JavaScript code doesn't rely on the order it's
loaded) we're requiring `app/assets/javascripts/custom.js` after
requiring any files in the `app/assets/javascripts/custom/` folder. This
is done for consistency, since we load the content of
`app/assets/javascripts/application.js` after requiring everything else.
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.
Although most Consul Democracy installations will only have a few
available languages using `config.i18n.available_locales`, there's a
chance some installation will keep every language as available and will
enable the desired ones using the admin interface. In these cases,
enabling (or disabling) every language would be tedious, particularly
when casually experimenting in a staging environment or while using the
official Consul Democracy demo.
So we're adding buttons to simplify the process. Since some
installations might have only a couple of available languages, and in
this case these buttons would be pretty much useless, we're only showing
them when there are many languages available.
People using screen readers usually expect links to take them somewhere
else in the page on to a different page, while they expect buttons to
change something on the page.
Since we're in the latter scenario, using a button is more accessible.
It's also more natural; with a button, we don't need to provide `#` as
the URL or stop the default event when the button is clicked. And,
unlike links, buttons can be activated with either the space or the
enter key. Finally, clicking a link pointing to `#` with the middle
mouse button opens a useless new tab, while buttons do nothing in this
case.
Now that we only have one "All" link on the page, we no longer need to
specify which "All" link we're clicking or which "All" link we are
checking, so we're simplifying the code doing so.
Now that we've moved the logic to generate the events data to a model,
and we've got access to the model in the component rendering the chart,
we can render the data inside the chart instead of doing an extra AJAX
request to get the same data.
Originally, this was problaby done this way so the page wouldn't take
several seconds to load while preparing the data for the chart when
there are thousands of dates being displayed. With an AJAX call, the
page would load as fast as usual, and then the chart would render after
a few seconds. However, we can have an even better performance
improvement in this scenario if we use a Set instead of an Array. The
method `Array#include?`, which we were calling for every date in the
data, is much slower that `Set#merge`. So now both the page and the
chart load as fast as expected.
We could also use something like:
```
def add
(...)
shared_keys.push(*collection.keys)
end
def build
(...)
shared_keys.uniq.each do |k|
(...)
end
def shared_keys
@shared_keys ||= []
end
```
Or other approaches to avoid using `Array#include?`. The performance
would be similar to the one we get when using `Set`. We're using a `Set`
because it makes more obvious that `shared_keys` is supposed to contain
unique elements.
We've had some tests failing in the past due to these AJAX requests
being triggered automatically during the tests and no expectations
checking the requests have finished, so now we're reducing the amount of
flaky tests.
We were using Foundation's accordion menu to open/close nested lists of
links. Unfortunately, Foundation's accordion makes it impossible to
access links in nested links using the keyboard [1] (note the issue is
closed, but in the latest version of Foundation, 6.8.1, it's still
present, and Foundation's development is mostly discontinued).
Furtheremore, it adds the `menuitem` role to links, but ARIA menus are
not ment for navigation but for application behavior and, since it
doesn't add the `menubar` or `menu` roles to the parent elements, it
results in accessibility issues for people using screen readers (also
reported by the Axe accessibility testing engine).
So we need to implement our own solution. We're using the most commonly
used pattern: a buttton with the `aria-expanded` attribute. And, for
people using browsers where JavaScript hasn't loaded, we're keeping the
submenus open at all times (just like we were doing until now), and
we're disabling the buttons (since they do nothing without JavaScript).
This might not be an ideal solution, but it's probably good enough, and
way better than what we had until now.
We've also considered using the <details> and <summary> elements instead
of using buttons to open/close items on the list. However, these
elements still present some accessibility issues [2], and the transition
between open and closed can't be animated unless we overwrite the
`click` event with JavaScript. The pattern of using these elements to
open/close a nested list of links isn't common either, and some people
using screen readers might get confused when entering/leaving the nested
list.
We tried other approaches to get the animation effect, all of them based
on adding `[aria-expanded="false"]:not([disabled]) + * { display: none;
}` to the CSS file.
Unfortunately, animation using CSS isn't feasible right now because
browsers can't animate a change form `height: 0` to `height: auto`.
There are some hacks like animating the `max-height` or the `flex-grow`
property, but the resulting animation is inconsistent. A perfect
animation can be done using the `grid-template-rows` property [3], but
it requires adding a grid container and only works in Firefox and recent
versions of Chrome and similar browsers.
Getting to a solution with JavaScript was also tricky. With the
following approach, `slideToggle()` opened the menu the first time, even
if it was already open (not sure why):
```
toggle_buttons.on("click", function() {
$(this).attr("aria-expanded", !JSON.parse($(this).attr("aria-expanded")));
$(this).next().slideToggle();
});
```
This made the arrow turn after the menu had slided instead of doing it
at the same time:
```
toggle_buttons.on("click", function() {
var button = $(this);
button.next().slideToggle(function() {
button.attr("aria-expanded",
!JSON.parse(button.attr("aria-expanded")));
});
}
```
With this, everything disappeared quickly:
```
toggle_buttons.on("click", function() {
var expanded = JSON.parse($(this).attr("aria-expanded"));
if (expanded) {
$(this).next().slideUp();
} else {
$(this).next().slideDown();
}
$(this).attr("aria-expanded", !expanded);
}
```
So, in the end, we're hiding the nested link lists with JavaScript
instead of CSS.
[1] Issue 12046 in https://github.com/foundation/foundation-sites
[2] https://www.scottohara.me/blog/2022/09/12/details-summary.html
[3] https://css-tricks.com/css-grid-can-do-auto-height-transitions
Using the standard `confirm` parameter, we can remove all the custom
code we added to do the same thing.
Since the code is similar, we're doing the same when asking for
confirmation to send notifications.
The initialjs-rails gem hasn't been maintained for years, and it
currently requires `railties < 7.0`, meaning we can't upgrade to Rails 7
while we depend on it.
Since the code in the gem is simple, and we were already rewriting its
most complex part (generating a background color), we can implement the
same code, only we're using Ruby instead of JavaScript. This way, the
avatars will be shown on browsers without JavaScript as well. Since
we're adding a component test that checks SVG images are displayed even
without JavaScript, we no longer need the test that checked images were
displayed after AJAX requests.
Now the tests show the user experience better; people don't care about
the internal name used to select the initial (which is what we were
checking); they care about the initial actually displayed.
Note initialjs generated an <img> tag using a `src="data:image/svg+xml;`
attribute. We're generating an <svg> tag instead, because it's easier.
For this reason, we need to change the code slightly, giving the <svg>
tag the `img` role and using `aria-label` so its contents won't be read
aloud by screen readers. We could give it a `presentation` role instead
and forget about `aria-label`, but then screen readers would read the
text anyway (or, at least, some of them would).
Although the gem is called jquery-fileupload-rails, the node package is
called blueimp-file-upload.
Note we're using the same version as provided by the gem.
The jquery-fileupload gem provided a `basic.js` file (which we were
requiring), which had the following content:
```
//= require jquery-fileupload/vendor/jquery.ui.widget
//= require jquery-fileupload/jquery.iframe-transport
//= require jquery-fileupload/jquery.fileupload
``
This file isn't available in the Node.js package, so we're adapting its
contents in our application.js file. Since we're already requiring
jQuery UI widget, we're omitting that line.
Since Foundation hasn't released a new gem for years, we haven't been
able to upgrade Foundation to its most recent version.
Thanks to this change, we'll be able to do so.
Note we're using motion-ui version 2.0.3 because version 2.0.5 (the
latest at the moment) requires Dart Sass.
Using the `document` or `documents` classes meant styles defined for the
public list of documents conflict with these ones.
So now we're using HTML classes that match the name of the Ruby
component classes, as we usually do.
Other packages depend on jQuery, so that's why these are the first one
we move from the Gemfile to the package.json file.
This way we can also test whether dependabot correctly opens pull
requests to update Node packages.
I've tried several configuration options for the asset pipeline in order
to be able to include images referenced in jQuery UI CSS files. So far,
adding the `node_modules/jquery-ui/themes/base` folder to the assets
paths is the only way I've found to make it work. Hopefully we can find
a better solution in the future so we don't have to study the internals
of every Node package in order to integrate it with the assets pipeline.
Internet Explorer 8 was released in 2009 and people using it already
know that most web pages look broken on it, so we don't need to warn
them.
Removing it makes our application layout file much easier to read and
modify.
It was obvious which project the comment was about. Also, no point
having a TODO referencing what to do after upgrading to Rails 5.1, since
we did it years ago.
Zooming with the mousewheel is useful when you want to use it, but
annoying when you don't want to.
Here we're taking an intermediary approach: by default, the mousewheel
isn't active, but it will be active after focusing on the map, so it can
be used both to scroll and to zoom.
This behavior presents usability issues, though, since we aren't making
users aware of the way the mousewheel works, and even if they were
aware, it could be confusing anyway. However, I currently think it's
better than always enabling or always disabling the mousewheel (might
change my mind, though).
Note that the "focus" event is only used on the map, so if we click on a
marker or navigate to a marker with the keyboard without focusing on the
map first, the mousewheel isn't enabled. The same would happen if we
used the "click" event.
We might use the Leaflet.GestureHandling plugin in the future to deal
with this issue and the scroll on touch screens.
By using the bindPopup function instead of the click event
popups work when using the keyboard.
Note that now we are loading all the map markers in the first
request in a single query to the database (needed when there
is a lot or markers to show). Because of that we removed the
AJAX endpoint.
Note that in the budgets wizard test we now create district with no
associated geozone, so the text "all city" will appear in the districts
table too, meaning we can't use `within "section", text: "All city" do`
anymore since it would result in an ambiguous match.
Co-Authored-By: Julian Herrero <microweb10@gmail.com>
Co-Authored-By: Javi Martín <javim@elretirao.net>
Using a button for interactive elements is better, as explained in
commit 5311daadf.
Since buttons with "type=button" do nothing by default, we no longer
need to call `preventDefault()` when clicking it.
The `marker` variable is like a global variable inside the
`initializeMap` function, so assigning it inside the `createMarker`
function was changing its value in other places.
So we're using different variable names like `newMarker` in order to
make the code easier to follow. Now we "only" change the `marker`
variable in functions that modify the marker.