Merge remote-tracking branch 'origin/dev' into maintenance/ruby-2.7.1-upgrade
@@ -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' }}
|
||||
@@ -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
|
||||
|
||||
@@ -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/**/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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.
|
||||
|
Before Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 187 KiB |
@@ -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));
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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 );
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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'});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
@import fonts/lato
|
||||
@import fonts/openproject_icon_font
|
||||
@@ -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
|
||||
@@ -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 +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> </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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Vendor definitions from plugins/gems
|
||||
|
||||
@import vendor/dragula
|
||||
@import vendor/jquery_ui
|
||||
@import vendor/enjoyhint
|
||||
@@ -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";
|
||||
@@ -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
|
||||
@@ -28,7 +28,7 @@
|
||||
# See docs/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class EmptyContract
|
||||
class EmptyContract < ModelContract
|
||||
def initialize(*); end
|
||||
|
||||
def validate
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||