Skip to content

Commit 5d6f02b

Browse files
Add dashboard copy feature with security checks requiring editable permission #123
1 parent cc60b72 commit 5d6f02b

File tree

21 files changed

+303
-0
lines changed

21 files changed

+303
-0
lines changed

app/controllers/dashboards_controller.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ def new
5858
@dashboard = Dashboard.new project: @project,
5959
author: User.current
6060
@dashboard.dashboard_type = assign_dashboard_type
61+
if params[:copy].present?
62+
@copy_from = Dashboard.visible.find_by id: params[:copy]
63+
# Security: Only allow copying from dashboards the user can edit
64+
# because block settings may contain sensitive data (credentials, API keys)
65+
@copy_from = nil unless @copy_from&.editable?
66+
@dashboard.copy_from @copy_from
67+
end
6168
@allowed_projects = @dashboard.allowed_target_projects
6269
end
6370

@@ -78,6 +85,17 @@ def create
7885
@dashboard.dashboard_type = assign_dashboard_type
7986
@dashboard.role_ids = params[:dashboard][:role_ids] if params[:dashboard].present?
8087

88+
# Copy layout and settings from source dashboard if copying
89+
# Security: Only allow copying from dashboards the user can edit
90+
# because block settings may contain sensitive data (credentials, API keys)
91+
if params[:copy].present?
92+
copy_from = Dashboard.visible.find_by id: params[:copy]
93+
if copy_from&.editable?
94+
@dashboard.layout = copy_from.layout.deep_dup
95+
@dashboard.layout_settings = copy_from.layout_settings.deep_dup
96+
end
97+
end
98+
8199
@allowed_projects = @dashboard.allowed_target_projects
82100

83101
if @dashboard.save

app/models/dashboard.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,21 @@ def visible(user = User.current, **options)
139139
end
140140
end
141141

142+
def copy_from(source)
143+
return if source.blank?
144+
145+
dashboard = source.is_a?(Dashboard) ? source : Dashboard.find_by(id: source)
146+
return if dashboard.nil?
147+
148+
excluded_attrs = %w[id name author_id system_default locked created_at updated_at options]
149+
self.attributes = dashboard.attributes.dup.except(*excluded_attrs)
150+
# Deep copy layout and layout_settings from options
151+
self.layout = dashboard.layout.deep_dup if dashboard.layout.present?
152+
self.layout_settings = dashboard.layout_settings.deep_dup if dashboard.layout_settings.present?
153+
self.role_ids = dashboard.role_ids.dup if dashboard.visibility == VISIBILITY_ROLES
154+
self
155+
end
156+
142157
def initialize(attributes = nil, *args)
143158
super
144159
set_options_hash

app/views/dashboards/new.html.slim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
h2 = l :label_new_dashboard
22
= labelled_form_for [@project, @dashboard],
33
html: { multipart: true, id: 'dashboard-form' } do |f|
4+
- if @copy_from.present?
5+
= hidden_field_tag :copy, @copy_from.id
46
= render('form', f:)
57
= submit_tag l(:button_create)

app/views/projects/show.html.slim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
= link_to sprite_icon('edit', entity_headline(object_name: :label_dashboard, type: :edit)),
5353
edit_project_dashboard_path(@project, @dashboard),
5454
class: 'icon icon-edit'
55+
= link_to sprite_icon('copy', entity_headline(object_name: :label_dashboard, type: :copy)),
56+
new_project_dashboard_path(@project, copy: @dashboard.id),
57+
class: 'icon icon-copy'
5558

5659
- if @dashboard&.deletable?
5760
= delete_dashboard_link project_dashboard_path(@project, @dashboard), @dashboard

app/views/welcome/index.html.slim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
= link_to sprite_icon('edit', entity_headline(object_name: :label_dashboard, type: :edit)),
2323
edit_dashboard_path(@dashboard),
2424
class: 'icon icon-edit'
25+
= link_to sprite_icon('copy', entity_headline(object_name: :label_dashboard, type: :copy)),
26+
new_dashboard_path(copy: @dashboard.id),
27+
class: 'icon icon-copy'
2528
- if @dashboard&.deletable?
2629
= delete_dashboard_link dashboard_path(@dashboard), @dashboard
2730
= sidebar_action_toggle @dashboard_sidebar, @dashboard

config/locales/cs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ cs:
3535
alert_only_visible_by_admins: "Vidí je pouze uživatelé s oprávněními správce"
3636
alert_only_visible_by_yourself: "Vidíte jen vy"
3737
button_assign_to_me: "Přiřaďte mi"
38+
button_copy_object: "Kopírovat %{object_name}"
3839
button_merge: "Sloučení"
3940
disabled_modules_info: "Moduly, které by neměly být k dispozici pro výběr v rámci projektů. Pokud jsou tyto moduly již aktivovány ve stávajících projektech, musíte nejprve změnit a znovu uložit příslušná nastavení projektu."
4041
emoji_support_info_html: 'Převod emoji do textu. Activate this option if :heart:, :+1: etc are to be converted to emojis. In the <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" class="external">Emoji cheat sheet</a> the available emoji codes can be used.'

config/locales/de.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ de:
3535
alert_only_visible_by_admins: Kann nur von Benutzer mit Admin-Rechten gesehen werden
3636
alert_only_visible_by_yourself: Kann nur von Dir selbst gesehen werden
3737
button_assign_to_me: Mir zuweisen
38+
button_copy_object: "%{object_name} kopieren"
3839
button_merge: "Zusammenführen"
3940
disabled_modules_info: "Module, die nicht zur Auswahl innerhalb der Projekte zur Verfügung stehen sollen. Sind in bestehenden Projekten schon diese Module aktiviert worden, werden die Einstellungen für diese Projekte erst nach erneuten Abspeichern der jeweiligen Projekteinstellungen aktiv."
4041
emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" class="external">Emoji cheat sheet</a> können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.'

config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ en:
3535
alert_only_visible_by_admins: "Can only be seen by users with admin permissions"
3636
alert_only_visible_by_yourself: "Can only be seen by you"
3737
button_assign_to_me: Assign to me
38+
button_copy_object: "Copy %{object_name}"
3839
button_merge: "Merge"
3940
disabled_modules_info: "Modules which should not be available for selection within the projects. If these modules already activated in existing projects, you will have to change and re-save the respective project settings first."
4041
emoji_support_info_html: 'Convert emojis in text. Activate this option if :heart:, :+1: etc are to be converted to emojis. In the <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" class="external">Emoji cheat sheet</a> the available emoji codes can be used.'

config/locales/es.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ es:
3535
alert_only_visible_by_admins: "Sólo puede ser visto por usuarios con permisos de administrador"
3636
alert_only_visible_by_yourself: "Sólo puede ser visto por ti"
3737
button_assign_to_me: Asignar a mi mismo
38+
button_copy_object: "Copiar %{object_name}"
3839
button_merge: "Combinar"
3940
disabled_modules_info: "Módulos que no estarán disponibles para ser utilizados en los proyectos. Si estos módulos están actualmente activos en proyectos existentes, deberá modificar y guardar esos proyectos primero."
4041
emoji_support_info_html: 'Convertir emojis en texto. Activate this option if :heart:, :+1: etc are to be converted to emojis. In the <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" class="external">Emoji cheat sheet</a> the available emoji codes can be used.'

config/locales/fr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fr:
3535
alert_only_visible_by_admins: "Ne peut être vu que par les utilisateurs ayant les droits d'administrateur"
3636
alert_only_visible_by_yourself: "Ne peut être vu que par vous"
3737
button_assign_to_me: M'affecter
38+
button_copy_object: "Copier %{object_name}"
3839
button_merge: Fusionner
3940
disabled_modules_info: "Les modules qui ne devraient pas être disponibles pour la sélection dans le cadre des projets. Si ces modules sont déjà activés dans des projets existants, vous devez d'abord modifier et sauvegarder à nouveau les paramètres projet respectifs."
4041
emoji_support_info_html: 'Convertir les emojis en texte. Activate this option if :heart:, :+1: etc are to be converted to emojis. In the <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" class="external">Emoji cheat sheet</a> the available emoji codes can be used.'

0 commit comments

Comments
 (0)