Fix adding and removing blocks on my page with CSP

pull/6386/head
Oliver Günther 6 years ago
parent 970b276d2d
commit ca4de761de
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 139
      app/assets/javascripts/my_page.js
  2. 6
      app/controllers/my_controller.rb
  3. 8
      app/views/my/_block.html.erb
  4. 4
      app/views/my/add_block.html.erb
  5. 11
      app/views/my/add_block.js.erb
  6. 39
      app/views/my/page_layout.html.erb
  7. 4
      app/views/my/remove_block.js.erb
  8. 1
      config/initializers/assets.rb
  9. 1
      config/locales/js-en.yml
  10. 7
      frontend/src/app/modules/common/notifications/notifications.service.ts

@ -0,0 +1,139 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
jQuery(document).ready(function($) {
// Add block
$('.my-page--block-form').submit(function (evt) {
var form = $(this);
var block = $('#block-options').val();
evt.preventDefault();
jQuery
.ajax(
{
url: form.attr('action'),
type: 'POST',
dataType: 'html',
data: {
block: block
}
})
.fail(function(error, text) {
jQuery(window).trigger(
'op:notifications:add',
{
type: 'error',
message: I18n.t('js.error.cannot_save_changes_with_message', { error: _.get(error, 'responseText', 'Internal error') })
}
);
})
.done(function(response) {
// Add partial to the top container.
jQuery("#top").prepend(response);
// Revert options selection back to the default one.
jQuery('#block-options option').first().prop('selected', true);
// Disable the option for this block in the blocks-to-add-to-page dropdown.
jQuery('#block-options option[value="' + block + '"]').attr("disabled", "true");
});
return false;
});
// Remove block
$('.my-page--container')
.on('click', '.my-page--remove-block', function (evt) {
evt.preventDefault();
var link = $(this);
var block = link.data('name');
var dasherized = link.data('dasherized');
jQuery
.ajax(
{
url: link.attr('href'),
type: 'POST',
dataType: 'html',
data: {
block: block
}
})
.fail(function(error) {
jQuery(window).trigger(
'op:notifications:add',
{
type: 'error',
message: I18n.t('js.error.cannot_save_changes_with_message', { error: _.get(error, 'responseText', 'Internal error') })
}
);
})
.done(function() {
jQuery("#block-" + dasherized).remove();
// Enable the option for this block in the blocks-to-add-to-page dropdown.
jQuery('#block-options option[value="' + block + '"]').removeAttr("disabled");
});
return false;
});
// Canonical list of containers that will exchange draggable elements.
var containers = jQuery('.dragula-container').toArray();
var drake = dragula(containers);
// On 'el' drop, we fire an Ajax request to persist the order chosen by
// the user. Actual ordering details are handled on the server.
drake.on('drop', function(el, target, source, sibling){
var url = "<%= my_order_blocks_url %>";
// Array of target ordered children after this drop.
var target_ordered_children = jQuery(target).find('.block-wrapper').map(function(){
return jQuery(this).data('name');
}).get();
// Array of source ordered children after this drop.
var source_ordered_children = jQuery(source).find('.block-wrapper').map(function(){
return jQuery(this).data('name');
}).get();
// We send the source, target, and the new order of the children in both
// containers to the server.
jQuery.ajax({
url: url,
type: 'POST',
data: {
target: jQuery(target).attr('id'),
source: jQuery(source).attr('id'),
target_ordered_children: target_ordered_children,
source_ordered_children: source_ordered_children
}
});
});
});

@ -181,7 +181,7 @@ class MyController < ApplicationController
# Add a block to the user's page at the top.
# params[:block] : id of the block to add
#
# Responds with a JS layout.
# Responds with a HTML block.
def add_block
@block = params[:block].to_s.underscore
@ -202,6 +202,8 @@ class MyController < ApplicationController
# Save user preference.
@user.pref[:my_page_layout] = layout
@user.pref.save
render layout: false
end
# Remove a block from the user's `my` page.
@ -219,6 +221,8 @@ class MyController < ApplicationController
# Save user preference.
@user.pref[:my_page_layout] = layout
@user.pref.save
head 200, content_type: "text/html"
end
def order_blocks

@ -31,9 +31,11 @@ See docs/COPYRIGHT.rdoc for more details.
<% content_for "#{block_name}-remove-block" do %>
<div class="box-actions">
<%= link_to '', { action: "remove_block", block: block_name },
class: "icon icon-close close-icon", title: l(:button_remove_widget),
remote: true
<%= link_to '',
{ action: "remove_block", block: block_name },
class: "icon icon-close close-icon my-page--remove-block",
data: { name: block_name, dasherized: block_name.dasherize },
title: l(:button_remove_widget)
%>
</div>
<% end %>

@ -0,0 +1,4 @@
<%= render partial: 'block',
locals: { user: @user,
block_name: @block
} %>

@ -1,11 +0,0 @@
// Add partial to the top container.
jQuery("#top").prepend("<%= j (render partial: 'block',
locals: { user: @user,
block_name: @block
}) %>");
// Revert options selection back to the default one.
jQuery('#block-options option[value="--<%= t(:button_add) %>--"]').prop('selected', true);
// Disable the option for this block in the blocks-to-add-to-page dropdown.
jQuery('#block-options option[value="<%= @block %>"]').attr("disabled", "true");

@ -26,45 +26,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details.
++#%>
<%= nonced_javascript_tag do %>
jQuery(document).ready(function($) {
// Canonical list of containers that will exchange draggable elements.
var containers = jQuery('.dragula-container').toArray();
var drake = dragula(containers);
// On 'el' drop, we fire an Ajax request to persist the order chosen by
// the user. Actual ordering details are handled on the server.
drake.on('drop', function(el, target, source, sibling){
var url = "<%= my_order_blocks_url %>";
// Array of target ordered children after this drop.
var target_ordered_children = jQuery(target).find('.block-wrapper').map(function(){
return jQuery(this).data('name');
}).get();
// Array of source ordered children after this drop.
var source_ordered_children = jQuery(source).find('.block-wrapper').map(function(){
return jQuery(this).data('name');
}).get();
// We send the source, target, and the new order of the children in both
// containers to the server.
jQuery.ajax({
url: url,
type: 'POST',
data: {
target: jQuery(target).attr('id'),
source: jQuery(source).attr('id'),
target_ordered_children: target_ordered_children,
source_ordered_children: source_ordered_children
}
});
});
});
<% end %>
<%= javascript_include_tag 'my_page' %>
<%= toolbar title: l(:label_my_page) do %>
<%= styled_form_tag({ action: "add_block" }, remote: true) do %>
<%= styled_form_tag({ action: "add_block" }, class: 'my-page--block-form') do %>
<% options = "<option>--#{t(:button_add)}--</option>"
.html_safe
.concat(options_for_select(@block_options)) %>

@ -1,4 +0,0 @@
jQuery("#block-<%= @block.dasherize %>").remove();
// Enable the option for this block in the blocks-to-add-to-page dropdown.
jQuery('#block-options option[value="<%= @block %>"]').removeAttr("disabled");

@ -18,6 +18,7 @@ OpenProject::Application.configure do
locales/*.js
members_form.js
members_select_boxes.js
my_page.js
new_user.js
project/responsible_attribute.js
project/description_handling.js

@ -83,6 +83,7 @@ en:
description_subwork_package: "Child of work package #%{id}"
error:
internal: "An internal error has occurred."
cannot_save_changes_with_message: "Cannot save your changes due to the following error: %{error}"
query_saving: "The query could not be saved."
filter:
description:

@ -30,12 +30,14 @@ import {ConfigurationService} from 'core-app/modules/common/config/configuration
import {input, State} from 'reactivestates';
import {Injectable} from '@angular/core';
import {UploadInProgress} from "core-components/api/op-file-upload/op-file-upload.service";
import {OpQueryConfigurationTriggerEvent} from "core-components/wp-table/external-configuration/external-query-configuration.constants";
export function removeSuccessFlashMessages() {
jQuery('.flash.notice').remove();
}
export type NotificationType = 'success'|'error'|'warning'|'info'|'upload';
export const OPNotificationEvent = 'op:notifications:add';
export interface INotification {
message:string;
@ -51,6 +53,11 @@ export class NotificationsService {
private stack = input<INotification[]>([]);
constructor(readonly configurationService:ConfigurationService) {
jQuery(window)
.on(OPNotificationEvent,
(event:JQueryEventObject, notification:INotification) => {
this.add(notification)
});
}
/**

Loading…
Cancel
Save