Commit Graph

9884 Commits

Author SHA1 Message Date
Javi Martín
827f73d22d Merge pull request #5038 from consul/fix_investment_content_blocks
Fix crash voting on a heading with a content block
2022-11-28 18:09:49 +01:00
Javi Martín
061ae94f98 Use the tenant name as default organization name
This way we'll avoid having all tenant organizations named "CONSUL" by
default, and we'll also use different email senders per tenant, which
will reduce the change of the emails being marked as spam.
2022-11-28 16:42:41 +01:00
Javi Martín
03614325d1 Use current host as default email address domain
We were using `consul.dev` as default, which might be fine for the
development environment, but it's something that definitely needs to be
changed on production.

Now we're providing a better default setting: using the same domain
which is used to generate URLs in emails. This also makes it easier to
configure new tenants, since the setting will default to whatever host
the new tenant is using.

The `|| "consul.dev"` part might not be needed; I'm keeping it because
I'm not sure production environents would not experience any new issues
if we don't add it, but we might remove it in the future.
2022-11-28 16:39:09 +01:00
Javi Martín
236796406a Fix crash voting on a heading with a content block
When voting investment projects, the sidebar was rendered without the
`@heading_content_blocks` being set. That resulted in a 500 error when
the heading had content blocks.

By extracting the logic to a component, we make sure the heading content
blocks are properly set every time this code is rendered, no matter
which controller is rendering the view.
2022-11-28 13:28:22 +01:00
Javi Martín
ccbdfb8e78 Allow different hover button colors per tenant 2022-11-20 00:29:12 +01:00
Javi Martín
d0d8f9cc1e Allow different layout backgounds per tenant
We were already doing the same for the main header color; now we also
make it easier to use different top links, subnavigation and footer
colors per tenant.

Just like we do with SCSS variables, we use the brand-secondary color
for the top links when the `--top-links` variable isn't defined.
2022-11-20 00:29:12 +01:00
Javi Martín
af040fcc2b Allow different link colors per tenant
Just like we did with SCSS variables, we use the --anchor-color CSS
variable and, if it isn't defined, we use the --brand CSS variable
instead.
2022-11-20 00:29:12 +01:00
Javi Martín
36a1b2cdc2 Allow different secondary colors per tenant 2022-11-20 00:29:12 +01:00
Javi Martín
91614fa2a9 Allow different main header colors per tenant
Just like we did with SCSS variables, we use the `--main-header` CSS
variable and, if it isn't defined, we use the `--brand` CSS variable
instead.

Note that we're still using the `inverted-selection` mixin based on the
default `$main-header` color, so it's possible that we get the inverted
selection in the main header when using a dark color with `$main-header`
but a light color with `--main-header`, which doesn't make much sense.
Not sure whether there's anything we can do about it.
2022-11-20 00:29:12 +01:00
Javi Martín
fcc63cb436 Allow different brand colors per tenant
Until now, overwriting the styles for a certain tenant was a very
tedious task. For example, if we wanted to use a different brand color
for a tenant, we had to manually overwrite the styles for every element
using that color.

It isn't possible to use different SCSS variables per tenant unless we
generate a different stylesheet per tenant. However, doing so would make
the CSS compilation take way too long on installations with more than a
couple of tenants, and it wouldn't allow to get the colors dynamically
from the database, which we intend to support in the future.

So we're using CSS variables instead. These variables are supported by
97% of the browsers (as of October 2022), and for the other 3% of the
browsers we're using the default colors (SCSS variables) instead.

CSS variables have some limitations: for instance, it isn't possible to
use functions like `lighten`, `darken` or `scale-color` with CSS
variables, so the application might behave in a strange way when we use
these functions.

It also isn't possible to automatically get whether black or white text
makes a better contrast with a certain background color. To overcome
this limitation, we're providing variables ending with `-contrast`. For
instance, since the default `$brand` color is a dark one, when assigning
a light color to `--brand`, we probably want to assign
`--brand-contrast: #{$black}` as well, so the text is still readable.
2022-11-20 00:29:12 +01:00
Javi Martín
a2c032573f Rename brand-text mixin to brand-color
This is consistent with the `body-colors` mixin and with other mixins
we're about to add, like `anchor-color`.
2022-11-20 00:29:12 +01:00
Javi Martín
d4c8606f43 Simplify brand-background mixin
We don't need the color parameter anymore since we can now use a more
generic mixin for any background, like brand-secondary.
2022-11-20 00:29:12 +01:00
Javi Martín
e248a40ff3 Automatically invert selection based on contrast
So now inverting the selection for brand-secondary backgrounds will
depend on the value of brand-secondary.
2022-11-20 00:29:12 +01:00
Javi Martín
35e95121e2 Add variables to customize main layout colors
Until now, we didn't have specific variables for the headers and were
using the brand colors instead. Now we maintain the brand colors as
default values, but allow overwriting them.

For the navigation and footer, we didn't even have variables.
2022-11-20 00:29:12 +01:00
Javi Martín
277f8b1ddc Revert "Use the same color for solid and hollow buttons"
Back in commit 5dbd69486, I said:

> I'm choosing to use the same color for solid and hollow buttons
> because these elements are usually isolated and so from the UX
> perspective they are similar; links, on the other hand, are often in
> the middle of some text.

However, I made a mistake. The crucial factor is that solid buttons
might have a light background if we choose the brand color to be a light
one, and in this case they automatically get black text. However, hollow
buttons always have a light background and so we can't use a light color
for the text and border of these buttons.
2022-11-20 00:29:07 +01:00
Javi Martín
384057cb48 Allow different custom images per tenant
Note this only affects images which can also be customized using the
administration interface; other images like `avatar_admin.png` must be
the same for all tenants. I think this is good enough for now, since the
images that can't be different are the images that aren't customized
that often, and if there's a need to use different images in a certain
CONSUL installation, it can be achieved by changing the code which
renders that image so it uses `image_path_for`.
2022-11-11 01:41:17 +01:00
Javi Martín
edd47877c2 Extract methods to get tenant subfolder/file paths
We were using the same logic in many different places, so we're
simplifying the code. I'm not convinced about the method names, though,
so we might change them in the future.

Note using this method for the default tenant in the `TenantDiskService`
class resulted in a `//` in the path, which is probably harmless but
very ugly and it also generates a different key than the one we got
until now. I've added an extra test to make sure that isn't the case.
2022-11-11 01:41:14 +01:00
Javi Martín
2f312bf474 Use a different machine learning folder per tenant
We're using the "tenants" subfolder for consistency with the folder
structure we use in ActiveStorage and because some CONSUL installations
might have folders inside the `data` folder which might conflict with
the folders created by tenants.

Note that the Python scripts have a lot of duplication, meaning we need
to change all of them. I'm not refactoring them because I'm not familiar
enough with these scripts (or with Python, for that matter).

Also note that the scripts folder is still shared by all tenants,
meaning it isn't possible to have different scripts for different
tenants. I'm not sure how this situation should be handled; again, I'm
not familiar enough with this feature.
2022-11-11 01:40:04 +01:00
Eduardo Vilar
0ea61b9b61 Allow different omniauth settings per tenant
Co-Authored-By: Javi Martín <javim@elretirao.net>
2022-11-11 01:39:30 +01:00
Javi Martín
a3be1e174b Allow different HTTP basic auth settings per tenant 2022-11-11 01:39:30 +01:00
Javi Martín
18f1d5c1a3 Allow different remote translation keys per tenant
Note we don't need to update the tests; the tests themselves help us
confirm that `Rails.application.secrets` and `Tenant.current_secrets`
return the same object on single-tenant applications.
2022-11-11 01:39:29 +01:00
Javi Martín
06d0c26126 Allow having different SMTP settings per tenant
Right now this is configured using the `secrets.yml` file, which is the
file we've used in the past to configure SMTP settings.

Note that, in the `current_secrets` method, the `if default?` condition
is there so in single-tenant applications it returns the exact same
object as `Rails.application.secrets`, and it makes it immediately clear
for developers reading the code. We're also caching the tenant secrets
(using `||=`) so they behave the same way as Rails secrets; for this to
work properly 100% of the time (for example, in tests) we need to expire
these cached secrets whenever the Rails secrets change.

A similar `unless Tenant.default?` condition is present in the
ApplicationMailer because there's a chance some CONSUL installations
might not be using secrets to define the SMTP settings(they might be
using environment variables, for example) and so in this case we don't
want to force settings based on the secrets.yml file because it would
break the application.

The structure of the SMTP settings in the secrets file should be:

```
production:
  tenants:
    name_of_the_tenant_subdomain:
      smtp_settings:
        address:
        (...)
```
2022-11-11 01:39:29 +01:00
Javi Martín
5c61b72d21 Identify the current tenant in the <html> tag
This way it will be possible to write CSS and JavaScript code that will
only apply to specific tenants.

Note that CSS customization is still limited because it isn't possible
to use different SCSS variables per tenant.
2022-11-11 01:39:29 +01:00
Javi Martín
2c0ede3aaa Use the dir attribute in dashboard and mailer layouts
We forgot to do so in commit d827768c0. In order to avoid the same
mistake in the future, we're extracting a method to get these
attributes. We're also adding tests, since we didn't have any tests to
check that the `dir` attribute was properly set.
2022-11-11 01:39:29 +01:00
Javi Martín
a71f4d87f8 Add an interface to manage tenants
Note we aren't allowing to delete a tenant because it would delete all
its data, so this action is a very dangerous one. We might need to add a
warning when creating a tenant, indicating the tenant cannot be
destroyed. We can also add an action to delete a tenant which forces the
admin to write the name of the tenant before deleting it and with a big
warning about the danger of this operation.

For now, we're letting administrators of the "main" (default) tenant to
create other tenants. However, we're only allowing to manage tenants
when the multitenancy configuration option is enabled. This way the
interface won't get in the way on single-tenant applications.

We've thought about creating a new role to manage tenants or a new URL
out of the admin area. We aren't doing so for simplicity purposes and
because we want to keep CONSUL working the same way it has for
single-tenant installations, but we might change it in the future.
There's also the fact that by default we create one user with a known
password, and if by default we create a new role and a new user to
handle tenants, the chances of people forgetting to change the password
of one of these users increases dramatically, particularly if they
aren't using multitenancy.
2022-11-09 18:19:20 +01:00
Javi Martín
d904fe8b4f Move subdomain logic to tenant model
We had some of the logic in the ApplicationMailer. Since we're going to
use this logic in more places, we're moving it to the Tenant model,
which is the model handling everything related to hosts.
2022-11-09 18:19:20 +01:00
Javi Martín
468761253b Add per-tenant sitemap to robots.txt file
While we ping some search engines (currently, only Google) when
generating the sitemap files, we weren't telling search engines
accessing through the `robots.txt` file where to find the sitemap. Now
we're doing so, using the right sitemap file for the right tenant.
2022-11-09 18:19:20 +01:00
Javi Martín
5100884110 Generate one sitemap per tenant
Note that the `sitemap:refresh` task only pings search engines at the
end, so it only does so for the `Sitemap.default_host` defined last. So
we're using the `sitemap:refresh:no_ping` task instead and pinging
search engines after creating each sitemap.

Note we're pinging search engines in staging and preproduction
environments. I'm leaving it that way because that's what we've done
until now, but I wonder whether we should only do so on production.

Since we're creating a new method to get the current url_options, we're
also using it in the dev_seeds.
2022-11-09 18:19:20 +01:00
Javi Martín
9759288f3b Run DB rake tasks on all tenants
Some tasks don't have to run on every tenant. The task to calculate the
TSV is only done for records which were present before we added the TSV
column, and that isn't going to happen in any tenants because we added
the TSV column before adding the tenants table. Similarly, the migration
needed for existing polls isn't necessary because there weren't any
tenants before we allowed to set the starting/ending time to polls.

We aren't adding any tests for these tasks because tests for rake tasks
are slow and tests creating tenants are also slow, making the
combination of the two even slower, particularly if we add tests for
every single task we're changing. We're adding tests for the
`.run_on_each` method instead.
2022-11-09 18:19:20 +01:00
Javi Martín
275ddb08d8 Consider the current tenant in email URLs 2022-11-09 18:19:20 +01:00
Javi Martín
5983006657 Use a custom method to detect the current tenant
The subdomain elevator we were using, which is included in apartment,
didn't work on hosts already including a subdomain (like
demo.consul.dev, for instance). In those cases, we would manually add
the subdomain to the list of excluded subdomains. Since these subdomains
will be different for different CONSUL installations, it meant each
installation had to customize the code. Furthermore, existing
installations using subdomains would stop working.

So we're using a custom method to find the current tenant, based on the
host defined in `default_url_options`.

In order to avoid any side-effects on single-tenant applications, we're
adding a new configuration option to enable multitenancy

We're enabling two ways to handle this configuration option:

a) Change the application_custom.rb file, which is under version control
b) Change the secrets.yml file, which is not under version control

This way people prefering to handle configuration options through
version control can do so, while people who prefer handling
configuration options through te secrets.yml file can do so as well.

We're also disabling the super-annoying warnings mentioning there are no
tenants which we got every time we run migrations on single-tenant
applications. These messages will only be enabled when the multitenancy
feature is enabled too. For this reason, we're also disabling the
multitenancy feature in the development environment by default.
2022-11-09 18:19:20 +01:00
Javi Martín
d77cf77761 Validate format of subdomains / schema names
Note we're using the `:HOST` regular expression since subdomains can
contain the same characters as domains do. This isn't 100% precise,
though, since subdomains have a maximum length of 63 characters, but is
good enough for our purposes.
2022-11-09 18:19:01 +01:00
Javi Martín
c483c6036a Install extensions in a shared schema
This way all tenants will be able to access them instead of just the
default one.

The apartment gem recommends using a rake task instead of a migration,
but that's a solution which is primarily meant for new installations.
Migrations are easier to execute on existing installations.

However, since this migration doesn't affect the `schema.rb` file, we
still need to make sure the shared schema is created in tasks which do
not execute migrations, like `db:schema:load` or `db:test:prepare`, just
like the apartment gem recommends. That's why we're enhancing these
tasks so they execute this migration.

Note that there might be cases where the database user isn't a superuser
(as it's usually the case on production environments), meaning commands
to create, alter or drop extensions will fail. There's also the case
where users don't have permissions to create schemas, which is needed in
order to create the shared extensions schema and the schemas used by the
tenants. For these reasons, we're minimizing the number of commands, and
so we only alter or create extensions when it is really necessary.

When users don't have permission, we aren't running the commands but
showing a warning with the steps needed to run the migration manually.
This is only necessary on installations which are going to use
multitenancy; single-tenant applications upgrading don't need to run
this migration, and that's why we aren't raising exceptions when we
can't run it.

For new installations, we'll change the CONSUL installer so extensions
are automatically created in the shared schema.

Also note the plpgsql extension is not handled here. This is a special
extension which must be installed on the pg_catalog schema, which is
always in the search path and so is shared by all tenants.

Finally, we also need to change the `database.yml` file in order to
search for shared extensions while running migrations or model tests,
since none of our enabled extensions are executed during migrations;
we're also adding a rake task for existing installations. Quoting the
apartment documentation:

> your database.yml file must mimic what you've set for your default and
> persistent schemas in Apartment. When you run migrations with Rails,
> it won't know about the extensions schema because Apartment isn't
> injected into the default connection, it's done on a per-request
> basis.
2022-11-09 17:53:31 +01:00
Eduardo Vilar
382abb3666 Add multitenancy with apartment
Co-Authored-By: Javi Martín <javim@elretirao.net>
2022-11-09 17:52:05 +01:00
Javi Martín
9ac8ddb6bf Unify dark and brand-secondary SCSS variables
We were using each one half the time, while they both had the same value
by default. It was impossible to know when me meant "use a dark color
here" or "use the secondary brand color" here.

So now we're only using one variable, so it's easy it'll be easy to add
CSS custom properties to overwrite this variable. We're choosing
`brand-secondary` because its name makes it less problematic to use a
light color.
2022-10-28 14:07:33 +02:00
Javi Martín
b96d745e0e Use $body-font-color instead of $text
This is consistent with the usage of `$body-background`. This way
Foundation elements using `$body-font-color`, like the `<body>` tag,
will be changed when changing this variable, which wouldn't happen when
using `$text`.
2022-10-28 14:07:33 +02:00
Javi Martín
ea3ae4d262 Use $anchor-color instead of $link
The variables `$anchor-color` and `$anchor-color-hover` are the ones
Foundation uses internally; by using them, we make sure every link will
use the colors we define.

Now we can simplify the default styles for the `<a>` tags, since by
default they already use these variables.
2022-10-28 14:07:33 +02:00
Javi Martín
7c740f1daa Remove duplicate HTML in dashboard mail preview
We noticed this while editing the styles for this class in the previous
commits.
2022-10-28 13:58:04 +02:00
Javi Martín
6cb4f4acde Extract mixin to get a background with text contrast
This way we simplify the code a bit.

Note we're only using this function when variables for background colors
are already defined, since that means customizing the variable using the
background color will automatically change the color of the text.
Customization isn't easier when using raw colors.
2022-10-28 13:58:04 +02:00
Javi Martín
1b1b5b5755 Use color-pick-contrast to get text colors
We were defining (for instance) white text against the `$brand`
background. That meant that, if somebody customized the `$brand` color
so it used a light color, they had to customize the text color as well
in order to guarantee proper contrast between text and background
colors.

So we're using `color-pick-contrast` instead, which means we don't have
to manually calculate whether white or black will be the color which
makes the text more readable.
2022-10-28 13:58:04 +02:00
Javi Martín
025d7bf9f8 Remove duplication in sidebar color definition
We're going to change that code so it uses color-pick-contrast, so we're
refactoring it first.
2022-10-28 13:57:18 +02:00
Javi Martín
5845dd46d9 Extract variable for SDG text color
We were using `#fff` in some places and `$white` in others. We're
choosing `#fff` because it has a better contrast against the lighter SDG
colors and because it's the one officially used by the United Nations.
2022-10-27 15:35:27 +02:00
Javi Martín
3aa73110c5 Use foundation variables to set caption color
This way we can simplify the code a bit since Foundation automatically
chooses the text color of the caption so there's enough contrast with
the background color defined in `$orbit-caption-background`.
2022-10-27 15:35:27 +02:00
Javi Martín
0be0f459ab Extract mixin to use links with the text color 2022-10-27 15:14:48 +02:00
Javi Martín
33eea0b21a Extract mixin to use body background and text color
This way we reduce the usages of the `$body-background` and `$text`
variables, making it easier to replace them with CSS variables in the
future.
2022-10-27 15:14:48 +02:00
Javi Martín
3c8d97fc57 Simplify hover color in link to delete comments
Since we were using the default color, we can do so without overwriting
it twice.
2022-10-27 15:09:13 +02:00
Javi Martín
335f0d8bde Inherit text color instead of defining it again
This way it's easier to customize colors, particularly when links use
the same colors as the text surrounding them.

Note that the `markdown-editor-header` isn't displayed since commit
76b7f66fb, which was probably an unintended side-effect. So we're
modifying the colors here as well instead of removing the element; we
might display it again in the future.

Also note the change in `.supported` only affects the proposals section;
budget investments use a different color for the `.supported` sucess
message. Not sure whether this was originally intended or done by
accident.
2022-10-27 15:07:53 +02:00
Javi Martín
09471c1432 Remove redundant background definitions
These elements didn't need a background because their parent elements
already defined the same background.
2022-10-27 01:10:46 +02:00
Javi Martín
590f557aaa Remove redundant link colors definitions
These elements already used these colors because their parent elements
already used these colors for links.
2022-10-27 01:10:36 +02:00
Javi Martín
e050e5cc70 Remove redundant text color definitions
These elements were already using these colors because they inherited
them from their parent elements or Foundation already defined them.

Note that the only contents of `.comment-info` are: `.user-name` and
`.is-author`, `.is-association`, `.level-1`, ... and the link to the
comment. All of these elements were overwriting the `$text-medium`
color, so there's no need to set it. That means we can use the default
text color for `.user-name` without specifically setting it.
2022-10-27 01:10:31 +02:00