Merge branch 'release/6.0' into dev

pull/4779/head
Oliver Günther 8 years ago
commit 2dc21bcd0a
  1. 19
      .teatro.yml
  2. 2
      app/assets/javascripts/specific/toggable_fieldset.js
  3. 1
      app/assets/stylesheets/_work_packages_show_view_overwrite.scss
  4. 6
      app/assets/stylesheets/content/_datepicker.sass
  5. 450
      app/assets/stylesheets/content/_in_place_editing.sass
  6. 4
      app/assets/stylesheets/content/_watchers.sass
  7. 1
      app/assets/stylesheets/content/_widget_box.sass
  8. 1
      app/assets/stylesheets/content/_work_packages.lsg
  9. 156
      app/assets/stylesheets/content/_work_packages.sass
  10. 67
      app/assets/stylesheets/content/_work_packages_table_create.sass
  11. 1
      app/assets/stylesheets/content/work_package_details/_attachments_tab.lsg
  12. 0
      app/assets/stylesheets/content/work_packages/_table_content.lsg
  13. 77
      app/assets/stylesheets/content/work_packages/_table_content.sass
  14. 50
      app/assets/stylesheets/content/work_packages/inplace_editing/_edit_fields.sass
  15. 93
      app/assets/stylesheets/content/work_packages/inplace_editing/_legacy_inplace_styles.sass
  16. 103
      app/assets/stylesheets/content/work_packages/inplace_editing/_textareas.sass
  17. 0
      app/assets/stylesheets/content/work_packages/single_view/_attachments.sass
  18. 43
      app/assets/stylesheets/content/work_packages/single_view/_inplace_esque_fields.sass
  19. 136
      app/assets/stylesheets/content/work_packages/single_view/_single_view.sass
  20. 0
      app/assets/stylesheets/content/work_packages/tabs/_activities.lsg
  21. 0
      app/assets/stylesheets/content/work_packages/tabs/_activities.sass
  22. 0
      app/assets/stylesheets/content/work_packages/tabs/_relations.lsg
  23. 0
      app/assets/stylesheets/content/work_packages/tabs/_relations.sass
  24. 9
      app/assets/stylesheets/default.css.sass
  25. 2
      app/assets/stylesheets/layout/_angular-busy.sass
  26. 18
      app/assets/stylesheets/layout/_work_package.sass
  27. 15
      app/assets/stylesheets/specific/accessibility.sass
  28. 2
      app/controllers/wiki_controller.rb
  29. 8
      app/controllers/wiki_menu_items_controller.rb
  30. 6
      app/models/menu_items/wiki_menu_item.rb
  31. 9
      app/models/wiki.rb
  32. 9
      app/models/wiki_page.rb
  33. 3
      app/views/news/_form.html.erb
  34. 2
      app/views/repositories/settings/_vendor_form.html.erb
  35. 13
      app/views/repositories/settings/repository_form.js.erb
  36. 1
      config/locales/js-en.yml
  37. 57
      db/migrate/20160803094931_wiki_menu_titles_to_slug.rb
  38. 4
      features/menu_items/wiki_menu_items.feature
  39. 6
      frontend/app/components/common/config/configuration.service.ts
  40. 3
      frontend/app/components/context-menus/wp-context-menu/wp-context-menu.controller.ts
  41. 3
      frontend/app/components/context-menus/wp-context-menu/wp-context-menu.service.test.ts
  42. 18
      frontend/app/components/routing/ui-router.config.ts
  43. 3
      frontend/app/components/routing/wp-details/wp-details.controller.test.ts
  44. 2
      frontend/app/components/routing/wp-list/wp-list.controller.ts
  45. 2
      frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.html
  46. 4
      frontend/app/components/work-packages/wp-attachments-formattable-field/wp-attachments-formattable.directive.ts
  47. 4
      frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.html
  48. 10
      frontend/app/components/wp-edit/field-types/wp-edit-select-field.module.ts
  49. 2
      frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html
  50. 52
      frontend/app/components/wp-edit/wp-edit-mode-state.service.ts
  51. 3
      frontend/app/components/wp-table/wp-table.directive.html
  52. 1
      frontend/app/components/wp-table/wp-table.directive.ts
  53. 1
      frontend/app/templates/work_packages/activities/_link.html
  54. 2
      frontend/app/ui_components/wiki-toolbar-directive.js
  55. 1
      frontend/tests/unit/tests/work_packages/directives/work-package-details-toolbar-test.js
  56. 1
      frontend/tests/unit/tests/work_packages/helpers/work-package-context-menu-helper-test.js
  57. 5
      lib/redmine/menu_manager/menu_helper.rb
  58. 10
      spec/controllers/projects_controller_spec.rb
  59. 28
      spec/controllers/wiki_controller_spec.rb
  60. 16
      spec/controllers/wiki_menu_items_controller_spec.rb
  61. 10
      spec/features/repositories/create_repository_spec.rb
  62. 78
      spec/features/wiki/wiki_menu_item_migration_spec.rb
  63. 152
      spec/features/work_packages/cancel_editing_spec.rb
  64. 2
      spec/features/work_packages/edit_work_package_spec.rb
  65. 7
      spec/models/menu_items/wiki_menu_item_spec.rb
  66. 6
      spec/models/wiki_page_spec.rb
  67. 35
      spec/support/shared/scroll_element_into_view.rb
  68. 8
      spec/support/work_packages/work_package_text_area_field.rb

@ -1,19 +0,0 @@
project:
after:
- npm install npm
stage:
before:
- pushd frontend;
npm install --unsafe-perm --ignore-scripts;
bower install --allow-root;
popd
- cp config/configuration.yml.example config/configuration.yml
- cp config/database.teatro.yml config/database.yml
- bundle exec rake generate_secret_token
database:
- bundle exec rake db:create db:migrate
- bundle exec rake db:seed RAILS_ENV=development
assets: bundle exec rake assets:precompile RAILS_ENV=development
run: foreman start -f Procfile.dev -c all=1,assets=0

@ -38,7 +38,7 @@ function createFieldsetToggleStateLabel(legend, text) {
legendLink.append(toggleLabel);
}
toggleLabel.text(text);
toggleLabel.text(' ' + text);
}
function setFieldsetToggleState(fieldset) {

@ -42,6 +42,7 @@ body.controller-work_packages.action-show {
}
.work-packages--split-view {
height: auto;
border-top: 1px solid #ccc;
overflow: hidden;
position: absolute;

@ -196,6 +196,8 @@ $dp-shadow-box-opacity: 1
filter: Alpha(Opacity = 100)
font-weight: bold
.ui-datepicker--container
z-index: 100
// ui-datepicker arbitrarily sets the css z-index using JS
// So we have to override that value.
#ui-datepicker-div
z-index: 100 !important
position: absolute

@ -1,450 +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.
//++
%inline-date-picker-container-position-absolute
display: none
z-index: 99999
position: absolute
.inplace-edit
@include grid-block
position: inherit
overflow: visible
.-preview
button
visibility: hidden
.jstb_preview
visibility: visible
&.-busy
opacity: 0.5
.jstElements button
opacity: 0.5
*
cursor: wait!important
.inplace-edit--date
margin: 0
position: relative
& > *
margin-bottom: 0
& > .inplace-edit--date-picker
@extend %inline-date-picker-container-position-absolute
left: 0
&.attribute-date
.inplace-edit--read-value
span
display: inline-block
vertical-align: middle
.inplace-edit--date-range
display: table
width: 100%
position: relative
& > *
display: table-cell
margin-bottom: 0
& > .delimeter
padding-right: 5px
padding-left: 5px
text-align: center
vertical-align: middle
& > .inplace-edit--date-range-start-date-picker
@extend %inline-date-picker-container-position-absolute
left: 0
& > .inplace-edit--date-range-end-date-picker
@extend %inline-date-picker-container-position-absolute
right: 0
&.-editable.-no-label:not(.-active)
margin-left: -0.375rem
&.-small
margin-top: 3px
padding-bottom: 0px
padding-top: 0px
.inplace-edit--read-value
padding-top: 0
padding-bottom: 0
select, input
height: 24px
padding-top: 0
padding-bottom: 0
line-height: normal
font-size: 14px
&.-shrink
.inplace-edit--read-value,
flex: 0 1 auto
.textarea-wrapper
margin-bottom: 39px
// This is just a neutral element that one can
// put in so that it does no longer interfere
// with the flex boxes
.inplace-edit--block
@include grid-block
overflow: visible
#ui-datepicker-div
z-index: 9999 !important
.inplace-edit--form,
.user-comment--form
flex: 1 1 auto
display: block
position: relative
.inplace-edit
.textarea-wrapper
.jstHandle, .jstSpacer
display: none
.jstElements
button
display: none
.jstb_strong,
.jstb_em,
.jstb_ins,
.jstb_del,
.jstb_ul,
.jstb_ol,
.jstb_preview,
.jstb_help
display: inline-flex
justify-content: center
// Enable greater clickable area for constrained movable users
.wp-inline-edit--boolean-field
width: 100%
.inplace-edit--select
.select2-display-none
display: none
&.-full-width
max-width: 100%
.inplace-edit--textarea
min-height: 20px
width: 100%
margin-bottom: -1px
overflow-y: auto !important
// Resize done by angular-elastic
&[msd-elastic]
resize: none
&.-animated:focus
transition: height 50ms ease-in-out
.inplace-edit--text-field
margin-bottom: 0!important
padding: 0.375rem
font-size: inherit
line-height: inherit
.inplace-edit--dashboard
position: relative
width: 100%
z-index: 100
font-size: 0.875rem
.inplace-edit--errors
line-height: 1.2em
width: 100%
padding: 5px
left: 251px
bottom: -11px
background: rgb(254, 208, 209)
border: 1px solid $inplace-edit--color--very-dark
min-height: 42px
color: $body-font-color
.inplace-edit--errors--icon
position: absolute
top: 8px
.inplace-edit--errors--text
white-space: pre-line
padding-left: 18px
.wp-edit-field
.jstHandle, .jstSpacer
display: none
.inplace-edit--write
@include grid-block
.inplace-edit--read
@include grid-block
overflow: visible
.inplace-edit--read-value
@include grid-block
&.-default
font-style: italic
p
font-size: inherit
&:last-of-type
margin-bottom: 0
.user-field-user-link
display: inline
.macro-unavailable.permanent
position: relative
top: 0
span
overflow: hidden
text-overflow: ellipsis
p
overflow: inherit
text-overflow: inherit
.inplace-edit--read-value--value,
@include grid-block
&.-placeholder
font-style: italic
.progress-bar-legend
font-style: initial
// When user is not allowed to edit the field
#work-package-description
span:not(.inplace-editing--container)
.inplace-edit--read-value span
width: 100%
.macro-unavailable.permanent
width: 50%
.inplace-edit--preview
border: 1px solid $inplace-edit--border-color
min-height: 114px
padding: 0.375rem
.inplace-edit--icon-wrapper
@include grid-block(shrink)
text-align: center
width: 33px
background: $gray-light
border-left: 1px solid $inplace-edit--border-color
align-items: center
justify-content: center
visibility: hidden
font-size: rem-calc(14px)
.icon-edit:before,
.icon-delete:before,
.icon-remove:before
// HACK: overriding default padding here
padding-right: 0
a.inplace-edit--icon-wrapper
color: $body-font-color
&:hover
color: $body-font-color
text-decoration: none
.inplace-editing--trigger-container
@include grid-block
overflow: visible
width: 100%
// need to specify the a explicitly as otherwise
// the default class will win
a.inplace-editing--trigger-link,
.inplace-editing--trigger-link,
@include grid-block
color: $body-font-color
font-weight: inherit
overflow: visible
width: 100%
&:hover,
&:focus,
&.-focus
text-decoration: none
color: $body-font-color
.inplace-editing--container,
.wp-edit-field
border-color: $inplace-edit--border-color
&.-active
border-color: transparent
.inplace-edit--icon-wrapper
visibility: visible
display: flex
// Otherwise the description will show up in one line on IE10
.inplace-edit--read-value
display: block
// Do not hover trigger-link when element is read-only
.-read-only
.inplace-editing--trigger-link:hover .inplace-editing--container
border-color: transparent
.inplace-editing--container,
@include grid-block
border-color: transparent
border-style: solid
border-radius: 2px
border-width: 1px
overflow: visible
width: 100%
.inplace-edit--controls
display: inline-block
background: $inplace-edit--dark-background
border: 1px solid $inplace-edit--color--very-dark
box-shadow: 2px 2px 4px $inplace-edit--border-color
text-align: center
float: right
padding: 5px
line-height: 1.6rem
margin-top: -1px
// Having to get the element out of the normal dom flow to
// prevent the element overlapping and thereby blocking the click on
// inplace-editable elements below the current one.
position: absolute
right: 0
// Disabled submit styles when not applicable
.inplace-edit--control--save[disabled],
.inplace-edit--control--send[disabled]
background-color: $inplace-edit--bg-color--disabled
span
color: $inplace-edit--color--disabled
cursor: not-allowed
.inplace-edit--control
width: 1.75rem
height: 1.75rem
line-height: 1.75rem
display: inline-block
font-size: 0.9rem
a
display: inline-block
width: 100%
height: 100%
border: 1px solid transparent
color: $body-font-color
text-decoration: none
&:hover, &:active
border-color: $inplace-edit--border-color
border-radius: 2px
.icon-context:before
padding: 0
// custom hack to have the jsToolbar in the same line as the "Description" title
.inplace-edit.attribute-description
.inplace-edit--write
margin-top: -46px
// for non-textile description we need to not overlap the 'Description' header
// which in textile is shown due to edit buttons on the right side
&.edit-strategy-textarea
margin-top: auto
.jstElements
margin-bottom: 18px !important
// Explicit styles for input-esque trigger appearance
.work-packages--activity--add-comment,
.work-package--watchers-lookup
.inplace-editing--container
border: 1px solid #eee
.work-packages--activity--add-comment
margin: 20px 0
.inplace-edit--read-value
padding: 10px 5px
.inplace-editing--trigger-link
font-size: 1rem
.work-package--watchers-lookup .inplace-edit--read-value
padding: 0.375rem
.work-packages--split-view
.work-packages--details--title,
.work-packages--details--description
a.inplace-editing--trigger-link
margin-left: 1px
&:focus
outline-offset: -2px
outline-width: 1px
.inplace-edit--highlight
border-color: $inplace-edit--color-highlight !important
.work-packages--details
.inplace-edit--read-value .macro-unavailable.permanent
width: 75%
margin-left: -10rem
.work-package--details--long-field
margin-top: 10px
.inplace-editing--trigger-container
line-height: 1.6
.inplace-edit--read
margin-left: 0px
max-width: 100%
padding-bottom: 5px
.inplace-edit--read-value
span.deleting
opacity: 0.5
img.avatar-mini
float: inherit
i
vertical-align: text-top

@ -44,3 +44,7 @@
.work-package--watchers.-read-only
.remove-watcher-btn
display: none
// Fix margin for inplace-edit controls
.work-package--watchers-lookup .dropdown-wrapper
margin-bottom: 50px

@ -64,6 +64,7 @@ $widget-box--enumeration-width: 20px
margin-right: 0
.widget-box
position: relative
@include widget-box--style
padding: 10px 20px 30px 20px
min-height: 250px

@ -26,143 +26,19 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
.work-packages--details-content
font-size: 0.875rem
.inplace-edit--write-value
textarea
overflow: hidden
.inplace-edit--write-value
textarea
font-size: 1rem
line-height: 1.6
@mixin details-pane--form-field
@include grid-visible-overflow
padding: 0
.work-packages--details--title
@include grid-block
@include details-pane--form-field
align-items: center
font-size: 1.125rem
font-weight: bold
div[class*='work-packages--details--']
width: calc(100% + 0.375rem)
.inplace-edit--read
&>span:first-of-type
width: 100%
.inplace-edit--read-value
width: 100%
.dynamic-attribute
width: 100%
.version-wrapper,
.spent-time-wrapper
width: 100%
.work-packages--details--subject
@include grid-content
@include grid-size(expand)
@include details-pane--form-field
// overriding default in place editing padding
// because the heigt will otherwise be too much
// and the change from read to write will flicker
.inplace-edit--text-field
padding: 0.15625rem 0.375rem
.report-category-actions
margin-top: -28px
width: 100%
text-align: right
.detail-panel-description
margin: 0
line-height: 18px
.comments-form
float: left
width: 100%
margin: 10px 0 100px 0
textarea
border: 1px solid #dddddd
padding: 8px
border-radius: 2px
font-size: 0.8125rem
width: 100%
button
float: right
font-size: 0.8125rem
background: linear-gradient(to bottom, white 0%, #eeeeee 74%, #eeeeee 100%) repeat scroll 0 0 rgba(0, 0, 0, 0)
border: 1px solid #CCCCCC
border-radius: 2px
color: #222222
margin: 10px 0 0 0
padding: 4px 10px 2px
cursor: pointer
height: 32px
i
&.icon-left
padding: 0 5px 0 0
&.icon-right
vertical-align: -2px
padding: 0 0 0 4px
#tabs
position: relative
width: 100%
$work-package-details--tab-height: 40px
.tabrow
text-align: left
list-style: none
margin: 0 0 0 10px
padding: 4px 0 0 0
line-height: $work-package-details--tab-height - 10px
height: $work-package-details--tab-height
overflow: hidden
position: relative
width: 97.5%
font-weight: bold
text-transform: uppercase
li
background: #ffffff
display: inline-block
position: relative
margin: 0
padding: 0
text-align: center
width: 23.25%
cursor: pointer
a
color: #333
text-decoration: none
&:hover
text-decoration: none
li.selected
color: #999
border-bottom: 2px solid $content-link-color
a
color: $content-link-color
.work-package--new-state
padding: 0 20px 0px 0
overflow-y: auto
.work-packages--edit-actions
margin-left: -20px
.generic-table
.assigned_to a
display: block
min-height: 22px
overflow: hidden
text-overflow: ellipsis
// Table editing styles
@import work_packages/table_content
// Specific field styles
@import work_packages/inplace_editing/edit_fields
@import work_packages/inplace_editing/legacy_inplace_styles
@import work_packages/inplace_editing/textareas
// WP single view styles
@import work_packages/single_view/single_view
@import work_packages/single_view/attachments
@import work_packages/single_view/inplace_esque_fields
// Tabs
@import work_packages/tabs/_activities
@import work_packages/tabs/_relations

@ -1,67 +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.
//++
.wp-inline-create-button-row td
padding: 0
.wp-inline-create-button
border-bottom: none
a
width: 100%
padding: 0.5rem 0
display: block
line-height: 1.6
&:hover
background: #e4f7fb
.wp-inline-create--add-link
font-weight: bold
.icon::before
padding-left: 7px
padding-right: 10px
font-size: 11px
&:hover
text-decoration: none
.wp--row.-new,
tr.context-menu-selection.-new
background: #BEF3CA
&:hover
background: darken(#BEF3CA, 5%)
.wp-table--cell-span:hover
border-color: #35c53f

@ -1 +0,0 @@
# Work Packages - [Details Pane] - Attachments

@ -26,6 +26,20 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
.work-package-table--container table.generic-table tbody td
padding-left: 0
line-height: 24px
// Styles for inline editable attributes
.work-package-table--container td.-editable
padding-top: 0
padding-bottom: 0
display: table-cell
width: auto
&:hover .wp-edit-field.-error:hover
border-color: $nm-color-error-border
// Show details view button when hovering
.wp-table--details-link
.issue:hover &,
@ -44,6 +58,61 @@
&:hover
text-decoration: none
table.generic-table tbody tr.issue .checkbox
overflow: visible
padding-left: 5px
.wp-table--cell .ui-datepicker
margin-left: -38%
.inplace-editing--container
display: inline-block
border: 1px solid transparent
border-radius: 2px
overflow: visible
width: 100%
//
// INLINE CREATE
//
.wp-inline-create-button-row td
padding: 0
.wp-inline-create-button
border-bottom: none
a
width: 100%
padding: 0.5rem 0
display: block
line-height: 1.6
&:hover
background: #e4f7fb
.wp-inline-create--add-link
font-weight: bold
.icon::before
padding-left: 7px
padding-right: 10px
font-size: 11px
&:hover
text-decoration: none
.wp--row.-new,
tr.context-menu-selection.-new
background: #BEF3CA
&:hover
background: darken(#BEF3CA, 5%)
.wp-table--cell-span:hover
border-color: #35c53f
.wp-table--cancel-create-link
.icon
position: relative
@ -55,12 +124,4 @@
&:hover
text-decoration: none
table.generic-table tbody tr.issue .checkbox
overflow: visible
padding-left: 5px
.wp--row .wp-table--cell
overflow: visible
.wp-table--cell .ui-datepicker
margin-left: -38%

@ -26,22 +26,16 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
.work-package-table--container table.generic-table tbody td
.wp-edit-field
padding-left: 0
line-height: 24px
// Styles for inline editable attributes
.work-package-table--container td.-editable
padding-top: 0
padding-bottom: 0
display: table-cell
width: auto
// Min width is necessary to assure that
// the smallet element (checkbox) is shown
min-width: 1.5rem
&:hover .wp-edit-field.-error:hover
border-color: $nm-color-error-border
.wp-edit-field
padding-left: 0
// Remove overflow with ellipsis
text-overflow: ellipsis
overflow: hidden
// Avoid jumping subject field
&:first-of-type
@ -50,7 +44,6 @@
padding: 0
&.-error
.wp-table--cell-span,
.wp-inline-edit--field
background: $nm-color-error-background
@ -62,9 +55,16 @@
form
width: 100%
.-hidden-overflow
overflow: hidden
text-overflow: ellipsis
// Style actual edit inputs
select, input
// Full width to inline-edit inputs
width: 100%
// Same height as the row - padding
height: 24px
padding-top: 0
padding-bottom: 0
line-height: normal
font-size: 14px
// Editable fields cursor
.-editable .wp-table--cell-span
@ -73,7 +73,7 @@
border-style: solid
border-radius: 2px
border-width: 1px
line-height: 1.6
line-height: normal
&:hover,
&:focus
@ -87,6 +87,20 @@
.error.macro-unavailable
display: inline-block
// Style no-label fields (long text, description, ..) with padding
.-editable.-no-label:not(.-active)
margin-left: -0.375rem
.wp-table--cell-span
display: block
padding: 5px
padding-right: 0
// Do not hover trigger-link when element is read-only
.-read-only
.inplace-editing--trigger-link:hover .inplace-editing--container
border-color: transparent
// Mark focused, non-editable read-values
.inplace-edit--read-value.-read-only
&:focus, &:hover

@ -0,0 +1,93 @@
//-- 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.
//++
// *** NOTE ***
// Following code handles trigger-links and inplace icons
// It is used for example for the attachements list and comment form
// need to specify the a explicitly as otherwise
// the default class will win
a.inplace-editing--trigger-link,
.inplace-editing--trigger-link,
&:hover,
&:focus,
&.-focus
text-decoration: none
color: $body-font-color
.inplace-editing--container,
.wp-edit-field
border-color: $inplace-edit--border-color
&.-active
border-color: transparent
.inplace-edit--icon-wrapper
visibility: visible
.inplace-edit--read-value
display: inline-block
span
line-height: 2
i
vertical-align: middle
.work-package--details--long-field
.inplace-edit--read .inplace-edit--read-value
// Use the whole space and leave room for the icon on the right
width: calc(100% - 42px)
padding: 3px
line-height: 2
span.deleting
opacity: 0.5
img.avatar-mini
float: inherit
// Do not hover trigger-link when element is read-only
.-read-only
.inplace-editing--trigger-link:hover .inplace-editing--container
border-color: transparent
.inplace-edit--icon-wrapper
display: inline-block
text-align: center
width: 32px
line-height: 2.5
background: $gray-light
border-left: 1px solid $inplace-edit--border-color
color: $body-font-color
visibility: hidden
float: right
.icon-context:before
// HACK: overriding default padding here
padding-right: 0
&:hover
text-decoration: none

@ -0,0 +1,103 @@
//-- 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.
//++
// Avoid overlapping text selection in FF
// https://community.openproject.com/work_packages/23742
.inplace-edit--textarea
line-height: normal
.inplace-edit--write-value
textarea
font-size: 1rem
line-height: 1.6
.inplace-edit
.textarea-wrapper
// Leave room below textarea for inplace edit controls
position: relative
margin-bottom: 50px
// Don't show all jstb buttons in inplace-edit textareas
.jstHandle, .jstSpacer
display: none
.jstElements
button
display: none
.jstb_strong,
.jstb_em,
.jstb_ins,
.jstb_del,
.jstb_ul,
.jstb_ol,
.jstb_preview,
.jstb_help
display: inline-flex
justify-content: center
// Styles for the Save | Cancel controls below textareas
.inplace-edit--controls
position: absolute
right: 0
width: 80px
height: 40px
bottom: -39px // 40px - border
background: $inplace-edit--dark-background
border: 1px solid $inplace-edit--color--very-dark
box-shadow: 2px 2px 4px $inplace-edit--border-color
text-align: center
// Align controls via flex
display: flex
// Disabled submit styles when not applicable
.inplace-edit--control--save[disabled] a,
.inplace-edit--control--send[disabled] a
background-color: $inplace-edit--bg-color--disabled
color: $inplace-edit--color--disabled
cursor: not-allowed
// A single save/cancel control
.inplace-edit--control
font-size: 0.9rem
flex: 1
padding: 5px
a
// Center save/cancel links
width: 100%
line-height: 27px
border: 1px solid transparent
display: inline-block
color: $body-font-color
text-decoration: none
&:hover, &:active
border-color: $inplace-edit--border-color
.icon-context:before
padding: 0

@ -0,0 +1,43 @@
// Explicit styles for input-esque trigger appearance
.work-packages--activity--add-comment,
.work-package--watchers-lookup
.inplace-editing--container
border: 1px solid #eee
// Explicit styling for the comment and watcher container
.work-packages--activity--add-comment,
.work-package--watchers-lookup
margin: 20px 0
// Styles the comment trigger link similar to attachment
// but without icon and actual link
a.inplace-editing--trigger-link
color: $body-font-color
font-style: italic
.inplace-edit--read-value
padding-left: 5px
// Editing existing comments
.comments-form
float: left
width: 100%
margin: 10px 0 100px 0
textarea
border: 1px solid #dddddd
padding: 8px
border-radius: 2px
font-size: 0.8125rem
width: 100%
button
float: right
font-size: 0.8125rem
background: linear-gradient(to bottom, white 0%, #eeeeee 74%, #eeeeee 100%) repeat scroll 0 0 rgba(0, 0, 0, 0)
border: 1px solid #CCCCCC
border-radius: 2px
color: #222222
margin: 10px 0 0 0
padding: 4px 10px 2px
cursor: pointer
height: 32px

@ -0,0 +1,136 @@
//-- 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.
//++
$work-package-details--tab-height: 40px
// Subject field
@mixin details-pane--form-field
@include grid-visible-overflow
padding: 0
.work-packages--details--title
@include grid-block
@include details-pane--form-field
align-items: center
font-size: 1.125rem
font-weight: bold
.work-packages--details--subject
@include grid-content
@include grid-size(expand)
@include details-pane--form-field
// overriding default in place editing padding
// because the heigt will otherwise be too much
// and the change from read to write will flicker
.inplace-edit--text-field
padding: 0.15625rem 0.375rem
.work-packages--details-content
font-size: 0.875rem
.inplace-edit--write-value
textarea
overflow: hidden
div[class*='work-packages--details--']
width: calc(100% + 0.375rem)
.inplace-edit--read
&>span:first-of-type
width: 100%
.inplace-edit--read-value
width: 100%
.dynamic-attribute
width: 100%
.version-wrapper,
.spent-time-wrapper
width: 100%
.report-category-actions
margin-top: -28px
width: 100%
text-align: right
.detail-panel-description
margin: 0
line-height: 18px
i
&.icon-left
padding: 0 5px 0 0
&.icon-right
vertical-align: -2px
padding: 0 0 0 4px
.work-package--new-state
padding: 0 20px 0px 0
overflow-y: auto
.work-packages--edit-actions
margin-left: -20px
// TABS row styling
#tabs
position: relative
width: 100%
.tabrow
text-align: left
list-style: none
margin: 0 0 0 10px
padding: 4px 0 0 0
line-height: $work-package-details--tab-height - 10px
height: $work-package-details--tab-height
overflow: hidden
position: relative
width: 97.5%
font-weight: bold
text-transform: uppercase
li
background: #ffffff
display: inline-block
position: relative
margin: 0
padding: 0
text-align: center
width: 23.25%
cursor: pointer
a
color: #333
text-decoration: none
&:hover
text-decoration: none
li.selected
color: #999
border-bottom: 2px solid $content-link-color
a
color: $content-link-color

@ -66,12 +66,8 @@
@import content/icon_control
@import content/boxes
@import content/watchers
@import content/work_packages
@import content/simple_filters
@import content/advanced_filters
@import content/work_packages_table
@import content/work_packages_table_edit
@import content/work_packages_table_create
@import content/attributes_key_value
@import content/attributes_group
@import content/attributes_table
@ -79,9 +75,7 @@
@import content/widget_box
@import content/list
@import content/work_package_details/activities_tab
@import content/work_package_details/attachments_tab
@import content/work_package_details/relations_tab
@import content/work_packages
@import content/expandable_group_content
@import content/control_colors
@ -98,7 +92,6 @@
@import content/journal
@import content/pagination
@import content/progress_bar
@import content/in_place_editing
@import content/form_error_messages
@import content/ajax_indicator

@ -46,5 +46,5 @@
margin: 12px 0
.cg-busy-default-spinner
top: -30px
top: 30px
left: 30px

@ -90,23 +90,23 @@
height: auto
> .work-packages--split-view
+flex-grow(1)
min-height: 100px
.work-packages--split-view
+display(flex)
width: 100%
height: 100%
.work-packages--list
+flex-grow(1)
position: relative
+flex(2)
overflow-x: auto
height: 100%
.action-details .work-package--details-container
flex: 1
.work-packages--list-table-area
position: absolute
top: 0
bottom: 55px
right: 0
width: 100%
height: calc(100% - 55px)
table
tr
@ -126,8 +126,6 @@
white-space: normal
.work-packages--list-pagination-area
position: absolute
bottom: 0
width: 100%
height: 55px
padding: 3px 10px 0 0

@ -66,11 +66,22 @@ body.accessibility-mode
// Neccessary to have enough space for the border in IE
.work-packages--page-container
.inplace-edit .inplace-edit--read-value,
.id a,
.inplace-edit form
.inplace-edit .inplace-edit--read,
.inplace-edit form,
.id a
margin: 3px
max-width: calc(100% - 6px)
.inplace-edit form
display: block
.inplace-edit .inplace-edit--read-value
display: inline-block
// Allow a greater sensible area to click input checkboxes
// On the WP table this is not neccessary because the whole cell is clickable
table:not(.work-package-table) input[type='checkbox']
width: 100%
// Show WP skip links for motorically handicapped users
.skip-navigation-link:focus
top: auto
left: auto

@ -388,7 +388,7 @@ class WikiController < ApplicationController
end
def build_wiki_page_and_content
@page = WikiPage.new wiki: @wiki
@page = WikiPage.new wiki: @wiki, title: wiki_page_title.presence || I18n.t(:label_wiki_page)
@page.content = WikiContent.new page: @page
@content = @page.content_for_version nil

@ -119,7 +119,11 @@ class WikiMenuItemsController < ApplicationController
@page = wiki.find_page(params[:id])
@page_title = @page.title
@wiki_menu_item = MenuItems::WikiMenuItem.find_or_initialize_by(navigatable_id: wiki.id, title: @page_title)
@wiki_menu_item = MenuItems::WikiMenuItem.find_or_initialize_by(
navigatable_id: wiki.id,
name: @page.slug
)
@wiki_menu_item.title ||= @page_title
possible_parent_menu_items = MenuItems::WikiMenuItem.main_items(wiki.id) - [@wiki_menu_item]
@parent_menu_item_options = possible_parent_menu_items.map { |item| [item.name, item.id] }
@ -151,7 +155,7 @@ class WikiMenuItemsController < ApplicationController
menu_item = if item = page.menu_item
item.tap { |item| item.parent_id = nil }
else
wiki.wiki_menu_items.build(title: page.title, name: page.title)
wiki.wiki_menu_items.build(name: page.slug, title: page.title)
end
menu_item.options = options

@ -36,8 +36,12 @@ class MenuItems::WikiMenuItem < MenuItem
.order('id ASC')
}
def slug
name.to_url
end
def item_class
title.dasherize
slug
end
def index_page

@ -79,13 +79,6 @@ class Wiki < ActiveRecord::Base
page
end
##
# Unescape the given title from user input to retrieve the
# unicode title of a wiki page
def self.from_param(title)
CGI.unescape(CGI.unescapeHTML(title))
end
# Finds a page by title
# The given string can be of one of the forms: "title" or "project:title"
# Examples:
@ -108,7 +101,7 @@ class Wiki < ActiveRecord::Base
def create_menu_item_for_start_page
wiki_menu_item = wiki_menu_items.find_or_initialize_by(title: start_page) { |item|
item.name = 'Wiki'
item.name = 'wiki'
}
wiki_menu_item.new_wiki_page = true
wiki_menu_item.index_page = true

@ -87,6 +87,10 @@ class WikiPage < ActiveRecord::Base
end
end
def slug
read_attribute(:slug).presence || title.try(:to_url)
end
def delete_wiki_menu_item
menu_item.destroy if menu_item
# ensure there is a menu item for the wiki
@ -117,8 +121,9 @@ class WikiPage < ActiveRecord::Base
wiki.redirects << WikiRedirect.new(title: previous_slug, redirects_to: slug) unless redirect_existing_links == '0'
# Change title of dependent wiki menu item
dependent_item = MenuItems::WikiMenuItem.find_by(navigatable_id: wiki.id, title: @previous_title)
dependent_item = MenuItems::WikiMenuItem.find_by(navigatable_id: wiki.id, name: previous_slug)
if dependent_item
dependent_item.name = slug
dependent_item.title = title
dependent_item.save!
end
@ -205,7 +210,7 @@ class WikiPage < ActiveRecord::Base
end
def menu_item
MenuItems::WikiMenuItem.find_by(title: title, navigatable_id: wiki_id)
MenuItems::WikiMenuItem.find_by(name: slug, navigatable_id: wiki_id)
end
def nearest_menu_item

@ -34,6 +34,5 @@ See doc/COPYRIGHT.rdoc for more details.
<%= f.text_area :summary, cols: 60, rows: 2 %>
</div>
<div class="form--field">
<%= f.text_area :description, required: true, cols: 60, rows: 15, class: 'wiki-edit' %>
<%= f.text_area :description, required: true, cols: 60, rows: 15, class: 'wiki-edit wiki-toolbar' %>
</div>
<%= wikitoolbar_for 'news_description' %>

@ -34,7 +34,7 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="form--field">
<%= styled_label_tag type, l("repositories.#{@repository.vendor}.#{type}_title") %>
<div class="form--field-container">
<%= styled_radio_button_tag 'scm_type', type, false, id: type %>
<%= styled_radio_button_tag 'scm_type', type, scm_types.length == 1 ? true : false, id: type, autofocus: true %>
</div>
</div>
<% end %>

@ -46,6 +46,19 @@
// Focus on first header
headers.first().focus();
// Open content if there is only one possible selection
var checkedInput = jQuery('input[name=scm_type]:checked');
if(checkedInput.length > 0) {
toggleContent(content, checkedInput.val());
}
// Necessary for accessibilty purpose
jQuery('#scm_vendor').on('change', function(){
window.setTimeout(function(){
document.getElementsByName('scm_type')[0].focus();
}, 500)
});
// Toggle content
switches.on('change', function() {
toggleContent(content, this.value);

@ -451,6 +451,7 @@ en:
summary: "Table with rows of work package and columns of work package attributes."
text_inline_edit: "Most cells of this table are buttons that activate inline-editing functionality of that attribute."
text_sort_hint: "With the links in the table headers you can sort, group, reorder, remove and add table columns."
text_select_hint: "Select boxes should be opened with 'ALT' and arrow keys."
tabs:
overview: Overview
activity: Activity

@ -0,0 +1,57 @@
class WikiMenuTitlesToSlug < ActiveRecord::Migration
def up
migrate_menu_items
end
def down
rollback_menu_items
end
##
# Fix lookup of wiki pages in menu items by referencing the actual slug in the title attribute.
# As the title attribute is fixed from MenuItem, and the name was used, swap the two around
# to avoid confusing the actual title of the menu item (previously == name).
def migrate_menu_items
ActiveRecord::Base.transaction do
::MenuItems::WikiMenuItem.includes(:wiki).find_each do |item|
# Find the page
wiki_page = item.wiki.find_page(item.title)
# Set the title to the actual slug
# If the page could not be found, migrate the title to form a slug
slug = wiki_page.nil? ? item.title.to_url : wiki_page.slug
# Use the name to set the title.
# This clears up the previously irritating mixup of the two.
menu_item_title = item.name
item.update_columns(title: menu_item_title, name: slug)
end
end
end
##
#
# Restore the old title wherever possible
# This tries to remove the slug usages without guaranteeing that links
# will be valid afterwards.
def rollback_menu_items
ActiveRecord::Base.transaction do
::MenuItems::WikiMenuItem.includes(:wiki).find_each do |item|
# Find the page
wiki_page = item.wiki.find_page(item.title)
# Restore the switch of title and name
old_name = item.title
old_title =
if wiki_page.present?
wiki_page.title
else
item.name
end
item.update_columns(title: old_title, name: old_name)
end
end
end
end

@ -119,9 +119,9 @@ Feature: Wiki menu items
Scenario: When I delete the last wiki page with a menu item I can select a new menu item and the menu item is replaced
Given the project "Awesome Project" has a wiki menu item with the following:
| title | AwesomePage |
| name | AwesomePage |
| name | awesomepage |
And the wiki menu item of the wiki page "Wiki" of project "Awesome Project" has been deleted
When I go to the wiki page "AwesomePage" for the project called "Awesome Project"
When I go to the wiki page "awesomepage" for the project called "Awesome Project"
And I click on "More"
And I click on "Configure menu item"
And I choose "Do not show this wikipage in project navigation"

@ -64,7 +64,8 @@ function ConfigurationService($q, $http, $window, PathHelper, I18n) {
impaired: false,
time_zone: '',
others: {
comments_sorting: 'asc'
comments_sorting: 'asc',
warn_on_leaving_unsaved: true
}
}
};
@ -89,6 +90,9 @@ function ConfigurationService($q, $http, $window, PathHelper, I18n) {
commentsSortedInDescendingOrder: function () {
return this.settings.user_preferences.others.comments_sorting === 'desc';
},
warnOnLeavingUnsaved: function () {
return this.settings.user_preferences.others.warn_on_leaving_unsaved === true;
},
isTimezoneSet: function () {
return this.settings.user_preferences.time_zone !== '';
},

@ -98,8 +98,7 @@ function wpContextMenuController($scope,
workPackageId: selected[0].id
};
wpEditModeState.start();
$state.transitionTo('work-packages.show', params);
$state.transitionTo('work-packages.show.edit', params);
}
function getWorkPackagesFromSelectedRows() {

@ -48,7 +48,8 @@ describe('workPackageContextMenu', () => {
beforeEach(angular.mock.module('openproject.templates', ($provide) => {
var configurationService = {
isTimezoneSet: sinon.stub().returns(false),
accessibilityModeEnabled: sinon.stub().returns(false)
accessibilityModeEnabled: sinon.stub().returns(false),
warnOnLeavingUnsaved: sinon.stub().returns(false)
};
$provide.constant('ConfigurationService', configurationService);

@ -119,15 +119,18 @@ openprojectModule
})
.state('work-packages.edit', {
url: '/{projects}/{projectPath}/work_packages/{workPackageId}/edit',
url: '/{projects}/{projectPath}/work_packages/{workPackageId:[0-9]+}/edit',
params: {
projectPath: {value: null, squash: true},
projects: {value: null, squash: true}
projects: {value: null, squash: true},
},
onEnter: ($state, $stateParams, wpEditModeState:WorkPackageEditModeStateService) => {
onEnter: ($state, $timeout, $stateParams, wpEditModeState:WorkPackageEditModeStateService) => {
wpEditModeState.start();
$state.go('work-packages.list.details.overview', $stateParams);
// Transitioning to a new state may cause a reported issue
// $timeout is a workaround: https://github.com/angular-ui/ui-router/issues/326#issuecomment-66566642
// I believe we should replace this with an explicit edit state
$timeout(() => $state.go('work-packages.list.details.overview', $stateParams, { notify: false }));
}
})
@ -142,9 +145,12 @@ openprojectModule
.state('work-packages.show.edit', {
url: '/edit',
reloadOnSearch: false,
onEnter: ($state, $stateParams, wpEditModeState:WorkPackageEditModeStateService) => {
onEnter: ($state, $timeout, $stateParams, wpEditModeState:WorkPackageEditModeStateService) => {
wpEditModeState.start();
$state.go('work-packages.show', $stateParams);
// Transitioning to a new state may cause a reported issue
// $timeout is a workaround: https://github.com/angular-ui/ui-router/issues/326#issuecomment-66566642
// I believe we should replace this with an explicit edit state
$timeout(() => $state.go('work-packages.show', $stateParams, { notify: false }));
}
})
.state('work-packages.show.activity', panels.activity)

@ -124,7 +124,8 @@ describe('WorkPackageDetailsController', () => {
beforeEach(angular.mock.module('openproject.templates', function ($provide) {
$provide.constant('ConfigurationService', {
isTimezoneSet: sinon.stub().returns(false)
isTimezoneSet: sinon.stub().returns(false),
warnOnLeavingUnsaved: sinon.stub().returns(false)
});
$provide.constant('$stateParams', stateParams);

@ -261,7 +261,7 @@ function WorkPackagesListController($scope,
loadingIndicator.mainPage = $state.go(
'work-packages.show',
angular.extend(params, $state.params)
angular.extend($state.params, params)
);
}
};

@ -2,7 +2,7 @@
<div id="work-package-comment-container">
<ng-transclude ng-if="!vm.showAbove"></ng-transclude>
<div class="work-package-field work-packages--activity--add-comment" ng-if="vm.canAddComment">
<div class="work-package--details--long-field work-packages--activity--add-comment" ng-if="vm.canAddComment">
<div class="wp-edit-field inplace-edit"
cg-busy="{promise: vm.loadingPromise}"
ng-switch="vm.active">

@ -134,7 +134,9 @@ export class WpAttachmentsFormattableController {
protected insertDelayedAttachments(dropData:DropModel, description):void {
for (var i = 0; i < dropData.files.length; i++) {
description.insertAttachmentLink(dropData.files[i].name.replace(/ /g, '_'), InsertMode.ATTACHMENT, true);
var currentFile = new SingleAttachmentModel(dropData.files[i]);
var insertMode = currentFile.isAnImage ? InsertMode.INLINE : InsertMode.ATTACHMENT;
description.insertAttachmentLink(dropData.files[i].name.replace(/ /g, '_'), insertMode, true);
this.$rootScope.$broadcast('work_packages.attachment.add', dropData.files[i]);
}

@ -1,4 +1,4 @@
<div ng-if="$ctrl.workPackage && $ctrl.formCtrl">
<div ng-if="$ctrl.workPackage && $ctrl.formCtrl" class="work-package--single-view">
<div class="attributes-group">
<div class="attributes-group--header">
<div class="attributes-group--header-container">
@ -16,7 +16,7 @@
wp-attachments-formattable
wp-edit-field-wrapper-classes="'-no-label'"
display-placeholder="$ctrl.I18n.t('js.work_packages.placeholders.description')"
class="single-attribute wiki">
class="single-attribute wiki work-packages--details--description">
</div>
</div>

@ -70,7 +70,15 @@ export class SelectEditField extends EditField {
}
private addEmptyOption() {
if (!this.schema.required) {
// Empty options are not available for required fields
if (this.schema.required) {
return;
}
// Since we use the original schema values, avoid adding
// the option if one is returned / exists already.
const emptyOption = _.find(this.options, { name: this.text.placeholder });
if (emptyOption === undefined) {
this.options.unshift({
name: this.text.placeholder,
});

@ -33,7 +33,7 @@
work-package="::vm.workPackage"
placeholder="::vm.displayPlaceholder"
label="vm.fieldLabel"
class="-hidden-overflow inplace-edit--read-value __d__inplace-edit--read-value"
class="inplace-edit--read-value __d__inplace-edit--read-value"
ng-class="vm.displayClasses"
ng-click="vm.activateIfEditable($event)"
data-click-on-keypress="[13, 32]"

@ -30,23 +30,40 @@ import {openprojectModule} from "../../angular-modules";
import {WorkPackageEditFormController} from "./wp-edit-form.directive";
export class WorkPackageEditModeStateService {
public form: WorkPackageEditFormController;
private _active: boolean = false;
public form:WorkPackageEditFormController;
constructor(protected $rootScope, protected $window, protected $q, protected I18n) {
private _active:boolean = false;
$rootScope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) => {
if (this.active && fromParams.workPackageId
&& toParams.workPackageId !== fromParams.workPackageId) {
constructor(protected $rootScope, protected ConfigurationService, protected $window, protected $q, protected I18n) {
const confirmText = I18n.t('js.work_packages.confirm_edit_cancel');
const cancelEventName = 'beforeunload.confirm_cancel';
const requiresConfirmation = ConfigurationService.warnOnLeavingUnsaved();
if (!$window.confirm(I18n.t('js.work_packages.confirm_edit_cancel'))) {
$rootScope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) => {
// Show confirmation message when transitioning to a new state
// that's not withing the edit mode.
if (this.active && !this.allowedStateChange(toState, toParams, fromState, fromParams)) {
if (requiresConfirmation && !$window.confirm(confirmText)) {
return event.preventDefault();
}
this.cancel();
}
});
// Show confirmation message when browsing to a new page
angular.element($window).on(cancelEventName, (event) => {
if (requiresConfirmation && this.active) {
event.returnValue = confirmText;
event.preventDefault();
return confirmText;
}
});
$rootScope.$on('$destroy', () => {
return angular.element($window).off(cancelEventName);
});
}
public start():boolean {
@ -62,22 +79,22 @@ export class WorkPackageEditModeStateService {
}
return this._active = false;
}
public save() {
if (this.active) {
return this.form.updateWorkPackage().then(wp => {
// Doesn't use cancel() since that resets all values
this.form.closeAllFields();
this._active = false;
return wp;
});
}
return this.$q.reject();
}
public register(form: WorkPackageEditFormController) {
public register(form:WorkPackageEditFormController) {
this.form = form;
// Activate form when it registers after the
@ -90,6 +107,17 @@ export class WorkPackageEditModeStateService {
public get active() {
return this._active;
}
private allowedStateChange(toState, toParams, fromState, fromParams) {
// In new/copy mode, transitions to the same controller are allowed
if (fromState.name.match(/\.(new|copy)$/)) {
return fromState.controller === toState.controller;
}
// When editing an existing WP, transitions on the same WP id are allowed
return toParams.workPackageId !== undefined && toParams.workPackageId === fromParams.workPackageId;
}
}

@ -123,7 +123,8 @@
wp-edit-form="row.object"
wp-edit-form-on-error="handleErroneousColumns(workPackage, fields, attributes)"
wp-edit-form-on-save="onWorkPackageSave(workPackage, fields)"
wp-attachments-formattable>
wp-attachments-formattable
tabindex="-1">
<td ng-if="!row.object.isNew" class="checkbox -short hide-when-print">
<accessible-checkbox name="ids[]"
checkbox-id="work_package{{row.object.id}}"

@ -283,6 +283,7 @@ function WorkPackagesTableController($scope, $rootScope, I18n) {
tableSummary: I18n.t('js.work_packages.table.summary'),
tableSummaryHints: [
I18n.t('js.work_packages.table.text_inline_edit'),
I18n.t('js.work_packages.table.text_select_hint'),
I18n.t('js.work_packages.table.text_sort_hint')
].join(' ')
};

@ -1,4 +1,3 @@
<a id ="{{ activityHtmlId }}-link"
ng-bind="'#' + activityNo"
tabindex="-1"
ui-sref="work-packages.show.activity({ workPackageId: workPackageId, '#': activityHtmlId})"></a>

@ -80,7 +80,7 @@ module.exports = function() {
}
return {
restrict: 'A',
restrict: 'AC',
transclude: false,
link: link,
scope: {

@ -51,6 +51,7 @@ describe('workPackageDetailsToolbar', function() {
var configurationService = {};
configurationService.accessibilityModeEnabled = sinon.stub().returns(false);
configurationService.warnOnLeavingUnsaved = sinon.stub().returns(false);
$provide.constant('$stateParams', stateParams);
$provide.constant('ConfigurationService', configurationService);

@ -83,6 +83,7 @@ describe('WorkPackageContextMenuHelper', function() {
var configurationService = {};
configurationService.isTimezoneSet = sinon.stub().returns(false);
configurationService.warnOnLeavingUnsaved = sinon.stub().returns(false);
$provide.constant('$stateParams', stateParams);
$provide.constant('ConfigurationService', configurationService);

@ -52,7 +52,7 @@ module Redmine::MenuManager::MenuHelper
MenuItems::WikiMenuItem.main_items(project_wiki).each do |main_item|
Redmine::MenuManager.loose :project_menu do |menu|
menu.push "#{main_item.item_class}".to_sym,
{ controller: '/wiki', action: 'show', id: CGI.escape(main_item.title) },
{ controller: '/wiki', action: 'show', id: main_item.slug },
param: :project_id,
caption: main_item.name,
after: :repository,
@ -60,13 +60,12 @@ module Redmine::MenuManager::MenuHelper
main_item.children.each do |child|
menu.push "#{child.item_class}".to_sym,
{ controller: '/wiki', action: 'show', id: CGI.escape(child.title) },
{ controller: '/wiki', action: 'show', id: child.slug },
param: :project_id,
caption: child.name,
html: { class: 'icon2 icon-wiki2' },
parent: "#{main_item.item_class}".to_sym
end
# FIXME using wiki_menu_item#title to reference the wiki page and wiki_menu_item#name as the menu item representation feels wrong
end
end
end

@ -86,14 +86,14 @@ describe ProjectsController, type: :controller do
it 'renders main menu with wiki menu item' do
get 'show', @params
assert_select '#main-menu a.Wiki-menu-item', 'Wiki'
assert_select '#main-menu a.wiki-menu-item', 'wiki'
end
end
describe 'with custom wiki menu item' do
before do
main_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id, name: 'Example', title: 'Example')
sub_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id, name: 'Sub', title: 'Sub', parent_id: main_item.id)
main_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id, name: 'example', title: 'Example')
sub_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id, name: 'sub', title: 'Sub', parent_id: main_item.id)
end
it 'renders show' do
@ -105,13 +105,13 @@ describe ProjectsController, type: :controller do
it 'renders main menu with wiki menu item' do
get 'show', @params
assert_select '#main-menu a.Example-menu-item', 'Example'
assert_select '#main-menu a.example-menu-item', 'example'
end
it 'renders main menu with sub wiki menu item' do
get 'show', @params
assert_select '#main-menu a.Sub-menu-item', 'Sub'
assert_select '#main-menu a.sub-menu-item', 'sub'
end
end
end

@ -260,21 +260,21 @@ describe WikiController, type: :controller do
describe '- main menu links' do
before do
@main_menu_item_for_page_with_content = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id,
name: 'Item for Page with Content',
title: @page_with_content.title)
title: 'Item for Page with Content',
name: @page_with_content.slug)
@main_menu_item_for_new_wiki_page = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id,
name: 'Item for new WikiPage',
title: 'NewWikiPage')
title: 'Item for new WikiPage',
name: 'new-wiki-page')
@other_menu_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id,
name: 'Item for other page',
title: @unrelated_page.title)
title: 'Item for other page',
name: @unrelated_page.slug)
end
shared_examples_for 'all wiki menu items' do
it 'is inactive, when an unrelated page is shown' do
get 'show', id: @unrelated_page.title, project_id: @project.id
get 'show', id: @unrelated_page.slug, project_id: @project.id
expect(response).to be_success
expect(response).to have_exactly_one_selected_menu_item_in(:project_menu)
@ -284,7 +284,7 @@ describe WikiController, type: :controller do
end
it "is inactive, when another wiki menu item's page is shown" do
get 'show', id: @other_wiki_menu_item.title, project_id: @project.id
get 'show', id: @other_wiki_menu_item.name, project_id: @project.id
expect(response).to be_success
expect(response).to have_exactly_one_selected_menu_item_in(:project_menu)
@ -294,7 +294,7 @@ describe WikiController, type: :controller do
end
it 'is active, when the given wiki menu item is shown' do
get 'show', id: @wiki_menu_item.title, project_id: @project.id
get 'show', id: @wiki_menu_item.name, project_id: @project.id
expect(response).to be_success
expect(response).to have_exactly_one_selected_menu_item_in(:project_menu)
@ -306,7 +306,7 @@ describe WikiController, type: :controller do
shared_examples_for 'all existing wiki menu items' do
# TODO: Add tests for new and toc options within menu item
it 'is active on parents item, when new page is shown' do
get 'new_child', id: @wiki_menu_item.title, project_id: @project.identifier
get 'new_child', id: @wiki_menu_item.name, project_id: @project.identifier
expect(response).to be_success
expect(response).to have_no_selected_menu_item_in(:project_menu)
@ -316,7 +316,7 @@ describe WikiController, type: :controller do
end
it 'is inactive, when a toc page is shown' do
get 'index', id: @wiki_menu_item.title, project_id: @project.id
get 'index', id: @wiki_menu_item.name, project_id: @project.id
expect(response).to be_success
expect(response).to have_no_selected_menu_item_in(:project_menu)
@ -328,7 +328,7 @@ describe WikiController, type: :controller do
shared_examples_for 'all wiki menu items with child pages' do
it 'is active, when the given wiki menu item is an ancestor of the shown page' do
get 'show', id: @child_page.title, project_id: @project.id
get 'show', id: @child_page.slug, project_id: @project.id
expect(response).to be_success
expect(response).to have_exactly_one_selected_menu_item_in(:project_menu)
@ -361,8 +361,8 @@ describe WikiController, type: :controller do
describe '- wiki_menu_item containing special chars only' do
before do
@wiki_menu_item = FactoryGirl.create(:wiki_menu_item, navigatable_id: @project.wiki.id,
name: '?',
title: 'Help')
title: '?',
name: 'help')
@other_wiki_menu_item = @other_menu_item
end

@ -36,7 +36,7 @@ describe WikiMenuItemsController, type: :controller do
let(:wiki) { project.wiki }
let(:wiki_page) { FactoryGirl.create(:wiki_page, wiki: wiki) } # first wiki page without child pages
let!(:top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :with_menu_item_options, wiki: wiki, title: wiki_page.title) }
let!(:top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :with_menu_item_options, wiki: wiki, name: wiki_page.slug) }
before :each do
# log in user
@ -46,20 +46,20 @@ describe WikiMenuItemsController, type: :controller do
describe '#edit' do
# more wiki pages with menu items
let(:another_wiki_page) { FactoryGirl.create(:wiki_page, wiki: wiki) } # second wiki page with two child pages
let!(:another_wiki_page_top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: another_wiki_page.title) }
let!(:another_wiki_page_top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: another_wiki_page.slug) }
# child pages of another_wiki_page
let(:child_page) { FactoryGirl.create(:wiki_page, parent: another_wiki_page, wiki: wiki) }
let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: child_page.title) }
let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: child_page.slug) }
let(:another_child_page) { FactoryGirl.create(:wiki_page, parent: another_wiki_page, wiki: wiki) }
let!(:another_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: another_child_page.title, parent: top_level_wiki_menu_item) }
let!(:another_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: another_child_page.slug, parent: top_level_wiki_menu_item) }
let(:grand_child_page) { FactoryGirl.create(:wiki_page, parent: child_page, wiki: wiki) }
let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: grand_child_page.title) }
let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: grand_child_page.slug) }
context 'when no parent wiki menu item has been configured yet' do
context 'and it is a child page' do
before do get :edit, project_id: project.id, id: child_page.title end
before do get :edit, project_id: project.id, id: child_page.slug end
subject { response }
it 'preselects the wiki menu item of the parent page as parent wiki menu item option' do
@ -72,7 +72,7 @@ describe WikiMenuItemsController, type: :controller do
before do
# ensure the parent page of grand_child_page is not a main item
child_page_wiki_menu_item.tap { |page| page.parent = top_level_wiki_menu_item }.save
get :edit, project_id: project.id, id: grand_child_page.title
get :edit, project_id: project.id, id: grand_child_page.slug
end
subject { response }
@ -84,7 +84,7 @@ describe WikiMenuItemsController, type: :controller do
end
context 'when a parent wiki menu item has already been configured' do
before do get :edit, project_id: project.id, id: another_child_page.title end
before do get :edit, project_id: project.id, id: another_child_page.slug end
subject { response }
it 'preselects the parent wiki menu item that is already assigned' do

@ -82,7 +82,7 @@ describe 'Create repository', type: :feature, js: true, selenium: true do
find("option[value='#{vendor}']").select_option
end
shared_examples 'has only the type which is hidden' do |type, vendor|
shared_examples 'has only the type which is selected' do |type, vendor|
it 'should display one type' do
# There seems to be an issue with how the
# select is accessed after the async form loading
@ -98,7 +98,7 @@ describe 'Create repository', type: :feature, js: true, selenium: true do
content = find("#"+"#{vendor}-#{type}", visible: false)
expect(content).not_to be_nil
expect(content[:style]).to match("display: none")
scm_type.should be_checked
end
end
@ -168,7 +168,7 @@ describe 'Create repository', type: :feature, js: true, selenium: true do
context 'with Subversion selected' do
let(:vendor) { 'subversion' }
it_behaves_like 'has only the type which is hidden', 'existing', 'subversion'
it_behaves_like 'has only the type which is selected', 'existing', 'subversion'
context 'and managed repositories' do
include_context 'with tmpdir'
@ -186,12 +186,12 @@ describe 'Create repository', type: :feature, js: true, selenium: true do
context 'with Git selected' do
let(:vendor) { 'git' }
it_behaves_like 'has only the type which is hidden', 'local', 'git'
it_behaves_like 'has only the type which is selected', 'local', 'git'
context 'and managed repositories, but not ours' do
let(:config) {
{ subversion: { manages: '/tmp/whatever' } }
}
it_behaves_like 'has only the type which is hidden', 'local', 'git'
it_behaves_like 'has only the type which is selected', 'local', 'git'
end
context 'and managed repositories' do

@ -0,0 +1,78 @@
#-- 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.
#++
require 'spec_helper'
require Rails.root.join('db/migrate/20160803094931_wiki_menu_titles_to_slug.rb')
describe 'Wiki menu_items migration', type: :feature do
let(:project) { FactoryGirl.create :project }
let(:wiki_page) {
FactoryGirl.create :wiki_page_with_content,
wiki: project.wiki,
title: 'Base de donées'
}
let!(:menu_item) {
FactoryGirl.create(:wiki_menu_item,
:with_menu_item_options,
wiki: project.wiki,
name: 'My linked page',
title: wiki_page.title)
}
before do
project.wiki.pages << wiki_page
# Run the title replacement of the migration
::WikiMenuTitlesToSlug.new.migrate_menu_items
menu_item.reload
end
it 'updates the menu item' do
expect(menu_item.name).to eq(wiki_page.slug)
expect(menu_item.title).to eq('My linked page')
end
describe 'visiting the wiki' do
let(:user) { FactoryGirl.create :admin }
before do
login_as(user)
end
it 'shows the menu item' do
visit project_wiki_path(project, project.wiki)
link = page.find('#menu-sidebar a.icon-wiki', text: menu_item.name)
link.click
expect(page).to have_selector('.wiki-title', text: wiki_page.title)
expect(current_path).to eq(project_wiki_path(project, wiki_page.slug))
end
end
end

@ -0,0 +1,152 @@
# -- 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.
# ++
require 'spec_helper'
describe 'Cancel editing work package', js: true do
let(:user) { FactoryGirl.create(:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:work_package) { FactoryGirl.create(:work_package, project: project) }
let(:work_package2) { FactoryGirl.create(:work_package, project: project) }
let(:wp_page) { ::Pages::AbstractWorkPackage.new(work_package) }
let(:wp_table) { ::Pages::WorkPackagesTable.new }
let(:paths) {
[
new_work_packages_path,
new_split_work_packages_path,
new_project_work_packages_path(project),
new_split_project_work_packages_path(project)
]
}
before do
work_package
work_package2
login_as(user)
end
def expect_active_edit(path)
visit path
loading_indicator_saveguard
expect(page).to have_selector('.wp-edit-field.subject.-active')
end
def expect_subject(val)
subject = page.find('#wp-new-inline-edit--field-subject')
expect(subject.value).to eq(val)
end
def move_to_home_page(alert: true)
find('.home-link').click
page.driver.browser.switch_to.alert.accept if alert
expect(page).to have_selector('h2', text: 'OpenProject')
end
it 'shows an alert when moving to other pages' do
paths.each do |path|
expect_active_edit(path)
move_to_home_page
end
end
it 'shows an alert when moving to other states' do
expect_active_edit(new_split_work_packages_path)
loading_indicator_saveguard
wp_table.expect_work_package_listed(work_package2)
wp_table.open_split_view(work_package2)
page.driver.browser.switch_to.alert.dismiss
expect(page).to have_selector('.wp-edit-field.subject.-active')
expect(wp_page).not_to have_alert_dialog
# Actually move somewhere to accept the beforeunload
move_to_home_page
end
it 'cancels the editing when clicking the button' do
paths.each do |path|
expect_active_edit(path)
find('#work-packages--edit-actions-cancel').click
expect(wp_page).not_to have_alert_dialog
end
end
it 'allows to move from split to full screen in edit mode' do
# Start creating on split view
expect_active_edit(new_split_work_packages_path)
find('#wp-new-inline-edit--field-subject').set 'foobar'
# Expect editing works when moving to full screen
find('#work-packages-show-view-button').click
expect(wp_page).not_to have_alert_dialog
expect(page).to have_selector('.wp-edit-field.subject.-active')
expect_subject('foobar')
# Moving back also works
page.evaluate_script('window.history.back()')
expect(wp_page).not_to have_alert_dialog
expect(page).to have_selector('.wp-edit-field.subject.-active')
expect_subject('foobar')
# Cancel edition
find('#work-packages--edit-actions-cancel').click
expect(wp_page).not_to have_alert_dialog
# Visiting another page does not create alert
find('.home-link').click
expect(wp_page).not_to have_alert_dialog
end
context 'when user does not want to be warned' do
before do
FactoryGirl.create(:user_preference, user: user, others: { warn_on_leaving_unsaved: false })
end
it 'does not alert when moving anywhere' do
# Moving to angular states
expect_active_edit(new_split_work_packages_path)
wp_table.expect_work_package_listed(work_package2)
wp_table.open_split_view(work_package2)
expect(wp_page).not_to have_alert_dialog
expect(page).to have_no_selector('.wp-edit-field.subject.-active')
expect(page).to have_selector('.work-packages--details--subject', text: work_package2.subject)
# Moving somewhere else
expect_active_edit(new_split_work_packages_path)
move_to_home_page(alert: false)
end
end
end

@ -181,6 +181,8 @@ describe 'edit work package', js: true do
cf_field = wp_page.edit_field("customField#{custom_field.id}")
cf_field.expect_active!
cf_field.expect_value('')
find('#work-packages--edit-actions-cancel').click
end
end

@ -43,8 +43,9 @@ describe MenuItems::WikiMenuItem, type: :model do
@project.reload
wiki_item = @project.wiki.wiki_menu_items.first
expect(wiki_item.name).to eql 'Wiki'
expect(wiki_item.name).to eql 'wiki'
expect(wiki_item.title).to eql 'Wiki'
expect(wiki_item.slug).to eql 'wiki'
expect(wiki_item.options[:index_page]).to eql true
expect(wiki_item.options[:new_wiki_page]).to eql true
end
@ -53,8 +54,8 @@ describe MenuItems::WikiMenuItem, type: :model do
wikipage = FactoryGirl.create(:wiki_page, title: 'Oldtitle')
menu_item_1 = FactoryGirl.create(:wiki_menu_item, navigatable_id: wikipage.wiki.id,
name: 'Item 1',
title: 'Oldtitle')
title: 'Item 1',
name: wikipage.slug)
wikipage.title = 'Newtitle'
wikipage.save!

@ -58,9 +58,9 @@ describe WikiPage, type: :model do
describe '#nearest_parent_menu_item' do
let(:child_page) { FactoryGirl.create(:wiki_page, parent: wiki_page, wiki: wiki) }
let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: child_page.title, parent: wiki_page.menu_item) }
let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: child_page.slug, parent: wiki_page.menu_item) }
let(:grand_child_page) { FactoryGirl.create(:wiki_page, parent: child_page, wiki: wiki) }
let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, title: grand_child_page.title) }
let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, wiki: wiki, name: grand_child_page.slug) }
context 'when called without options' do
it 'returns the menu item of the parent page' do
@ -95,7 +95,7 @@ describe WikiPage, type: :model do
it 'ensures that there is still a wiki menu item named like the wiki start page' do
expect(wiki.wiki_menu_items).to be_one
expect(wiki.wiki_menu_items.first.name).to eq(wiki.start_page)
expect(wiki.wiki_menu_items.first.name).to eq(wiki.start_page.to_url)
end
end
end

@ -0,0 +1,35 @@
#-- 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.
#++
# Scrolls a native element into view using JS
def scroll_to_element(element)
script = <<-JS
arguments[0].scrollIntoView(true);
JS
Capybara.current_session.driver.browser.execute_script(script, element.native)
end

@ -30,7 +30,9 @@ class WorkPackageTextAreaField < WorkPackageField
end
def submit_by_click
element.find(control_link).click
target = element.find(control_link)
scroll_to_element(target)
target.click
end
def submit_by_keyboard
@ -38,7 +40,9 @@ class WorkPackageTextAreaField < WorkPackageField
end
def cancel_by_click
element.find(control_link(:cancel)).click
target = element.find(control_link(:cancel))
scroll_to_element(target)
target.click
end
def field_type

Loading…
Cancel
Save