Merge branch 'dev' into feature/grid_my_page

pull/6834/head
Jens Ulferts 6 years ago
commit 4867d4ccc8
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 3
      .codeclimate.yml
  2. 5
      .pkgr.yml
  3. 2
      .travis.yml
  4. 11
      Dockerfile
  5. 118
      Gemfile
  6. 581
      Gemfile.lock
  7. 2
      Gemfile.modules
  8. 2
      Rakefile
  9. 2
      app/assets/javascripts/action_menu.js
  10. 5
      app/assets/javascripts/onboarding/backlogs_tour.js
  11. 4
      app/assets/javascripts/onboarding/homescreen_tour.js
  12. 17
      app/assets/javascripts/onboarding/work_package_tour.js
  13. 2
      app/assets/javascripts/vendor/enjoyhint.js
  14. 2
      app/assets/stylesheets/content/_action_menu_main.sass
  15. 10
      app/assets/stylesheets/content/_advanced_filters.sass
  16. 2
      app/assets/stylesheets/content/_attributes_group.sass
  17. 21
      app/assets/stylesheets/content/_attributes_key_value.sass
  18. 24
      app/assets/stylesheets/content/_autocomplete.sass
  19. 3
      app/assets/stylesheets/content/_buttons.sass
  20. 3
      app/assets/stylesheets/content/_custom_actions.sass
  21. 27
      app/assets/stylesheets/content/_forms.lsg
  22. 38
      app/assets/stylesheets/content/_forms.sass
  23. 41
      app/assets/stylesheets/content/_forum.sass
  24. 2
      app/assets/stylesheets/content/_index.sass
  25. 7
      app/assets/stylesheets/content/_loading_indicator.sass
  26. 27
      app/assets/stylesheets/content/_modal.sass
  27. 5
      app/assets/stylesheets/content/_notifications.sass
  28. 3
      app/assets/stylesheets/content/_notifications_mobile.sass
  29. 17
      app/assets/stylesheets/content/_simple_filters.sass
  30. 14
      app/assets/stylesheets/content/_table.sass
  31. 9
      app/assets/stylesheets/content/editor/_ckeditor.sass
  32. 10
      app/assets/stylesheets/content/work_packages/_table_content.sass
  33. 2
      app/assets/stylesheets/content/work_packages/_table_hierarchy.sass
  34. 5
      app/assets/stylesheets/content/work_packages/inplace_editing/_display_fields.sass
  35. 1
      app/assets/stylesheets/content/work_packages/inplace_editing/_textareas.sass
  36. 5
      app/assets/stylesheets/content/work_packages/new/_type_status_row.sass
  37. 50
      app/assets/stylesheets/content/work_packages/single_view/_single_view.sass
  38. 12
      app/assets/stylesheets/content/work_packages/timelines/_timelines.sass
  39. 8
      app/assets/stylesheets/content/work_packages/timelines/_timelines_header.sass
  40. 2
      app/assets/stylesheets/layout/_base_mobile.sass
  41. 2
      app/assets/stylesheets/layout/_breadcrumb.sass
  42. 1
      app/assets/stylesheets/layout/_drop_down.sass
  43. 16
      app/assets/stylesheets/layout/_top_menu.sass
  44. 16
      app/assets/stylesheets/layout/work_packages/_details_view.sass
  45. 1
      app/assets/stylesheets/layout/work_packages/_full_view.sass
  46. 3
      app/assets/stylesheets/layout/work_packages/_mobile.sass
  47. 2
      app/assets/stylesheets/layout/work_packages/_table.sass
  48. 101
      app/assets/stylesheets/openproject/_accessibility.sass
  49. 3
      app/assets/stylesheets/openproject/_generic.sass
  50. 13
      app/assets/stylesheets/openproject/_mixins.sass
  51. 2
      app/assets/stylesheets/openproject/_onboarding.sass
  52. 3
      app/assets/stylesheets/vendor/_enjoyhint.sass
  53. 2
      app/cells/members/table_cell.rb
  54. 59
      app/cells/oauth/applications/row_cell.rb
  55. 50
      app/cells/oauth/applications/table_cell.rb
  56. 3
      app/cells/table_cell.rb
  57. 2
      app/cells/views/table/show.erb
  58. 63
      app/contracts/oauth/application_contract.rb
  59. 39
      app/contracts/projects/base_contract.rb
  60. 43
      app/contracts/projects/delete_contract.rb
  61. 21
      app/controllers/account_controller.rb
  62. 10
      app/controllers/application_controller.rb
  63. 2
      app/controllers/auth_sources_controller.rb
  64. 2
      app/controllers/boards_controller.rb
  65. 2
      app/controllers/groups_controller.rb
  66. 5
      app/controllers/messages_controller.rb
  67. 2
      app/controllers/news_controller.rb
  68. 118
      app/controllers/oauth/applications_controller.rb
  69. 20
      app/controllers/oauth/auth_base_controller.rb
  70. 67
      app/controllers/oauth/grants_controller.rb
  71. 8
      app/controllers/onboarding_controller.rb
  72. 21
      app/controllers/projects_controller.rb
  73. 2
      app/controllers/repositories_controller.rb
  74. 10
      app/controllers/roles_controller.rb
  75. 2
      app/controllers/statuses_controller.rb
  76. 2
      app/controllers/sys_controller.rb
  77. 2
      app/controllers/timelog_controller.rb
  78. 4
      app/controllers/types_controller.rb
  79. 4
      app/controllers/versions_controller.rb
  80. 8
      app/controllers/wiki_controller.rb
  81. 4
      app/controllers/workflows_controller.rb
  82. 32
      app/helpers/application_helper.rb
  83. 4
      app/helpers/hide_sections_helper.rb
  84. 34
      app/helpers/oauth_helper.rb
  85. 2
      app/helpers/repositories_helper.rb
  86. 4
      app/helpers/sort_helper.rb
  87. 39
      app/helpers/static_links_helper.rb
  88. 2
      app/helpers/work_packages_helper.rb
  89. 85
      app/mailers/base_mailer.rb
  90. 90
      app/mailers/project_mailer.rb
  91. 82
      app/mailers/user_mailer.rb
  92. 4
      app/models/changeset.rb
  93. 4
      app/models/concerns/virtual_attribute.rb
  94. 2
      app/models/custom_actions/actions/project.rb
  95. 2
      app/models/custom_actions/conditions/project.rb
  96. 2
      app/models/custom_style.rb
  97. 2
      app/models/enterprise_token.rb
  98. 2
      app/models/enumeration.rb
  99. 22
      app/models/journal/aggregated_journal.rb
  100. 2
      app/models/menu_items/wiki_menu_item.rb
  101. Some files were not shown because too many files have changed in this diff Show More

@ -29,8 +29,9 @@ plugins:
enabled: true
brakeman:
enabled: true
# as long as bundler-audit does not support bundler 2.0 we disable it
bundler-audit:
enabled: true
enabled: false
eslint:
enabled: true
checks:

@ -4,6 +4,11 @@ targets:
debian-8: &debian8
build_dependencies:
- libsqlite3-dev
dependencies:
- poppler-utils
- unrtf
- tesseract-ocr
- catdoc
debian-9:
<<: *debian8
ubuntu-14.04:

@ -59,7 +59,7 @@ before_install:
# work around https://github.com/travis-ci/travis-ci/issues/8969
- travis_retry gem update --system
# Don't install 1.16.3
- gem install bundler -v 1.16.2
- gem install bundler -v 2.0.1 --no-document
# Install Node latest LTS
# This should only be necessary when preparing the cache or for npm test runs

@ -1,12 +1,15 @@
FROM ruby:2.5-stretch
ENV NODE_VERSION="8.11.1"
ENV BUNDLER_VERSION="1.16.2"
ENV NODE_VERSION="10.15.0"
ENV BUNDLER_VERSION="2.0.1"
ENV APP_USER app
ENV APP_PATH /usr/src/app
ENV APP_DATA /var/db/openproject
ENV ATTACHMENTS_STORAGE_PATH /var/db/openproject/files
# Set a default key base, ensure to provide a secure value in production environments!
ENV SECRET_KEY_BASE=OVERWRITE_ME
# install node + npm
RUN curl https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz | tar xzf - -C /usr/local --strip-components=1
@ -25,7 +28,7 @@ RUN apt-get update -qq && \
# we don't want to pollute any locally-mounted directory
RUN useradd -d /home/$APP_USER -m $APP_USER
RUN mkdir -p $APP_PATH $APP_DATA
RUN gem install bundler --version "${bundler_version}"
RUN gem install bundler --version "${bundler_version}" --no-document
WORKDIR $APP_PATH
@ -58,7 +61,7 @@ RUN chown -R $APP_USER:$APP_USER $APP_DATA
COPY packaging/conf/database.yml ./config/database.yml
# Run the npm postinstall manually after it was copied
RUN DATABASE_URL=sqlite3:///tmp/db.sqlite3 SECRET_TOKEN=foobar RAILS_ENV=production bundle exec rake assets:precompile
RUN DATABASE_URL=sqlite3:///tmp/db.sqlite3 RAILS_ENV=production bundle exec rake assets:precompile
# Include pandoc
RUN DATABASE_URL=sqlite3:///tmp/db.sqlite3 RAILS_ENV=production bundle exec rails runner "puts ::OpenProject::TextFormatting::Formats::Markdown::PandocDownloader.check_or_download!"

@ -33,13 +33,16 @@ ruby '~> 2.5.1'
gem 'actionpack-xml_parser', '~> 2.0.0'
gem 'activemodel-serializers-xml', '~> 1.0.1'
gem 'activerecord-session_store', '~> 1.1.0'
gem 'rails', '~> 5.1.6'
gem 'listen', '~> 3.1' # Use for event-based reloaders
gem 'rails', '~> 5.2.2'
gem 'responders', '~> 2.4'
gem 'rubytree', git: 'https://github.com/dr0verride/RubyTree.git', ref: '06f53ee'
gem 'rdoc', '>= 2.4.2'
gem 'omniauth', git: 'https://github.com/oliverguenther/omniauth', ref: '40c6f5f751d2da7cce5444bbd96c390c450440a9'
# Maintain our own omniauth due to relative URL root issues
# see upstream PR: https://github.com/omniauth/omniauth/pull/903
gem 'omniauth', git: 'https://github.com/opf/omniauth', ref: 'fe862f986b2e846e291784d2caa3d90a658c67f0'
gem 'doorkeeper', git: 'https://github.com/doorkeeper-gem/doorkeeper', ref: 'ce969eee6c16aa8082b0c77ebb5968d9e9b6a57b'
gem 'request_store', '~> 1.4.1'
gem 'warden', '~> 1.2'
@ -50,20 +53,21 @@ gem 'will_paginate', '~> 3.1.0'
gem 'friendly_id', '~> 5.2.1'
gem 'acts_as_list', '~> 0.9.9'
gem 'acts_as_tree', '~> 2.7.0'
gem 'acts_as_tree', '~> 2.8.0'
gem 'awesome_nested_set', '~> 3.1.3'
gem 'rubytree', git: 'https://github.com/dr0verride/RubyTree.git', ref: '06f53ee'
gem 'typed_dag', '~> 2.0.2'
gem 'addressable', '~> 2.5.2'
# Provide timezone info for TZInfo used by AR
gem 'tzinfo-data', '~> 1.2018.4'
gem 'tzinfo-data', '~> 1.2018.9'
# to generate html-diffs (e.g. for wiki comparison)
gem 'htmldiff'
# Generate url slugs with #to_url and other string niceties
gem 'stringex', '~> 2.7.1'
gem 'stringex', '~> 2.8.5'
# CommonMark markdown parser with GFM extension
gem 'commonmarker', '~> 0.17.9'
@ -76,13 +80,12 @@ gem 'escape_utils', '~> 1.0'
# Syntax highlighting used in html-pipeline with rouge
gem 'rouge', '~> 3.1.1'
# HTML sanitization used for html-pipeline
gem 'sanitize', '~> 4.6.0'
gem 'sanitize', '~> 5.0.0'
# HTML autolinking for mails and urls (replaces autolink)
gem 'rinku', '~> 2.0.4'
# Version parsing with semver
gem 'semantic', '~> 1.6.1'
# generates SVG Graphs
# used for statistics on svn repositories
gem 'svg-graph', '~> 2.1.0'
@ -98,13 +101,13 @@ gem 'posix-spawn', '~> 0.3.13', require: false
gem 'bcrypt', '~> 3.1.6'
gem 'multi_json', '~> 1.12.1'
gem 'oj', '~> 3.5.0'
# We rely on this specific version, which is the latest as of now (end of 2016),
gem 'multi_json', '~> 1.13.1'
gem 'oj', '~> 3.7.0'
# We rely on this specific version, which is the latest as of now (start of 2019),
# because we have to apply to it a bugfix which could break things in other versions.
# This can be removed as soon as said bugfix is integrated into rabl itself.
# See: config/initializers/rabl_hack.rb
gem 'rabl', '~> 0.13.0'
gem 'rabl', '~> 0.14.0'
gem 'daemons'
gem 'delayed_job_active_record', '~> 4.1.1'
@ -115,19 +118,19 @@ gem 'rack-protection', '~> 2.0.0'
# It allows whitelisting, blacklisting, throttling, and tracking based
# on arbitrary properties of the request.
# https://github.com/kickstarter/rack-attack
gem 'rack-attack', '~> 5.2.0'
gem 'rack-attack', '~> 5.4.2'
# CSP headers
gem 'secure_headers', '~> 5.0.5'
gem 'secure_headers', '~> 6.0.0'
# Providing health checks
gem 'okcomputer', '~> 1.16.0'
gem 'okcomputer', '~> 1.17.3'
gem 'gon', '~> 6.2.0'
gem 'gon', '~> 6.2.1'
# catch exceptions and send them to any airbrake compatible backend
# don't require by default, instead load on-demand when actually configured
gem 'airbrake', '~> 5.1.0', require: false
gem 'airbrake', '~> 7.4.0', require: false
gem 'transactional_lock', git: 'https://github.com/finnlabs/transactional_lock.git',
branch: 'master'
@ -135,8 +138,8 @@ gem 'transactional_lock', git: 'https://github.com/finnlabs/transactional_lock.g
gem 'prawn', '~> 2.2'
gem 'prawn-table', '~> 0.2.2'
gem 'cells-rails', '~> 0.0.6'
gem 'cells-erb', '~> 0.0.8'
gem 'cells-erb', '~> 0.1.0'
gem 'cells-rails', '~> 0.0.9'
gem 'meta-tags', '~> 2.6.0'
@ -150,10 +153,10 @@ group :production do
gem 'unicorn-worker-killer', require: false
end
gem 'autoprefixer-rails', '~> 7.1.5'
gem 'autoprefixer-rails', '~> 9.4.5'
gem 'bourbon', '~> 4.3.4'
gem 'i18n-js', '~> 3.0.0'
gem 'sass', '3.5.1'
gem 'i18n-js', '~> 3.2.0'
gem 'sass', '3.7.3'
gem 'sass-rails', '~> 5.0.6'
gem 'sprockets', '~> 3.7.0'
@ -161,30 +164,27 @@ gem 'sprockets', '~> 3.7.0'
# also, better than thin since we can control worker concurrency.
gem 'unicorn'
gem 'nokogiri', '~> 1.8.5'
gem 'nokogiri', '~> 1.10.0'
# carrierwave 0.11.3 should allow to use fog-aws without the rest of the
# fog dependency chain. We only need aws here, so we can avoid it
# at the cost of referencing carrierwave#master for now.
gem 'carrierwave', '~> 1.3.1'
gem 'fog-aws'
gem 'carrierwave', '~> 1.2.2'
gem 'aws-sdk-core', '~> 3.20.2'
gem 'aws-sdk-core', '~> 3.45.0'
# File upload via fog + screenshots on travis
gem 'aws-sdk-s3', '~> 1.9.1'
gem 'aws-sdk-s3', '~> 1.30.1'
gem 'openproject-token', '~> 1.0.1'
gem 'plaintext', '0.1.0'
gem 'plaintext', '~> 0.3.0'
gem 'rest-client', '~> 2.0'
gem 'ruby-progressbar', '~> 1.9.0', require: false
gem 'ruby-progressbar', '~> 1.10.0', require: false
group :test do
gem 'rack-test', '~> 1.0.0'
gem 'shoulda-context', '~> 1.2'
gem 'launchy', '~> 2.4.3'
gem 'rack-test', '~> 1.1.0'
gem 'shoulda-context', '~> 1.2'
# Require factory_bot for usage with openproject plugins testing
# FactoryBot needs to be available when loading app otherwise factory
@ -197,42 +197,40 @@ group :test do
# and other niceties
gem 'test-prof', '~> 0.7.3'
gem 'cucumber', '~> 3.0.0'
gem 'cucumber', '~> 3.1.0'
gem 'cucumber-rails', '~> 1.6.0', require: false
gem 'database_cleaner', '~> 1.6'
gem 'rack_session_access'
# not possible to upgrade to 3.6+ until rails is 5.1+
gem 'rspec', '~> 3.7.0'
gem 'rspec', '~> 3.8.0'
gem 'rspec-activemodel-mocks', '~> 1.1.0', git: 'https://github.com/rspec/rspec-activemodel-mocks'
# also add to development group, so "spec" rake task gets loaded
gem 'rspec-rails', '~> 3.7.2', group: :development
gem 'rspec-activemodel-mocks', '~> 1.0.3', git: 'https://github.com/rspec/rspec-activemodel-mocks'
gem 'rspec-rails', '~> 3.8.1', group: :development
# Retry failures within the same environment
gem 'retriable', '~> 3.1.1'
gem 'rspec-retry', '~> 0.5.6'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec-example_disabler', git: 'https://github.com/finnlabs/rspec-example_disabler.git'
gem 'rspec-legacy_formatters', '~> 1.0.1', require: false
# brings back testing for 'assigns' and 'assert_template' extracted in rails 5
gem 'rails-controller-testing', '~> 1.0.2'
gem 'capybara', '~> 3.11.1'
gem 'capybara', '~> 3.12.0'
gem 'capybara-screenshot', '~> 1.0.17'
gem 'capybara-select2', git: 'https://github.com/goodwill/capybara-select2', ref: '585192e'
gem 'chromedriver-helper', '~> 2.1.0'
gem 'selenium-webdriver', '~> 3.14'
gem 'fuubar', '~> 2.3.1'
gem 'fuubar', '~> 2.3.2'
gem 'timecop', '~> 0.9.0'
gem 'webmock', '~> 3.1.0', require: false
gem 'simplecov', '~> 0.16.0', require: false
gem 'shoulda-matchers', '~> 3.1', require: nil
gem 'json_spec', '~> 1.1.4'
gem 'equivalent-xml', '~> 0.6'
gem 'json_spec', '~> 1.1.4'
gem 'shoulda-matchers', '~> 3.1', require: nil
gem 'simplecov', '~> 0.16.0', require: false
gem 'parallel_tests', '~> 2.21.3'
gem 'parallel_tests', '~> 2.27.1'
end
group :ldap do
@ -240,31 +238,34 @@ group :ldap do
end
group :development do
gem 'letter_opener'
gem 'faker'
gem 'letter_opener'
gem 'livingstyleguide', '~> 2.0.1'
gem 'spring'
gem 'spring-commands-rspec'
gem 'rubocop'
gem 'active_record_query_trace'
end
group :development, :test do
gem 'puma', '~> 3.12.0'
gem 'thin', '~> 1.7.2'
gem 'ruby-prof', require: false
gem 'puma', '~> 3.11.3'
# Tracing and profiling gems
gem 'rack-mini-profiler', require: false
gem 'flamegraph', require: false
gem 'rack-mini-profiler', require: false
gem 'ruby-prof', require: false
gem 'stackprof', require: false
gem 'pry-byebug', '~> 3.6.0', platforms: [:mri]
gem 'pry-rails', '~> 0.3.6'
gem 'pry-rescue', '~> 1.5.0'
gem 'pry-stack_explorer', '~> 0.4.9.2'
gem 'pry-rescue', '~> 1.4.5'
gem 'pry-byebug', '~> 3.6.0', platforms: [:mri]
gem 'bootsnap', '~> 1.1.2', require: false
end
gem 'bootsnap', '~> 1.3.2', require: true
# API gems
gem 'grape', '~> 1.1'
@ -278,7 +279,7 @@ platforms :mri, :mingw, :x64_mingw do
end
group :postgres do
gem 'pg', '~> 1.0.0'
gem 'pg', '~> 1.1.0'
end
end
@ -287,13 +288,13 @@ group :opf_plugins do
end
group :docker, optional: true do
gem 'passenger', '~> 5.3.3'
gem 'passenger', '~> 6.0.1'
# Used to easily precompile assets
gem 'sqlite3', require: false
gem 'rails_12factor', require: !!ENV['HEROKU']
gem 'health_check', require: !!ENV['HEROKU']
gem 'newrelic_rpm', require: !!ENV['HEROKU']
gem 'rails_12factor', require: !!ENV['HEROKU']
gem 'sqlite3', require: false
end
# Load Gemfile.local, Gemfile.plugins, plugins', and custom Gemfiles
@ -302,5 +303,6 @@ gemfiles = Dir.glob File.expand_path('../{Gemfile.plugins,Gemfile.modules,Gemfil
gemfiles << ENV['CUSTOM_PLUGIN_GEMFILE'] unless ENV['CUSTOM_PLUGIN_GEMFILE'].nil?
gemfiles.each do |file|
next unless File.readable?(file)
eval_gemfile(file)
end

File diff suppressed because it is too large Load Diff

@ -10,7 +10,7 @@ gem 'omniauth-openid_connect-providers',
gem 'omniauth-openid-connect',
git: 'https://github.com/finnlabs/omniauth-openid-connect.git',
ref: '42c4bd27aaab986e0d8723da50314135691a0955'
ref: '46f0c33bee2c885c89dd2866f5cf847da62b3482'
group :opf_plugins do
# included so that engines can reference OpenProject::Version

@ -32,7 +32,7 @@
require File.expand_path('../config/application', __FILE__)
OpenProject::Application.load_tasks
OpenProject::Application.load_rake_tasks
Rake::Task[:default].clear
task default: 'test:suite:run'

@ -94,7 +94,7 @@ jQuery(function ($) {
});
}
$('.project-actions, .legacy-actions-main, .legacy-actions-specific, .toolbar-items').each(function (idx, menu) {
$('.project-actions, .toolbar-items').each(function (idx, menu) {
install_menu_logic($(menu));
});
});

@ -4,6 +4,7 @@
{
'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_overview'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow'
},
{
@ -11,6 +12,7 @@
'selector': '#sprint_backlogs_container .backlog .menu-trigger',
'description': I18n.t('js.onboarding.steps.backlogs_task_board_arrow'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('#sprint_backlogs_container .backlog .menu-trigger')[0].click();
}
@ -20,6 +22,7 @@
'selector': '#sprint_backlogs_container .backlog .menu .items',
'description': I18n.t('js.onboarding.steps.backlogs_task_board_select'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark',
onNext: function () {
$('#sprint_backlogs_container .backlog .show_task_board')[0].click();
@ -31,11 +34,13 @@
{
'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_task_board'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow'
},
{
'next #main-menu-work-packages-wrapper': I18n.t('js.onboarding.steps.wp_toggler'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('#main-menu-work-packages')[0].click();
}

@ -3,7 +3,8 @@
window.homescreenOnboardingTourSteps = [
{
'next #top-menu': I18n.t('js.onboarding.steps.welcome'),
'skipButton': {className: 'enjoyhint_btn-transparent'},
'skipButton': {className: 'enjoyhint_btn-transparent', text: I18n.t('js.onboarding.buttons.skip')},
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-hidden-arrow'
},
{
@ -12,7 +13,6 @@
'event': 'custom',
'showSkip': false,
'containerClass': '-dark -hidden-arrow',
'containerClass': '-dark -hidden-arrow',
'clickable': true,
onBeforeStart: function () {
// Handle the correct project selection and redirection

@ -4,6 +4,7 @@
{
'next .wp-table--row': I18n.t('js.onboarding.steps.wp_list'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$(".wp-table--cell-span.id a ")[0].click();
}
@ -11,11 +12,13 @@
{
'next .work-packages-full-view--split-left': I18n.t('js.onboarding.steps.wp_full_view'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow'
},
{
'next .work-packages-list-view-button': I18n.t('js.onboarding.steps.wp_back_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('.work-packages-list-view-button')[0].click();
}
@ -23,11 +26,13 @@
{
'next .add-work-package': I18n.t('js.onboarding.steps.wp_create_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'shape': 'circle'
},
{
'next .timeline-toolbar--button': I18n.t('js.onboarding.steps.wp_timeline_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'shape': 'circle',
onNext: function () {
$('.timeline-toolbar--button')[0].click();
@ -36,28 +41,32 @@
{
'next .work-packages-tabletimeline--timeline-side': I18n.t('js.onboarding.steps.wp_timeline'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow'
},
{
'next .main-menu--arrow-left-to-project': I18n.t('js.onboarding.steps.sidebar_arrow'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('.main-menu--arrow-left-to-project')[0].click();
}
},
{
'next .members-menu-item': I18n.t('js.onboarding.steps.members'),
'showSkip': false
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
},
{
'next .wiki-menu--main-item': I18n.t('js.onboarding.steps.wiki'),
'showSkip': false
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
},
{
'next .menu-item--help': I18n.t('js.onboarding.steps.help_menu'),
'shape': 'circle',
'nextButton': {text: I18n.t('js.onboarding.steps.got_it')},
'showSkip': false
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.got_it')}
}
];
});

@ -1288,7 +1288,7 @@ var EnjoyHint;
top: label_y + label_height + 20
});
var left_skip = next_btn_x + that.$next_btn.width() + 10;
var left_skip = next_btn_x + that.$next_btn.width() + 30;
if (that.nextBtn == "hide"){

@ -31,7 +31,7 @@
ul
list-style-type: none
margin: 0
width: 240px
min-width: 240px
border: 1px solid #dddddd
padding: 3px 0
background: #ffffff

@ -55,6 +55,8 @@
grid-gap: 10px
align-items: center
margin-bottom: 10px
&.--without-operator
grid-template-columns: 20% 45% 50px
.advanced-filters--filter-name,
.advanced-filters--add-filter-label
@ -109,19 +111,15 @@
.advanced-filters--spacer
border-top: 1px solid $filters--border-color
border-top: 1px solid $filters--border-color
height: 1px
margin: 0.75rem 1rem
margin: 0.75rem 0
.advanced-filters--spacer,
.advanced-filters--filter
&.hidden
display: none !important
fieldset#date-range p
margin: 2px 0 2px 0
@include breakpoint(680px down)
.advanced-filters--filters
.advanced-filters--filter

@ -28,6 +28,8 @@
.attributes-group
margin-top: 1.6875rem
position: relative
.attributes-group--header
@include grid-block

@ -27,26 +27,29 @@
//++
.attributes-key-value
@include grid-block
@include grid-layout(2)
@include grid-visible-overflow
display: flex
flex-wrap: wrap
font-size: 0.875rem
line-height: 1.6
.attributes-key-value--key
@extend .form--label
@include grid-size(4)
@include text-shortener
display: flex
flex: 1 0 35%
max-width: 35%
margin-bottom: 0.1875rem
padding: 0.375rem 0 !important
padding: 0.375rem 0
font-weight: bold
// Ensure that the text is shortened while the help icon will be displayed
> wp-replacement-label
@include text-shortener
flex: 0 1 auto
.attributes-key-value--value-container
@include grid-content(8)
@include grid-visible-overflow
display: flex
flex: 1 0 65%
max-width: 65%
margin-bottom: 0.1875rem
padding: 0 !important
align-self: center
&.not-editable

@ -136,3 +136,27 @@ mark.ui-autocomplete-match
padding: 0
font-weight: bold
text-transform: uppercase
// -------------------------- ng-select --------------------------
.ng-select
width: 100%
font-size: 14px
.ng-value-container
width: calc(100% - 30px)
.ng-select-container
z-index: auto !important
.ng-value-container input
-webkit-box-sizing: border-box !important
-moz-box-sizing: border-box !important
box-sizing: border-box !important
.ng-value
@include text-shortener
.ng-option
font-size: 0.875rem
line-height: 1.6
.work-package-table--container .ng-dropdown-panel
z-index: auto !important

@ -155,6 +155,9 @@ html.-browser-windows.-browser-chrome
border: none
padding: 0 1px
&.-expand
@include button-expand
&:visited, &:active
@include varprop(color, content-link-color)

@ -35,8 +35,9 @@
.custom-action
display: flex
justify-content: flex-end
flex: 1 1 auto
flex: 0 1 auto
margin-bottom: 0.5rem
max-width: calc(100vw - 20px)
@include text-shortener
.button

@ -1067,33 +1067,6 @@ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
</form>
```
## Styled checkboxes [TO BE REFACTORED]
```
<label class="checkbox-label">
<input type="checkbox">
<div class="styled-checkbox"></div>
</label>
<br>
<br>
<label class="checkbox-label">
checkbox label
<input type="checkbox">
<div class="styled-checkbox"></div>
</label>
<br>
<br>
<label class="checkbox-label">
<input type="checkbox">
<div class="styled-checkbox"></div>
checkbox label
</label>
```
# Forms: Radio buttons
## Within a form, multiple

@ -208,44 +208,6 @@ hr
width: 18px
opacity: 0.001
\:checked + .styled-checkbox:after
opacity: 1
.styled-checkbox
box-sizing: content-box
display: inline-block
vertical-align: top
width: 18px
height: 18px
padding: 0 5px
user-select: none
&:before
content: ''
position: absolute
width: 18px
height: 18px
background: #ffffff
border: 1px solid #cacaca
border-radius: 2px
cursor: pointer
box-shadow: inset 0 1px #fff
&:after
opacity: 0
content: ''
position: absolute
margin: 5px 2px
// Length of check tail
width: 11px
// Length of check foot
height: 3px
background: transparent
border: 3px solid #666666
border-top: none
border-right: none
transform: rotate(-50deg)
%form--fieldset-or-section
padding: 1rem 0 0
margin-bottom: 1rem

@ -26,47 +26,8 @@
// See docs/COPYRIGHT.rdoc for more details.
//++
@mixin legacy-actions-defaults($margin-top: 7px)
float: right
margin-top: $margin-top
font-size: 0.9rem
> li
float: left
position: relative
list-style: none
@mixin contextual($margin-top: 8px)
.message-reply-menu
float: right
white-space: nowrap
line-height: 1.4em
margin-top: $margin-top
padding-left: 10px
ul.legacy-actions-main
@include legacy-actions-defaults
margin-left: 20px
ul.legacy-actions-specific,
.nosidebar ul.legacy-actions-specific
@include legacy-actions-defaults(-7px)
p.subtitle + ul.legacy-actions-specific
@include legacy-actions-defaults(-57px)
.message-reply-menu
@include contextual(-60px)
> .button
margin-right: 0
.legacy-actions--inline-label
@extend .inline-label
margin-bottom: 1rem
height: 1.5em
.form-label
color: $content-icon-link-color
input, select
font-size: 1em
select
padding: 0 1.5rem 0.0 0.5rem

@ -6,6 +6,7 @@
@import content/editable_toolbar
@import content/forms
@import content/forms_mobile
@import content/forum
@import content/choice
@import content/collapsible_section
@import content/copy_to_clipboard
@ -16,7 +17,6 @@
@import content/links
@import content/loading_indicator
@import content/action_menu_main
@import content/legacy_actions
@import content/enterprise
@import content/members
@import content/my_account

@ -48,6 +48,8 @@
width: 6px
display: inline-block
animation: block-waveStretchDelay 1.2s infinite ease-in-out
margin: 0 1px
.block-1
animation-delay: -1.2s
.block-2
@ -62,9 +64,6 @@
&.-compact
margin: 20px auto
[class^='block-']
margin: 0 1px
&.-small
height: 34px
margin: 0
@ -73,8 +72,6 @@
[class^='block-']
width: 4px
@keyframes block-waveStretchDelay
0%, 40%, 100%
transform: scaleY(0.4)

@ -31,6 +31,8 @@ $ng-modal-background: $body-background
$ng-modal-close-color: $light-gray
$ng-modal-image-height: 135px
$ng-modal-image-width: $ng-modal-image-height
$modal-header-height: $header-height
$modal-footer-height: $modal-header-height
// The portal element of a modal element
// ( = the outer element should span the entire view )
@ -73,6 +75,9 @@ $ng-modal-image-width: $ng-modal-image-height
.op-modal--modal-body
margin: 1em 0
padding: 0 1.5rem
max-height: calc(100vh - #{$modal-header-height} - #{$modal-footer-height})
overflow: auto
@include styled-scroll-bar-vertical
&.-formattable
p:last-of-type
@ -86,7 +91,9 @@ $ng-modal-image-width: $ng-modal-image-height
margin-right: 0
.op-modal--modal-close-button
float: right
position: absolute
top: 0.75rem
right: 0.75rem
cursor: pointer
@include varprop(color, body-font-color)
&:hover
@ -101,8 +108,8 @@ $ng-modal-image-width: $ng-modal-image-height
.op-modal--modal-header
display: flex
align-items: center
height: $header-height
padding: 0 1.5rem
height: $modal-header-height
padding: 0 45px 0 1.5rem
border-bottom-style: solid
@include varprop(background-color, header-bg-color)
@include varprop(border-bottom-width, header-border-bottom-width)
@ -110,26 +117,22 @@ $ng-modal-image-width: $ng-modal-image-height
*
@include varprop(color, header-item-font-color)
height: $header-height
line-height: $header-height
height: $modal-header-height
line-height: $modal-header-height
h2, h3
@include text-shortener
.op-modal--modal-close-button
float: none
position: absolute
right: 1rem
right: 10px
top: 0
@include varprop(line-height, modal-header-height)
@include varprop(color, header-item-font-color)
.avatar,
.icon-context
margin-right: 0.5rem
.op-modal--modal-close-button
@include varprop(line-height, header-height)
@include varprop(color, header-item-font-color)
// Specific styles for columns-modal
.columns-modal--content
margin-bottom: 15px

@ -363,11 +363,6 @@ $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

@ -31,7 +31,4 @@
.flash,
#errorExplanation
left: 20px !important
margin: 0 !important
right: 20px !important
width: calc(100% - 40px) !important
top: 22px !important

@ -75,14 +75,18 @@ $filters--border-color: $gray !default
grid-template-columns: repeat(auto-fill, 50%)
align-items: center
.form--radio-button-container
margin-right: 20px
flex-grow: 0
button,
.button
margin: 0 10px 10px 0
.simple-filters--filter.-with-radio-buttons
grid-template-columns: 10% repeat(auto-fit, minmax(45%, 1fr))
grid-template-rows: repeat(auto-fill, 35px)
grid-gap: 10px 0
.simple-filters--filter-name.form--label
grid-column: 2
.simple-filters--controls
grid-column: 1 / -1
margin-top: 1rem
@ -92,5 +96,6 @@ $filters--border-color: $gray !default
margin: auto 0
@include breakpoint(680px down)
.simple-filters--filter-value
grid-column: 1 / -1
.simple-filters--filter:not(.-with-radio-buttons)
.simple-filters--filter-value
grid-column: 1 / -1

@ -87,6 +87,9 @@ table.generic-table
text-align: left
line-height: 34px
padding: 0
// This is needed for a bug in FF as described here
// https://www.456bereastreet.com/archive/201305/firefox_and_the_magical_text-overflowellipsis_z-index/
z-index: 1
&.-right
text-align: right
@ -192,12 +195,11 @@ table.generic-table
margin: 0
// Enable sticky headers in chrome
html.-supports-sticky-headers
thead.-sticky th
position: sticky
top: 0
background: white
// Enable sticky headers
thead.-sticky th
position: sticky
top: 0
background: white
.generic-table--footer-outer

@ -59,3 +59,12 @@
.ck.ck-block-toolbar-button
transform: translateX( -15px )
z-index: 1000 !important
// Override fixed position of toolbar
// Otherwise the toolbar will 'disappear' behind the topmenu
.ck.ck-sticky-panel__placeholder
height: 0 !important
.ck.ck-sticky-panel__content
position: unset !important

@ -33,19 +33,23 @@
.wp-table--row
cursor: pointer
.wp-table--row,
#empty-row-notification
height: $table-timeline--row-height
// Hide collapsed rows (hierarchies, relations)
&.-collapsed
display: none
.work-package-table--container table.generic-table tbody td
padding-left: 0
padding-top: 0
padding-bottom: 0
// To display the input field border correctly (input height is 24px)
line-height: 24px
height: 41px
// Styles for inline editable attributes
.work-package-table--container td.-editable
padding-top: 0
padding-bottom: 0
display: table-cell
width: auto

@ -26,7 +26,7 @@
display: inline
.wp-table--hierarchy-span
text-align: end
text-align: right
display: block
float: left
margin-left: 5px

@ -30,6 +30,8 @@
.wp-edit-field--display-field
display: inline-block
max-width: 100%
@include text-shortener
white-space: inherit
&.-placeholder
font-style: italic
@ -37,5 +39,6 @@
p
word-wrap: break-word
.wp-edit-field--text
.wp-edit-field--text,
wp-edit-field
width: 100%

@ -58,7 +58,6 @@
text-align: center
// Align controls via flex
display: flex
z-index: 1
// Disabled submit styles when not applicable
.inplace-edit--control--save[disabled] a,

@ -3,14 +3,11 @@
display: flex
font-size: 24px
// Set the correct height when editing these fields
.wp-inline-edit--field
height: 40px
// Add some minor margin between active status and type field
#wp-new-inline-edit--field-type,
.work-packages--type-selector
margin-left: 5px
min-width: 100px
// Fix display left padding of tpye
.wp-edit-field--display-field

@ -194,29 +194,20 @@ i
// (b) ugly content wrap (e.g. in the label)
// These values are what matches best at the moment. So be careful when changing them.
.attributes-key-value--key
flex: 0 0 45%
flex: 1 0 45%
max-width: 45%
.attributes-key-value--value-container
flex: 0 0 55%
position: relative
flex: 1 0 55%
max-width: 55%
// Implement two column layout for WP full screen view
@media screen and (min-width: 92rem), print
.action-show .attributes-group,
.full-create .attributes-group
.action-show .-can-have-columns,
.full-create .-can-have-columns
.-columns-2
column-count: 2
column-gap: 3rem
.attributes-key-value
-webkit-column-break-inside: avoid
page-break-inside: avoid
break-inside: avoid
overflow: hidden
// For some reason chrome seems to treat a two column layout
// as if it would result in showing the backside of this element.
// This leads to input and select elements not showing their values.
backface-visibility: visible
@include two-column-layout
@supports (column-span: all)
// Let some elements still span both columns
@ -238,39 +229,40 @@ i
.wp-info-wrapper
display: flex
flex-wrap: wrap
align-items: center
padding-top: 0.5rem
.wp-status-button .button
@include text-shortener
padding: 5px
margin-bottom: 0
@include varprop(background-color, status-selector-bg-color)
border: none
color: white
white-space: nowrap
// Should not be longer than mobile screen width (margin of 15px left and right)
max-width: calc(100vw - 30px)
.button--text
margin: 0 5px
attribute-help-text .help-text--entry
line-height: 25px
margin: 0 10px 0 -10px
.icon:before
padding: 0
attribute-help-text
flex: 0
.help-text--entry
line-height: 25px
margin: 0 10px 0 -14px
.icon:before
padding: 0
.work-packages--info-row
flex-grow: 2
flex: 1 1 200px
attribute-help-text,
.wp-status-button,
.work-packages--info-row
margin-bottom: 0.5rem
&.-wrapped
flex-wrap: wrap
.work-packages--info-row,
attribute-help-text,
wp-custom-action
margin-bottom: 0.5rem
.work-packages--new .wp-status-button
padding: 3px
.button

@ -53,21 +53,19 @@
// Add bottom border to timeline
.wp-table-timeline--body
border-bottom: 1px solid $table-row-border-color
outline: 1px solid $table-row-border-color
// Ensure the height of table and timeline rows
.wp-timeline-cell
// Ensure the height of table and timeline rows
height: $table-timeline--row-height
// Vertically center elements
display: flex
align-items: center
&:first-child
// The first table child is 1px larger
height: calc(#{$table-timeline--row-height} + 1px)
.wp-timeline-cell
// Vertically center elements
display: flex
align-items: center
// Need to disable flex behavior on children
> div
flex: 0 0 auto

@ -1,9 +1,12 @@
.wp-table-timeline--header
// In Safari custom elements have width and height 0 per default
// That is why position: sticky in its children won't work
wp-timeline-header
display: block
height: $generic-table--header-height
width: 100%
position: sticky
top: 0
z-index: 1
z-index: 2
.wp-timeline--header-element
background: white
@ -39,4 +42,3 @@
font-weight: bold
font-size: 11px
height: 15px

@ -33,7 +33,7 @@
-webkit-overflow-scrolling: touch
#main-menu ~ #content-wrapper
padding: 5px 15px 0 15px !important
padding: 15px
#main
grid-template-columns: auto

@ -109,5 +109,5 @@ ul.breadcrumb
@include text-shortener
// Hide projects in normal mode
body:not(.accessibility-mode) .breadcrumb .breadcrumb-project-element
.breadcrumb .breadcrumb-project-element
display: none

@ -44,7 +44,6 @@
.dropdown .dropdown-menu,
.dropdown .dropdown-panel,
.drop-down .menu-drop-down-container
min-width: 200px
list-style: none
background: #FFF
border: solid 1px #DDD

@ -29,6 +29,8 @@
$hamburger-right: -3px
$hamburger-width: 50px
$search-input-width: 180px
%top-menu-hover-styles
@include varprop(background, header-item-bg-hover-color)
@include varprop(color, header-item-font-hover-color)
@ -206,7 +208,7 @@ input.top-menu-search--input
display: inline-block
border: none !important
border-radius: 25px
width: 180px
width: $search-input-width
outline: 0
font-size: 0.75rem
&::-ms-clear
@ -215,6 +217,18 @@ input.top-menu-search--input
.top-menu-search.-collapsed &
display: none
.search-autocomplete--results
width: 300px
.top-menu-search--loading
top: $header-height
left: -($search-input-width - $header-height) - 1
.search-autocomplete--wp-id
color: $gray-dark
font-size: 13px
white-space: nowrap
#quick-search
float: right

@ -42,17 +42,9 @@ body.action-create
// Will eventually be overridden by the resizer
flex-basis: 580px
&.-columns-2
&.-can-have-columns
.-columns-2
column-count: 2
column-gap: 4rem
.attributes-key-value
-webkit-column-break-inside: avoid
page-break-inside: avoid
break-inside: avoid
overflow: hidden
backface-visibility: visible
@include two-column-layout
.work-packages--details
height: 100%
@ -105,7 +97,6 @@ body.action-create
.wp-inline-edit--field
height: 38px
line-height: 36px
padding: 0 0.375rem
.work-packages--type-selector
flex-shrink: 0
@ -117,5 +108,8 @@ body.action-create
.work-package--new-state
margin-bottom: 55px
.title-container
overflow: visible
.work-packages--attachments
margin-bottom: 25px

@ -197,6 +197,7 @@
.work-packages--subject-type-row
display: flex
position: relative
// Fix: align and size hover border like buttons in toolbar.
.wp-edit-field.-no-label:not(.-active) .wp-table--cell-span

@ -86,7 +86,6 @@
.attributes-key-value--key,
.attributes-key-value--value-container
@include grid-size(6)
font-size: 1rem
margin-bottom: 20px
@ -97,6 +96,7 @@
border: none
.attributes-key-value--key
flex-basis: 30% !important
padding: 0 !important
.work-packages--panel-inner > .attributes-group:first-child
@ -147,6 +147,7 @@
.toolbar-item
flex-grow: 0
.work-packages-full-view--split-container
border-top: none

@ -81,7 +81,7 @@
.row-hovered
background: #E5F2FA
z-index: 100
z-index: 1
// Left part of the split view

@ -34,104 +34,3 @@
width: 1px
height: 1px
overflow: hidden
// -------------------- Rules for accessibility mode --------------------
body.accessibility-mode
// -------------------- General --------------------
a:focus,
input:focus,
select:focus,
textarea:focus,
button:focus,
*:focus
outline: 3px solid darkblue
.hidden-for-accessibility
display: none !important
// Highlight invalid input fields
.form--field-container,
.wp-edit-field
input:out-of-range,
input:invalid
border: 2px solid #9E2A1C
h1:hover a.wiki-anchor,
h2:hover a.wiki-anchor,
h3:hover a.wiki-anchor
display: none
// Show skip links for motorically handicapped users
.skip-navigation-link:focus
top: auto
left: auto
// Necessary for correct contrast ratio
button:disabled,
a.button:disabled
color: $body-font-color
opacity: 0.80
button.-active,
a.button.-active
color: $body-font-color
&:hover,
&:focus
color: $body-font-color
// Show breadcrumb for better navigation
&:not(.controller-homescreen) #breadcrumb
display: 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%
// -------------------- Top menu --------------------
// Mark currently selected top menu items
#top-menu
li a,
li.drop-down a
border: 1px solid transparent
li a.selected,
li.drop-down.selected a
background: $main-menu-bg-selected-background !important
&:hover
border-color: $main-menu-border-color
// -------------------- WP view & table --------------------
// Special columns modal for accessibility mode
.op-modal--portal.columns-modal .op-modal--modal-container
overflow: auto
margin-top: 5vh
&.controller-work_packages
// Cancel out breadcrumb
#content
height: calc(100% - #{$breadcrumb-height})
// Neccessary to have enough space for the border in IE
.wp-edit-field--container .wp-inline-edit--field
margin: 3px
max-width: calc(100% - 6px)
// Show inplace-editing icons so that they can be accessed directly
.inplace-editing--trigger-link
.inplace-editing--container
border-color: $inplace-edit--border-color
.inplace-edit--icon-wrapper
visibility: visible
// Give space to show the border
.wp-relations-create-button .relation-create
width: calc(100% - 6px)
padding: 0
margin: 3px
.wp-status-button .button
@include varprop(color, body-font-color)
font-weight: bold

@ -53,6 +53,9 @@
.-hidden-overflow
overflow: hidden !important
.-visible-overflow
overflow: visible !important
.indent
padding-left: 10px

@ -140,3 +140,16 @@
&::-webkit-scrollbar-thumb
background: #ddd
visibility: visible
@mixin two-column-layout
column-count: 2
column-gap: 3rem
.attributes-key-value
-webkit-column-break-inside: avoid
page-break-inside: avoid
break-inside: avoid
// For some reason chrome seems to treat a two column layout
// as if it would result in showing the backside of this element.
// This leads to input and select elements not showing their values.
backface-visibility: visible

@ -46,8 +46,6 @@
.onboarding--footer
display: flex
align-items: center
margin: 1em 0
margin-top: 0
justify-content: flex-end
.button

@ -42,13 +42,14 @@
.enjoyhint_next_btn, .enjoyhint_skip_btn
-webkit-box-sizing: content-box
width: 100px
min-width: 100px
height: 40px
font: normal normal normal 17px/40px "Advent Pro", Helvetica, sans-serif
cursor: pointer
margin: 0 auto
letter-spacing: 1px
text-align: center
padding: 0 5px
.enjoyhint_close_btn, .enjoyhint_next_btn, .enjoyhint_skip_btn
z-index: 1012

@ -38,7 +38,7 @@ module Members
end
def fix_groups_order(sort_clause)
sort_clause.gsub /groups/, "groups.group_name"
sort_clause.gsub /groups/, "groupalias.group_name"
end
def fix_roles_order(sort_clause)

@ -0,0 +1,59 @@
module OAuth
module Applications
class RowCell < ::RowCell
include ::IconsHelper
include ::OAuthHelper
include ::OpenProject::ObjectLinking
def application
model
end
def name
link_to application.name, oauth_application_path(application)
end
def owner
link_to application.owner.name, user_path(application.owner)
end
def confidential
if application.confidential?
op_icon 'icon icon-checkmark'
end
end
def redirect_uri
urls = application.redirect_uri.split("\n")
safe_join urls, '<br/>'.html_safe
end
def client_credentials
if user_id = application.client_credentials_user_id
link_to_user User.find(user_id)
else
'-'
end
end
def confidential
application.confidential
end
def edit_link
link_to(
I18n.t(:button_edit),
edit_oauth_application_path(application),
class: "oauth-application--edit-link icon icon-edit"
)
end
def button_links
[
edit_link,
delete_link(oauth_application_path(application))
]
end
end
end
end

@ -0,0 +1,50 @@
require_dependency 'oauth/applications/row_cell'
module OAuth
module Applications
class TableCell < ::TableCell
class << self
def row_class
::OAuth::Applications::RowCell
end
end
def initial_sort
%i[id asc]
end
def sortable?
false
end
def columns
headers.map(&:first)
end
def inline_create_link
link_to new_oauth_application_path,
aria: { label: t('oauth.application.new') },
class: 'wp-inline-create--add-link',
title: t('oauth.application.new') do
op_icon('icon icon-add')
end
end
def empty_row_message
I18n.t :no_results_title_text
end
def headers
[
['name', caption: ::Doorkeeper::Application.human_attribute_name(:name)],
['owner', caption: ::Doorkeeper::Application.human_attribute_name(:owner)],
['client_credentials', caption: I18n.t('oauth.client_credentials')],
['redirect_uri', caption: ::Doorkeeper::Application.human_attribute_name(:redirect_uri)],
['confidential', caption: ::Doorkeeper::Application.human_attribute_name(:confidential)],
]
end
end
end
end

@ -102,8 +102,7 @@ class TableCell < RailsCell
end
def render_row(row)
prefix = (self.class.namespace || "table").underscore
cell("#{prefix}/row", row, table: self).call
cell(self.class.row_class, row, table: self).call
end
def initial_sort

@ -65,7 +65,7 @@ See doc/COPYRIGHT.rdoc for more details.
<td colspan="<%= headers.length + 1 %>"><%= empty_row_message %></td>
</tr>
<% end %>
<% for row in rows -%>
<% for row in rows do -%>
<%= render_row row %>
<% end -%>
</tbody>

@ -0,0 +1,63 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'model_contract'
module Oauth
class ApplicationContract < ::ModelContract
def self.model
::Doorkeeper::Application
end
def validate
validate_client_credential_user
super
end
attribute :name
attribute :redirect_uri
attribute :confidential
attribute :owner_id
attribute :owner_type
attribute :scopes
attribute :client_credentials_user_id
private
def validate_client_credential_user
return unless model.client_credentials_user_id.present?
unless User.where(id: model.client_credentials_user_id).exists?
errors.add :client_credentials_user_id, :invalid
end
end
end
end

@ -0,0 +1,39 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'model_contract'
module Projects
class BaseContract < ::ModelContract
def self.model
::Project
end
end
end

@ -0,0 +1,43 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'model_contract'
module Projects
class DeleteContract < BaseContract
def validate
unless user.admin?
errors.add :base, :error_unauthorized
end
super
end
end
end

@ -93,7 +93,7 @@ class AccountController < ApplicationController
render template: 'account/password_recovery'
elsif request.post?
mail = params[:mail]
user = User.find_by_mail(mail)
user = User.find_by_mail(mail) if mail.present?
# Ensure the same request is sent regardless of which email is entered
# to avoid detecability of mails
@ -200,8 +200,7 @@ class AccountController < ApplicationController
if user.save
token.destroy
flash[:notice] = with_accessibility_notice :notice_account_activated,
locale: user.language
flash[:notice] = I18n.t(:notice_account_activated)
else
flash[:error] = I18n.t(:notice_activation_failed)
end
@ -513,8 +512,7 @@ class AccountController < ApplicationController
if @user.save
session[:auth_source_registration] = nil
self.logged_user = @user
flash[:notice] = with_accessibility_notice :notice_account_activated,
locale: @user.language
flash[:notice] = I18n.t(:notice_account_activated)
redirect_to controller: '/my', action: 'account'
end
# Otherwise render register view again
@ -619,8 +617,7 @@ class AccountController < ApplicationController
OpenProject::OmniAuth::Authorization.after_login! user, auth_hash, self
end
flash[:notice] = with_accessibility_notice :notice_account_registered_and_logged_in,
locale: user.language
flash[:notice] = I18n.t(:notice_account_registered_and_logged_in)
redirect_after_login user
end
@ -709,14 +706,4 @@ class AccountController < ApplicationController
token.user
end
end
def with_accessibility_notice(key, locale:)
locale = locale.presence || I18n.locale
text = I18n.t(key, locale: locale)
notice = link_translate(:notice_accessibility_mode,
links: { url: my_settings_url },
locale: locale)
"#{text} #{notice}".html_safe
end
end

@ -260,6 +260,8 @@ class ApplicationController < ActionController::Base
'X-Reason' => 'login needed',
'WWW-Authenticate' => auth_header
end
format.all { head :not_acceptable }
end
return false
end
@ -268,11 +270,7 @@ class ApplicationController < ActionController::Base
def require_admin
return unless require_login
unless User.current.admin?
render_403
return false
end
true
render_403 unless User.current.admin?
end
def deny_access
@ -646,7 +644,7 @@ class ApplicationController < ActionController::Base
self.logged_user = nil
flash[:warning] = I18n.t('notice_forced_logout', ttl_time: Setting.session_ttl)
redirect_to(controller: 'account', action: 'login', back_url: login_back_url)
redirect_to(controller: '/account', action: 'login', back_url: login_back_url)
end
session[:updated_at] = Time.now
end

@ -35,7 +35,7 @@ class AuthSourcesController < ApplicationController
before_action :block_if_password_login_disabled
def index
@auth_sources = AuthSource.page(params[:page])
@auth_sources = AuthSource.page(page_param)
.per_page(per_page_param)
render 'auth_sources/index'

@ -96,7 +96,7 @@ class BoardsController < ApplicationController
.topics
.order(["#{Message.table_name}.sticked_on ASC", sort_clause].compact.join(', '))
.includes(:author, last_reply: :author)
.page(params[:page])
.page(page_param)
.per_page(per_page_param)
end

@ -38,7 +38,7 @@ class GroupsController < ApplicationController
# GET /groups
# GET /groups.xml
def index
@groups = Group.order('lastname ASC')
@groups = Group.order(Arel.sql('lastname ASC'))
respond_to do |format|
format.html # index.html.erb

@ -152,9 +152,8 @@ class MessagesController < ApplicationController
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
respond_to do |format|
format.json do
render json: { subject: subject, content: content }
end
format.json { render json: { subject: subject, content: content } }
format.any { head :not_acceptable }
end
end
end

@ -45,7 +45,7 @@ class NewsController < ApplicationController
scope = @project ? @project.news : News.all
@newss = scope.merge(News.latest_for(current_user, count: 0))
.page(params[:page])
.page(page_param)
.per_page(per_page_param)
respond_to do |format|

@ -0,0 +1,118 @@
#-- encoding: UTF-8
#-- 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.
#++
module OAuth
class ApplicationsController < ::ApplicationController
before_action :require_admin
before_action :new_app, only: %i[new create]
before_action :find_app, only: %i[edit update show destroy]
layout 'admin'
menu_item :oauth_applications
def index
@applications = ::Doorkeeper::Application.includes(:owner).all
end
def new; end
def edit; end
def show
@reveal_secret = flash[:reveal_secret]
flash.delete :reveal_secret
end
def create
call = ::OAuth::PersistApplicationService.new(@application, user: current_user)
.call(permitted_params.oauth_application)
if call.success?
flash[:notice] = t(:notice_successful_create)
flash[:_application_secret] = call.result.plaintext_secret
redirect_to action: :show, id: call.result.id
else
@errors = call.errors
flash[:error] = call.errors.full_messages.join('\n')
render action: :new
end
end
def update
call = ::OAuth::PersistApplicationService.new(@application, user: current_user)
.call(permitted_params.oauth_application)
if call.success?
flash[:notice] = t(:notice_successful_update)
redirect_to action: :index
else
@errors = call.errors
flash[:error] = call.errors.full_messages.join('\n')
render action: :edit
end
end
def destroy
if @application.destroy
flash[:notice] = t(:notice_successful_delete)
else
flash[:error] = t(:error_can_not_delete_entry)
end
redirect_to action: :index
end
protected
def default_breadcrumb
if action_name == 'index'
t('oauth.application.plural')
else
ActionController::Base.helpers.link_to(t('oauth.application.plural'), oauth_applications_path)
end
end
def show_local_breadcrumb
current_user.admin?
end
private
def new_app
@application = ::Doorkeeper::Application.new
end
def find_app
@application = ::Doorkeeper::Application.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end
end

@ -27,12 +27,14 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
# This configuration was added so that we do not accidentially pass a filter chain
# because it was not yet updated
#
# We'll have to look out for deprecation warnings like:
#
# DEPRECATION WARNING: Returning `false` in Active Record and Active Model callbacks
# will not implicitly halt a callback chain in the next release of Rails.
# To explicitly halt the callback chain, please use `throw :abort` instead.
ActiveSupport.halt_callback_chains_on_return_false = true
module OAuth
##
# Base controller for doorkeeper to skip the login check
# because it needs to set a specific return URL
# See config/initializers/doorkeeper.rb
class AuthBaseController < ::ApplicationController
skip_before_action :check_if_login_required
layout 'only_logo'
end
end

@ -0,0 +1,67 @@
#-- encoding: UTF-8
#-- 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.
#++
module OAuth
class GrantsController < ::ApplicationController
before_action :require_login
layout 'my'
menu_item :access_token
def index
@applications = ::Doorkeeper::Application.authorized_for(current_user)
end
def revoke_application
application = find_application
if application.nil?
render_404
return
end
::Doorkeeper::Application.revoke_tokens_and_grants_for(
application.id,
current_user
)
flash[:notice] = I18n.t('oauth.grants.successful_application_revocation', application_name: application.name)
redirect_to controller: '/my', action: :access_token
end
private
def find_application
::Doorkeeper::Application
.where(id: params[:application_id])
.select(:name, :id)
.take
end
end
end

@ -36,11 +36,11 @@ class OnboardingController < ApplicationController
.call(request, permitted_params, params)
if result && result.success
# Remove the first_time_user param so that the modal is not shown again after redirect
# Remove all query params:
# the first_time_user param so that the modal is not shown again after redirect,
# the welcome param so that the analytics is not fired again
uri = Addressable::URI.parse(request.referrer.to_s)
params = uri.query_values
params.delete('first_time_user')
uri.query_values = params
uri.query_values = {}
redirect_to uri.to_s
flash[:notice] = l(:notice_account_updated)

@ -171,6 +171,8 @@ class ProjectsController < ApplicationController
def modules
@project.enabled_module_names = permitted_params.project[:enabled_module_names]
# Ensure the project is touched to update its cache key
@project.touch
flash[:notice] = I18n.t(:notice_successful_update)
redirect_to settings_project_path(@project, tab: 'modules')
end
@ -209,20 +211,17 @@ class ProjectsController < ApplicationController
# Delete @project
def destroy
@project_to_destroy = @project
service = ::Projects::DeleteProjectService.new(user: current_user, project: @project)
call = service.call(delayed: true)
OpenProject::Notifications.send('project_deletion_imminent', project: @project_to_destroy)
@project_to_destroy.destroy
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_delete)
redirect_to controller: 'projects', action: 'index'
end
if call.success?
flash[:notice] = I18n.t('projects.delete.scheduled')
else
flash[:error] = I18n.t('projects.delete.schedule_failed', errors: call.errors.full_messages.join("\n"))
end
hide_project_in_layout
update_demo_project_settings @project_to_destroy, false
redirect_to controller: 'projects', action: 'index'
update_demo_project_settings @project, false
end
def destroy_info

@ -153,7 +153,7 @@ class RepositoriesController < ApplicationController
def revisions
@changesets = @repository.changesets
.includes(:user, :repository)
.page(params[:page])
.page(page_param)
.per_page(per_page_param)
respond_to do |format|

@ -36,7 +36,7 @@ class RolesController < ApplicationController
def index
@roles = Role
.order('builtin, position')
.order(Arel.sql('builtin, position'))
.page(page_param)
.per_page(per_page_param)
@ -48,7 +48,7 @@ class RolesController < ApplicationController
@role = Role.new(permitted_params.role? || { permissions: Role.non_member.permissions })
@permissions = @role.setable_permissions
@roles = Role.order('builtin, position')
@roles = Role.order(Arel.sql('builtin, position'))
end
def create
@ -63,7 +63,7 @@ class RolesController < ApplicationController
notify_changed_roles(:added, @role)
else
@permissions = @role.setable_permissions
@roles = Role.order('builtin, position')
@roles = Role.order(Arel.sql('builtin, position'))
render action: 'new'
end
@ -99,12 +99,12 @@ class RolesController < ApplicationController
end
def report
@roles = Role.order('builtin, position')
@roles = Role.order(Arel.sql('builtin, position'))
@permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
end
def bulk_update
@roles = Role.order('builtin, position')
@roles = Role.order(Arel.sql('builtin, position'))
@roles.each do |role|
new_permissions = params[:permissions][role.id.to_s].presence || []

@ -36,7 +36,7 @@ class StatusesController < ApplicationController
verify method: :get, only: :index, render: { nothing: true, status: :method_not_allowed }
def index
@statuses = Status.page(params[:page])
@statuses = Status.page(page_param)
.per_page(per_page_param)
render action: 'index', layout: false if request.xhr?

@ -39,7 +39,7 @@ class SysController < ActionController::Base
p = Project.active.has_module(:repository)
.includes(:repository)
.references(:repositories)
.order('identifier')
.order(Arel.sql('identifier'))
respond_to do |format|
format.json do
render json: p.to_json(include: :repository)

@ -224,7 +224,7 @@ class TimelogController < ApplicationController
.where(cond.conditions)
.distinct(false)
.order(sort_clause)
.page(params[:page])
.page(page_param)
.per_page(per_page_param)
end

@ -36,7 +36,7 @@ class TypesController < ApplicationController
before_action :require_admin
def index
@types = ::Type.page(params[:page]).per_page(per_page_param)
@types = ::Type.page(page_param).per_page(per_page_param)
end
def type
@ -137,7 +137,7 @@ class TypesController < ApplicationController
end
def load_projects_and_types
@types = ::Type.order('position')
@types = ::Type.order(Arel.sql('position'))
@projects = Project.all
end

@ -36,7 +36,7 @@ class VersionsController < ApplicationController
before_action :authorize
def index
@types = @project.types.order('position')
@types = @project.types.order(Arel.sql('position'))
retrieve_selected_type_ids(@types, @types.select(&:is_in_roadmap?))
@with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_work_packages? : (params[:with_subprojects].to_i == 1)
project_ids = @with_subprojects ? @project.self_and_descendants.map(&:id) : [@project.id]
@ -84,7 +84,7 @@ class VersionsController < ApplicationController
if request.post?
if @version.save
flash[:notice] = l(:notice_successful_create)
redirect_to controller: '/project_settings', action: 'show', tab: 'versions', id: @project
redirect_back_or_default(controller: '/project_settings', action: 'show', tab: 'versions', id: @project)
else
render action: 'new'
end

@ -271,8 +271,8 @@ class WikiController < ApplicationController
.content
.versions
.select(:id, :user_id, :notes, :created_at, :version)
.order('version DESC')
.page(params[:page])
.order(Arel.sql('version DESC'))
.page(page_param)
.per_page(per_page_param)
render layout: !request.xhr?
@ -337,7 +337,7 @@ class WikiController < ApplicationController
# Export wiki to a single html file
def export
if User.current.allowed_to?(:export_wiki_pages, @project)
@pages = @wiki.pages.order('title')
@pages = @wiki.pages.order(Arel.sql('title'))
export = render_to_string action: 'export_multiple', layout: false
send_data(export, type: 'text/html', filename: 'wiki.html')
else
@ -423,7 +423,7 @@ class WikiController < ApplicationController
end
def load_pages_for_index
@pages = @wiki.pages.with_updated_on.order('title').includes(wiki: :project)
@pages = @wiki.pages.with_updated_on.order(Arel.sql('title')).includes(wiki: :project)
end
def default_breadcrumb

@ -116,10 +116,10 @@ class WorkflowsController < ApplicationController
private
def find_roles
@roles = Role.order('builtin, position')
@roles = Role.order(Arel.sql('builtin, position'))
end
def find_types
@types = ::Type.order('position')
@types = ::Type.order(Arel.sql('position'))
end
end

@ -161,21 +161,11 @@ module ApplicationHelper
html_options = { class: css_classes.join(' '), role: 'alert' }.merge(html_options)
content_tag :div, html_options do
if User.current.impaired?
concat(content_tag('a', join_flash_messages(message),
href: '#',
class: 'impaired--empty-link'))
concat(content_tag(:i, '', class: 'icon-close close-handler',
tabindex: '0',
role: 'button',
aria: { label: ::I18n.t('js.close_popup_title') }))
else
concat(join_flash_messages(message))
concat(content_tag(:i, '', class: 'icon-close close-handler',
tabindex: '0',
role: 'button',
aria: { label: ::I18n.t('js.close_popup_title') }))
end
concat(join_flash_messages(message))
concat(content_tag(:i, '', class: 'icon-close close-handler',
tabindex: '0',
role: 'button',
aria: { label: ::I18n.t('js.close_popup_title') }))
end
end
@ -311,10 +301,6 @@ module ApplicationHelper
def body_css_classes
css = ['theme-' + OpenProject::Design.identifier.to_s]
if accessibility_css_enabled? && User.current.impaired?
css << 'accessibility-mode'
end
if params[:controller] && params[:action]
css << 'controller-' + params[:controller]
css << 'action-' + params[:action]
@ -486,14 +472,6 @@ module ApplicationHelper
end
end
def disable_accessibility_css!
@accessibility_css_disabled = true
end
def accessibility_css_enabled?
!@accessibility_css_disabled
end
#
# Returns the footer text displayed in the layout file.
#

@ -37,6 +37,8 @@ module HideSectionsHelper
}
)
include_gon(nonce: content_security_policy_script_nonce, camel_case: true, camel_depth: 15)
nonced_javascript_tag do
include_gon(need_tag: false, nonce: content_security_policy_script_nonce, camel_case: true, camel_depth: 15)
end
end
end

@ -1,4 +1,5 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
@ -27,24 +28,23 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
module ChiliProject
VERSION = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
'ChiliProject::VERSION', 'OpenProject::VERSION'
)
Database = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
'ChiliProject::Database', 'OpenProject::Database'
)
module OAuthHelper
##
# Output the translated scope names for the given application
def oauth_scope_translations(application)
strings = application.scopes.to_a
module PrincipalAllowanceEvaluator
Base = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
'ChiliProject::PrincipalAllowanceEvaluator::Base',
'OpenProject::PrincipalAllowanceEvaluator::Base'
)
if strings.empty?
I18n.t("oauth.scopes.api_v3")
else
safe_join(strings.map { |scope| I18n.t("oauth.scopes.#{scope}", default: scope) }, '</br>'.html_safe)
end
end
Default = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
'ChiliProject::PrincipalAllowanceEvaluator::Default',
'OpenProject::PrincipalAllowanceEvaluator::Default'
)
##
# Get granted applications for the given user
def granted_applications(user = current_user)
tokens = ::Doorkeeper::AccessToken.active_for(user).includes(:application)
tokens.group_by(&:application)
end
end

@ -63,7 +63,7 @@ module RepositoriesHelper
end
def render_changeset_changes
changes = @changeset.file_changes.limit(1000).order('path').map { |change|
changes = @changeset.file_changes.limit(1000).order(Arel.sql('path')).map { |change|
case change.action
when 'A'
# Detects moved/copied files

@ -125,6 +125,10 @@ module SortHelper
.compact
end
def map_each
to_a.map { |criteria| yield criteria }
end
def add!(key, asc)
@criteria.delete_if do |k, _o| k == key end
@criteria = [[key, asc]] + @criteria

@ -0,0 +1,39 @@
#-- encoding: UTF-8
#-- 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.
#++
module StaticLinksHelper
##
# Create a static link to the given key entry
def static_link_to(key)
item = OpenProject::Static::Links.links.fetch key
link_to t(item[:label]), item[:href], class: 'openproject--static-link'
end
end

@ -155,7 +155,7 @@ module WorkPackagesHelper
changed_dates = {}
journals = work_package.journals.where(['created_at >= ?', Date.today.to_time - 7.day])
.order('created_at desc')
.order(Arel.sql('created_at desc'))
journals.each do |journal|
break if changed_dates['start_date'] && changed_dates['due_date']

@ -28,11 +28,96 @@
#++
class BaseMailer < ActionMailer::Base
helper :application, # for format_text
:work_packages, # for css classes
:custom_fields # for show_value
helper IssuesHelper
include OpenProject::LocaleHelper
# wrap in a lambda to allow changing at run-time
default from: Proc.new { Setting.mail_from }
class << self
# Activates/deactivates email deliveries during +block+
def with_deliveries(temporary_state = true, &_block)
old_state = ActionMailer::Base.perform_deliveries
ActionMailer::Base.perform_deliveries = temporary_state
yield
ensure
ActionMailer::Base.perform_deliveries = old_state
end
def generate_message_id(object, user)
# id + timestamp should reduce the odds of a collision
# as far as we don't send multiple emails for the same object
journable = (object.is_a? Journal) ? object.journable : object
timestamp = mail_timestamp(object)
hash = 'openproject'\
'.'\
"#{journable.class.name.demodulize.underscore}"\
'-'\
"#{user.id}"\
'-'\
"#{journable.id}"\
'.'\
"#{timestamp.strftime('%Y%m%d%H%M%S')}"
host = Setting.mail_from.to_s.gsub(%r{\A.*@}, '')
host = "#{::Socket.gethostname}.openproject" if host.empty?
"#{hash}@#{host}"
end
def remove_self_notifications(message, author)
if author.pref && author.pref[:no_self_notified]
message.to = message.to.reject { |address| address == author.mail } if message.to.present?
end
end
def mail_timestamp(object)
if object.respond_to? :created_at
object.send(object.respond_to?(:created_at) ? :created_at : :updated_at)
else
object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
end
end
def host
if OpenProject::Configuration.rails_relative_url_root.blank?
Setting.host_name
else
Setting.host_name.to_s.gsub(%r{\/.*\z}, '')
end
end
def protocol
Setting.protocol
end
def default_url_options
options = super.merge host: host, protocol: protocol
unless OpenProject::Configuration.rails_relative_url_root.blank?
options[:script_name] = OpenProject::Configuration.rails_relative_url_root
end
options
end
end
def mail(headers = {}, &block)
block ||= method(:default_formats_for_setting)
super(headers, &block)
end
def message_id(object, user)
headers['Message-ID'] = "<#{self.class.generate_message_id(object, user)}>"
end
# Prepends given fields with 'X-OpenProject-' to save some duplication
def open_project_headers(hash)
hash.each { |key, value| headers["X-OpenProject-#{key}"] = value.to_s }
end
private
def default_formats_for_setting(format)

@ -0,0 +1,90 @@
#-- encoding: UTF-8
#-- 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.
#++
class ProjectMailer < BaseMailer
def delete_project_completed(project, user:)
open_project_headers Project: project.identifier,
Author: user.login
message_id project, user
with_locale_for(user) do
@project = project
mail to: user.mail, subject: I18n.t('projects.delete.completed', name: project.name)
end
end
def delete_project_failed(project, user:)
open_project_headers Project: project.identifier,
Author: user.login
message_id project, user
with_locale_for(user) do
@project = project
mail to: user.mail, subject: I18n.t('projects.delete.failed', name: project.name)
end
end
def copy_project_failed(user, source_project, target_project_name, errors)
@source_project = source_project
@target_project_name = target_project_name
@errors = errors
open_project_headers 'Source-Project' => source_project.identifier,
'Author' => user.login
message_id project, user
with_locale_for(user) do
subject = I18n.t('copy_project.failed', source_project_name: source_project.name)
mail to: user.mail, subject: subject
end
end
def copy_project_succeeded(user, source_project, target_project, errors)
@source_project = source_project
@target_project = target_project
@errors = errors
open_project_headers 'Source-Project' => source_project.identifier,
'Target-Project' => target_project.identifier,
'Author' => user.login
message_id target_project, user
with_locale_for(user) do
subject = I18n.t('copy_project.succeeded', target_project_name: target_project.name)
mail to: user.mail, subject: subject
end
end
end

@ -28,15 +28,6 @@
#++
class UserMailer < BaseMailer
helper :application, # for format_text
:work_packages, # for css classes
:custom_fields # for show_value
helper IssuesHelper
include OpenProject::LocaleHelper
# wrap in a lambda to allow changing at run-time
default from: Proc.new { Setting.mail_from }
def test_mail(user)
@welcome_url = url_for(controller: '/homescreen')
@ -320,35 +311,6 @@ class UserMailer < BaseMailer
end
end
# Activates/deactivates email deliveries during +block+
def self.with_deliveries(temporary_state = true, &_block)
old_state = ActionMailer::Base.perform_deliveries
ActionMailer::Base.perform_deliveries = temporary_state
yield
ensure
ActionMailer::Base.perform_deliveries = old_state
end
def self.generate_message_id(object, user)
# id + timestamp should reduce the odds of a collision
# as far as we don't send multiple emails for the same object
journable = (object.is_a? Journal) ? object.journable : object
timestamp = mail_timestamp(object)
hash = 'openproject'\
'.'\
"#{journable.class.name.demodulize.underscore}"\
'-'\
"#{user.id}"\
'-'\
"#{journable.id}"\
'.'\
"#{timestamp.strftime('%Y%m%d%H%M%S')}"
host = Setting.mail_from.to_s.gsub(%r{\A.*@}, '')
host = "#{::Socket.gethostname}.openproject" if host.empty?
"#{hash}@#{host}"
end
private
def subject_for_work_package(work_package)
@ -368,45 +330,6 @@ class UserMailer < BaseMailer
message
end
def self.remove_self_notifications(message, author)
if author.pref && author.pref[:no_self_notified]
message.to = message.to.reject { |address| address == author.mail } if message.to.present?
end
end
def self.mail_timestamp(object)
if object.respond_to? :created_at
object.send(object.respond_to?(:created_at) ? :created_at : :updated_at)
else
object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
end
end
def self.host
if OpenProject::Configuration.rails_relative_url_root.blank?
Setting.host_name
else
Setting.host_name.to_s.gsub(%r{\/.*\z}, '')
end
end
def self.protocol
Setting.protocol
end
def self.default_url_options
options = super.merge host: host, protocol: protocol
unless OpenProject::Configuration.rails_relative_url_root.blank?
options[:script_name] = OpenProject::Configuration.rails_relative_url_root
end
options
end
def message_id(object, user)
headers['Message-ID'] = "<#{self.class.generate_message_id(object, user)}>"
end
def references(object, user)
headers['References'] = "<#{self.class.generate_message_id(object, user)}>"
end
@ -421,11 +344,6 @@ class UserMailer < BaseMailer
open_project_headers 'Issue-Assignee' => work_package.assigned_to.login
end
end
# Prepends given fields with 'X-OpenProject-' to save some duplication
def open_project_headers(hash)
hash.each { |key, value| headers["X-OpenProject-#{key}"] = value.to_s }
end
end
##

@ -173,12 +173,12 @@ class Changeset < ActiveRecord::Base
# Returns the previous changeset
def previous
@previous ||= Changeset.where(['id < ? AND repository_id = ?', id, repository_id]).order('id DESC').first
@previous ||= Changeset.where(['id < ? AND repository_id = ?', id, repository_id]).order(Arel.sql('id DESC')).first
end
# Returns the next changeset
def next
@next ||= Changeset.where(['id > ? AND repository_id = ?', id, repository_id]).order('id ASC').first
@next ||= Changeset.where(['id > ? AND repository_id = ?', id, repository_id]).order(Arel.sql('id ASC')).first
end
# Creates a new Change from it's common parameters

@ -94,12 +94,12 @@ module Concerns
# Using attribute_will_change! does not place the value in the tracker but merely forces
# the attribute to be returned when asking the object for changes.
def set_virtual_attribute_was(attribute, value)
attributes = mutation_tracker.send(:attributes)
attributes = mutations_from_database.send(:attributes)
attributes[attribute.to_s].instance_variable_set(:@value_before_type_cast, value)
end
def set_virtual_attribute(attribute, value)
attributes = mutation_tracker.send(:attributes)
attributes = mutations_from_database.send(:attributes)
attributes[attribute.to_s] = attributes[attribute.to_s].with_value_from_user(value)
end
end

@ -50,7 +50,7 @@ class CustomActions::Actions::Project < CustomActions::Actions::Base
def associated
::Project
.select(:id, :name)
.order('LOWER(name)')
.order(Arel.sql('LOWER(name)'))
.map { |u| [u.id, u.name] }
end
end

@ -43,7 +43,7 @@ class CustomActions::Conditions::Project < CustomActions::Conditions::Base
def associated
::Project
.select(:id, :name)
.order('LOWER(name)')
.order(Arel.sql('LOWER(name)'))
.map { |u| [u.id, u.name] }
end
end

@ -6,7 +6,7 @@ class CustomStyle < ActiveRecord::Base
class << self
def current
RequestStore.fetch(:current_custom_style) do
custom_style = CustomStyle.order('created_at DESC').first
custom_style = CustomStyle.order(Arel.sql('created_at DESC')).first
if custom_style.nil?
return nil
else

@ -46,7 +46,7 @@ class EnterpriseToken < ActiveRecord::Base
end
def set_current_token
token = EnterpriseToken.order('created_at DESC').first
token = EnterpriseToken.order(Arel.sql('created_at DESC')).first
if token && token.token_object
token

@ -141,7 +141,7 @@ class Enumeration < ActiveRecord::Base
# Does the +new+ Hash have the same custom values as the previous Enumeration?
def self.same_custom_values?(new, previous)
previous.custom_field_values.each do |custom_value|
if custom_value.value != new['custom_field_values'][custom_value.custom_field_id.to_s]
if new && custom_value.value != new['custom_field_values'][custom_value.custom_field_id.to_s]
return false
end
end

@ -101,14 +101,14 @@ class Journal::AggregatedJournal
# (that means if we can find a valid predecessor), we drop our current row, because it will
# already be present (in a merged form) in the row of our predecessor.
Journal.from("(#{sql_rough_group(1, journable, until_version, journal_id)}) #{table_name}")
.joins("LEFT OUTER JOIN (#{sql_rough_group(2, journable, until_version, journal_id)}) addition
ON #{sql_on_groups_belong_condition(table_name, 'addition')}")
.joins("LEFT OUTER JOIN (#{sql_rough_group(3, journable, until_version, journal_id)}) predecessor
ON #{sql_on_groups_belong_condition('predecessor', table_name)}")
.where('predecessor.id IS NULL')
.order("COALESCE(addition.created_at, #{table_name}.created_at) ASC")
.order("#{version_projection} ASC")
.select("#{table_name}.journable_id,
.joins(Arel.sql("LEFT OUTER JOIN (#{sql_rough_group(2, journable, until_version, journal_id)}) addition
ON #{sql_on_groups_belong_condition(table_name, 'addition')}"))
.joins(Arel.sql("LEFT OUTER JOIN (#{sql_rough_group(3, journable, until_version, journal_id)}) predecessor
ON #{sql_on_groups_belong_condition('predecessor', table_name)}"))
.where(Arel.sql('predecessor.id IS NULL'))
.order(Arel.sql("COALESCE(addition.created_at, #{table_name}.created_at) ASC"))
.order(Arel.sql("#{version_projection} ASC"))
.select(Arel.sql("#{table_name}.journable_id,
#{table_name}.journable_type,
#{table_name}.user_id,
#{table_name}.notes,
@ -117,7 +117,7 @@ class Journal::AggregatedJournal
#{table_name}.activity_type,
COALESCE(addition.created_at, #{table_name}.created_at) \"created_at\",
COALESCE(addition.id, #{table_name}.id) \"id\",
#{version_projection} \"version\"")
#{version_projection} \"version\""))
end
# Returns whether "notification-hiding" should be assumed for the given journal pair.
@ -396,7 +396,7 @@ class Journal::AggregatedJournal
raw_journal = self.class.query_aggregated_journals(journable: journable)
.where("#{self.class.version_projection} < ?", version)
.except(:order)
.order("#{self.class.version_projection} DESC")
.order(Arel.sql("#{self.class.version_projection} DESC"))
.first
@predecessor = raw_journal ? Journal::AggregatedJournal.new(raw_journal) : nil
@ -410,7 +410,7 @@ class Journal::AggregatedJournal
raw_journal = self.class.query_aggregated_journals(journable: journable)
.where("#{self.class.version_projection} > ?", version)
.except(:order)
.order("#{self.class.version_projection} ASC")
.order(Arel.sql("#{self.class.version_projection} ASC"))
.first
@successor = raw_journal ? Journal::AggregatedJournal.new(raw_journal) : nil

@ -33,7 +33,7 @@ class MenuItems::WikiMenuItem < MenuItem
scope :main_items, ->(wiki_id) {
where(navigatable_id: wiki_id, parent_id: nil)
.includes(:children)
.order('id ASC')
.order(Arel.sql('id ASC'))
}
def slug

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save