show the password confirmation modal again on changes to user account

pull/6346/head
Jens Ulferts 6 years ago
parent 0f176e398e
commit 6f1b830b77
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 56
      app/views/my/account.html.erb
  2. 100
      frontend/legacy/app/components/modals/request-for-confirmation/request-for-confirmation.directive.ts
  3. 3
      frontend/src/app/components/modals/request-for-confirmation/password-confirmation.modal.ts
  4. 4
      frontend/src/app/components/op-modals/op-modal.component.ts
  5. 16
      frontend/src/app/modules/plugins/plugin-context.ts

@ -33,36 +33,38 @@ See docs/COPYRIGHT.rdoc for more details.
<%= toolbar title: l(:label_profile) %>
<%= error_messages_for 'user' %>
<%= password_confirmation_form_for @user,
as: :user,
url: { action: 'account' },
builder: ::TabularFormBuilder,
lang: current_language,
html: { id: 'my_account_form', class: '-wide-labels' } do |f| %>
<section class="form--section">
<div class="form--field">
<label class="form--label" for="username">
<%= User.human_attribute_name(:login) %>
</label>
<div class="form--field-container">
<%= @user.login %>
<%= activate_angular_js do %>
<%= password_confirmation_form_for @user,
as: :user,
url: { action: 'account' },
builder: ::TabularFormBuilder,
lang: current_language,
html: { id: 'my_account_form', class: '-wide-labels' } do |f| %>
<section class="form--section">
<div class="form--field">
<label class="form--label" for="username">
<%= User.human_attribute_name(:login) %>
</label>
<div class="form--field-container">
<%= @user.login %>
</div>
</div>
</div>
<div class="form--field"><%= f.text_field :firstname, required: true, container_class: '-middle' %></div>
<div class="form--field"><%= f.text_field :lastname, required: true, container_class: '-middle' %></div>
<div class="form--field"><%= f.text_field :mail, required: true, container_class: '-middle' %></div>
<div class="form--field"><%= f.text_field :firstname, required: true, container_class: '-middle' %></div>
<div class="form--field"><%= f.text_field :lastname, required: true, container_class: '-middle' %></div>
<div class="form--field"><%= f.text_field :mail, required: true, container_class: '-middle' %></div>
<%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %>
<div class="form--field"><%= pref_fields.check_box :hide_mail %></div>
<% end %>
<%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %>
<div class="form--field"><%= pref_fields.check_box :hide_mail %></div>
<% end %>
<%= call_hook(:view_my_account, user: @user, form: f) %>
<%= call_hook(:view_my_account, user: @user, form: f) %>
<%= render partial: 'customizable/field',
collection: @user.custom_field_values.select(&:editable?),
as: :value,
locals: { form: f, input_size: :middle } %>
</section>
<%= render partial: 'customizable/field',
collection: @user.custom_field_values.select(&:editable?),
as: :value,
locals: { form: f, input_size: :middle } %>
</section>
<%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %>
<%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %>
<% end %>
<% end %>

@ -0,0 +1,100 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 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-2013 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 doc/COPYRIGHT.rdoc for more details.
// ++
import IAugmentedJQuery = angular.IAugmentedJQuery;
import {openprojectLegacyModule} from "../../../openproject-legacy-app";
export class RequestForConfirmationController {
// Allow original form submission after dialog was closed
private passwordConfirmed = false;
private opModalService:any;
private passwordConfirmationModal:any;
constructor(readonly $element:IAugmentedJQuery,
readonly $timeout:ng.ITimeoutService,
readonly $scope:ng.IScope) {
window.OpenProject.pluginContext.valuesPromise().then((context) => {
this.opModalService = context.services.opModalService;
this.passwordConfirmationModal = context.classes.modals.passwordConfirmation;
});
}
public $onInit() {
this.$element.submit((evt) => {
if (!this.passwordConfirmed) {
evt.preventDefault();
this.openConfirmationDialog();
}
});
}
public openConfirmationDialog() {
const confirmModal = this.opModalService.show(this.passwordConfirmationModal);
confirmModal.openingEvent.subscribe((modal:any) => {
console.log('open event');
setTimeout(() => {
//HACK: need to trigger an angular digest in order to have the
//modal template be evaluated. Without it, the onInit will not be run.
jQuery('#request_for_confirmation_password').click();
}, 0);
});
confirmModal.closingEvent.subscribe((modal:any) => {
if (modal.confirmed) {
this.appendPassword(modal.password_confirmation!);
this.$element.trigger('submit');
}
});
}
/**
* Post the confirmation to the endpoint
*/
private appendPassword(value:string) {
angular.element('<input>').attr({
type: 'hidden',
name: '_password_confirmation',
value: value
}).appendTo(this.$element);
this.passwordConfirmed = true;
}
}
function requestForConfirmation() {
return {
restrict: 'AC',
scope: {},
bindToController: true,
controller: RequestForConfirmationController,
controllerAs: '$ctrl',
};
}
openprojectLegacyModule
.directive('requestForConfirmation', requestForConfirmation);

@ -57,7 +57,8 @@ export class PasswordConfirmationModal extends ConfirmDialogModal implements OnI
}
}
public onOpen() {
public onOpen(modalElement:JQuery) {
super.onOpen(modalElement);
this.passwordConfirmationField.nativeElement.focus();
}

@ -19,6 +19,8 @@ export abstract class OpModalComponent implements OnInit, OnDestroy {
/** Closing event called from the service when closing this modal */
public closingEvent = new EventEmitter<this>();
public openingEvent = new EventEmitter<this>();
constructor(public locals:OpModalLocalsMap, readonly elementRef:ElementRef) {
}
@ -28,6 +30,7 @@ export abstract class OpModalComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.closingEvent.complete();
this.openingEvent.complete();
}
/**
@ -45,6 +48,7 @@ export abstract class OpModalComponent implements OnInit, OnDestroy {
}
public onOpen(modalElement:JQuery) {
this.openingEvent.emit();
}
protected get afterFocusOn():JQuery {

@ -5,6 +5,8 @@ import {ConfirmDialogService} from "core-components/modals/confirm-dialog/confir
import {I18nService} from "core-app/modules/common/i18n/i18n.service";
import {ExternalQueryConfigurationService} from "core-components/wp-table/external-configuration/external-query-configuration.service";
import {HalResourceService} from "core-app/modules/hal/services/hal-resource.service";
import {PasswordConfirmationModal} from "../../components/modals/request-for-confirmation/password-confirmation.modal";
import {OpModalService} from "../../components/op-modals/op-modal.service";
/**
* Plugin context bridge for plugins outside the CLI compiler context
@ -19,12 +21,20 @@ export class OpenProjectPluginContext {
// Common services referencable by index
public readonly services = {
hooks: this.injector.get<HookService>(HookService),
notifications: this.injector.get<NotificationsService>(NotificationsService),
confirmDialog: this.injector.get<ConfirmDialogService>(ConfirmDialogService),
externalQueryConfiguration: this.injector.get<ExternalQueryConfigurationService>(ExternalQueryConfigurationService),
halResource: this.injector.get<HalResourceService>(HalResourceService),
i18n: this.injector.get<I18nService>(I18nService)
hooks: this.injector.get<HookService>(HookService),
i18n: this.injector.get<I18nService>(I18nService),
notifications: this.injector.get<NotificationsService>(NotificationsService),
opModalService: this.injector.get<OpModalService>(OpModalService)
};
// Random collection of classes needed outside of angular
public readonly classes = {
modals: {
passwordConfirmation: PasswordConfirmationModal
}
};
// Hooks

Loading…
Cancel
Save