diff --git a/app/views/account/_password_login_form.html.erb b/app/views/account/_password_login_form.html.erb
index ae6ffcfcfa..9882655d9e 100644
--- a/app/views/account/_password_login_form.html.erb
+++ b/app/views/account/_password_login_form.html.erb
@@ -67,14 +67,14 @@ See docs/COPYRIGHT.rdoc for more details.
<%= link_to t(:label_register),
'',
title: t(:label_register),
- class: 'modal-wrapper--activation-link',
- id: 'registration-modal--activation-link--login-form' %>
+ class: 'modal-wrapper--activation-link' %>
<% end %>
<% end %>
-
+
<% @user ||= User.new %>
<%= render partial: '/account/register' %>
-
+
diff --git a/app/views/account/register.html.erb b/app/views/account/register.html.erb
index a17f70cfee..48ca2557ee 100644
--- a/app/views/account/register.html.erb
+++ b/app/views/account/register.html.erb
@@ -28,8 +28,10 @@ See docs/COPYRIGHT.rdoc for more details.
++#%>
<%= activate_angular_js do %>
-
+
<% @user ||= User.new %>
<%= render partial: '/account/register' %>
-
+
<% end %>
diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb
index 5344adb816..738108ebe9 100644
--- a/app/views/layouts/base.html.erb
+++ b/app/views/layouts/base.html.erb
@@ -146,11 +146,12 @@ See docs/COPYRIGHT.rdoc for more details.
<%= render_flash_messages %>
<% if show_onboarding_modal? %>
-
+
<%= render partial: '/onboarding/starting_video_modal' %>
-
+
<% end %>
<%= l(:label_content) %>
diff --git a/app/views/onboarding/_menu_item.html.erb b/app/views/onboarding/_menu_item.html.erb
index bf6b137774..45540204f8 100644
--- a/app/views/onboarding/_menu_item.html.erb
+++ b/app/views/onboarding/_menu_item.html.erb
@@ -26,8 +26,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See docs/COPYRIGHT.rdoc for more details.
++#%>
-
+
<%= link_to l(:label_introduction_video),
'',
@@ -35,4 +36,4 @@ See docs/COPYRIGHT.rdoc for more details.
class: 'modal-wrapper--activation-link' %>
<%= render partial: '/onboarding/starting_video_modal' %>
-
+
diff --git a/frontend/legacy/app/components/modals/modal-wrapper/modal-wrapper.directive.ts b/frontend/legacy/app/components/modals/modal-wrapper/modal-wrapper.directive.ts
deleted file mode 100644
index 384963e944..0000000000
--- a/frontend/legacy/app/components/modals/modal-wrapper/modal-wrapper.directive.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-// -- 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 {openprojectLegacyModule} from "../../../openproject-legacy-app";
-
-export class ModalWrapperController {
- public activationLinkId:string;
- public iframeSelector = '.iframe-target-wrapper';
-
- private modalBody:string;
- public modalClassName = 'ngdialog-theme-openproject';
-
- protected opModalService:any;
- protected dynamicContentModal:any;
-
- constructor(protected $element:ng.IAugmentedJQuery,
- protected $attrs:ng.IAttributes) {
-
- window.OpenProject.pluginContext.valuesPromise().then((context) => {
- this.opModalService = context.services.opModalService;
- this.dynamicContentModal = context.classes.modals.dynamicContent;
-
- // Find activation link
- var activationLink = $element.find('.modal-wrapper--activation-link');
- if (this.activationLinkId) {
- activationLink = jQuery(this.activationLinkId);
- }
-
- // Set modal class name
- if ($attrs['modalClassName']) {
- this.modalClassName = $attrs['modalClassName'];
- }
-
- // Set template from wrapped element
- const wrappedElement = $element.find('.modal-wrapper--content');
- this.modalBody = wrappedElement.html();
-
- if ($attrs['iframeUrl']) {
- this.appendIframe($attrs['iframeUrl']);
- }
-
- if (!!$attrs['initialize']) {
- this.initialize();
- }
- else {
- activationLink.click(() => this.initialize());
- }
-
- });
- }
-
- public initialize() {
- let modal = this.opModalService.show(this.dynamicContentModal, { modalBody: this.modalBody, modalClassName: this.modalClassName });
- modal.openingEvent.subscribe((modal:any) => {
- //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('.op-modal--modal-container').click();
- });
- }
-
- private appendIframe(url:string) {
- let subdom = angular.element(this.modalBody);
- let iframe = angular.element('');
- iframe.attr('src', url);
-
- subdom.find(this.iframeSelector).append(iframe);
-
- this.modalBody = subdom.html();
- }
-}
-
-function modalWrapper():any {
- return {
- restrict: 'E',
- scope: {
- modalParams: '=',
- activationLinkId: '=?'
- },
- controller: ModalWrapperController,
- controllerAs: '$ctrl',
- bindToController: true,
- };
-}
-
-openprojectLegacyModule.directive('modalWrapper', modalWrapper);
diff --git a/frontend/src/app/angular4-modules.ts b/frontend/src/app/angular4-modules.ts
index 8f2d2f1860..f950168d22 100644
--- a/frontend/src/app/angular4-modules.ts
+++ b/frontend/src/app/angular4-modules.ts
@@ -207,6 +207,7 @@ import {OpenProjectFileUploadService} from "core-components/api/op-file-upload/o
import {AttributeHelpTextModal} from "./modules/common/help-texts/attribute-help-text.modal";
import {CopyToClipboardDirective} from 'core-app/modules/common/copy-to-clipboard/copy-to-clipboard.directive';
import {WorkPackageEmbeddedTableEntryComponent} from "core-components/wp-table/embedded/wp-embedded-table-entry.component";
+import {ModalWrapperAugmentService} from "core-app/globals/augmenting/modal-wrapper.augment.service";
@NgModule({
imports: [
@@ -306,6 +307,10 @@ import {WorkPackageEmbeddedTableEntryComponent} from "core-components/wp-table/e
// External query configuration
ExternalQueryConfigurationService,
+
+ // Augmenting Rails
+ ModalWrapperAugmentService,
+
],
declarations: [
ConfirmFormSubmitController,
@@ -566,8 +571,11 @@ export function bootstrapOptional(appRef:ApplicationRef, ...elements:{ tagName:s
export function initializeServices(injector:Injector) {
return () => {
- const ExternalQueryConfiguration:ExternalQueryConfigurationService = injector.get(ExternalQueryConfigurationService);
- const global = (window as any);
+ const ExternalQueryConfiguration = injector.get(ExternalQueryConfigurationService);
+ const ModalWrapper = injector.get(ModalWrapperAugmentService);
+
+ // Setup modal wrapping
+ ModalWrapper.setupListener();
// Setup query configuration listener
ExternalQueryConfiguration.setupListener();
diff --git a/frontend/src/app/globals/augmenting/modal-wrapper.augment.service.ts b/frontend/src/app/globals/augmenting/modal-wrapper.augment.service.ts
new file mode 100644
index 0000000000..1695382846
--- /dev/null
+++ b/frontend/src/app/globals/augmenting/modal-wrapper.augment.service.ts
@@ -0,0 +1,104 @@
+// -- 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 {Inject, Injectable} from "@angular/core";
+import {DOCUMENT} from "@angular/common";
+import {DynamicContentModal} from "core-components/modals/modal-wrapper/dynamic-content.modal";
+import {OpModalService} from "core-components/op-modals/op-modal.service";
+
+const iframeSelector = '.iframe-target-wrapper';
+
+@Injectable()
+export class ModalWrapperAugmentService {
+
+ constructor(@Inject(DOCUMENT) protected documentElement:Document,
+ protected opModalService:OpModalService) {
+ }
+
+ /**
+ * Create initial listeners for Rails-rendered modals
+ */
+ public setupListener() {
+ const matches = this.documentElement.querySelectorAll('section[data-augmented-model-wrapper]');
+ for (let i = 0; i < matches.length; ++i) {
+ this.wrapElement(jQuery(matches[i]));
+ }
+ }
+
+ /**
+ * Wrap a section[data-augmented-modal-wrapper] element
+ */
+ public wrapElement(element:JQuery) {
+ // Find activation link
+ const activationLink = element.find('.modal-wrapper--activation-link');
+ const initializeNow = element.data('modalInitializeNow');
+
+ if (initializeNow) {
+ this.show(element);
+ } else {
+ activationLink.click((evt:JQueryEventObject) => {
+ this.show(element);
+ evt.preventDefault();
+ });
+ }
+ }
+
+ private show(element:JQuery) {
+ // Set modal class name
+ const modalClassName = element.data('modalClassName');
+ // Append CSP-whitelisted IFrame for onboarding
+ const iframeUrl = element.data('modalIframeUrl');
+
+ // Set template from wrapped element
+ const wrappedElement = element.find('.modal-wrapper--content');
+ let modalBody = wrappedElement.html();
+
+ if (iframeUrl) {
+ modalBody = this.appendIframe(modalBody, iframeUrl);
+ }
+
+ this.opModalService.show(
+ DynamicContentModal,
+ {
+ modalBody: modalBody,
+ modalClassName: modalClassName
+ }
+ );
+ }
+
+ private appendIframe(body:string, url:string) {
+ let subdom = jQuery(body);
+ let iframe = jQuery('');
+ iframe.attr('src', url);
+
+ subdom.find(iframeSelector).append(iframe);
+
+ return subdom.html();
+ }
+}