permission_forge 0.14.1
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 +7 -0
- data/CHANGELOG.md +1138 -0
- data/LICENSE.txt +21 -0
- data/README.md +356 -0
- data/app/controllers/concerns/permission_forge/acting_admin.rb +17 -0
- data/app/controllers/concerns/permission_forge/api/crud_actions.rb +84 -0
- data/app/controllers/concerns/permission_forge/api/nested_resource_lookup.rb +18 -0
- data/app/controllers/concerns/permission_forge/api/pagination.rb +50 -0
- data/app/controllers/concerns/permission_forge/api/response_renderer.rb +65 -0
- data/app/controllers/concerns/permission_forge/api/serializer_resolver.rb +30 -0
- data/app/controllers/concerns/permission_forge/api_authentication.rb +40 -0
- data/app/controllers/concerns/permission_forge/assignment_authorization.rb +97 -0
- data/app/controllers/concerns/permission_forge/audit_log_gate.rb +55 -0
- data/app/controllers/concerns/permission_forge/basic_authentication.rb +77 -0
- data/app/controllers/concerns/permission_forge/cache_confirmation.rb +32 -0
- data/app/controllers/concerns/permission_forge/forgery_protection.rb +29 -0
- data/app/controllers/concerns/permission_forge/html_format.rb +40 -0
- data/app/controllers/concerns/permission_forge/param_shapes.rb +87 -0
- data/app/controllers/concerns/permission_forge/tenant_required.rb +42 -0
- data/app/controllers/permission_forge/api/audit_events_controller.rb +33 -0
- data/app/controllers/permission_forge/api/base_controller.rb +220 -0
- data/app/controllers/permission_forge/api/dashboard_controller.rb +30 -0
- data/app/controllers/permission_forge/api/permission_set_group_users_controller.rb +51 -0
- data/app/controllers/permission_forge/api/permission_set_groups_controller.rb +71 -0
- data/app/controllers/permission_forge/api/permission_set_rules_controller.rb +37 -0
- data/app/controllers/permission_forge/api/permission_set_users_controller.rb +105 -0
- data/app/controllers/permission_forge/api/permission_sets_controller.rb +87 -0
- data/app/controllers/permission_forge/api/resource_actions_controller.rb +58 -0
- data/app/controllers/permission_forge/api/resource_fields_controller.rb +30 -0
- data/app/controllers/permission_forge/api/resources_controller.rb +56 -0
- data/app/controllers/permission_forge/application_controller.rb +198 -0
- data/app/controllers/permission_forge/assets_controller.rb +97 -0
- data/app/controllers/permission_forge/assignments_controller.rb +266 -0
- data/app/controllers/permission_forge/audit_events_controller.rb +94 -0
- data/app/controllers/permission_forge/bulk_assignments_controller.rb +108 -0
- data/app/controllers/permission_forge/field_lookups_controller.rb +38 -0
- data/app/controllers/permission_forge/home_controller.rb +24 -0
- data/app/controllers/permission_forge/maintenance_controller.rb +13 -0
- data/app/controllers/permission_forge/people_controller.rb +190 -0
- data/app/controllers/permission_forge/permission_set_editor.rb +71 -0
- data/app/controllers/permission_forge/permission_set_group_users_controller.rb +57 -0
- data/app/controllers/permission_forge/permission_set_groups_controller.rb +111 -0
- data/app/controllers/permission_forge/permission_set_rules_controller.rb +97 -0
- data/app/controllers/permission_forge/permission_sets_controller.rb +227 -0
- data/app/controllers/permission_forge/preferences_controller.rb +26 -0
- data/app/controllers/permission_forge/resource_actions_controller.rb +106 -0
- data/app/controllers/permission_forge/resource_fields_controller.rb +129 -0
- data/app/controllers/permission_forge/resources_controller.rb +189 -0
- data/app/controllers/permission_forge/settings_controller.rb +33 -0
- data/app/frontend/permission_forge/brand/apple-touch-icon.png +0 -0
- data/app/frontend/permission_forge/brand/chevron.svg +1 -0
- data/app/frontend/permission_forge/brand/favicon.ico +0 -0
- data/app/frontend/permission_forge/brand/favicon.svg +1 -0
- data/app/frontend/permission_forge/brand/logo-horizontal-dark.svg +1 -0
- data/app/frontend/permission_forge/brand/logo-horizontal-light.svg +1 -0
- data/app/frontend/permission_forge/brand/mark-small.svg +1 -0
- data/app/frontend/permission_forge/brand/mark.svg +1 -0
- data/app/frontend/permission_forge/pf_admin.css +766 -0
- data/app/frontend/permission_forge/pf_admin.js +482 -0
- data/app/helpers/permission_forge/application_helper.rb +167 -0
- data/app/helpers/permission_forge/audit_helper.rb +230 -0
- data/app/helpers/permission_forge/audit_sentences_helper.rb +84 -0
- data/app/helpers/permission_forge/badges_helper.rb +134 -0
- data/app/helpers/permission_forge/changes_helper.rb +117 -0
- data/app/helpers/permission_forge/people_helper.rb +118 -0
- data/app/helpers/permission_forge/sentences_helper.rb +175 -0
- data/app/helpers/permission_forge/settings_helper.rb +152 -0
- data/app/helpers/permission_forge/ui_helper.rb +184 -0
- data/app/models/permission_forge/audit_event.rb +159 -0
- data/app/models/permission_forge/permission_set.rb +100 -0
- data/app/models/permission_forge/permission_set_group.rb +83 -0
- data/app/models/permission_forge/permission_set_group_user.rb +43 -0
- data/app/models/permission_forge/permission_set_rule.rb +99 -0
- data/app/models/permission_forge/permission_set_user.rb +158 -0
- data/app/models/permission_forge/resource.rb +100 -0
- data/app/models/permission_forge/resource_action.rb +58 -0
- data/app/models/permission_forge/resource_field.rb +69 -0
- data/app/views/layouts/permission_forge/application.html.erb +30 -0
- data/app/views/layouts/permission_forge/fragment.html.erb +2 -0
- data/app/views/permission_forge/assignments/_person_options.html.erb +26 -0
- data/app/views/permission_forge/assignments/confirm_destroy.html.erb +24 -0
- data/app/views/permission_forge/assignments/new.html.erb +99 -0
- data/app/views/permission_forge/audit_events/index.html.erb +161 -0
- data/app/views/permission_forge/audit_events/show.html.erb +146 -0
- data/app/views/permission_forge/bulk_assignments/confirm_revoke.html.erb +21 -0
- data/app/views/permission_forge/bulk_assignments/new.html.erb +69 -0
- data/app/views/permission_forge/field_lookups/show.html.erb +32 -0
- data/app/views/permission_forge/home/_audit_card.html.erb +19 -0
- data/app/views/permission_forge/home/_ending_card.html.erb +32 -0
- data/app/views/permission_forge/home/_expired_card.html.erb +34 -0
- data/app/views/permission_forge/home/_privacy_card.html.erb +67 -0
- data/app/views/permission_forge/home/_rules_card.html.erb +23 -0
- data/app/views/permission_forge/home/_stale_card.html.erb +39 -0
- data/app/views/permission_forge/home/_tidy_card.html.erb +18 -0
- data/app/views/permission_forge/home/index.html.erb +69 -0
- data/app/views/permission_forge/people/_access_tab.html.erb +58 -0
- data/app/views/permission_forge/people/_audit_tab.html.erb +93 -0
- data/app/views/permission_forge/people/_effective_field_source.html.erb +11 -0
- data/app/views/permission_forge/people/_effective_tab.html.erb +118 -0
- data/app/views/permission_forge/people/_effective_why.html.erb +48 -0
- data/app/views/permission_forge/people/_why_item.html.erb +19 -0
- data/app/views/permission_forge/people/confirm_remove_all.html.erb +27 -0
- data/app/views/permission_forge/people/consent_preview.html.erb +39 -0
- data/app/views/permission_forge/people/erasure_preview.html.erb +28 -0
- data/app/views/permission_forge/people/index.html.erb +78 -0
- data/app/views/permission_forge/people/show.html.erb +54 -0
- data/app/views/permission_forge/permission_set_groups/confirm_destroy.html.erb +12 -0
- data/app/views/permission_forge/permission_set_groups/index.html.erb +55 -0
- data/app/views/permission_forge/permission_set_groups/show.html.erb +100 -0
- data/app/views/permission_forge/permission_set_rules/_builder.html.erb +145 -0
- data/app/views/permission_forge/permission_set_rules/_value_input.html.erb +42 -0
- data/app/views/permission_forge/permission_set_rules/confirm_destroy.html.erb +13 -0
- data/app/views/permission_forge/permission_set_rules/edit.html.erb +3 -0
- data/app/views/permission_forge/permission_set_rules/new.html.erb +3 -0
- data/app/views/permission_forge/permission_sets/_assignments_tab.html.erb +74 -0
- data/app/views/permission_forge/permission_sets/_audit_tab.html.erb +80 -0
- data/app/views/permission_forge/permission_sets/_conditions_tab.html.erb +83 -0
- data/app/views/permission_forge/permission_sets/_header.html.erb +36 -0
- data/app/views/permission_forge/permission_sets/_records_summary.html.erb +16 -0
- data/app/views/permission_forge/permission_sets/_resource_summary.html.erb +11 -0
- data/app/views/permission_forge/permission_sets/_resources_tab.html.erb +49 -0
- data/app/views/permission_forge/permission_sets/confirm_destroy.html.erb +23 -0
- data/app/views/permission_forge/permission_sets/confirm_switch_mode.html.erb +15 -0
- data/app/views/permission_forge/permission_sets/index.html.erb +83 -0
- data/app/views/permission_forge/permission_sets/new.html.erb +63 -0
- data/app/views/permission_forge/permission_sets/review.html.erb +26 -0
- data/app/views/permission_forge/permission_sets/show.html.erb +50 -0
- data/app/views/permission_forge/resource_actions/_across_sets.html.erb +24 -0
- data/app/views/permission_forge/resource_actions/_add_select.html.erb +12 -0
- data/app/views/permission_forge/resource_actions/_effect.html.erb +25 -0
- data/app/views/permission_forge/resource_actions/_endpoints.html.erb +16 -0
- data/app/views/permission_forge/resource_actions/_form.html.erb +44 -0
- data/app/views/permission_forge/resource_actions/_managed_note.html.erb +11 -0
- data/app/views/permission_forge/resource_actions/confirm_destroy.html.erb +13 -0
- data/app/views/permission_forge/resource_actions/new.html.erb +31 -0
- data/app/views/permission_forge/resource_actions/show.html.erb +39 -0
- data/app/views/permission_forge/resource_fields/_explain.html.erb +24 -0
- data/app/views/permission_forge/resource_fields/show.html.erb +186 -0
- data/app/views/permission_forge/resources/_actions_tab.html.erb +88 -0
- data/app/views/permission_forge/resources/_fields_tab.html.erb +89 -0
- data/app/views/permission_forge/resources/_rules_tab.html.erb +55 -0
- data/app/views/permission_forge/resources/_sync.html.erb +57 -0
- data/app/views/permission_forge/resources/confirm_destroy.html.erb +17 -0
- data/app/views/permission_forge/resources/new.html.erb +57 -0
- data/app/views/permission_forge/resources/show.html.erb +117 -0
- data/app/views/permission_forge/settings/_ladder.html.erb +15 -0
- data/app/views/permission_forge/settings/_row.html.erb +38 -0
- data/app/views/permission_forge/settings/show.html.erb +67 -0
- data/app/views/permission_forge/shared/_across_sets.html.erb +31 -0
- data/app/views/permission_forge/shared/_audit_events.html.erb +36 -0
- data/app/views/permission_forge/shared/_audit_footer.html.erb +16 -0
- data/app/views/permission_forge/shared/_audit_off_note.html.erb +15 -0
- data/app/views/permission_forge/shared/_audit_pagination.html.erb +19 -0
- data/app/views/permission_forge/shared/_audit_refused_note.html.erb +7 -0
- data/app/views/permission_forge/shared/_audit_timeline.html.erb +29 -0
- data/app/views/permission_forge/shared/_crud_cell.html.erb +18 -0
- data/app/views/permission_forge/shared/_dialog_frame.html.erb +11 -0
- data/app/views/permission_forge/shared/_expiry_picker.html.erb +27 -0
- data/app/views/permission_forge/shared/_head_extra.html.erb +3 -0
- data/app/views/permission_forge/shared/_help_banner.html.erb +10 -0
- data/app/views/permission_forge/shared/_level_control.html.erb +18 -0
- data/app/views/permission_forge/shared/_mode_banners.html.erb +22 -0
- data/app/views/permission_forge/shared/_nav.html.erb +12 -0
- data/app/views/permission_forge/shared/_preferences.html.erb +24 -0
- data/app/views/permission_forge/shared/_reason_field.html.erb +20 -0
- data/app/views/permission_forge/shared/_savebar.html.erb +13 -0
- data/app/views/permission_forge/shared/_sheet_frame.html.erb +22 -0
- data/app/views/permission_forge/shared/_sidebar.html.erb +18 -0
- data/app/views/permission_forge/shared/_tech_details.html.erb +20 -0
- data/app/views/permission_forge/shared/_toasts.html.erb +27 -0
- data/app/views/permission_forge/shared/_topbar.html.erb +15 -0
- data/app/views/permission_forge/shared/_user_select.html.erb +16 -0
- data/app/views/permission_forge/shared/audit_refused.html.erb +7 -0
- data/config/locales/permission_forge.en.yml +2115 -0
- data/config/locales/permission_forge.mk.yml +2166 -0
- data/config/locales/permission_forge.sq.yml +2164 -0
- data/config/routes.rb +143 -0
- data/lib/generators/permission_forge/install/install_generator.rb +105 -0
- data/lib/generators/permission_forge/install/templates/initializer.rb.tt +148 -0
- data/lib/generators/permission_forge/install/templates/migration.rb.tt +198 -0
- data/lib/generators/permission_forge/rls/rls_generator.rb +57 -0
- data/lib/generators/permission_forge/rls/templates/enable_permission_forge_rls.rb.tt +15 -0
- data/lib/generators/permission_forge/upgrade/templates/add_admin_ui_columns.rb.tt +8 -0
- data/lib/generators/permission_forge/upgrade/templates/add_audit_events.rb.tt +98 -0
- data/lib/generators/permission_forge/upgrade/templates/add_resource_fields_unique_index.rb.tt +69 -0
- data/lib/generators/permission_forge/upgrade/upgrade_generator.rb +66 -0
- data/lib/permission_forge/admin/action_across_sets.rb +76 -0
- data/lib/permission_forge/admin/action_rows.rb +156 -0
- data/lib/permission_forge/admin/attention_items.rb +207 -0
- data/lib/permission_forge/admin/audit_event_detail.rb +75 -0
- data/lib/permission_forge/admin/audit_log.rb +193 -0
- data/lib/permission_forge/admin/audit_log_days.rb +41 -0
- data/lib/permission_forge/admin/audit_lookups.rb +105 -0
- data/lib/permission_forge/admin/audit_name_history.rb +85 -0
- data/lib/permission_forge/admin/audit_trail.rb +169 -0
- data/lib/permission_forge/admin/bulk_people_matcher.rb +109 -0
- data/lib/permission_forge/admin/change_set.rb +123 -0
- data/lib/permission_forge/admin/change_set_parts/actions.rb +27 -0
- data/lib/permission_forge/admin/change_set_parts/base.rb +58 -0
- data/lib/permission_forge/admin/change_set_parts/context.rb +50 -0
- data/lib/permission_forge/admin/change_set_parts/field_edit.rb +67 -0
- data/lib/permission_forge/admin/change_set_parts/field_levels.rb +71 -0
- data/lib/permission_forge/admin/change_set_parts/new_actions.rb +91 -0
- data/lib/permission_forge/admin/change_set_parts/new_fields.rb +63 -0
- data/lib/permission_forge/admin/change_set_parts/resource_edit.rb +55 -0
- data/lib/permission_forge/admin/change_set_parts/resource_flags.rb +19 -0
- data/lib/permission_forge/admin/change_set_parts/set_attrs.rb +45 -0
- data/lib/permission_forge/admin/change_set_parts/values.rb +20 -0
- data/lib/permission_forge/admin/column_fields.rb +48 -0
- data/lib/permission_forge/admin/conditions_form.rb +78 -0
- data/lib/permission_forge/admin/controller_catalog.rb +166 -0
- data/lib/permission_forge/admin/csv.rb +41 -0
- data/lib/permission_forge/admin/editable_lock.rb +157 -0
- data/lib/permission_forge/admin/effective_access.rb +158 -0
- data/lib/permission_forge/admin/effective_fields.rb +176 -0
- data/lib/permission_forge/admin/expiry_presets.rb +51 -0
- data/lib/permission_forge/admin/exports/document.rb +89 -0
- data/lib/permission_forge/admin/exports/events.rb +51 -0
- data/lib/permission_forge/admin/exports/holdings.rb +118 -0
- data/lib/permission_forge/admin/exports.rb +37 -0
- data/lib/permission_forge/admin/field_across_sets.rb +51 -0
- data/lib/permission_forge/admin/field_detail.rb +112 -0
- data/lib/permission_forge/admin/field_level.rb +74 -0
- data/lib/permission_forge/admin/field_rows.rb +137 -0
- data/lib/permission_forge/admin/last_changes.rb +94 -0
- data/lib/permission_forge/admin/model_catalog.rb +216 -0
- data/lib/permission_forge/admin/people_candidates.rb +108 -0
- data/lib/permission_forge/admin/people_directory.rb +158 -0
- data/lib/permission_forge/admin/permission_set_factory.rb +48 -0
- data/lib/permission_forge/admin/person_label.rb +95 -0
- data/lib/permission_forge/admin/record_id.rb +19 -0
- data/lib/permission_forge/admin/rule_builder.rb +58 -0
- data/lib/permission_forge/admin/rule_form.rb +175 -0
- data/lib/permission_forge/admin/rule_form_input.rb +74 -0
- data/lib/permission_forge/admin/sensitive_columns.rb +68 -0
- data/lib/permission_forge/admin/settings_catalog.rb +261 -0
- data/lib/permission_forge/admin/settings_values.rb +130 -0
- data/lib/permission_forge/api/serializers/audit_event_serializer.rb +27 -0
- data/lib/permission_forge/api/serializers/base_serializer.rb +36 -0
- data/lib/permission_forge/api/serializers/permission_set_group_serializer.rb +12 -0
- data/lib/permission_forge/api/serializers/permission_set_group_user_serializer.rb +11 -0
- data/lib/permission_forge/api/serializers/permission_set_rule_serializer.rb +25 -0
- data/lib/permission_forge/api/serializers/permission_set_serializer.rb +13 -0
- data/lib/permission_forge/api/serializers/permission_set_user_serializer.rb +11 -0
- data/lib/permission_forge/api/serializers/resource_action_serializer.rb +13 -0
- data/lib/permission_forge/api/serializers/resource_field_serializer.rb +14 -0
- data/lib/permission_forge/api/serializers/resource_serializer.rb +15 -0
- data/lib/permission_forge/audit/actor.rb +144 -0
- data/lib/permission_forge/audit/actor_resolver.rb +103 -0
- data/lib/permission_forge/audit/audited_views.rb +208 -0
- data/lib/permission_forge/audit/boot_warnings.rb +58 -0
- data/lib/permission_forge/audit/column_values.rb +56 -0
- data/lib/permission_forge/audit/context.rb +98 -0
- data/lib/permission_forge/audit/detached.rb +62 -0
- data/lib/permission_forge/audit/event_catalog.rb +110 -0
- data/lib/permission_forge/audit/event_query.rb +192 -0
- data/lib/permission_forge/audit/failure.rb +47 -0
- data/lib/permission_forge/audit/field_access.rb +186 -0
- data/lib/permission_forge/audit/gate.rb +167 -0
- data/lib/permission_forge/audit/job_context.rb +32 -0
- data/lib/permission_forge/audit/log_access.rb +36 -0
- data/lib/permission_forge/audit/recorder.rb +168 -0
- data/lib/permission_forge/audit/refusals.rb +61 -0
- data/lib/permission_forge/audit/request_context.rb +152 -0
- data/lib/permission_forge/audit/runtime.rb +178 -0
- data/lib/permission_forge/audit/runtime_emission.rb +135 -0
- data/lib/permission_forge/audit/settings_observer.rb +70 -0
- data/lib/permission_forge/audit/source.rb +54 -0
- data/lib/permission_forge/audit/subscribers/base.rb +69 -0
- data/lib/permission_forge/audit/subscribers/legacy_hooks.rb +97 -0
- data/lib/permission_forge/audit/subscribers/log.rb +45 -0
- data/lib/permission_forge/audit/subscribers.rb +58 -0
- data/lib/permission_forge/audit.rb +302 -0
- data/lib/permission_forge/boot_checks.rb +184 -0
- data/lib/permission_forge/cache/backlog.rb +142 -0
- data/lib/permission_forge/cache/base.rb +200 -0
- data/lib/permission_forge/cache/bulk_invalidation.rb +131 -0
- data/lib/permission_forge/cache/failure.rb +171 -0
- data/lib/permission_forge/cache/handled_errors.rb +82 -0
- data/lib/permission_forge/cache/holder_invalidation.rb +85 -0
- data/lib/permission_forge/cache/invalidation.rb +180 -0
- data/lib/permission_forge/cache/memory_cache.rb +77 -0
- data/lib/permission_forge/cache/null_cache.rb +40 -0
- data/lib/permission_forge/cache/rails_cache.rb +231 -0
- data/lib/permission_forge/cache/rebuild.rb +149 -0
- data/lib/permission_forge/cache/settlement.rb +63 -0
- data/lib/permission_forge/cache/user_keys.rb +206 -0
- data/lib/permission_forge/cache/variant_removal.rb +55 -0
- data/lib/permission_forge/cache/withheld_notice.rb +60 -0
- data/lib/permission_forge/concerns/audited_collection.rb +34 -0
- data/lib/permission_forge/concerns/auto_permission_scoping.rb +158 -0
- data/lib/permission_forge/concerns/controller_action_guard.rb +106 -0
- data/lib/permission_forge/concerns/field_mask_checker.rb +45 -0
- data/lib/permission_forge/concerns/has_active_scope.rb +12 -0
- data/lib/permission_forge/concerns/has_audit_trail.rb +59 -0
- data/lib/permission_forge/concerns/has_permission_set_users.rb +115 -0
- data/lib/permission_forge/concerns/has_tenant_scoping.rb +86 -0
- data/lib/permission_forge/concerns/has_uuid.rb +24 -0
- data/lib/permission_forge/concerns/invalidates_in_batch.rb +207 -0
- data/lib/permission_forge/concerns/invalidates_managed_actions.rb +80 -0
- data/lib/permission_forge/concerns/masked_attributes/alias_access.rb +280 -0
- data/lib/permission_forge/concerns/masked_attributes/store_access.rb +100 -0
- data/lib/permission_forge/concerns/masked_attributes/validation_reads.rb +153 -0
- data/lib/permission_forge/concerns/masked_attributes/virtual_readers.rb +242 -0
- data/lib/permission_forge/concerns/masked_attributes.rb +576 -0
- data/lib/permission_forge/concerns/permission_cache_accessor.rb +416 -0
- data/lib/permission_forge/concerns/permission_scoped_findable.rb +337 -0
- data/lib/permission_forge/concerns/permissioned_user.rb +78 -0
- data/lib/permission_forge/concerns/permissions_memo.rb +160 -0
- data/lib/permission_forge/concerns/permissions_per_context.rb +288 -0
- data/lib/permission_forge/concerns/permissions_regenerator.rb +43 -0
- data/lib/permission_forge/concerns/records_audit_events.rb +286 -0
- data/lib/permission_forge/concerns/resource_permission_checker.rb +116 -0
- data/lib/permission_forge/concerns/validates_column_lengths.rb +69 -0
- data/lib/permission_forge/concerns/validates_dependency.rb +54 -0
- data/lib/permission_forge/conditional_sets.rb +361 -0
- data/lib/permission_forge/configuration/admin_ui_settings.rb +108 -0
- data/lib/permission_forge/configuration/audit_settings.rb +184 -0
- data/lib/permission_forge/configuration/cache_settings.rb +76 -0
- data/lib/permission_forge/configuration/controller_action_settings.rb +112 -0
- data/lib/permission_forge/configuration/gdpr_settings.rb +70 -0
- data/lib/permission_forge/configuration/masked_attributes_settings.rb +83 -0
- data/lib/permission_forge/configuration/permits_attributes_settings.rb +26 -0
- data/lib/permission_forge/configuration/presets.rb +139 -0
- data/lib/permission_forge/configuration/query_scoping_settings.rb +107 -0
- data/lib/permission_forge/configuration/strategy_values.rb +57 -0
- data/lib/permission_forge/configuration/virtual_attributes_settings.rb +65 -0
- data/lib/permission_forge/configuration.rb +624 -0
- data/lib/permission_forge/controller_actions/authorizer.rb +171 -0
- data/lib/permission_forge/controller_actions/managed_index.rb +213 -0
- data/lib/permission_forge/controller_actions/mapping.rb +167 -0
- data/lib/permission_forge/controller_actions.rb +122 -0
- data/lib/permission_forge/deprecation.rb +86 -0
- data/lib/permission_forge/engine.rb +113 -0
- data/lib/permission_forge/execution_state.rb +318 -0
- data/lib/permission_forge/invalidation_batch.rb +206 -0
- data/lib/permission_forge/masked_models.rb +226 -0
- data/lib/permission_forge/masking_sync.rb +271 -0
- data/lib/permission_forge/model_bindings.rb +90 -0
- data/lib/permission_forge/open_transaction.rb +303 -0
- data/lib/permission_forge/operators/constants.rb +41 -0
- data/lib/permission_forge/operators/registry.rb +300 -0
- data/lib/permission_forge/operators/rule_column.rb +120 -0
- data/lib/permission_forge/operators/validations.rb +111 -0
- data/lib/permission_forge/operators/value_casters/number.rb +68 -0
- data/lib/permission_forge/operators/value_casters/temporal.rb +69 -0
- data/lib/permission_forge/operators/value_casters.rb +80 -0
- data/lib/permission_forge/operators/value_typing.rb +115 -0
- data/lib/permission_forge/query_scoping_warnings.rb +68 -0
- data/lib/permission_forge/rake_audit.rb +65 -0
- data/lib/permission_forge/resource_chain.rb +190 -0
- data/lib/permission_forge/rls.rb +209 -0
- data/lib/permission_forge/services/attribute_access_guard.rb +138 -0
- data/lib/permission_forge/services/audit_event_purger.rb +150 -0
- data/lib/permission_forge/services/bulk_permission_set_assignment.rb +108 -0
- data/lib/permission_forge/services/concerns/across_tenants.rb +69 -0
- data/lib/permission_forge/services/concerns/audited_operation.rb +43 -0
- data/lib/permission_forge/services/concerns/clonable.rb +29 -0
- data/lib/permission_forge/services/consent_withdrawal_handler.rb +135 -0
- data/lib/permission_forge/services/dashboard_stats.rb +43 -0
- data/lib/permission_forge/services/data_subject_erasure.rb +306 -0
- data/lib/permission_forge/services/expired_assignment_cleaner.rb +152 -0
- data/lib/permission_forge/services/extra_field_defaults.rb +71 -0
- data/lib/permission_forge/services/field_permission_merger.rb +83 -0
- data/lib/permission_forge/services/filters_response_attributes.rb +144 -0
- data/lib/permission_forge/services/gdpr_field_auditor/text_report.rb +63 -0
- data/lib/permission_forge/services/gdpr_field_auditor.rb +223 -0
- data/lib/permission_forge/services/host_key_guard.rb +110 -0
- data/lib/permission_forge/services/mask_value.rb +93 -0
- data/lib/permission_forge/services/permission_cache_clearer.rb +174 -0
- data/lib/permission_forge/services/permission_hash_builder.rb +305 -0
- data/lib/permission_forge/services/permission_rule_applier.rb +192 -0
- data/lib/permission_forge/services/permission_set_cloner.rb +89 -0
- data/lib/permission_forge/services/permission_set_condition_evaluator.rb +102 -0
- data/lib/permission_forge/services/permission_set_group_cloner.rb +52 -0
- data/lib/permission_forge/services/permits_attributes/nested_records.rb +106 -0
- data/lib/permission_forge/services/permits_attributes.rb +270 -0
- data/lib/permission_forge/services/resource_field_generator.rb +93 -0
- data/lib/permission_forge/services/resource_with_fields_builder.rb +47 -0
- data/lib/permission_forge/services/rls_operator_sql.rb +131 -0
- data/lib/permission_forge/services/rls_policy_generator.rb +365 -0
- data/lib/permission_forge/services/security_audit/action_check.rb +157 -0
- data/lib/permission_forge/services/security_audit/assignment_check.rb +120 -0
- data/lib/permission_forge/services/security_audit/audit_trail_check.rb +166 -0
- data/lib/permission_forge/services/security_audit/base_check.rb +57 -0
- data/lib/permission_forge/services/security_audit/config_check.rb +212 -0
- data/lib/permission_forge/services/security_audit/integrity_check.rb +105 -0
- data/lib/permission_forge/services/security_audit/personal_data_check.rb +79 -0
- data/lib/permission_forge/services/security_audit/privilege_check.rb +67 -0
- data/lib/permission_forge/services/security_audit/rule_check.rb +141 -0
- data/lib/permission_forge/services/security_audit/text_report.rb +46 -0
- data/lib/permission_forge/services/security_audit/user_model_check.rb +67 -0
- data/lib/permission_forge/services/security_audit.rb +75 -0
- data/lib/permission_forge/services/stale_assignment_auditor.rb +68 -0
- data/lib/permission_forge/services/unscoped_query_detector.rb +132 -0
- data/lib/permission_forge/version.rb +3 -0
- data/lib/permission_forge/virtual_fields/discovery.rb +65 -0
- data/lib/permission_forge/virtual_fields.rb +102 -0
- data/lib/permission_forge.rb +464 -0
- data/lib/tasks/permission_forge.rake +31 -0
- data/lib/tasks/permission_forge_audit_events.rake +28 -0
- data/lib/tasks/permission_forge_cache.rake +26 -0
- data/lib/tasks/permission_forge_expiration.rake +30 -0
- data/lib/tasks/permission_forge_gdpr.rake +69 -0
- data/lib/tasks/permission_forge_rls.rake +38 -0
- metadata +515 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<% set = @permission_set %>
|
|
2
|
+
<% resource = @resource %>
|
|
3
|
+
<% pf_audit_preload_last_changes([resource, *(@tab == 'rules' ? @rules : [])]) %>
|
|
4
|
+
<% muting = set.is_muting %>
|
|
5
|
+
<% locked = pf_locked_record?(set) || pf_locked_record?(resource) %>
|
|
6
|
+
<% label = pf_resource_label(resource) %>
|
|
7
|
+
<% plural = pf_resource_plural(resource.name) %>
|
|
8
|
+
<% base = "pf[resources][#{resource.id}]" %>
|
|
9
|
+
<% content_for :pf_title, "#{label} · #{set.name}" %>
|
|
10
|
+
<%= pf_breadcrumb([t('permission_forge.nav.permission_sets'), permission_forge.permission_sets_path],
|
|
11
|
+
[set.name, permission_forge.permission_set_path(set)], label) %>
|
|
12
|
+
|
|
13
|
+
<% if muting %>
|
|
14
|
+
<div class="pf-banner pf-banner--muting" role="note">
|
|
15
|
+
<span class="pf-banner__glyph" aria-hidden="true">⊘</span>
|
|
16
|
+
<div class="pf-banner__body"><span><b><%= t('permission_forge.ui.badges.muting') %>.</b> <%= t('.muting_note', set: set.name) %></span></div>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
<%= render 'permission_forge/shared/mode_banners', record: (pf_locked_record?(resource) ? resource : set), muting: false,
|
|
20
|
+
duplicate_path: permission_forge.duplicate_permission_set_path(set) %>
|
|
21
|
+
<%= pf_help_banner('resource', term: t('.help_term'), text: t('.help_text', model: resource.name)) %>
|
|
22
|
+
|
|
23
|
+
<%= form_with url: permission_forge.review_permission_set_resource_path(set, resource.name), method: :post, id: 'pf-res-form',
|
|
24
|
+
data: { pf_track: 'pf-res-savebar', pf_review: true } do %>
|
|
25
|
+
<input type="hidden" name="return_to" value="<%= request.fullpath %>">
|
|
26
|
+
<%# The form's default button. Without it, Enter in a text input submits through the first
|
|
27
|
+
submit button on the page — a bulk "Set to" button, proposing that level for the ticked rows.
|
|
28
|
+
Out of sight but rendered, not `hidden`: Safari before 16.4 skips a display:none button
|
|
29
|
+
when it picks the default button (webkit.org/b/247545) and would press a bulk button. %>
|
|
30
|
+
<button type="submit" class="pf-sr-only" tabindex="-1" aria-hidden="true"><%= t('permission_forge.shared.savebar.review') %></button>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<div class="pf-page-header">
|
|
34
|
+
<div class="pf-page-header__text">
|
|
35
|
+
<div class="pf-row">
|
|
36
|
+
<h1 class="pf-h1"><%= label %></h1>
|
|
37
|
+
<code class="pf-mono-key"><%= resource.name %></code>
|
|
38
|
+
</div>
|
|
39
|
+
<label class="pf-sr-only" for="pf-res-desc"><%= t('.description') %></label>
|
|
40
|
+
<input id="pf-res-desc" class="pf-desc-input" type="text" name="<%= base %>[description]" value="<%= resource.description %>"
|
|
41
|
+
form="pf-res-form" placeholder="<%= t('.description_placeholder') %>" <%= 'readonly' if locked %>>
|
|
42
|
+
<span class="pf-impact"><span class="pf-dot" aria-hidden="true"></span><%= t('.impact', people: pf_people_text(@people_count)) %></span>
|
|
43
|
+
</div>
|
|
44
|
+
<label class="pf-switch">
|
|
45
|
+
<input type="hidden" name="<%= base %>[is_active]" value="0" form="pf-res-form">
|
|
46
|
+
<input type="checkbox" role="switch" name="<%= base %>[is_active]" value="1" form="pf-res-form" <%= 'checked' if resource.is_active %> <%= 'disabled' if locked %>>
|
|
47
|
+
<span class="pf-switch__track" aria-hidden="true"></span>
|
|
48
|
+
<span class="pf-switch__on"><%= t('permission_forge.shared.active') %></span>
|
|
49
|
+
<span class="pf-switch__off"><%= t('permission_forge.ui.badges.off') %></span>
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="pf-grid-2" data-pf-crud="r<%= resource.id %>">
|
|
54
|
+
<section class="pf-card" aria-labelledby="pf-res-crud">
|
|
55
|
+
<div class="pf-card__body pf-stack">
|
|
56
|
+
<h2 class="pf-h2" id="pf-res-crud"><%= t('.what_can_do') %></h2>
|
|
57
|
+
<div class="pf-crud-grid">
|
|
58
|
+
<% PermissionForge::Admin::EffectiveAccess::CRUD.each do |flag| %>
|
|
59
|
+
<div class="pf-crud-grid__col">
|
|
60
|
+
<span class="pf-fs-12 pf-text-3"><%= t("permission_forge.ui.flags_short.#{flag}") %></span>
|
|
61
|
+
<%= render 'permission_forge/shared/crud_cell', name: "#{base}[#{flag}]", flag: flag, checked: resource.public_send(flag),
|
|
62
|
+
muting: muting, locked: locked, size: :lg, form: 'pf-res-form',
|
|
63
|
+
requires: (%w[edit_access delete_access].include?(flag.to_s) ? 'read_access' : nil),
|
|
64
|
+
aria: "#{t("permission_forge.ui.flags_short.#{flag}")} #{plural}" %>
|
|
65
|
+
</div>
|
|
66
|
+
<% end %>
|
|
67
|
+
</div>
|
|
68
|
+
<span class="pf-hint"><%= muting ? t('.muting_hint') : t('.needs_read_hint') %></span>
|
|
69
|
+
</div>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<% unless muting %>
|
|
73
|
+
<section class="pf-card" aria-labelledby="pf-res-scope">
|
|
74
|
+
<div class="pf-card__body pf-stack">
|
|
75
|
+
<h2 class="pf-h2" id="pf-res-scope"><%= t('.record_scope') %></h2>
|
|
76
|
+
<% { 'view_all' => 'read_access', 'modify_all' => 'view_all edit_access' }.each do |flag, requires| %>
|
|
77
|
+
<label class="pf-scope-option">
|
|
78
|
+
<input type="hidden" name="<%= base %>[<%= flag %>]" value="0" form="pf-res-form">
|
|
79
|
+
<input type="checkbox" name="<%= base %>[<%= flag %>]" value="1" form="pf-res-form" <%= 'checked' if resource.public_send(flag) %>
|
|
80
|
+
<%= 'disabled' if locked %> data-pf-flag="<%= flag %>" data-pf-requires="<%= requires %>" <%= 'data-pf-locked' if locked %>>
|
|
81
|
+
<span class="pf-fs-13"><%= t(".#{flag}_html", resource: label) %>
|
|
82
|
+
<span class="pf-text-3"><%= t(".#{flag}_needs") %></span></span>
|
|
83
|
+
</label>
|
|
84
|
+
<% end %>
|
|
85
|
+
<% active_rules = @rules.count(&:is_active) %>
|
|
86
|
+
<% if resource.view_all && active_rules.positive? %>
|
|
87
|
+
<span class="pf-fs-13 pf-text-warn">⚠ <%= t('.rules_ignored', count: active_rules) %></span>
|
|
88
|
+
<% end %>
|
|
89
|
+
</div>
|
|
90
|
+
</section>
|
|
91
|
+
<% end %>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<% if @rows.sync_needed? && !locked %>
|
|
95
|
+
<%= render 'sync', set: set, resource: resource, muting: muting %>
|
|
96
|
+
<% end %>
|
|
97
|
+
|
|
98
|
+
<nav class="pf-tabs" role="tablist" aria-label="<%= t('.tabs_label') %>">
|
|
99
|
+
<%= pf_tab t('permission_forge.ui.fields'), permission_forge.permission_set_resource_path(set, resource.name), selected: @tab == 'fields', count: @rows.rows.size %>
|
|
100
|
+
<%= pf_tab t('permission_forge.ui.actions'), permission_forge.permission_set_resource_path(set, resource.name, tab: 'actions'), selected: @tab == 'actions', count: @action_rows.rows.size %>
|
|
101
|
+
<%= pf_tab t('permission_forge.ui.rules_tab'), permission_forge.permission_set_resource_path(set, resource.name, tab: 'rules'), selected: @tab == 'rules', count: @rules.size %>
|
|
102
|
+
</nav>
|
|
103
|
+
|
|
104
|
+
<div role="tabpanel">
|
|
105
|
+
<%= render "#{@tab}_tab", set: set, resource: resource, muting: muting, locked: locked, base: base %>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<% unless locked %>
|
|
109
|
+
<%= render 'permission_forge/shared/savebar', id: 'pf-res-savebar', form: 'pf-res-form', people: @people_count %>
|
|
110
|
+
<% end %>
|
|
111
|
+
|
|
112
|
+
<%= pf_tech_details(resource, extras: { model: resource.name }) do %>
|
|
113
|
+
<% unless locked %>
|
|
114
|
+
<div><%= pf_modal_link t('.remove', resource: label), permission_forge.delete_permission_set_resource_path(set, resource.name), class: 'pf-btn pf-btn--danger pf-btn--xs' %></div>
|
|
115
|
+
<% end %>
|
|
116
|
+
<% end %>
|
|
117
|
+
<%= pf_audit_footer(resource) %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% config = PermissionForge.configuration %>
|
|
2
|
+
<% tiles = [['enforce', 'auto_query_scoping', config.auto_query_scoping.to_s != 'off', config.auto_query_scoping],
|
|
3
|
+
['detect', 'unscoped_query_detection', config.unscoped_query_detection.to_s != 'off', config.unscoped_query_detection],
|
|
4
|
+
['database', 'database_row_level_security', config.database_row_level_security == true, config.database_row_level_security]] %>
|
|
5
|
+
<div class="pf-stack pf-stack--tight">
|
|
6
|
+
<p class="pf-fs-13 pf-text-2 pf-setting"><%= t('.intro') %></p>
|
|
7
|
+
<div class="pf-ladder">
|
|
8
|
+
<% tiles.each do |level, key, on, value| %>
|
|
9
|
+
<%= link_to permission_forge.settings_path(focus: key, anchor: "set-#{key}"), class: "pf-ladder__tile#{' pf-ladder__tile--on' if on}" do %>
|
|
10
|
+
<span class="pf-fw-600"><%= on ? '✓' : '–' %> <%= t(".#{level}") %> · <%= on ? t('.active') : t('.off') %></span>
|
|
11
|
+
<code class="pf-mono-key"><%= key %> = <%= value.inspect %></code>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<% key = row.key %>
|
|
2
|
+
<% name = t("permission_forge.settings.items.#{key}.name") %>
|
|
3
|
+
<% desc = t("permission_forge.settings.items.#{key}.desc") %>
|
|
4
|
+
<div class="pf-setting" id="set-<%= key %>" data-pf-setting-row="<%= "#{key} #{name} #{desc}".downcase %>" <%= pf_attrs_when(focused, data: { pf_focus: true }) %>>
|
|
5
|
+
<div class="pf-setting__about">
|
|
6
|
+
<div class="pf-row pf-row--tight">
|
|
7
|
+
<b><%= name %></b>
|
|
8
|
+
<%= pf_badge(t('.changed'), variant: :changed) if row.changed? %>
|
|
9
|
+
<%= pf_badge("⚠ #{t('.risk')}", variant: :warn) if row.warn? %>
|
|
10
|
+
<%= pf_badge("✓ #{t('.secure')}", variant: :secure) if row.ok? %>
|
|
11
|
+
</div>
|
|
12
|
+
<span class="pf-fs-13 pf-text-2"><%= desc %></span>
|
|
13
|
+
<code class="pf-mono-key"><%= key %></code>
|
|
14
|
+
<% if row.warn? %><span class="pf-fs-13 pf-text-warn"><%= t("permission_forge.settings.risks.#{key}") %></span><% end %>
|
|
15
|
+
<% if row.ok? %><span class="pf-fs-12 pf-text-grant"><%= t("permission_forge.settings.oks.#{key}") %></span><% end %>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="pf-setting__value">
|
|
18
|
+
<%= pf_setting_value(row) %>
|
|
19
|
+
<span class="pf-fs-12 pf-text-3"><%= t('.default', value: pf_setting_default(row)) %></span>
|
|
20
|
+
</div>
|
|
21
|
+
<details class="pf-setting__how" <%= 'open' if focused %>>
|
|
22
|
+
<summary class="pf-btn pf-btn--sm"><%= t('.how_to_change') %></summary>
|
|
23
|
+
<div class="pf-setting__panel">
|
|
24
|
+
<% snippet = "# config/initializers/permission_forge.rb\nPermissionForge.configure do |config|\n config.#{key} = #{@catalog.snippet_value(row)}\nend" %>
|
|
25
|
+
<div class="pf-row pf-row--between">
|
|
26
|
+
<span class="pf-fs-12 pf-text-3"><%= row.warn? && @catalog.recommended_value(key) ? t('.snippet_recommended') : t('.snippet_current') %></span>
|
|
27
|
+
<button type="button" class="pf-btn pf-btn--xs" data-pf-copy="<%= snippet %>" data-pf-copied="<%= t('permission_forge.shared.tech_details.copied') %>" hidden data-pf-js-show><%= t('permission_forge.shared.tech_details.copy') %></button>
|
|
28
|
+
</div>
|
|
29
|
+
<pre class="pf-code"><%= snippet %></pre>
|
|
30
|
+
<ol class="pf-stack pf-stack--tight pf-fs-13">
|
|
31
|
+
<li>1. <%= t('.step_edit') %></li>
|
|
32
|
+
<li>2. <%= t('.step_deploy') %></li>
|
|
33
|
+
<li>3. <%= t('.step_reload') %></li>
|
|
34
|
+
</ol>
|
|
35
|
+
<%= link_to t('.docs', key: key), "#{PermissionForge.configuration.admin_docs_url}##{key}", class: 'pf-fs-12', target: '_blank', rel: 'noopener noreferrer' %>
|
|
36
|
+
</div>
|
|
37
|
+
</details>
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<% content_for :pf_title, t('permission_forge.nav.settings') %>
|
|
2
|
+
<% risky = @catalog.risky_rows %>
|
|
3
|
+
<div class="pf-page-header">
|
|
4
|
+
<div class="pf-page-header__text">
|
|
5
|
+
<div class="pf-row">
|
|
6
|
+
<h1 class="pf-h1"><%= t('permission_forge.nav.settings') %></h1>
|
|
7
|
+
<code class="pf-mono-key">PermissionForge <%= PermissionForge::VERSION %></code>
|
|
8
|
+
<span class="pf-pill"><%= t('.read_only') %></span>
|
|
9
|
+
</div>
|
|
10
|
+
<p class="pf-subtitle"><%= t('.intro_html', file: content_tag(:code, 'config/initializers/permission_forge.rb')) %></p>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<section class="pf-card" aria-labelledby="pf-health">
|
|
15
|
+
<div class="pf-card__header"><h2 class="pf-h2" id="pf-health"><%= t('.health') %></h2></div>
|
|
16
|
+
<% risky.each do |row| %>
|
|
17
|
+
<div class="pf-card__row pf-card__row--top">
|
|
18
|
+
<span class="pf-text-warn" aria-hidden="true">⚠</span>
|
|
19
|
+
<span class="pf-stack pf-stack--tight pf-grow">
|
|
20
|
+
<b><%= t("permission_forge.settings.items.#{row.key}.name") %></b>
|
|
21
|
+
<span class="pf-fs-13 pf-text-2"><%= t("permission_forge.settings.risks.#{row.key}") %></span>
|
|
22
|
+
<code class="pf-mono-key"><%= row.key %> = <%= pf_setting_inline_value(row) %></code>
|
|
23
|
+
</span>
|
|
24
|
+
<%= link_to t('.how_to_fix'), permission_forge.settings_path(focus: row.key, anchor: "set-#{row.key}"), class: 'pf-btn pf-btn--sm' %>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
<div class="pf-card__row">
|
|
28
|
+
<span class="pf-text-grant">✓ <%= t('.secure_count', count: @catalog.secure_count) %></span>
|
|
29
|
+
</div>
|
|
30
|
+
</section>
|
|
31
|
+
|
|
32
|
+
<%= form_with url: permission_forge.settings_path, method: :get, class: 'pf-row', role: 'search' do %>
|
|
33
|
+
<label class="pf-sr-only" for="pf-settings-q"><%= t('.search') %></label>
|
|
34
|
+
<input id="pf-settings-q" class="pf-input pf-input--auto pf-grow" type="search" name="q" value="<%= params[:q] %>"
|
|
35
|
+
placeholder="<%= t('.search') %>" data-pf-settings-filter>
|
|
36
|
+
<label class="pf-checkbox">
|
|
37
|
+
<input type="checkbox" name="changed" value="1" <%= 'checked' if @changed_only %> data-pf-autosubmit>
|
|
38
|
+
<span><%= t('.changed_only') %></span>
|
|
39
|
+
</label>
|
|
40
|
+
<button type="submit" class="pf-btn pf-btn--sm" data-pf-js-hide><%= t('permission_forge.ui.apply') %></button>
|
|
41
|
+
<% end %>
|
|
42
|
+
|
|
43
|
+
<div class="pf-card pf-card--visible">
|
|
44
|
+
<% if @sections.empty? %><p class="pf-empty"><%= t('.no_match') %></p><% end %>
|
|
45
|
+
<% @sections.each do |section, rows| %>
|
|
46
|
+
<% section_risky = rows.any?(&:warn?) %>
|
|
47
|
+
<% open = @focus.present? ? rows.any? { |r| r.key == @focus } : (@query.present? || @changed_only || section_risky) %>
|
|
48
|
+
<details class="pf-setting-section" data-pf-setting-section <%= 'open' if open %>>
|
|
49
|
+
<summary>
|
|
50
|
+
<span class="pf-disclosure" aria-hidden="true">▸</span>
|
|
51
|
+
<span class="pf-h2"><%= t("permission_forge.settings.sections.#{section}") %></span>
|
|
52
|
+
<% if section_risky %><span class="pf-text-warn" aria-label="<%= t('.has_risk') %>">⚠</span><% end %>
|
|
53
|
+
<span class="pf-fs-12 pf-text-3 pf-ml-auto"><%= t('.section_meta', settings: t('.settings_count', count: rows.size), changed: t('.changed_count', count: rows.count(&:changed?))) %></span>
|
|
54
|
+
</summary>
|
|
55
|
+
<% if section == 'admin_api' && !PermissionForge.configuration.api_enabled && !@query.present? %>
|
|
56
|
+
<p class="pf-setting pf-text-2"><%= t('.api_disabled_html', key: content_tag(:code, 'api_enabled = false')) %></p>
|
|
57
|
+
<% rows = rows.select { |r| r.key == 'api_enabled' } %>
|
|
58
|
+
<% end %>
|
|
59
|
+
<% if section == 'record_rules' %>
|
|
60
|
+
<%= render 'ladder' %>
|
|
61
|
+
<% end %>
|
|
62
|
+
<% rows.each do |row| %>
|
|
63
|
+
<%= render 'row', row: row, focused: row.key == @focus %>
|
|
64
|
+
<% end %>
|
|
65
|
+
</details>
|
|
66
|
+
<% end %>
|
|
67
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%# "Who can see this field?" — one row per Permission Set containing the resource.
|
|
2
|
+
Locals: rows ([{ resource:, set:, field:, people:, in_effect: }] from Admin::FieldAcrossSets), key, current_set (opt) %>
|
|
3
|
+
<% if rows.empty? %>
|
|
4
|
+
<p class="pf-fs-13 pf-text-3"><%= t('.none') %></p>
|
|
5
|
+
<% else %>
|
|
6
|
+
<div class="pf-listbox">
|
|
7
|
+
<% rows.each do |row| %>
|
|
8
|
+
<% set = row[:set] %>
|
|
9
|
+
<% field = row[:field] %>
|
|
10
|
+
<% level = field && PermissionForge::Admin::FieldLevel.level_of(field, muting: set.is_muting) %>
|
|
11
|
+
<%= link_to permission_forge.permission_set_resource_field_path(set, row[:resource].name, key),
|
|
12
|
+
class: 'pf-option', 'aria-current': (local_assigns[:current_set] == set ? 'true' : nil) do %>
|
|
13
|
+
<span class="pf-option__main">
|
|
14
|
+
<span class="pf-row pf-row--tight">
|
|
15
|
+
<% if set.is_muting %><span class="pf-text-restrict" aria-hidden="true">⊘</span><% end %>
|
|
16
|
+
<span class="pf-fw-600"><%= set.name %></span>
|
|
17
|
+
<%= pf_off_badge unless row[:in_effect] %>
|
|
18
|
+
</span>
|
|
19
|
+
</span>
|
|
20
|
+
<% if level.nil? %>
|
|
21
|
+
<%= pf_level_badge('none', label: t('permission_forge.ui.badges.not_configured')) %>
|
|
22
|
+
<% elsif set.is_muting %>
|
|
23
|
+
<%= pf_level_badge(level == 'hide' ? 'muted' : level, label: (t('.muted_hidden') if level == 'hide')) %>
|
|
24
|
+
<% else %>
|
|
25
|
+
<%= pf_level_badge(level, strategy: (field.mask_strategy if level == 'masked')) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<span class="pf-option__aside"><%= pf_people_text(row[:people]) %></span>
|
|
28
|
+
<% end %>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<%# A list of audit events, newest first: when, what (a sentence), who, how.
|
|
2
|
+
Locals: events, lookups; optional empty (text when there are none). %>
|
|
3
|
+
<% if events.empty? %>
|
|
4
|
+
<p class="pf-empty"><%= local_assigns[:empty] || t('permission_forge.audit_log.empty') %></p>
|
|
5
|
+
<% else %>
|
|
6
|
+
<ol class="pf-audit-list" aria-label="<%= t('permission_forge.audit_log.list_label') %>">
|
|
7
|
+
<% events.each do |event| %>
|
|
8
|
+
<li class="pf-audit-event">
|
|
9
|
+
<time class="pf-audit-event__when" datetime="<%= event.occurred_at.utc.iso8601 %>">
|
|
10
|
+
<span class="pf-fw-600"><%= pf_date(event.occurred_at) %></span>
|
|
11
|
+
<span class="pf-text-3"><%= event.occurred_at.in_time_zone.strftime('%H:%M:%S') %></span>
|
|
12
|
+
</time>
|
|
13
|
+
<div class="pf-audit-event__main">
|
|
14
|
+
<%= link_to pf_audit_sentence(event, lookups), pf_audit_event_path(event), class: 'pf-audit-event__sentence' %>
|
|
15
|
+
<div class="pf-audit-event__meta">
|
|
16
|
+
<%= pf_audit_category_badge(event.category) %>
|
|
17
|
+
<%= link_to event.event, pf_audit_log_path(event: event.event), class: 'pf-mono-key',
|
|
18
|
+
title: t('permission_forge.audit_log.filter_event', event: event.event) %>
|
|
19
|
+
<span aria-hidden="true">·</span>
|
|
20
|
+
<%= link_to pf_audit_actor_name(event), pf_audit_log_path(actor_type: event.actor_type, actor_id: event.actor_id),
|
|
21
|
+
title: t('permission_forge.audit_log.filter_actor') %>
|
|
22
|
+
<span aria-hidden="true">·</span>
|
|
23
|
+
<span><%= pf_audit_source_label(event.source) %></span>
|
|
24
|
+
<% if event.reason.present? %>
|
|
25
|
+
<span aria-hidden="true">·</span>
|
|
26
|
+
<span class="pf-audit-event__reason"><%= t('permission_forge.audit_log.reason_inline', reason: event.reason) %></span>
|
|
27
|
+
<% end %>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<%= link_to pf_audit_event_path(event), class: 'pf-btn pf-btn--xs pf-audit-event__open' do %>
|
|
31
|
+
<%= t('permission_forge.audit_log.details') %> <span aria-hidden="true">→</span>
|
|
32
|
+
<% end %>
|
|
33
|
+
</li>
|
|
34
|
+
<% end %>
|
|
35
|
+
</ol>
|
|
36
|
+
<% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<footer class="pf-audit-footer">
|
|
2
|
+
<% if PermissionForge.configuration.audit_trail_enabled %>
|
|
3
|
+
<% last_event = pf_audit_last_change(record) %>
|
|
4
|
+
<%= prefix %>
|
|
5
|
+
<%= t('.created', created_by: pf_person_name(record.try(:created_by)), created_at: pf_date(record.created_at)) %>
|
|
6
|
+
·
|
|
7
|
+
<% if last_event %>
|
|
8
|
+
<%= t('.edited', updated_by: pf_audit_actor_name(last_event), updated_at: pf_relative(last_event.occurred_at)) %>
|
|
9
|
+
· <%= link_to t('.history'), pf_audit_log_path(record_type: last_event.record_type, record_id: last_event.record_id) %>
|
|
10
|
+
<% else %>
|
|
11
|
+
<%= t('.edited', updated_by: pf_person_name(record.try(:updated_by)), updated_at: pf_relative(record.updated_at)) %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% else %>
|
|
14
|
+
<%= t('.disabled') %> <%= pf_setting_link('audit_trail_enabled') %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</footer>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%# Locals: problem (Admin::AuditLog.recording_problem: :off, :failing or :missing_table). %>
|
|
2
|
+
<div class="pf-banner pf-banner--warn" role="note">
|
|
3
|
+
<span class="pf-banner__glyph" aria-hidden="true">⚠</span>
|
|
4
|
+
<div class="pf-banner__body">
|
|
5
|
+
<span class="pf-banner__title"><%= t('permission_forge.audit_log.off.title') %></span>
|
|
6
|
+
<% if problem == :off %>
|
|
7
|
+
<span><%= t('permission_forge.audit_log.off.disabled') %></span>
|
|
8
|
+
<% elsif problem == :failing %>
|
|
9
|
+
<span><%= t('permission_forge.audit_log.off.failing') %></span>
|
|
10
|
+
<% else %>
|
|
11
|
+
<span><%= t('permission_forge.audit_log.off.missing_table') %></span>
|
|
12
|
+
<% end %>
|
|
13
|
+
</div>
|
|
14
|
+
<%= link_to t('permission_forge.ui.open_setting'), pf_setting_path('audit_events'), class: 'pf-btn pf-btn--xs' %>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# Page links for an AuditLog::Page. Locals: page, path (lambda: page number -> path). %>
|
|
2
|
+
<% if page.total.positive? %>
|
|
3
|
+
<nav class="pf-card__footer pf-card__footer--muted pf-pagination" aria-label="<%= t('permission_forge.audit_log.pages_label') %>">
|
|
4
|
+
<span><%= t('permission_forge.audit_log.showing', from: page.first_index, to: page.last_index, count: page.total) %></span>
|
|
5
|
+
<% if page.pages > 1 %>
|
|
6
|
+
<span class="pf-actions">
|
|
7
|
+
<% if page.previous? %>
|
|
8
|
+
<%= link_to t('permission_forge.audit_log.first_page'), path.call(1), class: 'pf-btn pf-btn--xs' %>
|
|
9
|
+
<%= link_to t('permission_forge.ui.previous'), path.call(page.number - 1), class: 'pf-btn pf-btn--xs', rel: 'prev' %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<span class="pf-fs-13"><%= t('permission_forge.audit_log.page_of', page: page.number, pages: page.pages) %></span>
|
|
12
|
+
<% if page.next? %>
|
|
13
|
+
<%= link_to t('permission_forge.ui.next'), path.call(page.number + 1), class: 'pf-btn pf-btn--xs', rel: 'next' %>
|
|
14
|
+
<%= link_to t('permission_forge.audit_log.last_page'), path.call(page.pages), class: 'pf-btn pf-btn--xs' %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</nav>
|
|
19
|
+
<% end %>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<div class="pf-banner pf-banner--block" role="alert">
|
|
2
|
+
<span class="pf-banner__glyph" aria-hidden="true">⊘</span>
|
|
3
|
+
<div class="pf-banner__body">
|
|
4
|
+
<span class="pf-banner__title"><%= t('permission_forge.audit_log.refused.title') %></span>
|
|
5
|
+
<span><%= t('permission_forge.audit_log.refused.body') %></span>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<%# A record's or a person's timeline on its audit tab.
|
|
2
|
+
Locals: title, description, page, lookups, path (page number -> path), links ([[label, path], ...]),
|
|
3
|
+
views_path (the same timeline with the views of the log; nil when they are shown). %>
|
|
4
|
+
<section class="pf-card" aria-labelledby="pf-timeline-title">
|
|
5
|
+
<div class="pf-card__header">
|
|
6
|
+
<div class="pf-stack pf-stack--tight pf-grow">
|
|
7
|
+
<h2 class="pf-h2" id="pf-timeline-title"><%= title %></h2>
|
|
8
|
+
<span class="pf-card__desc"><%= description %></span>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="pf-actions">
|
|
11
|
+
<% links.each do |label, href| %>
|
|
12
|
+
<%= link_to label, href, class: 'pf-btn pf-btn--sm' %>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<% if (problem = PermissionForge::Admin::AuditLog.recording_problem) %>
|
|
17
|
+
<div class="pf-card__body">
|
|
18
|
+
<%= render 'permission_forge/shared/audit_off_note', problem: problem %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% if local_assigns[:views_path] %>
|
|
22
|
+
<p class="pf-card__row pf-fs-13 pf-text-2 pf-audit-views-note">
|
|
23
|
+
<%= t('permission_forge.audit_events.index.views_hidden') %>
|
|
24
|
+
<%= link_to t('permission_forge.audit_events.index.views_show'), views_path %>
|
|
25
|
+
</p>
|
|
26
|
+
<% end %>
|
|
27
|
+
<%= render 'permission_forge/shared/audit_events', events: page.events, lookups: lookups %>
|
|
28
|
+
<%= render 'permission_forge/shared/audit_pagination', page: page, path: path %>
|
|
29
|
+
</section>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%# CRUD toggle cell: a checkbox styled as a cell (hidden "0" so unchecking is submitted).
|
|
2
|
+
Locals: name, flag, checked, muting, locked, aria, requires (opt), size (opt), form (opt) %>
|
|
3
|
+
<% form_attr = local_assigns[:form] ? { form: form } : {} %>
|
|
4
|
+
<% requires = local_assigns[:requires] %>
|
|
5
|
+
<label class="pf-cell<%= ' pf-cell--muting' if muting %><%= ' pf-cell--lg' if local_assigns[:size] == :lg %>"
|
|
6
|
+
title="<%= t('.needs_read') if requires && !muting %>">
|
|
7
|
+
<%= tag.input(type: 'hidden', name: name, value: '0', **form_attr) %>
|
|
8
|
+
<%= tag.input(type: 'checkbox', name: name, value: '1', checked: checked, disabled: locked,
|
|
9
|
+
'aria-label': aria, data: { pf_flag: flag, pf_requires: (muting ? nil : requires), pf_locked: (locked || nil) }, **form_attr) %>
|
|
10
|
+
<% if muting %>
|
|
11
|
+
<span class="pf-cell__on"><span class="pf-cell__glyph" aria-hidden="true">⊘</span><%= t('.muted') %></span>
|
|
12
|
+
<span class="pf-cell__off"><span class="pf-cell__glyph" aria-hidden="true">–</span><%= t('.unchanged') %></span>
|
|
13
|
+
<% else %>
|
|
14
|
+
<span class="pf-cell__on"><span class="pf-cell__glyph" aria-hidden="true">✓</span><%= t('.allowed') %></span>
|
|
15
|
+
<span class="pf-cell__off"><span class="pf-cell__glyph" aria-hidden="true">–</span><%= t('.no') %></span>
|
|
16
|
+
<span class="pf-cell__needs"><span class="pf-cell__glyph" aria-hidden="true">–</span><%= t('.needs_read_short') %></span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</label>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%# Confirmation dialog: a standalone page without JS, a <dialog> with JS. %>
|
|
2
|
+
<% danger = local_assigns.fetch(:danger, false) %>
|
|
3
|
+
<% unless pf_fragment? %>
|
|
4
|
+
<% if local_assigns[:breadcrumb] %><%= breadcrumb %><% end %>
|
|
5
|
+
<% end %>
|
|
6
|
+
<div class="pf-confirm<%= ' pf-confirm--danger' if danger %><%= ' pf-card pf-modal-page' unless pf_fragment? %>"
|
|
7
|
+
data-pf-role="<%= local_assigns.fetch(:role, 'alertdialog') %>" <%= pf_attrs_when(local_assigns[:wide], data: { pf_size: 'wide' }) %>>
|
|
8
|
+
<% if local_assigns[:eyebrow] %><span class="pf-badge pf-badge--block pf-badge--square"><%= eyebrow %></span><% end %>
|
|
9
|
+
<%= content_tag(pf_fragment? ? :h2 : :h1, title, class: 'pf-confirm__title', data: { pf_title: true }) %>
|
|
10
|
+
<%= body %>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%# Expiry presets as radio cards. Locals: selected, custom_date, error, name (default 'expiry'). %>
|
|
2
|
+
<% name = local_assigns.fetch(:name, 'expiry') %>
|
|
3
|
+
<% tomorrow = (Time.zone.today + 1).iso8601 %>
|
|
4
|
+
<div class="pf-expiry pf-stack">
|
|
5
|
+
<div class="pf-choices pf-choices--expiry" role="radiogroup" aria-label="<%= t('.label') %>">
|
|
6
|
+
<% PermissionForge::Admin::ExpiryPresets::KEYS.each do |key| %>
|
|
7
|
+
<label class="pf-choice">
|
|
8
|
+
<input type="radio" name="<%= name %>" value="<%= key %>" <%= 'checked' if selected == key %>>
|
|
9
|
+
<span class="pf-choice__box">
|
|
10
|
+
<span class="pf-choice__title"><%= t(".presets.#{key}") %></span>
|
|
11
|
+
<span class="pf-choice__sub">
|
|
12
|
+
<% if key == 'custom' %><%= t('.pick_date') %>
|
|
13
|
+
<% elsif key == 'never' %><%= t('.default') %>
|
|
14
|
+
<% else %><%= pf_expiry_preset_date(key) %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</span>
|
|
17
|
+
</span>
|
|
18
|
+
</label>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="pf-custom-date pf-field">
|
|
22
|
+
<label class="pf-label" for="pf-custom-date"><%= t('.custom_label') %></label>
|
|
23
|
+
<input id="pf-custom-date" class="pf-input pf-input--auto" type="date" name="custom_date" min="<%= tomorrow %>" value="<%= custom_date %>"
|
|
24
|
+
<%= pf_attrs_when(error, aria: { invalid: true }) %>>
|
|
25
|
+
</div>
|
|
26
|
+
<% if error %><p class="pf-error" role="alert"><%= error %></p><% end %>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="pf-banner pf-banner--help" role="note">
|
|
2
|
+
<div class="pf-banner__body">
|
|
3
|
+
<span><strong><%= term %>:</strong> <%= text %></span>
|
|
4
|
+
</div>
|
|
5
|
+
<%= form_with url: permission_forge.preferences_path, method: :patch, class: 'pf-inline-form' do %>
|
|
6
|
+
<input type="hidden" name="dismiss_help" value="<%= key %>">
|
|
7
|
+
<input type="hidden" name="return_to" value="<%= request.fullpath %>">
|
|
8
|
+
<button type="submit" class="pf-btn pf-btn--xs"><%= t('.got_it') %></button>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%# 4-level field control (radio group styled as a segmented control).
|
|
2
|
+
Locals: name, checked (level or nil), muting, aria, locked, edit_disabled, form (opt), large (opt), id_prefix %>
|
|
3
|
+
<% form_attr = local_assigns[:form] ? { form: form } : {} %>
|
|
4
|
+
<% levels = PermissionForge::Admin::FieldLevel.levels_for(muting: muting) %>
|
|
5
|
+
<div class="pf-levels<%= ' pf-levels--lg' if local_assigns[:large] %>" role="radiogroup" aria-label="<%= aria %>">
|
|
6
|
+
<% levels.each do |level| %>
|
|
7
|
+
<% id = "#{id_prefix}-#{level}" %>
|
|
8
|
+
<% disabled = locked || (level == 'edit' && edit_disabled) %>
|
|
9
|
+
<% title = if locked then t('permission_forge.ui.locked.title')
|
|
10
|
+
elsif level == 'edit' && edit_disabled then t('.needs_edit', resource: local_assigns[:resource_label])
|
|
11
|
+
end %>
|
|
12
|
+
<span class="pf-level pf-level--<%= level %>">
|
|
13
|
+
<%= tag.input(type: 'radio', id: id, name: name, value: level, checked: checked == level, disabled: disabled,
|
|
14
|
+
data: { pf_locked: (locked || nil) }, **form_attr) %>
|
|
15
|
+
<label class="pf-level__label" for="<%= id %>" title="<%= title %>"><%= pf_level_label(level) %></label>
|
|
16
|
+
</span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%# Muting / Locked notices for set, resource and field pages. %>
|
|
2
|
+
<% if muting %>
|
|
3
|
+
<div class="pf-banner pf-banner--muting" role="note">
|
|
4
|
+
<span class="pf-banner__glyph" aria-hidden="true">⊘</span>
|
|
5
|
+
<div class="pf-banner__body">
|
|
6
|
+
<span class="pf-banner__title"><%= t('.muting_title') %></span>
|
|
7
|
+
<span><%= t('.muting_body') %></span>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% if pf_locked_record?(record) %>
|
|
12
|
+
<div class="pf-banner" role="note">
|
|
13
|
+
<span class="pf-banner__glyph" aria-hidden="true"><%= pf_lock_icon %></span>
|
|
14
|
+
<div class="pf-banner__body">
|
|
15
|
+
<span class="pf-banner__title"><%= t('permission_forge.ui.locked.title') %></span>
|
|
16
|
+
<span><%= t('.locked_body_html', flag: content_tag(:code, 'is_editable: false')) %></span>
|
|
17
|
+
</div>
|
|
18
|
+
<% if local_assigns[:duplicate_path] %>
|
|
19
|
+
<%= button_to t('.duplicate_to_edit'), duplicate_path, method: :post, class: 'pf-btn pf-btn--sm', form_class: 'pf-inline-form' %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<% attention = pf_attention.total %>
|
|
2
|
+
<% risks = pf_settings_catalog.risky_rows.size %>
|
|
3
|
+
<div class="pf-nav">
|
|
4
|
+
<%= pf_nav_link t('permission_forge.nav.home'), permission_forge.root_path, exact: true,
|
|
5
|
+
badge: attention, badge_label: t('permission_forge.nav.badge_aria', count: attention) %>
|
|
6
|
+
<%= pf_nav_link t('permission_forge.nav.people'), permission_forge.people_path %>
|
|
7
|
+
<%= pf_nav_link t('permission_forge.nav.permission_sets'), permission_forge.permission_sets_path %>
|
|
8
|
+
<%= pf_nav_link t('permission_forge.nav.groups'), permission_forge.groups_path %>
|
|
9
|
+
<%= pf_nav_link t('permission_forge.nav.audit_log'), permission_forge.audit_events_path %>
|
|
10
|
+
<%= pf_nav_link t('permission_forge.nav.settings'), permission_forge.settings_path,
|
|
11
|
+
badge: risks, badge_label: t('permission_forge.nav.badge_aria', count: risks) %>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% locales = pf_available_locales %>
|
|
2
|
+
<% if locales.size > 1 %>
|
|
3
|
+
<div class="pf-stack pf-stack--tight">
|
|
4
|
+
<span class="pf-sidebar__label" aria-hidden="true"><%= t('.language') %></span>
|
|
5
|
+
<div class="pf-segmented" role="group" aria-label="<%= t('.language') %>">
|
|
6
|
+
<% locales.each do |locale| %>
|
|
7
|
+
<%= link_to locale.to_s.upcase, pf_locale_switch_path(locale), class: 'pf-segmented__item',
|
|
8
|
+
lang: locale, title: pf_locale_label(locale), 'aria-current': (I18n.locale.to_s == locale.to_s).to_s %>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<%= form_with url: permission_forge.preferences_path, method: :patch, class: 'pf-stack pf-stack--tight' do %>
|
|
14
|
+
<span class="pf-sidebar__label" aria-hidden="true"><%= t('.theme') %></span>
|
|
15
|
+
<input type="hidden" name="return_to" value="<%= request.fullpath %>">
|
|
16
|
+
<div class="pf-segmented" role="group" aria-label="<%= t('.theme') %>">
|
|
17
|
+
<% %w[auto light dark].each do |theme| %>
|
|
18
|
+
<button type="submit" name="theme" value="<%= theme %>" class="pf-segmented__item" aria-current="<%= (pf_theme == theme).to_s %>"><%= t(".themes.#{theme}") %></button>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% if pf_current_admin %>
|
|
23
|
+
<span class="pf-sidebar__label"><%= t('.signed_in_as', name: pf_person_name(pf_current_admin)) %></span>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%# Optional reason for a change, kept on its audit events (and on an
|
|
2
|
+
assignment). Collapsed; works without JS. Locals (all optional):
|
|
3
|
+
id — unique input id on the page; compact — a small toggle for a row's
|
|
4
|
+
inline form; form — the id of the form the input belongs to when it sits
|
|
5
|
+
outside it; value — the reason typed before a re-render (opens the toggle). %>
|
|
6
|
+
<% input_id = local_assigns[:id] || 'pf-reason' %>
|
|
7
|
+
<% compact = local_assigns[:compact] %>
|
|
8
|
+
<% value = local_assigns[:value].is_a?(String) ? local_assigns[:value] : nil %>
|
|
9
|
+
<details class="pf-reason<%= ' pf-reason--compact' if compact %>"<%= ' open' if value.present? %>>
|
|
10
|
+
<summary class="pf-reason__summary">
|
|
11
|
+
<span class="pf-disclosure" aria-hidden="true">›</span>
|
|
12
|
+
<%= compact ? t('permission_forge.shared.reason_field.summary_short') : t('permission_forge.shared.reason_field.summary') %>
|
|
13
|
+
</summary>
|
|
14
|
+
<div class="pf-reason__body">
|
|
15
|
+
<label class="<%= compact ? 'pf-sr-only' : 'pf-label' %>" for="<%= input_id %>"><%= t('permission_forge.shared.reason_field.label') %></label>
|
|
16
|
+
<input id="<%= input_id %>" class="pf-input pf-input--sm" type="text" name="reason" maxlength="255" autocomplete="off" value="<%= value %>"
|
|
17
|
+
placeholder="<%= t('permission_forge.shared.reason_field.placeholder') %>"<%= tag.attributes(form: local_assigns[:form]) if local_assigns[:form] %>>
|
|
18
|
+
<% unless compact %><span class="pf-hint"><%= t('permission_forge.shared.reason_field.hint') %></span><% end %>
|
|
19
|
+
</div>
|
|
20
|
+
</details>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<%# Unsaved-changes bar: the submit area of a batched-edit form (form="id"). %>
|
|
2
|
+
<div id="<%= id %>" class="pf-savebar" role="region" aria-label="<%= t('.label') %>" data-pf-dirty="false">
|
|
3
|
+
<div class="pf-savebar__text">
|
|
4
|
+
<span class="pf-savebar__count pf-savebar__count--dirty" data-pf-dirty-count
|
|
5
|
+
data-pf-one="<%= t('.count.one', count: '%{count}') %>" data-pf-other="<%= t('.count.other', count: '%{count}') %>"></span>
|
|
6
|
+
<span class="pf-savebar__count pf-savebar__count--clean"><%= t('.clean') %></span>
|
|
7
|
+
<span class="pf-savebar__note"><%= people.to_i.positive? ? t('.note', people: pf_people_text(people)) : t('.note_nobody') %></span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="pf-actions">
|
|
10
|
+
<button type="reset" form="<%= form %>" class="pf-btn pf-btn--sm pf-btn--on-dark" hidden data-pf-js-show><%= t('.discard') %></button>
|
|
11
|
+
<button type="submit" form="<%= form %>" class="pf-btn pf-btn--sm pf-btn--inverse"><%= t('.review') %></button>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|