clickwrap 0.0.0 → 0.1.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +90 -0
  3. data/CHANGELOG.md +612 -0
  4. data/README.md +830 -1204
  5. data/SECURITY.md +33 -0
  6. data/app/assets/stylesheets/clickwrap.css +241 -0
  7. data/app/controllers/clickwrap/application_controller.rb +79 -0
  8. data/app/controllers/clickwrap/captures_controller.rb +145 -0
  9. data/app/controllers/clickwrap/document_versions_controller.rb +71 -0
  10. data/app/controllers/clickwrap/receipts_controller.rb +115 -0
  11. data/app/controllers/clickwrap/withdrawals_controller.rb +60 -0
  12. data/app/helpers/clickwrap/engine_helper.rb +97 -0
  13. data/app/views/clickwrap/captures/show.html.erb +34 -0
  14. data/app/views/clickwrap/receipts/index.html.erb +38 -0
  15. data/app/views/clickwrap/receipts/show.html.erb +91 -0
  16. data/app/views/clickwrap/shared/_error_summary.html.erb +39 -0
  17. data/app/views/clickwrap/shared/_fields.html.erb +100 -0
  18. data/app/views/clickwrap/shared/_statement.html.erb +105 -0
  19. data/app/views/clickwrap/withdrawals/new.html.erb +30 -0
  20. data/config/locales/en.yml +160 -0
  21. data/config/locales/es.yml +138 -0
  22. data/config/routes.rb +41 -0
  23. data/exe/clickwrap +374 -0
  24. data/guides/README.md +30 -0
  25. data/guides/accessibility.md +249 -0
  26. data/guides/consent-and-lifecycle.md +299 -0
  27. data/guides/integrating.md +610 -0
  28. data/guides/integrity.md +212 -0
  29. data/guides/migrating.md +335 -0
  30. data/guides/naming.md +320 -0
  31. data/guides/organizations.md +320 -0
  32. data/guides/receipts-and-verification.md +415 -0
  33. data/guides/request-evidence.md +512 -0
  34. data/guides/retention-and-legal-holds.md +438 -0
  35. data/lib/clickwrap/actor_proxy.rb +147 -0
  36. data/lib/clickwrap/anonymous_actor.rb +47 -0
  37. data/lib/clickwrap/authority.rb +174 -0
  38. data/lib/clickwrap/canonical_json.rb +216 -0
  39. data/lib/clickwrap/capture/event_builder.rb +220 -0
  40. data/lib/clickwrap/capture/presentation_verifier.rb +521 -0
  41. data/lib/clickwrap/capture.rb +650 -0
  42. data/lib/clickwrap/configuration.rb +1129 -0
  43. data/lib/clickwrap/controller_helpers.rb +758 -0
  44. data/lib/clickwrap/current_state.rb +282 -0
  45. data/lib/clickwrap/digest.rb +125 -0
  46. data/lib/clickwrap/doctor.rb +418 -0
  47. data/lib/clickwrap/document_definition.rb +255 -0
  48. data/lib/clickwrap/document_renderer.rb +83 -0
  49. data/lib/clickwrap/document_renderers/markdown.rb +175 -0
  50. data/lib/clickwrap/document_renderers/markdown_rails.rb +126 -0
  51. data/lib/clickwrap/dsl/policy_builder.rb +462 -0
  52. data/lib/clickwrap/dsl/retention_builder.rb +89 -0
  53. data/lib/clickwrap/durable_commit_callback.rb +37 -0
  54. data/lib/clickwrap/engine.rb +184 -0
  55. data/lib/clickwrap/errors.rb +181 -0
  56. data/lib/clickwrap/form_builder_extensions.rb +341 -0
  57. data/lib/clickwrap/front_matter.rb +67 -0
  58. data/lib/clickwrap/identifier.rb +112 -0
  59. data/lib/clickwrap/import/external_receipt.rb +241 -0
  60. data/lib/clickwrap/import/fine_print.rb +290 -0
  61. data/lib/clickwrap/import/legacy.rb +450 -0
  62. data/lib/clickwrap/integrations/organizations_authority.rb +81 -0
  63. data/lib/clickwrap/integrity/anchor.rb +130 -0
  64. data/lib/clickwrap/integrity/attestation_reconciler.rb +114 -0
  65. data/lib/clickwrap/integrity/attestor.rb +221 -0
  66. data/lib/clickwrap/integrity/chain.rb +313 -0
  67. data/lib/clickwrap/integrity/timestamp.rb +143 -0
  68. data/lib/clickwrap/ip_geolocation/location.rb +112 -0
  69. data/lib/clickwrap/ip_geolocation/null_resolver.rb +35 -0
  70. data/lib/clickwrap/ip_geolocation/resolver.rb +97 -0
  71. data/lib/clickwrap/ip_geolocation/static_resolver.rb +107 -0
  72. data/lib/clickwrap/ip_geolocation/trackdown_resolver.rb +330 -0
  73. data/lib/clickwrap/ip_geolocation.rb +16 -0
  74. data/lib/clickwrap/lifecycle.rb +534 -0
  75. data/lib/clickwrap/linter.rb +382 -0
  76. data/lib/clickwrap/localized_text.rb +101 -0
  77. data/lib/clickwrap/macros.rb +203 -0
  78. data/lib/clickwrap/models/application_record.rb +20 -0
  79. data/lib/clickwrap/models/chain_head.rb +79 -0
  80. data/lib/clickwrap/models/concerns/has_clickwraps.rb +55 -0
  81. data/lib/clickwrap/models/disposition_plan.rb +208 -0
  82. data/lib/clickwrap/models/document.rb +46 -0
  83. data/lib/clickwrap/models/document_version.rb +163 -0
  84. data/lib/clickwrap/models/event.rb +743 -0
  85. data/lib/clickwrap/models/event_document.rb +79 -0
  86. data/lib/clickwrap/models/event_statement.rb +92 -0
  87. data/lib/clickwrap/models/external_action.rb +150 -0
  88. data/lib/clickwrap/models/integrity_attestation.rb +90 -0
  89. data/lib/clickwrap/models/legal_hold.rb +81 -0
  90. data/lib/clickwrap/models/policy_revision.rb +115 -0
  91. data/lib/clickwrap/models/presentation.rb +59 -0
  92. data/lib/clickwrap/models/receipt_access.rb +53 -0
  93. data/lib/clickwrap/models/recording_sequence.rb +21 -0
  94. data/lib/clickwrap/models/request_evidence.rb +378 -0
  95. data/lib/clickwrap/models/statement_identity_lock.rb +38 -0
  96. data/lib/clickwrap/models/statement_state.rb +130 -0
  97. data/lib/clickwrap/pending_receipt.rb +177 -0
  98. data/lib/clickwrap/policy.rb +283 -0
  99. data/lib/clickwrap/presentation_manifest.rb +210 -0
  100. data/lib/clickwrap/presenter.rb +716 -0
  101. data/lib/clickwrap/privacy.rb +419 -0
  102. data/lib/clickwrap/protected_outcome.rb +120 -0
  103. data/lib/clickwrap/receipt.rb +606 -0
  104. data/lib/clickwrap/receipt_html.rb +235 -0
  105. data/lib/clickwrap/receipt_verifier.rb +978 -0
  106. data/lib/clickwrap/reference.rb +44 -0
  107. data/lib/clickwrap/registration.rb +236 -0
  108. data/lib/clickwrap/registry.rb +54 -0
  109. data/lib/clickwrap/remediation_token.rb +155 -0
  110. data/lib/clickwrap/request_evidence_extractor.rb +590 -0
  111. data/lib/clickwrap/request_evidence_policy.rb +261 -0
  112. data/lib/clickwrap/retention/applier.rb +231 -0
  113. data/lib/clickwrap/retention/disposition.rb +221 -0
  114. data/lib/clickwrap/retention/planner.rb +502 -0
  115. data/lib/clickwrap/retention_class.rb +97 -0
  116. data/lib/clickwrap/reviewed_text.rb +28 -0
  117. data/lib/clickwrap/schema_requirements.rb +196 -0
  118. data/lib/clickwrap/services/authorize_external_action.rb +149 -0
  119. data/lib/clickwrap/services/load_policies.rb +69 -0
  120. data/lib/clickwrap/services/publish_documents.rb +251 -0
  121. data/lib/clickwrap/services/validate_policy_references.rb +166 -0
  122. data/lib/clickwrap/statement.rb +248 -0
  123. data/lib/clickwrap/subject_fingerprint.rb +28 -0
  124. data/lib/clickwrap/submission.rb +169 -0
  125. data/lib/clickwrap/system_actor.rb +31 -0
  126. data/lib/clickwrap/test_helpers.rb +676 -0
  127. data/lib/clickwrap/testing.rb +211 -0
  128. data/lib/clickwrap/trusted_proxy_configuration.rb +92 -0
  129. data/lib/clickwrap/verification.rb +504 -0
  130. data/lib/clickwrap/version.rb +12 -1
  131. data/lib/clickwrap/view_helpers.rb +190 -0
  132. data/lib/clickwrap/vocabulary.rb +294 -0
  133. data/lib/clickwrap.rb +497 -7
  134. data/lib/generators/clickwrap/document_generator.rb +164 -0
  135. data/lib/generators/clickwrap/hardening_generator.rb +177 -0
  136. data/lib/generators/clickwrap/install_generator.rb +1287 -0
  137. data/lib/generators/clickwrap/link_generator.rb +56 -0
  138. data/lib/generators/clickwrap/policy_generator.rb +118 -0
  139. data/lib/generators/clickwrap/templates/clickwrap_hardening.rb.erb +256 -0
  140. data/lib/generators/clickwrap/templates/clickwrap_policies.rb.erb +192 -0
  141. data/lib/generators/clickwrap/templates/create_clickwrap_external_action_tables.rb.erb +128 -0
  142. data/lib/generators/clickwrap/templates/create_clickwrap_integrity_tables.rb.erb +157 -0
  143. data/lib/generators/clickwrap/templates/create_clickwrap_presentation_tables.rb.erb +160 -0
  144. data/lib/generators/clickwrap/templates/create_clickwrap_request_evidence_tables.rb.erb +180 -0
  145. data/lib/generators/clickwrap/templates/create_clickwrap_retention_tables.rb.erb +174 -0
  146. data/lib/generators/clickwrap/templates/create_clickwrap_tables.rb.erb +568 -0
  147. data/lib/generators/clickwrap/templates/initializer.rb.erb +439 -0
  148. data/lib/generators/clickwrap/templates/link_clickwrap_event_migration.rb.erb +12 -0
  149. data/lib/generators/clickwrap/templates/policy.rb.erb +31 -0
  150. data/lib/generators/clickwrap/templates/policy_test.rb.erb +56 -0
  151. data/lib/generators/clickwrap/templates/privacy.md.erb +58 -0
  152. data/lib/generators/clickwrap/templates/terms.md.erb +49 -0
  153. data/lib/generators/clickwrap/upgrade_generator.rb +50 -0
  154. data/lib/generators/clickwrap/views_generator.rb +101 -0
  155. data/lib/tasks/clickwrap.rake +569 -0
  156. metadata +311 -16
@@ -0,0 +1,105 @@
1
+ <%#
2
+ ONE CONTROL: one checkbox or one choice group, one label carrying the exact
3
+ words the server offered, the documents it is about, and its own error.
4
+
5
+ It renders two things, the same way. A single statement — an optional consent,
6
+ a recorded yes/no, an assertion the application worded itself, anything a
7
+ policy's composed line could not honestly absorb — and the composed line
8
+ itself, which is one control standing for several statements at once. Both
9
+ answer the same questions about a control (name, id, required, choices, error)
10
+ and this file asks nothing else of them.
11
+
12
+ Rules this file exists to keep:
13
+
14
+ * a control means exactly what the server signed it means. The composed line
15
+ is one box for several statements ONLY because the manifest says which
16
+ ones and the server answers all of them from it; two boxes answering one
17
+ statement each is the shape nothing can reconstruct later;
18
+ * the control is REAL and starts EMPTY. There is no `checked` anywhere in
19
+ this file and there never will be: a pre-ticked box records the page's
20
+ default rather than a person's action;
21
+ * the label contains the words and is tied to the control, so pressing them
22
+ works and assistive technology announces them together;
23
+ * document links come BEFORE the action and carry the document's own name
24
+ (not "click here");
25
+ * `required` is progressive enhancement only. THE SERVER DECIDES. A browser
26
+ that ignores it, a client that never sends the field, and a script that
27
+ posts by hand all meet the same server-side check at capture — which is
28
+ also why nothing here prints the word "Required" beside a control.
29
+
30
+ Locals: statement, error
31
+ %>
32
+ <%
33
+ error = local_assigns[:error].presence
34
+ described_by = error ? statement.error_id : nil
35
+ statement_classes = ["clickwrap-statement", "clickwrap-statement--#{statement.kind}"]
36
+ statement_classes << "clickwrap-statement--invalid" if error
37
+ %>
38
+ <div class="<%= statement_classes.join(" ") %>">
39
+ <% if statement.choices.present? %>
40
+ <%# An explicit yes/no decision: every option unselected, so the answer is
41
+ one the person actually made rather than one the page made for them. %>
42
+ <%
43
+ fieldset_options = { class: "clickwrap-statement__choices" }
44
+ fieldset_options[:"aria-invalid"] = "true" if error
45
+ fieldset_options[:"aria-describedby"] = described_by if described_by
46
+ %>
47
+ <%= tag.fieldset(**fieldset_options) do %>
48
+ <legend class="clickwrap-statement__legend"><%= statement.assertion %></legend>
49
+
50
+ <% statement.choices.each_key do |choice| %>
51
+ <%
52
+ choice_id = "#{statement.control_id}_#{choice}"
53
+ choice_options = { id: choice_id, class: "clickwrap-statement__radio" }
54
+ choice_options[:required] = true if statement.requires_an_explicit_choice?
55
+ choice_options[:"aria-describedby"] = described_by if described_by
56
+ %>
57
+ <span class="clickwrap-statement__choice">
58
+ <%= radio_button_tag statement.control_name, choice, false, choice_options %>
59
+ <%= label_tag choice_id,
60
+ t("clickwrap.choices.#{choice}", default: choice.to_s.humanize),
61
+ class: "clickwrap-statement__choice-label" %>
62
+ </span>
63
+ <% end %>
64
+ <% end %>
65
+ <% else %>
66
+ <%
67
+ control_options = { id: statement.control_id, class: "clickwrap-statement__checkbox" }
68
+ control_options[:required] = true if statement.required?
69
+ control_options[:"aria-invalid"] = "true" if error
70
+ control_options[:"aria-describedby"] = described_by if described_by
71
+ %>
72
+ <div class="clickwrap-statement__control">
73
+ <%= check_box_tag statement.control_name, "1", false, control_options %>
74
+ <%= label_tag statement.control_id, clickwrap_statement_label(statement),
75
+ class: "clickwrap-statement__label" %>
76
+ </div>
77
+ <% end %>
78
+
79
+ <%# Only for a statement standing on its own. The composed line's links are
80
+ inside its sentence, so it has none to list here. %>
81
+ <% if statement.documents.any? %>
82
+ <ul class="clickwrap-documents">
83
+ <% statement.documents.each do |document| %>
84
+ <li class="clickwrap-documents__item"><%= clickwrap_document_link(document) %></li>
85
+ <% end %>
86
+ </ul>
87
+ <% end %>
88
+
89
+ <% if error %>
90
+ <%# Announced through aria-describedby above, and readable as ordinary text
91
+ here: the meaning is in the words, not in the color they are printed in. %>
92
+ <p class="clickwrap-statement__error" id="<%= statement.error_id %>">
93
+ <span class="clickwrap-statement__error-prefix"><%= t("clickwrap.ui.error_prefix") %></span>
94
+ <%= Array(error).join(" ") %>
95
+ </p>
96
+ <% end %>
97
+
98
+ <% if statement.withdrawal_path.present? %>
99
+ <p class="clickwrap-statement__withdrawal">
100
+ <%= link_to t("clickwrap.ui.withdrawal_link", purpose: statement.purpose_key.humanize.downcase),
101
+ statement.withdrawal_path,
102
+ class: "clickwrap-statement__withdrawal-link" %>
103
+ </p>
104
+ <% end %>
105
+ </div>
@@ -0,0 +1,30 @@
1
+ <%#
2
+ Withdrawing one consent purpose: one sentence, one button.
3
+
4
+ Nothing is added to this page. Withdrawal must be no harder than granting was,
5
+ and every extra step here would be a step someone did not have to take to say
6
+ yes.
7
+ %>
8
+ <%= clickwrap_styles %>
9
+
10
+ <div class="clickwrap clickwrap-screen">
11
+ <header class="clickwrap-screen__header">
12
+ <h1 class="clickwrap-screen__title">
13
+ <%= t("clickwrap.withdrawals.title", purpose: @purpose_key.humanize.downcase) %>
14
+ </h1>
15
+ <p class="clickwrap-screen__intro"><%= t("clickwrap.withdrawals.explanation") %></p>
16
+ </header>
17
+
18
+ <% if flash.now[:alert].present? || flash[:alert].present? %>
19
+ <p class="clickwrap-flash clickwrap-flash--alert" role="alert"><%= flash[:alert] %></p>
20
+ <% end %>
21
+
22
+ <%= form_with url: clickwrap_routes.withdrawal_path(@purpose_key),
23
+ method: :post,
24
+ class: "clickwrap-screen__form" do %>
25
+ <%= hidden_field_tag :return_to, @return_to, id: nil %>
26
+ <%= submit_tag t("clickwrap.withdrawals.confirm_button"), class: "clickwrap-submit" %>
27
+ <% end %>
28
+
29
+ <p class="clickwrap-note"><%= t("clickwrap.withdrawals.what_this_does") %></p>
30
+ </div>
@@ -0,0 +1,160 @@
1
+ # The gem's own English strings.
2
+ #
3
+ # Two things to know before changing anything in here:
4
+ #
5
+ # 1. Wording is product design, and some of these sentences carry meaning the
6
+ # rest of the gem depends on. A person AGREES TO Terms and ACKNOWLEDGES a
7
+ # Privacy Notice — a privacy notice is information someone is entitled to,
8
+ # not a bargain they strike, and "I agree to the privacy policy" quietly
9
+ # turns one into the other. Keep the verbs matched to the kinds.
10
+ # 2. Nothing here may claim, or read as claiming, that anything is compliant,
11
+ # enforceable, legally binding, court-proof, or that a person read or
12
+ # understood a document. These strings appear on screens and in receipts,
13
+ # which is exactly where an overclaim would do damage.
14
+ #
15
+ # Hosts override any of this by defining the same key in their own locale files
16
+ # (the application's translations load last and win), and a policy can name its
17
+ # own key or literal text instead.
18
+ en:
19
+ clickwrap:
20
+ # Conventional first-person assertions. `agree_to :terms` with no
21
+ # `statement:` resolves to clickwrap.statements.agreement.terms; a policy
22
+ # that says something more specific passes its own text or key.
23
+ statements:
24
+ agreement:
25
+ terms: "I agree to the Terms."
26
+ acknowledgment:
27
+ privacy_notice: "I acknowledge the Privacy Notice."
28
+
29
+ # The composed one-line offer. When every statement in a policy is an
30
+ # ordinary, required, default-worded agreement or acknowledgment, they
31
+ # render as ONE control carrying ONE sentence, with the documents linked
32
+ # inside it:
33
+ #
34
+ # I agree to the Terms of Service and I acknowledge the Privacy Policy.
35
+ #
36
+ # One fragment per kind, and the verbs stay matched to the kinds for the
37
+ # reason at the top of this file. `%{documents}` is where the links go;
38
+ # a fragment without it composes nothing and the statements render on
39
+ # lines of their own, which is also what happens in any locale that has
40
+ # not translated these four keys.
41
+ sentence:
42
+ agreement: "I agree to the %{documents}"
43
+ acknowledgment: "I acknowledge the %{documents}"
44
+ # Between two documents on ONE statement ("the Terms and the Schedules"),
45
+ # between two fragments, and at the end of the whole line.
46
+ documents_joiner: " and the "
47
+ joiner: " and "
48
+ terminator: "."
49
+
50
+ ui:
51
+ error_summary_heading: "Check these before continuing"
52
+ error_prefix: "Error:"
53
+ # Announced, never drawn. A sighted person can see the icon their browser
54
+ # gives a new tab; a screen-reader user cannot, and neither of them needs
55
+ # the words printed beside every link. Rendered only when the link really
56
+ # does open a new tab, because a same-window link claiming otherwise would
57
+ # be the page lying about itself.
58
+ opens_in_new_tab: "(opens in a new tab)"
59
+ # Receipts only. A version label beside a checkbox is a fact nobody can
60
+ # act on; a version label on the record is the whole point of the record.
61
+ document_version: "Version %{version}"
62
+ withdrawal_link: "Withdraw %{purpose} at any time"
63
+
64
+ # Labels for an explicit yes/no consent choice. Quoted because unquoted
65
+ # `yes:` and `no:` are booleans in YAML, not the words.
66
+ choices:
67
+ "yes": "Yes"
68
+ "no": "No"
69
+ grant: "Yes"
70
+ decline: "No"
71
+
72
+ errors:
73
+ required_statement: "You need to answer this before continuing."
74
+ answer_not_accepted: "This answer was not accepted. Please check it and try again."
75
+ presentation_no_longer_valid: "This page expired before it was submitted, so it has been
76
+ offered again. Please check your answers and submit once more."
77
+ temporarily_unavailable: "This could not be recorded because the database was temporarily
78
+ busy. Nothing was accepted. Please check your answers and try again."
79
+
80
+ captures:
81
+ title: "Before you continue"
82
+ intro: "Please look over the documents below and make your choices. Nothing is selected for
83
+ you."
84
+ submit_button_text: "Continue"
85
+ recorded: "Thank you — that has been recorded."
86
+
87
+ withdrawals:
88
+ title: "Withdraw %{purpose}"
89
+ explanation: "You can withdraw this at any time, and it takes one press — the same as giving
90
+ it did."
91
+ confirm_button: "Withdraw"
92
+ confirmed: "That purpose has been withdrawn."
93
+ already_withdrawn: "That purpose was already withdrawn."
94
+ recorded_reason: "The person withdrew this purpose from the consent withdrawal screen."
95
+ what_this_does: "Withdrawing records that you have withdrawn this purpose from now on. It
96
+ adds a new record rather than deleting the earlier one, so the history stays accurate."
97
+
98
+ receipts:
99
+ index_title: "Your records"
100
+ show_title: "Record"
101
+ empty: "There is nothing here yet."
102
+ policy: "Policy"
103
+ event_id: "Record ID"
104
+ event_type: "What happened"
105
+ recorded_at_by_server: "Recorded by the server at"
106
+ submit_button_text: "Call to action in the server offer"
107
+ combined_sentence: "Sentence the server offered, in one control"
108
+ acts: "Recorded statements and answers"
109
+ documents: "Document versions bound to this record"
110
+ offered_and_not_taken: "offered, not taken"
111
+ canonical_json: "Download the canonical JSON"
112
+ what_this_records: "This record shows the exact wording and document versions this application
113
+ generated as an offer, and what was submitted back. It does not show that anyone read or
114
+ understood the documents, and it is not a legal opinion about them."
115
+
116
+ # The six kinds, for display. This taxonomy is product design; naming an act
117
+ # an agreement or a consent does not decide its legal effect.
118
+ kinds:
119
+ agreement: "Agreement"
120
+ acknowledgment: "Acknowledgment"
121
+ consent: "Consent"
122
+ declaration: "Declaration"
123
+ attestation: "Attestation"
124
+ authorization: "Authorization"
125
+
126
+ actions:
127
+ agreed: "Agreed"
128
+ acknowledged: "Acknowledged"
129
+ granted: "Granted"
130
+ declined: "Declined"
131
+ declared: "Declared"
132
+ attested: "Attested"
133
+ authorized: "Authorized"
134
+ withdrawn: "Withdrawn"
135
+ renewed: "Renewed"
136
+ scope_changed: "Scope changed"
137
+ superseded: "Superseded"
138
+ expired: "Expired"
139
+ consumed: "Used"
140
+ revoked: "Revoked"
141
+ corrected: "Corrected"
142
+
143
+ event_types:
144
+ capture: "Submitted"
145
+ withdrawal: "Withdrawn"
146
+ correction: "Corrected"
147
+ supersession: "Superseded"
148
+ expiry: "Expired"
149
+ consumption: "Used"
150
+ revocation: "Revoked"
151
+ renewal: "Renewed"
152
+ scope_change: "Scope changed"
153
+ exemption: "Recorded exemption — no human action"
154
+ imported_legacy: "Imported from earlier records"
155
+ external_receipt: "Recorded by another provider"
156
+ disposition: "Disposed of under a retention rule"
157
+ legal_hold_placed: "Placed on hold"
158
+ legal_hold_released: "Hold released"
159
+ receipt_access: "Record accessed"
160
+ provider_outcome: "Provider outcome recorded"
@@ -0,0 +1,138 @@
1
+ # The gem's own Spanish strings.
2
+ #
3
+ # The same two rules as en.yml, which is the reference file:
4
+ #
5
+ # 1. Verbs stay matched to the kinds. A person AGREES TO Terms ("acepto") and
6
+ # ACKNOWLEDGES a Privacy Notice ("doy por recibida") — a privacy notice is
7
+ # information someone is entitled to, not a bargain they strike, and
8
+ # "acepto la política de privacidad" quietly turns one into the other
9
+ # (this is the AEPD's own distinction; see the request-evidence guide).
10
+ # 2. Nothing here may claim, or read as claiming, that anything is compliant,
11
+ # enforceable, legally binding, or that a person read or understood a
12
+ # document. "He leído..." is deliberately absent.
13
+ #
14
+ # Grammatical gender: forms address the person directly ("tú") and avoid
15
+ # gendered self-descriptions ("informado/a"), so every string works for
16
+ # everyone without slashed suffixes.
17
+ es:
18
+ clickwrap:
19
+ statements:
20
+ agreement:
21
+ terms: "Acepto los Términos."
22
+ acknowledgment:
23
+ privacy_notice: "Doy por recibida la Política de Privacidad."
24
+
25
+ # La frase de una sola línea. En español el artículo concuerda con el
26
+ # documento ("los Términos", "la Política"), y solo la aplicación sabe qué
27
+ # documento nombra, así que el artículo vive en la etiqueta del enlace y no
28
+ # en estas plantillas — por eso `documents_joiner` es " y " y no " y el ".
29
+ sentence:
30
+ agreement: "Acepto %{documents}"
31
+ acknowledgment: "He recibido %{documents}"
32
+ documents_joiner: " y "
33
+ joiner: " y "
34
+ terminator: "."
35
+
36
+ ui:
37
+ error_summary_heading: "Revisa esto antes de continuar"
38
+ error_prefix: "Error:"
39
+ opens_in_new_tab: "(se abre en una pestaña nueva)"
40
+ document_version: "Versión %{version}"
41
+ withdrawal_link: "Retira %{purpose} cuando quieras"
42
+
43
+ choices:
44
+ "yes": "Sí"
45
+ "no": "No"
46
+ grant: "Sí"
47
+ decline: "No"
48
+
49
+ errors:
50
+ required_statement: "Debes responder esto antes de continuar."
51
+ answer_not_accepted: "Esta respuesta no se ha aceptado. Revísala e inténtalo de nuevo."
52
+ presentation_no_longer_valid: "Esta página caducó antes de enviarse, así que se ha vuelto
53
+ a mostrar. Revisa tus respuestas y envíala otra vez."
54
+ temporarily_unavailable: "No se ha podido registrar porque la base de datos estaba
55
+ temporalmente ocupada. No se ha aceptado nada. Revisa tus respuestas e inténtalo de nuevo."
56
+
57
+ captures:
58
+ title: "Antes de continuar"
59
+ intro: "Revisa los documentos siguientes y marca tus respuestas. No hay nada seleccionado
60
+ de antemano."
61
+ submit_button_text: "Continuar"
62
+ recorded: "Gracias — ha quedado registrado."
63
+
64
+ withdrawals:
65
+ title: "Retirar %{purpose}"
66
+ explanation: "Puedes retirarlo cuando quieras, y cuesta una sola pulsación — igual que
67
+ costó darlo."
68
+ confirm_button: "Retirar"
69
+ confirmed: "Ese permiso ha quedado retirado."
70
+ already_withdrawn: "Ese permiso ya estaba retirado."
71
+ recorded_reason: "La persona retiró este permiso desde la pantalla de retirada de
72
+ consentimiento."
73
+ what_this_does: "Retirarlo registra que a partir de ahora has retirado este permiso. Se
74
+ añade un registro nuevo en lugar de borrar el anterior, así el historial sigue siendo
75
+ exacto."
76
+
77
+ receipts:
78
+ index_title: "Tus registros"
79
+ show_title: "Registro"
80
+ empty: "Aquí no hay nada todavía."
81
+ policy: "Política"
82
+ event_id: "ID del registro"
83
+ event_type: "Qué ocurrió"
84
+ recorded_at_by_server: "Registrado por el servidor el"
85
+ submit_button_text: "Botón de acción de la oferta del servidor"
86
+ combined_sentence: "Frase que ofreció el servidor, en una sola casilla"
87
+ acts: "Declaraciones y respuestas registradas"
88
+ documents: "Versiones de documentos vinculadas a este registro"
89
+ offered_and_not_taken: "ofrecido, no marcado"
90
+ canonical_json: "Descargar el JSON canónico"
91
+ what_this_records: "Este registro muestra la redacción y las versiones de documentos
92
+ exactas que esta aplicación generó como oferta, y lo que se envió de vuelta. No
93
+ demuestra que nadie leyera o entendiera los documentos, y no es una opinión legal
94
+ sobre ellos."
95
+
96
+ kinds:
97
+ agreement: "Acuerdo"
98
+ acknowledgment: "Acuse de recibo"
99
+ consent: "Consentimiento"
100
+ declaration: "Declaración"
101
+ attestation: "Certificación"
102
+ authorization: "Autorización"
103
+
104
+ actions:
105
+ agreed: "Aceptado"
106
+ acknowledged: "Recibido"
107
+ granted: "Concedido"
108
+ declined: "Rechazado"
109
+ declared: "Declarado"
110
+ attested: "Certificado"
111
+ authorized: "Autorizado"
112
+ withdrawn: "Retirado"
113
+ renewed: "Renovado"
114
+ scope_changed: "Alcance modificado"
115
+ superseded: "Sustituido"
116
+ expired: "Caducado"
117
+ consumed: "Utilizado"
118
+ revoked: "Revocado"
119
+ corrected: "Corregido"
120
+
121
+ event_types:
122
+ capture: "Enviado"
123
+ withdrawal: "Retirado"
124
+ correction: "Corregido"
125
+ supersession: "Sustituido"
126
+ expiry: "Caducado"
127
+ consumption: "Utilizado"
128
+ revocation: "Revocado"
129
+ renewal: "Renovado"
130
+ scope_change: "Alcance modificado"
131
+ exemption: "Exención registrada — sin acción humana"
132
+ imported_legacy: "Importado de registros anteriores"
133
+ external_receipt: "Registrado por otro proveedor"
134
+ disposition: "Eliminado según una regla de retención"
135
+ legal_hold_placed: "Puesto en retención legal"
136
+ legal_hold_released: "Retención legal levantada"
137
+ receipt_access: "Registro consultado"
138
+ provider_outcome: "Resultado del proveedor registrado"
data/config/routes.rb ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The engine's own routes, drawn under whatever the host mounted it at:
4
+ #
5
+ # mount Clickwrap::Engine => "/agreements"
6
+ #
7
+ # Four surfaces, and nothing else: complete a policy, read a receipt, withdraw a
8
+ # consent, read a document version. They exist so that a required agreement, an
9
+ # expired declaration, or a consent someone wants back is resolvable IN PLACE
10
+ # instead of turning into a support ticket.
11
+ #
12
+ # Every screen has BOTH a GET and a form-action path at the same URL. That is
13
+ # deliberate: a validation failure re-renders under the address the browser is
14
+ # already on, so a no-JavaScript submission, a Turbo Drive visit, and a Hotwire
15
+ # Native web screen all stay in the navigation context the host intended,
16
+ # instead of landing on a POST-only URL that cannot be reloaded or shared.
17
+ Clickwrap::Engine.routes.draw do
18
+ # Policy keys, consent purposes, and document version ids are plain
19
+ # identifiers. Constraining them keeps a path segment from carrying something
20
+ # shaped like a path.
21
+ identifier = %r{[^/.]+}
22
+
23
+ get "policies/:policy_key", to: "captures#show", as: :capture,
24
+ constraints: { policy_key: identifier }
25
+ post "policies/:policy_key", to: "captures#create", as: :capture_submission,
26
+ constraints: { policy_key: identifier }
27
+
28
+ get "consents/:purpose_key/withdrawal", to: "withdrawals#new", as: :withdrawal,
29
+ constraints: { purpose_key: identifier }
30
+ post "consents/:purpose_key/withdrawal", to: "withdrawals#create", as: :withdrawal_submission,
31
+ constraints: { purpose_key: identifier }
32
+
33
+ resources :receipts, only: %i[index show]
34
+
35
+ # What every document link in every presentation points at, and what an
36
+ # auditor opens years later: one immutable published version, by id.
37
+ get "documents/:id", to: "document_versions#show", as: :document_version,
38
+ constraints: { id: identifier }
39
+
40
+ root to: "receipts#index"
41
+ end