decidim-toggle 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.erb-lint.yml +2134 -0
  3. data/.github/workflows/website.yml +57 -0
  4. data/.gitignore +13 -0
  5. data/.gitlab-ci.yml +165 -0
  6. data/.node-version +1 -0
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +2 -0
  9. data/.ruby-version +1 -0
  10. data/.simplecov +18 -0
  11. data/CONTRIBUTING.md +17 -0
  12. data/Gemfile +33 -0
  13. data/Gemfile.lock +843 -0
  14. data/LICENSE.md +661 -0
  15. data/README.md +90 -0
  16. data/Rakefile +38 -0
  17. data/app/commands/decidim/toggle/update_authorizations_command.rb +31 -0
  18. data/app/commands/decidim/toggle/update_emails_command.rb +30 -0
  19. data/app/commands/decidim/toggle/update_file_upload_settings_command.rb +31 -0
  20. data/app/commands/decidim/toggle/update_locale_command.rb +47 -0
  21. data/app/commands/decidim/toggle/update_module_config_command.rb +31 -0
  22. data/app/commands/decidim/toggle/update_name_command.rb +31 -0
  23. data/app/commands/decidim/toggle/update_omniauth_command.rb +30 -0
  24. data/app/commands/decidim/toggle/update_security_command.rb +31 -0
  25. data/app/controllers/decidim_toggle/system/settings_tab_controller.rb +64 -0
  26. data/app/forms/decidim/toggle/update_authorizations_form.rb +54 -0
  27. data/app/forms/decidim/toggle/update_emails_form.rb +28 -0
  28. data/app/forms/decidim/toggle/update_file_upload_settings_form.rb +26 -0
  29. data/app/forms/decidim/toggle/update_locale_form.rb +116 -0
  30. data/app/forms/decidim/toggle/update_name_form.rb +63 -0
  31. data/app/forms/decidim/toggle/update_omniauth_form.rb +37 -0
  32. data/app/forms/decidim/toggle/update_security_form.rb +65 -0
  33. data/app/helpers/decidim/toggle/javascript_config_helper.rb +11 -0
  34. data/app/helpers/decidim/toggle/system_settings_tab_helper.rb +59 -0
  35. data/app/models/decidim/toggle/organization_module_config.rb +15 -0
  36. data/app/overrides/add_toggle_javascript_admin.rb +11 -0
  37. data/app/overrides/add_toggle_javascript_public.rb +11 -0
  38. data/app/packs/entrypoints/decidim_toggle.js +3 -0
  39. data/app/packs/src/decidim/toggle/organization_settings_tabs.js +114 -0
  40. data/app/packs/stylesheets/decidim/toggle/organization_settings.scss +160 -0
  41. data/app/views/decidim/system/organizations/edit.html.erb +20 -0
  42. data/app/views/decidim_toggle/system/organizations/_default_form_tab.html.erb +5 -0
  43. data/app/views/decidim_toggle/system/organizations/_encryption_not_configured_callout.html.erb +6 -0
  44. data/app/views/decidim_toggle/system/organizations/_settings_tab_active_tab_field.html.erb +1 -0
  45. data/app/views/decidim_toggle/system/organizations/_settings_tab_submit.html.erb +4 -0
  46. data/app/views/decidim_toggle/system/organizations/_settings_tabs.html.erb +31 -0
  47. data/app/views/decidim_toggle/system/organizations/tabs/_authorizations_tab.html.erb +9 -0
  48. data/app/views/decidim_toggle/system/organizations/tabs/_emails_tab.html.erb +5 -0
  49. data/app/views/decidim_toggle/system/organizations/tabs/_file_upload_tab.html.erb +5 -0
  50. data/app/views/decidim_toggle/system/organizations/tabs/_language_tab.html.erb +35 -0
  51. data/app/views/decidim_toggle/system/organizations/tabs/_omniauth_tab.html.erb +5 -0
  52. data/app/views/decidim_toggle/system/organizations/tabs/_security_tab.html.erb +20 -0
  53. data/app/views/layouts/decidim/toggle/_javascript_config.html.erb +3 -0
  54. data/bin/check +10 -0
  55. data/bin/i18n-tasks +27 -0
  56. data/bin/postversion +14 -0
  57. data/config/assets.rb +8 -0
  58. data/config/locales/decidim_toggle_en.yml +58 -0
  59. data/crowdin.yml +15 -0
  60. data/db/migrate/20260321120000_create_decidim_toggle_organization_module_configs.rb +20 -0
  61. data/decidim-toggle.gemspec +35 -0
  62. data/docker-compose.yml +41 -0
  63. data/lib/decidim/toggle/engine.rb +62 -0
  64. data/lib/decidim/toggle/expose_attributes_to_js.rb +26 -0
  65. data/lib/decidim/toggle/expose_attributes_to_js_validator.rb +32 -0
  66. data/lib/decidim/toggle/gem_registry.rb +15 -0
  67. data/lib/decidim/toggle/informative_callouts.rb +76 -0
  68. data/lib/decidim/toggle/javascript_config.rb +87 -0
  69. data/lib/decidim/toggle/module_config.rb +64 -0
  70. data/lib/decidim/toggle/module_config_form.rb +41 -0
  71. data/lib/decidim/toggle/module_config_i18n.rb +44 -0
  72. data/lib/decidim/toggle/module_configuration_presenter.rb +55 -0
  73. data/lib/decidim/toggle/organization_settings_tabs.rb +69 -0
  74. data/lib/decidim/toggle/settings_form_builder.rb +200 -0
  75. data/lib/decidim/toggle/settings_tab_item.rb +37 -0
  76. data/lib/decidim/toggle/settings_tab_registry.rb +109 -0
  77. data/lib/decidim/toggle/settings_tabs.rb +56 -0
  78. data/lib/decidim/toggle/tab_form.rb +20 -0
  79. data/lib/decidim/toggle/version.rb +14 -0
  80. data/lib/decidim/toggle.rb +36 -0
  81. data/lib/tasks/decidim/toggle/toggle_upgrade.rake +13 -0
  82. data/lib/tasks/decidim/toggle/toggle_webpacker.rake +60 -0
  83. data/log/.gitignore +2 -0
  84. data/package.json +18 -0
  85. data/prettier.config.js +15 -0
  86. data/spec/commands/decidim/toggle/update_authorizations_command_spec.rb +41 -0
  87. data/spec/commands/decidim/toggle/update_emails_command_spec.rb +84 -0
  88. data/spec/commands/decidim/toggle/update_file_upload_settings_command_spec.rb +28 -0
  89. data/spec/commands/decidim/toggle/update_locale_command_spec.rb +53 -0
  90. data/spec/commands/decidim/toggle/update_module_config_command_spec.rb +38 -0
  91. data/spec/commands/decidim/toggle/update_name_command_spec.rb +49 -0
  92. data/spec/commands/decidim/toggle/update_omniauth_command_spec.rb +80 -0
  93. data/spec/commands/decidim/toggle/update_security_command_spec.rb +25 -0
  94. data/spec/decidim/toggle/settings_tab_item_spec.rb +34 -0
  95. data/spec/decidim/toggle/settings_tab_registry_spec.rb +66 -0
  96. data/spec/decidim/toggle/settings_tabs_spec.rb +60 -0
  97. data/spec/forms/concerns/decidim/toggle/informative_callouts_spec.rb +48 -0
  98. data/spec/forms/decidim/toggle/update_authorizations_form_spec.rb +40 -0
  99. data/spec/forms/decidim/toggle/update_emails_form_spec.rb +35 -0
  100. data/spec/forms/decidim/toggle/update_file_upload_settings_form_spec.rb +20 -0
  101. data/spec/forms/decidim/toggle/update_locale_form_spec.rb +64 -0
  102. data/spec/forms/decidim/toggle/update_name_form_spec.rb +57 -0
  103. data/spec/forms/decidim/toggle/update_omniauth_form_spec.rb +56 -0
  104. data/spec/forms/decidim/toggle/update_security_form_spec.rb +32 -0
  105. data/spec/helpers/decidim/toggle/system_settings_tab_helper_spec.rb +99 -0
  106. data/spec/lib/decidim/toggle/expose_attributes_to_js_spec.rb +31 -0
  107. data/spec/lib/decidim/toggle/expose_attributes_to_js_validator_spec.rb +30 -0
  108. data/spec/lib/decidim/toggle/gem_registry_spec.rb +30 -0
  109. data/spec/lib/decidim/toggle/javascript_config_spec.rb +169 -0
  110. data/spec/lib/decidim/toggle/module_config_form_spec.rb +45 -0
  111. data/spec/lib/decidim/toggle/module_config_spec.rb +74 -0
  112. data/spec/lib/decidim/toggle/module_configuration_presenter_spec.rb +53 -0
  113. data/spec/lib/decidim/toggle/settings_form_builder_spec.rb +115 -0
  114. data/spec/requests/decidim_toggle/system/settings_tab_spec.rb +144 -0
  115. data/spec/spec_helper.rb +12 -0
  116. data/spec/support/decidim_factories.rb +3 -0
  117. data/spec/support/devise.rb +5 -0
  118. data/spec/system/decidim_toggle/javascript_config_spec.rb +56 -0
  119. data/website/.gitignore +4 -0
  120. data/website/docs/developer/_category_.json +8 -0
  121. data/website/docs/developer/code-of-conduct.md +99 -0
  122. data/website/docs/developer/contribute.md +50 -0
  123. data/website/docs/developer/deface-usage.md +37 -0
  124. data/website/docs/developer/documentation.md +58 -0
  125. data/website/docs/developer/error-handling.md +51 -0
  126. data/website/docs/developer/tab-registry.md +51 -0
  127. data/website/docs/developer/view-customization.md +49 -0
  128. data/website/docs/index.md +80 -0
  129. data/website/docs/integrate/_category_.json +8 -0
  130. data/website/docs/integrate/attributes.md +121 -0
  131. data/website/docs/integrate/customize-views.md +62 -0
  132. data/website/docs/integrate/index.md +38 -0
  133. data/website/docs/integrate/informative_callout.md +80 -0
  134. data/website/docs/integrate/javascript.md +84 -0
  135. data/website/docs/integrate/labels.md +91 -0
  136. data/website/docs/integrate/quickstart.md +77 -0
  137. data/website/docusaurus.config.ts +100 -0
  138. data/website/package.json +31 -0
  139. data/website/sidebars.ts +7 -0
  140. data/website/src/css/custom.css +37 -0
  141. data/website/static/img/logo.svg +1 -0
  142. data/website/static/img/schema_overview.png +0 -0
  143. data/website/static/img/screenshot_informative_callouts.png +0 -0
  144. data/website/static/img/screenshot_saved_flash.png +0 -0
  145. data/website/static/img/screenshots_locale_tab.png +0 -0
  146. data/website/static/img/screenshots_name_tab.png +0 -0
  147. data/website/static/img/screenshots_security_tab.png +0 -0
  148. data/website/tsconfig.json +6 -0
  149. data/website/yarn.lock +8336 -0
  150. data/yarn.lock +13 -0
  151. metadata +249 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "label": "Contribute",
3
+ "position": 3,
4
+ "link": {
5
+ "type": "generated-index",
6
+ "description": "Code of conduct, contribution guide, and gem internals."
7
+ }
8
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ sidebar_position: 1
3
+ slug: /code-of-conduct
4
+ title: Code of Conduct
5
+ description: Rules for contributing to decidim-toggle
6
+ ---
7
+
8
+ # Contributor Covenant 3.0 Code of Conduct
9
+
10
+ ## Our Pledge
11
+
12
+ We pledge to make our community welcoming, safe, and equitable for all.
13
+
14
+ We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
15
+
16
+
17
+ ## Encouraged Behaviors
18
+
19
+ While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
20
+
21
+ With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
22
+
23
+ 1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
24
+ 2. Engaging **kindly and honestly** with others.
25
+ 3. Respecting **different viewpoints** and experiences.
26
+ 4. **Taking responsibility** for our actions and contributions.
27
+ 5. Gracefully giving and accepting **constructive feedback**.
28
+ 6. Committing to **repairing harm** when it occurs.
29
+ 7. Behaving in other ways that promote and sustain the **well-being of our community**.
30
+
31
+
32
+ ## Restricted Behaviors
33
+
34
+ We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
35
+
36
+ 1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
37
+ 2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
38
+ 3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
39
+ 4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
40
+ 5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
41
+ 6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
42
+ 7. Behaving in other ways that **threaten the well-being** of our community.
43
+
44
+ ### Other Restrictions
45
+
46
+ 1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
47
+ 2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
48
+ 3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
49
+ 4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
50
+
51
+
52
+ ## Reporting an Issue
53
+
54
+ Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
55
+
56
+ When an incident does occur, it is important to report it promptly. To report a possible violation, **[NOTE: describe your means of reporting here.]**
57
+
58
+ Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.
59
+
60
+
61
+ ## Addressing and Repairing Harm
62
+
63
+ **[NOTE: The remedies and repairs outlined below are suggestions based on best practices in code of conduct enforcement. If your community has its own established enforcement process, be sure to edit this section to describe your own policies.]**
64
+
65
+ If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
66
+
67
+ 1) Warning
68
+ 1) Event: A violation involving a single incident or series of incidents.
69
+ 2) Consequence: A private, written warning from the Community Moderators.
70
+ 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
71
+ 2) Temporarily Limited Activities
72
+ 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
73
+ 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
74
+ 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
75
+ 3) Temporary Suspension
76
+ 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
77
+ 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
78
+ 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
79
+ 4) Permanent Ban
80
+ 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
81
+ 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
82
+ 3) Repair: There is no possible repair in cases of this severity.
83
+
84
+ This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
85
+
86
+
87
+ ## Scope
88
+
89
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
90
+
91
+
92
+ ## Attribution
93
+
94
+ This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
95
+
96
+ Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
97
+
98
+ For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
99
+
@@ -0,0 +1,50 @@
1
+ ---
2
+ sidebar_position: 2
3
+ slug: /contributing
4
+ title: Contribute
5
+ description: Who maintains decidim-toggle and where to send issues and merge requests
6
+ ---
7
+
8
+ # Contribute
9
+
10
+ **decidim-toggle** is maintained by [Voca](https://voca.city), a project from [Octree](https://octree.ch).
11
+
12
+ Issues and merge requests are welcome on GitLab:
13
+
14
+ - [Repository](https://git.octree.ch/decidim/vocacity/decidim-modules/decidim-toggle)
15
+ - [Issues](https://git.octree.ch/decidim/vocacity/decidim-modules/decidim-toggle/-/issues)
16
+ - [Merge requests](https://git.octree.ch/decidim/vocacity/decidim-modules/decidim-toggle/-/merge_requests)
17
+
18
+ Read the [Code of conduct](/code-of-conduct) before participating.
19
+
20
+ Module developers adding a tab to their own gem: [Integrate](../integrate/index.md).
21
+
22
+ ## Before you push
23
+
24
+ ```bash
25
+ docker compose up -d
26
+ docker compose exec toggle bash -lc 'cd /home/module && bundle install -j$(nproc) && ./bin/check'
27
+ cd website && yarn build
28
+ ```
29
+
30
+ | Check | Command |
31
+ |-------|---------|
32
+ | All | `./bin/check` (RuboCop, erblint, RSpec) |
33
+ | RSpec | `unset DATABASE_URL && RAILS_ENV=test bundle exec rspec` |
34
+ | Docs | `cd website && yarn build` |
35
+
36
+ ## Deep dives
37
+
38
+ | Topic | Page |
39
+ |-------|------|
40
+ | Registry and `add_tab` internals | [Tab registry](./tab-registry.md) |
41
+ | Gem-owned views and layouts | [View customization](./view-customization.md) |
42
+ | Invalid form replay and redirects | [Error handling](./error-handling.md) |
43
+ | Deface overrides (`window.DecidimToggle`) | [Deface usage](./deface-usage.md) |
44
+ | Editing this documentation site | [Documentation website](./documentation.md) |
45
+
46
+ ## See also
47
+
48
+ - [Code of conduct](/code-of-conduct)
49
+ - [Integrate](../integrate/index.md)
50
+ - [Overview](../index.md)
@@ -0,0 +1,37 @@
1
+ ---
2
+ sidebar_position: 6
3
+ title: Deface usage
4
+ description: JavaScript config injection overrides
5
+ ---
6
+
7
+ # Deface usage
8
+
9
+ Deface injects `window.DecidimToggle` into participant and admin layouts without forking decidim-core. Overrides live in `app/overrides/` and are **ignored by Zeitwerk** — Deface loads them at boot.
10
+
11
+ ## Public site
12
+
13
+ | File | Virtual path | Inserts |
14
+ |------|--------------|---------|
15
+ | `add_toggle_javascript_public.rb` | `layouts/decidim/_decidim_javascript` | `layouts/decidim/toggle/javascript_config` after `js_configuration` |
16
+
17
+ ## Admin
18
+
19
+ | File | Virtual path | Inserts |
20
+ |------|--------------|---------|
21
+ | `add_toggle_javascript_admin.rb` | `layouts/decidim/admin/_header` | same partial after admin stylesheet pack tag |
22
+
23
+ ## Partial
24
+
25
+ `app/views/layouts/decidim/toggle/_javascript_config.html.erb` — sets `window.DecidimToggle` from `Decidim::Toggle.javascript_config_for(current_organization)`.
26
+
27
+ Forms opt in with `Decidim::Toggle::ExposeAttributesToJs` and `expose_to_javascript` — see [JavaScript](../integrate/javascript.md).
28
+
29
+ ## Drift check
30
+
31
+ When upgrading decidim-core, diff upstream layout templates against the `original:` strings in override files. System specs in `spec/system/decidim_toggle/javascript_config_spec.rb` assert injection end-to-end.
32
+
33
+ ## See also
34
+
35
+ - [JavaScript](../integrate/javascript.md)
36
+ - [Contribute](./contribute.md)
37
+ - [Documentation website](./documentation.md)
@@ -0,0 +1,58 @@
1
+ ---
2
+ sidebar_position: 7
3
+ title: Documentation website
4
+ description: Edit and build the Docusaurus site
5
+ ---
6
+
7
+ # Documentation website
8
+
9
+ [Docusaurus 3](https://docusaurus.io/) site with docs at the site root (`routeBasePath: /`). Published at [octree-gva.github.io/decidim-toggle](https://octree-gva.github.io/decidim-toggle/).
10
+
11
+ ## Site layout
12
+
13
+ ```text
14
+ website/docs/
15
+ index.md # Homepage
16
+ integrate/ # Module developers
17
+ developer/ # Gem contributors
18
+ ```
19
+
20
+ Sidebar is autogenerated from the folder tree (`sidebars.ts`).
21
+
22
+ ## Page structure
23
+
24
+ Every content page follows the same shape:
25
+
26
+ 1. **Front matter** — `title`, `description`, `sidebar_position`
27
+ 2. **H1** — matches the page title
28
+ 3. **Metadata** — `Who reads this`, `Prerequisite` (when applicable), `Next` (reading-order hint)
29
+ 4. **Intro** — one short paragraph
30
+ 5. **Body** — `##` sections with steps, tables, or code
31
+ 6. **See also** — links back to the section index, siblings, and cross-section pages
32
+
33
+ Homepage (`index.md`) uses the same metadata block; **See also** is a table of entry points.
34
+
35
+ ## Edit locally
36
+
37
+ ```bash
38
+ cd website
39
+ yarn
40
+ yarn start
41
+ ```
42
+
43
+ ## Build before merge
44
+
45
+ ```bash
46
+ cd website && yarn build
47
+ ```
48
+
49
+ `onBrokenLinks: 'throw'` — fix broken links before merging. GitLab job `node::docs` runs the same build on MRs and `main`.
50
+
51
+ ## Images
52
+
53
+ Static assets: `website/static/img/`. Reference from markdown as `/img/filename.png`.
54
+
55
+ ## See also
56
+
57
+ - [Contribute](./contribute.md)
58
+ - [Overview](../index.md)
@@ -0,0 +1,51 @@
1
+ ---
2
+ sidebar_position: 5
3
+ title: Error handling
4
+ description: Invalid tab saves, flash replay, and redirect anchors
5
+ ---
6
+
7
+ # Error handling
8
+
9
+ Each tab PATCH is independent. Validation failures must re-open the same tab with errors — without a full page POST/redirect cycle per field.
10
+
11
+ ## Happy path
12
+
13
+ `SettingsTabController#update` runs the tab command. On `:ok`:
14
+
15
+ - `flash[:notice]` — success copy
16
+ - Redirect to `decidim_system.edit_organization_path(organization)` with optional `anchor: "panel-toggle-<tab_id>"` when `decidim_toggle_active_tab` matches a registered tab
17
+
18
+ ## Invalid form
19
+
20
+ On `:invalid`, the controller stores a payload in `flash[:decidim_toggle_invalid_settings_tab]`:
21
+
22
+ ```ruby
23
+ {
24
+ organization_id:,
25
+ tab_id:,
26
+ params: { ... submitted organization params ... },
27
+ errors: { "attribute" => ["message", ...] }
28
+ }
29
+ ```
30
+
31
+ Redirect to the organization edit page (same anchor rules).
32
+
33
+ ## Replay on re-render
34
+
35
+ `decidim_toggle_settings_tab_form` checks that flash key. When `organization_id` and `tab_id` match the current tab:
36
+
37
+ 1. Rebuilds the form from stored params (not the DB model)
38
+ 2. Re-applies error messages on the form object
39
+ 3. Renders the tab with `form-error` markup
40
+
41
+ Custom `form_layout_partial:` tabs must use `tf.object` (or fields through `tf`) inside `decidim_toggle_settings_tab_form` so replayed errors bind to inputs — see `tabs/_language_tab.html.erb`.
42
+
43
+ ## Unknown tab
44
+
45
+ `PATCH …/settings_tab/<unknown>` → `404`.
46
+
47
+ ## See also
48
+
49
+ - [Tab registry](./tab-registry.md)
50
+ - [View customization](./view-customization.md)
51
+ - [Contribute](./contribute.md)
@@ -0,0 +1,51 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: Tab registry
4
+ description: SettingsTabRegistry, add_tab, and boot-time registration
5
+ ---
6
+
7
+ # Tab registry
8
+
9
+ Extensions register tabs at boot; the System UI reads the registry to render panels and route PATCH requests.
10
+
11
+ ## Boot flow
12
+
13
+ ```text
14
+ Engine initializer decidim_toggle.organization_settings_tabs
15
+ └── OrganizationSettingsTabs.register!
16
+ Your engine initializer
17
+ └── Decidim::Toggle.settings_tabs(:organization_settings) { |tabs| tabs.add_tab ... }
18
+ └── SettingsTabRegistry.register_form_tab(identifier, form, command, module_name:)
19
+ ```
20
+
21
+ ## Key files
22
+
23
+ ```text
24
+ lib/decidim/toggle/settings_tabs.rb # add_tab / remove_tab DSL
25
+ lib/decidim/toggle/settings_tab_registry.rb # form + command map per tab id
26
+ lib/decidim/toggle/settings_tab_item.rb # tab metadata (position, partials, if:)
27
+ lib/decidim/toggle/organization_settings_tabs.rb
28
+ app/controllers/decidim_toggle/system/settings_tab_controller.rb
29
+ ```
30
+
31
+ ## `add_tab` contract
32
+
33
+ One tab = one `Decidim::Form` subclass + one `Decidim::Command`.
34
+
35
+ - `identifier` — URL segment and DOM id (`panel-toggle-<identifier>`)
36
+ - `module_name:` — optional; links tab to JSON config row (`ModuleConfigForm` path)
37
+ - `remove_tab(id)` — drop a built-in tab before registering a replacement
38
+
39
+ Duplicate registration: last `add_tab` wins. In development/test, same id with a **different** form/command raises `DuplicateTabRegistrationError`.
40
+
41
+ ## Save endpoint
42
+
43
+ `PATCH /decidim_toggle/system/organizations/:organization_id/settings_tab/:tab_id`
44
+
45
+ Controller resolves `registry.form_tab(tab_id)`, builds the form from params, runs the command.
46
+
47
+ ## See also
48
+
49
+ - [Contribute](./contribute.md)
50
+ - [Error handling](./error-handling.md)
51
+ - [Add a settings tab](../integrate/quickstart.md) — integrator walkthrough
@@ -0,0 +1,49 @@
1
+ ---
2
+ sidebar_position: 4
3
+ title: View customization
4
+ description: Internal views that render the tabbed organization settings UI
5
+ ---
6
+
7
+ # View customization
8
+
9
+ Internal templates for decidim-toggle. Integrators customize **their** tab via `form_layout_partial:` — see [Customize views](../integrate/customize-views.md).
10
+
11
+ ## Override entry point
12
+
13
+ `app/views/decidim/system/organizations/edit.html.erb` — replaces the flat org edit form with `settings_tabs` partial.
14
+
15
+ ## Tab shell
16
+
17
+ | Partial | Role |
18
+ |---------|------|
19
+ | `_settings_tabs.html.erb` | Tab nav + panels; encryption gate |
20
+ | `_default_form_tab.html.erb` | Default wrapper: `decidim_toggle_settings_tab_form` + `all_fields` |
21
+ | `tabs/_omniauth_tab.html.erb` | Built-in OmniAuth layout |
22
+ | `tabs/_emails_tab.html.erb` | Built-in SMTP layout |
23
+ | `_settings_tab_submit.html.erb` | Cancel / save |
24
+ | `_settings_tab_active_tab_field.html.erb` | Hidden `decidim_toggle_active_tab` |
25
+ | `_encryption_not_configured_callout.html.erb` | Shown when `encryption_configured?` is false |
26
+ | `tabs/_language_tab.html.erb` | Built-in locale table layout |
27
+ | `tabs/_security_tab.html.erb` | Built-in security layout |
28
+ | `tabs/_authorizations_tab.html.erb` | Built-in authorizations layout |
29
+ | `tabs/_file_upload_tab.html.erb` | Built-in file upload layout |
30
+
31
+ ## Helpers
32
+
33
+ `Decidim::Toggle::SystemSettingsTabHelper`:
34
+
35
+ - `decidim_toggle_settings_tab_form(organization, tab)` — form shell, flash error replay, callouts
36
+ - `decidim_toggle_update_settings_tab_organization_path(organization, tab_id:)`
37
+ - `encryption_configured?`
38
+
39
+ `Decidim::Toggle::SettingsFormBuilder` — `all_fields`, `fields_for_names`, `informative_callouts`.
40
+
41
+ ## Styles
42
+
43
+ `app/packs/stylesheets/decidim/toggle/organization_settings.scss` — tab accordion layout.
44
+
45
+ ## See also
46
+
47
+ - [Customize views](../integrate/customize-views.md) — integrator partials
48
+ - [Tab registry](./tab-registry.md)
49
+ - [Error handling](./error-handling.md)
@@ -0,0 +1,80 @@
1
+ ---
2
+ sidebar_position: 1
3
+ slug: /
4
+ title: Overview
5
+ description: Tabbed System administration for Decidim — register org settings from your module
6
+ ---
7
+
8
+ # Decidim Toggle
9
+
10
+ decidim-toggle rewrites **System → Organizations** in the Decidim admin. Instead of one long form, settings are grouped in tabs. The gem ships built-in tabs (name, OmniAuth, SMTP, language, authorizations, security, file upload) and exposes a small API so your `decidim-*` module can add its own tab.
11
+
12
+ With this gem you **add a tab**, **plug in your form and command**, and **read or write configuration** per organization through `Decidim::Toggle.config_for` — no extra tables .
13
+
14
+
15
+ <div class="full">
16
+
17
+ ![Decidim Toggle /system menu for security](/img/screenshots_security_tab.png)
18
+
19
+ </div>
20
+
21
+
22
+ ## Compare
23
+
24
+
25
+ | | Decidim | Decidim Toggle |
26
+ | -- | ------| --------------- |
27
+ | Update tenant name, host | ✅ | ✅ |
28
+ | Advanced configuration | ✅ | ✅ |
29
+ | Update SMTP credentials | ✅ | ✅ |
30
+ | Configure machine translation | ❌ | ✅ |
31
+ | Change locales | ❌ | ✅ |
32
+ | Can extend the /system | ❌ | ✅ |
33
+
34
+
35
+
36
+ ## Get started
37
+
38
+ <div class="full">
39
+
40
+ ![How it works](/img/schema_overview.png)
41
+
42
+ </div>
43
+
44
+
45
+ ### Use in your `decidim-*` gem
46
+
47
+ Add the dependency in your gemspec:
48
+
49
+ ```ruby
50
+ # decidim-my_module.gemspec
51
+ s.add_dependency "decidim-toggle"
52
+ ```
53
+
54
+ Then register a tab in your engine — full steps in [Add a settings tab](./integrate/quickstart.md).
55
+
56
+ ### Install in a Decidim app (optional)
57
+
58
+ Add to the Gemfile:
59
+
60
+ ```ruby
61
+ gem "decidim-toggle"
62
+ ```
63
+
64
+ ```bash
65
+ bundle install
66
+ rails decidim_toggle:install:migrations
67
+ rails db:migrate
68
+ ```
69
+
70
+ ## Compatibility
71
+
72
+ Tested on Decidim **0.29**. Works on **0.31** as well (same System organization surface and Form/Command contract).
73
+
74
+ ## See also
75
+
76
+ | You are… | Read |
77
+ |----------|------|
78
+ | Building a module tab | [Integrate](./integrate/index.md) → [Add a settings tab](./integrate/quickstart.md) |
79
+ | Hacking decidim-toggle | [Contribute](/contributing) |
80
+ | Something wrong | [GitLab issues](https://git.octree.ch/decidim/vocacity/decidim-modules/decidim-toggle/-/issues) |
@@ -0,0 +1,8 @@
1
+ {
2
+ "label": "Integrate",
3
+ "position": 2,
4
+ "link": {
5
+ "type": "generated-index",
6
+ "description": "Add a tab → define attributes → customize views."
7
+ }
8
+ }
@@ -0,0 +1,121 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: Attributes
4
+ description: Form attributes supported by SettingsFormBuilder
5
+ ---
6
+
7
+ # Attributes
8
+
9
+ **Who reads this:** module developers defining tab form fields.
10
+ **Prerequisite:** [Add a settings tab](./quickstart.md) — form class from step 1.
11
+ **Next:** [Informative callouts](./informative_callout.md) or [Customize views](./customize-views.md).
12
+
13
+ Declare attributes on your `Decidim::Form`. `Decidim::Toggle::SettingsFormBuilder` renders them via `all_fields` (default tab) or `fields_for_names` (inside a `form_layout_partial:`).
14
+
15
+ ```ruby
16
+ module MyModule
17
+ class AdminConfigForm < Decidim::Form
18
+ include Decidim::Toggle::ModuleConfigForm
19
+
20
+ self.module_config_name = "my_module"
21
+ mimic :organization
22
+
23
+ attribute :enabled, :boolean
24
+ attribute :api_key, :string
25
+ attribute :max_items, :integer
26
+ attribute :mode, :string
27
+ attribute :tags, [String]
28
+
29
+ def self.collection_for_mode
30
+ [%w[live Live], %w[draft Draft]]
31
+ end
32
+ end
33
+ end
34
+ ```
35
+
36
+ ## Supported attribute types
37
+
38
+ | Declaration | Widget | Notes |
39
+ |-------------|--------|-------|
40
+ | `attribute :x, :boolean` | Checkbox | |
41
+ | `attribute :x, :string` | Text field | `secondary_hosts` renders as textarea |
42
+ | `attribute :x, :integer` | Number field | |
43
+ | Other scalar types | Text field | Fallback when no collection |
44
+ | `attribute :x, [String]` + `collection_for_x` | Check boxes | Multi-select |
45
+ | Scalar + `collection_for_x` | Radio buttons | Single choice |
46
+ | `translatable_attribute :x, String` | Translated field | Requires `Decidim::TranslatableAttributes`; locale keys hidden from `all_fields` |
47
+ | `translatable_attribute :x, Decidim::Attributes::RichText` | Translated editor | Rich text per locale |
48
+
49
+ `id` and per-locale keys (e.g. `name_en`) are excluded from `all_fields` when they belong to a translatable hash.
50
+
51
+ ## Collections
52
+
53
+ Add `collection_for_<attribute_name>` returning `[[value, label], ...]`:
54
+
55
+ ```ruby
56
+ attribute :users_registration_mode, :string
57
+
58
+ def self.collection_for_users_registration_mode
59
+ Decidim::Organization.users_registration_modes.map do |mode|
60
+ [mode.first, I18n.t("decidim.system.organizations.users_registration_mode.#{mode.first}")]
61
+ end
62
+ end
63
+ ```
64
+
65
+ - **Scalar attribute** → radio buttons
66
+ - **Array attribute** (`[String]`, etc.) → check boxes
67
+
68
+ ## Field helptext
69
+
70
+ Optional copy under a field — see [Labels](./labels.md).
71
+
72
+ ```yaml
73
+ # config/locales/my_module_en.yml
74
+ en:
75
+ decidim_toggle:
76
+ system:
77
+ my_module:
78
+ helptext:
79
+ api_key: "Shown to server-side jobs only."
80
+ ```
81
+
82
+ ## Disabled fields
83
+
84
+ Disable a field in the default builder by implementing either:
85
+
86
+ - `#attribute_disabled?(attribute)` on the form instance, or
87
+ - `#disabled_for_<attribute>?` per attribute
88
+
89
+ ```ruby
90
+ def attribute_disabled?(attribute)
91
+ attribute == :beta_enabled && !Decidim::Toggle.gem_present?("decidim-beta")
92
+ end
93
+ ```
94
+
95
+ Disabled inputs are not submitted; keep your command aligned (ignore or reject unknown params).
96
+
97
+ The field wrapper gets `class="field is-disabled"` (Decidim convention) so you can style muted labels/inputs in CSS.
98
+
99
+ ## Builder methods
100
+
101
+ | Method | Use |
102
+ |--------|-----|
103
+ | `f.all_fields` | Every form attribute (default tab body) |
104
+ | `f.fields_for_names(:enabled, :mode)` | Named subset inside a partial or layout |
105
+
106
+ Used in views as `f` / `tf` (`Decidim::Toggle::SettingsFormBuilder`).
107
+
108
+ ## Nested or custom widgets
109
+
110
+ Nested forms (e.g. `Decidim::System::FileUploadSettingsForm`) or non-standard markup are **not** auto-rendered. Use `form_layout_partial:` — [Customize views](./customize-views.md).
111
+
112
+ For info, warning, and danger banners above the fields, see [Informative callouts](./informative_callout.md).
113
+
114
+ ## See also
115
+
116
+ - [Add a settings tab](./quickstart.md)
117
+ - [Labels](./labels.md)
118
+ - [Informative callouts](./informative_callout.md)
119
+ - [Customize views](./customize-views.md)
120
+ - [JavaScript](./javascript.md)
121
+ - [Integrate](./index.md)