Merge remote-tracking branch 'origin/dev' into maintenance/ruby-2.7.1-upgrade

This commit is contained in:
ulferts
2020-08-14 10:10:12 +02:00
2162 changed files with 45318 additions and 37679 deletions
Vendored
BIN
View File
Binary file not shown.
+24
View File
@@ -0,0 +1,24 @@
name: Docker
on:
# Scheduled workflows run on the latest commit on the default or base branch
schedule:
- cron: '20 2 * * *' # Daily at 02:20
push:
tags:
- v*
jobs:
publish:
# restrict this job to base repo for now
if: github.repository == 'opf/openproject'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to registry
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: openproject/community-test
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tag_semver: true
cache: ${{ github.event_name != 'schedule' }}
+1 -1
View File
@@ -51,9 +51,9 @@ npm-debug.log*
/backup
/.project
/.loadpath
/app/assets/javascripts/bundles/*.*
/app/assets/javascripts/editor/*
/app/assets/javascripts/locales/*.*
/frontend/src/locales/*.js
/config/additional_environment.rb
/config/configuration.yml
/config/database.yml
-3
View File
@@ -1,5 +1,2 @@
app/assets/javascripts/date-de-DE.js
app/assets/javascripts/date-en-US.js
app/assets/javascripts/jquery_noconflict.js
app/assets/javascripts/project/**/*
app/assets/javascripts/lib/**/*
+2 -2
View File
@@ -11,12 +11,12 @@ targets:
- catdoc
debian-10:
<<: *debian9
ubuntu-14.04:
<<: *debian9
ubuntu-16.04:
<<: *debian9
ubuntu-18.04:
<<: *debian9
ubuntu-20.04:
<<: *debian9
centos-8:
dependencies:
- epel-release
+58 -60
View File
@@ -38,8 +38,7 @@ cache:
bundler: true
directories:
- public/assets
- app/assets/javascripts/bundles
- app/assets/javascripts/locales
- frontend/src/locales
branches:
only:
@@ -91,68 +90,67 @@ jobs:
- bash script/ci/runner.sh npm
- stage: test
name: 'spec_legacy (1/1)'
name: 'legacy specs + cukes (1/1)'
script:
- bash script/ci/setup.sh spec_legacy
- bash script/ci/runner.sh spec_legacy 1 1
- stage: test
name: 'units (1/4)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 4 1
- stage: test
name: 'units (2/4)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 4 2
- stage: test
name: 'units (3/4)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 4 3
- stage: test
name: 'units (4/4)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 4 4
- stage: test
name: 'features (1/4)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 4 1
- stage: test
name: 'features (2/4)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 4 2
- stage: test
name: 'features (3/4)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 4 3
- stage: test
name: 'features (4/4)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 4 4
- stage: test
name: 'plugins:units (1/1)'
script:
- bash script/ci/setup.sh plugins:units
- bash script/ci/runner.sh plugins:units 1 1
if: head_branch !~ /^core\//
- stage: test
name: 'plugins:features (1/1)'
script:
- bash script/ci/setup.sh plugins:features
- bash script/ci/runner.sh plugins:features 1 1
if: head_branch !~ /^core\//
- stage: test
name: 'plugins:cucumber (1/1)'
script:
- bash script/ci/setup.sh plugins:cucumber
- bash script/ci/runner.sh plugins:cucumber 1 1
if: head_branch !~ /^core\//
- stage: test
name: 'units (1/5)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 5 1
- stage: test
name: 'units (2/5)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 5 2
- stage: test
name: 'units (3/5)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 5 3
- stage: test
name: 'units (4/5)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 5 4
- stage: test
name: 'units (5/5)'
script:
- bash script/ci/setup.sh units
- bash script/ci/runner.sh units 5 5
- stage: test
name: 'features (1/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 1
- stage: test
name: 'features (2/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 2
- stage: test
name: 'features (3/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 3
- stage: test
name: 'features (4/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 4
- stage: test
name: 'features (5/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 5
- stage: test
name: 'features (6/6)'
script:
- bash script/ci/setup.sh features
- bash script/ci/runner.sh features 6 6
addons:
chrome: stable
+9 -8
View File
@@ -34,7 +34,7 @@ gem 'actionpack-xml_parser', '~> 2.0.0'
gem 'activemodel-serializers-xml', '~> 1.0.1'
gem 'activerecord-import', '~> 1.0.2'
gem 'activerecord-session_store', '~> 1.1.0'
gem 'rails', '~> 6.0.3'
gem 'rails', '~> 6.0.3.2'
gem 'responders', '~> 3.0'
gem 'rdoc', '>= 2.4.2'
@@ -85,7 +85,7 @@ gem 'escape_utils', '~> 1.0'
# Syntax highlighting used in html-pipeline with rouge
gem 'rouge', '~> 3.17.0'
# HTML sanitization used for html-pipeline
gem 'sanitize', '~> 5.1.0'
gem 'sanitize', '~> 5.2.1'
# HTML autolinking for mails and urls (replaces autolink)
gem 'rinku', '~> 2.0.4'
# Version parsing with semver
@@ -156,29 +156,27 @@ group :production do
gem 'unicorn-worker-killer', require: false
end
gem 'autoprefixer-rails', '~> 9.7.4'
gem 'bourbon', '~> 6.0.0'
gem 'i18n-js', '~> 3.6.0'
gem 'rails-i18n', '~> 6.0.0'
gem 'sassc-rails', '~> 2.1.0'
gem 'sprockets', '~> 3.7.0'
# required by Procfile, for deployment on heroku or packaging with packager.io.
# also, better than thin since we can control worker concurrency.
gem 'unicorn'
gem 'puma', '~> 4.3.1' # used for development and optionally for production
gem 'puma', '~> 4.3.5' # used for development and optionally for production
gem 'nokogiri', '~> 1.10.8'
gem 'carrierwave', '~> 1.3.1'
gem 'carrierwave_direct', '~> 2.1.0'
gem 'fog-aws'
gem 'aws-sdk-core', '~> 3.91.0'
# File upload via fog + screenshots on travis
gem 'aws-sdk-s3', '~> 1.61.0'
gem 'openproject-token', '~> 1.0.2'
gem 'openproject-token', '~> 2.1.1'
gem 'plaintext', '~> 0.3.2'
@@ -245,12 +243,15 @@ group :development do
gem 'faker'
gem 'letter_opener'
gem 'livingstyleguide', '~> 2.1.0'
gem 'spring'
gem 'spring-commands-rspec'
gem 'rubocop'
# Gems for living styleguide
gem 'sassc-rails'
gem 'livingstyleguide', '~> 2.1.0'
end
group :development, :test do
+79 -78
View File
@@ -114,6 +114,11 @@ PATH
specs:
grids (1.0.0)
PATH
remote: modules/job_status
specs:
openproject-job_status (1.0.0)
PATH
remote: modules/ldap_groups
specs:
@@ -168,7 +173,6 @@ PATH
remote: modules/reporting
specs:
openproject-reporting (1.0.0)
jquery-tablesorter (~> 1.27.0)
openproject-costs
reporting_engine
@@ -195,26 +199,26 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.0.3)
actioncable (6.0.3)
actionpack (= 6.0.3)
actioncable (6.0.3.2)
actionpack (= 6.0.3.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.3)
actionpack (= 6.0.3)
activejob (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
actionmailbox (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
mail (>= 2.7.1)
actionmailer (6.0.3)
actionpack (= 6.0.3)
actionview (= 6.0.3)
activejob (= 6.0.3)
actionmailer (6.0.3.2)
actionpack (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.3)
actionview (= 6.0.3)
activesupport (= 6.0.3)
actionpack (6.0.3.2)
actionview (= 6.0.3.2)
activesupport (= 6.0.3.2)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
@@ -222,30 +226,30 @@ GEM
actionpack-xml_parser (2.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
actiontext (6.0.3)
actionpack (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
actiontext (6.0.3.2)
actionpack (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
nokogiri (>= 1.8.5)
actionview (6.0.3)
activesupport (= 6.0.3)
actionview (6.0.3.2)
activesupport (= 6.0.3.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.3)
activesupport (= 6.0.3)
activejob (6.0.3.2)
activesupport (= 6.0.3.2)
globalid (>= 0.3.6)
activemodel (6.0.3)
activesupport (= 6.0.3)
activemodel (6.0.3.2)
activesupport (= 6.0.3.2)
activemodel-serializers-xml (1.0.2)
activemodel (> 5.x)
activesupport (> 5.x)
builder (~> 3.1)
activerecord (6.0.3)
activemodel (= 6.0.3)
activesupport (= 6.0.3)
activerecord (6.0.3.2)
activemodel (= 6.0.3.2)
activesupport (= 6.0.3.2)
activerecord-import (1.0.4)
activerecord (>= 3.2)
activerecord-nulldb-adapter (0.4.0)
@@ -256,12 +260,12 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0)
activestorage (6.0.3)
actionpack (= 6.0.3)
activejob (= 6.0.3)
activerecord (= 6.0.3)
activestorage (6.0.3.2)
actionpack (= 6.0.3.2)
activejob (= 6.0.3.2)
activerecord (= 6.0.3.2)
marcel (~> 0.3.1)
activesupport (6.0.3)
activesupport (6.0.3.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -283,8 +287,6 @@ GEM
attr_required (1.0.1)
auto_strip_attributes (2.5.0)
activerecord (>= 4.0)
autoprefixer-rails (9.7.4)
execjs
awesome_nested_set (3.2.0)
activerecord (>= 4.0.0, < 7.0)
aws-eventstream (1.0.3)
@@ -313,8 +315,6 @@ GEM
debug_inspector (>= 0.0.1)
bootsnap (1.4.6)
msgpack (~> 1.0)
bourbon (6.0.0)
thor (~> 0.19)
brakeman (4.8.0)
browser (2.6.1)
builder (3.2.4)
@@ -334,6 +334,9 @@ GEM
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
carrierwave_direct (2.1.0)
carrierwave (>= 1.0.0)
fog-aws
cells (4.1.7)
declarative-builder (< 0.2.0)
declarative-option (< 0.2.0)
@@ -357,7 +360,7 @@ GEM
ruby-enum (~> 0.5)
compare-xml (0.66)
nokogiri (~> 1.8)
concurrent-ruby (1.1.6)
concurrent-ruby (1.1.7)
cookiejar (0.3.3)
cork (0.3.0)
colored2 (~> 3.1)
@@ -478,7 +481,6 @@ GEM
eventmachine (1.2.7)
eventmachine_httpserver (0.2.1)
excon (0.72.0)
execjs (2.7.0)
factory_bot (5.1.1)
activesupport (>= 4.2.0)
factory_bot_rails (5.1.1)
@@ -547,7 +549,7 @@ GEM
domain_name (~> 0.5)
http_parser.rb (0.6.0)
httpclient (2.8.3)
i18n (1.8.2)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
i18n-js (3.6.0)
i18n (>= 0.6.6)
@@ -559,8 +561,6 @@ GEM
iso8601 (0.12.1)
jaro_winkler (1.5.4)
jmespath (1.4.0)
jquery-tablesorter (1.27.1)
railties (>= 3.2)
json (2.3.0)
json-jwt (1.11.0)
activesupport (>= 4.2)
@@ -597,7 +597,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.5.0)
loofah (2.6.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -615,7 +615,7 @@ GEM
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minisyntax (0.2.5)
minitest (5.14.0)
minitest (5.14.1)
mixlib-shellout (2.1.0)
msgpack (1.3.3)
multi_json (1.14.1)
@@ -631,7 +631,7 @@ GEM
newrelic_rpm (6.9.0.363)
nio4r (2.5.2)
no_proxy_fix (0.1.2)
nokogiri (1.10.9)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
nokogumbo (2.0.2)
nokogiri (~> 1.8, >= 1.8.4)
@@ -654,7 +654,8 @@ GEM
validate_email
validate_url
webfinger (>= 1.0.1)
openproject-token (1.0.2)
openproject-token (2.1.3)
activemodel
parallel (1.19.1)
parallel_tests (2.32.0)
parallel
@@ -710,9 +711,9 @@ GEM
eventmachine_httpserver
http_parser.rb (~> 0.6.0)
multi_json
puma (4.3.3)
puma (4.3.5)
nio4r (~> 2.0)
rack (2.2.2)
rack (2.2.3)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (6.2.2)
@@ -732,20 +733,20 @@ GEM
rack_session_access (0.2.0)
builder (>= 2.0.0)
rack (>= 1.0.0)
rails (6.0.3)
actioncable (= 6.0.3)
actionmailbox (= 6.0.3)
actionmailer (= 6.0.3)
actionpack (= 6.0.3)
actiontext (= 6.0.3)
actionview (= 6.0.3)
activejob (= 6.0.3)
activemodel (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
rails (6.0.3.2)
actioncable (= 6.0.3.2)
actionmailbox (= 6.0.3.2)
actionmailer (= 6.0.3.2)
actionpack (= 6.0.3.2)
actiontext (= 6.0.3.2)
actionview (= 6.0.3.2)
activejob (= 6.0.3.2)
activemodel (= 6.0.3.2)
activerecord (= 6.0.3.2)
activestorage (= 6.0.3.2)
activesupport (= 6.0.3.2)
bundler (>= 1.3.0)
railties (= 6.0.3)
railties (= 6.0.3.2)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
@@ -764,9 +765,9 @@ GEM
rails_stdout_logging
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (6.0.3)
actionpack (= 6.0.3)
activesupport (= 6.0.3)
railties (6.0.3.2)
actionpack (= 6.0.3.2)
activesupport (= 6.0.3.2)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
@@ -860,7 +861,7 @@ GEM
structured_warnings (~> 0.3)
rubyzip (1.3.0)
safe_yaml (1.0.5)
sanitize (5.1.0)
sanitize (5.2.1)
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
@@ -915,7 +916,7 @@ GEM
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (0.20.3)
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.10)
timecop (0.9.1)
@@ -958,15 +959,15 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
websocket-driver (0.7.1)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.4)
websocket-extensions (0.1.5)
will_paginate (3.3.0)
with_advisory_lock (4.6.0)
activerecord (>= 4.2)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.3.0)
zeitwerk (2.4.0)
PLATFORMS
ruby
@@ -982,18 +983,17 @@ DEPENDENCIES
addressable (~> 2.7.0)
airbrake (~> 9.4.3)
auto_strip_attributes (~> 2.5)
autoprefixer-rails (~> 9.7.4)
awesome_nested_set (~> 3.2.0)
aws-sdk-core (~> 3.91.0)
aws-sdk-s3 (~> 1.61.0)
bcrypt (~> 3.1.6)
bootsnap (~> 1.4.5)
bourbon (~> 6.0.0)
brakeman (~> 4.8.0)
browser (~> 2.6.1)
capybara (~> 3.32.0)
capybara-screenshot (~> 1.0.17)
carrierwave (~> 1.3.1)
carrierwave_direct (~> 2.1.0)
cells-erb (~> 0.1.0)
cells-rails (~> 0.0.9)
commonmarker (~> 0.21.0)
@@ -1055,13 +1055,14 @@ DEPENDENCIES
openproject-documents!
openproject-github_integration!
openproject-global_roles!
openproject-job_status!
openproject-ldap_groups!
openproject-meeting!
openproject-openid_connect!
openproject-pdf_export!
openproject-recaptcha!
openproject-reporting!
openproject-token (~> 1.0.2)
openproject-token (~> 2.1.1)
openproject-translations!
openproject-two_factor_authentication!
openproject-webhooks!
@@ -1079,13 +1080,13 @@ DEPENDENCIES
pry-rescue (~> 1.5.0)
pry-stack_explorer (~> 0.4.9.2)
puffing-billy (~> 2.3.1)
puma (~> 4.3.1)
puma (~> 4.3.5)
rack-attack (~> 6.2.2)
rack-mini-profiler
rack-protection (~> 2.0.8)
rack-test (~> 1.1.0)
rack_session_access
rails (~> 6.0.3)
rails (~> 6.0.3.2)
rails-controller-testing (~> 1.0.2)
rails-i18n (~> 6.0.0)
rails_12factor
@@ -1109,8 +1110,8 @@ DEPENDENCIES
ruby-prof
ruby-progressbar (~> 1.10.0)
rubytree (~> 1.0.0)
sanitize (~> 5.1.0)
sassc-rails (~> 2.1.0)
sanitize (~> 5.2.1)
sassc-rails
secure_headers (~> 6.3.0)
selenium-webdriver (~> 3.14)
semantic (~> 1.6.1)
+1
View File
@@ -38,6 +38,7 @@ group :opf_plugins do
gem 'openproject-github_integration', path: 'modules/github_integration'
gem 'openproject-ldap_groups', path: 'modules/ldap_groups'
gem 'openproject-recaptcha', path: 'modules/recaptcha'
gem 'openproject-job_status', path: 'modules/job_status'
gem 'grids', path: 'modules/grids'
gem 'my_page', path: 'modules/my_page'
+28
View File
@@ -0,0 +1,28 @@
# Security Policy
At its core, OpenProject is an open-source software that is [developed and published on GitHub](https://github.com/opf/openproject). Every change to the OpenProject code base ends up in an open repository accessible to everyone. This results in a transparent software where every commit can be traced back to the contributor.
Automated tests and manual code reviews ensure that these contributions are safe for the entire community of OpenProject. These tests encompass the correctness of security and access control features. We have ongoing collaborations with security professionals from to test the OpenProject code base for security exploits.
### Security announcements mailing list
We provide a mailing list for security advisories on OpenProject at <https://groups.google.com/forum/#!forum/openproject-security>. Please register there to get immediate notifications as we publish them.
Any security related information will also be published on our blog and website at https://www.openproject.com
### Reporting a vulnerability
We take all facets of security seriously at OpenProject. If you want to report a security concerns, have remarks, or contributions regarding security at OpenProject, please reach out to us at [security@openproject.com](mailto:security@openproject.com).
If you can, please send us a PGP-encrypted email using the following key:
- Key ID: [0x7D669C6D47533958](https://pgp.mit.edu/pks/lookup?op=get&search=0x7D669C6D47533958) ,
- Fingerprint BDCF E01E DE84 EA19 9AE1 72CE 7D66 9C6D 4753 3958
- You may also find the key [attached in our OpenProject repository.](https://github.com/opf/openproject/blob/dev/docs/development/security/security-at-openproject.com.asc)
Please include a description on how to reproduce the issue if possible. Our security team will get your email and will attempt to reproduce and fix the issue as soon as possible.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 187 KiB

-100
View File
@@ -1,100 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
/*
The action menu is a menu that usually belongs to an OpenProject entity (like an Issue, WikiPage, Meeting, ..).
Most likely it looks like this:
<ul class="action_menu_main">
<li><a>Menu item text</a></li>
<li><a>Menu item text</a></li>
<li class="drop-down">
<a class="icon icon-more" href="#">More functions</a>
<ul style="display:none;" class="menu-drop-down-container">
<li><a>Menu item text</a></li>
</ul>
</li>
</ul>
The following code is responsible to open and close the "more functions" submenu.
*/
jQuery(function ($) {
var animationSpeed = 100; // ms
function menu_top_position(menu) {
// if an h2 tag follows the submenu should unfold out at the border
var menu_start_position;
if (menu.next().get(0) != undefined && (menu.next().get(0).tagName == 'H2')) {
menu_start_position = menu.next().innerHeight() + menu.next().position().top;
}
else if (menu.next().hasClass("wiki-content") && menu.next().children().next().first().get(0) != undefined && menu.next().children().next().first().get(0).tagName == 'H1') {
var wiki_heading = menu.next().children().next().first();
menu_start_position = wiki_heading.innerHeight() + wiki_heading.position().top;
}
return menu_start_position;
}
function close_menu(event) {
var menu = $(event.data.menu);
// do not close the menu, if the user accidentally clicked next to a menu item (but still within the menu)
if (event.target !== menu.find(" > li.drop-down.open > ul").get(0)) {
menu.find(" > li.drop-down.open").removeClass("open").find("> ul").slideUp(animationSpeed);
// no need to watch for clicks, when the menu is already closed
$('html').off('click', close_menu);
}
}
function open_menu(menu) {
var drop_down = menu.find(" > li.drop-down");
// do not open a menu, which is already open
if (!drop_down.hasClass('open')) {
drop_down.find('> ul').slideDown(animationSpeed, function () {
drop_down.find('li > a:first').focus();
// when clicking on something, which is not the menu, close the menu
$('html').on('click', {menu: menu.get(0)}, close_menu);
});
drop_down.addClass('open');
}
}
// open the given submenu when clicking on it
function install_menu_logic(menu) {
menu.find(" > li.drop-down").click(function (event) {
open_menu(menu);
// and prevent default action (href) for that element
// but not for the menu items.
var target = $(event.target);
if (target.is('.drop-down') || target.closest('li, ul').is('.drop-down')) {
event.preventDefault();
}
});
}
$('.project-actions, .toolbar-items').each(function (idx, menu) {
install_menu_logic($(menu));
});
});
-398
View File
@@ -1,398 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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 docs/COPYRIGHT.rdoc for more details.
//++
//= require lib/jquery.colorcontrast
//= require lib/jquery.trap
//= require lib/jquery.is_visible_within
//= require top_menu
//= require action_menu
//= require breadcrumb
//= require findDomElement
//= require settings
//= require openproject_plugins
//= require versions
//= require forums
//= require_tree ./specific
//= require custom-fields
//= require date-range
//= require search
//= require colors
//= require tooltips
//= require danger_zone_validation
//= require flash_messages
//= require_tree ./onboarding
function checkAll(selector, checked) {
jQuery('#' + selector + ' input:checkbox').not(':disabled').each(function() {
this.checked = checked;
});
}
function toggleCheckboxesBySelector(selector) {
boxes = jQuery(selector);
var all_checked = true;
for (i = 0; i < boxes.length; i++) { if (boxes[i].checked === false) { all_checked = false; } }
for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
}
function setCheckboxesBySelector(checked, selector) {
var boxes = $(selector);
boxes.each(function(ele) {
ele.checked = checked;
});
}
var fileFieldCount = 1;
function addFileField() {
fileFieldCount++;
if (fileFieldCount >= 10) return false;
var clone = jQuery('#attachment_template').clone(true);
clone.removeAttr('id');
clone.html(clone.html().replace(/\[1\]/g, '['+ fileFieldCount + ']'));
jQuery('#attachments_fields').append(clone);
}
function randomKey(size) {
var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
var key = '';
for (i = 0; i < size; i++) {
key += chars[Math.floor(Math.random() * chars.length)];
}
return key;
}
// Automatic project identifier generation
var projectIdentifierLocked;
var projectIdentifierDefault;
var projectIdentifierMaxLength;
function generateProjectIdentifier() {
var identifier = jQuery('#project_name').val(); // project name
var diacriticsMap = [
{'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},
{'base':'aa','letters':/[\uA733\uA732]/g},
{'base':'ae','letters':/[\u00E4\u00E6\u01FD\u01E3\u00C4\u00C6\u01FC\u01E2]/g},
{'base':'ao','letters':/[\uA735\uA734]/g},
{'base':'au','letters':/[\uA737\uA736]/g},
{'base':'av','letters':/[\uA739\uA73B\uA738\uA73A]/g},
{'base':'ay','letters':/[\uA73D\uA73C]/g},
{'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g},
{'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g},
{'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g},
{'base':'dz','letters':/[\u01F3\u01C6\u01F1\u01C4\u01F2\u01C5]/g},
{'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g},
{'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g},
{'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g},
{'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g},
{'base':'hv','letters':/[\u0195]/g},
{'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g},
{'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249\u004A\u24BF\uFF2A\u0134\u0248]/g},
{'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g},
{'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g},
{'base':'lj','letters':/[\u01C9\u01C7\u01C8]/g},
{'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g},
{'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g},
{'base':'nj','letters':/[\u01CC\u01CA\u01CB]/g},
{'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g},
{'base':'oe','letters': /[\u00F6\u0153\u00D6\u0152]/g},
{'base':'oi','letters':/[\u01A3\u01A2]/g},
{'base':'ou','letters':/[\u0223\u0222]/g},
{'base':'oo','letters':/[\uA74F\uA74E]/g},
{'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g},
{'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759\u0051\u24C6\uFF31\uA756\uA758\u024A]/g},
{'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g},
{'base':'s','letters':/[\u0073\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g},
{'base':'ss','letters':/[\u00DF]/g},
{'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g},
{'base':'tz','letters':/[\uA729\uA728]/g},
{'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g},
{'base':'ue','letters':/[\u00FC\u00DC]/g},
{'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g},
{'base':'vy','letters':/[\uA761\uA760]/g},
{'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g},
{'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D\u0058\u24CD\uFF38\u1E8A\u1E8C]/g},
{'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g},
{'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}
];
for(var i=0; i<diacriticsMap.length; i++) {
identifier = identifier.replace(diacriticsMap[i].letters, diacriticsMap[i].base);
}
identifier = identifier.replace(/[^a-z0-9]+/gi, '-'); // remaining non-alphanumeric => hyphen
identifier = identifier.replace(/^[-\d]*|-*$/g, ''); // remove hyphens and numbers at beginning and hyphens at end
identifier = identifier.toLowerCase(); // to lower
identifier = identifier.substr(0,projectIdentifierMaxLength); // max characters
return identifier;
}
function observeProjectName() {
jQuery('#project_name').keyup(function() {
if(!projectIdentifierLocked) {
jQuery('#project_identifier').val(generateProjectIdentifier());
}
});
}
function observeProjectIdentifier() {
jQuery('#project_identifier').keyup(function() {
if(jQuery('#project_identifier').getValue() !== '' &&
jQuery('#project_identifier').getValue() != generateProjectIdentifier()) {
projectIdentifierLocked = true;
} else {
projectIdentifierLocked = false;
}
});
}
function hideOnLoad() {
jQuery('.hol').hide();
}
jQuery(hideOnLoad);
function addClickEventToAllErrorMessages() {
jQuery('a.afocus').each(function() {
var target = jQuery(this);
target.click(function(evt) {
var field = jQuery('#' + target.readAttribute('href').substr(1));
if (field === null) {
// Cut off '_id' (necessary for select boxes)
field = jQuery('#' + target.readAttribute('href').substr(1).concat('_id'));
}
if (field) {
field.down('input, textarea, select').focus();
}
target.unbind(evt);
return false;
});
});
}
// a few constants for animations speeds, etc.
var animationRate = 100;
/* jQuery code from #263 */
// returns viewport height
jQuery.viewportHeight = function() {
return self.innerHeight ||
jQuery.boxModel && document.documentElement.clientHeight ||
document.body.clientHeight;
};
/*
* 1 - registers a callback which copies the csrf token into the
* X-CSRF-Token header with each ajax request. Necessary to
* work with rails applications which have fixed
* CVE-2011-0447
* 2 - shows and hides ajax indicator
*/
jQuery(document).ready(function($) {
jQuery(document).ajaxSend(function (event, request) {
if ($(event.target.activeElement).closest('[ajax-indicated]').length &&
$('ajax-indicator')) {
$('#ajax-indicator').show();
}
var csrf_meta_tag = $('meta[name=csrf-token]');
if (csrf_meta_tag) {
var header = 'X-CSRF-Token',
token = csrf_meta_tag.attr('content');
request.setRequestHeader(header, token);
}
request.setRequestHeader('X-Authentication-Scheme', "Session");
});
// ajaxStop gets called when ALL Requests finish, so we won't need a counter as in PT
jQuery(document).ajaxStop(function () {
if ($('#ajax-indicator')) {
$('#ajax-indicator').hide();
}
addClickEventToAllErrorMessages();
});
// show/hide the files table
jQuery(".attachments h4").click(function() {
jQuery(this).toggleClass("closed").next().slideToggle(animationRate);
});
jQuery(window).resize(function() {
// wait 200 milliseconds for no further resize event
// then readjust breadcrumb
if(this.resizeTO) clearTimeout(this.resizeTO);
this.resizeTO = setTimeout(function() {
jQuery(this).trigger('resizeEnd');
}, 200);
});
// Do not close the login window when using it
jQuery('#nav-login-content').click(function(event){
event.stopPropagation();
});
// Set focus on first error message
var error_focus = $('a.afocus').first();
var input_focus = $('.autofocus').first();
if (error_focus !== undefined) {
error_focus.focus();
}
else if (input_focus !== undefined){
input_focus.focus();
if (input_focus.tagName === "INPUT") {
input_focus.select();
}
}
// Focus on field with error
addClickEventToAllErrorMessages();
// Skip menu on content
jQuery('#skip-navigation--content').click(skipMenu);
// Click handler for formatting help
jQuery(document.body).on('click', '.formatting-help-link-button', function() {
window.open(window.appBasePath + '/help/wiki_syntax',
"",
"resizable=yes, location=no, width=600, height=640, menubar=no, status=no, scrollbars=yes"
);
return false;
});
});
var Administration = (function ($) {
var update_default_language_options,
init_language_selection_handling,
toggle_default_language_select;
update_default_language_options = function (input) {
var default_language_select = $('#setting_default_language select'),
default_language_select_active;
if (input.attr('checked')) {
default_language_select.find('option[value="' + input.val() + '"]').removeAttr('disabled');
} else {
default_language_select.find('option[value="' + input.val() + '"]').attr('disabled', 'disabled');
}
default_language_select_active = default_language_select.find('option:not([disabled="disabled"])');
toggle_disabled_state(default_language_select_active.length === 0);
if (default_language_select_active.length === 1) {
default_language_select_active.attr('selected', true);
} else if (default_language_select.val() === input.val() && !input.attr('checked')) {
default_language_select_active.first().attr('selected', true);
}
};
toggle_disabled_state = function (active) {
jQuery('#setting_default_language select').attr('disabled', active)
.closest('form')
.find('input:submit')
.attr('disabled', active);
};
init_language_selection_handling = function () {
jQuery('#setting_available_languages input:not([checked="checked"])').each(function (index, input) {
update_default_language_options($(input));
});
jQuery('#setting_available_languages input').click(function () {
update_default_language_options($(this));
});
};
return {
init_language_selection_handling: init_language_selection_handling
};
}(jQuery));
var activateFlash = function(selector) {
var flashMessages = jQuery(selector);
// Ignore flash messages of class 'ignored-by-flash-activation' because those
// messages are completely handled via JavaScript (see types_checkboxes.js for
// details). We wouldn't have to ignore this message if the flash element
// would be completely created via JavaScript and not available in the DOM by
// default.
flashMessages.each(function (ix, e) {
flashMessage = jQuery(e);
if (!flashMessage.hasClass('ignored-by-flash-activation')) {
flashMessage.show();
}
});
};
var activateFlashNotice = function () {
var notice = '.flash';
activateFlash(notice);
};
var activateFlashError = function () {
var error = '.errorExplanation[role="alert"]';
activateFlash(error);
};
var focusFirstErroneousField = function() {
var firstErrorSpan = jQuery('span.errorSpan').first();
var erroneousInput = firstErrorSpan.find('*').filter(":input");
erroneousInput.focus();
};
function initMainMenuExpandStatus() {
let wrapper = jQuery('#wrapper');
let upToggle = jQuery('ul.menu_root.closed li.open a.arrow-left-to-project');
if (upToggle.length === 1 && wrapper.hasClass('hidden-navigation')) {
upToggle.trigger('click');
}
}
var setupServerResponse = function() {
initMainMenuExpandStatus();
focusFirstErroneousField();
activateFlashNotice();
activateFlashError();
jQuery(document).ajaxComplete(activateFlashNotice);
jQuery(document).ajaxComplete(activateFlashError);
};
jQuery(document).ready(setupServerResponse);
-83
View File
@@ -1,83 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
jQuery.fn.reverse = [].reverse;
(function($){
$.fn.adjustBreadcrumbToWindowSize = function(){
var breadcrumbElements = this.find(' > li');
var breadcrumb = this;
var lastChanged;
if (breadcrumb.breadcrumbOutOfBounds()){
breadcrumbElements.each(function(index) {
if (breadcrumb.breadcrumbOutOfBounds()){
if (!$(this).find(' > a').hasClass('nocut')){
$(this).addClass('cutme ellipsis');
}
}
else {
return false;
}
});
}
else {
breadcrumbElements.reverse().each(function(index) {
if (!breadcrumb.breadcrumbOutOfBounds()){
if (!$(this).find(' > a').hasClass('nocut')){
$(this).removeClass('cutme ellipsis');
lastChanged = $(this);
}
}
});
if (breadcrumb.breadcrumbOutOfBounds()){
if (lastChanged != undefined){
lastChanged.addClass('cutme ellipsis');
return false;
}
}
}
};
$.fn.breadcrumbOutOfBounds = function(){
var lastElement = this.find(' > li').last();
if (lastElement) {
var rightCorner = lastElement.width() + lastElement.offset().left;
var windowSize = jQuery(window).width();
if ((Math.max(1000,windowSize) - rightCorner) < 10) {
return true;
}
else {
return false;
}
} else {
return false;
}
};
})(jQuery);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-62
View File
@@ -1,62 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
(function( $ ){
$.fn.nextElementInDom = function(selector, options) {
return $(this).findElementInDom(selector, $.extend(options, { direction: 'front' }));
};
$.fn.previousElementInDom = function(selector, options) {
return $(this).findElementInDom(selector, $.extend(options, { direction: 'back' }));
};
$.fn.findElementInDom = function(selector, options) {
var defaults, parent, direction, found, children;
defaults = { stopAt : 'body', direction: 'front' };
options = $.extend(defaults, options);
parent = $(this).parent();
direction = (options.direction === 'front') ? ":gt" : ":lt";
children = parent.children(direction + "(" + $(this).index() + ")");
children = (options.direction === 'front') ? children : children.reverse();
found = parent.children(direction + "(" + $(this).index() + ")").find(selector).filter(":first");
if (found.length > 0) {
return found;
} else {
if (parent.length === 0 || parent.is(options.stopAt)) {
return $([]);
} else {
return parent.findElementInDom(selector, options);
}
}
};
})( jQuery );
-29
View File
@@ -1,29 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
$.noConflict();
@@ -1,30 +0,0 @@
/**
* Copyright 2012, Digital Fusion
* Licensed under the MIT license.
* http://teamdf.com/jquery-plugins/license/
*
* @author Sam Sehnert
* @desc A small plugin that checks whether elements are within
* the user visible viewport of a web browser.
* only accounts for vertical position, not horizontal.
*
* Extended here to include an optional container used as parent,
* as the original plugin only supports window.
*/
(function ($) {
$.fn.isVisibleWithin = function (container, partial, hidden) {
var $t = $(this).eq(0),
t = $t.get(0),
$w = (container != null ? container : $(window)),
viewTop = (container != null ? container.offset().top : $w.scrollTop()),
viewBottom = viewTop + $w.height(),
_top = $t.offset().top,
_bottom = _top + $t.height(),
compareTop = partial === true ? _bottom : _top,
compareBottom = partial === true ? _top : _bottom,
clientSize = hidden === true ? t.offsetWidth * t.offsetHeight : true;
return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop));
};
})(jQuery);
-203
View File
@@ -1,203 +0,0 @@
/*!
Copyright (c) 2011, 2012 Julien Wajsberg <felash@gmail.com>
All rights reserved.
Official repository: https://github.com/julienw/jquery-trap-input
License is there: https://github.com/julienw/jquery-trap-input/blob/master/LICENSE
This is version 1.2.0.
*/
(function( $, undefined ){
/*
(this comment is after the first line of code so that uglifyjs removes it)
Redistribution and use in source and binary forms, with or without
modification, are permitted without condition.
Although that's not an obligation, I would appreciate that you provide a
link to the official repository.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED.
*/
/*jshint boss: true, bitwise: true, curly: true, expr: true, newcap: true, noarg: true, nonew: true, latedef: true, regexdash: true */
var DATA_ISTRAPPING_KEY = "trap.isTrapping";
function onkeypress(e) {
if (e.keyCode === 9) {
var goReverse = !!(e.shiftKey);
if (processTab(this, e.target, goReverse)) {
e.preventDefault();
e.stopPropagation();
}
}
}
// will return true if we could process the tab event
// otherwise, return false
function processTab(container, elt, goReverse) {
var $focussable = getFocusableElementsInContainer(container),
curElt = elt,
index, nextIndex, prevIndex, lastIndex;
do {
index = $focussable.index(curElt);
nextIndex = index + 1;
prevIndex = index - 1;
lastIndex = $focussable.length - 1;
switch(index) {
case -1:
return false; // that's strange, let the browser do its job
case 0:
prevIndex = lastIndex;
break;
case lastIndex:
nextIndex = 0;
break;
}
if (goReverse) {
nextIndex = prevIndex;
}
curElt = $focussable.get(nextIndex);
if (!curElt || curElt === elt) { return true; }
try {
curElt.focus();
} catch(e) { // IE sometimes throws when an element is not visible
return true;
}
} while ($focussable.length > 1 && elt === elt.ownerDocument.activeElement);
return true;
}
function filterKeepSpeciallyFocusable() {
return this.tabIndex > 0;
}
function filterKeepNormalElements() {
return !this.tabIndex; // true if no tabIndex or tabIndex == 0
}
function sortFocusable(a, b) {
return (a.t - b.t) || (a.i - b.i);
}
function getFocusableElementsInContainer(container) {
var $container = $(container);
var result = [],
cnt = 0;
fixIndexSelector.enable && fixIndexSelector.enable();
// leaving away command and details for now
$container.find("a[href], link[href], [draggable=true], [contenteditable=true], :input:enabled, [tabindex=0]")
.filter(":visible")
.filter(filterKeepNormalElements)
.each(function(i, val) {
result.push({
v: val, // value
t: 0, // tabIndex
i: cnt++ // index for stable sort
});
});
$container
.find("[tabindex]")
.filter(":visible")
.filter(filterKeepSpeciallyFocusable)
.each(function(i, val) {
result.push({
v: val, // value
t: val.tabIndex, // tabIndex
i: cnt++ // index
});
});
fixIndexSelector.disable && fixIndexSelector.disable();
result = $.map(result.sort(sortFocusable), // needs stable sort
function(val) {
return val.v;
}
);
return $(result);
}
function trap() {
this.keydown(onkeypress);
this.data(DATA_ISTRAPPING_KEY, true);
return this;
}
function untrap() {
this.unbind('keydown', onkeypress);
this.removeData(DATA_ISTRAPPING_KEY);
return this;
}
function isTrapping() {
return !!this.data(DATA_ISTRAPPING_KEY);
}
$.fn.extend({
trap: trap,
untrap: untrap,
isTrapping: isTrapping
});
// jQuery 1.6.x tabindex attr hooks management
// this triggers problems for tabindex attribute
// selectors in IE7-
// see https://github.com/julienw/jquery-trap-input/issues/3
var fixIndexSelector = {};
if ($.find.find && $.find.attr !== $.attr) {
// jQuery uses Sizzle (this is jQuery >= 1.3)
// sizzle uses its own attribute handling (in jq 1.6.x and below)
(function() {
var tabindexKey = "tabindex";
var sizzleAttrHandle = $.expr.attrHandle;
// this function comes directly from jQuery 1.7.2 (propHooks.tabIndex.get)
// we have to put it here if we want to support jQuery < 1.6 which
// doesn't have an attrHooks object to reference.
function getTabindexAttr(elem) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
var attributeNode = elem.getAttributeNode(tabindexKey);
return attributeNode && attributeNode.specified ?
parseInt( attributeNode.value, 10 ) :
undefined;
}
function fixSizzleAttrHook() {
// in jQ <= 1.6.x, we add to Sizzle the attrHook from jQuery's attr method
sizzleAttrHandle[tabindexKey] = sizzleAttrHandle.tabIndex = getTabindexAttr;
}
function unfixSizzleAttrHook() {
delete sizzleAttrHandle[tabindexKey];
delete sizzleAttrHandle.tabIndex;
}
fixIndexSelector = {
enable: fixSizzleAttrHook,
disable: unfixSizzleAttrHook
};
})();
}
})( jQuery );
@@ -1,50 +0,0 @@
(function ($) {
$(function() {
window.scrumBacklogsTourSteps = [
{
'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs.overview'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow'
},
{
'event_type': 'next',
'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();
}
},
{
'event_type': 'next',
'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();
}
}
];
window.scrumTaskBoardTourSteps = [
{
'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();
}
},
];
});
}(jQuery))
@@ -1,45 +0,0 @@
(function ($) {
$(function() {
window.boardTourSteps = [
{
'next .board-view-menu-item': I18n.t('js.onboarding.steps.boards.overview'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('.board-view-menu-item ~ .toggler')[0].click();
waitForElement('.boards--menu-items', '#main-menu', function() {
$(".main-menu--children-sub-item:contains('Kanban')")[0].click();
});
}
},
{
'next .board-list--container': I18n.t('js.onboarding.steps.boards.lists'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow',
'timeout': function() {
return new Promise(function(resolve) {
waitForElement('.wp-card', '#content', function() {
resolve();
});
});
}
},
{
'next .board-list--add-button': I18n.t('js.onboarding.steps.boards.add'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
},
{
'next .boards-list--container': I18n.t('js.onboarding.steps.boards.drag'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-dark -hidden-arrow',
onNext: function () {
$('.main-menu--arrow-left-to-project')[0].click();
}
}
];
});
}(jQuery))
@@ -1,35 +0,0 @@
(function ($) {
$(function() {
window.homescreenOnboardingTourSteps = [
{
'next #top-menu': I18n.t('js.onboarding.steps.welcome'),
'skipButton': {className: 'enjoyhint_btn-transparent', text: I18n.t('js.onboarding.buttons.skip')},
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'containerClass': '-hidden-arrow',
'bottom': 7
},
{
'description': I18n.t('js.onboarding.steps.project_selection'),
'selector': '.widget-box.welcome',
'event': 'custom',
'showSkip': false,
'containerClass': '-dark -hidden-arrow',
'clickable': true,
onBeforeStart: function () {
// Handle the correct project selection and redirection
// This will be removed once the project selection is implemented
jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")").click(function () {
tutorialInstance.trigger('next');
window.location = this.href + '/backlogs/?start_scrum_onboarding_tour=true';
});
jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")").click(function () {
tutorialInstance.trigger('next');
window.location = this.href + '/work_packages/?start_onboarding_tour=true';
});
// Disable clicks on other links
$('.widget-box.welcome a').addClass('-disabled').bind('click', preventClickHandler);
}
}
];
});
}(jQuery))
@@ -1,22 +0,0 @@
(function ($) {
$(function() {
window.menuTourSteps = [
{
'next .members-menu-item': I18n.t('js.onboarding.steps.members'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
},
{
'next .wiki-menu--main-item': I18n.t('js.onboarding.steps.wiki'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
},
{
'next .menu-item--help': I18n.t('js.onboarding.steps.help_menu'),
'shape': 'circle',
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.got_it')}
}
];
});
}(jQuery))
@@ -1,149 +0,0 @@
(function ($) {
$(function() {
// ------------------------------- Global -------------------------------
window.tutorialInstance;
window.preventClickHandler = function (e) {
e.preventDefault();
e.stopPropagation();
};
window.waitForElement = function(element, container, execFunction) {
// Wait for the element to be ready
var observer = new MutationObserver(function (mutations, observerInstance) {
if ($(element).length) {
observerInstance.disconnect(); // stop observing
execFunction();
return;
}
});
observer.observe($(container)[0], {
childList: true,
subtree: true
});
};
window.demoProjectName = 'Demo project';
window.scrumDemoProjectName = 'Scrum project';
var storageKey = 'openProject-onboardingTour';
var currentTourPart = sessionStorage.getItem(storageKey);
var url = new URL(window.location.href);
var isMobile = document.body.classList.contains('-browser-mobile');
var demoProjectsAvailable = $('meta[name=demo_projects_available]').attr('content') === "true";
var boardsDemoDataAvailable = $('meta[name=boards_demo_data_available]').attr('content') === "true";
var eeTokenAvailable = !$('body').hasClass('ee-banners-visible');
var tourCancelled = false;
// ------------------------------- Initial start -------------------------------
// Do not show the tutorial on mobile or when the demo data has been deleted
if(!isMobile && demoProjectsAvailable) {
// Start after the intro modal (language selection)
// This has to be changed once the project selection is implemented
if (url.searchParams.get("first_time_user") && demoProjectsLinks().length == 2) {
currentTourPart = '';
sessionStorage.setItem(storageKey, 'readyToStart');
// Start automatically when the language selection is closed
$('.op-modal--modal-close-button').click(function () {
tourCancelled = true;
homescreenTour();
});
//Start automatically when the escape button is pressed
document.addEventListener('keydown', function(event) {
if (event.key == "Escape" && !tourCancelled) {
tourCancelled = true;
homescreenTour();
}
}, { once: true });
}
// ------------------------------- Tutorial Homescreen page -------------------------------
if (currentTourPart === "readyToStart") {
homescreenTour();
}
// ------------------------------- Tutorial WP page -------------------------------
if (currentTourPart === "startMainTourFromBacklogs" || url.searchParams.get("start_onboarding_tour")) {
mainTour();
}
// ------------------------------- Tutorial Backlogs page -------------------------------
if (url.searchParams.get("start_scrum_onboarding_tour")) {
if ($('.backlogs-menu-item').length > 0) {
backlogsTour();
}
}
// ------------------------------- Tutorial Task Board page -------------------------------
if (currentTourPart === "startTaskBoardTour") {
taskboardTour();
}
}
function demoProjectsLinks() {
demoProjects = [];
demoProjectsLink = jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")");
scrumDemoProjectsLink = jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")");
if (demoProjectsLink.length) demoProjects.push(demoProjectsLink);
if (scrumDemoProjectsLink.length) demoProjects.push(scrumDemoProjectsLink);
return demoProjects;
}
function initializeTour(storageValue, disabledElements, projectSelection) {
tutorialInstance = new EnjoyHint({
onStart: function () {
$('#content-wrapper, #menu-sidebar').addClass('-hidden-overflow');
},
onEnd: function () {
sessionStorage.setItem(storageKey, storageValue);
$('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow');
},
onSkip: function () {
sessionStorage.setItem(storageKey, 'skipped');
if (disabledElements) jQuery(disabledElements).removeClass('-disabled').unbind('click', preventClickHandler);
if (projectSelection) $.each(demoProjectsLinks(), function(i, e) { $(e).off('click')});
$('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow');
}
});
}
function startTour(steps) {
tutorialInstance.set(steps);
tutorialInstance.run();
}
function homescreenTour() {
initializeTour('startProjectTour', '.widget-box--blocks--buttons a', true);
startTour(homescreenOnboardingTourSteps);
}
function backlogsTour() {
initializeTour('startTaskBoardTour');
startTour(scrumBacklogsTourSteps);
}
function taskboardTour() {
initializeTour('startMainTourFromBacklogs');
startTour(scrumTaskBoardTourSteps);
}
function mainTour() {
initializeTour('mainTourFinished');
waitForElement('.work-package--results-tbody', '#content', function() {
var steps;
// Check for EE edition, and available seed data of boards.
// Then add boards to the tour, otherwise skip it.
if (eeTokenAvailable && boardsDemoDataAvailable) {
steps = wpOnboardingTourSteps.concat(boardTourSteps).concat(menuTourSteps);
} else {
steps = wpOnboardingTourSteps.concat(menuTourSteps);
}
startTour(steps);
});
}
});
}(jQuery));
@@ -1,70 +0,0 @@
(function ($) {
$(function() {
window.wpOnboardingTourSteps = [
{
'next .wp-table--row': I18n.t('js.onboarding.steps.wp.list'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$(".inline-edit--display-field.id a ")[0].click();
}
},
{
'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-back-button': I18n.t('js.onboarding.steps.wp.back_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
onNext: function () {
$('.work-packages-back-button')[0].click();
}
},
{
'next .add-work-package': I18n.t('js.onboarding.steps.wp.create_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'shape': 'circle',
'timeout': function() {
return new Promise(function(resolve) {
// We are waiting here for the badge to appear,
// because its the last that appears and it shifts the WP create button to the left.
// Thus it is important that the tour rendering starts after the badge is visible
waitForElement('#work-packages-filter-toggle-button .badge', '#content', function() {
resolve();
});
});
},
onNext: function () {
$('#wp-view-toggle-button').click();
}
},
{
'next #wp-view-toggle-button': I18n.t('js.onboarding.steps.wp.timeline_button'),
'showSkip': false,
'nextButton': {text: I18n.t('js.onboarding.buttons.next')},
'bottom': '-100',
onNext: function () {
$('#wp-view-context-menu .icon-view-timeline')[0].click();
}
},
{
'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();
}
}
];
});
}(jQuery))
@@ -1,43 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
jQuery(document).ready(function($) {
$("#project_responsible_id").autocomplete({
ajax: {
null_element: {id: -1, name: I18n.t("js.filter.noneElement")},
data: function (term, page) {
return {
q: term, //search term
page_limit: 10, // page size
page: page, // current page number
id: $("#project_responsible_id").attr("data-projectId")
};
}
}
});
});
@@ -1,66 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
function moveOptions(sourceId, destId) {
var sourceSelection = jQuery('#' + sourceId);
var destSelection = jQuery('#' + destId);
var selectedOptions = sourceSelection.find('option:selected');
selectedOptions.each(function() {
var option = jQuery('<option>', { value: this.value,
text: this.text });
destSelection.append(option);
this.remove();
});
}
function swapOptions(option1, option2) {
if (option1.length == 1 && option2.length == 1) {
option2.after(option1);
}
}
function moveOptionUp(selectionId) {
var selection = jQuery('#' + selectionId);
var selectedOptions = selection.find('option:selected');
swapOptions(selectedOptions.prev(), selectedOptions);
}
function moveOptionDown(selectionId) {
var selection = jQuery('#' + selectionId);
var selectedOptions = selection.find('option:selected');
swapOptions(selectedOptions, selectedOptions.next());
}
function selectAllOptions(id) {
jQuery("#" + id + " option").attr('selected',true);
}
-149
View File
@@ -1,149 +0,0 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2020 the OpenProject GmbH
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.
++#%>
(function($) {
$(function() {
$('#settings_session_ttl_enabled').on('change', function(){
$('#settings_session_ttl_container').toggle($(this).is(':checked'));
}).trigger('change');
/** Sync SCM vendor select when enabled SCMs are changed */
$('[name="settings[enabled_scm][]"]').change(function() {
var wasDisabled = !this.checked,
vendor = this.value,
select = $('#settings_repositories_automatic_managed_vendor'),
option = select.find('option[value="' + vendor + '"]');
// Skip non-manageable SCMs
if (option.length === 0) {
return;
}
option.prop('disabled', wasDisabled);
if (wasDisabled && option.prop('selected')) {
select.val('');
}
});
/* Javascript for Settings::TextSettingCell */
let langSelectSwitchData = function(select) {
let self = $(select);
let id = self.attr("id");
let settingName = id.replace('lang-for-', '');
let newLang = self.val();
let textArea =$(`#settings-${settingName}`)
let editor = textArea.siblings('ckeditor-augmented-textarea').data('editor');
return { id: id, settingName: settingName, newLang: newLang, textArea: textArea, editor: editor }
};
// Upon focusing:
// * store the current value of the editor in the hidden field for that lang.
// Upon change:
// * get the current value from the hidden field for that lang and set the editor text to that value.
// * Set the name of the textarea to reflect the current lang so that the value stored in the hidden field
// is overwritten.
$(".lang-select-switch")
.focus(function() {
let data = langSelectSwitchData(this);
$(`#${data.id}-${data.newLang}`).val(data.editor.getData());
})
.change(function() {
let data = langSelectSwitchData(this);
let storedValue = $(`#${data.id}-${data.newLang}`).val();
data.editor.setData(storedValue);
data.textArea.attr('name', `settings[${data.settingName}][${data.newLang}]`)
});
/* end Javascript for Settings::TextSettingCell */
$('.admin-settings--form').submit(function() {
/* Update consent time if consent required */
if ($('#settings_consent_required').is(':checked') && $('#toggle_consent_time').is(':checked')) {
$('#settings_consent_time')
.val(new Date().toISOString())
.prop('disabled', false);
}
return true;
});
/** Toggle notification settings fields */
$("#email_delivery_method_switch").on("change", function() {
delivery_method = $(this).val();
$(".email_delivery_method_settings").hide();
$("#email_delivery_method_" + delivery_method).show();
}).trigger("change");
$('#settings_smtp_authentication').on('change', function() {
var isNone = $(this).val() === 'none';
$('#settings_smtp_user_name,#settings_smtp_password')
.closest('.form--field')
.toggle(!isNone);
});
/** Toggle repository checkout fieldsets required when option is disabled */
$('.settings-repositories--checkout-toggle').change(function() {
var wasChecked = this.checked,
fieldset = $(this).closest('fieldset');
fieldset
.find('input,select')
.filter(':not([type=checkbox])')
.filter(':not([type=hidden])')
.removeAttr('required') // Rails 4.0 still seems to use attribute
.prop('required', wasChecked);
})
/** Toggle highlighted attributes visibility depending on if the highlighting mode 'inline' was selected*/
$('.settings--highlighting-mode select').change(function() {
var highlightingMode = $(this).val();
$(".settings--highlighted-attributes").toggle(highlightingMode === "inline")
})
/** Initialize hightlighted attributes checkboxes. If none is selected, it means we want them all. So let's
* show them all as selected.
* On submitting the form, we remove all checkboxes before sending to communicate, we actually want all and not
* only the selected.*/
if ($(".settings--highlighted-attributes input[type='checkbox']:checked").length == 0) {
$(".settings--highlighted-attributes input[type='checkbox']").prop("checked", true);
}
$('#tab-content-work_packages form').submit(function() {
var availableAttributes = $(".settings--highlighted-attributes input[type='checkbox']");
var selectedAttributes = $(".settings--highlighted-attributes input[type='checkbox']:checked");
if (selectedAttributes.length == availableAttributes.length) {
availableAttributes.prop("checked", false);
}
})
});
}(jQuery));
-21
View File
@@ -1,21 +0,0 @@
jQuery(function($) {
var tooltipTriggers = $('.advanced-tooltip-trigger');
tooltipTriggers.each(function (index, el) {
var tooltip = $("#" + $(el).attr('aria-describedby'));
$(el).bind('mouseover focus', function () {
var top = $(this).offset().top - $(window).scrollTop();
// Adjust top for small elements
var POINTER_HEIGHT = 16.5;
var middle = $(this).outerHeight() / 2;
if (middle < POINTER_HEIGHT) top -= POINTER_HEIGHT - middle;
// On the left side of the element + 5px Distance
var left = $(this).offset().left + $(this).width() + 5;
tooltip.css({'opacity': 1, 'visibility': 'visible', 'top': top, 'left': left});
}).bind('mouseout focusout', function () {
tooltip.css({'opacity': 0, 'visibility': 'hidden'});
});
});
});
-283
View File
@@ -1,283 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
(function ($, undefined) {
"use strict";
function TopMenu (menu_container) {
this.menu_container = $(menu_container);
this.setup(menu_container);
}
TopMenu.prototype = $.extend(TopMenu.prototype, {
setup: function () {
this.hover = false;
this.menuIsOpen = false;
this.withHeadingFoldOutAtBorder();
this.setupDropdownClick();
this.registerEventHandlers();
this.closeOnBodyClick();
this.accessibility();
},
accessibility: function () {
$(".drop-down > ul").attr("aria-expanded","false");
},
toggleClick: function (dropdown) {
if (this.menuIsOpen) {
if (this.isOpen(dropdown)) {
this.closing();
} else {
this.open(dropdown);
}
} else {
this.opening();
this.open(dropdown);
}
},
// somebody opens the menu via click, hover possible afterwards
opening: function () {
this.startHover();
this.menuIsOpen = true;
this.menu_container.trigger("openedMenu", this.menu_container);
},
// the entire menu gets closed, no hover possible afterwards
closing: function () {
this.stopHover();
this.closeAllItems();
this.menuIsOpen = false;
this.menu_container.trigger("closedMenu", this.menu_container);
},
stopHover: function () {
this.hover = false;
this.menu_container.removeClass("hover");
},
startHover: function () {
this.hover = true;
this.menu_container.addClass("hover");
},
closeAllItems: function () {
var self = this;
this.openDropdowns().each(function (ix, item) {
self.close($(item));
});
},
closeOnBodyClick: function () {
var self = this;
document.getElementById('wrapper').addEventListener('click', function (evt) {
if (self.menuIsOpen && !self.openDropdowns()[0].contains(evt.target)) {
self.closing();
}
}, true);
},
openDropdowns: function () {
return this.dropdowns().filter(".open");
},
dropdowns: function () {
return this.menu_container.find("li.drop-down");
},
withHeadingFoldOutAtBorder: function () {
var menu_start_position;
if (this.menu_container.next().get(0) != undefined && (this.menu_container.next().get(0).tagName == 'H2')){
menu_start_position = this.menu_container.next().innerHeight() + this.menu_container.next().position().top;
this.menu_container.find("ul.menu-drop-down-container").css({ top: menu_start_position });
}
else if(this.menu_container.next().hasClass("wiki-content") && this.menu_container.next().children().next().first().get(0) != undefined && this.menu_container.next().children().next().first().get(0).tagName == 'H1'){
var wiki_heading = this.menu_container.next().children().next().first();
menu_start_position = wiki_heading.innerHeight() + wiki_heading.position().top;
this.menu_container.find("ul.menu-drop-down-container").css({ top: menu_start_position });
}
},
setupDropdownClick: function () {
var self = this;
this.dropdowns().each(function (ix, it) {
$(it).click(function () {
self.toggleClick($(this));
return false;
});
$(it).on('touchstart', function(e) {
// This shall avoid the hover event is fired,
// which would otherwise lead to menu being closed directly after its opened.
// Ignore clicks from within the dropdown
if ($(e.target).closest('.menu-drop-down-container').length) {
return true;
}
e.preventDefault();
$(this).click();
return false;
});
});
},
isOpen: function (dropdown) {
return dropdown.filter(".open").length == 1;
},
isClosed: function (dropdown) {
return !this.isOpen(dropdown);
},
open: function (dropdown) {
this.dontCloseWhenUsing(dropdown);
this.closeOtherItems(dropdown);
this.slideAndFocus(dropdown, function() {
dropdown.trigger("opened", dropdown);
});
},
close: function (dropdown, immediate) {
this.slideUp(dropdown, immediate);
dropdown.trigger("closed", dropdown);
},
closeOtherItems: function (dropdown) {
var self = this;
this.openDropdowns().each(function (ix, it) {
if ($(it) != $(dropdown)) {
self.close($(it), true);
}
});
},
dontCloseWhenUsing: function (dropdown) {
$(dropdown).find("li").click(function (event) {
event.stopPropagation();
});
$(dropdown).bind("mousedown mouseup click", function (event) {
event.stopPropagation();
});
},
slideAndFocus: function (dropdown, callback) {
this.slideDown(dropdown, callback);
this.focusFirstInputOrLink(dropdown);
},
slideDown: function (dropdown, callback) {
var toDrop = dropdown.find("> ul");
dropdown.addClass("open");
toDrop.slideDown(animationRate, callback).attr("aria-expanded","true");
},
slideUp: function (dropdown, immediate) {
var toDrop = $(dropdown).find("> ul");
dropdown.removeClass("open");
if (immediate) {
toDrop.hide();
} else {
toDrop.slideUp(animationRate);
}
toDrop.attr("aria-expanded","false");
},
// If there is ANY input, it will have precedence over links,
// i.e. links will only get focussed, if there is NO input whatsoever
focusFirstInputOrLink: function (dropdown) {
var toFocus = dropdown.find("ul :input:visible:first");
if (toFocus.length == 0) {
toFocus = dropdown.find("ul a:visible:first");
}
// actually a simple focus should be enough.
// The rest is only there to work around a rendering bug in webkit (as of Oct 2011),
// occuring mostly inside the login/signup dropdown.
toFocus.blur();
setTimeout(function() {
toFocus.focus();
}, 10);
},
registerEventHandlers: function () {
var self = this;
var toggler = $("#main-menu-toggle");
this.menu_container.on("closeDropDown", function (event) {
self.close($(event.target));
}).on("openDropDown", function (event) {
self.open($(event.target));
}).on("closeMenu", function () {
self.closing();
}).on("openMenu", function () {
self.open(self.dropdowns().first());
self.opening();
});
toggler.on("click", function() { // click on hamburger icon is closing other menu
self.closing();
});
}
});
// this holds all top menus currently active.
// if one opens, all others are closed.
var top_menus = [];
$.fn.top_menu = function () {
var new_menu;
$(this).each(function () {
new_menu = new TopMenu($(this));
top_menus.forEach(function (menu) {
menu.menu_container.on("openedMenu", function () {
new_menu.closing();
});
new_menu.menu_container.on("openedMenu", function () {
menu.closing();
});
});
top_menus.push(new_menu);
});
};
}(jQuery));
function skipMenu() {
// Skip to the breadcrumb or the first link in the toolbar or the first link in the content (homescreen)
const selectors = '.first-breadcrumb-element a, .toolbar-container a:first-of-type, #content a:first-of-type';
const visibleLink = jQuery(selectors)
.not(':hidden')
.first();
if (visibleLink.length) {
visibleLink.focus();
}
}
jQuery(document).ready(function($) {
$("#top-menu-items").top_menu();
});
-143
View File
@@ -1,143 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
(function($) {
var TypesCheckboxes = function () {
this.init();
};
TypesCheckboxes.prototype = $.extend(TypesCheckboxes.prototype, {
init: function () {
this.append_checkbox_listeners();
this.append_check_uncheck_all_listeners();
if (this.everything_unchecked()) {
this.check_and_disable_standard_type();
}
},
append_checkbox_listeners: function () {
var self = this;
this.all_checkboxes().on("change", function () {
if (self.everything_unchecked()) {
self.check_and_disable_standard_type();
self.display_explanation();
} else {
self.hide_explanation();
self.enable_standard_type();
}
});
},
append_check_uncheck_all_listeners: function () {
var self = this;
$("#project_types #check_all_types").click(function (event) {
self.enable_all_checkboxes();
self.check(self.all_checkboxes());
self.hide_explanation();
event.preventDefault();
});
$("#project_types #uncheck_all_types").click(function (event) {
self.enable_all_checkboxes();
self.uncheck(self.all_except_standard());
self.check_and_disable_standard_type();
self.display_explanation();
event.preventDefault();
});
},
everything_unchecked: function () {
return !(this.all_except_standard().filter(":checked").length > 0);
},
check_and_disable_standard_type: function () {
var standard = this.standard_check_boxes();
this.check($(standard));
this.disable($(standard));
},
enable_standard_type: function () {
this.enable(this.standard_check_boxes());
},
enable_all_checkboxes: function () {
this.enable(this.all_checkboxes());
},
check: function (boxes) {
$(boxes).prop("checked", true);
},
uncheck: function (boxes) {
$(boxes).prop("checked", false);
},
disable: function (boxes) {
var self = this;
$(boxes).prop('disabled', true);
$(boxes).each(function (ix, item) {
self.hidden_type_field($(item)).prop("value", $(item).prop("value"));
});
},
enable: function (boxes) {
var self = this;
$(boxes).prop('disabled', false);
$(boxes).each(function (ix, item) {
self.hidden_type_field($(item)).prop("value", "");
});
},
display_explanation: function () {
$("#types_flash_notice").show();
},
hide_explanation: function () {
$("#types_flash_notice").hide();
},
all_checkboxes: function () {
return $(".types :input[type='checkbox']");
},
all_except_standard: function () {
return $(".types :input[type='checkbox'][data-standard='false']");
},
standard_check_boxes: function () {
return $(".types :input[type='checkbox'][data-standard='true']");
},
hidden_type_field: function (for_box) {
return $(".types :input[type='hidden'][data-for='" + $(for_box).prop("id") + "']");
}
});
$('document').ready(function () {
new TypesCheckboxes();
});
})(jQuery);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-72
View File
@@ -1,72 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
(function ($) {
var applicable,
register_change_wp_by_status,
handle_change_wp_by_status,
init;
applicable = function () {
return $('body.controller-versions.action-show').length === 1;
};
init = function () {
register_change_wp_by_status();
};
register_change_wp_by_status = function () {
$('#status_by_select').change(function () {
handle_change_wp_by_status();
return false;
});
};
handle_change_wp_by_status = function () {
var form = $('#status_by_form'),
url = form.attr('action'),
data = form.serialize();
$.ajax({ url: url,
headers: { Accept: 'text/javascript' },
dataType: 'html',
data: data,
complete: function (jqXHR) {
form.replaceWith(jqXHR.responseText);
register_change_wp_by_status();
}
});
};
$('document').ready(function () {
if (applicable()) {
init();
}
});
})(jQuery);
@@ -1,18 +0,0 @@
## Datepicker
```
<input type="text" id="datepicker-input">
<div id="datepicker-inline"></div>
```
```
@javascript
jQuery(function($) {
$("#datepicker-inline").datepicker();
$("#datepicker-input").datepicker();
});
```
@@ -1,203 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
//$dp- var prefix for date picker
$dp-theme-color: #CCCCCC
$dp-theme-lines-color1: #FFFFFF
$dp-theme-lines-color2: #F8F8F8
$dp-theme-border-color: #DDDDDD
$dp-header-elements-height: 30px
$dp-header-top: 5px
$dp-header-padding: .2em 0
$dp-hover-normalisation: 2px
$dp-round-corners-radius: 0
$dp-cell-corner-radius: 3px
$dp-day-margin: 2px
$dp-day-margin-hover: 1px
$dp-days-text-color: #535353
$dp-week-padding-top: 7px
$dp-week-padding-bottom: 7px
$dp-week-text-color: #CCCCCC
$dp-border: 1px solid $dp-theme-border-color
$dp-font-size-rewrite: .9em
$dp-number-of-columns: 8
$dp-table-width: 100%
$dp-month-year-width: 40%
$dp-month-year-margin: 0 4.5%
$dp-shadow-box: 1px 1px 5px 0
$dp-shadow-box-opacity: 1
@mixin set-table-cells-width($table-width, $columns-number)
width: $table-width/$columns-number
table-layout: fixed
.ui-datepicker,
.work-package-table--container .ui-datepicker
border: $dp-border
padding: 0
background-color: $dp-theme-lines-color2
box-shadow: $dp-shadow-box rgba($dp-theme-color, $dp-shadow-box-opacity)
&.ui-datepicker-inline
box-shadow: none
&.ui-corner-all
border-radius: $dp-round-corners-radius
//[class^="ui-corner"] this selector is not working...
//a list is used
$corner-types: tl tr lf br top bottom right left all
@each $corner-type in $corner-types
.ui-corner-#{$corner-type}
border-radius: $dp-round-corners-radius
.ui-datepicker-header
padding: $dp-header-padding
border: none
background-color: $dp-theme-lines-color1
.ui-icon.ui-icon-circle-triangle-w
background-position: -96px -16px
user-select: none
.ui-icon.ui-icon-circle-triangle-e
background-position: -32px -16px
user-select: none
.ui-state-hover
border-width: 0 !important
background-color: transparent
cursor: pointer !important
.ui-datepicker-prev-hover
left: $dp-hover-normalisation
.ui-datepicker-next-hover
right: $dp-hover-normalisation
.ui-datepicker-prev,
.ui-datepicker-next
top: $dp-header-top
.ui-datepicker-title
.ui-datepicker-month,
.ui-datepicker-year
width: $dp-month-year-width
select
@extend .form--select
height: $dp-header-elements-height
font-size: $dp-font-size-rewrite
font-weight: normal
padding-top: 0
padding-bottom: 0
margin: $dp-month-year-margin
border: $dp-border
.ui-datepicker-calendar
margin-bottom: 0
border-spacing: 0
border-collapse: collapse
background-color: $dp-theme-lines-color1
th
background-color: $dp-theme-lines-color2
font-weight: normal
border-top: $dp-border
border-bottom: $dp-border
padding-top: $dp-week-padding-top
padding-bottom: $dp-week-padding-bottom
@include set-table-cells-width($dp-table-width, $dp-number-of-columns)
text-align: center
font-size: 0.9em
// Cancel out effect of min-width css in generic-table in cases where the
// date picker is included inside a table.
table.generic-table &
min-width: 0px
td
padding: 0
color: $dp-days-text-color
@include set-table-cells-width($dp-table-width, $dp-number-of-columns)
a
border: none
text-align: center
background-color: #FFFFFF
margin: $dp-day-margin
&.ui-state-highlight,
&.ui-state-active,
&:hover
margin: $dp-day-margin-hover
border: $dp-border
border-radius: $dp-cell-corner-radius
&.ui-state-active
border-color: $inplace-edit--selected-date-border-color
// Cancel out effect of min-width css in generic-table in cases where the
// date picker is included inside a table.
table.generic-table &
min-width: 0px
tbody tr
border-bottom: none
&:hover
background-color: $dp-theme-lines-color1
.ui-datepicker-week-col
color: $dp-week-text-color
text-align: center
.ui-datepicker-buttonpane
border-top: $dp-border
margin-top: 0
button
font-weight: normal
font-size: $dp-font-size-rewrite
border: none
background-color: transparent
.ui-priority-secondary
opacity: 1
filter: Alpha(Opacity = 100)
font-weight: bold
// ui-datepicker arbitrarily sets the css z-index using JS
// So we have to override that value.
#ui-datepicker-div
z-index: 10001 !important
position: absolute
@@ -1,80 +0,0 @@
@import content/accounts
@import content/accounts_mobile
@import content/augmented
@import content/badges
@import content/blockquotes
@import content/context_menu
@import content/editable_toolbar
@import content/forms
@import content/forms_mobile
@import content/calendars
@import content/comments
@import content/collapsible_section
@import content/copy_to_clipboard
@import content/custom_logo
@import content/notifications
@import content/notifications_mobile
@import content/links
@import content/loading_indicator
@import content/enterprise
@import content/members
@import content/my_account
@import content/my_page
@import content/project_overview
@import content/news
@import content/buttons
@import content/icon_control
@import content/drag_and_drop
@import content/boxes
@import content/info_boxes
@import content/headings
@import content/watchers
@import content/simple_filters
@import content/advanced_filters
@import content/attributes_key_value
@import content/attributes_group
@import content/information_section
@import content/widget_box
@import content/wiki
@import content/list
@import content/work_packages
@import content/table
@import content/tables
@import content/tabular
@import content/types_form_configuration
@import content/user
@import content/preview
@import content/modal
@import content/journal
@import content/pagination
@import content/progress_bar
@import content/request_for_confirmation
@import content/form_error_messages
@import content/ajax_indicator
@import content/tooltips
@import content/tabs
@import content/autocomplete
@import content/diff
@import content/projects_list
@import content/datepicker
@import content/focus_within
@import content/help_texts
@import content/on_off_status
@import content/custom_actions
@import content/user_mention
@import content/hide_section
@import content/hide_until_initialized
@import content/hidden
@import content/search
@import content/security_badge
@import content/contextual
@import content/tooltip
@import content/grid
@import content/grid_mobile
@import content/resizer
@import content/version
@import content/project_status
@import content/menus/_project_autocompletion
@import content/menus/_menu_blocks
@import content/editor/index
@@ -1,17 +0,0 @@
# Preview
```
<fieldset class="preview">
<legend class="preview--legend">Preview - Description</legend>
<div class="wiki">
<h2>An article that's still being written.</h2>
</div>
</fieldset>
```
```
<fieldset class="preview">
<legend class="preview--legend">Preview - Description</legend>
<span class="preview--nothing-to-preview">Nothing to preview</span>
</fieldset>
```
@@ -1,45 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
.preview
@extend %form--fieldset-or-section
padding: 1rem
background: image-url('draft.png')
// Min-width has to be defined to avoid a content overflow
// of the preview window. Since this class is also applied to the buttons
// it should however not be too large.
min-width: 0
overflow-wrap: break-word
word-wrap: break-word
.preview--legend
@extend %form--fieldset-legend-or-section-title
border: 0
.preview--nothing-to-preview
color: red
@@ -1,5 +0,0 @@
@import './markdown'
@import './ckeditor'
@import './macros'
@import './rouge'
@import './codemirror'
@@ -1,44 +0,0 @@
// Specific styles for displaying rendered markdown on all pages
// This is still selected by div.wiki since this is still used throughout the app.
div.wiki
// Style pre tags alone
pre
margin: 1em 1em 1em 1.6em
padding: 2px 2px 2px 0
background-color: #fafafa
border: 1px solid #dadada
width: auto
overflow-x: auto
overflow-y: hidden
// Avoid doubling borders in pre > code
pre > code
display: block
border: none
background: transparent
color: initial
ul.task-list
list-style: none
margin-left: 25px
.task-list-item
display: flex
flex-wrap: wrap
white-space: pre-wrap
input
height: 25px
margin-right: 5px
// Ensure nested task lists are wrapped
// by making nested lists 100% row width
ul.task-list
flex-basis: 100%
white-space: nowrap
// remove indentation of top-level task-list
> ul.task-list
margin-left: 0
-2
View File
@@ -1,2 +0,0 @@
@import fonts/lato
@import fonts/openproject_icon_font
-53
View File
@@ -1,53 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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.
//++
@import layout/admin
@import layout/base
@import layout/base_mobile
@import layout/grid
@import layout/tree_menu
@import layout/warning_bar
@import layout/top_menu
@import layout/top_menu_mobile
@import layout/breadcrumb
@import layout/main_menu
@import layout/main_menu_mobile
@import layout/colors
@import layout/drop_down
@import layout/drop_down_mobile
@import layout/toolbar
@import layout/toolbar_mobile
@import layout/zen_mode
// Work packages page layouts
@import layout/work_packages/index
// Print layout
@import layout/print
@@ -1,7 +0,0 @@
@import ./table
@import ./table_embedded
@import ./details_view
@import ./full_view
@import ./mobile
@import ./print
@import ./query_menu
-48
View File
@@ -1,48 +0,0 @@
/*-- copyright
* OpenProject is an open source project management software.
* Copyright (C) 2012-2020 the OpenProject GmbH
*
* 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 docs/COPYRIGHT.rdoc for more details. ++
*/
// Enable asset pipeline for Bourbon mixins (e.g. +font-face)
// @see http://bourbon.io/docs/#asset-pipeline
$bourbon: ("rails-asset-pipeline": function-exists(font-url))
// Variables
@import openproject/variables
// Foundation variables
@import openproject/settings
// Dependencies
@import bourbon
@import foundation
// Core styles
@import fonts/index
@import openproject/index
@import vendor/index
@import layout/index
@import content/index
@@ -1,13 +0,0 @@
@import openproject/accessibility
@import openproject/announcements
@import openproject/functions
@import openproject/homescreen
@import openproject/generic
@import openproject/mixins
@import openproject/onboarding
@import openproject/scm
// allow plugins to hook into global assets
@import openproject/plugins
@@ -1,5 +0,0 @@
<% Redmine::Plugin.all.collect do |plugin| %>
<% plugin.registered_global_assets[:css].each do |path| %>
@import "<%= path %>";
<% end %>
<% end %>
@@ -1,11 +0,0 @@
Color Variables
===============
## Global Color Variables
@colors {
- $body-font-color - -
- $gray-lighter $gray-light $gray $gray-dark
- $primary-color $primary-color-dark $alternative-color
- $secondary-color
}
@@ -1,41 +0,0 @@
<%#
Depend on the design file so that this is recompiled on core changes.
Doesn't work for theme plugins, sorry :-/ just update the core design.rb as well to reload!
%>
<% depend_on Rails.root.join('lib', 'open_project', 'custom_styles', 'design.rb') %>
<%#
Set defaults for the following variables
primary-color
primary-color-dark
alternative-color
This allows us to dynamically define CSS4 + Sass variables from a single map
%>
<% ::OpenProject::CustomStyles::Design.variables.each do |var, hexcode| %>
$<%= var %>: <%= hexcode %>;
<% end %>
:root {
<% ::OpenProject::CustomStyles::Design.variables.each do |var, definition| %>
<% css4definition = definition.gsub(/\$([\w-]+)/, 'var(--\1)') %>
--<%= var %>: <%= css4definition %>;
<% end %>
}
<%# Construct a sass map to lookup variables in the mixin below %>
$variable-map: (
<% ::OpenProject::CustomStyles::Design.variables.each do |var, definition| %>
<%= var %>: #{<%= definition %>},
<% end %>
);
// use CSS4 + Sass variable fallback for the given property
// e.g., @include varprop(background-color, primary-color)
@mixin varprop($prop, $name, $suffix:"") {
#{$prop}: map-get($variable-map, $name) unquote($suffix);
#{$prop}: var(--#{$name}) unquote($suffix);
}
// Other variables
$work-package-details--tab-height: 40px;
+1 -108
View File
@@ -1,108 +1 @@
@require sassc
@sass ../stylesheets/openproject.sass
@title "Living Style Guide for OpenProject"
@style base-font: Lato
@style base-font-size: 16px
@style background-color: white
@default preprocessor: sass
@css
.livingstyleguide--header {
@include grid-content;
}
.livingstyleguide--footer {
@include grid-content;
}
.livingstyleguide--intro {
@include grid-content;
}
.styleguide-banner {
height: 280px;
padding: 200px 0 0;
background: url('/assets/styleguide/logo_openproject.png');
background-position: top center;
background-repeat: no-repeat;
}
.styleguide-banner--text {
text-align: center;
}
.styleguide-nav--menu-items {
@extend %menu-bar;
@include menu-bar-layout;
@include menu-bar-style(#eee);
}
.livingstyleguide--code-block {
max-height: 300px;
}
.livingstyleguide--example {
overflow: visible;
}
.lsg-search-box {
width: 40%;
margin: 0 auto;
}
.icon-list {
display: flex;
flex-flow: row wrap;
width: 640px;
margin: 0 auto;
}
.icon-list li {
flex: 1;
flex-basis: 15%;
display: block;
text-align: center;
margin: 10px;
font-size: 12px;
}
.icon-list .span {
display: block;
font-size: 30px;
}
@header
<header class="livingstyleguide--header">
<div class="styleguide-banner">
<h1 class="styleguide-banner--text">Living Style Guide</h1>
</div>
</header>
<nav class="styleguide-nav">
<ul class="styleguide-nav--menu-items">
<li><a target="_self" href="#color-variables">Colors</a></li>
<li><a target="_self" href="#fonts">Fonts</a></li>
<li><a target="_self" href="#forms">Forms</a></li>
<li><a target="_self" href="#notifications">Notifications</a></li>
<li><a target="_self" href="#buttons">Buttons</a></li>
<li><a target="_self" href="#pagination">Pagination</a></li>
</ul>
</nav>
<section class="livingstyleguide--intro">
<p>&nbsp;</p>
</section>
@javascript-before /assets/styleguide.js
@search-box
@footer
<footer class="livingstyleguide--footer">
Copyright © 2019 OpenProject - All rights reserved.
</footer>
@import ../stylesheets/**/_*.lsg
@import ../../../frontend/src/global_styles/**/_*.lsg
-5
View File
@@ -1,5 +0,0 @@
// Vendor definitions from plugins/gems
@import vendor/dragula
@import vendor/jquery_ui
@import vendor/enjoyhint
-431
View File
@@ -1,431 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) 2012-2020 the OpenProject GmbH
//
// 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 docs/COPYRIGHT.rdoc for more details.
//++
/*!
* jQuery UI CSS Framework 1.10.4
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/theming/
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/
*/
/* Component containers
----------------------------------*/
.ui-widget {
font-family: $body-font-family;
font-size: rem-calc(15px);
}
.ui-widget .ui-widget {
font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
font-family: $body-font-family;
font-size: 1em;
}
.ui-widget-content {
border: 1px solid #aaaaaa/*{borderColorContent}*/;
background: #ffffff/*{bgColorContent}*/;
color: #222222/*{fcContent}*/;
}
.ui-widget-content a {
color: #222222/*{fcContent}*/;
}
.ui-widget-header {
border: 1px solid #aaaaaa/*{borderColorHeader}*/;
background: #cccccc/*{bgColorHeader}*/;
color: #222222/*{fcHeader}*/;
font-weight: bold;
}
.ui-widget-header a {
color: #222222/*{fcHeader}*/;
}
/* Interaction states
----------------------------------*/
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #d3d3d3/*{borderColorDefault}*/;
background: #e6e6e6/*{bgColorDefault}*/;
font-weight: normal/*{fwDefault}*/;
color: #555555/*{fcDefault}*/;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited {
color: #555555/*{fcDefault}*/;
text-decoration: none;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
border: 1px solid #999999/*{borderColorHover}*/;
background: #dadada/*{bgColorHover}*/;
font-weight: normal/*{fwDefault}*/;
color: #212121/*{fcHover}*/;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited,
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited {
color: #212121/*{fcHover}*/;
text-decoration: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: 1px solid #aaaaaa/*{borderColorActive}*/;
background: #ffffff/*{bgColorActive}*/;
font-weight: normal/*{fwDefault}*/;
color: #212121/*{fcActive}*/;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
color: #212121/*{fcActive}*/;
text-decoration: none;
}
/* Interaction Cues
----------------------------------*/
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #fcefa1/*{borderColorHighlight}*/;
background: #fbf9ee/*{bgColorHighlight}*/;
color: #363636/*{fcHighlight}*/;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
color: #363636/*{fcHighlight}*/;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #cd0a0a/*{borderColorError}*/;
background: #fef1ec/*{bgColorError}*/;
color: #cd0a0a/*{fcError}*/;
}
.ui-state-error a,
.ui-widget-content .ui-state-error a,
.ui-widget-header .ui-state-error a {
color: #cd0a0a/*{fcError}*/;
}
.ui-state-error-text,
.ui-widget-content .ui-state-error-text,
.ui-widget-header .ui-state-error-text {
color: #cd0a0a/*{fcError}*/;
}
.ui-priority-primary,
.ui-widget-content .ui-priority-primary,
.ui-widget-header .ui-priority-primary {
font-weight: bold;
}
.ui-priority-secondary,
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
background-image: none;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
width: 16px;
height: 16px;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: image-url('jquery-ui/ui-icons_222222_256x240.png')/*{iconsContent}*/;
}
.ui-widget-header .ui-icon {
background-image: image-url('jquery-ui/ui-icons_222222_256x240.png')/*{iconsHeader}*/;
}
.ui-state-default .ui-icon {
background-image: image-url('jquery-ui/ui-icons_888888_256x240.png')/*{iconsDefault}*/;
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: image-url('jquery-ui/ui-icons_454545_256x240.png')/*{iconsHover}*/;
}
.ui-state-active .ui-icon {
background-image: image-url('jquery-ui/ui-icons_454545_256x240.png')/*{iconsActive}*/;
}
.ui-state-highlight .ui-icon {
background-image: image-url('jquery-ui/ui-icons_2e83ff_256x240.png')/*{iconsHighlight}*/;
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: image-url('jquery-ui/ui-icons_cd0a0a_256x240.png')/*{iconsError}*/;
}
/* positioning */
.ui-icon-blank { background-position: 16px 16px; }
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 4px/*{cornerRadius}*/;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 4px/*{cornerRadius}*/;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 4px/*{cornerRadius}*/;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 4px/*{cornerRadius}*/;
}
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa/*{bgColorOverlay}*/;
opacity: .3/*{opacityOverlay}*/;
}
.ui-widget-shadow {
margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/;
padding: 8px/*{thicknessShadow}*/;
background: #aaaaaa/*{bgColorShadow}*/;
opacity: .3/*{opacityShadow}*/;
border-radius: 8px/*{cornerRadiusShadow}*/;
}
@@ -1,49 +0,0 @@
// Foundation for Apps
// by ZURB
// foundation.zurb.com
// Licensed under MIT Open Source
$foundation-version: '1.1.0';
// Make sure the charset is set appropriately
@charset "UTF-8";
// Libraries (let's make Normalize an external dependency eventually)
@import
"vendor/normalize";
// Helpers
@import
"helpers/functions",
"helpers/mixins",
"helpers/breakpoints",
"helpers/images";
// Global styles
@import
"global";
// Components
@import
"components/action-sheet",
"components/block-list",
"components/button",
"components/button-group",
"components/card",
"components/extras",
"components/forms",
"components/grid",
"components/title-bar",
"components/label",
"components/list",
"components/menu-bar",
"components/modal",
"components/motion",
"components/notification",
"components/off-canvas",
"components/popup",
"components/switch",
"components/tabs",
"components/accordion",
"components/typography",
"components/utilities";
+1 -1
View File
@@ -4,7 +4,7 @@
<form action="<%= form_url %>"
method="post"
id="<%= row.roles_css_id %>-form"
class="hol <%= row.toggle_item_class_name %>"
class="<%= row.toggle_item_class_name %>"
style="display:none"
accept-charset="UTF-8"
>
@@ -0,0 +1,53 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 AttributeHelpTexts
class BaseContract < ::ModelContract
include Attachments::ValidateReplacements
def validate
validate_user_allowed_to_manage
super
end
def self.model
AttributeHelpText
end
attribute :type
attribute :attribute_name
attribute :help_text
def validate_user_allowed_to_manage
errors.add :base, :error_unauthorized unless user.admin?
end
end
end
@@ -1,3 +1,5 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
@@ -26,9 +28,7 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
module OpenProject::Documents
class Hooks < Redmine::Hook::ViewListener
render_on :activity_index_head,
partial: 'hooks/documents/activity_index_head'
module AttributeHelpTexts
class CreateContract < BaseContract
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 AttributeHelpTexts
class UpdateContract < BaseContract
end
end
+1 -1
View File
@@ -28,7 +28,7 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
class EmptyContract
class EmptyContract < ModelContract
def initialize(*); end
def validate
+3 -24
View File
@@ -28,8 +28,10 @@
module Projects
class ArchiveContract < ModelContract
include Projects::Archiver
def validate
user_allowed
validate_admin_only
validate_no_foreign_wp_references
super
@@ -37,31 +39,8 @@ module Projects
protected
def user_allowed
unless authorized?
errors.add :base, :error_unauthorized
end
end
# Check that there is no wp of a non descendant project that is assigned
# to one of the project or descendant versions
def validate_no_foreign_wp_references
version_ids = model.rolled_up_versions.select(:id)
exists = WorkPackage
.where.not(project_id: model.self_and_descendants.select(:id))
.where(version_id: version_ids)
.exists?
errors.add :base, :foreign_wps_reference_version if exists
end
def validate_model?
false
end
def authorized?
user.admin?
end
end
end
+56
View File
@@ -0,0 +1,56 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 Projects
module Archiver
def validate_admin_only
unless user.admin?
errors.add :base, :error_unauthorized
end
end
# Check that there is no wp of a non descendant project that is assigned
# to one of the project or descendant versions
def validate_no_foreign_wp_references
version_ids = model.rolled_up_versions.select(:id)
exists = WorkPackage
.where.not(project_id: model.self_and_descendants.select(:id))
.where(version_id: version_ids)
.exists?
errors.add :base, :foreign_wps_reference_version if exists
end
def validate_all_ancestors_active
if model.ancestors.any?(&:archived?)
errors.add :base, :archived_ancestor
end
end
end
end
+36 -1
View File
@@ -32,6 +32,7 @@ module Projects
class BaseContract < ::ModelContract
include AssignableValuesContract
include AssignableCustomFieldValues
include Projects::Archiver
attribute :name
attribute :identifier
@@ -39,6 +40,7 @@ module Projects
attribute :public
attribute :active do
validate_active_present
validate_changing_active
end
attribute :parent do
validate_parent_assignable
@@ -46,6 +48,9 @@ module Projects
attribute :status do
validate_status_code_included
end
attribute :templated do
validate_templated_set_by_admin
end
def validate
validate_user_allowed_to_manage
@@ -93,7 +98,9 @@ module Projects
def validate_user_allowed_to_manage
with_unchanged_id do
errors.add :base, :error_unauthorized unless user.allowed_to?(manage_permission, model)
with_active_assumed do
errors.add :base, :error_unauthorized unless user.allowed_to?(manage_permission, model)
end
end
end
@@ -101,6 +108,12 @@ module Projects
errors.add :status, :inclusion if model.status&.code && !Projects::Status.codes.keys.include?(model.status.code.to_s)
end
def validate_templated_set_by_admin
if model.templated_changed? && !user.admin?
errors.add :templated, :error_unauthorized
end
end
def manage_permission
raise NotImplementedError
end
@@ -113,5 +126,27 @@ module Projects
ensure
model.id = project_id
end
def with_active_assumed
active = model.active
model.active = true
yield
ensure
model.active = active
end
def validate_changing_active
return unless model.active_changed?
validate_admin_only
if model.active?
# switched to active -> unarchiving
validate_all_ancestors_active
else
validate_no_foreign_wp_references
end
end
end
end
+7 -1
View File
@@ -28,10 +28,16 @@
module Projects
class CopyContract < BaseContract
protected
def validate_model?
false
end
private
def validate_user_allowed_to_manage
errors.add :base, :error_unauthorized unless user.allowed_to?(:copy_projects, options[:copied_from])
errors.add :base, :error_unauthorized unless user.allowed_to?(:copy_projects, options[:copy_source])
end
end
end
@@ -0,0 +1,59 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 Projects
class InstantiateTemplateContract < CreateContract
def self.visible_templates(user)
Project
.allowed_to(user, :copy_projects)
.where(templated: true)
end
def validate
validate_user_allowed_to_instantiate_template
super
end
private
def validate_user_allowed_to_instantiate_template
errors.add(:base, :error_unauthorized) unless visible_template?
end
def visible_template?
return false if template_project_id.nil?
self.class.visible_templates(user).exists?(template_project_id)
end
def template_project_id
options[:template_project_id]
end
end
end
+3 -17
View File
@@ -28,8 +28,10 @@
module Projects
class UnarchiveContract < ModelContract
include Projects::Archiver
def validate
user_allowed
validate_admin_only
validate_all_ancestors_active
super
@@ -37,24 +39,8 @@ module Projects
protected
def user_allowed
unless authorized?
errors.add :base, :error_unauthorized
end
end
def validate_all_ancestors_active
if model.ancestors.any?(&:archived?)
errors.add :base, :archived_ancestor
end
end
def validate_model?
false
end
def authorized?
user.admin?
end
end
end
+35
View File
@@ -0,0 +1,35 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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.
#++
require 'queries/base_contract'
module Queries
class CopyContract < BaseContract
end
end
@@ -73,6 +73,12 @@ module TimeEntries
end
end
# Necessary for custom fields
# of type version.
def assignable_versions
work_package.try(:assignable_versions) || project.try(:assignable_versions) || []
end
private
def validate_work_package
+6 -4
View File
@@ -80,11 +80,13 @@ module WorkPackages
model.project.possible_responsible_members
end
attribute :schedule_manually
attribute :start_date,
writeable: ->(*) {
model.leaf?
model.leaf? || model.schedule_manually?
} do
if start_before_soonest_start?
if !model.schedule_manually? && start_before_soonest_start?
message = I18n.t('activerecord.errors.models.work_package.attributes.start_date.violates_relationships',
soonest_start: model.soonest_start)
@@ -94,7 +96,7 @@ module WorkPackages
attribute :due_date,
writeable: ->(*) {
model.leaf?
model.leaf? || model.schedule_manually?
}
validates :due_date,
@@ -134,7 +136,7 @@ module WorkPackages
ret = super
# If we're in a readonly status and did not move into that status right now
# only allow other status transitions
# only allow other status transitions. But also prevent that if the associated version is closed.
if model.readonly_status? && !model.status_id_change
ret &= %w(status status_id)
end
+3 -1
View File
@@ -63,9 +63,11 @@ class AccountController < ApplicationController
# Keep attributes from the session
# to identify the user
previous_session = session.to_h.with_indifferent_access
previous_user = current_user
logout_user
perform_post_logout previous_session
perform_post_logout previous_session, previous_user
end
# Enable user to choose a new password
@@ -28,10 +28,12 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
class MyPage::AngularController < ::ApplicationController
class AngularController < ApplicationController
before_action :require_login
def no_menu
render layout: 'no_menu'
def empty_layout
# Frontend will handle rendering
# but we will need to render with layout
render html: '', layout: 'angular'
end
end
@@ -37,6 +37,8 @@ class AttributeHelpTextsController < ApplicationController
before_action :find_type_scope
before_action :require_enterprise_token_grant
helper_method :gon
def new
@attribute_help_text = AttributeHelpText.new type: @attribute_scope
end
@@ -44,23 +46,30 @@ class AttributeHelpTextsController < ApplicationController
def edit; end
def update
@attribute_help_text.attributes = permitted_params.attribute_help_text
call = ::AttributeHelpTexts::UpdateService
.new(user: current_user, model: @attribute_help_text)
.call(permitted_params_with_attachments)
if @attribute_help_text.save
if call.success?
flash[:notice] = t(:notice_successful_update)
redirect_to attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)
else
flash[:error] = call.message || I18n.t('notice_internal_server_error')
render action: 'edit'
end
end
def create
@attribute_help_text = AttributeHelpText.new permitted_params.attribute_help_text
call = ::AttributeHelpTexts::CreateService
.new(user: current_user)
.call(permitted_params_with_attachments)
if @attribute_help_text.save
if call.success?
flash[:notice] = t(:notice_successful_create)
redirect_to attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)
redirect_to attribute_help_texts_path(tab: call.result.attribute_scope)
else
@attribute_help_text = call.result
flash[:error] = call.message || I18n.t('notice_internal_server_error')
render action: 'new'
end
end
@@ -95,6 +104,20 @@ class AttributeHelpTextsController < ApplicationController
private
def permitted_params_with_attachments
permitted_params.attribute_help_text.merge(attachment_params)
end
def attachment_params
attachment_params = permitted_params.attachments.to_h
if attachment_params.any?
{ attachment_ids: attachment_params.values.map(&:values).flatten }
else
{}
end
end
def find_entry
@attribute_help_text = AttributeHelpText.find(params[:id])
rescue ActiveRecord::RecordNotFound
@@ -103,13 +103,25 @@ module Accounts::CurrentUser
end
# Redirect the user according to the logout scheme
def perform_post_logout(_prev_session)
def perform_post_logout(prev_session, prev_user)
# First, check if there is an SLO callback for a given
# omniauth provider of the user
provider = ::OpenProject::Plugins::AuthPlugin.login_provider_for(prev_user)
if provider && (callback = provider[:single_sign_out_callback])
instance_exec prev_session, prev_user, &callback
return if performed?
end
# Otherwise, if there is an omniauth direct login
# and we're not logging out globablly, ensure the
# user does not get re-logged in
if Setting.login_required? && omniauth_direct_login?
flash.now[:notice] = I18n.t :notice_logged_out
render :exit, locals: { instructions: :after_logout }
else
redirect_to home_url
return
end
redirect_to(home_url) unless performed?
end
# Login the current user
@@ -81,14 +81,8 @@ module Accounts::OmniauthLogin
return user unless user.nil?
if Setting.oauth_allow_remapping_of_existing_users?
# Allow to map existing users with an Omniauth source if the login
# already exists, and no existing auth source or omniauth provider is
# linked
user = User.find_by(
login: user_attributes[:login],
identity_url: nil,
auth_source_id: nil
)
# Allow to map existing users with an Omniauth source if the login already exists
user = User.find_by(login: user_attributes[:login])
end
if user.nil?
@@ -202,8 +196,14 @@ module Accounts::OmniauthLogin
end
end
##
# Allow strategies to map a value for uid instead
# of always taking the global UID.
# For SAML, the global UID may change with every session
# (in case of transient nameIds)
def identity_url_from_omniauth(auth)
"#{auth[:provider]}:#{auth[:uid]}"
identifier = auth[:info][:uid] || auth[:uid]
"#{auth[:provider]}:#{identifier}"
end
# if the omni auth registration happened too long ago,
+11 -2
View File
@@ -123,13 +123,16 @@ module AuthSourceSSO
end
def sso_login_failed?(user)
user.nil? || user.new_record? || !user.active?
user.nil? || user.new_record? || !(user.active? || user.invited?)
end
def handle_sso_for!(user, login)
if sso_login_failed?(user)
handle_sso_failure!({ user: user, login: login })
else # valid user
# If a user is invited, ensure it gets activated
activate_user_if_invited! user
handle_sso_success user
end
end
@@ -141,7 +144,13 @@ module AuthSourceSSO
user
end
def perform_post_logout(prev_session)
def activate_user_if_invited!(user)
return unless user.invited?
user.activate!
end
def perform_post_logout(prev_session, previous_user)
if prev_session[:user_from_auth_header] && header_slo_url.present?
redirect_to header_slo_url
else
+6 -3
View File
@@ -36,10 +36,10 @@ class CopyProjectsController < ApplicationController
call = project_copy(@copy_project)
if call.success?
enqueue_copy_job
job = enqueue_copy_job
copy_started_notice
redirect_to origin
redirect_to job_status_path job.job_id
else
@errors = call.errors
render action: copy_action
@@ -47,7 +47,10 @@ class CopyProjectsController < ApplicationController
end
def copy_project
@copy_project = Project.copy_attributes(@project)
@copy_project = Projects::CopyService
.new(user: current_user, source: @project)
.call(target_project_params: {}, attributes_only: true)
.result
if @copy_project
project_copy(@copy_project, EmptyContract)
+18 -38
View File
@@ -89,20 +89,28 @@ class CustomStylesController < ApplicationController
def update_colors
variable_params = params[:design_colors].first
set_colors(variable_params)
set_theme(params)
::Design::UpdateDesignService
.new(colors: variable_params, theme: params[:theme])
.call
redirect_to action: :show
end
def update_themes
theme = OpenProject::CustomStyles::ColorThemes::THEMES.find { |t| t[:name] == params[:theme] }
color_params = theme[:colors]
logo = theme[:logo]
theme = OpenProject::CustomStyles::ColorThemes.themes.find { |t| t[:theme] == params[:theme] }
set_logo(logo)
set_colors(color_params)
set_theme(params)
call = ::Design::UpdateDesignService
.new(theme)
.call
call.on_success do
flash[:notice] = I18n.t(:notice_successful_update)
end
call.on_failure do
flash[:error] = call.message
end
redirect_to action: :show
end
@@ -114,40 +122,12 @@ class CustomStylesController < ApplicationController
private
def options_for_theme_select
options = OpenProject::CustomStyles::ColorThemes::THEMES.map { |val| val[:name] }
options << [t('admin.custom_styles.color_theme_custom'), '', disabled: true] if @current_theme.empty?
options = OpenProject::CustomStyles::ColorThemes.themes.map { |val| val[:theme] }
options << [t('admin.custom_styles.color_theme_custom'), '', selected: true, disabled: true] unless @current_theme.present?
options
end
def set_logo(logo)
get_or_create_custom_style.update(theme_logo: logo)
end
def set_colors(variable_params)
variable_params.each do |param_variable, param_hexcode|
if design_color = DesignColor.find_by(variable: param_variable)
if param_hexcode.blank?
design_color.destroy
elsif design_color.hexcode != param_hexcode
design_color.hexcode = param_hexcode
design_color.save
end
else
# create that design_color
design_color = DesignColor.new variable: param_variable, hexcode: param_hexcode
design_color.save
end
end
end
def set_theme(params)
theme = ActionController::Parameters.new(theme: params[:theme] || '').permit(:theme)
@custom_style = get_or_create_custom_style
@custom_style.update(theme)
end
def get_or_create_custom_style
CustomStyle.current || CustomStyle.create!
end
+23 -4
View File
@@ -38,6 +38,7 @@ class EnterprisesController < ApplicationController
before_action :youtube_content_security_policy
before_action :require_admin
before_action :check_user_limit, only: [:show]
before_action :check_domain, only: [:show]
def show
@current_token = EnterpriseToken.current
@@ -66,7 +67,10 @@ class EnterprisesController < ApplicationController
@token.encoded_token = saved_encoded_token
@current_token = @token || EnterpriseToken.new
end
render action: :show
respond_to do |format|
format.html { render action: :show }
format.json { render json: { description: @token.errors.full_messages.join(", ") }, status: 400 }
end
end
end
@@ -76,8 +80,7 @@ class EnterprisesController < ApplicationController
token.destroy
flash[:notice] = t(:notice_successful_delete)
trial_key = Token::EnterpriseTrialKey.find_by(user_id: User.system.id)
trial_key&.destroy
delete_trial_key
redirect_to action: :show
else
@@ -89,19 +92,25 @@ class EnterprisesController < ApplicationController
Token::EnterpriseTrialKey.create(user_id: User.system.id, value: params[:trial_key])
end
def delete_trial_key
Token::EnterpriseTrialKey.where(user_id: User.system.id).delete_all
end
private
def write_trial_key_to_gon
@trial_key = Token::EnterpriseTrialKey.find_by(user_id: User.system.id)
if @trial_key
gon.ee_trial_key = {
value: @trial_key.value
value: @trial_key.value,
created: @trial_key.created_on
}
end
end
def write_augur_to_gon
gon.augur_url = OpenProject::Configuration.enterprise_trial_creation_host
gon.token_version = OpenProject::Token::VERSION
end
def default_breadcrumb
@@ -121,4 +130,14 @@ class EnterprisesController < ApplicationController
)
end
end
def check_domain
if OpenProject::Enterprise.token.try(:invalid_domain?)
flash.now[:error] = I18n.t(
"error_enterprise_token_invalid_domain",
expected: Setting.host_name,
actual: OpenProject::Enterprise.token.domain
)
end
end
end
+3 -2
View File
@@ -1,4 +1,5 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
@@ -101,7 +102,7 @@ class JournalsController < ApplicationController
end
def valid_diff?
return false unless valid_field?(params[:field])
@journal.journable.class == WorkPackage
valid_field?(params[:field]) &&
@journal.journable.class == WorkPackage
end
end
@@ -34,6 +34,17 @@ module OAuth
# See config/initializers/doorkeeper.rb
class AuthBaseController < ::ApplicationController
skip_before_action :check_if_login_required
after_action :extend_content_security_policy
layout 'only_logo'
def extend_content_security_policy
use_content_security_policy_named_append(:oauth)
end
def allowed_forms
allowed_redirect_urls = pre_auth&.client&.application&.redirect_uri
urls = allowed_redirect_urls.to_s.split
urls.map { |url| URI.join(url, '/') }.map(&:to_s)
end
end
end
+36 -10
View File
@@ -54,7 +54,6 @@ class ProjectsController < ApplicationController
end
@projects = load_projects query
@custom_fields = ProjectCustomField.visible(User.current)
render layout: 'no_menu'
end
@@ -80,16 +79,16 @@ class ProjectsController < ApplicationController
end
def create
call_result = Projects::CreateService
.new(user: current_user)
.call(permitted_params.project)
call_result =
if params[:from_template].present?
create_from_template
else
create_from_params
end
@project = call_result.result
if call_result.success?
flash[:notice] = t(:notice_successful_create)
redirect_work_packages_or_overview
else
# In success case, nothing to do
call_result.on_failure do
@project = call_result.result
@errors = call_result.errors
assign_default_create_variables
@@ -261,6 +260,33 @@ class ProjectsController < ApplicationController
protected
def create_from_params
call_result = Projects::CreateService
.new(user: current_user)
.call(permitted_params.project)
@project = call_result.result
call_result.on_success do
flash[:notice] = t(:notice_successful_create)
redirect_work_packages_or_overview
end
call_result
end
def create_from_template
call_result = Projects::InstantiateTemplateService
.new(user: current_user, template_id: params[:from_template])
.call(permitted_params.project)
call_result.on_success do
flash[:notice] = t('project.template.copying')
redirect_to job_status_path(call_result.result.job_id)
end
call_result
end
def set_sorting(query)
orders = query.orders.select(&:valid?).map { |o| [o.attribute.to_s, o.direction.to_s] }
+8 -10
View File
@@ -79,18 +79,16 @@ class UsersController < ApplicationController
events = Activities::Fetcher.new(User.current, author: @user).events(nil, nil, limit: 10)
@events_by_day = events.group_by { |e| e.event_datetime.to_date }
unless User.current.admin?
if !(@user.active? ||
@user.registered?) ||
(@user != User.current && @memberships.empty? && events.empty?)
render_404
return
if !User.current.admin? &&
(!(@user.active? ||
@user.registered?) ||
(@user != User.current && @memberships.empty? && events.empty?))
render_404
else
respond_to do |format|
format.html { render layout: 'no_menu' }
end
end
respond_to do |format|
format.html { render layout: 'no_menu' }
end
end
def new
+23 -11
View File
@@ -53,6 +53,8 @@ class WikiController < ApplicationController
diff
annotate
destroy]
before_action :find_wiki_page, only: %i[show]
before_action :handle_new_wiki_page, only: %i[show]
before_action :build_wiki_page_and_content, only: %i[new create]
include AttachmentsHelper
@@ -120,17 +122,6 @@ class WikiController < ApplicationController
# display a page (in editing mode if it doesn't exist)
def show
@page = @wiki.find_or_new_page(wiki_page_title)
if @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit
render action: 'new'
else
render_404
end
return
end
# Set the related page ID to make it the parent of new links
flash[:_related_wiki_page_id] = @page.id
@@ -389,6 +380,27 @@ class WikiController < ApplicationController
render_404
end
# Finds or created the wiki page associated
# to the wiki
def find_wiki_page
@page = @wiki.find_or_new_page(wiki_page_title)
end
# Handles new pages for non-editable permissions
def handle_new_wiki_page
return unless @page.new_record?
if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
edit
render action: :new
elsif params[:id] == 'wiki'
flash[:info] = I18n.t('wiki.page_not_editable_index')
redirect_to action: :index
else
render_404
end
end
# Finds the requested page and returns a 404 error if it doesn't exist
def find_existing_page
@page = @wiki.find_page(wiki_page_title.presence || params[:id])
@@ -1,96 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# 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 WorkPackages::ExportsController < ApplicationController
self._model_object = WorkPackages::Export
before_action :find_model_object,
:authorize_current_user
before_action :find_job_status, only: :status
def show
if @export.ready?
redirect_to attachment_content_path
else
headers['Link'] = "<#{status_work_packages_export_path(@export.id, format: :json)}> rel=\"status\""
head 202
end
end
def status
if @job_status.success?
headers['Link'] = "<#{attachment_content_path}> rel=\"download\""
end
respond_to do |format|
format.json do
render json: status_json_body(@job_status),
status: 200
end
end
end
private
def status_json_body(job)
body = { status: job_status(job), message: job.message }
if job.success?
body[:link] = attachment_content_path
end
body
end
def job_status(job)
if job.success?
'Completed'
elsif job.failure? || job.error?
'Error'
else
'Processing'
end
end
def authorize_current_user
deny_access(not_found: true) unless @export.visible?(current_user)
end
def attachment_content_path
# Not including the API PathHelper here as it messes up error rendering probably due to it
# including the url helper again.
::API::V3::Utilities::PathHelper::ApiV3Path.attachment_content(@export.attachments.first.id)
end
def find_job_status
@job_status = Delayed::Job::Status.of_reference(@export).first
end
end
@@ -100,7 +100,7 @@ class WorkPackages::MovesController < ApplicationController
def dependent_error_ids(parent_id, service_call)
ids = service_call
.results_with_errors(include_self: false)
.map { |result| result.context[:copied_from]&.id }
.map { |result| result.state.copied_from_work_package_id }
.compact
if ids.present?
+6 -2
View File
@@ -83,12 +83,16 @@ class WorkPackagesController < ApplicationController
protected
def export_list(mime_type)
export_storage = WorkPackages::Exports::ScheduleService
job_id = WorkPackages::Exports::ScheduleService
.new(user: current_user)
.call(query: @query, mime_type: mime_type, params: params)
.result
redirect_to work_packages_export_path(export_storage.id)
if request.headers['Accept']&.include?('application/json')
render json: { job_id: job_id }
else
redirect_to job_status_path(job_id)
end
end
def export_single(mime_type)
+2 -2
View File
@@ -366,9 +366,9 @@ module ApplicationHelper
end
def check_all_links(form_name)
link_to_function(t(:button_check_all), "checkAll('#{form_name}', true)") +
link_to_function(t(:button_check_all), "OpenProject.helpers.checkAll('#{form_name}', true)") +
' | ' +
link_to_function(t(:button_uncheck_all), "checkAll('#{form_name}', false)")
link_to_function(t(:button_uncheck_all), "OpenProject.helpers.checkAll('#{form_name}', false)")
end
def current_layout
+5 -5
View File
@@ -92,12 +92,12 @@ module CustomFieldsHelper
tag = case field_format.try(:edit_as)
when 'date'
styled_text_field_tag(field_name, custom_value.value, id: field_id, class: '-augmented-datepicker', size: 10, container_class: '-slim')
styled_text_field_tag(field_name, custom_value.value, id: field_id, class: '-augmented-datepicker', size: 10, container_class: '-slim', required: custom_field.is_required)
when 'text'
styled_text_area_tag(field_name, custom_value.value, id: field_id, rows: 3, container_class: '-middle')
styled_text_area_tag(field_name, custom_value.value, id: field_id, rows: 3, container_class: '-middle', required: custom_field.is_required)
when 'bool'
hidden_tag = hidden_field_tag(field_name, '0')
checkbox_tag = styled_check_box_tag(field_name, '1', custom_value.typed_value, id: field_id)
checkbox_tag = styled_check_box_tag(field_name, '1', custom_value.typed_value, id: field_id, required: custom_field.is_required)
hidden_tag + checkbox_tag
when 'list'
blank_option = if custom_field.is_required? && custom_field.default_value.blank?
@@ -110,9 +110,9 @@ module CustomFieldsHelper
options = blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value)
styled_select_tag(field_name, options, id: field_id, container_class: '-middle')
styled_select_tag(field_name, options, id: field_id, container_class: '-middle', required: custom_field.is_required)
else
styled_text_field_tag(field_name, custom_value.value, id: field_id, container_class: '-middle')
styled_text_field_tag(field_name, custom_value.value, id: field_id, container_class: '-middle', required: custom_field.is_required)
end
tag = content_tag :span, tag, lang: custom_field.name_locale, class: 'form--field-container'
+1 -1
View File
@@ -63,7 +63,7 @@ module PasswordHelper
end
end
def render_password_complexity_tooltip
def render_password_complexity_hint
rules = password_rules_description
s = OpenProject::Passwords::Evaluator.min_length_description
+15
View File
@@ -45,6 +45,7 @@ module ProjectsHelper
def whitelisted_project_filter?(filter)
whitelist = [
Queries::Projects::Filters::ActiveFilter,
Queries::Projects::Filters::TemplatedFilter,
Queries::Projects::Filters::ProjectStatusFilter,
Queries::Projects::Filters::CreatedAtFilter,
Queries::Projects::Filters::LatestActivityAtFilter,
@@ -64,6 +65,14 @@ module ProjectsHelper
end
end
def project_custom_fields_for_index
@project_custom_fields_for_index ||= if EnterpriseToken.allows_to?(:custom_fields_in_projects_list)
ProjectCustomField.visible(User.current).order(:position)
else
ProjectCustomField.none
end
end
def project_more_menu_items(project)
[project_more_menu_subproject_item(project),
project_more_menu_settings_item(project),
@@ -145,6 +154,12 @@ module ProjectsHelper
end
end
def project_options_for_templated
::Projects::InstantiateTemplateContract
.visible_templates(current_user)
.pluck(:name, :id)
end
def shorten_text(text, length)
text.to_s.gsub(/\A(.{#{length}[^\n\r]*).*\z/m, '\1...').strip
end
+1 -1
View File
@@ -33,7 +33,7 @@ module SecurityBadgeHelper
info = {
uuid: Setting.installation_uuid,
type: OpenProject::Configuration[:installation_type],
version: OpenProject::VERSION.to_s,
version: OpenProject::VERSION.to_semver,
db: ActiveRecord::Base.connection.adapter_name.downcase,
lang: User.current.try(:language),
ee: EnterpriseToken.current.present?,
-1
View File
@@ -31,7 +31,6 @@
module TextFormattingHelper
extend Forwardable
def_delegators :current_formatting_helper,
:text_formatting_js_includes,
:wikitoolbar_for
def preview_context(object, project = nil)
+1
View File
@@ -160,6 +160,7 @@ class UserMailer < BaseMailer
def user_signed_up(token)
return unless token.user
@user = token.user
@token = token
@activation_url = url_for(controller: '/account',
action: :activate,
@@ -293,7 +293,7 @@ class Activities::BaseActivityProvider
end
def activity_journals_table
@activity_journals_table ||= JournalManager.journal_class(activitied_type).arel_table
@activity_journals_table ||= activitied_type.journal_class.arel_table
end
def activity_journal_projection_statement(column, name)

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