diff --git a/.rubocop.yml b/.rubocop.yml index f0f4c177b4..a8408a729c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -125,7 +125,7 @@ LineEndConcatenation: Enabled: false LineLength: - Max: 100 + Max: 130 MethodLength: Enabled: false diff --git a/.travis.yml b/.travis.yml index 4e5cb32012..250d5753e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,8 +110,6 @@ notifications: on_failure: always on_pull_requests: false rooms: - # core - - secure: "jTMREkFmtNRxgHYN7PQpCpqbJ4icGPfTKAUuY/UvjAgn7Lv0VRqkhikBPSJs+zzwTBcHIY+xMLLF5fRCU+gcq/1mNbl8jNpXIO9JSxqRkxUlKYGP5q1hYrPKrhRGquzGQSR/q1XiTmIDkZ17yl8PQa+5c9NH9GrMx6xMzD5GWOQ=" # CE - secure: "mQqyZRjOix72MAAcjIanPOCfzMlQOqMhYOEd+6SNCcK7nb9ku0rPtFYWtifvp3+ajA5YKVJZ/W2qRn+Flw99zg8SfhPBV89SALXapUnjuZW0rcPexP0vrQW/AR6176DG3+WQOM8BFOYmN1yuGsz7YZK3xZo7yPp8XHKwzWoEYS6BOEUyfyE5T8dGJGqIKwpGEnnpBJf+CVsXeX56xg6wL+9CPVIEDb7IcrPoYQ5K6Kh9gq+7Ube7I7lbsSpdm1TAS4si7G9A6IaJ4WD1QvnUDrajGz2IM/bkP0zs50kGSrbagm707QMC8P4aLzJ64aUOfziyeYA1BSiDl5dZCUc3/dJtjMkUoRlmErwe6x8N5mwn6iVQ5LtWQeJIiy+wrBvjnghkl2/B7z3iZvEsDl6Uip6Dtv7ccmSskkh0ulsEykxWdAsRddpSaEYHv5pqex9aVgIMljM8o3DFTQclhVyGM0nrryDiMjhkCR5spighp/uR7nHEULlmJMGilyjqy6iB3/S6O1CXY110jpgYEAqhxkY9VA1hYYVLsoKV90uUGlnJcQRqQPoP3q1OqUGlRT5Y2ydM2FTBvsBGB+bhMQgZhZORqlkfIYzWDoUs4v6vM9v16XsP6TeXzNd0BPCZ+WamtOZBpwvSXZl85lqIEFONZ12uIr1R/sPsv6bQndN6gLk=" # EE diff --git a/app/assets/stylesheets/content/_notifications.sass b/app/assets/stylesheets/content/_notifications.sass index 542d250e90..51599d47c8 100644 --- a/app/assets/stylesheets/content/_notifications.sass +++ b/app/assets/stylesheets/content/_notifications.sass @@ -353,6 +353,11 @@ $nm-upload-box-padding: rem-calc(15) rem-calc(25) &::before color: $nm-color-error-icon !important +a.impaired--empty-link, +a.impaired--empty-link:link, +.impaired--empty-link + @include varprop(color, body-font-color) + a.notification-box--target-link cursor: pointer text-decoration: underline diff --git a/app/assets/stylesheets/content/_work_packages.sass b/app/assets/stylesheets/content/_work_packages.sass index 5bd631fc6b..673f837c6f 100644 --- a/app/assets/stylesheets/content/_work_packages.sass +++ b/app/assets/stylesheets/content/_work_packages.sass @@ -28,6 +28,7 @@ // Table editing styles @import work_packages/table_content +@import work_packages/table_hierarchy // Specific field styles @import work_packages/inplace_editing/edit_fields diff --git a/app/assets/stylesheets/content/work_packages/_table_hierarchy.sass b/app/assets/stylesheets/content/work_packages/_table_hierarchy.sass new file mode 100644 index 0000000000..8321e8a1be --- /dev/null +++ b/app/assets/stylesheets/content/work_packages/_table_hierarchy.sass @@ -0,0 +1,40 @@ + +// Style the hierarchy group indicator arrow +// default: open arrow down +// collapsed: left arrow +.wp-table--hierarchy-indicator + @include varprop(color, body-font-color) + + &:hover + text-decoration: none + +// Toggle the indicator accessibility texts +// accordingly +.wp-table--hierarchy-indicator-collapsed + display: none + +.-hierarchy-collapsed + .wp-table--hierarchy-indicator-expanded + display: none + .wp-table--hierarchy-indicator-collapsed + display: inline + + +.wp-table--hierarchy-indicator-icon + @include icon-common + @extend .icon-arrow-down2 + font-size: 0.75rem + + .-hierarchy-collapsed & + @extend .icon-arrow-right7 + +.wp-table--row[class*="__collapsed-group-"] + display: none + +.wp-table--hierarchy-td + min-width: 0px !important + +// Highlight the additional hierarchy +// so it becomes clear they're not part of the sort +.wp-table--hierarchy-aditional-row + @include varprop(background, gray-light) \ No newline at end of file diff --git a/app/assets/stylesheets/openproject/_legacy.sass b/app/assets/stylesheets/openproject/_legacy.sass index b4ee30d9e3..135cfb7ba1 100644 --- a/app/assets/stylesheets/openproject/_legacy.sass +++ b/app/assets/stylesheets/openproject/_legacy.sass @@ -566,10 +566,6 @@ h4.comment @include breakpoint(medium down) font-size: $h2-font-size -a.impaired--empty-link, -.impaired--empty-link - color: inherit - #members_add_form margin-bottom: 1rem .-flex diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index efbc63a1b1..5c996ef077 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -88,7 +88,7 @@ class VersionsController < ApplicationController flash[:notice] = l(:notice_successful_create) redirect_to controller: '/projects', action: 'settings', tab: 'versions', id: @project else - format.html do render action: 'new' end + render action: 'new' end end end diff --git a/app/seeders/admin_user_seeder.rb b/app/seeders/admin_user_seeder.rb index a69d070be9..32237f9317 100644 --- a/app/seeders/admin_user_seeder.rb +++ b/app/seeders/admin_user_seeder.rb @@ -56,7 +56,21 @@ class AdminUserSeeder < Seeder user.mail_notification = User::USER_MAIL_OPTION_ONLY_MY_EVENTS.first user.language = I18n.locale.to_s user.status = User::STATUSES[:active] - user.force_password_change = Rails.env != 'development' + user.force_password_change = force_password_change? end end + + def force_password_change? + Rails.env != 'development' && !force_password_change_disabled? + end + + def force_password_change_disabled? + off_values = ["off", "false", "no", "0"] + + off_values.include? ENV[force_password_change_env_switch_name] + end + + def force_password_change_env_switch_name + "OP_ADMIN_USER_SEEDER_FORCE_PASSWORD_CHANGE" + end end diff --git a/app/views/common/_tabs.html.erb b/app/views/common/_tabs.html.erb index 324fc9d8d8..16567ff3e1 100644 --- a/app/views/common/_tabs.html.erb +++ b/app/views/common/_tabs.html.erb @@ -50,10 +50,13 @@ See doc/COPYRIGHT.rdoc for more details. - + +<% content_for :header_tags do %> + +<% end %> <% tabs.each do |tab| %> <%= content_tag('div', diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 92dc78ad12..1e3a681485 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -433,6 +433,10 @@ en: header_no_type: 'New work package (Type not yet set)' header_with_parent: 'New %{type} (Child of %{parent_type} #%{id})' button: 'Create' + hierarchy: + leaf: 'Work package leaf at level %{level}.' + children_collapsed: 'Hierarchy level %{level}, collapsed. Click to show the filtered children' + children_expanded: 'Hierarchy level %{level}, expanded. Click to collapse the filtered children' faulty_query: title: Work packages could not be loaded. description: Your query is erroneous and could not be processed. @@ -508,6 +512,8 @@ en: sort_by: "Sort by ..." group_by: "Group by ..." display_sums: "Display sums" + display_hierarchy: "Display hierarchy" + hide_hierarchy: "Hide hierarchy" hide_sums: "Hide sums" save: "Save" save_as: "Save as ..." diff --git a/docker-compose.pullpreview.yml b/docker-compose.pullpreview.yml index a953554a8a..e8261b9b90 100644 --- a/docker-compose.pullpreview.yml +++ b/docker-compose.pullpreview.yml @@ -22,6 +22,7 @@ worker: &ruby - "SECRET_KEY_BASE=d4e74f017910ac56c6ebad01165b7e1b37f4c9c02e9716836f8670cdc8d65a231e64e4f6416b19c8" - "RAILS_ENV=production" - "HEROKU=true" + - "OP_ADMIN_USER_SEEDER_FORCE_PASSWORD_CHANGE=off" command: "./docker/wait-for-it.sh -t 60 -h db -p 5432 --strict -- bundle exec rake db:migrate db:seed jobs:work" memory: 384 diff --git a/frontend/app/components/api/api-v3/hal-resource-types/hal-resource-types.config.ts b/frontend/app/components/api/api-v3/hal-resource-types/hal-resource-types.config.ts index f511c3b917..aeb7fa6c1e 100644 --- a/frontend/app/components/api/api-v3/hal-resource-types/hal-resource-types.config.ts +++ b/frontend/app/components/api/api-v3/hal-resource-types/hal-resource-types.config.ts @@ -35,6 +35,7 @@ function halResourceTypesConfig(halResourceTypes:HalResourceTypesService) { className: 'WorkPackageResource', attrTypes: { parent: 'WorkPackage', + ancestors: 'WorkPackage', children: 'WorkPackage', relations: 'Relation', schema: 'Schema' diff --git a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts index ddce26f917..3a47aa4bba 100644 --- a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts +++ b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts @@ -43,6 +43,7 @@ import {RelationResourceInterface} from './relation-resource.service'; interface WorkPackageResourceEmbedded { activities: CollectionResourceInterface; + ancestors: WorkPackageResourceInterface[]; assignee: HalResource|any; attachments: AttachmentCollectionResourceInterface; author: HalResource|any; @@ -122,7 +123,6 @@ export class WorkPackageResource extends HalResource { public $embedded: WorkPackageResourceEmbedded; public $links: WorkPackageResourceLinks; - public id: string; public schema: SchemaResource; public $pristine: { [attribute: string]: any } = {}; public parentId: number; @@ -138,6 +138,18 @@ export class WorkPackageResource extends HalResource { private form:any; + public get id():string { + return this.$source.id || this.idFromLink; + } + + public get idFromLink():string { + if (this.href) { + return this.href.split('/').pop()!; + } + + return ''; + } + public get isNew(): boolean { return this.id === 'new'; } @@ -529,7 +541,7 @@ export class WorkPackageResource extends HalResource { public initializeNewResource(form:any) { this.schema = form.schema; this.form = $q.when(form); - this.id = 'new'; + this.$source.id = 'new'; // Set update link to form this['update'] = this.$links.update = form.$links.self; diff --git a/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.html b/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.html index 4d5a5269a7..c98f2ed672 100644 --- a/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.html +++ b/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.html @@ -3,7 +3,7 @@ id="work-packages--edit-actions-save" class="button -alt-highlight" accesskey="3" - ng-click="$ctrl.onSave()"> + ng-click="$ctrl.save()"> @@ -11,7 +11,7 @@ id="work-packages--edit-actions-cancel" class="button" accesskey="7" - ng-click="$ctrl.onCancel()"> + ng-click="$ctrl.cancel()"> diff --git a/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.ts b/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.ts index be40d9a2c9..a80e07e487 100644 --- a/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.ts +++ b/frontend/app/components/common/edit-actions-bar/edit-actions-bar.directive.ts @@ -31,13 +31,23 @@ import {wpDirectivesModule} from "../../../angular-modules"; export class EditActionsBarController { public text:any; public onSave:Function; + public throttledSave:Function; public onCancel:Function; - constructor(I18n:op.I18n) { + constructor($timeout:ng.ITimeoutService, I18n:op.I18n) { this.text = { save: I18n.t('js.button_save'), cancel: I18n.t('js.button_cancel') } + this.throttledSave = _.throttle(this.onSave, 500); + } + + public save():void { + this.throttledSave(); + } + + public cancel():void { + this.onCancel(); } } diff --git a/frontend/app/components/context-menus/settings-menu/settings-menu.controller.ts b/frontend/app/components/context-menus/settings-menu/settings-menu.controller.ts index cbf4f4d402..4271958637 100644 --- a/frontend/app/components/context-menus/settings-menu/settings-menu.controller.ts +++ b/frontend/app/components/context-menus/settings-menu/settings-menu.controller.ts @@ -27,9 +27,11 @@ import {opWorkPackagesModule} from '../../../angular-modules'; import {ContextMenuService} from '../context-menu.service'; +import {WorkPackageTableHierarchyService} from '../../wp-fast-table/state/wp-table-hierarchy.service'; interface IMyScope extends ng.IScope { displaySumsLabel:string; + displayHierarchies:boolean; saveQuery:Function; deleteQuery:Function; query:op.Query; @@ -42,6 +44,7 @@ interface IMyScope extends ng.IScope { showGroupingModal:Function; showSortingModal:Function; toggleDisplaySums:Function; + toggleHierarchies:Function; showSettingsModalInvalid:Function; showShareModalInvalid:Function; showExportModalInvalid:Function; @@ -64,14 +67,15 @@ function SettingsDropdownMenuController($scope:IMyScope, sortingModal:any, groupingModal:any, contextMenu:ContextMenuService, + wpTableHierarchy:WorkPackageTableHierarchyService, QueryService:any, AuthorisationService:any, NotificationsService:any) { + + $scope.displayHierarchies = wpTableHierarchy.isEnabled; $scope.$watch('query.displaySums', function (newValue) { - $timeout(function () { - $scope.displaySumsLabel = (newValue) ? I18n.t('js.toolbar.settings.hide_sums') - : I18n.t('js.toolbar.settings.display_sums'); - }); + $scope.displaySumsLabel = (newValue) ? I18n.t('js.toolbar.settings.hide_sums') + : I18n.t('js.toolbar.settings.display_sums'); }); $scope.saveQuery = function (event:JQueryEventObject) { @@ -164,6 +168,10 @@ function SettingsDropdownMenuController($scope:IMyScope, }; $scope.showGroupingModal = function (event:JQueryEventObject) { + if ($scope.displayHierarchies) { + return; + } + event.stopPropagation(); showModal.call(groupingModal); updateFocusInModal('selected_columns_new'); @@ -175,6 +183,15 @@ function SettingsDropdownMenuController($scope:IMyScope, updateFocusInModal('modal-sorting-attribute-0'); }; + $scope.toggleHierarchies = function () { + if (!!$scope.query.groupBy) { + return; + } + + const isEnabled = wpTableHierarchy.isEnabled; + wpTableHierarchy.setEnabled(!isEnabled); + }; + $scope.toggleDisplaySums = function () { closeAnyContextMenu(); $scope.query.displaySums = !$scope.query.displaySums; diff --git a/frontend/app/components/context-menus/settings-menu/settings-menu.service.html b/frontend/app/components/context-menus/settings-menu/settings-menu.service.html index 550dc8ee84..df79fcc48b 100644 --- a/frontend/app/components/context-menus/settings-menu/settings-menu.service.html +++ b/frontend/app/components/context-menus/settings-menu/settings-menu.service.html @@ -6,7 +6,16 @@ {{ I18n.t('js.toolbar.settings.columns') }}