decidim-action_delegator 0.8.2 → 0.9.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.
- checksums.yaml +4 -4
- data/README.md +104 -58
- data/Rakefile +2 -2
- data/app/commands/decidim/action_delegator/admin/create_delegation.rb +1 -3
- data/app/commands/decidim/action_delegator/admin/create_setting.rb +5 -2
- data/app/commands/decidim/action_delegator/admin/update_setting.rb +4 -2
- data/app/controllers/concerns/decidim/action_delegator/devise/sessions_controller_override.rb +1 -1
- data/app/controllers/concerns/decidim/action_delegator/election_per_question_votes_controller_override.rb +65 -0
- data/app/controllers/concerns/decidim/action_delegator/election_votes_controller_override.rb +38 -0
- data/app/controllers/concerns/decidim/action_delegator/votes_controller_methods.rb +46 -0
- data/app/controllers/decidim/action_delegator/admin/application_controller.rb +11 -5
- data/app/controllers/decidim/action_delegator/admin/delegations_controller.rb +2 -10
- data/app/controllers/decidim/action_delegator/admin/invite_participants_controller.rb +0 -8
- data/app/controllers/decidim/action_delegator/admin/manage_delegations_controller.rb +13 -20
- data/app/controllers/decidim/action_delegator/admin/manage_participants_controller.rb +11 -19
- data/app/controllers/decidim/action_delegator/admin/participants_controller.rb +1 -9
- data/app/controllers/decidim/action_delegator/admin/permissions_controller.rb +1 -24
- data/app/controllers/decidim/action_delegator/admin/ponderations_controller.rb +0 -8
- data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +12 -12
- data/app/controllers/decidim/action_delegator/application_controller.rb +3 -3
- data/app/controllers/decidim/action_delegator/elections/admin/results_controller.rb +58 -0
- data/app/controllers/decidim/action_delegator/elections/results_controller.rb +36 -0
- data/app/controllers/decidim/action_delegator/user_delegations_controller.rb +1 -1
- data/app/controllers/decidim/action_delegator/verifications/delegations_verifier/authorizations_controller.rb +14 -18
- data/app/forms/decidim/action_delegator/admin/action_delegator_census_form.rb +44 -0
- data/app/forms/decidim/action_delegator/admin/csv_import_form.rb +17 -0
- data/app/forms/decidim/action_delegator/admin/ponderation_form.rb +2 -1
- data/app/forms/decidim/action_delegator/admin/setting_form.rb +16 -10
- data/app/forms/decidim/action_delegator/censuses/internal_users_form.rb +32 -0
- data/app/forms/decidim/action_delegator/verifications/delegations_verifier_form.rb +41 -25
- data/app/helpers/decidim/action_delegator/admin/{delegation_helper.rb → settings_helper.rb} +3 -16
- data/app/helpers/decidim/action_delegator/settings_helper.rb +124 -0
- data/app/jobs/decidim/action_delegator/admin/invite_participants_job.rb +1 -1
- data/app/jobs/decidim/action_delegator/send_sms_job.rb +2 -2
- data/app/jobs/decidim/action_delegator/twilio_send_sms_job.rb +2 -2
- data/app/models/decidim/action_delegator/delegation.rb +15 -14
- data/app/models/decidim/action_delegator/participant.rb +7 -10
- data/app/models/decidim/action_delegator/ponderation.rb +0 -2
- data/app/models/decidim/action_delegator/setting.rb +14 -32
- data/app/overrides/decidim/elections/admin/dashboard/_results/_add_results_contents.html.erb.deface +10 -0
- data/app/overrides/decidim/elections/admin/dashboard/_results/_add_results_tabs.html.erb.deface +3 -0
- data/app/overrides/decidim/elections/elections/_election_aside/add_delegation_buttons.html.erb.deface +3 -0
- data/app/overrides/decidim/elections/elections/_vote_results/replace_results_div.html.erb.deface +8 -0
- data/app/overrides/decidim/elections/per_question_votes/show/add_delegation_hidden_input.html.erb.deface +3 -0
- data/app/overrides/decidim/elections/per_question_votes/show/add_delegation_id_to_links.html.erb.deface +3 -0
- data/app/overrides/decidim/elections/per_question_votes/waiting/add_delegation_buttons.html.erb.deface +3 -0
- data/app/overrides/decidim/elections/votes/receipt/add_delegation_buttons.html.erb.deface +3 -0
- data/app/packs/entrypoints/decidim_action_delegator_elections.js +1 -0
- data/app/packs/src/decidim/action_delegator/elections_live_results.js +160 -0
- data/app/permissions/decidim/action_delegator/admin/permissions.rb +29 -0
- data/app/permissions/decidim/action_delegator/permissions.rb +6 -30
- data/app/presenters/decidim/action_delegator/setting_presenter.rb +33 -0
- data/app/queries/decidim/action_delegator/action_delegator_census_users.rb +51 -0
- data/app/queries/decidim/action_delegator/authorized_resources.rb +28 -0
- data/app/queries/decidim/action_delegator/election_settings.rb +23 -0
- data/app/queries/decidim/action_delegator/{delegated_votes_versions.rb → elections_delegated_votes_versions.rb} +8 -8
- data/app/queries/decidim/action_delegator/elections_question_responses_by_type.rb +33 -0
- data/app/queries/decidim/action_delegator/elections_question_weighted_responses.rb +28 -0
- data/app/queries/decidim/action_delegator/elections_votes_with_ponderations.rb +62 -0
- data/app/services/decidim/action_delegator/participants_csv_importer.rb +1 -1
- data/app/services/decidim/action_delegator/sms_gateway.rb +2 -2
- data/app/views/decidim/action_delegator/admin/censuses/_action_delegator_census_form.html.erb +44 -0
- data/app/views/decidim/action_delegator/admin/censuses/_action_delegator_census_options_form.html.erb +25 -0
- data/app/views/decidim/action_delegator/admin/delegations/index.html.erb +12 -13
- data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +29 -27
- data/app/views/decidim/action_delegator/admin/manage_delegations/new.html.erb +36 -24
- data/app/views/decidim/action_delegator/admin/manage_participants/new.html.erb +47 -33
- data/app/views/decidim/action_delegator/admin/participants/_form.html.erb +14 -8
- data/app/views/decidim/action_delegator/admin/participants/edit.html.erb +11 -13
- data/app/views/decidim/action_delegator/admin/participants/index.html.erb +20 -25
- data/app/views/decidim/action_delegator/admin/participants/new.html.erb +11 -13
- data/app/views/decidim/action_delegator/admin/ponderations/_form.html.erb +13 -7
- data/app/views/decidim/action_delegator/admin/ponderations/edit.html.erb +11 -13
- data/app/views/decidim/action_delegator/admin/ponderations/index.html.erb +8 -11
- data/app/views/decidim/action_delegator/admin/ponderations/new.html.erb +11 -13
- data/app/views/decidim/action_delegator/admin/settings/_form.html.erb +26 -12
- data/app/views/decidim/action_delegator/admin/settings/_participants_sync_check.html.erb +1 -1
- data/app/views/decidim/action_delegator/admin/settings/_setting_checks.html.erb +11 -16
- data/app/views/decidim/action_delegator/admin/settings/edit.html.erb +11 -12
- data/app/views/decidim/action_delegator/admin/settings/index.html.erb +22 -19
- data/app/views/decidim/action_delegator/admin/settings/new.html.erb +11 -12
- data/app/views/decidim/action_delegator/admin/shared/_tabs_menu.html.erb +15 -0
- data/app/views/decidim/action_delegator/censuses/_internal_users_form.html.erb +61 -0
- data/app/views/decidim/action_delegator/elections/_delegation_buttons.html.erb +10 -0
- data/app/views/decidim/action_delegator/elections/_normal_election_buttons.html.erb +13 -0
- data/app/views/decidim/action_delegator/elections/_per_question_buttons.html.erb +9 -0
- data/app/views/decidim/action_delegator/elections/_per_question_waiting_buttons.html.erb +19 -0
- data/app/views/decidim/action_delegator/elections/_vote_results.html.erb +10 -0
- data/app/views/decidim/action_delegator/elections/_vote_results_question.html.erb +13 -0
- data/app/views/decidim/action_delegator/elections/admin/dashboard/_by_type_and_weight.html.erb +45 -0
- data/app/views/decidim/action_delegator/elections/admin/dashboard/_results_tabs.html.erb +5 -0
- data/app/views/decidim/action_delegator/elections/admin/dashboard/_results_type_info.html.erb +4 -0
- data/app/views/decidim/action_delegator/elections/admin/dashboard/_sum_of_weights.html.erb +43 -0
- data/app/views/decidim/action_delegator/elections/admin/dashboard/_totals.html.erb +27 -0
- data/app/views/decidim/action_delegator/user_delegations/index.html.erb +13 -14
- data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/edit.html.erb +19 -29
- data/app/views/decidim/action_delegator/verifications/delegations_verifier/authorizations/new.html.erb +42 -40
- data/config/assets.rb +3 -36
- data/config/i18n-tasks.yml +27 -5
- data/config/locales/ca.yml +103 -146
- data/config/locales/cs.yml +161 -145
- data/config/locales/en.yml +110 -75
- data/config/locales/es.yml +108 -154
- data/db/migrate/20200824113801_create_settings.rb +1 -1
- data/db/migrate/20250729104037_add_title_to_action_delegator_settings.rb +31 -0
- data/lib/decidim/action_delegator/admin_engine.rb +72 -45
- data/lib/decidim/action_delegator/common_rake.rb +13 -0
- data/lib/decidim/action_delegator/engine.rb +48 -19
- data/lib/decidim/action_delegator/test/delegation_examples.rb +144 -0
- data/lib/decidim/action_delegator/test/factories.rb +11 -6
- data/lib/decidim/action_delegator/verifications/delegations_authorizer.rb +80 -47
- data/lib/decidim/action_delegator/version.rb +3 -3
- data/lib/decidim/action_delegator.rb +8 -26
- data/lib/tasks/migrate_consultations.rake +382 -0
- data/lib/tasks/upgrade_tasks.rake +5 -0
- data/package.json +10 -27
- metadata +72 -122
- data/app/commands/concerns/decidim/action_delegator/consultations/multiple_vote_question_override.rb +0 -31
- data/app/commands/concerns/decidim/action_delegator/consultations/vote_question_override.rb +0 -44
- data/app/commands/decidim/action_delegator/admin/fix_resource_permissions.rb +0 -46
- data/app/commands/decidim/action_delegator/vote_delegation.rb +0 -28
- data/app/controllers/concerns/decidim/action_delegator/consultations/consultations_controller_override.rb +0 -16
- data/app/controllers/concerns/decidim/action_delegator/consultations/question_multiple_votes_controller_override.rb +0 -29
- data/app/controllers/concerns/decidim/action_delegator/consultations/question_votes_controller_override.rb +0 -57
- data/app/controllers/concerns/decidim/action_delegator/consultations/questions_controller_override.rb +0 -16
- data/app/controllers/concerns/decidim/action_delegator/needs_consultation_styles.rb +0 -24
- data/app/controllers/decidim/action_delegator/admin/consultations/exports_controller.rb +0 -27
- data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +0 -47
- data/app/controllers/decidim/action_delegator/admin/exports/_sum_of_weights_controller.rb +0 -15
- data/app/controllers/decidim/action_delegator/questions_summary_controller.rb +0 -14
- data/app/forms/concerns/decidim/action_delegator/consultations/vote_form_override.rb +0 -15
- data/app/helpers/decidim/action_delegator/delegation_helper.rb +0 -13
- data/app/jobs/decidim/action_delegator/export_consultation_results_job.rb +0 -51
- data/app/models/concerns/decidim/action_delegator/consultations/question_override.rb +0 -36
- data/app/models/concerns/decidim/action_delegator/consultations/vote_override.rb +0 -15
- data/app/models/decidim/action_delegator/unversioned_vote.rb +0 -19
- data/app/models/decidim/action_delegator/whodunnit_vote.rb +0 -28
- data/app/overrides/decidim/consultations/admin/consultations/results/add_ongoing_warning.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/consultations/_question/add_delegation_link.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/consultations/_question/replace_vote_info.html.erb.deface +0 -4
- data/app/overrides/decidim/consultations/consultations/_regular_questions/prevent_empty_questions.html.erb.deface +0 -10
- data/app/overrides/decidim/consultations/consultations/_regular_questions/remove_highlighted_scopes.html.erb.deface +0 -5
- data/app/overrides/decidim/consultations/question_multiple_votes/_form/add_delegation_notice.html.erb.deface +0 -8
- data/app/overrides/decidim/consultations/questions/_results/replace_results.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_button/add_delegations_link.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_button/add_modal.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_button/add_modal_javascript.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_button/replace_delegation_to_multivote_link.html.erb.deface +0 -8
- data/app/overrides/decidim/consultations/questions/_vote_modal/add_delegation_callout.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_delegation_callout.html.erb.deface +0 -3
- data/app/overrides/decidim/consultations/questions/_vote_modal_confirm/add_hidden_field.html.erb.deface +0 -3
- data/app/overrides/layouts/decidim/_consultation_header/add_consultation_callout.html.erb.deface +0 -9
- data/app/overrides/layouts/decidim/_question_header/add_consultation_callout.html.erb.deface +0 -9
- data/app/overrides/layouts/decidim/admin/remove_deprecation.rb +0 -10
- data/app/packs/entrypoints/decidim_action_delegator.scss +0 -1
- data/app/packs/entrypoints/decidim_action_delegator_questions.js +0 -5
- data/app/packs/entrypoints/decidim_action_delegator_questions_summary.js +0 -1
- data/app/packs/src/decidim/action_delegator/questions.js +0 -33
- data/app/packs/src/decidim/action_delegator/summary.js +0 -24
- data/app/packs/stylesheets/decidim/action_delegator/questions.scss +0 -26
- data/app/permissions/concerns/decidim/action_delegator/consultations/permissions_override.rb +0 -35
- data/app/presenters/decidim/action_delegator/question_with_totals.rb +0 -24
- data/app/queries/decidim/action_delegator/consultation_delegations.rb +0 -25
- data/app/queries/decidim/action_delegator/delegates_votes_by_consultation.rb +0 -24
- data/app/queries/decidim/action_delegator/delegates_votes_by_question.rb +0 -26
- data/app/queries/decidim/action_delegator/delegation_votes.rb +0 -30
- data/app/queries/decidim/action_delegator/grantee_delegations.rb +0 -24
- data/app/queries/decidim/action_delegator/organization_delegations.rb +0 -26
- data/app/queries/decidim/action_delegator/organization_settings.rb +0 -31
- data/app/queries/decidim/action_delegator/responses.rb +0 -24
- data/app/queries/decidim/action_delegator/responses_by_membership.rb +0 -58
- data/app/queries/decidim/action_delegator/scrutiny.rb +0 -87
- data/app/queries/decidim/action_delegator/setting_delegations.rb +0 -19
- data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +0 -44
- data/app/queries/decidim/action_delegator/sum_of_weights.rb +0 -25
- data/app/queries/decidim/action_delegator/type_and_weight.rb +0 -26
- data/app/queries/decidim/action_delegator/voted_with_ponderations.rb +0 -30
- data/app/queries/decidim/action_delegator/votes_count_aggregation.rb +0 -34
- data/app/serializers/decidim/action_delegator/consultation_results_serializer.rb +0 -19
- data/app/views/decidim/action_delegator/admin/consultations/_ongoing_consultation_warning.html.erb +0 -3
- data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +0 -65
- data/app/views/decidim/action_delegator/admin/consultations/weighted_results.html.erb +0 -66
- data/app/views/decidim/action_delegator/consultations/_link_to_question.html.erb +0 -11
- data/app/views/decidim/action_delegator/consultations/_link_with_results.html.erb +0 -11
- data/app/views/decidim/action_delegator/consultations/questions/_callout.html.erb +0 -5
- data/app/views/decidim/action_delegator/consultations/questions/_delegations_modal.html.erb +0 -34
- data/app/views/decidim/action_delegator/consultations/questions/_link_to_delegations.html.erb +0 -15
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_active.html.erb +0 -32
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_finished.html.erb +0 -9
- data/app/views/decidim/action_delegator/consultations/questions/_vote_delegated_upcoming.html.erb +0 -8
- data/app/views/decidim/action_delegator/consultations/questions/_weight_results.html.erb +0 -8
- data/app/views/decidim/consultations/question_votes/_callout.html.erb +0 -47
- data/app/views/decidim/consultations/question_votes/update_vote_button.js.erb +0 -82
- data/lib/tasks/import_direct_verification.rake +0 -30
@@ -0,0 +1,160 @@
|
|
1
|
+
/* eslint-disable max-params */
|
2
|
+
document.addEventListener("DOMContentLoaded", () => {
|
3
|
+
const watchingDiv = document.querySelector("[data-weight-results-live-update]");
|
4
|
+
const questionsContainer = document.querySelector("[data-questions-container]");
|
5
|
+
const questionTemplate = document.getElementById("question-0");
|
6
|
+
const optionTemplate = document.querySelector("[data-option-template]");
|
7
|
+
if (!watchingDiv) {
|
8
|
+
return;
|
9
|
+
}
|
10
|
+
|
11
|
+
const url = watchingDiv.dataset.weightResultsLiveUpdate;
|
12
|
+
const optionVotesPercentTexts = () => document.querySelectorAll("[data-option-votes-percent-text]");
|
13
|
+
const optionVotesCountTexts = () => document.querySelectorAll("[data-option-votes-count-text]");
|
14
|
+
const optionVotesWidths = () => document.querySelectorAll("[data-option-votes-width]");
|
15
|
+
const questionUnweightedCountTexts = () => document.querySelectorAll("[data-question-unweighted-count-text]");
|
16
|
+
const questionWeightedCountTexts = () => document.querySelectorAll("[data-question-weighted-count-text]");
|
17
|
+
const questionDelegatedCountTexts = () => document.querySelectorAll("[data-question-delegated-count-text]");
|
18
|
+
const questionParticipantsCountTexts = () => document.querySelectorAll("[data-question-participants-count-text]");
|
19
|
+
|
20
|
+
const animateText = (element, value) => {
|
21
|
+
if (element.textContent === value) {
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
element.textContent = value;
|
25
|
+
element.classList.add("live_results-number_changing");
|
26
|
+
setTimeout(() => {
|
27
|
+
element.classList.remove("live_results-number_changing");
|
28
|
+
}, 1000);
|
29
|
+
};
|
30
|
+
|
31
|
+
const digOptionValue = (data, questionId, optionId, ponderationId, key) => {
|
32
|
+
data.questions = data.questions || [];
|
33
|
+
const question = data.questions.find((item) => item.id === parseInt(questionId, 10));
|
34
|
+
if (!question) {
|
35
|
+
return null;
|
36
|
+
}
|
37
|
+
const responseOptions = question.response_options || [];
|
38
|
+
if (!Array.isArray(responseOptions)) {
|
39
|
+
return null;
|
40
|
+
}
|
41
|
+
const option = responseOptions.find((item) => {
|
42
|
+
if (ponderationId) {
|
43
|
+
return item.id === parseInt(optionId, 10) && item.ponderation_id === parseInt(ponderationId, 10);
|
44
|
+
}
|
45
|
+
return item.id === parseInt(optionId, 10);
|
46
|
+
});
|
47
|
+
// console.log("Digging option:", questionId, optionId, ponderationId, option);
|
48
|
+
if (!option) {
|
49
|
+
return null;
|
50
|
+
}
|
51
|
+
if (key in option) {
|
52
|
+
return option[key];
|
53
|
+
}
|
54
|
+
return null;
|
55
|
+
};
|
56
|
+
|
57
|
+
const createAdditionalQuestions = (data) => {
|
58
|
+
if (!questionTemplate || !questionsContainer || !optionTemplate) {
|
59
|
+
return;
|
60
|
+
}
|
61
|
+
const questions = data.questions || [];
|
62
|
+
const additionalQuestions = questions.filter((question) => !document.getElementById(`question-${question.id}`) && question.published_results);
|
63
|
+
additionalQuestions.forEach((question) => {
|
64
|
+
const questionElement = questionTemplate.cloneNode(true);
|
65
|
+
questionElement.id = `question-${question.id}`;
|
66
|
+
questionElement.classList.remove("hidden");
|
67
|
+
questionElement.querySelector("[data-question-body]").textContent = question.body;
|
68
|
+
const optionsContainer = questionElement.querySelector("[data-options-container]");
|
69
|
+
question.response_options.forEach((option) => {
|
70
|
+
const optionElement = optionTemplate.cloneNode(true);
|
71
|
+
optionElement.classList.remove("hidden");
|
72
|
+
optionElement.querySelector("[data-option-body]").textContent = option.body;
|
73
|
+
optionElement.querySelector("[data-option-votes-count-text").dataset.optionVotesCountText = `${question.id},${option.id}`;
|
74
|
+
optionElement.querySelector("[data-option-votes-percent-text").dataset.optionVotesPercentText = `${question.id},${option.id}`;
|
75
|
+
optionElement.querySelector("[data-option-votes-width").dataset.optionVotesWidth = `${question.id},${option.id}`;
|
76
|
+
optionsContainer.appendChild(optionElement);
|
77
|
+
});
|
78
|
+
questionsContainer.appendChild(questionElement);
|
79
|
+
|
80
|
+
});
|
81
|
+
};
|
82
|
+
const fetchResults = async () => {
|
83
|
+
try {
|
84
|
+
const response = await fetch(url, {
|
85
|
+
method: "GET",
|
86
|
+
headers: {
|
87
|
+
"Accept": "application/json",
|
88
|
+
"Content-Type": "application/json",
|
89
|
+
"X-Requested-With": "XMLHttpRequest"
|
90
|
+
}
|
91
|
+
});
|
92
|
+
if (!response.ok) {
|
93
|
+
throw new Error("Network response was not ok");
|
94
|
+
}
|
95
|
+
const data = await response.json();
|
96
|
+
// console.log("Fetched data:", data);
|
97
|
+
|
98
|
+
createAdditionalQuestions(data);
|
99
|
+
optionVotesCountTexts().forEach((el) => {
|
100
|
+
const [questionId, optionId, ponderationId] = el.dataset.optionVotesCountText.split(",");
|
101
|
+
console.log("Updating count text for:", questionId, optionId, ponderationId);
|
102
|
+
const val = digOptionValue(data, questionId, optionId, ponderationId, "votes_count_text")
|
103
|
+
if (val) {
|
104
|
+
animateText(el, val);
|
105
|
+
}
|
106
|
+
});
|
107
|
+
optionVotesPercentTexts().forEach((el) => {
|
108
|
+
const [questionId, optionId, ponderationId] = el.dataset.optionVotesPercentText.split(",");
|
109
|
+
const val = digOptionValue(data, questionId, optionId, ponderationId, "votes_percent_text")
|
110
|
+
if (val) {
|
111
|
+
animateText(el, val);
|
112
|
+
}
|
113
|
+
});
|
114
|
+
optionVotesWidths().forEach((el) => {
|
115
|
+
const [questionId, optionId, ponderationId] = el.dataset.optionVotesWidth.split(",");
|
116
|
+
const val = digOptionValue(data, questionId, optionId, ponderationId, "votes_percent")
|
117
|
+
if (val) {
|
118
|
+
el.style.width = `${val}%`;
|
119
|
+
}
|
120
|
+
});
|
121
|
+
questionUnweightedCountTexts().forEach((el) => {
|
122
|
+
const questionId = el.dataset.questionUnweightedCountText;
|
123
|
+
const question = data.questions.find((item) => item.id === parseInt(questionId, 10));
|
124
|
+
if (question && question.unweighted_votes_text) {
|
125
|
+
animateText(el, question.unweighted_votes_text);
|
126
|
+
}
|
127
|
+
});
|
128
|
+
questionWeightedCountTexts().forEach((el) => {
|
129
|
+
const questionId = el.dataset.questionWeightedCountText;
|
130
|
+
const question = data.questions.find((item) => item.id === parseInt(questionId, 10));
|
131
|
+
if (question && question.weighted_votes_text) {
|
132
|
+
animateText(el, question.weighted_votes_text);
|
133
|
+
}
|
134
|
+
});
|
135
|
+
questionDelegatedCountTexts().forEach((el) => {
|
136
|
+
const questionId = el.dataset.questionDelegatedCountText;
|
137
|
+
const question = data.questions.find((item) => item.id === parseInt(questionId, 10));
|
138
|
+
if (question && question.delegated_votes_text) {
|
139
|
+
animateText(el, question.delegated_votes_text);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
questionParticipantsCountTexts().forEach((el) => {
|
143
|
+
const questionId = el.dataset.questionParticipantsCountText;
|
144
|
+
const question = data.questions.find((item) => item.id === parseInt(questionId, 10));
|
145
|
+
if (question && question.participants_text) {
|
146
|
+
animateText(el, question.participants_text);
|
147
|
+
}
|
148
|
+
});
|
149
|
+
|
150
|
+
// repeat for ongoing elections only
|
151
|
+
if (data.ongoing) {
|
152
|
+
setTimeout(fetchResults, 4000);
|
153
|
+
}
|
154
|
+
} catch (error) {
|
155
|
+
console.error("Error fetching results:", error);
|
156
|
+
}
|
157
|
+
};
|
158
|
+
|
159
|
+
fetchResults();
|
160
|
+
});
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
module Admin
|
6
|
+
class Permissions < Decidim::DefaultPermissions
|
7
|
+
def permissions
|
8
|
+
return permission_action if permission_action.scope != :admin
|
9
|
+
return permission_action unless user && user.admin?
|
10
|
+
return permission_action unless [:delegation, :ponderation, :participant, :setting].include?(permission_action.subject)
|
11
|
+
|
12
|
+
if permission_action.action == :destroy
|
13
|
+
toggle_allow(resource.present?)
|
14
|
+
else
|
15
|
+
allow!
|
16
|
+
end
|
17
|
+
|
18
|
+
permission_action
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def resource
|
24
|
+
@resource ||= context.fetch(:resource, nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,16 +3,12 @@
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
5
|
class Permissions < Decidim::DefaultPermissions
|
6
|
-
SUBJECTS_WHITELIST = [:delegation, :ponderation, :participant, :setting, :consultation].freeze
|
7
|
-
|
8
6
|
def permissions
|
9
|
-
|
7
|
+
return permission_action unless user
|
10
8
|
|
11
|
-
return permission_action
|
12
|
-
return permission_action unless permission_action.scope == :admin
|
13
|
-
return permission_action unless action_delegator_subject?
|
9
|
+
return Decidim::ActionDelegator::Admin::Permissions.new(user, permission_action, context).permissions if permission_action.scope == :admin
|
14
10
|
|
15
|
-
|
11
|
+
allowed_delegation_action?
|
16
12
|
|
17
13
|
permission_action
|
18
14
|
end
|
@@ -20,9 +16,9 @@ module Decidim
|
|
20
16
|
private
|
21
17
|
|
22
18
|
def allowed_delegation_action?
|
23
|
-
return unless delegation
|
19
|
+
return false unless delegation
|
24
20
|
# Check that the required question verifications are fulfilled
|
25
|
-
return unless authorized?(:vote, delegation.grantee)
|
21
|
+
return false unless authorized?(:vote, delegation.grantee)
|
26
22
|
|
27
23
|
case permission_action.action
|
28
24
|
when :vote_delegation
|
@@ -33,7 +29,7 @@ module Decidim
|
|
33
29
|
end
|
34
30
|
|
35
31
|
def authorized?(permission_action, user, resource: nil)
|
36
|
-
return unless resource || question
|
32
|
+
return false unless resource || question
|
37
33
|
|
38
34
|
ActionAuthorizer.new(user, permission_action, question, resource).authorize.ok?
|
39
35
|
end
|
@@ -50,26 +46,6 @@ module Decidim
|
|
50
46
|
@ponderation ||= context.fetch(:ponderation, nil)
|
51
47
|
end
|
52
48
|
|
53
|
-
def consultation_results_exports_action?
|
54
|
-
permission_action.subject == :consultation && permission_action.action == :export_results
|
55
|
-
end
|
56
|
-
|
57
|
-
def consultation
|
58
|
-
@consultation ||= context.fetch(:consultation)
|
59
|
-
end
|
60
|
-
|
61
|
-
def action_delegator_subject?
|
62
|
-
SUBJECTS_WHITELIST.include?(permission_action.subject)
|
63
|
-
end
|
64
|
-
|
65
|
-
def can_perform_action?
|
66
|
-
if permission_action.action == :destroy
|
67
|
-
resource.present?
|
68
|
-
else
|
69
|
-
true
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
49
|
def resource
|
74
50
|
@resource ||= context.fetch(:resource, nil)
|
75
51
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class SettingPresenter < SimpleDelegator
|
6
|
+
include Decidim::TranslationsHelper
|
7
|
+
|
8
|
+
def initialize(setting)
|
9
|
+
@setting = setting
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def translated_title
|
15
|
+
translated_attribute(__getobj__.title).html_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
def translated_description
|
19
|
+
ActionView::Base.full_sanitizer.sanitize(translated_attribute(__getobj__.description)).html_safe
|
20
|
+
end
|
21
|
+
|
22
|
+
def translated_resources_list
|
23
|
+
Decidim::ActionDelegator::AuthorizedResources.new(setting: self).query.map(&:presenter)
|
24
|
+
end
|
25
|
+
|
26
|
+
def path_for(resource)
|
27
|
+
return nil unless resource
|
28
|
+
|
29
|
+
Decidim::ResourceLocatorPresenter.new(resource).path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
# Query to find eligible users for corporate governance census elections
|
6
|
+
class ActionDelegatorCensusUsers < Decidim::Query
|
7
|
+
def initialize(election)
|
8
|
+
@election = election
|
9
|
+
@setting_id = election.census_settings["setting_id"]
|
10
|
+
@authorization_handlers = election.census_settings["authorization_handlers"]&.keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def query
|
14
|
+
return Decidim::User.none unless @setting_id
|
15
|
+
|
16
|
+
setting = Decidim::ActionDelegator::Setting.find_by(id: @setting_id)
|
17
|
+
return Decidim::User.none unless setting
|
18
|
+
|
19
|
+
if @authorization_handlers.present?
|
20
|
+
authorized_participants_and_delegates(setting)
|
21
|
+
else
|
22
|
+
all_confirmed_users
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :election
|
29
|
+
|
30
|
+
def authorized_participants_and_delegates(setting)
|
31
|
+
participant_ids = setting.participants.pluck(:decidim_user_id)
|
32
|
+
delegation_ids = setting.delegations.joins(:grantee).pluck("decidim_action_delegator_delegations.grantee_id")
|
33
|
+
|
34
|
+
eligible_user_ids = (participant_ids + delegation_ids).compact.uniq
|
35
|
+
return @election.organization.users.none if eligible_user_ids.empty?
|
36
|
+
|
37
|
+
base_scope = @election.organization.users.where(id: eligible_user_ids)
|
38
|
+
|
39
|
+
Decidim::AuthorizedUsers.new(
|
40
|
+
organization: @election.organization,
|
41
|
+
handlers: @authorization_handlers,
|
42
|
+
strict: true
|
43
|
+
).query.where(id: base_scope.select(:id))
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_confirmed_users
|
47
|
+
@election.organization.users.not_deleted.not_blocked.confirmed
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class AuthorizedResources < Decidim::Query
|
6
|
+
# Finds all the resources that have configured the action delegator verifier
|
7
|
+
# Note that currently only returns elections (Decidim::Election)
|
8
|
+
# In the future components could be returned here as well
|
9
|
+
def initialize(setting:)
|
10
|
+
@setting = setting
|
11
|
+
end
|
12
|
+
|
13
|
+
def query
|
14
|
+
condition = [
|
15
|
+
"(census_manifest = 'internal_users' AND census_settings -> 'authorization_handlers' -> 'delegations_verifier' -> 'options' -> 'setting' ? :setting_id) OR " \
|
16
|
+
"(census_manifest = 'action_delegator_census' AND census_settings ->> 'setting_id' = :setting_id)",
|
17
|
+
{ setting_id: @setting.id.to_s }
|
18
|
+
]
|
19
|
+
|
20
|
+
Decidim::Elections::Election.where(component: components).where(condition)
|
21
|
+
end
|
22
|
+
|
23
|
+
def components
|
24
|
+
Decidim::PublicComponents.for(@setting.organization, manifest_name: "elections")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class ElectionSettings < Decidim::Query
|
6
|
+
# Finds all the settings for the given election that have the action delegator verifier configured
|
7
|
+
def initialize(election)
|
8
|
+
@election = election
|
9
|
+
end
|
10
|
+
|
11
|
+
def query
|
12
|
+
setting_id = case @election.census_manifest
|
13
|
+
when "action_delegator_census"
|
14
|
+
@election.census_settings["setting_id"]
|
15
|
+
when "internal_users"
|
16
|
+
@election.census_settings.dig("authorization_handlers", "delegations_verifier", "options", "setting")
|
17
|
+
end
|
18
|
+
|
19
|
+
setting_id.present? ? Decidim::ActionDelegator::Setting.where(id: setting_id) : Decidim::ActionDelegator::Setting.none
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -2,24 +2,24 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
|
-
# Returns all PaperTrail versions of a
|
5
|
+
# Returns all PaperTrail versions of a election's delegated votes for auditing purposes.
|
6
6
|
# It is intended to be used to easily fetch this data when a judge ask us so.
|
7
|
-
class
|
8
|
-
def initialize(
|
9
|
-
@
|
7
|
+
class ElectionsDelegatedVotesVersions
|
8
|
+
def initialize(election)
|
9
|
+
@election = election
|
10
|
+
@settings = ElectionSettings.new(election).query
|
10
11
|
end
|
11
12
|
|
12
13
|
def query
|
13
14
|
PaperTrail::Version
|
14
15
|
.joins("INNER JOIN decidim_action_delegator_delegations ON decidim_action_delegator_delegations.id = versions.decidim_action_delegator_delegation_id")
|
15
16
|
.joins("INNER JOIN decidim_action_delegator_settings ON decidim_action_delegator_settings.id = decidim_action_delegator_delegations.decidim_action_delegator_setting_id")
|
16
|
-
.where(
|
17
|
+
.where(item_type: "Decidim::Elections::Vote")
|
18
|
+
.where(decidim_action_delegator_settings: { id: @settings.select(:id) })
|
17
19
|
.order("versions.created_at ASC")
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
attr_reader :consultation
|
22
|
+
attr_reader :election, :settings
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class ElectionsQuestionResponsesByType
|
6
|
+
# Add your query logic here
|
7
|
+
|
8
|
+
def initialize(question, settings)
|
9
|
+
@question = question
|
10
|
+
@settings = settings
|
11
|
+
end
|
12
|
+
|
13
|
+
def query
|
14
|
+
return Decidim::Elections::ResponseOption.none if question.blank? || settings.blank?
|
15
|
+
|
16
|
+
ElectionsVotesWithPonderations
|
17
|
+
.new(question.response_options, settings)
|
18
|
+
.query
|
19
|
+
.select("#{Decidim::Elections::ResponseOption.table_name}.*,
|
20
|
+
COALESCE(decidim_action_delegator_ponderation_id, 0) AS ponderation_id,
|
21
|
+
COALESCE(CAST(decidim_action_delegator_ponderations.weight AS FLOAT), 1.0) AS ponderation_weight,
|
22
|
+
COUNT(decidim_elections_votes.id) AS votes_total")
|
23
|
+
.group("#{Decidim::Elections::ResponseOption.table_name}.id,
|
24
|
+
COALESCE(decidim_action_delegator_ponderation_id, 0),
|
25
|
+
COALESCE(CAST(decidim_action_delegator_ponderations.weight AS FLOAT), 1.0)")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :question, :settings
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class ElectionsQuestionWeightedResponses
|
6
|
+
def initialize(question, settings)
|
7
|
+
@question = question
|
8
|
+
@settings = settings
|
9
|
+
end
|
10
|
+
|
11
|
+
def query
|
12
|
+
return Decidim::Elections::ResponseOption.none if question.blank? || settings.blank?
|
13
|
+
|
14
|
+
subquery = ElectionsQuestionResponsesByType.new(question, settings).query
|
15
|
+
Decidim::Elections::ResponseOption.unscoped
|
16
|
+
.joins("INNER JOIN (#{subquery.to_sql}) AS subquery ON subquery.id = #{Decidim::Elections::ResponseOption.table_name}.id")
|
17
|
+
.select(
|
18
|
+
"#{Decidim::Elections::ResponseOption.table_name}.*, SUM(subquery.votes_total * subquery.ponderation_weight) AS weighted_votes_total"
|
19
|
+
)
|
20
|
+
.group("#{Decidim::Elections::ResponseOption.table_name}.id")
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :question, :settings
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
class ElectionsVotesWithPonderations < Decidim::Query
|
6
|
+
def initialize(relation, settings)
|
7
|
+
@relation = relation
|
8
|
+
@settings = settings
|
9
|
+
|
10
|
+
@participants = Arel::Table.new("decidim_action_delegator_participants")
|
11
|
+
@ponderations = Arel::Table.new("decidim_action_delegator_ponderations")
|
12
|
+
@votes = Arel::Table.new("decidim_elections_votes")
|
13
|
+
@user_global_id_prefix = Decidim::User.new(id: 0).to_global_id.to_s.sub(%r{/0\z}, "/")
|
14
|
+
end
|
15
|
+
|
16
|
+
def query
|
17
|
+
return relation.none if settings.blank?
|
18
|
+
|
19
|
+
relation
|
20
|
+
.left_joins(:votes)
|
21
|
+
.joins(participants_join)
|
22
|
+
.joins(ponderations_join)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :relation, :settings, :participants, :ponderations, :votes, :user_global_id_prefix
|
28
|
+
|
29
|
+
# LEFT OUTER JOIN "decidim_action_delegator_participants"
|
30
|
+
# ON "decidim_action_delegator_participants"."decidim_action_delegator_setting_id" IN (1, 2, 3)
|
31
|
+
# AND CONCAT(#{user_global_id_prefix}, "decidim_action_delegator_participants"."decidim_user_id") = "decidim_election_votes"."voter_uid"
|
32
|
+
# LEFT OUTER JOIN "decidim_action_delegator_ponderations"
|
33
|
+
# ON "decidim_action_delegator_ponderations"."id" = "decidim_action_delegator_participants"."decidim_action_delegator_ponderation_id"
|
34
|
+
def participants_join
|
35
|
+
Arel::Nodes::OuterJoin.new(
|
36
|
+
participants,
|
37
|
+
Arel::Nodes::On.new(
|
38
|
+
participants[:decidim_action_delegator_setting_id].in(settings.pluck(:id))
|
39
|
+
.and(
|
40
|
+
Arel::Nodes::NamedFunction.new(
|
41
|
+
"CONCAT",
|
42
|
+
[
|
43
|
+
Arel::Nodes.build_quoted(user_global_id_prefix),
|
44
|
+
participants[:decidim_user_id]
|
45
|
+
]
|
46
|
+
).eq(votes[:voter_uid])
|
47
|
+
)
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ponderations_join
|
53
|
+
Arel::Nodes::OuterJoin.new(
|
54
|
+
ponderations,
|
55
|
+
Arel::Nodes::On.new(
|
56
|
+
ponderations[:id].eq(participants[:decidim_action_delegator_ponderation_id])
|
57
|
+
)
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -83,7 +83,7 @@ module Decidim
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def check_exists?(field, form)
|
86
|
-
@participant = Decidim::ActionDelegator::Participant.find_by(field => form.send(field), setting
|
86
|
+
@participant = Decidim::ActionDelegator::Participant.find_by(field => form.send(field), :setting => @current_setting) if form.send(field).present?
|
87
87
|
@participant.present?
|
88
88
|
end
|
89
89
|
|
@@ -30,11 +30,11 @@ module Decidim
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def sender
|
33
|
-
ENV
|
33
|
+
ENV.fetch("SMS_SENDER", nil)
|
34
34
|
end
|
35
35
|
|
36
36
|
def sms_gateway_provider
|
37
|
-
ENV
|
37
|
+
ENV.fetch("SMS_GATEWAY_PROVIDER", nil)
|
38
38
|
end
|
39
39
|
|
40
40
|
def sms_gateway_provider_valid?
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<div class="row column form__wrapper pb-4" id="corporate-governance-census-settings">
|
2
|
+
<%= form.select(:setting_id,
|
3
|
+
options_for_select(form.object.settings_for_select, form.object.setting_id),
|
4
|
+
{ label: t("setting_group", scope: "decidim.action_delegator.admin.census"), prompt: false },
|
5
|
+
{ id: "action_delegator_census_setting_id" }) %>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="row column font-semibold pb-4"><%= t("decidim.elections.censuses.internal_users.internal_type_title") %></div>
|
9
|
+
<div class="row column">
|
10
|
+
<div class="callout">
|
11
|
+
<%= t("decidim.elections.censuses.action_delegator_census.authorizer_handler_admin_callout_html") %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
</div>
|
15
|
+
<div class="mt-4 form__wrapper gap-1">
|
16
|
+
<%= form.collection_check_boxes :authorization_handlers_names,
|
17
|
+
@form.available_authorizations,
|
18
|
+
:name,
|
19
|
+
:description do |builder| %>
|
20
|
+
<div class="row">
|
21
|
+
<%= builder.label { builder.check_box + builder.text } %>
|
22
|
+
<div id="authorization-handler-<%= builder.value %>" class="authorization-handler my-4 ml-6" style="display: <%= form.object.authorization_handlers_names.include?(builder.value) ? "block" : "none" %>;">
|
23
|
+
<%= render "decidim/action_delegator/admin/censuses/action_delegator_census_options_form", form: form, handler_name: builder.value %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<script>
|
30
|
+
document.addEventListener("turbo:load", function() {
|
31
|
+
const checkboxes = document.querySelectorAll("input[type='checkbox'][id^='action_delegator_census_authorization_handlers_names_']");
|
32
|
+
checkboxes.forEach(checkbox => {
|
33
|
+
checkbox.addEventListener("change", function() {
|
34
|
+
const handlerId = `authorization-handler-${this.value}`;
|
35
|
+
const handlerElement = document.getElementById(handlerId);
|
36
|
+
if (this.checked) {
|
37
|
+
handlerElement.style.display = "block";
|
38
|
+
} else {
|
39
|
+
handlerElement.style.display = "none";
|
40
|
+
}
|
41
|
+
});
|
42
|
+
});
|
43
|
+
});
|
44
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<%
|
2
|
+
handler_name = local_assigns[:handler_name]
|
3
|
+
schema = form.object.options_schema(handler_name)
|
4
|
+
attributes = form.object.options_attributes(handler_name)
|
5
|
+
# this variable is send as a context in the settings_attribute_input helper
|
6
|
+
@component = current_component
|
7
|
+
%>
|
8
|
+
|
9
|
+
<%= form.fields_for(:authorization_handlers_options) do |options_form| %>
|
10
|
+
<%= options_form.fields_for(handler_name, schema) do |attribute_options_form| %>
|
11
|
+
<% attributes.each do |name, attribute| %>
|
12
|
+
<%# Skip the 'setting' field for delegations_verifier when using action_delegator_census %>
|
13
|
+
<%# because the setting is already selected at the census level %>
|
14
|
+
<% next if handler_name == "delegations_verifier" && name.to_s == "setting" %>
|
15
|
+
|
16
|
+
<%= settings_attribute_input(
|
17
|
+
attribute_options_form,
|
18
|
+
attribute,
|
19
|
+
name,
|
20
|
+
"decidim.authorization_handlers.#{handler_name}.fields",
|
21
|
+
tabs_prefix: "#{name}_tabs_prefix"
|
22
|
+
) %>
|
23
|
+
<% end %>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|