diff --git a/.codeclimate.yml b/.codeclimate.yml index f114b0b5a9..f00891fd1e 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,8 +1,30 @@ --- +version: "2" languages: Ruby: true JavaScript: true -engines: +checks: + file-lines: + enabled: false + method-count: + enabled: false + method-lines: + enabled: false + method-complexity: + enabled: true + config: + threshold: 10 + complex-logic: + enabled: true + config: + threshold: 10 + # Similar codes trips up at the long TS/angular injection + # constructors all the time, which are similar by nature. + similar-code: + enabled: false + identical-code: + enabled: true +plugins: rubocop: enabled: true brakeman: @@ -35,6 +57,8 @@ exclude_paths: - .bundle/**/* - lib/plugins/rfpdf/**/* - frontend/vendor/**/* + - "**/*.d.ts" + - "**/node_modules/" - spec/** - spec_legacy/**/* - lib/redcloth3.rb diff --git a/Gemfile b/Gemfile index f8868a5dd5..3d954896bc 100644 --- a/Gemfile +++ b/Gemfile @@ -153,9 +153,6 @@ gem 'cocaine', '~> 0.5.8' # also, better than thin since we can control worker concurrency. gem 'unicorn' -# Puma server for development and on paas. -gem 'puma', '~> 3.11.3' - gem 'nokogiri', '~> 1.8.2' # carrierwave 0.11.3 should allow to use fog-aws without the rest of the @@ -237,6 +234,7 @@ group :development do end group :development, :test do + gem 'puma', '~> 3.11.3' gem 'pry-rails', '~> 0.3.6' gem 'pry-stack_explorer', '~> 0.4.9.2' @@ -267,6 +265,7 @@ group :opf_plugins do end group :docker, optional: true do + gem 'passenger', '~> 5.2' # Used to easily precompile assets gem 'sqlite3', require: false gem 'rails_12factor', require: !!ENV['HEROKU'] diff --git a/Gemfile.lock b/Gemfile.lock index be1a3fb778..ec50ee9dee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -371,6 +371,9 @@ GEM parallel parser (2.4.0.0) ast (~> 2.2) + passenger (5.2.1) + rack + rake (>= 0.8.1) pdf-core (0.7.0) pg (0.21.0) plaintext (0.1.0) @@ -661,6 +664,7 @@ DEPENDENCIES openproject-token (~> 1.0.1) openproject-translations! parallel_tests (~> 2.14.0) + passenger (~> 5.2) pg (~> 0.21.0) plaintext (= 0.1.0) prawn (~> 2.2) diff --git a/app/models/queries/work_packages.rb b/app/models/queries/work_packages.rb index 8331b80b72..09f558c55e 100644 --- a/app/models/queries/work_packages.rb +++ b/app/models/queries/work_packages.rb @@ -57,6 +57,7 @@ module Queries::WorkPackages register.filter Query, filters_module::UpdatedAtFilter register.filter Query, filters_module::VersionFilter register.filter Query, filters_module::WatcherFilter + register.filter Query, filters_module::ParentFilter columns_module = Queries::WorkPackages::Columns diff --git a/app/models/queries/work_packages/filter/filter_for_wp_mixing.rb b/app/models/queries/work_packages/filter/filter_for_wp_mixing.rb new file mode 100644 index 0000000000..3f4fbf5075 --- /dev/null +++ b/app/models/queries/work_packages/filter/filter_for_wp_mixing.rb @@ -0,0 +1,75 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module Queries::WorkPackages::Filter::FilterForWpMixin + def type + :list + end + + def allowed_values + raise NotImplementedError, 'There would be too many candidates' + end + + def value_objects + scope.find(values) + end + + def allowed_objects + raise NotImplementedError, 'There would be too many candidates' + end + + def available? + scope.exists? + end + + def ar_object_filter? + true + end + + def allowed_values_subset + scope.where(id: values).pluck(:id).map(&:to_s) + end + + private + + def scope + if context.project + WorkPackage + .visible + .for_projects(context.project.self_and_descendants) + else + WorkPackage.visible + end + end + + def type_strategy + @type_strategy ||= Queries::Filters::Strategies::HugeList.new(self) + end +end diff --git a/app/models/queries/work_packages/filter/id_filter.rb b/app/models/queries/work_packages/filter/id_filter.rb index f9b163ef96..8337525391 100644 --- a/app/models/queries/work_packages/filter/id_filter.rb +++ b/app/models/queries/work_packages/filter/id_filter.rb @@ -28,48 +28,10 @@ # See docs/COPYRIGHT.rdoc for more details. #++ -class Queries::WorkPackages::Filter::IdFilter < Queries::WorkPackages::Filter::WorkPackageFilter - def type - :list - end +require_relative './filter_for_wp_mixing' - def allowed_values - raise NotImplementedError, 'There would be too many candidates' - end +class Queries::WorkPackages::Filter::IdFilter < + Queries::WorkPackages::Filter::WorkPackageFilter - def value_objects - raise NotImplementedError, 'There would be too many candidates' - end - - def allowed_objects - raise NotImplementedError, 'There would be too many candidates' - end - - def available? - scope.exists? - end - - def ar_object_filter? - true - end - - def allowed_values_subset - scope.where(id: values).pluck(:id).map(&:to_s) - end - - private - - def scope - if context.project - WorkPackage - .visible - .for_projects(context.project.self_and_descendants) - else - WorkPackage.visible - end - end - - def type_strategy - @type_strategy ||= Queries::Filters::Strategies::HugeList.new(self) - end + include ::Queries::WorkPackages::Filter::FilterForWpMixin end diff --git a/app/models/queries/work_packages/filter/parent_filter.rb b/app/models/queries/work_packages/filter/parent_filter.rb new file mode 100644 index 0000000000..47c1f6d4b8 --- /dev/null +++ b/app/models/queries/work_packages/filter/parent_filter.rb @@ -0,0 +1,47 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require_relative './filter_for_wp_mixing' + +class Queries::WorkPackages::Filter::ParentFilter < + Queries::WorkPackages::Filter::WorkPackageFilter + + include ::Queries::WorkPackages::Filter::FilterForWpMixin + + def includes + :parent_relation + end + + def where + operator_strategy.sql_for_field(values, + Relation.table_name, + 'from_id') + end +end diff --git a/app/models/queries/work_packages/filter/type_filter.rb b/app/models/queries/work_packages/filter/type_filter.rb index fba3785f64..a64b914812 100644 --- a/app/models/queries/work_packages/filter/type_filter.rb +++ b/app/models/queries/work_packages/filter/type_filter.rb @@ -1,4 +1,5 @@ #-- encoding: UTF-8 + #-- copyright # OpenProject is a project management system. # Copyright (C) 2012-2018 the OpenProject Foundation (OPF) diff --git a/app/models/work_package.rb b/app/models/work_package.rb index ea178b8d39..7b9a62d33c 100644 --- a/app/models/work_package.rb +++ b/app/models/work_package.rb @@ -299,6 +299,12 @@ class WorkPackage < ActiveRecord::Base project && type ? (project.all_work_package_custom_fields & type.custom_fields) : [] end + # aliasing subject to name + # using :alias is not possible as AR will add the subject method later + def name + subject + end + def status_id=(sid) self.status = nil write_attribute(:status_id, sid) diff --git a/config/environments/production.rb b/config/environments/production.rb index 1679d5574f..5a3a8c8ef8 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -52,15 +52,7 @@ OpenProject::Application.configure do # config.action_dispatch.rack_cache = true # Enable Rails's static asset server when requested - if OpenProject::Configuration.enable_internal_assets_server? - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Access-Control-Allow-Origin' => '*', - 'Access-Control-Allow-Methods' => 'GET, OPTIONS, HEAD', - 'Cache-Control' => 'public, s-maxage=31536000, max-age=15552000', - 'Expires' => "#{1.year.from_now.to_formatted_s(:rfc822)}" - } - end + config.public_file_server.enabled = false # Compress JavaScripts and CSS. config.assets.js_compressor = nil diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index 4a75073896..813996b964 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -2225,7 +2225,7 @@ hu: text_journal_aggregation_time_explanation: Fűzze össze a tartalmat az értesítésekben, amennyiben az idő kisebb, mint a megadott érték. Ez ugyanúgy késleltetheti az e-mail kiküldését. - text_journal_changed: "%{label} changed from %{old}
to %{new}" + text_journal_changed: "%{label} megváltoztatva %{old}
%{new}" text_journal_changed_no_detail: "%{label} frissítve" text_journal_changed_with_diff: "%{label} megváltozott (%{link})" text_journal_deleted: "%{label} törölve (%{old})" diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml index 5e3187bb7d..dd852f8948 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -6,10 +6,10 @@ lt: loading: Įkeliama ... autocomplete_select: placeholder: - multi: Add "%{name}" - single: Select "%{name}" - remove: Remove %{name} - active: Active %{label} %{name} + multi: Pridėti „%{name}“ + single: Pasirinkti „%{name}“ + remove: Pašalinti %{name} + active: Aktyvus %{label} %{name} close_popup_title: Užverti iškylantį langą close_filter_title: Užverti filtrą close_form_title: Užverti formą @@ -71,8 +71,8 @@ lt: one: Pirmasis rikiavimo kriterijus two: Antrasis rikiavimo kriterijus three: Trečiasis rikiavimo kriterijus - upsale_for_more: For more advanced filters, check out the - upsale_link: Enterprise Edition. + upsale_for_more: Norėdami naudoti daugiau papildomų filtrų, patikrinkite + upsale_link: "„Enterprise Edition“." general_text_no: ne general_text_yes: taip general_text_No: Ne diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index c35d65583e..c4c191c77a 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -61,16 +61,16 @@ lt: custom_actions: actions: name: Veiksmai - add: Add action - conditions: Conditions - plural: Custom actions - new: New custom action - edit: Edit custom action %{name} - execute: Execute %{name} + add: Pridėti veiksmą + conditions: Sąlygos + plural: Pasirinktiniai veiksmai + new: Naujas pasirinktinis veiksmas + edit: Redaguoti pasirinktinį veiksmą %{name} + execute: Vykdyti %{name} upsale: - title: Custom actions is an Enterprise Edition feature - description: Custom actions streamline everyday work by combining a set of individual - steps into one button. + title: Pasirinktiniai veiksmai yra „Enterprise Edition“ funkcija + description: Pasirinktiniai veiksmai supaprastina kasdienį darbą suderindami + atskirus žingsnius į vieną mygtuką. custom_fields: text_add_new_custom_field: 'Norėdami pridėti naujus pritaikytus laukelius prie projekto, pirmiausia reikia juos sukurti. Tik tada yra galimybė juos pridėti @@ -271,8 +271,8 @@ lt: announcements: show_until: Rodyti iki attachment: - attachment_content: Attachment content - attachment_file_name: Attachment file name + attachment_content: Prisegto failo turinys + attachment_file_name: Prisegto failo vardas downloads: Atsisiuntimai file: Failas filename: Failas @@ -453,12 +453,12 @@ lt: custom_field: at_least_one_custom_option: Turi būti prieinama bent viena savybė. custom_actions: - only_one_allowed: "(%{name}) only one value is allowed." - empty: "(%{name}) value can't be empty." - inclusion: "(%{name}) value is not set to one of the allowed values." - not_an_integer: "(%{name}) is not an integer." - smaller_than_or_equal_to: "(%{name}) must be smaller than or equal to %{count}." - greater_than_or_equal_to: "(%{name}) must be greater than or equal to %{count}." + only_one_allowed: leidžiama (%{name}) tik viena reikšmė. + empty: "(%{name}) reikšmė negali būti tuščia." + inclusion: "(%{name}) nenustatyta kaip viena iš leidžiamų reikšmių." + not_an_integer: "(%{name}) nėra sveikasis skaičius." + smaller_than_or_equal_to: "(%{name}) turi būti mažesnis arba lygus %{count}." + greater_than_or_equal_to: "(%{name}) turi būti didesnis arba lygus %{count}." enterprise_token: unreadable: neįmanoma perskaityti. Ar tai tikrai yra paramos raktas? parse_schema_filter_params_service: @@ -571,7 +571,7 @@ lt: attribute_help_text: Atributo pagalbos tekstas board: Forumas comment: Komentaras - custom_action: Custom action + custom_action: Pasirinktinis veiksmas custom_field: Kliento laukelis group: Grupė category: Kategorija @@ -1026,12 +1026,12 @@ lt: pdf_with_descriptions: PDF su aprašymu extraction: available: - pdftotext: Pdftotext available (optional) - unrtf: Unrtf available (optional) - catdoc: Catdoc available (optional) - xls2csv: Xls2csv available (optional) - catppt: Catppt available (optional) - tesseract: Tesseract available (optional) + pdftotext: Pdf-į-tekstą pasiekiamas (pasirinktinai) + unrtf: Unrtf pasiekiamas (pasirinktinai) + catdoc: Catdoc pasiekiamas (pasirinktinai) + xls2csv: Xls2csv pasiekiamas (pasirinktinai) + catppt: Catppt pasiekiamas (pasirinktinai) + tesseract: Tesseract pasiekiamas (pasirinktinai) general_csv_decimal_separator: "." general_csv_encoding: UTF-8 general_csv_separator: "," @@ -2107,7 +2107,7 @@ lt: setting_file_max_size_displayed: Maksimalus tekstinių failų dydis rodomas vienoje eilutėje setting_host_name: Šeimininko vardas - setting_invitation_expiration_days: Activation E-Mail expires after + setting_invitation_expiration_days: Aktyvavimo laiškas baigia galioti po setting_work_package_done_ratio: Skaičiuoti darbų paketo atlikimo santykį su setting_work_package_done_ratio_field: Naudoti darbų paketo laukelį setting_work_package_done_ratio_status: Naudoti darbų paketo būseną @@ -2171,7 +2171,7 @@ lt: setting_accessibility_mode_for_anonymous: Leisti lengvos prieigos režimą anoniminiams vartotojams setting_user_format: Vartotojo atvaizdavimo formatas - setting_user_default_timezone: Users default time zone + setting_user_default_timezone: Numatytoji vartotojo laiko juosta setting_users_deletable_by_admins: Administratoriai gali ištrinti vartotojų paskyras setting_users_deletable_by_self: Vartotojai gali ištrinti savo paskyras setting_welcome_text: Pasisveikinimo bloko tekstas @@ -2243,7 +2243,7 @@ lt: naudojant iOS. ' - text_database_allows_tsv: Database allows TSVector (optional) + text_database_allows_tsv: Duomenų bazė leidžia TSVector (pasirinktinai) text_default_administrator_account_changed: Administratoriaus numatytoji paskyra pakeista text_default_encoding: 'Numatytasis: UTF-8' @@ -2281,7 +2281,7 @@ lt: text_journal_aggregation_time_explanation: Kombinuoti žurnalus rodymui, ar jų amžius skiriasi mažiau nei nustatyta trukmė. Tai taip pat vėlins įspėjimo el. laiškų atsiuntimą tokia pat laiko trukme. - text_journal_changed: "%{label} changed from %{old}
to %{new}" + text_journal_changed: "%{label} pakeista iš %{old}
į %{new}" text_journal_changed_no_detail: "%{label} atnaujintas" text_journal_changed_with_diff: "%{label} pasikeitė (%{link})" text_journal_deleted: "%{label} ištrintas (%{old})" @@ -2543,8 +2543,8 @@ lt: years: Metai(-ų) title_remove_and_delete_user: Pašalinti pakviestą vartotoją iš projekto ir ištrinti jį/ją. - tooltip_user_default_timezone: 'The default time zone for new users. Can be changed - in a user''s settings. + tooltip_user_default_timezone: 'Numatytoji laiko juosta naujiems vartotojams. Gali + būti pakeista vartotojo nustatymuose. ' tooltip_resend_invitation: 'Siunčia naują pakvietimo laišką su nauju atpažinimo @@ -2620,9 +2620,11 @@ lt: warning: Įspėjimas warning_attachments_not_saved: "%{count} failas (-ai, -ų) negali būti išsaugotas (-i)." - warning_registration_token_expired: | - The activation email has expired. We sent you a new one to %{email}. - Please click the link inside of it to activate your account. + warning_registration_token_expired: 'Aktyvavimo el. laiško galiojimas pasibaigė. + Išsiuntėme jums naują %{email}. Paspauskite laiške duotą nuorodą, kad aktyvuotumėte + savo paskyrą. + +' menu_item: Meniu punktas menu_item_setting: Matomumas wiki_menu_item_for: Meniu punktas wiki puslapiui „%{title}“ diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 4c537ea085..2525565721 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -2279,7 +2279,7 @@ ru: text_journal_aggregation_time_explanation: Объединить журналы для отображения, если разница в их возрасте меньше заданного значения. Это приведет к задержке почтовых уведомлений на указанное время. - text_journal_changed: "%{label} changed from %{old}
to %{new}" + text_journal_changed: "%{label} изменено с %{old}
на %{new}" text_journal_changed_no_detail: "%{label} обновлено" text_journal_changed_with_diff: "%{label} изменена (%{link})" text_journal_deleted: "%{label} удалена (%{old})" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000000..503121e660 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,143 @@ +#!/usr/bin/env puma + +# Load "path" as a rackup file. +# +# The default is "config.ru". +# +# rackup '/u/apps/lolcat/config.ru' + +# Set the port on which puma binds +port ENV['PORT'] || 3000 + +# Set the environment in which the rack's app will run. The value must be a string. +# +# The default is "development". +# +# environment 'production' +environment ENV['RAILS_ENV'] || 'development' + +# Disable request logging. +# +# The default is "false". +# +# quiet + +# Configure "min" to be the minimum number of threads to use to answer +# requests and "max" the maximum. +# +# The default is "0, 16". +# +threads 1, 1 + +# Code to run before doing a restart. This code should +# close log files, database connections, etc. +# +# This can be called multiple times to add code each time. +# +# on_restart do +# puts 'On restart...' +# end + + +# Command to use to restart puma. This should be just how to +# load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments +# to puma, as those are the same as the original process. +# +# restart_command '/u/app/lolcat/bin/restart_puma' + +# === Cluster mode === + +# How many worker processes to run. Typically this is set to +# to the number of available cores. +# +# The default is "0". +# +workers ENV['WEB_CONCURRENCY'] || 4 + +# Code to run immediately before the master starts workers. +# +# before_fork do +# puts "Starting workers..." +# end + +# Code to run in a worker before it starts serving requests. +# +# This is called everytime a worker is to be started. +# +on_worker_boot do + ActiveRecord::Base.establish_connection +end + +# Code to run in a worker right before it exits. +# +# This is called everytime a worker is to about to shutdown. +# +# on_worker_shutdown do +# puts 'On worker shutdown...' +# end + +# Code to run in the master right before a worker is started. The worker's +# index is passed as an argument. +# +# This is called everytime a worker is to be started. +# +# on_worker_fork do +# puts 'Before worker fork...' +# end + +# Code to run in the master after a worker has been started. The worker's +# index is passed as an argument. +# +# This is called everytime a worker is to be started. +# +# after_worker_fork do +# puts 'After worker fork...' +# end + +# Allow workers to reload bundler context when master process is issued +# a USR1 signal. This allows proper reloading of gems while the master +# is preserved across a phased-restart. (incompatible with preload_app) +# (off by default) + +# prune_bundler + +# Preload the application before starting the workers; this conflicts with +# phased restart feature. (off by default) + +preload_app! if ENV['RAILS_ENV'] == 'production' + +# Additional text to display in process listing +# +# tag 'app name' +# +# If you do not specify a tag, Puma will infer it. If you do not want Puma +# to add a tag, use an empty string. + +# Verifies that all workers have checked in to the master process within +# the given timeout. If not the worker process will be restarted. This is +# not a request timeout, it is to protect against a hung or dead process. +# Setting this value will not protect against slow requests. +# Default value is 60 seconds. +# +# worker_timeout 60 + +# Change the default worker timeout for booting +# +# If unspecified, this defaults to the value of worker_timeout. +# +# worker_boot_timeout 60 + +# === Puma control rack application === + +# Start the puma control rack application on "url". This application can +# be communicated with to control the main server. Additionally, you can +# provide an authentication token, so all requests to the control server +# will need to include that token as a query parameter. This allows for +# simple authentication. +# +# Check out https://github.com/puma/puma/blob/master/lib/puma/app/status.rb +# to see what the app has available. +# +# activate_control_app 'unix:///var/run/pumactl.sock' +# activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' } +# activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true } diff --git a/frontend/app/components/wp-fast-table/wp-table-filters.ts b/frontend/app/components/wp-fast-table/wp-table-filters.ts index 88b69d7f6c..d457f9df37 100644 --- a/frontend/app/components/wp-fast-table/wp-table-filters.ts +++ b/frontend/app/components/wp-fast-table/wp-table-filters.ts @@ -78,8 +78,8 @@ export class WorkPackageTableFilters extends WorkPackageTableBaseState (schema.filter.allowedValues as QueryFilterResource[])[0]); - // We do not use the id filter as of now as we do not have adequate + // We do not use the filters id and parent as of now as we do not have adequate // means to select the values. - return _.filter(availableFilters, filter => filter.id !== 'id'); + return _.filter(availableFilters, filter => filter.id !== 'id' && filter.id !== 'parent'); } } diff --git a/lib/api/v3/queries/schemas/by_work_package_filter_dependency_representer.rb b/lib/api/v3/queries/schemas/by_work_package_filter_dependency_representer.rb new file mode 100644 index 0000000000..910f12e8a2 --- /dev/null +++ b/lib/api/v3/queries/schemas/by_work_package_filter_dependency_representer.rb @@ -0,0 +1,59 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module Queries + module Schemas + class ByWorkPackageFilterDependencyRepresenter < + FilterDependencyRepresenter + + def json_cache_key + super + (filter.project.present? ? [filter.project.id] : []) + end + + def href_callback + if filter.project + api_v3_paths.work_packages_by_project(filter.project.id) + else + api_v3_paths.work_packages + end + end + + private + + def type + '[]WorkPackage' + end + end + end + end + end +end diff --git a/lib/api/v3/queries/schemas/id_filter_dependency_representer.rb b/lib/api/v3/queries/schemas/id_filter_dependency_representer.rb index 7ca259f011..6e015550cc 100644 --- a/lib/api/v3/queries/schemas/id_filter_dependency_representer.rb +++ b/lib/api/v3/queries/schemas/id_filter_dependency_representer.rb @@ -32,27 +32,7 @@ module API module V3 module Queries module Schemas - class IdFilterDependencyRepresenter < - FilterDependencyRepresenter - - def json_cache_key - super + (filter.project.present? ? [filter.project.id] : []) - end - - def href_callback - if filter.project - api_v3_paths.work_packages_by_project(filter.project.id) - else - api_v3_paths.work_packages - end - end - - private - - def type - '[]WorkPackage' - end - end + class IdFilterDependencyRepresenter < ByWorkPackageFilterDependencyRepresenter; end end end end diff --git a/lib/api/v3/queries/schemas/parent_filter_dependency_representer.rb b/lib/api/v3/queries/schemas/parent_filter_dependency_representer.rb new file mode 100644 index 0000000000..dd3109decb --- /dev/null +++ b/lib/api/v3/queries/schemas/parent_filter_dependency_representer.rb @@ -0,0 +1,39 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module Queries + module Schemas + class ParentFilterDependencyRepresenter < ByWorkPackageFilterDependencyRepresenter; end + end + end + end +end diff --git a/lib/open_project/configuration.rb b/lib/open_project/configuration.rb index cfa524842b..6f3d4b5c48 100644 --- a/lib/open_project/configuration.rb +++ b/lib/open_project/configuration.rb @@ -65,8 +65,6 @@ module OpenProject 'rails_relative_url_root' => '', 'rails_force_ssl' => false, 'rails_asset_host' => nil, - # Enable internal asset server - 'enable_internal_assets_server' => false, # user configuration 'default_comment_sort_order' => 'asc', diff --git a/spec/lib/api/v3/queries/schemas/parent_filter_dependency_representer_spec.rb b/spec/lib/api/v3/queries/schemas/parent_filter_dependency_representer_spec.rb new file mode 100644 index 0000000000..a93ee60c17 --- /dev/null +++ b/spec/lib/api/v3/queries/schemas/parent_filter_dependency_representer_spec.rb @@ -0,0 +1,133 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' + +describe ::API::V3::Queries::Schemas::ParentFilterDependencyRepresenter, clear_cache: true do + include ::API::V3::Utilities::PathHelper + + let(:project) { FactoryGirl.build_stubbed(:project) } + let(:query) { FactoryGirl.build_stubbed(:query, project: project) } + let(:filter) { Queries::WorkPackages::Filter::ParentFilter.create!(context: query) } + let(:form_embedded) { false } + + let(:instance) do + described_class.new(filter, + operator, + form_embedded: form_embedded) + end + + subject(:generated) { instance.to_json } + + context 'generation' do + context 'properties' do + describe 'values' do + context 'within project' do + let(:path) { 'values' } + let(:type) { '[]WorkPackage' } + let(:href) { api_v3_paths.work_packages_by_project(project.id) } + + context "for operator 'Queries::Operators::Equals'" do + let(:operator) { Queries::Operators::Equals } + + it_behaves_like 'filter dependency with allowed link' + end + + context "for operator 'Queries::Operators::NotEquals'" do + let(:operator) { Queries::Operators::NotEquals } + + it_behaves_like 'filter dependency with allowed link' + end + end + + context 'outside of a project' do + let(:project) { nil } + let(:path) { 'values' } + let(:type) { '[]WorkPackage' } + let(:href) { api_v3_paths.work_packages } + + context "for operator 'Queries::Operators::Equals'" do + let(:operator) { Queries::Operators::Equals } + + it_behaves_like 'filter dependency with allowed link' + end + + context "for operator 'Queries::Operators::NotEquals'" do + let(:operator) { Queries::Operators::NotEquals } + + it_behaves_like 'filter dependency with allowed link' + end + end + end + end + + describe 'caching' do + let(:operator) { Queries::Operators::Equals } + let(:other_project) { FactoryGirl.build_stubbed(:project) } + + before do + # fill the cache + instance.to_json + end + + it 'is cached' do + expect(instance) + .not_to receive(:to_hash) + + instance.to_json + end + + it 'busts the cache on a different operator' do + instance.send(:operator=, Queries::Operators::NotEquals) + + expect(instance) + .to receive(:to_hash) + + instance.to_json + end + + it 'busts the cache on a different project' do + query.project = other_project + + expect(instance) + .to receive(:to_hash) + + instance.to_json + end + + it 'busts the cache on changes to the locale' do + expect(instance) + .to receive(:to_hash) + + I18n.with_locale(:de) do + instance.to_json + end + end + end + end +end diff --git a/spec/lib/open_project/configuration_spec.rb b/spec/lib/open_project/configuration_spec.rb index 2208650d4b..2e9aedd3e9 100644 --- a/spec/lib/open_project/configuration_spec.rb +++ b/spec/lib/open_project/configuration_spec.rb @@ -243,6 +243,8 @@ describe OpenProject::Configuration do Setting[:email_delivery_method] = nil # reload configuration to isolate specs OpenProject::Configuration.load + # clear settings cache to isolate specs + Setting.clear_cache end it 'does nothing if no legacy configuration given' do @@ -292,6 +294,8 @@ describe OpenProject::Configuration do after do # reload configuration to isolate specs OpenProject::Configuration.load + # clear settings cache to isolate specs + Setting.clear_cache end it 'uses the legacy method to configure email settings' do diff --git a/spec/models/queries/work_packages/filter/id_filter_spec.rb b/spec/models/queries/work_packages/filter/id_filter_spec.rb index 4afbcbf374..322c051082 100644 --- a/spec/models/queries/work_packages/filter/id_filter_spec.rb +++ b/spec/models/queries/work_packages/filter/id_filter_spec.rb @@ -57,7 +57,7 @@ describe Queries::WorkPackages::Filter::IdFilter, type: :model do expect(instance).to be_available end - it 'is fals if no work package exists/ is visible' do + it 'is false if no work package exists/ is visible' do allow(WorkPackage) .to receive_message_chain(:visible, :for_projects, :exists?) .with(no_args) @@ -105,8 +105,18 @@ describe Queries::WorkPackages::Filter::IdFilter, type: :model do end describe '#value_object' do - it 'raises an error' do - expect { instance.value_objects }.to raise_error NotImplementedError + let(:visible_wp) { FactoryGirl.build_stubbed(:work_package) } + + it 'returns the work package for the values' do + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :find) + .with(no_args) + .with(project) + .with(instance.values) + .and_return([visible_wp]) + + expect(instance.value_objects) + .to match_array [visible_wp] end end diff --git a/spec/models/queries/work_packages/filter/parent_filter_spec.rb b/spec/models/queries/work_packages/filter/parent_filter_spec.rb new file mode 100644 index 0000000000..b3beb2c480 --- /dev/null +++ b/spec/models/queries/work_packages/filter/parent_filter_spec.rb @@ -0,0 +1,259 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' + +describe Queries::WorkPackages::Filter::ParentFilter, type: :model do + let(:project) { FactoryGirl.build_stubbed(:project) } + let(:query) do + FactoryGirl.build_stubbed(:query, project: project) + end + + it_behaves_like 'basic query filter' do + let(:class_key) { :parent } + let(:type) { :list } + + before do + instance.context = query + end + + describe '#available?' do + context 'within a project' do + it 'is true if any work package exists and is visible' do + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :exists?) + .with(no_args) + .with(project) + .with(no_args) + .and_return true + + expect(instance).to be_available + end + + it 'is false if no work package exists/ is visible' do + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :exists?) + .with(no_args) + .with(project) + .with(no_args) + .and_return false + + expect(instance).not_to be_available + end + end + + context 'outside of a project' do + let(:project) { nil } + + it 'is true if any work package exists and is visible' do + allow(WorkPackage) + .to receive_message_chain(:visible, :exists?) + .with(no_args) + .and_return true + + expect(instance).to be_available + end + + it 'is false if no work package exists/ is visible' do + allow(WorkPackage) + .to receive_message_chain(:visible, :exists?) + .with(no_args) + .and_return false + + expect(instance).not_to be_available + end + end + end + + describe '#ar_object_filter?' do + it 'is true' do + expect(instance).to be_ar_object_filter + end + end + + describe '#allowed_values' do + it 'raises an error' do + expect { instance.allowed_values }.to raise_error NotImplementedError + end + end + + describe '#value_object' do + let(:visible_wp) { FactoryGirl.build_stubbed(:work_package) } + + it 'returns the work package for the values' do + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :find) + .with(no_args) + .with(project) + .with(instance.values) + .and_return([visible_wp]) + + expect(instance.value_objects) + .to match_array [visible_wp] + end + end + + describe '#allowed_objects' do + it 'raises an error' do + expect { instance.allowed_objects }.to raise_error NotImplementedError + end + end + + describe '#valid_values!' do + let(:visible_wp) { FactoryGirl.build_stubbed(:work_package) } + let(:invisible_wp) { FactoryGirl.build_stubbed(:work_package) } + + context 'within a project' do + it 'removes all non existing/non visible ids' do + instance.values = [visible_wp.id.to_s, invisible_wp.id.to_s, '999999'] + + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :where, :pluck) + .with(no_args) + .with(project) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + instance.valid_values! + + expect(instance.values) + .to match_array [visible_wp.id.to_s] + end + end + + context 'outside of a project' do + let(:project) { nil } + + it 'removes all non existing/non visible ids' do + instance.values = [visible_wp.id.to_s, invisible_wp.id.to_s, '999999'] + + allow(WorkPackage) + .to receive_message_chain(:visible, :where, :pluck) + .with(no_args) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + instance.valid_values! + + expect(instance.values) + .to match_array [visible_wp.id.to_s] + end + end + end + + describe '#validate' do + let(:visible_wp) { FactoryGirl.build_stubbed(:work_package) } + let(:invisible_wp) { FactoryGirl.build_stubbed(:work_package) } + + context 'within a project' do + it 'is valid if only visible wps are values' do + instance.values = [visible_wp.id.to_s] + + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :where, :pluck) + .with(no_args) + .with(project) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + expect(instance).to be_valid + end + + it 'is invalid if invisible wps are values' do + instance.values = [invisible_wp.id.to_s, visible_wp.id.to_s] + + allow(WorkPackage) + .to receive_message_chain(:visible, :for_projects, :where, :pluck) + .with(no_args) + .with(project) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + expect(instance).not_to be_valid + end + end + + context 'outside of a project' do + let(:project) { nil } + + it 'is valid if only visible wps are values' do + instance.values = [visible_wp.id.to_s] + + allow(WorkPackage) + .to receive_message_chain(:visible, :where, :pluck) + .with(no_args) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + expect(instance).to be_valid + end + + it 'is invalid if invisible wps are values' do + instance.values = [invisible_wp.id.to_s, visible_wp.id.to_s] + + allow(WorkPackage) + .to receive_message_chain(:visible, :where, :pluck) + .with(no_args) + .with(id: instance.values) + .with(:id) + .and_return([visible_wp.id]) + + expect(instance).not_to be_valid + end + end + end + + describe '#where and #includes' do + let(:parent) { FactoryGirl.create(:work_package) } + let(:visible_wp) { FactoryGirl.create(:work_package, parent: parent) } + + before do + visible_wp + instance.values = [parent.id.to_s] + instance.operator = '=' + end + + it 'filters' do + scope = WorkPackage + .references(instance.includes) + .includes(instance.includes) + .where(instance.where) + + expect(scope) + .to match_array [visible_wp] + end + end + end +end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index c3d6ee93e6..6c9ca7f5e5 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -41,7 +41,7 @@ Capybara.register_server :single_puma do |app, port, host| workers: 0, daemon: false, Silent: false, - Threads: "0:4", + Threads: "1:1", config_files: ['-'] end Capybara.server = :single_puma