diff --git a/Procfile.dev b/Procfile.dev index f1fd0e71bc..60c25648b2 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,3 @@ web: bundle exec rails server -p 3000 -b ${HOST:="127.0.0.1"} --environment ${RAILS_ENV:="development"} -legacy: cd frontend && RAILS_ENV=${RAILS_ENV:="development"} npm run legacy-webpack-watch angular: npm run serve worker: bundle exec rake jobs:work diff --git a/app/helpers/angular_helper.rb b/app/helpers/angular_helper.rb index d308f7e834..27b6837ee1 100644 --- a/app/helpers/angular_helper.rb +++ b/app/helpers/angular_helper.rb @@ -34,17 +34,4 @@ module AngularHelper options[:class] = options.fetch(:class, '') + ' op-angular-component' tag(component, options) end - - def activate_angular_js(type = :div, options = {}, &block) - content_for(:header_tags) do - javascript_include_tag 'bundles/openproject-legacy-app' - end - - if block_given? - merged_options = options.merge('ng-app': 'OpenProjectLegacy') - content_tag(type, merged_options, &block) - else - 'ng-app="OpenProjectLegacy"'.html_safe - end - end end diff --git a/app/views/account/register.html.erb b/app/views/account/register.html.erb index 48ca2557ee..b6010ab770 100644 --- a/app/views/account/register.html.erb +++ b/app/views/account/register.html.erb @@ -27,11 +27,9 @@ See docs/COPYRIGHT.rdoc for more details. ++#%> -<%= activate_angular_js do %> -
- <% @user ||= User.new %> - <%= render partial: '/account/register' %> -
-<% end %> +
+ <% @user ||= User.new %> + <%= render partial: '/account/register' %> +
diff --git a/app/views/attribute_help_texts/_tab.html.erb b/app/views/attribute_help_texts/_tab.html.erb index 50ebf7ca58..e865cfcfee 100644 --- a/app/views/attribute_help_texts/_tab.html.erb +++ b/app/views/attribute_help_texts/_tab.html.erb @@ -29,7 +29,7 @@ See docs/COPYRIGHT.rdoc for more details. <% entries = @texts_by_type[tab[:name]] || [] %> <% if entries.any? %> - <%= activate_angular_js :div, class: 'generic-table--container' do %> +
@@ -91,7 +91,7 @@ See docs/COPYRIGHT.rdoc for more details.
- <% end %> +
<% else %> <%= no_results_box %> <% end %> diff --git a/app/views/my/account.html.erb b/app/views/my/account.html.erb index 1aada67732..70eda9dddb 100644 --- a/app/views/my/account.html.erb +++ b/app/views/my/account.html.erb @@ -33,38 +33,36 @@ See docs/COPYRIGHT.rdoc for more details. <%= toolbar title: l(:label_profile) %> <%= error_messages_for 'user' %> -<%= activate_angular_js do %> - <%= password_confirmation_form_for @user, - as: :user, - url: { action: 'update_account' }, - builder: ::TabularFormBuilder, - lang: current_language, - html: { id: 'my_account_form', class: '-wide-labels' } do |f| %> -
-
- -
- <%= @user.login %> -
+<%= password_confirmation_form_for @user, + as: :user, + url: { action: 'update_account' }, + builder: ::TabularFormBuilder, + lang: current_language, + html: { id: 'my_account_form', class: '-wide-labels' } do |f| %> +
+
+ +
+ <%= @user.login %>
-
<%= f.text_field :firstname, required: true, container_class: '-middle' %>
-
<%= f.text_field :lastname, required: true, container_class: '-middle' %>
-
<%= f.text_field :mail, required: true, container_class: '-middle' %>
+
+
<%= f.text_field :firstname, required: true, container_class: '-middle' %>
+
<%= f.text_field :lastname, required: true, container_class: '-middle' %>
+
<%= f.text_field :mail, required: true, container_class: '-middle' %>
- <%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %> -
<%= pref_fields.check_box :hide_mail %>
- <% end %> + <%= fields_for :pref, @user.pref, builder: TabularFormBuilder, lang: current_language do |pref_fields| %> +
<%= pref_fields.check_box :hide_mail %>
+ <% 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 } %> -
+ <%= render partial: 'customizable/field', + collection: @user.custom_field_values.select(&:editable?), + as: :value, + locals: { form: f, input_size: :middle } %> +
- <%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %> - <% end %> + <%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %> <% end %> diff --git a/app/views/users/_mail_notifications.html.erb b/app/views/users/_mail_notifications.html.erb index c39fce50bf..9fe4d7193b 100644 --- a/app/views/users/_mail_notifications.html.erb +++ b/app/views/users/_mail_notifications.html.erb @@ -36,22 +36,21 @@ See docs/COPYRIGHT.rdoc for more details. <%= initialize_hide_sections_with [{key: 'notified_projects'}], active_sections %> -<%= activate_angular_js do %>
<%= styled_label_tag "user_mail_notification", t(:'user.settings.mail_notifications') %>
- - <%= styled_select_tag 'user[mail_notification]', - options_for_select(user_mail_notification_options(@user), - @user.mail_notification), - container_class: '-wide' %> + <%= styled_select_tag 'user[mail_notification]', + options_for_select(user_mail_notification_options(@user), @user.mail_notification), + container_class: '-wide' %> +
- +
<% @user.projects.each do |project| %> @@ -68,7 +67,7 @@ See docs/COPYRIGHT.rdoc for more details. <%= t(:'user.settings.mail_project_explanaition') %>
- +
<%= styled_label_tag 'self_notified', t(:'user.settings.mail_self_notified') %> @@ -78,4 +77,3 @@ See docs/COPYRIGHT.rdoc for more details.
-<% end %> diff --git a/app/views/users/deletion_info.html.erb b/app/views/users/deletion_info.html.erb index 94f90941d4..9f910c4da3 100644 --- a/app/views/users/deletion_info.html.erb +++ b/app/views/users/deletion_info.html.erb @@ -28,43 +28,41 @@ See docs/COPYRIGHT.rdoc for more details. ++#%> <% html_title(l(:label_administration), "#{ l('account.deletion_info.heading', name: @user.name)}") -%> -<%= activate_angular_js do %> - <%= labelled_tabular_form_for( - :user, - url: user_path(@user), - html: { - method: :delete, class: 'confirm_required form danger-zone', - data: password_confirmation_data_attribute - }) do %> -
-
-

- <%= l('account.deletion_info.heading', name: "#{h(@user.name)}").html_safe %> -

+<%= labelled_tabular_form_for( + :user, + url: user_path(@user), + html: { + method: :delete, class: 'confirm_required request-for-confirmation form danger-zone', + data: password_confirmation_data_attribute + }) do %> +
+
+

+ <%= l('account.deletion_info.heading', name: "#{h(@user.name)}").html_safe %> +

-

- <%= l("account.deletion_info.login_consequences.#{User.current == @user ? 'self' : 'other'}") %> -

+

+ <%= l("account.deletion_info.login_consequences.#{User.current == @user ? 'self' : 'other'}") %> +

-

- <%= l("account.deletion_info.data_consequences.#{User.current == @user ? 'self' : 'other'}") %> -

-

- - <%= l("account.deletion_info.info.#{User.current == @user ? 'self' : 'other'}") %> -

-

- <%= l("account.deletion_info.login_verification.#{User.current == @user ? 'self' : 'other'}", - name: "#{h(@user.login)}").html_safe %> -

-
- - <%= styled_button_tag '', class: '-highlight', disabled: true do - concat content_tag :i, '', class: 'button--icon icon-delete' - concat content_tag :span, l(:button_delete), class: 'button--text' - end %> -
-
-
- <% end %> +

+ <%= l("account.deletion_info.data_consequences.#{User.current == @user ? 'self' : 'other'}") %> +

+

+ + <%= l("account.deletion_info.info.#{User.current == @user ? 'self' : 'other'}") %> +

+

+ <%= l("account.deletion_info.login_verification.#{User.current == @user ? 'self' : 'other'}", + name: "#{h(@user.login)}").html_safe %> +

+
+ + <%= styled_button_tag '', class: '-highlight', disabled: true do + concat content_tag :i, '', class: 'button--icon icon-delete' + concat content_tag :span, l(:button_delete), class: 'button--text' + end %> +
+
+
<% end %> diff --git a/bin/setup_dev b/bin/setup_dev index 700a30996b..0ec8b2dede 100755 --- a/bin/setup_dev +++ b/bin/setup_dev @@ -22,15 +22,10 @@ try 'npm install --no-shrinkwrap >> log/setup_dev.log' echo "Linking plugin modules" try 'bundle exec rake openproject:plugins:register_frontend >> log/setup_dev.log' -printf "Building legacy webpack bundle ... " -try 'npm run legacy-webpack >> log/setup_dev.log' -echo "done." - echo "---------------------------------------" echo "Done. Now start the following services" echo '- Rails server `RAILS_ENV=development ./bin/rails s`' echo '- Angular CLI: `npm run serve`' -echo '- (Optional, only if you work on frontend/legacy): `npm run legacy-webpack-watch`' echo "" echo 'You can also run `foreman start -f Procfile.dev` to run all the above on a single terminal.' diff --git a/frontend/doc/API.md b/frontend/doc/API.md deleted file mode 100644 index a3dcfeea8d..0000000000 --- a/frontend/doc/API.md +++ /dev/null @@ -1,66 +0,0 @@ -API Handling -============ - -In general, the OpenProject Frontend uses _all_ of the existing working APIs to provide its functionality, as the current working version for `APIv3` is not feature complete. - -The documentation for these APIs and their capabilities: - -- [APIv3](http://opf.github.io/apiv3-doc/) - -To get a feeling for which API is used at which point, please refer to the `PathHelper` located at `./frontend/app/helpers/path-helper.js`. It is used throughout the application to centralize knowledge about paths. - -## HAL - -While having a `PathHelper` certainly helps, the long-term idea is to leverage the [HAL](http://stateless.co/hal_specification.html)-capabilities of the APIv3 (thereby leaving `v2` behind) to let any client discover the paths available for a resource by inspecting the responses from any given call. - -__Note:__ All responses from the APIv3 are thereby of `Content-Type: application/hal+json` and not just `Content-Type: application/json`. Some developer client tools sometimes get confused with that and will not interpret the formatting correctly. - -Example: - -```json -// calling a project - -{ - "_type": "Project", - "_links": { - "self": { - "href": "/api/v3/projects/1", - "title": "Lorem" - }, - "createWorkPackage": { - "href": "/api/v3/projects/1/work_packages/form", - "method": "post" - }, - "createWorkPackageImmediate": { - "href": "/api/v3/projects/1/work_packages", - "method": "post" - }, - "categories": { "href": "/api/v3/projects/1/categories" }, - "types": { "href": "/api/v3/projects/1/types" }, - "versions": { "href": "/api/v3/projects/1/versions" } - }, - "id": 1, - "identifier": "project_identifier", - "name": "Project example", - "description": "Lorem ipsum dolor sit amet" -} -``` - -The `Project` structure contains links to ressources associated. Given the knowledge about `_links`, one may easily infer the path from the response: - -```javascript -// some magic to retrieve an object, note that the services used are examplary -// and can not be found in the actual codebase -ProjectsService.getProject('project_identifier').then(function(project) { - var pathToVersions = project._links.versions.href; - // the VersionsService has knowledge about pathToVersions in its - // forProject method - VersionsService.forProject(project).then(function(versions) { - // versions should be the result of the call to pathtoVersions - console.log(versions); - }); -}); -``` - -This is in principle a very good concept to delegate responsibility of inference to the client and absolves the client of having to know each path in the application in advance. - diff --git a/frontend/doc/LEGACY.md b/frontend/doc/LEGACY.md deleted file mode 100644 index 413fe1eafc..0000000000 --- a/frontend/doc/LEGACY.md +++ /dev/null @@ -1,39 +0,0 @@ -# Additional information on Legacy frontend - -The legacy bundle is only used from Rails to add functionality to specific parts of the application. - -## Loading and bootstrapping the legacy AngularJS frontend - -To bootstrap the AngularJS frontend from Rails, use the `activate_angular_js` helper block: - -```html - -<%= activate_angular_js do %> - -
- Something rendered from Rails ... -
-
-<% end %> -``` - -The legacy frontend with AngularJS can be bootstrapped _with_ content contained within. This is not possible in Angular, -since the root component needs to be empty (or will be emptied during bootstrap). - -## Passing information and configuration from Rails to Angular - -There are three ways of passing information from Rails to `AngularJS`: - -1. Using tag attributes written directly to the DOM by the rendering process of Rails as in the example before. - -2. Using the `gon` gem - -This is included by all layouts in ``: - -```js -<%= nonced_javascript_tag do %> - <%= include_gon(need_tag: false) -%> -<% end %> -``` - -`gon` will provide arbitrary settings from Rails to all JavaScript functionality, including `AngularJS`. In an `angular` context a `ConfigurationService` is provided for picking up the settings. diff --git a/frontend/doc/README.md b/frontend/doc/README.md index b94111a5bc..72bc8e9ef9 100644 --- a/frontend/doc/README.md +++ b/frontend/doc/README.md @@ -7,16 +7,6 @@ from the previous frontend that cannot be converted to Angular. (Mainly because - **The Angular frontend** is located at `frontend/src` and uses the Angular CLI to compile and serve locally. -## Legacy frontend - -When developing, the legacy bundle can be watched with `npm run legacy-webpack-watch` in separate tab. -It will result in a single output bundle at `app/assets/javascripts/bundles/openproject-legacy-app.js`. -That bundle is loaded manually by templates in Rails whenever a legacy directive is used with `<%= activate_angular_js %>`. - -For production, this bundle is also produced when running the `rake assets:precompile` task. - -For more information, see [LEGACY](./LEGACY.md). - ## Angular frontend When developing, `npm run serve` will open a proxy server (webpack-dev-server) that will serve assets from memory. diff --git a/frontend/legacy/README.md b/frontend/legacy/README.md deleted file mode 100644 index 5e698a1034..0000000000 --- a/frontend/legacy/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# OpenProject Legacy AngularJS Frontend - -These directives are remains of the AngularJS frontend used solely for the purpose of -retaining functionality of embedded components in Rails templates. diff --git a/frontend/legacy/app/openproject-legacy-app.ts b/frontend/legacy/app/openproject-legacy-app.ts deleted file mode 100644 index 9cb45c7eee..0000000000 --- a/frontend/legacy/app/openproject-legacy-app.ts +++ /dev/null @@ -1,94 +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 {ExpressionService} from "../../common/expression.service"; -require('angular'); - -var angularDragula:any = require('angular-dragula'); -export const opTemplatesModule = angular.module('openproject.templates', []); -export const openprojectLegacyModule = angular.module('OpenProjectLegacy', [ - angularDragula(angular) -]); - -// Bootstrap app - -openprojectLegacyModule - .config([ - '$compileProvider', - '$httpProvider', - function($compileProvider:any, $httpProvider:any) { - - // Disable debugInfo outside development mode - $compileProvider.debugInfoEnabled(window.OpenProject.environment === 'development'); - - $httpProvider.defaults.headers.common['X-CSRF-TOKEN'] = jQuery( - 'meta[name=csrf-token]').attr('content'); - $httpProvider.defaults.headers.common['X-Authentication-Scheme'] = 'Session'; - // Add X-Requested-With for request.xhr? - $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; - // prepend a given base path to requests performed via $http - // - $httpProvider.interceptors.push(function($q:ng.IQService) { - return { - 'request': function(config:any) { - // OpenProject can run in a subpath e.g. https://mydomain/open_project. - // We append the path found as the base-tag value to all http requests - // to the server except: - // * when the path is already appended - // * when we are getting a template - if (!config.url.match('(^/templates|\\.html$|^' + window.appBasePath + ')')) { - config.url = window.appBasePath + (config.url as string); - } - - return config || $q.when(config); - } - }; - }); - } - ]) - .run([ - '$rootScope', - function($rootScope:any) { - // Set the escaping target of opening double curly braces - // This is what returned by rails-angular-xss when it discoveres double open curly braces - // See https://github.com/opf/rails-angular-xss for more information. - $rootScope.DOUBLE_LEFT_CURLY_BRACE = ExpressionService.UNESCAPED_EXPRESSION; - - // Mark the bootstrap has run for testing purposes. - document.body.classList.add('__ng-bootstrap-has-run'); - - $rootScope.$on('$stateChangeError', - function(event:JQueryEventObject){ - event.preventDefault(); - // transitionTo() promise will be rejected with - // a 'transition prevented' error - }); - } - ]); - diff --git a/frontend/legacy/postcss.config.js b/frontend/legacy/postcss.config.js deleted file mode 100644 index c83222405c..0000000000 --- a/frontend/legacy/postcss.config.js +++ /dev/null @@ -1,17 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var _ = require('lodash'); -var autoprefixer = require('autoprefixer'); - -var browsersListConfig = fs.readFileSync(path.join(__dirname, '..', 'browserslist'), 'utf8'); -var browsersList = _.filter(browsersListConfig.split('\n'), function (entry) { - return entry && entry.charAt(0) !== '#'; -}); - -module.exports = { - plugins: [ - autoprefixer({ - browsers: browsersList, cascade: false - }) - ] -} diff --git a/frontend/legacy/tsconfig.json b/frontend/legacy/tsconfig.json deleted file mode 100644 index 4efcf754a4..0000000000 --- a/frontend/legacy/tsconfig.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "compilerOptions": { - "target": "ES5", - "module": "es2015", - "moduleResolution": "node", - "removeComments": true, - "preserveConstEnums": true, - "sourceMap": true, - "noEmitOnError": false, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - // Increase strictness - // Enable strict once "use strict" errors can be resolved - // "strict": true; - "noImplicitAny": true, - "noImplicitThis": true, - "noImplicitReturns": true, - "strictFunctionTypes": true, - // Causes lots of errors in linked angularjs properties - "strictPropertyInitialization": false, - "noFallthroughCasesInSwitch": true, - "strictNullChecks": true, - "skipLibCheck": true, - "preserveSymlinks": true, - "baseUrl": ".", - "typeRoots": [ - "../node_modules/@types", - "./typings/open-project-legacy.typings.d.ts" - ], - "paths": { - "core-app/*": ["./app/*"], - "core-components/*": ["./app/components/*"] - } - }, - "compileOnSave": false, - "exclude": [ - "node_modules" - ] -} diff --git a/frontend/legacy/typings/open-project-legacy.typings.d.ts b/frontend/legacy/typings/open-project-legacy.typings.d.ts deleted file mode 100644 index 758da8955b..0000000000 --- a/frontend/legacy/typings/open-project-legacy.typings.d.ts +++ /dev/null @@ -1,58 +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 * as TAngular from 'angular'; -import * as TLodash from 'lodash'; -import {InputState, State} from "reactivestates"; -import {GlobalI18n} from "../../src/app/modules/common/i18n/i18n.service"; - -export interface IPluginContext { - classes:any; - services:any; - bootstrap(element:HTMLElement):void; -} - -declare global { - interface Window { - appBasePath:string; - OpenProject:{ - guardedLocalStorage(key:string, newValue?:string):string|void, - environment:string, - getPluginContext():Promise, - pluginContext:InputState - }; - } - const angular:typeof TAngular; - const _:typeof TLodash; - const I18n:GlobalI18n; -} diff --git a/frontend/legacy/webpack.config.js b/frontend/legacy/webpack.config.js deleted file mode 100644 index 200c6665fd..0000000000 --- a/frontend/legacy/webpack.config.js +++ /dev/null @@ -1,227 +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. -// ++ - -var webpack = require('webpack'); -var path = require('path'); -var _ = require('lodash'); -//var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; - -var CleanWebpackPlugin = require('clean-webpack-plugin'); -var MiniCssExtractPlugin = require('mini-css-extract-plugin'); -var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); - -var mode = 'production'; -var production = true; -if (process.env['RAILS_ENV'] == 'development') { - mode = 'development'; - production = false; -} - -var debug_output = (!production || !!process.env['OP_FRONTEND_DEBUG_OUTPUT']); - -var node_root = path.resolve(__dirname, '..', 'node_modules'); -var output_root = path.resolve(__dirname, '..', '..', 'app', 'assets', 'javascripts'); -var bundle_output = path.resolve(output_root, 'bundles'); - -var loaders = [ - { - test: /\.tsx?$/, - use: [ - { - loader: 'ts-loader', - options: { - logLevel: 'info', - configFile: path.resolve(__dirname, 'tsconfig.json') - } - } - ] - }, - { - test: /\.css$/, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader', - 'postcss-loader' - ] - }, - { - test: /\.png$/, - use: [ - { - loader: 'url-loader', - options: { - limit: '100000', - mimetype: 'image/png' - } - } - ] - }, - { - test: /\.gif$/, - use: ['file-loader'] - }, - { - test: /\.jpg$/, - use: ['file-loader'] - }, -]; - -loaders.push({ - test: /\.html$/, - use: [ - { - loader: 'ngtemplate-loader', - options: { - module: 'OpenProjectLegacy', - relativeTo: path.resolve(__dirname, './app') - } - }, - { - loader: 'html-loader', - options: { - minimize: false - } - } - ] -}); - -function getLegacyWebpackConfig() { - var config = { - mode: mode, - - devtool: 'source-map', - - context: path.resolve(__dirname, 'app'), - - entry: { - 'legacy-app': './openproject-legacy-app' - }, - - output: { - filename: 'openproject-[name].js', - path: bundle_output, - publicPath: '/assets/bundles/' - }, - - module: { - rules: loaders - }, - - resolve: { - // Don't map symlinks from dynamically linked plugins to their real paths - symlinks: false, - - modules: [ - path.resolve(__dirname, '..', 'node_modules') - ], - - extensions: ['.ts', '.tsx', '.js'], - - // Allow empty import without extension - // enforceExtension: true, - - alias: { - 'angular': path.resolve(node_root, 'angular', 'angular.min.js'), - 'angular-dragula': path.resolve(node_root, 'angular-dragula', 'dist', 'angular-dragula.min.js'), - 'core-app': path.resolve(__dirname, 'app'), - 'core-components': path.resolve(__dirname, 'app', 'components'), - } - }, - - externals: { - "I18n": "I18n", - "_": "_", - }, - - optimization: { - splitChunks: { - cacheGroups: { - common: { - name: "common", - chunks: "initial", - minChunks: 2 - } - } - } - }, - - plugins: [ - // Define modes for debug output - new webpack.DefinePlugin({ - DEBUG: !!debug_output, - PRODUCTION: !!production - }), - - // Clean the output directory - new CleanWebpackPlugin(['bundles'], { - root: output_root, - verbose: true, - exclude: ['openproject-vendors.js'] - }), - - // new BundleAnalyzerPlugin(), - - new MiniCssExtractPlugin({ - // Options similar to the same options in webpackOptions.output - // both options are optional - filename: "openproject-[name].css", - chunkFilename: "[id].css" - }), - ] - }; - - if (production) { - console.log("Applying webpack.optimize plugins for production."); - // Add compression and optimization plugins - // to the webpack build. - config.plugins.push( - new webpack.LoaderOptionsPlugin({ - // Let loaders know that we're in minification mode - minimize: true - }) - ); - - config.optimization.minimizer = [ - // we specify a custom UglifyJsPlugin here to get source maps in production - new UglifyJsPlugin({ - cache: true, - parallel: true, - uglifyOptions: { - compress: true, - mangle: false, - ecma: 5, - }, - sourceMap: true - }) - ]; - } - - return config; -} - -module.exports = getLegacyWebpackConfig; diff --git a/frontend/package.json b/frontend/package.json index 8b7628a0f9..bf908d47fe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -134,14 +134,12 @@ }, "scripts": { "prebuild": "./scripts/link_plugin_placeholder.js", - "build": "ng build --prod && npm run legacy-webpack", + "build": "ng build --prod", "preserve": "./scripts/link_plugin_placeholder.js", "serve": "node --max_old_space_size=8096 ./node_modules/@angular/cli/bin/ng serve --public-host http://localhost:4200", "pretest": "./scripts/link_plugin_placeholder.js", "test": "ng test --watch=false", "tslint_typechecks": "./node_modules/.bin/tslint -p . -c tslint_typechecks.json", - "generate-typings": "tsc -d -p src/tsconfig.app.json", - "legacy-webpack": "./node_modules/.bin/webpack --colors --config legacy/webpack.config.js", - "legacy-webpack-watch": "RAILS_ENV=development ./node_modules/.bin/webpack --config legacy/webpack.config.js --display-error-details --watch --colors --cache --debug" + "generate-typings": "tsc -d -p src/tsconfig.app.json" } } diff --git a/frontend/src/app/components/wp-edit/wp-notification.service.ts b/frontend/src/app/components/wp-edit/wp-notification.service.ts index de2293b47f..75247f75c3 100644 --- a/frontend/src/app/components/wp-edit/wp-notification.service.ts +++ b/frontend/src/app/components/wp-edit/wp-notification.service.ts @@ -115,7 +115,7 @@ export class WorkPackageNotificationService { public retrieveError(response:unknown):ErrorResource|unknown { // we try to detect what we got, this may either be an HttpErrorResponse, // some older XHR response object or a string - let errorBody:any; + let errorBody:any = response; // Angular http response have an error body attribute if (response instanceof HttpErrorResponse) { @@ -131,7 +131,7 @@ export class WorkPackageNotificationService { return this.halResourceService.createHalResourceOfClass(ErrorResource, errorBody); } - return response; + return errorBody; } protected handleErrorResponse(errorResource:any, workPackage?:WorkPackageResource) { diff --git a/frontend/src/app/modules/common/hide-section/show-section-dropdown.component.ts b/frontend/src/app/modules/common/hide-section/show-section-dropdown.component.ts index cf0383902d..fa8f919a49 100644 --- a/frontend/src/app/modules/common/hide-section/show-section-dropdown.component.ts +++ b/frontend/src/app/modules/common/hide-section/show-section-dropdown.component.ts @@ -1,61 +1,56 @@ -// //-- 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. -// //++ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2018 the OpenProject Foundation (OPF) // -// import {HideSectionService} from "./hide-section.service"; +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. // -// export class ShowSectionDropdownComponent { -// public optValue:string; // value of option for which hide-section should be visible -// public hideSecWithName:string; // section-name of hide-section +// 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 // -// constructor(protected HideSectionService:HideSectionService, -// private $element:ng.IAugmentedJQuery) { -// } +// 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. // -// $onInit() { -// this.$element.change(event => { -// let selectedOption = jQuery("option:selected", event.target); +// 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. // -// if (selectedOption.val() !== this.optValue) { -// this.HideSectionService.hide(this.hideSecWithName); -// } -// else { -// this.HideSectionService.show({key: this.hideSecWithName, label: ""}); -// } -// }); -// } -// } +// 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. // -// openprojectLegacyModule.component('showSectionDropdown', { -// template: '', -// transclude: true, -// controller: ShowSectionDropdownComponent, -// bindings: { -// optValue: "@", -// hideSecWithName: "@" -// } -// }); +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import {HideSectionService} from "./hide-section.service"; +import {Component, ElementRef, OnInit} from "@angular/core"; + +@Component({ + selector: 'show-section-dropdown', + template: '' +}) +export class ShowSectionDropdownComponent implements OnInit { + public optValue:string; // value of option for which hide-section should be visible + public hideSecWithName:string; // section-name of hide-section + + constructor(private HideSectionService:HideSectionService, + private elementRef:ElementRef) { + } + + ngOnInit() { + const target = jQuery(this.elementRef.nativeElement).prev(); + target.on('change', event => { + let selectedOption = jQuery("option:selected", event.target); + + if (selectedOption.val() !== this.optValue) { + this.HideSectionService.hide(this.hideSecWithName); + } else { + this.HideSectionService.show(this.hideSecWithName); + } + }); + } +} diff --git a/frontend/src/app/modules/common/openproject-common.module.ts b/frontend/src/app/modules/common/openproject-common.module.ts index 41909cff5d..f929da17e2 100644 --- a/frontend/src/app/modules/common/openproject-common.module.ts +++ b/frontend/src/app/modules/common/openproject-common.module.ts @@ -92,6 +92,7 @@ import {HideSectionLinkComponent} from "core-app/modules/common/hide-section/hid import {HideSectionService} from "core-app/modules/common/hide-section/hide-section.service"; import {RemoteFieldUpdaterComponent} from 'core-app/modules/common/remote-field-updater/remote-field-updater.component'; import {AutofocusDirective} from "core-app/modules/common/autofocus/autofocus.directive"; +import {ShowSectionDropdownComponent} from "core-app/modules/common/hide-section/show-section-dropdown.component"; export function bootstrapModule(injector:Injector) { return () => { @@ -242,6 +243,7 @@ export function bootstrapModule(injector:Injector) { PersistentToggleComponent, AutocompleteSelectDecorationComponent, HideSectionLinkComponent, + ShowSectionDropdownComponent, AddSectionDropdownComponent, RemoteFieldUpdaterComponent, diff --git a/frontend/src/app/modules/common/remote-field-updater/remote-field-updater.component.ts b/frontend/src/app/modules/common/remote-field-updater/remote-field-updater.component.ts index a60296b80f..28826c867c 100644 --- a/frontend/src/app/modules/common/remote-field-updater/remote-field-updater.component.ts +++ b/frontend/src/app/modules/common/remote-field-updater/remote-field-updater.component.ts @@ -64,7 +64,7 @@ export class RemoteFieldUpdaterComponent implements OnInit { // special cases where the tab code is not correctly recognized (undefined). // Thus the focus is kept on the first element of the result list. let keyCodesArray = [keyCodes.TAB, keyCodes.ENTER, keyCodes.SHIFT]; - if (event.which && keyCodesArray.indexOf(event.which) === -1) { + if (event.type === 'change' || (event.which && keyCodesArray.indexOf(event.which) === -1)) { this.updater(); } }, 500)); @@ -106,7 +106,7 @@ export class RemoteFieldUpdaterComponent implements OnInit { // Replace the given target this.target.html(response); } else { - _.each(response.data, (val:string, selector:string) => { + _.each(response, (val:string, selector:string) => { jQuery('#' + selector).html(val); }); } diff --git a/modules/costs/app/assets/stylesheets/costs/costs.sass b/modules/costs/app/assets/stylesheets/costs/costs.sass index 3a18cef9b7..2046851034 100644 --- a/modules/costs/app/assets/stylesheets/costs/costs.sass +++ b/modules/costs/app/assets/stylesheets/costs/costs.sass @@ -31,3 +31,7 @@ table.list.members .progress-bar .inner-progress.done background-color: #E1B9B9 + +.budget-row-template, +.subform-row-template + display: none diff --git a/modules/costs/app/controllers/cost_objects_controller.rb b/modules/costs/app/controllers/cost_objects_controller.rb index 94374f56fd..aaeda5fb0d 100644 --- a/modules/costs/app/controllers/cost_objects_controller.rb +++ b/modules/costs/app/controllers/cost_objects_controller.rb @@ -187,7 +187,7 @@ class CostObjectsController < ApplicationController cost_type = CostType.where(id: params[:cost_type_id]).first if cost_type && params[:units].present? - volume = BigDecimal.new(Rate.clean_currency(params[:units])) rescue 0.0 + volume = BigDecimal(Rate.clean_currency(params[:units])) rescue 0.0 @costs = (volume * cost_type.rate_at(params[:fixed_date]).rate rescue 0.0) @unit = volume == 1.0 ? cost_type.unit : cost_type.unit_plural else diff --git a/modules/costs/app/helpers/costlog_helper.rb b/modules/costs/app/helpers/costlog_helper.rb index 81d47fb42f..4d7bec538f 100644 --- a/modules/costs/app/helpers/costlog_helper.rb +++ b/modules/costs/app/helpers/costlog_helper.rb @@ -49,7 +49,7 @@ module CostlogHelper value = value.strip value.gsub!(t(:currency_delimiter), '') if value.include?(t(:currency_delimiter)) && value.include?(t(:currency_separator)) value.gsub(',', '.') - BigDecimal.new(value) + BigDecimal(value) end def to_currency_with_empty(rate) diff --git a/modules/costs/app/models/variable_cost_object.rb b/modules/costs/app/models/variable_cost_object.rb index fdc770b67e..8ad4ee63d7 100644 --- a/modules/costs/app/models/variable_cost_object.rb +++ b/modules/costs/app/models/variable_cost_object.rb @@ -52,11 +52,11 @@ class VariableCostObject < CostObject end def material_budget - @material_budget ||= material_budget_items.visible_costs.inject(BigDecimal.new('0.0000')) { |sum, i| sum += i.costs } + @material_budget ||= material_budget_items.visible_costs.inject(BigDecimal('0.0000')) { |sum, i| sum += i.costs } end def labor_budget - @labor_budget ||= labor_budget_items.visible_costs.inject(BigDecimal.new('0.0000')) { |sum, i| sum += i.costs } + @labor_budget ||= labor_budget_items.visible_costs.inject(BigDecimal('0.0000')) { |sum, i| sum += i.costs } end def spent @@ -66,7 +66,7 @@ class VariableCostObject < CostObject def spent_material @spent_material ||= begin if cost_entries.blank? - BigDecimal.new('0.0000') + BigDecimal('0.0000') else cost_entries.visible_costs(User.current, project).sum("CASE WHEN #{CostEntry.table_name}.overridden_costs IS NULL THEN @@ -80,7 +80,7 @@ class VariableCostObject < CostObject def spent_labor @spent_labor ||= begin if time_entries.blank? - BigDecimal.new('0.0000') + BigDecimal('0.0000') else time_entries.visible_costs(User.current, project).sum("CASE WHEN #{TimeEntry.table_name}.overridden_costs IS NULL THEN diff --git a/modules/costs/app/views/cost_objects/_form.html.erb b/modules/costs/app/views/cost_objects/_form.html.erb index 7a50116941..5628320d43 100644 --- a/modules/costs/app/views/cost_objects/_form.html.erb +++ b/modules/costs/app/views/cost_objects/_form.html.erb @@ -36,10 +36,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <% if @cost_object.kind == "VariableCostObject" -%> - <%= activate_angular_js do %> <%= render partial: 'cost_objects/subform/material_budget_subform' %> <%= render partial: 'cost_objects/subform/labor_budget_subform' %> - <% end %> <%- end %>
diff --git a/modules/costs/app/views/cost_objects/_list.html.erb b/modules/costs/app/views/cost_objects/_list.html.erb index 71234f851a..bca86a003f 100644 --- a/modules/costs/app/views/cost_objects/_list.html.erb +++ b/modules/costs/app/views/cost_objects/_list.html.erb @@ -74,7 +74,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - <% total_budget = BigDecimal.new("0"); labor_budget = BigDecimal.new("0"); material_budget = BigDecimal.new("0"); spent = BigDecimal.new("0") %> + <% total_budget = BigDecimal("0"); labor_budget = BigDecimal("0"); material_budget = BigDecimal("0"); spent = BigDecimal("0") %> <% cost_objects.each do |cost_object| %> <%= link_to cost_object.id, cost_object_path(cost_object.id) %> diff --git a/modules/costs/app/views/cost_objects/items/_material_budget_item.html.erb b/modules/costs/app/views/cost_objects/items/_material_budget_item.html.erb index ebaa8f2a6a..fa23065bb7 100644 --- a/modules/costs/app/views/cost_objects/items/_material_budget_item.html.erb +++ b/modules/costs/app/views/cost_objects/items/_material_budget_item.html.erb @@ -75,7 +75,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <%= cost_form.hidden_field :budget, value: material_budget_item.budget %> <% end %> "> - + <%= number_to_currency(material_budget_item.budget || material_budget_item.calculated_costs(@cost_object.fixed_date)) %> diff --git a/modules/costs/app/views/cost_objects/subform/_material_budget_subform.html.erb b/modules/costs/app/views/cost_objects/subform/_material_budget_subform.html.erb index 8bf42601c7..fadd5dddfe 100644 --- a/modules/costs/app/views/cost_objects/subform/_material_budget_subform.html.erb +++ b/modules/costs/app/views/cost_objects/subform/_material_budget_subform.html.erb @@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <% template_object = @cost_object.material_budget_items.build(cost_type: CostType.default) %> + update-url="<%= url_for(action: :update_material_budget_item, project_id: @project.id) %>">
<%= VariableCostObject.human_attribute_name(:material_budget) %>
diff --git a/modules/costs/app/views/cost_types/edit.html.erb b/modules/costs/app/views/cost_types/edit.html.erb index 53056f66de..ba76a90a4d 100644 --- a/modules/costs/app/views/cost_types/edit.html.erb +++ b/modules/costs/app/views/cost_types/edit.html.erb @@ -31,7 +31,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <%= toolbar title: CostType.model_name.human %> -<%= activate_angular_js do %> <%= labelled_tabular_form_for @cost_type do |f| %> <%= error_messages_for 'cost_type' %> @@ -112,4 +111,3 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% end %> -<% end %> diff --git a/modules/costs/app/views/costlog/edit.html.erb b/modules/costs/app/views/costlog/edit.html.erb index fdaa42a99c..5695a5a50f 100644 --- a/modules/costs/app/views/costlog/edit.html.erb +++ b/modules/costs/app/views/costlog/edit.html.erb @@ -89,7 +89,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. <% if @cost_entry.cost_type.nil? %> <%= f.text_field :units, size: 6, required: true, container_class: '-slim' %> <% else %> - <% suffix = @cost_enngtry.units == 1 ? @cost_entry.cost_type.unit : @cost_entry.cost_type.unit_plural %> + <% suffix = @cost_entry.units == 1 ? @cost_entry.cost_type.unit : @cost_entry.cost_type.unit_plural %> <%= f.text_field :units, size: 6, required: true, @@ -116,7 +116,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= t(:help_override_rate) %> <% end %> - ng +
diff --git a/modules/costs/app/views/hourly_rates/edit.html.erb b/modules/costs/app/views/hourly_rates/edit.html.erb index 6990d940cb..8bb0aca16c 100644 --- a/modules/costs/app/views/hourly_rates/edit.html.erb +++ b/modules/costs/app/views/hourly_rates/edit.html.erb @@ -29,7 +29,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

<%= t(:label_current_default_rate) %>: <%= number_to_currency(default_rate.rate)%>

<% end %> -<%= activate_angular_js do %> <%= labelled_tabular_form_for @user, url: {action: 'update', project_id: @project}, method: :put do |f| %> <%= back_url_hidden_field_tag %> @@ -90,4 +89,3 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% end %> -<% end %> diff --git a/modules/costs/config/routes.rb b/modules/costs/config/routes.rb index 4fef9d5246..abc0dced2e 100644 --- a/modules/costs/config/routes.rb +++ b/modules/costs/config/routes.rb @@ -22,8 +22,8 @@ OpenProject::Application.routes.draw do resources :cost_entries, controller: 'costlog', only: [:new, :create] resources :cost_objects, only: [:new, :create, :index] do - get :update_labor_budget_item, on: :collection - get :update_material_budget_item, on: :collection + match :update_labor_budget_item, on: :collection, via: %i[get post] + match :update_material_budget_item, on: :collection, via: %i[get post] end resources :hourly_rates, only: [:show, :edit, :update] do diff --git a/modules/costs/frontend/legacy-app/components/budget/cost-budget-subform.directive.ts b/modules/costs/frontend/legacy-app/components/budget/cost-budget-subform.directive.ts deleted file mode 100644 index 3940bec116..0000000000 --- a/modules/costs/frontend/legacy-app/components/budget/cost-budget-subform.directive.ts +++ /dev/null @@ -1,82 +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 {PluginContextService} from "core-app/services/plugin-context.service"; - -/*eslint no-eval: "error"*/ -export class CostBudgetSubformController { - - // Container for rows - private container: ng.IAugmentedJQuery; - - // Template for new rows to insert, is rendered with INDEX placeholder - private rowTemplate: string; - - // Current row index - public rowIndex: number; - - // subform item count as output by rails - public itemCount: string; - - // Updater URL for the rows contained here - public updateUrl: string; - - constructor(public $element:ng.IAugmentedJQuery, - public $http:ng.IHttpService, - public pluginContext:PluginContextService, - private $scope:ng.IScope, - private $compile:any) { - - - } - - -} - -function costsBudgetSubform():any { - return { - restrict: 'E', - scope: { - updateUrl: '@', - itemCount: '@' - }, - link: (scope:ng.IScope, - element:ng.IAugmentedJQuery, - attr:ng.IAttributes, - ctrl:any) => { - const template = element.find('.budget-row-template'); - ctrl.rowTemplate = template[0].outerHTML; - template.remove(); - }, - bindToController: true, - controller: CostBudgetSubformController, - controllerAs: '$ctrl' - }; -} - -angular.module('OpenProjectLegacy').directive('costsBudgetSubform', costsBudgetSubform); diff --git a/modules/costs/frontend/module/augment/cost-budget-subform.augment.service.ts b/modules/costs/frontend/module/augment/cost-budget-subform.augment.service.ts index f5ffd4ab13..cd68906703 100644 --- a/modules/costs/frontend/module/augment/cost-budget-subform.augment.service.ts +++ b/modules/costs/frontend/module/augment/cost-budget-subform.augment.service.ts @@ -31,15 +31,20 @@ import {HttpClient} from '@angular/common/http'; import {WorkPackageNotificationService} from "core-app/components/wp-edit/wp-notification.service"; @Injectable() -export class CostSubformAugmentService { +export class CostBudgetSubformAugmentService { constructor(private wpNotifications:WorkPackageNotificationService, private http:HttpClient) { + } + + listen() { jQuery('costs-budget-subform').each((i, match) => { let el = jQuery(match); const container = el.find('.budget-item-container'); - const template:string = el.find('.budget-row-template')[0].outerHTML; + const templateEl = el.find('.budget-row-template'); + templateEl.detach(); + const template = templateEl[0].outerHTML; let rowIndex = parseInt(el.attr('item-count') as string); // Refresh row on changes @@ -57,7 +62,10 @@ export class CostSubformAugmentService { // Add new row handler el.find('.budget-add-row').click((evt) => { evt.preventDefault(); - container.append(template.replace(/INDEX/g, rowIndex.toString())); + let row = jQuery(template.replace(/INDEX/g, rowIndex.toString())); + row.show(); + row.removeClass('budget-row-template'); + container.append(row); rowIndex += 1; return false; }); diff --git a/modules/costs/frontend/module/augment/cost-subform.augment.service.ts b/modules/costs/frontend/module/augment/cost-subform.augment.service.ts index ac23af3004..4f473cc283 100644 --- a/modules/costs/frontend/module/augment/cost-subform.augment.service.ts +++ b/modules/costs/frontend/module/augment/cost-subform.augment.service.ts @@ -36,7 +36,10 @@ export class CostSubformAugmentService { let el = jQuery(match); const container = el.find('.subform-container'); - const template = el.find('.subform-row-template')[0].outerHTML; + + const templateEl = el.find('.subform-row-template'); + templateEl.detach(); + const template = templateEl[0].outerHTML; let rowIndex = parseInt(el.attr('item-count')!); el.on('click', '.delete-row-button,.delete-budget-item', (evt:any) => { @@ -47,7 +50,10 @@ export class CostSubformAugmentService { // Add new row handler el.find('.add-row-button,.wp-inline-create--add-link').click((evt:any) => { evt.preventDefault(); - container.append(template.replace(/INDEX/g, rowIndex.toString())); + let row = jQuery(template.replace(/INDEX/g, rowIndex.toString())); + row.show(); + row.removeClass('subform-row-template'); + container.append(row); rowIndex += 1; container.find('.costs-date-picker').datepicker(); diff --git a/modules/costs/frontend/module/augment/planned-costs-form.ts b/modules/costs/frontend/module/augment/planned-costs-form.ts index 4cd85a7f53..30abaf2c75 100644 --- a/modules/costs/frontend/module/augment/planned-costs-form.ts +++ b/modules/costs/frontend/module/augment/planned-costs-form.ts @@ -34,7 +34,7 @@ export class PlannedCostsFormAugment { static listen() { jQuery(document).on('click', '.costs--edit-planned-costs-btn', (evt) => { - const form = jQuery(evt.target).closest('cost-unit-subform'); + const form = jQuery(evt.target as any).closest('cost-unit-subform') as JQuery; new PlannedCostsFormAugment(form); }); } @@ -42,7 +42,7 @@ export class PlannedCostsFormAugment { constructor(public $element:JQuery) { this.objId = this.$element.attr('obj-id')!; this.objName = this.$element.attr('obj-name')!; - this.obj = jQuery(this.objId); + this.obj = jQuery(`#${this.objId}`) as any; this.makeEditable('#' + this.objId, this.objName); } @@ -77,7 +77,7 @@ export class PlannedCostsFormAugment {
-
'; +
diff --git a/modules/costs/frontend/module/main.ts b/modules/costs/frontend/module/main.ts index d6fc066ba0..8ee1ca095c 100644 --- a/modules/costs/frontend/module/main.ts +++ b/modules/costs/frontend/module/main.ts @@ -32,8 +32,9 @@ import {BudgetResource} from './hal/resources/budget-resource'; import {multiInput} from 'reactivestates'; import {CostSubformAugmentService} from "./augment/cost-subform.augment.service"; import {PlannedCostsFormAugment} from "core-app/modules/plugins/linked/openproject-costs/augment/planned-costs-form"; +import {CostBudgetSubformAugmentService} from "core-app/modules/plugins/linked/openproject-costs/augment/cost-budget-subform.augment.service"; -export function initializeCostsPlugin() { +export function initializeCostsPlugin(injector:Injector) { return () => { window.OpenProject.getPluginContext().then((pluginContext:OpenProjectPluginContext) => { pluginContext.services.editField.extendFieldType('select', ['Budget']); @@ -78,6 +79,9 @@ export function initializeCostsPlugin() { // Augment previous cost-subforms new CostSubformAugmentService(); PlannedCostsFormAugment.listen(); + + const budgetSubform = injector.get(CostBudgetSubformAugmentService); + budgetSubform.listen(); }); }; } @@ -86,6 +90,7 @@ export function initializeCostsPlugin() { @NgModule({ providers: [ { provide: APP_INITIALIZER, useFactory: initializeCostsPlugin, deps: [Injector], multi: true }, + CostBudgetSubformAugmentService, ], }) export class PluginModule { diff --git a/modules/my_project_page/app/views/my_projects_overviews/index.html.erb b/modules/my_project_page/app/views/my_projects_overviews/index.html.erb index d1b39534b4..973bf754dc 100644 --- a/modules/my_project_page/app/views/my_projects_overviews/index.html.erb +++ b/modules/my_project_page/app/views/my_projects_overviews/index.html.erb @@ -37,7 +37,6 @@ See doc/COPYRIGHT.md for more details. <% end %> <% end %> -<%= activate_angular_js do %>
<% top_fields.each do |f| %> <%= rendered_field f %> @@ -48,4 +47,3 @@ See doc/COPYRIGHT.md for more details. <% end %>
-<% end %> diff --git a/modules/my_project_page/app/views/my_projects_overviews/page_layout.html.erb b/modules/my_project_page/app/views/my_projects_overviews/page_layout.html.erb index 53f99d0801..5d1f9ef523 100644 --- a/modules/my_project_page/app/views/my_projects_overviews/page_layout.html.erb +++ b/modules/my_project_page/app/views/my_projects_overviews/page_layout.html.erb @@ -73,8 +73,5 @@ See doc/COPYRIGHT.md for more details.
-<%= activate_angular_js do %> - - -<% end %> + diff --git a/modules/reporting_engine/lib/reporting_engine/patches/big_decimal_patch.rb b/modules/reporting_engine/lib/reporting_engine/patches/big_decimal_patch.rb index 53df32ce07..b5257a8538 100644 --- a/modules/reporting_engine/lib/reporting_engine/patches/big_decimal_patch.rb +++ b/modules/reporting_engine/lib/reporting_engine/patches/big_decimal_patch.rb @@ -18,23 +18,21 @@ #++ module ReportingEngine::Patches::BigDecimalPatch - module BigDecimal - ::BigDecimal.send :include, self + class BigDecimal def to_d; self end end - module Integer - ::Integer.send :include, self + class Integer def to_d; to_f.to_d end end - module String - ::String.send :include, self - def to_d; ::BigDecimal.new(self) end + class String + def to_d + BigDecimal self + end end - module NilClass - ::NilClass.send :include, self + class NilClass def to_d; 0 end end end diff --git a/modules/two_factor_authentication/app/views/two_factor_authentication/my/two_factor_devices/index.html.erb b/modules/two_factor_authentication/app/views/two_factor_authentication/my/two_factor_devices/index.html.erb index 5cd6fff0ad..337b65781c 100644 --- a/modules/two_factor_authentication/app/views/two_factor_authentication/my/two_factor_devices/index.html.erb +++ b/modules/two_factor_authentication/app/views/two_factor_authentication/my/two_factor_devices/index.html.erb @@ -26,7 +26,6 @@
<% end %> -<%= activate_angular_js do %> <% breadcrumb_paths(t(:label_my_account), t('two_factor_authentication.label_two_factor_authentication')) -%> <%= toolbar title: t('two_factor_authentication.label_two_factor_authentication') do %>
  • @@ -65,4 +64,3 @@ class: 'button' end %> -<% end %> diff --git a/modules/two_factor_authentication/app/views/users/_two_factor_authentication_admin.html.erb b/modules/two_factor_authentication/app/views/users/_two_factor_authentication_admin.html.erb index 8ce45fcf96..87c8975294 100644 --- a/modules/two_factor_authentication/app/views/users/_two_factor_authentication_admin.html.erb +++ b/modules/two_factor_authentication/app/views/users/_two_factor_authentication_admin.html.erb @@ -31,7 +31,6 @@ <% end %> -<%= activate_angular_js do %>
    <%= toolbar title: t('two_factor_authentication.label_devices') do %> <% unless devices.empty? %> @@ -58,4 +57,3 @@ <%= cell ::TwoFactorAuthentication::Devices::TableCell, devices, admin_table: true || @user != User.current %>
    -<% end %> diff --git a/package.json b/package.json index 10735cbd61..93917b1f72 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,7 @@ "test": "cd frontend && npm test && cd ..", "tslint_typechecks": "cd frontend && npm run tslint_typechecks && cd ..", "serve": "cd frontend && npm run serve", - "serve-public": "cd frontend && ./node_modules/.bin/ng serve --host 0.0.0.0", - "legacy-webpack": "cd frontend && npm run legacy-webpack && cd ..", - "legacy-webpack-watch": "cd frontend && npm run legacy-webpack-watch" + "serve-public": "cd frontend && ./node_modules/.bin/ng serve --host 0.0.0.0" }, "private": true, "engines": {