diff --git a/decidim-admin/config/locales/en.yml b/decidim-admin/config/locales/en.yml index d7107b46b9c1..b1d4fb6e1e33 100644 --- a/decidim-admin/config/locales/en.yml +++ b/decidim-admin/config/locales/en.yml @@ -982,7 +982,7 @@ en: 'true': 'Yes' index: back_to_share_tokens: Back to share tokens - copied: Copied! + copied: Share URL Copied! copy_message: The text was successfully copied to clipboard. create_new_token: Create your first token! empty: There are no active tokens. %{new_token_link} diff --git a/decidim-assemblies/app/models/decidim/assembly.rb b/decidim-assemblies/app/models/decidim/assembly.rb index 5b99367ebfdc..4a045444c820 100644 --- a/decidim-assemblies/app/models/decidim/assembly.rb +++ b/decidim-assemblies/app/models/decidim/assembly.rb @@ -36,6 +36,7 @@ class Assembly < ApplicationRecord include Decidim::TranslatableResource include Decidim::HasArea include Decidim::FilterableResource + include Decidim::ShareableWithToken CREATED_BY = %w(city_council public others).freeze diff --git a/decidim-conferences/app/models/decidim/conference.rb b/decidim-conferences/app/models/decidim/conference.rb index b53ddb4171a9..e262b8b40601 100644 --- a/decidim-conferences/app/models/decidim/conference.rb +++ b/decidim-conferences/app/models/decidim/conference.rb @@ -20,6 +20,7 @@ class Conference < ApplicationRecord include Decidim::HasUploadValidations include Decidim::TranslatableResource include Decidim::FilterableResource + include Decidim::ShareableWithToken translatable_fields :title, :slogan, :short_description, :description, :objectives, :registration_terms diff --git a/decidim-core/app/controllers/decidim/homepage_controller.rb b/decidim-core/app/controllers/decidim/homepage_controller.rb index 6dd3c23367f0..a8784f8a8300 100644 --- a/decidim-core/app/controllers/decidim/homepage_controller.rb +++ b/decidim-core/app/controllers/decidim/homepage_controller.rb @@ -3,7 +3,6 @@ module Decidim class HomepageController < Decidim::ApplicationController skip_before_action :store_current_location - def show; end end end diff --git a/decidim-core/app/packs/src/decidim/clipboard.js b/decidim-core/app/packs/src/decidim/clipboard.js index b8948fd92a58..675bb95fb7db 100644 --- a/decidim-core/app/packs/src/decidim/clipboard.js +++ b/decidim-core/app/packs/src/decidim/clipboard.js @@ -43,8 +43,8 @@ $(() => { const $input = $($el.data("clipboard-copy")); - let selectedText = $el.data("clipboard-content"); - if (selectedText === "" && $input.length > 1 && $input.is("input, textarea, select")) { + let selectedText = $el.data("clipboard-content") || ""; + if (selectedText === "" && $input.is("input, textarea, select")) { selectedText = select($input[0]); } diff --git a/decidim-initiatives/app/models/decidim/initiative.rb b/decidim-initiatives/app/models/decidim/initiative.rb index 3246d4fe7172..4773a0ac98c5 100644 --- a/decidim-initiatives/app/models/decidim/initiative.rb +++ b/decidim-initiatives/app/models/decidim/initiative.rb @@ -24,6 +24,7 @@ class Initiative < ApplicationRecord include Decidim::HasResourcePermission include Decidim::HasArea include Decidim::FilterableResource + include Decidim::ShareableWithToken translatable_fields :title, :description, :answer diff --git a/decidim-participatory_processes/app/models/decidim/participatory_process.rb b/decidim-participatory_processes/app/models/decidim/participatory_process.rb index be9d2e0c2d83..84d54093af75 100644 --- a/decidim-participatory_processes/app/models/decidim/participatory_process.rb +++ b/decidim-participatory_processes/app/models/decidim/participatory_process.rb @@ -23,6 +23,7 @@ class ParticipatoryProcess < ApplicationRecord include Decidim::TranslatableResource include Decidim::HasArea include Decidim::FilterableResource + include Decidim::ShareableWithToken translatable_fields :title, :subtitle, :short_description, :description, :developer_group, :meta_scope, :local_area, :target, :participatory_scope, :participatory_structure, :announcement diff --git a/docs/modules/develop/pages/share_tokens.adoc b/docs/modules/develop/pages/share_tokens.adoc index c5012dcf5e9a..fd46b3b5bc9b 100644 --- a/docs/modules/develop/pages/share_tokens.adoc +++ b/docs/modules/develop/pages/share_tokens.adoc @@ -1,6 +1,6 @@ = Share tokens -Share tokens can be assigned to any model to provide a system to share unpublished resources with expirable and manageable tokens. +Share tokens can be assigned to any model to provide a system to share unpublished resources with expiration dates through the creation/destruction of tokens. A share token is created by a user with an expiration time, and can be added as a query param to access otherwise restricted locations. @@ -31,23 +31,140 @@ return unless token.present? allow! if Decidim::ShareToken.use!(token_for: your_resource, token: token) ---- +Note that, if you are using a controller who is inheriting from `Decidim::ApplicationController`, you don't need to include the `:share_token` in the context when calling methods like `enforce_permission_to`, as it is already included in the `Decidim::NeedsPermissions` class through the method `store_share_token`. + == Manage tokens -Tokens can be managed in the components view similar as other resources (with and action icon like permissions for instance). By default, assemblies, conferences and participatory spaces are configured to have share tokens. +By default, participatory spaces like process, assemblies, conferences and initiatives are configured to have share tokens, as well as the individual components that are included in them. Participatory spaces have a "Share tokens" tab in the admin view, where you can create new tokens, see the list of existing ones, and revoke them. +Tokens can also be managed in the components view similarly as other resources to give pre-access (with and action icon like permissions for instance). + +Tokens generated for a participatory space are valid for all the components included in it (regardless of their publication status), and tokens generated for a component are valid for that component only. + +== Implementation for participatory spaces + +In order to implement share tokens for a participatory spaces, you need to: + +=== 1. Routes + +Add the `share_tokens` CRUD routes in your `admin_engine.rb` file: + +[source,ruby] +---- +scope "/assemblies/:assembly_slug" do + ... + resources :assembly_share_tokens, except: [:show], path: "share_tokens" + ... +end +---- + +=== 2. Controller + +Add the controller for the participatory space, it only requires to inherit from `Decidim::Admin::ShareTokensController` and define the `resource` method to return the participatory space: + +[source,ruby] +---- +# frozen_string_literal: true + +module Decidim + module Assemblies + module Admin + # This controller allows sharing unpublished things. + # It is targeted for customizations for sharing unpublished things that lives under + # an assembly. + class AssemblyShareTokensController < Decidim::Admin::ShareTokensController + include Concerns::AssemblyAdmin + + def resource + current_assembly + end + end + end + end +end +---- + +=== 3. Menu entry + +Add the menu entry for the share tokens in the participatory space admin view. In Decidim we do this in the `menu.rb` file for each participatory space: + +[source,ruby] +---- +Decidim.menu :admin_assembly_menu do |menu| + ... + menu.add_item :assembly_share_tokens, + I18n.t("menu.share_tokens", scope: "decidim.admin"), + decidim_admin_assemblies.assembly_share_tokens_path(current_participatory_space), + active: is_active_link?(decidim_admin_assemblies.assembly_share_tokens_path(current_participatory_space)), + icon_name: "share-line", + if: allowed_to?(:read, :share_tokens, current_participatory_space:) + ... +end +---- + +=== 4. Model -In order to implement it in other participatory spaces, you should: +Ensure your participatory space model includes the `Decidim::ShareableWithToken` module and implements the `shareable_url` method: -1. Add the `share_tokens` CRUD routes in your `admin_engine.rb` file: +[source,ruby] +---- +module Decidim + class Assembly < ApplicationRecord + ... + include Decidim::ShareableWithToken + ... + def shareable_url(share_token) + EngineRouter.main_proxy(self).assembly_url(self, share_token: share_token.token) + end + ... + end +end +---- + +=== 5. Permissions + +Add the permissions logic to the participatory space controller in the `permissions.rb` file: + +For admin controllers: + +[source,ruby] +---- +allow! if permission_action.subject == :share_tokens +---- + +For frontend controllers: [source,ruby] ---- +token = context[:share_token] + +return unless token.present? + +allow! if Decidim::ShareToken.use!(token_for: current_assembly, token: token) +---- + +== Implementation for components + +Components all inherit from `Decidim::Component`, so they already have the `Decidim::ShareableWithToken` module included. But you still need to do some steps: + +=== 1. Routes + +Add the `share_tokens` CRUD routes in your `admin_engine.rb` file: + +[source,ruby] +---- +scope "/assemblies/:assembly_slug" do + ... resources :components do ... - resources :share_tokens, except: [:show] + resources :component_share_tokens, except: [:show], path: "share_tokens", as: "share_tokens" ... + end +end ---- -2. Add the controller for the participatory space, it only requires to inherit from `Decidim::Admin::ShareTokensController`: +=== 2. Controller + +Add the controller for the component, it only requires to inherit from `Decidim::Admin::ShareTokensController` and define the `resource` method to return the component: [source,ruby] ---- @@ -59,10 +176,24 @@ module Decidim # This controller allows sharing unpublished things. # It is targeted for customizations for sharing unpublished things that lives under # an assembly. - class ShareTokensController < Decidim::Admin::ShareTokensController + class ComponentShareTokensController < Decidim::Admin::ShareTokensController include Concerns::AssemblyAdmin + + def resource + @resource ||= current_participatory_space.components.find(params[:component_id]) + end end end end end ---- + +=== 3. Permissions + +Similarly, add the same permissions logic to the component controller in the `permissions.rb` file as for participatory spaces. + + +== Other implementations + +You can implement share tokens for any other model by following the same steps as for participatory spaces and components. +In that case, however, you might have to override some methods from the `Decidim::Admin::ShareTokensController` to adapt them to your model (check the source code for details). \ No newline at end of file