From ef98734abde0fc6a038ad1a876ce95e0fc2c3613 Mon Sep 17 00:00:00 2001 From: Philipp Tessenow Date: Mon, 11 Nov 2013 11:36:20 +0100 Subject: [PATCH 01/40] send coverage reports to code climate when running on a CI server --- .travis.yml | 24 ++++++++++++++---------- Gemfile | 2 +- Gemfile.lock | 3 +++ spec/spec_helper.rb | 5 +++++ test/test_helper.rb | 6 ++++++ 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index da15819859..186ef801cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,17 +34,17 @@ branches: - feature/rails3 env: # mysql2 - - "TEST_SUITE=cucumber RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" - - "TEST_SUITE=test:units RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" - - "TEST_SUITE=test:functionals RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" - - "TEST_SUITE=test:integration RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" - - "TEST_SUITE=spec RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" + - "TEST_SUITE=cucumber CI=true RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" + - "TEST_SUITE=test:units CI=true RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" + - "TEST_SUITE=test:functionals CI=true RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" + - "TEST_SUITE=test:integration CI=true RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" + - "TEST_SUITE=spec CI=true RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite:development" # postgres - - "TEST_SUITE=cucumber RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" - - "TEST_SUITE=test:units RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" - - "TEST_SUITE=test:functionals RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" - - "TEST_SUITE=test:integration RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" - - "TEST_SUITE=spec RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" + - "TEST_SUITE=cucumber CI=true RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" + - "TEST_SUITE=test:units CI=true RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" + - "TEST_SUITE=test:functionals CI=true RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" + - "TEST_SUITE=test:integration CI=true RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" + - "TEST_SUITE=spec CI=true RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite:development" script: "bundle exec rake $TEST_SUITE" before_install: - "export DISPLAY=:99.0" @@ -53,3 +53,7 @@ before_script: - "RAILS_ENV=production bundle exec rake ci:travis:prepare" notifications: email: false +addons: + code_climate: + repo_token: + secure: "BIooLUZt/ZPBh0KSOMrSdsm0uwyax9veNULE20hJLXxuj8dStxAHcrZW8gZvNaU0/vgz3RWKQeNJ6xV9BXYVjnAtYIkQmI+kiryRT9PE6OsLbqeU/OirahFi8Dwm4xfh/e9DVgTOyYHHhZ69aVFQxvKbXARGmfoHcy8gFduJ7ss=" diff --git a/Gemfile b/Gemfile index dd723e9222..17c44bc11a 100644 --- a/Gemfile +++ b/Gemfile @@ -129,8 +129,8 @@ group :test do gem 'simplecov', ">= 0.8.pre" gem "shoulda-matchers" gem "json_spec" - gem "activerecord-tableless", "~> 1.0" + gem "codeclimate-test-reporter", :require => nil end group :ldap do diff --git a/Gemfile.lock b/Gemfile.lock index 04221f1103..aa24f299b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -69,6 +69,8 @@ GEM timers (~> 1.1.0) childprocess (0.3.9) ffi (~> 1.0, >= 1.0.11) + codeclimate-test-reporter (0.1.1) + simplecov (>= 0.7.1, < 1.0.0) coderay (1.0.9) coffee-rails (3.2.2) coffee-script (>= 2.2.0) @@ -355,6 +357,7 @@ DEPENDENCIES awesome_nested_set capybara capybara-screenshot + codeclimate-test-reporter coderay (~> 1.0.5) coffee-rails (~> 3.2.1) color-tools (~> 1.3.0) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e1f7690b2e..debb4efa7a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,11 @@ #++ require 'rubygems' +if ENV['CI'] == true + # we are running on a CI server, report coverage to code climate + require "codeclimate-test-reporter" + CodeClimate::TestReporter.start +end # This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' diff --git a/test/test_helper.rb b/test/test_helper.rb index aa11a7c2e1..5a357ef0e1 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -29,6 +29,12 @@ ENV["RAILS_ENV"] = "test" +if ENV['CI'] == true + # we are running on a CI server, report coverage to code climate + require "codeclimate-test-reporter" + CodeClimate::TestReporter.start +end + require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' require 'fileutils' From 83f4aabde2e8a82e2ce6302b1860b2276ce58626 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Mon, 11 Nov 2013 19:06:51 +0100 Subject: [PATCH 02/40] Added syck gem for ruby 2.0 and yaml_migrator to transform yamls between different serializers e.g. syck to psych --- Gemfile | 2 + db/migrate/migration_utils/db_worker.rb | 4 ++ db/migrate/migration_utils/yaml_migrator.rb | 47 +++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 db/migrate/migration_utils/yaml_migrator.rb diff --git a/Gemfile b/Gemfile index dd723e9222..73825e3caa 100644 --- a/Gemfile +++ b/Gemfile @@ -78,6 +78,8 @@ gem 'daemons' gem 'rack-protection' +gem 'syck', :platforms => [:ruby_20, :mingw_20], :require => false + group :production do # we use dalli as standard memcache client remove this if you don't # requires memcached 1.4+ diff --git a/db/migrate/migration_utils/db_worker.rb b/db/migrate/migration_utils/db_worker.rb index c108c1a501..273be771fa 100644 --- a/db/migrate/migration_utils/db_worker.rb +++ b/db/migrate/migration_utils/db_worker.rb @@ -41,6 +41,10 @@ module Migration ActiveRecord::Base.connection.table_exists? name end + def db_column(name) + ActiveRecord::Base.connection.quote_column_name(name) + end + def db_columns(table_name) ActiveRecord::Base.connection.columns table_name end diff --git a/db/migrate/migration_utils/yaml_migrator.rb b/db/migrate/migration_utils/yaml_migrator.rb new file mode 100644 index 0000000000..9e7b71c3c0 --- /dev/null +++ b/db/migrate/migration_utils/yaml_migrator.rb @@ -0,0 +1,47 @@ +# OpenProject is a project management system. +# +# Copyright (C) 2012-2013 the OpenProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require_relative 'db_worker' +require 'syck' + +module Migration + module YamlMigrator + extend DbWorker + + def self.migrate(table, column, source_yamler, target_yamler) + current_yamler = YAML::ENGINE.yamler + fetch_data(table,column).each do | data | + db_execute <<-SQL + UPDATE #{quoted_table_name(table)} + SET #{db_column(column)} = #{quote_value(yaml_to_yaml(data[column],source_yamler, target_yamler))} + WHERE id = #{data['id']}; + SQL + end + ensure + # psych is the default starting at ruby 1.9.3, so we explicitely set it here + # in case no yamler was set to return to a sensible default + YAML::ENGINE.yamler = current_yamler.present? ? current_yamler : 'psych' + end + + def self.fetch_data(table, column) + ActiveRecord::Base.connection.select_all <<-SQL + SELECT #{db_column('id')}, #{db_column(column)} + FROM #{quoted_table_name(table)} + SQL + end + + def self.yaml_to_yaml(data, source_yamler, target_yamler) + YAML::ENGINE.yamler = source_yamler + original = YAML.load(data) + YAML::ENGINE.yamler = target_yamler + YAML.dump original + end + end +end From 869e41e026eed1f9d391a95c286d4d7f6ce2eb29 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Mon, 11 Nov 2013 20:01:06 +0100 Subject: [PATCH 03/40] Updated yaml_migrator to only select yaml columns --- db/migrate/migration_utils/yaml_migrator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/migrate/migration_utils/yaml_migrator.rb b/db/migrate/migration_utils/yaml_migrator.rb index 9e7b71c3c0..cffc747b8b 100644 --- a/db/migrate/migration_utils/yaml_migrator.rb +++ b/db/migrate/migration_utils/yaml_migrator.rb @@ -34,6 +34,7 @@ module Migration ActiveRecord::Base.connection.select_all <<-SQL SELECT #{db_column('id')}, #{db_column(column)} FROM #{quoted_table_name(table)} + WHERE #{db_column(column)} LIKE #{quote_value('---%')} SQL end From 8a25a1c0a174df86c467776fabea3051de95c989 Mon Sep 17 00:00:00 2001 From: Philipp Tessenow Date: Tue, 12 Nov 2013 00:05:49 +0100 Subject: [PATCH 04/40] add option to diable browser cache for security reasons. --- app/controllers/application_controller.rb | 19 ++++++++++++++++--- config/configuration.yml.example | 4 ++++ lib/open_project/configuration.rb | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 71c54e02ad..23706491f6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -45,8 +45,6 @@ require_dependency 'principal' class ApplicationController < ActionController::Base - # ensure the OpenProject models are required in the right order (as they have circular dependencies) - class_attribute :_model_object class_attribute :_model_scope class_attribute :accept_key_auth_actions @@ -87,7 +85,9 @@ class ApplicationController < ActionController::Base :reset_i18n_fallbacks, :set_localization, :check_session_lifetime, - :stop_if_feeds_disabled + :stop_if_feeds_disabled, + :set_cache_buster + rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token @@ -104,6 +104,19 @@ class ApplicationController < ActionController::Base { :layout => params["layout"] } end + # set http headers so that the browser does not store any + # data (caches) of this site + # see: https://websecuritytool.codeplex.com/wikipage?title=Checks#http-cache-control-header-no-store + # see: http://stackoverflow.com/questions/711418/how-to-prevent-browser-page-caching-in-rails + def set_cache_buster + if OpenProject::Configuration['disable_browser_cache'] + binding.pry + response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" + end + end + # the current user is a per-session kind of thing and session stuff is controller responsibility. # a globally accessible User.current is a big code smell. when used incorrectly it allows getting # the current user outside of a session scope, i.e. in the model layer, from mailers or in the console diff --git a/config/configuration.yml.example b/config/configuration.yml.example index 47c4091d64..25da82b92f 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -127,6 +127,10 @@ default: # autologin_cookie_path: # autologin_cookie_secure: + # disable browser cache for security reasons + # see: https://websecuritytool.codeplex.com/wikipage?title=Checks#http-cache-control-header-no-store + # disable_browser_cache: true + # Configuration of SCM executable command. # Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe) # On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work. diff --git a/lib/open_project/configuration.rb b/lib/open_project/configuration.rb index a1df6c7d3c..02988f2dfa 100644 --- a/lib/open_project/configuration.rb +++ b/lib/open_project/configuration.rb @@ -39,6 +39,7 @@ module OpenProject 'database_cipher_key' => nil, 'scm_git_command' => nil, 'scm_subversion_command' => nil, + 'disable_browser_cache' => false, # email configuration 'email_delivery_method' => nil, From e4446d89459606a4edd37ef59e755f9aec430e6c Mon Sep 17 00:00:00 2001 From: Philipp Tessenow Date: Tue, 12 Nov 2013 08:31:18 +0100 Subject: [PATCH 05/40] add changelog entry [skip ci] --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index e28ad992cb..93a99781c8 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -34,6 +34,7 @@ See doc/COPYRIGHT.rdoc for more details. * `#2473` [Timelines] Tooltip in timeline report shows star * instead of hash # in front of ID * `#2721` Fix: Fix: Fix: Missing journal entries for customizable_journals * `#2718` Newlines in workpackage descriptions aren't normalized for change tracking +* `#1748` Add option to diable browser cache ## 3.0.0pre28 From 7282104f00fd818c2f0ab0117ee2e39480a6b69b Mon Sep 17 00:00:00 2001 From: Philipp Tessenow Date: Tue, 12 Nov 2013 09:18:23 +0100 Subject: [PATCH 06/40] remove forgotten debug statement [ci skip] --- app/controllers/application_controller.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 23706491f6..9241455ca1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -110,7 +110,6 @@ class ApplicationController < ActionController::Base # see: http://stackoverflow.com/questions/711418/how-to-prevent-browser-page-caching-in-rails def set_cache_buster if OpenProject::Configuration['disable_browser_cache'] - binding.pry response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" response.headers["Pragma"] = "no-cache" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" From 3d148bcd54e5ee097f2fdbb3dfb6dc6c6b049010 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Tue, 12 Nov 2013 10:38:09 +0100 Subject: [PATCH 07/40] Migrated serialized yaml in core tables from syck to psych --- ...rate_serialized_yaml_from_syck_to_psych.rb | 45 +++++++++++++++++++ db/migrate/migration_utils/yaml_migrator.rb | 8 ++-- doc/CHANGELOG.md | 4 +- 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20130612120042_migrate_serialized_yaml_from_syck_to_psych.rb diff --git a/db/migrate/20130612120042_migrate_serialized_yaml_from_syck_to_psych.rb b/db/migrate/20130612120042_migrate_serialized_yaml_from_syck_to_psych.rb new file mode 100644 index 0000000000..477539cea4 --- /dev/null +++ b/db/migrate/20130612120042_migrate_serialized_yaml_from_syck_to_psych.rb @@ -0,0 +1,45 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2011-2013 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.md for more details. +#++ + +require_relative 'migration_utils/yaml_migrator' + +class MigrateSerializedYamlFromSyckToPsych < ActiveRecord::Migration + include Migration::YamlMigrator + + def up + migrate_yaml_columns('syck', 'psych') + end + + def down + migrate_yaml_columns('psych', 'syck') + end + + def migrate_yaml_columns(source_yamler, target_yamler) + ['filters', 'column_names', 'sort_criteria'].each do |column| + migrate_yaml('queries', column, source_yamler, target_yamler) + end + migrate_yaml('custom_field_translations', 'possible_values', source_yamler, target_yamler) + migrate_yaml('roles', 'permissions', source_yamler, target_yamler) + migrate_yaml('settings', 'value', source_yamler, target_yamler) + migrate_yaml('timelines', 'options', source_yamler, target_yamler) + migrate_yaml('user_preferences', 'others', source_yamler, target_yamler) + migrate_yaml('wiki_menu_items', 'options', source_yamler, target_yamler) + end + +end diff --git a/db/migrate/migration_utils/yaml_migrator.rb b/db/migrate/migration_utils/yaml_migrator.rb index cffc747b8b..4aa55283a2 100644 --- a/db/migrate/migration_utils/yaml_migrator.rb +++ b/db/migrate/migration_utils/yaml_migrator.rb @@ -13,9 +13,9 @@ require 'syck' module Migration module YamlMigrator - extend DbWorker + include DbWorker - def self.migrate(table, column, source_yamler, target_yamler) + def migrate_yaml(table, column, source_yamler, target_yamler) current_yamler = YAML::ENGINE.yamler fetch_data(table,column).each do | data | db_execute <<-SQL @@ -30,7 +30,7 @@ module Migration YAML::ENGINE.yamler = current_yamler.present? ? current_yamler : 'psych' end - def self.fetch_data(table, column) + def fetch_data(table, column) ActiveRecord::Base.connection.select_all <<-SQL SELECT #{db_column('id')}, #{db_column(column)} FROM #{quoted_table_name(table)} @@ -38,7 +38,7 @@ module Migration SQL end - def self.yaml_to_yaml(data, source_yamler, target_yamler) + def yaml_to_yaml(data, source_yamler, target_yamler) YAML::ENGINE.yamler = source_yamler original = YAML.load(data) YAML::ENGINE.yamler = target_yamler diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index e28ad992cb..590260f80c 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -29,9 +29,11 @@ See doc/COPYRIGHT.rdoc for more details. # Changelog +* `#2731` Migrated serialized yaml from syck to psych + ## 3.0.0pre29 -* `#2473` [Timelines] Tooltip in timeline report shows star * instead of hash # in front of ID +* `#2473` [Timelines] Tooltip in timeline report shows star * instead of hash # in front of ID * `#2721` Fix: Fix: Fix: Missing journal entries for customizable_journals * `#2718` Newlines in workpackage descriptions aren't normalized for change tracking From 2841c0fdd74c4f8ac763780aa425cd4095c7d9ac Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Tue, 12 Nov 2013 10:48:27 +0100 Subject: [PATCH 08/40] added changed Gemfile.lock --- Gemfile.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 04221f1103..1d94b6b7ef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -318,6 +318,7 @@ GEM railties (~> 3.0) structured_warnings (0.1.4) svg-graph (1.0.5) + syck (1.0.1) test-unit (2.5.5) therubyracer (0.11.4) libv8 (~> 3.11.8.12) @@ -424,6 +425,7 @@ DEPENDENCIES sqlite3 strong_parameters svg-graph + syck therubyracer thin timecop (~> 0.6.1) From bcc69b0c3d87722f48fd7f1188ea955e19348d93 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Tue, 12 Nov 2013 11:20:13 +0100 Subject: [PATCH 09/40] new special version for plugin compatibility --- lib/open_project/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_project/version.rb b/lib/open_project/version.rb index 3c590fc0f9..037b2d6b56 100644 --- a/lib/open_project/version.rb +++ b/lib/open_project/version.rb @@ -49,7 +49,7 @@ module OpenProject # # 2.0.0debian-2 def self.special - 'pre29' + 'pre30' end def self.revision From 6ba03c8ab9c1890a63f047f5a6ae09d70298baca Mon Sep 17 00:00:00 2001 From: Michael Frister Date: Tue, 12 Nov 2013 11:24:12 +0100 Subject: [PATCH 10/40] Disable caching by default --- lib/open_project/configuration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_project/configuration.rb b/lib/open_project/configuration.rb index 02988f2dfa..31c6b31911 100644 --- a/lib/open_project/configuration.rb +++ b/lib/open_project/configuration.rb @@ -39,7 +39,7 @@ module OpenProject 'database_cipher_key' => nil, 'scm_git_command' => nil, 'scm_subversion_command' => nil, - 'disable_browser_cache' => false, + 'disable_browser_cache' => true, # email configuration 'email_delivery_method' => nil, From 4d2bfce8bf27204c9f776aa95641bd5a8de81791 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 5 Nov 2013 17:54:51 +0100 Subject: [PATCH 11/40] Adds root id update statement for MySQL --- ..._planning_element_data_to_work_packages.rb | 25 ++++++++++++++++++- ...default_values_in_work_package_journals.rb | 11 +++----- db/migrate/migration_utils/utils.rb | 8 ++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/db/migrate/20130917131710_planning_element_data_to_work_packages.rb b/db/migrate/20130917131710_planning_element_data_to_work_packages.rb index bdf131a7e7..b351533b35 100644 --- a/db/migrate/20130917131710_planning_element_data_to_work_packages.rb +++ b/db/migrate/20130917131710_planning_element_data_to_work_packages.rb @@ -9,7 +9,12 @@ # See doc/COPYRIGHT.rdoc for more details. #++ # + +require_relative 'migration_utils/utils' + class PlanningElementDataToWorkPackages < ActiveRecord::Migration + include Migration::Utils + def up add_new_id_column @@ -251,13 +256,31 @@ class PlanningElementDataToWorkPackages < ActiveRecord::Migration num_updated = 1 while num_updated != 0 - num_updated = update <<-SQL + num_updated = update set_root_id_for_children_db_statement + end + end + + def set_root_id_for_children_db_statement + if mysql? + + <<-SQL + UPDATE #{db_work_packages_table} AS child + JOIN #{db_work_packages_table} AS parent + ON (child.#{db_column('parent_id')} = parent.#{db_column('id')}) + SET child.#{db_column('root_id')} = parent.#{db_column('id')} + WHERE child.#{db_column('root_id')} IS NULL + SQL + + else + + <<-SQL UPDATE #{db_work_packages_table} SET #{db_column('root_id')} = (SELECT parent.#{db_column('root_id')} FROM #{db_work_packages_table} AS parent WHERE parent.#{db_column('id')} = #{db_work_packages_table}.#{db_column('parent_id')}) WHERE #{db_work_packages_table}.#{db_column('root_id')} IS NULL SQL + end end diff --git a/db/migrate/20131101125921_migrate_default_values_in_work_package_journals.rb b/db/migrate/20131101125921_migrate_default_values_in_work_package_journals.rb index 932210b6fe..6d1abd4435 100644 --- a/db/migrate/20131101125921_migrate_default_values_in_work_package_journals.rb +++ b/db/migrate/20131101125921_migrate_default_values_in_work_package_journals.rb @@ -27,7 +27,10 @@ # See doc/COPYRIGHT.rdoc for more details. #++ +require_relative 'migration_utils/utils' + class MigrateDefaultValuesInWorkPackageJournals < ActiveRecord::Migration + include Migration::Utils def up @@ -47,14 +50,6 @@ class MigrateDefaultValuesInWorkPackageJournals < ActiveRecord::Migration %w(author_id status_id priority_id) end - def postgres? - ActiveRecord::Base.connection.instance_values["config"][:adapter] == "postgresql" - end - - def mysql? - ActiveRecord::Base.connection.instance_values["config"][:adapter] == "mysql2" - end - def migrate_field(field) if postgres? execute <<-SQL diff --git a/db/migrate/migration_utils/utils.rb b/db/migrate/migration_utils/utils.rb index 0132e5a13c..ec6705304b 100644 --- a/db/migrate/migration_utils/utils.rb +++ b/db/migrate/migration_utils/utils.rb @@ -56,6 +56,14 @@ module Migration ActiveRecord::Base.connection.reset_pk_sequence!(table) end + def postgres? + ActiveRecord::Base.connection.instance_values["config"][:adapter] == "postgresql" + end + + def mysql? + ActiveRecord::Base.connection.instance_values["config"][:adapter] == "mysql2" + end + private def select_rows_from_database(table, column_list, conditions) From 38884f331b348a2207c57b419c62ad069dfaa05a Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 5 Nov 2013 17:55:31 +0100 Subject: [PATCH 12/40] Removes superfluous newlines --- .../remove_timelines_historical_comparison_from_options.rake | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/tasks/remove_timelines_historical_comparison_from_options.rake b/lib/tasks/remove_timelines_historical_comparison_from_options.rake index c1f98d213f..830a284911 100644 --- a/lib/tasks/remove_timelines_historical_comparison_from_options.rake +++ b/lib/tasks/remove_timelines_historical_comparison_from_options.rake @@ -14,9 +14,6 @@ require_relative '../../db/migrate/migration_utils/timelines' namespace :migrations do namespace :timelines do - - - desc "Sets all timelines with historical comparison from 'historical' to 'none'" task :remove_timelines_historical_comparison_from_options => :environment do |task| setter = TimelinesHistoricalComparisonSetter.new From eb61b4c94154a326bc9eb85f48fd87f2aa7d0426 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 5 Nov 2013 17:55:45 +0100 Subject: [PATCH 13/40] Adds rake task for quoting invalid data --- ...invalid_characters_in_legacy_journals.rake | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 lib/tasks/quote_strings_with_invalid_characters_in_legacy_journals.rake diff --git a/lib/tasks/quote_strings_with_invalid_characters_in_legacy_journals.rake b/lib/tasks/quote_strings_with_invalid_characters_in_legacy_journals.rake new file mode 100644 index 0000000000..e8722bfb4c --- /dev/null +++ b/lib/tasks/quote_strings_with_invalid_characters_in_legacy_journals.rake @@ -0,0 +1,61 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# +# Copyright (C) 2012-2013 the OpenProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require_relative '../../db/migrate/migration_utils/timelines' + +namespace :migrations do + namespace :journals do + desc "Quotes all strings starting with an invalid character in column 'changed_data' of table 'legacy_journals'" + task :quote_strings_with_invalid_characters_in_legacy_journals => :environment do |task| + quoter = InvalidChangedDataStringQuoter.new + + quoter.quote_strings_with_invalid_characters + end + + private + + class InvalidChangedDataStringQuoter < ActiveRecord::Migration + include Migration::Utils + + def quote_strings_with_invalid_characters + say_with_time_silently "Quote journal strings with invalid characters" do + update_column_values('legacy_journals', + ['changed_data'], + quote_invalid_strings, + invalid_changed_data_filter) + end + end + + private + + def invalid_changed_data_filter + "changed_data LIKE '%- ,%'" + end + + INVALID_STARTING_CHARACTER_REGEX = /(?\n- )(?,.*)(?\n.*:)/ + + def quote_invalid_strings + Proc.new do |row| + changed_data = row['changed_data'] + + quoted_changed_data = changed_data.gsub(INVALID_STARTING_CHARACTER_REGEX) do |m| + "#{$1}\"#{$2}\"#{$3}" + end + + row['changed_data'] = quoted_changed_data + + UpdateResult.new(row, changed_data != quoted_changed_data) + end + end + end + end +end From 8305dc7de9919acb08021788546c6f8acb0f2521 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Wed, 6 Nov 2013 09:06:03 +0100 Subject: [PATCH 14/40] Does not abort migration on missing entities Displays all planning element type ids and planning element ids for which no type or work package exists. --- ...47_legacy_planning_element_journal_data.rb | 76 +++++++++++++++---- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/db/migrate/20130920095747_legacy_planning_element_journal_data.rb b/db/migrate/20130920095747_legacy_planning_element_journal_data.rb index e4a43ba697..84552b3bbe 100644 --- a/db/migrate/20130920095747_legacy_planning_element_journal_data.rb +++ b/db/migrate/20130920095747_legacy_planning_element_journal_data.rb @@ -44,6 +44,37 @@ class LegacyPlanningElementJournalData < ActiveRecord::Migration migrator.run reset_public_key_sequence_in_postgres 'journals' + + unless migrator.missing_type_ids.empty? + puts "Cannot resolve new type ids for all journals!"\ + "\n\n"\ + "The following list contains all legacy planning element "\ + "type ids for which no new type id exists. Furthermore, "\ + "the list contains all journal ids for which no new type "\ + "id exists."\ + "\n\n"\ + "#{migrator.missing_type_ids}"\ + "\n\n"\ + "The type id is set to '0' for all journals containing a "\ + "planning element type id for which no type id exists."\ + "\n\n\n" + end + + unless migrator.missing_journaled_ids.empty? + puts "Cannot resolve work package ids for all journals!"\ + "\n\n"\ + "The following list contains all legacy planning element "\ + "ids for which no new work package id exists. Furthermore,"\ + " the list contains all journal ids for which no new"\ + "work package id exists."\ + "\n\n"\ + "#{migrator.missing_journaled_ids}"\ + "\n\n"\ + "The work package id is set to '0' for all journals "\ + "containing a planning element type id for which no type "\ + "id exists." + "\n\n\n" + end end def down @@ -66,7 +97,7 @@ class LegacyPlanningElementJournalData < ActiveRecord::Migration def migrate_key_value_pairs!(to_insert, legacy_journal, journal_id) - update_type_id(to_insert) + update_type_id(to_insert, journal_id) set_empty_description(to_insert) @@ -77,30 +108,31 @@ class LegacyPlanningElementJournalData < ActiveRecord::Migration end def update_journaled_id(legacy_journal) - new_journaled_id = new_journaled_id_for_old(legacy_journal["journaled_id"]) + legecy_journal_id = legacy_journal["id"] + old_journaled_id = legacy_journal["journaled_id"] + new_journaled_id = new_journaled_id_for_old(old_journaled_id) if new_journaled_id.nil? - raise UnknownJournaledError, <<-MESSAGE.split("\n").map(&:strip!).join(" ") + "\n" - No new journaled_id could be found to replace the journaled_id value of - #{legacy_journal["journaled_id"]} for the legacy journal with the id - #{legacy_journal["id"]} - MESSAGE + add_missing_journaled_id_for_legacy_journal_id(old_journaled_id, legecy_journal_id) + + new_journaled_id = 0 end legacy_journal["journaled_id"] = new_journaled_id end - def update_type_id(to_insert) + def update_type_id(to_insert, journal_id) return if to_insert["planning_element_type_id"].nil? || to_insert["planning_element_type_id"].last.nil? - new_type_id = new_type_id_for_old(to_insert["planning_element_type_id"].last) + old_type_id = to_insert["planning_element_type_id"].last + + new_type_id = new_type_id_for_old(old_type_id) if new_type_id.nil? - raise UnknownTypeError, <<-MESSAGE.split("\n").map(&:strip!).join(" ") + "\n" - No new type_id could be found to replace the type_id value of - #{to_insert["planning_element_type_id"].last} - MESSAGE + add_missing_type_id_for_journal_id(old_type_id, journal_id) + + new_type_id = 0 end to_insert["type_id"] = [nil, new_type_id] @@ -146,6 +178,24 @@ class LegacyPlanningElementJournalData < ActiveRecord::Migration def set_empty_description(to_insert) to_insert['description'] = [nil, ''] unless to_insert.has_key?('description') end + + def missing_journaled_ids + @missing_journaled_ids ||= {} + end + + def add_missing_journaled_id_for_legacy_journal_id(old_journaled_id, legacy_journal_id) + missing_journaled_ids[old_journaled_id] = [] unless missing_journaled_ids.has_key? old_journaled_id + missing_journaled_ids[old_journaled_id] << legacy_journal_id + end + + def missing_type_ids + @missing_type_ids ||= {} + end + + def add_missing_type_id_for_journal_id(old_type_id, journal_id) + missing_type_ids[old_type_id] = [] unless missing_type_ids.has_key? old_type_id + missing_type_ids[old_type_id] << journal_id + end end end end From 1e32b61a2a893d55a81909f87ecc0b66e32e9258 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Fri, 8 Nov 2013 17:29:34 +0100 Subject: [PATCH 15/40] Handles MySQL collation error --- .../migration_utils/attachable_utils.rb | 32 +++++++++++++------ lib/tasks/fix_attachments_collation.rake | 24 ++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 lib/tasks/fix_attachments_collation.rake diff --git a/db/migrate/migration_utils/attachable_utils.rb b/db/migrate/migration_utils/attachable_utils.rb index 7766d0c9f0..46c16437aa 100644 --- a/db/migrate/migration_utils/attachable_utils.rb +++ b/db/migrate/migration_utils/attachable_utils.rb @@ -67,16 +67,28 @@ module Migration::Utils private def missing_attachments - result = select_all <<-SQL - SELECT * FROM ( - SELECT a.container_id AS journaled_id, a.container_type AS journaled_type, a.id AS attachment_id, a.filename, MAX(aj.id) AS aj_id, MAX(j.version) AS last_version - FROM attachments AS a JOIN journals AS j - ON (a.container_id = j.journable_id AND a.container_type = j.journable_type) LEFT JOIN attachable_journals AS aj - ON (a.id = aj.attachment_id) - GROUP BY a.container_id, a.container_type, a.id, a.filename - ) AS tmp - WHERE aj_id IS NULL - SQL + begin + result = select_all <<-SQL + SELECT * FROM ( + SELECT a.container_id AS journaled_id, a.container_type AS journaled_type, a.id AS attachment_id, a.filename, MAX(aj.id) AS aj_id, MAX(j.version) AS last_version + FROM attachments AS a JOIN journals AS j + ON (a.container_id = j.journable_id AND a.container_type = j.journable_type) LEFT JOIN attachable_journals AS aj + ON (a.id = aj.attachment_id) + GROUP BY a.container_id, a.container_type, a.id, a.filename + ) AS tmp + WHERE aj_id IS NULL + SQL + rescue ActiveRecord::StatementInvalid => ex + raise ex unless mysql? + + raise "An MySQL error occured (see details below)!"\ + "\n\n"\ + "If you're facing an 'Illegal mix of collations error, consider "\ + "running rake task "\ + "'migrations:journals:fix_attachments_collation'."\ + "\n\n"\ + "#{ex.message}" + end result.collect { |row| MissingAttachment.new(row['journaled_id'], row['journaled_type'], diff --git a/lib/tasks/fix_attachments_collation.rake b/lib/tasks/fix_attachments_collation.rake new file mode 100644 index 0000000000..473d099ddf --- /dev/null +++ b/lib/tasks/fix_attachments_collation.rake @@ -0,0 +1,24 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# +# Copyright (C) 2012-2013 the OpenProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require_relative '../../db/migrate/migration_utils/utils' + +namespace :migrations do + namespace :journals do + desc "Fixes 'attachments' table collation" + task :fix_attachments_collation => :environment do |task| + ActiveRecord::Base.connection.execute <<-SQL + ALTER TABLE attachments CONVERT TO character SET utf8 COLLATE utf8_unicode_ci; + SQL + end + end +end From a99cc4c55efc443e95b74839cc71ca9ec7446c61 Mon Sep 17 00:00:00 2001 From: Michael Frister Date: Tue, 12 Nov 2013 14:08:55 +0100 Subject: [PATCH 16/40] Add changelog entry --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 00cfd19974..977fa5d9af 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -30,6 +30,7 @@ See doc/COPYRIGHT.rdoc for more details. # Changelog * `#2731` Migrated serialized yaml from syck to psych +* Fix mysql data migrations ## 3.0.0pre29 From 7088d80cad11d9dbc9ec13fbf04dde8d77de8e33 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 12 Nov 2013 12:46:50 +0100 Subject: [PATCH 17/40] Normalizes predecessor data too --- app/models/journal_manager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/journal_manager.rb b/app/models/journal_manager.rb index 4bb53271b3..39b926cdeb 100644 --- a/app/models/journal_manager.rb +++ b/app/models/journal_manager.rb @@ -51,6 +51,7 @@ class JournalManager predecessor = journable.journals.last.data.journaled_attributes current = normalize_newlines(current) + predecessor = normalize_newlines(predecessor) return predecessor.map{|k,v| current[k.to_s] != v} .inject(false) { |r, c| r || c } From 60f56c0795b74c2198df0a29401ac665e1068167 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 12 Nov 2013 12:57:15 +0100 Subject: [PATCH 18/40] Deletes invalid work package custom values --- .../migration_utils/customizable_utils.rb | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/db/migrate/migration_utils/customizable_utils.rb b/db/migrate/migration_utils/customizable_utils.rb index 1b5993c5f9..05d46b9cfd 100644 --- a/db/migrate/migration_utils/customizable_utils.rb +++ b/db/migrate/migration_utils/customizable_utils.rb @@ -19,6 +19,8 @@ module Migration::Utils :last_version) def add_missing_customizable_journals + delete_invalid_work_package_custom_values + result = missing_custom_values repair_journals(result) @@ -66,6 +68,27 @@ module Migration::Utils private + # Removes all work package custom values that are not referenced by the + # work package's project AND type. + def delete_invalid_work_package_custom_values + delete <<-SQL + DELETE FROM custom_values AS cvd + WHERE EXISTS + ( + SELECT w.id, cf.id, cfp.project_id, p.name, cft.type_id + FROM work_packages AS w + JOIN custom_values AS cv ON (w.id = cv.customized_id AND cv.customized_type = 'WorkPackage') + JOIN custom_fields AS cf ON (cv.custom_field_id = cf.id) + JOIN projects AS p ON (w.project_id = p.id) + LEFT JOIN custom_fields_projects AS cfp ON (cv.custom_field_id = cfp.custom_field_id AND w.project_id = cfp.project_id) + LEFT JOIN custom_fields_types AS cft ON (cv.custom_field_id = cft.custom_field_id AND w.type_id = cft.type_id) + WHERE (cfp.project_id IS NULL + OR cft.type_id IS NULL) + AND cv.id = cvd.id + ); + SQL + end + def missing_custom_values result = select_all <<-SQL SELECT tmp.customized_id, From e077115f4af865047b18c967451ee5083603eedf Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 12 Nov 2013 16:03:43 +0100 Subject: [PATCH 19/40] Fixes delete statement for MySQL --- .../migration_utils/customizable_utils.rb | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/db/migrate/migration_utils/customizable_utils.rb b/db/migrate/migration_utils/customizable_utils.rb index 05d46b9cfd..b8d2a0867e 100644 --- a/db/migrate/migration_utils/customizable_utils.rb +++ b/db/migrate/migration_utils/customizable_utils.rb @@ -71,22 +71,35 @@ module Migration::Utils # Removes all work package custom values that are not referenced by the # work package's project AND type. def delete_invalid_work_package_custom_values - delete <<-SQL - DELETE FROM custom_values AS cvd - WHERE EXISTS - ( - SELECT w.id, cf.id, cfp.project_id, p.name, cft.type_id - FROM work_packages AS w - JOIN custom_values AS cv ON (w.id = cv.customized_id AND cv.customized_type = 'WorkPackage') - JOIN custom_fields AS cf ON (cv.custom_field_id = cf.id) - JOIN projects AS p ON (w.project_id = p.id) - LEFT JOIN custom_fields_projects AS cfp ON (cv.custom_field_id = cfp.custom_field_id AND w.project_id = cfp.project_id) - LEFT JOIN custom_fields_types AS cft ON (cv.custom_field_id = cft.custom_field_id AND w.type_id = cft.type_id) - WHERE (cfp.project_id IS NULL - OR cft.type_id IS NULL) - AND cv.id = cvd.id - ); - SQL + if mysql? + delete <<-SQL + DELETE cv.* FROM custom_values AS cv + JOIN work_packages AS w ON (w.id = cv.customized_id AND cv.customized_type = 'WorkPackage') + JOIN custom_fields AS cf ON (cv.custom_field_id = cf.id) + JOIN projects AS p ON (w.project_id = p.id) + LEFT JOIN custom_fields_projects AS cfp ON (cv.custom_field_id = cfp.custom_field_id AND w.project_id = cfp.project_id) + LEFT JOIN custom_fields_types AS cft ON (cv.custom_field_id = cft.custom_field_id AND w.type_id = cft.type_id) + WHERE cfp.project_id IS NULL + OR cft.type_id IS NULL + SQL + else + delete <<-SQL + DELETE FROM custom_values AS cvd + WHERE EXISTS + ( + SELECT w.id, cf.id, cfp.project_id, p.name, cft.type_id + FROM work_packages AS w + JOIN custom_values AS cv ON (w.id = cv.customized_id AND cv.customized_type = 'WorkPackage') + JOIN custom_fields AS cf ON (cv.custom_field_id = cf.id) + JOIN projects AS p ON (w.project_id = p.id) + LEFT JOIN custom_fields_projects AS cfp ON (cv.custom_field_id = cfp.custom_field_id AND w.project_id = cfp.project_id) + LEFT JOIN custom_fields_types AS cft ON (cv.custom_field_id = cft.custom_field_id AND w.type_id = cft.type_id) + WHERE (cfp.project_id IS NULL + OR cft.type_id IS NULL) + AND cv.id = cvd.id + ); + SQL + end end def missing_custom_values From e8bab8021668a4751514480507e5a87c26a92369 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Tue, 12 Nov 2013 16:22:30 +0100 Subject: [PATCH 20/40] Fixes spec --- .../work_package_acts_as_journalized_spec.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/spec/models/work_package/work_package_acts_as_journalized_spec.rb b/spec/models/work_package/work_package_acts_as_journalized_spec.rb index 1dcc819852..54cc8c8869 100644 --- a/spec/models/work_package/work_package_acts_as_journalized_spec.rb +++ b/spec/models/work_package/work_package_acts_as_journalized_spec.rb @@ -106,14 +106,16 @@ describe WorkPackage do end context 'when there is a legacy journal containing non-escaped newlines' do - before do - work_package_1.save - # force the latest journal to match the description with unescaped newline characters - legacy_journal = work_package_1.journals.last - legacy_journal.data.update_column :description, changed_description - # rollback work package description to normalized newlines - work_package_1.update_attributes description: description - end + let!(:work_package_journal_1) { FactoryGirl.create(:work_package_journal, + journable_id: work_package_1.id, + version: 2, + data: FactoryGirl.build(:journal_work_package_journal, + description: description)) } + let!(:work_package_journal_2) { FactoryGirl.create(:work_package_journal, + journable_id: work_package_1.id, + version: 3, + data: FactoryGirl.build(:journal_work_package_journal, + description: changed_description)) } subject { work_package_1.journals.last.details } From 17031576d32f0b5dc3d5a705dea2c9ab8006fe38 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Tue, 12 Nov 2013 17:16:31 +0100 Subject: [PATCH 21/40] updated changelog --- doc/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 977fa5d9af..29401e4a92 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -29,9 +29,13 @@ See doc/COPYRIGHT.rdoc for more details. # Changelog -* `#2731` Migrated serialized yaml from syck to psych * Fix mysql data migrations +## 3.0.0pre30 + +* `#2721` Fix: Fix: Fix: Fix: Missing journal entries for customizable_journals +* `#2731` Migrated serialized yaml from syck to psych + ## 3.0.0pre29 * `#2473` [Timelines] Tooltip in timeline report shows star * instead of hash # in front of ID From cb04b84f5f856aa931e4ba91432df95ebf63bcac Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Tue, 12 Nov 2013 17:24:46 +0100 Subject: [PATCH 22/40] Fixed LegacyJournalMigrator to also run with Ruby 2.0 with added syck --- db/migrate/migration_utils/legacy_journal_migrator.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/migrate/migration_utils/legacy_journal_migrator.rb b/db/migrate/migration_utils/legacy_journal_migrator.rb index aca7e2d9e5..0f6e0dc724 100644 --- a/db/migrate/migration_utils/legacy_journal_migrator.rb +++ b/db/migrate/migration_utils/legacy_journal_migrator.rb @@ -29,6 +29,7 @@ require_relative 'db_worker' require_relative 'legacy_table_checker' +require 'syck' module Migration class IncompleteJournalsError < ::StandardError @@ -279,7 +280,7 @@ module Migration changed_data = journal["changed_data"] return Hash.new if changed_data.nil? - current_yamler = YAML::ENGINE.yamler + current_yamler = YAML::ENGINE.yamler || 'psych' begin # The change to 'syck' ensures that legacy data is correctly read from # the 'legacy_journals' table. Otherwise, we would end up with false From cfef0dfd67b1371b31652f66511734a3bb4e49b7 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Wed, 13 Nov 2013 09:41:36 +0100 Subject: [PATCH 23/40] redirect old issue links to wp uris --- config/routes.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 64e1153247..b38747182c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,9 @@ OpenProject::Application.routes.draw do root :to => 'welcome#index', :as => 'home' + match '/issues(/)' => redirect('/work_packages/') + match '/issues/*rest' => redirect { |params, req| "/work_packages/#{params[:rest]}" } + scope :controller => 'account' do get '/account/force_password_change', :action => 'force_password_change' post '/account/change_password', :action => 'change_password' From c7dbb3942b7c3c58d9f92349e70b4a5623048c3d Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Wed, 13 Nov 2013 09:59:20 +0100 Subject: [PATCH 24/40] fixed redirect old issues to ne wp --- config/routes.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index b38747182c..e907adaf29 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,8 +30,10 @@ OpenProject::Application.routes.draw do root :to => 'welcome#index', :as => 'home' - match '/issues(/)' => redirect('/work_packages/') - match '/issues/*rest' => redirect { |params, req| "/work_packages/#{params[:rest]}" } + match '/issues(/)' => redirect('/work_packages/') + # The URI.escape doesn't escape / unless you ask it to. + # see https://github.com/rails/rails/issues/5688 + match '/issues/*rest' => redirect { |params, req| "/work_packages/#{URI.escape(params[:rest])}" } scope :controller => 'account' do get '/account/force_password_change', :action => 'force_password_change' From 807af0f898d7447fe85f74877c294ab228c60f39 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Wed, 13 Nov 2013 10:05:14 +0100 Subject: [PATCH 25/40] added comment for redirect issue 2 wp --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index e907adaf29..31592bde6e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,7 @@ OpenProject::Application.routes.draw do root :to => 'welcome#index', :as => 'home' + # Redirect deprecated issue links to new work packages uris match '/issues(/)' => redirect('/work_packages/') # The URI.escape doesn't escape / unless you ask it to. # see https://github.com/rails/rails/issues/5688 From 76140dd785f2041cf83be9f196b5f61642a82c3f Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Wed, 6 Nov 2013 17:59:00 +0100 Subject: [PATCH 26/40] URL-escape wiki_page#to_param --- app/models/wiki_page.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index e0eb2e3fc9..52861218bb 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -239,7 +239,7 @@ class WikiPage < ActiveRecord::Base end def to_param - title + CGI.escape title end def is_only_wiki_page? From 38ac23fd6f877a1847d466130561c019c9dc74f4 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Wed, 6 Nov 2013 18:01:00 +0100 Subject: [PATCH 27/40] Remove confusing wiki_controller#new --- app/controllers/wiki_controller.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 461715ba17..00d2e62d8b 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -57,6 +57,7 @@ class WikiController < ApplicationController :add_attachment, :list_attachments, :destroy] + before_filter :build_wiki_page_and_content, only: :create verify :method => :post, :only => [:protect], :redirect_to => { :action => :show } verify :method => :get, :only => [:new, :new_child], :render => {:nothing => true, :status => :method_not_allowed} @@ -93,29 +94,19 @@ class WikiController < ApplicationController @pages_by_date = @pages.group_by {|p| p.updated_on.to_date} end - def new - @page = WikiPage.new(:wiki => @wiki) - @page.content = WikiContent.new(:page => @page) - - @content = @page.content_for_version(nil) - @content.text = initial_page_content(@page) - end - def new_child find_existing_page return if performed? old_page = @page - new + build_wiki_page_and_content @page.parent = old_page render :action => 'new' end def create - new - @page.title = params[:page][:title] @page.parent_id = params[:page][:parent_id] @@ -352,7 +343,7 @@ class WikiController < ApplicationController nil end -private + private def find_wiki @project = Project.find(params[:project_id]) @@ -368,6 +359,14 @@ private render_404 if @page.nil? end + def build_wiki_page_and_content + @page = WikiPage.new wiki: @wiki + @page.content = WikiContent.new page: @page + + @content = @page.content_for_version nil + @content.text = initial_page_content @page + end + # Returns true if the current user is allowed to edit the page, otherwise false def editable?(page = @page) page.editable_by?(User.current) From bf0c197dc0fc312971039f0b5551f9f7f64cc336 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Wed, 6 Nov 2013 18:03:28 +0100 Subject: [PATCH 28/40] Ensure wiki_page#to_param is used for links in wiki index --- app/helpers/application_helper.rb | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9d51f2a773..c9d804807a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -240,19 +240,15 @@ module ApplicationHelper end def render_page_hierarchy(pages, node=nil, options={}) - content = '' - if pages[node] - content << "
    \n" - pages[node].each do |page| - content << "
  • " - content << link_to(page.pretty_title, project_wiki_path(page.project, page), - :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) - content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id] - content << "
  • \n" - end - content << "
\n" + content_tag :ul, class: 'pages-hierarchy' do + pages[node].collect do |page| + content_tag :li do + concat link_to(page.pretty_title, project_wiki_path(page.project, page), + title: (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) + concat render_page_hierarchy(pages, page.id, options) if pages[page.id] + end + end.join.html_safe end - content.html_safe end # Renders flash messages From a8265cef6c4a9fcdc502c674e16f6bbb6076bcad Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Wed, 6 Nov 2013 18:05:01 +0100 Subject: [PATCH 29/40] Ensure wiki_page#to_param is used for various links and redirects --- app/controllers/wiki_controller.rb | 10 ++++-- app/controllers/wiki_menu_items_controller.rb | 6 ++-- app/models/wiki_content.rb | 2 +- app/views/wiki/annotate.html.erb | 8 ++--- app/views/wiki/destroy.html.erb | 2 +- app/views/wiki/diff.html.erb | 6 ++-- app/views/wiki/edit.html.erb | 4 +-- app/views/wiki/edit_parent_page.html.erb | 2 +- app/views/wiki/export_multiple.html.erb | 2 +- app/views/wiki/history.html.erb | 4 +-- app/views/wiki/rename.html.erb | 2 +- app/views/wiki/show.html.erb | 34 +++++++++---------- app/views/wiki_menu_items/edit.html.erb | 4 +-- 13 files changed, 45 insertions(+), 41 deletions(-) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 00d2e62d8b..559a9efa61 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -295,7 +295,11 @@ class WikiController < ApplicationController end @page.destroy - redirect_to @wiki.pages.any? ? {:action => 'index', :project_id => @project} : project_path(@project) + if page = @wiki.start_page || @wiki.pages.first + redirect_to :action => 'index', :project_id => @project, id: page + else + redirect_to project_path(@project) + end end # Export wiki to a single html file @@ -325,7 +329,7 @@ class WikiController < ApplicationController return render_403 unless editable? attachments = Attachment.attach_files(@page, params[:attachments]) render_attachment_warning_if_needed(@page) - redirect_to :action => 'show', :id => @page.title, :project_id => @project + redirect_to :action => 'show', :id => @page, :project_id => @project end def list_attachments @@ -388,6 +392,6 @@ class WikiController < ApplicationController end def redirect_to_show - redirect_to :action => 'show', :project_id => @project, :id => @page.title + redirect_to action: :show, project_id: @project, id: @page end end diff --git a/app/controllers/wiki_menu_items_controller.rb b/app/controllers/wiki_menu_items_controller.rb index f3789b2d2f..8c1b65d78c 100644 --- a/app/controllers/wiki_menu_items_controller.rb +++ b/app/controllers/wiki_menu_items_controller.rb @@ -75,10 +75,10 @@ class WikiMenuItemsController < ApplicationController if not @wiki_menu_item.errors.size >= 1 and (@wiki_menu_item.destroyed? or @wiki_menu_item.save) flash[:notice] = l(:notice_successful_update) - redirect_back_or_default({ :action => 'edit', :id => @page_title }) + redirect_back_or_default({ :action => 'edit', :id => @page }) else respond_to do |format| - format.html { render :action => 'edit', :id => @page_title } + format.html { render :action => 'edit', :id => @page } end end end @@ -96,7 +96,7 @@ class WikiMenuItemsController < ApplicationController current_menu_item.destroy end - redirect_to action: :edit, id: current_page.title + redirect_to action: :edit, id: current_page end private diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb index 4e7a840a16..69ea7cad23 100644 --- a/app/models/wiki_content.rb +++ b/app/models/wiki_content.rb @@ -43,7 +43,7 @@ class WikiContent < ActiveRecord::Base acts_as_journalized :event_type => 'wiki-page', :event_title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.journal.journable.page.title} (##{o.journal.journable.version})"}, - :event_url => Proc.new {|o| {:controller => '/wiki', :action => 'show', :id => o.journal.journable.page.title, :project_id => o.journal.journable.page.wiki.project, :version => o.journal.journable.version}}, + :event_url => Proc.new {|o| {:controller => '/wiki', :action => 'show', :id => o.journal.journable.page, :project_id => o.journal.journable.page.wiki.project, :version => o.journal.journable.version}}, :activity_type => 'wiki_edits', :activity_permission => :view_wiki_edits, :activity_find_options => { :include => { :page => { :wiki => :project } } } diff --git a/app/views/wiki/annotate.html.erb b/app/views/wiki/annotate.html.erb index de4af5ab87..72ead84cad 100644 --- a/app/views/wiki/annotate.html.erb +++ b/app/views/wiki/annotate.html.erb @@ -28,14 +28,14 @@ See doc/COPYRIGHT.rdoc for more details. ++#%>
-<%= link_to(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit') %> -<%= link_to(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %> +<%= link_to(l(:button_edit), {:action => 'edit', :id => @page}, :class => 'icon icon-edit') %> +<%= link_to(l(:label_history), {:action => 'history', :id => @page}, :class => 'icon icon-history') %>

<%= h(@page.pretty_title) %>

-<%= Version.model_name.human %> <%= link_to h(@annotate.content.version), :action => 'show', :id => @page.title, :version => @annotate.content.version %> +<%= Version.model_name.human %> <%= link_to h(@annotate.content.version), :action => 'show', :id => @page, :version => @annotate.content.version %> (<%= h(@annotate.content.journable.author ? @annotate.content.journable.author.name : l(:label_user_anonymous)) %>, <%= format_time(@annotate.content.journable.updated_on) %>)

@@ -47,7 +47,7 @@ See doc/COPYRIGHT.rdoc for more details. <% @annotate.lines.each do |line| -%> <%= line_num %> - <%= link_to line[0], :controller => '/wiki', :action => 'show', :project_id => @project, :id => @page.title, :version => line[0] %> + <%= link_to line[0], :controller => '/wiki', :action => 'show', :project_id => @project, :id => @page, :version => line[0] %> <%= h(line[1]) %>
<%=h line[2] %>
diff --git a/app/views/wiki/destroy.html.erb b/app/views/wiki/destroy.html.erb index f829124c39..e0c151ba23 100644 --- a/app/views/wiki/destroy.html.erb +++ b/app/views/wiki/destroy.html.erb @@ -45,5 +45,5 @@ See doc/COPYRIGHT.rdoc for more details. <%= submit_tag l(:button_apply) %> -<%= link_to l(:button_cancel), :controller => '/wiki', :action => 'show', :project_id => @project, :id => @page.title %> +<%= link_to l(:button_cancel), controller: '/wiki', action: 'show', project_id: @project, id: @page %> <% end %> diff --git a/app/views/wiki/diff.html.erb b/app/views/wiki/diff.html.erb index e55e4a471e..ef4fb5a82e 100644 --- a/app/views/wiki/diff.html.erb +++ b/app/views/wiki/diff.html.erb @@ -28,16 +28,16 @@ See doc/COPYRIGHT.rdoc for more details. ++#%>
-<%= link_to(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %> +<%= link_to(l(:label_history), {:action => 'history', :id => @page}, :class => 'icon icon-history') %>

<%= h(@page.pretty_title) %>

-<%= Version.model_name.human %> <%= link_to @diff.content_from.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => @diff.content_from.version %>/<%= @page.content.version %> +<%= Version.model_name.human %> <%= link_to @diff.content_from.version, :action => 'show', :id => @page, :project_id => @page.project, :version => @diff.content_from.version %>/<%= @page.content.version %> (<%= @diff.content_from.user ? link_to_user(@diff.content_from.user) : l(:label_user_anonymous) %>, <%= format_time(@diff.content_from.created_at) %>) → -<%= Version.model_name.human %> <%= link_to @diff.content_to.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => @diff.content_to.version %>/<%= @page.content.version %> +<%= Version.model_name.human %> <%= link_to @diff.content_to.version, :action => 'show', :id => @page, :project_id => @page.project, :version => @diff.content_to.version %>/<%= @page.content.version %> (<%= @diff.content_to.user ? link_to_user(@diff.content_to.user) : l(:label_user_anonymous) %>, <%= format_time(@diff.content_to.created_at) %>)

diff --git a/app/views/wiki/edit.html.erb b/app/views/wiki/edit.html.erb index f213590583..297a51782a 100644 --- a/app/views/wiki/edit.html.erb +++ b/app/views/wiki/edit.html.erb @@ -29,7 +29,7 @@ See doc/COPYRIGHT.rdoc for more details.

<%=h @page.pretty_title %>

-<%= form_for @content, :as => :content, :url => {:action => 'update', :id => @page.title}, :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %> +<%= form_for @content, :as => :content, :url => {:action => 'update', :id => @page}, :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %> <%= f.hidden_field :lock_version %> <%= error_messages_for 'content' %> @@ -40,7 +40,7 @@ See doc/COPYRIGHT.rdoc for more details.

<%= submit_tag l(:button_save) %> <%= link_to_remote l(:label_preview), - { :url => { :controller => '/wiki', :action => 'preview', :project_id => @project, :id => @page.title }, + { :url => { :controller => '/wiki', :action => 'preview', :project_id => @project, :id => @page }, :method => :post, :update => 'preview', :before => 'var form_data = Form.serialize("wiki_form", true); form_data._method="post";', diff --git a/app/views/wiki/edit_parent_page.html.erb b/app/views/wiki/edit_parent_page.html.erb index 27e9eefbf3..8c496d361c 100644 --- a/app/views/wiki/edit_parent_page.html.erb +++ b/app/views/wiki/edit_parent_page.html.erb @@ -31,7 +31,7 @@ See doc/COPYRIGHT.rdoc for more details. <%= error_messages_for 'page' %> -<%= labelled_tabular_form_for @page, :url => { :action => 'update_parent_page' } do |f| %> +<%= labelled_tabular_form_for @page, :url => { id: @page, action: 'update_parent_page' } do |f| %>

<%= f.select :parent_id, diff --git a/app/views/wiki/export_multiple.html.erb b/app/views/wiki/export_multiple.html.erb index a1f856654e..e2e8f049dd 100644 --- a/app/views/wiki/export_multiple.html.erb +++ b/app/views/wiki/export_multiple.html.erb @@ -49,7 +49,7 @@ h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display <%= l(:label_index_by_title) %>

diff --git a/app/views/wiki/history.html.erb b/app/views/wiki/history.html.erb index 7a5272b624..4c58c5bba0 100644 --- a/app/views/wiki/history.html.erb +++ b/app/views/wiki/history.html.erb @@ -47,7 +47,7 @@ See doc/COPYRIGHT.rdoc for more details. <% line_num = 1 %> <% @versions.each do |ver| %> "> - <%= link_to h(ver.version), :action => 'show', :id => @page.title, :project_id => @page.project, :version => ver.version %> + <%= link_to h(ver.version), :action => 'show', :id => @page, :project_id => @page.project, :version => ver.version %> <% if show_diff && (line_num < @versions.size) %> @@ -63,7 +63,7 @@ See doc/COPYRIGHT.rdoc for more details. <%= format_time(ver.created_at) %> <%= link_to_user ver.user %> <%=h ver.notes %> - <%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %> + <%= link_to l(:button_annotate), :action => 'annotate', :id => @page, :version => ver.version %> <% line_num += 1 %> <% end %> diff --git a/app/views/wiki/rename.html.erb b/app/views/wiki/rename.html.erb index d1dbe0c51b..07145106a7 100644 --- a/app/views/wiki/rename.html.erb +++ b/app/views/wiki/rename.html.erb @@ -31,7 +31,7 @@ See doc/COPYRIGHT.rdoc for more details. <%= error_messages_for 'page' %> -<%= labelled_tabular_form_for @page, :url => { :action => 'rename' }, :as => :wiki_page do |f| %> +<%= labelled_tabular_form_for @page, :url => { id: @page, action: 'rename' }, :as => :wiki_page do |f| %>

<%= f.text_field :title, :required => true, :size => 100 %>

<%= f.check_box :redirect_existing_links %>

diff --git a/app/views/wiki/show.html.erb b/app/views/wiki/show.html.erb index 68c052ec93..9a850f2f7d 100644 --- a/app/views/wiki/show.html.erb +++ b/app/views/wiki/show.html.erb @@ -30,44 +30,44 @@ See doc/COPYRIGHT.rdoc for more details. <%= call_hook :wiki_navigation %> <% content_for :action_menu_main do %> <% if @editable %> - <%= li_unless_nil(link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit))) if @content.version == @page.content.version %> + <%= li_unless_nil(link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page}, :class => 'icon icon-edit', :accesskey => accesskey(:edit))) if @content.version == @page.content.version %> <%= li_unless_nil(watcher_link(@page, User.current)) if Setting.notified_events.include?("wiki_content_added") or Setting.notified_events.include?("wiki_content_updated") %> <% end %> <% end %> <% content_for :action_menu_more do %> <% if @editable %> - <%= li_unless_nil(link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock')) if !@page.protected? %> - <%= li_unless_nil(link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock')) if @page.protected? %> + <%= li_unless_nil(link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page, :protected => 1}, :method => :post, :class => 'icon icon-lock')) if !@page.protected? %> + <%= li_unless_nil(link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page, :protected => 0}, :method => :post, :class => 'icon icon-unlock')) if @page.protected? %> <% if User.current.allowed_to? :edit_wiki_pages, @project %> <% if @page %> - <%= content_tag(:li, link_to(l(:create_child_page), wiki_new_child_path(:id => @page.title, :project_id => @project), :class => 'icon icon-duplicate')) %> + <%= content_tag(:li, link_to(l(:create_child_page), wiki_new_child_path(:id => @page, :project_id => @project), :class => 'icon icon-duplicate')) %> <% end %> <% end %> <% if @content.version == @page.content.version %> <%= li_unless_nil(link_to_if_authorized(t(:button_rename), - {:action => 'rename', :id => @page.title}, + {:action => 'rename', :id => @page}, :class => 'icon icon-rename')) %> <%= li_unless_nil(link_to_if_authorized(t(:button_change_parent_page), - {:action => 'edit_parent_page', :id => @page.title}, + {:action => 'edit_parent_page', :id => @page}, :class => 'icon icon-parent')) %> <% end %> - <%= li_unless_nil(link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del')) %> - <%= li_unless_nil(link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page.title, :version => @content.version }, :class => 'icon icon-cancel')) if @content.version < @page.content.version %> + <%= li_unless_nil(link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del')) %> + <%= li_unless_nil(link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page, :version => @content.version }, :class => 'icon icon-cancel')) if @content.version < @page.content.version %> <% end %> - <%= li_unless_nil(link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history')) %> - <%= li_unless_nil(link_to_if_authorized(l(:button_wiki_menu_entry), {:controller => '/wiki_menu_items', :action => 'edit', :project_id => @project.identifier, :id => @page.title}, :class => 'icon icon-configure')) %> + <%= li_unless_nil(link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page}, :class => 'icon icon-history')) %> + <%= li_unless_nil(link_to_if_authorized(l(:button_wiki_menu_entry), {:controller => '/wiki_menu_items', :action => 'edit', :project_id => @project.identifier, :id => @page}, :class => 'icon icon-configure')) %> <% end %> -<% breadcrumb_paths(*(@page.ancestors.reverse.collect {|parent| link_to h(parent.breadcrumb_title), {:id => parent.title, :project_id => parent.project}} + [h(@page.breadcrumb_title)])) %> +<% breadcrumb_paths(*(@page.ancestors.reverse.collect {|parent| link_to h(parent.breadcrumb_title), {:id => parent, :project_id => parent.project}} + [h(@page.breadcrumb_title)])) %> <% if @content.version != @page.content.version %>

- <%= link_to(l(:label_previous), { :action => 'show', :id => @page.title, :project_id => @page.project, :version => (@content.version - 1) }, :class => 'navigate-left') + " - " if @content.version > 1 %> + <%= link_to(l(:label_previous), { :action => 'show', :id => @page, :project_id => @page.project, :version => (@content.version - 1) }, :class => 'navigate-left') + " - " if @content.version > 1 %> <%= "#{Version.model_name.human} #{@content.version}/#{@page.content.version}" %> - <%= '(' + link_to(l(:label_diff), :controller => '/wiki', :action => 'diff', :id => @page.title, :project_id => @page.project, :version => @content.version) + ')' if @content.version > 1 %> - - <%= link_to(l(:label_next), :action => 'show', :id => @page.title, :project_id => @page.project, :version => (@content.version + 1), :class => 'navigate-right') + " - " if @content.version < @page.content.version %> - <%= link_to(l(:label_current_version), :action => 'show', :id => @page.title, :project_id => @page.project) %> + <%= '(' + link_to(l(:label_diff), :controller => '/wiki', :action => 'diff', :id => @page, :project_id => @page.project, :version => @content.version) + ')' if @content.version > 1 %> - + <%= link_to(l(:label_next), :action => 'show', :id => @page, :project_id => @page.project, :version => (@content.version + 1), :class => 'navigate-right') + " - " if @content.version < @page.content.version %> + <%= link_to(l(:label_current_version), :action => 'show', :id => @page, :project_id => @page.project) %>
<%= @content.author ? link_to_user(@content.author) : l(:label_user_anonymous) %>, <%= format_time(@content.updated_on) %>
<%=h @content.comments %> @@ -83,7 +83,7 @@ See doc/COPYRIGHT.rdoc for more details.

<%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;", :id => 'attach_files_link' %>

-<%= form_tag({ :controller => '/wiki', :action => 'add_attachment', :project_id => @project, :id => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %> +<%= form_tag({ :controller => '/wiki', :action => 'add_attachment', :project_id => @project, :id => @page }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>

<%= render :partial => 'attachments/form' %>

@@ -100,7 +100,7 @@ See doc/COPYRIGHT.rdoc for more details. :key => User.current.rss_key } %> <%= f.link_to 'HTML', :url => { :version => @content.version } %> <%= f.link_to 'TXT', :url => { :version => @content.version } %> - <%= call_hook(:view_wiki_show_other_formats, {:link_builder => f, :url_params => {:id => @page.title, :version => @content.version}}) %> + <%= call_hook(:view_wiki_show_other_formats, {:link_builder => f, :url_params => {:id => @page, :version => @content.version}}) %> <% end if User.current.allowed_to?(:export_wiki_pages, @project) %> <% content_for :header_tags do %> diff --git a/app/views/wiki_menu_items/edit.html.erb b/app/views/wiki_menu_items/edit.html.erb index facbcf11d1..cae5c856d4 100644 --- a/app/views/wiki_menu_items/edit.html.erb +++ b/app/views/wiki_menu_items/edit.html.erb @@ -29,10 +29,10 @@ See doc/COPYRIGHT.rdoc for more details. <%= error_messages_for "wiki_menu_item", :object => @wiki_menu_item %> -<% breadcrumb_paths(*@page.ancestors.reverse.collect {|parent| link_to h(parent.pretty_title), {:id => parent.title, :project_id => parent.project}}.push(@page.title)) %> +<% breadcrumb_paths(*@page.ancestors.reverse.collect {|parent| link_to h(parent.pretty_title), {:id => parent.title, :project_id => parent.project}}.push(@page)) %>

<%= l(:wiki_menu_item_for, :title => @page_title) %>

- <%= form_for @wiki_menu_item, :html => {:id => 'wiki_menu_item_form', :class => 'wiki_menu_item_form', :method => :put}, :url => wiki_menu_item_path(@project, @page_title) do |form| %> + <%= form_for @wiki_menu_item, :html => {:id => 'wiki_menu_item_form', :class => 'wiki_menu_item_form', :method => :put}, :url => wiki_menu_item_path(@project, @page) do |form| %>

<%= form.label :name, l(:label_menu_item_name), {:id => 'name_of_item'} %> <% if @wiki_menu_item.name.nil? %> From ed4ccc9637ad1c02d9800329eed225144168b569 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Wed, 13 Nov 2013 10:17:37 +0100 Subject: [PATCH 30/40] URL-escape special characters in wiki project sub menu Conflicts: lib/redmine/menu_manager/menu_helper.rb --- lib/redmine/menu_manager/menu_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/redmine/menu_manager/menu_helper.rb b/lib/redmine/menu_manager/menu_helper.rb index 178b51e007..40b22c6a2a 100644 --- a/lib/redmine/menu_manager/menu_helper.rb +++ b/lib/redmine/menu_manager/menu_helper.rb @@ -49,27 +49,27 @@ module Redmine::MenuManager::MenuHelper WikiMenuItem.main_items(project_wiki).each do |main_item| Redmine::MenuManager.loose :project_menu do |menu| menu.push "#{main_item.item_class}".to_sym, - { :controller => '/wiki', :action => 'show', :id => h(main_item.title) }, + { :controller => '/wiki', :action => 'show', :id => CGI.escape(main_item.title) }, :param => :project_id, :caption => main_item.name, :after => :repository menu.push :"#{main_item.item_class}_new_page", - { :action=>"new_child", :controller=>"/wiki", :id => h(main_item.title) }, + { :action=>"new_child", :controller=>"/wiki", :id => CGI.escape(main_item.title) }, :param => :project_id, :caption => :create_child_page, :parent => "#{main_item.item_class}".to_sym if main_item.new_wiki_page and WikiPage.find_by_wiki_id_and_title(project_wiki.id, main_item.title) menu.push :"#{main_item.item_class}_toc", - { :action => 'index', :controller => '/wiki', :id => h(main_item.title) }, + { :action => 'index', :controller => '/wiki', :id => CGI.escape(main_item.title) }, :param => :project_id, :caption => :label_table_of_contents, :parent => "#{main_item.item_class}".to_sym if main_item.index_page main_item.children.each do |child| menu.push "#{child.item_class}".to_sym, - { :controller => '/wiki', :action => 'show', :id => h(child.title) }, + { :controller => '/wiki', :action => 'show', :id => CGI.escape(child.title) }, :param => :project_id, :caption => child.name, :parent => "#{main_item.item_class}".to_sym From 55b13d4067d23f7639cfe9fc6dbfb40e18a87a84 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 13:03:03 +0100 Subject: [PATCH 31/40] Add wiki#new again since it's in the specs --- app/controllers/wiki_controller.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 559a9efa61..d6ac0ad6e0 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -57,7 +57,7 @@ class WikiController < ApplicationController :add_attachment, :list_attachments, :destroy] - before_filter :build_wiki_page_and_content, only: :create + before_filter :build_wiki_page_and_content, only: [:new, :create] verify :method => :post, :only => [:protect], :redirect_to => { :action => :show } verify :method => :get, :only => [:new, :new_child], :render => {:nothing => true, :status => :method_not_allowed} @@ -94,6 +94,9 @@ class WikiController < ApplicationController @pages_by_date = @pages.group_by {|p| p.updated_on.to_date} end + def new + end + def new_child find_existing_page return if performed? From fef78ca81234c47c8e1a24a771c63ed69ad5fbad Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 13:06:56 +0100 Subject: [PATCH 32/40] Respecify redirect after wiki#destroy actually wiki PAGES are destroyed, maybe tidy this controller and add a wiki pages controller --- app/controllers/wiki_controller.rb | 2 +- spec/controllers/wiki_controller_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index d6ac0ad6e0..c63c6f4119 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -298,7 +298,7 @@ class WikiController < ApplicationController end @page.destroy - if page = @wiki.start_page || @wiki.pages.first + if page = @wiki.find_page(@wiki.start_page) || @wiki.pages.first redirect_to :action => 'index', :project_id => @project, id: page else redirect_to project_path(@project) diff --git a/spec/controllers/wiki_controller_spec.rb b/spec/controllers/wiki_controller_spec.rb index fb5067216f..11e657d1e5 100644 --- a/spec/controllers/wiki_controller_spec.rb +++ b/spec/controllers/wiki_controller_spec.rb @@ -192,6 +192,7 @@ describe WikiController do describe 'successful action' do context 'when it is not the only wiki page' do let(:wiki) { @project.wiki } + let(:redirect_page_after_destroy) { wiki.find_page(wiki.start_page) || wiki.pages.first } before do another_wiki_page = FactoryGirl.create :wiki_page, wiki: wiki @@ -199,7 +200,7 @@ describe WikiController do it 'redirects to wiki#index' do delete :destroy, project_id: @project, id: @existing_page - response.should redirect_to action: 'index', project_id: @project + response.should redirect_to action: 'index', project_id: @project, id: redirect_page_after_destroy end end From 0df4823a0a83c9c97b29ff95a327def6184619ea Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 14:22:16 +0100 Subject: [PATCH 33/40] Fix tests - redirects after wiki#destroy, wiki index html --- test/functional/wiki_controller_test.rb | 24 ++++++++++++------- .../redmine/wiki_formatting/macros_test.rb | 20 ++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index c2cff1d141..3566892a50 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -43,6 +43,14 @@ class WikiControllerTest < ActionController::TestCase User.current = nil end + def wiki + Project.first.wiki + end + + def redirect_page + wiki.find_page(wiki.start_page) || wiki.pages.first + end + def test_show_start_page get :show, :project_id => 'ecookbook' assert_response :success @@ -97,7 +105,7 @@ class WikiControllerTest < ActionController::TestCase :content => {:comments => 'Created the page', :text => "h1. New page\n\nThis is a new page" } assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page' - page = Project.find(1).wiki.find_page('New page') + page = wiki.find_page('New page') assert !page.new_record? assert_not_nil page.content assert_equal 'Created the page', page.content.last_journal.notes @@ -115,7 +123,7 @@ class WikiControllerTest < ActionController::TestCase :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}} end end - page = Project.find(1).wiki.find_page('New page') + page = wiki.find_page('New page') assert_equal 1, page.attachments.count assert_equal 'testfile.txt', page.attachments.first.filename end @@ -326,7 +334,6 @@ class WikiControllerTest < ActionController::TestCase :wiki_page => { :title => 'Another renamed page', :redirect_existing_links => 1 } assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page' - wiki = Project.find(1).wiki # Check redirects assert_not_nil wiki.find_page('Another page') assert_nil wiki.find_page('Another page', :with_redirect => false) @@ -338,7 +345,6 @@ class WikiControllerTest < ActionController::TestCase :wiki_page => { :title => 'Another renamed page', :redirect_existing_links => "0" } assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page' - wiki = Project.find(1).wiki # Check that there's no redirects assert_nil wiki.find_page('Another page') end @@ -346,7 +352,7 @@ class WikiControllerTest < ActionController::TestCase def test_destroy_child @request.session[:user_id] = 2 delete :destroy, :project_id => 1, :id => 'Child_1' - assert_redirected_to :action => 'index', :project_id => 'ecookbook' + assert_redirected_to action: 'index', project_id: 'ecookbook', id: redirect_page end def test_destroy_parent @@ -363,7 +369,7 @@ class WikiControllerTest < ActionController::TestCase assert_difference('WikiPage.count', -1) do delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'nullify' end - assert_redirected_to :action => 'index', :project_id => 'ecookbook' + assert_redirected_to action: 'index', project_id: 'ecookbook', id: redirect_page assert_nil WikiPage.find_by_id(2) end @@ -372,7 +378,7 @@ class WikiControllerTest < ActionController::TestCase assert_difference('WikiPage.count', -3) do delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'destroy' end - assert_redirected_to :action => 'index', :project_id => 'ecookbook' + assert_redirected_to action: 'index', project_id: 'ecookbook', id: redirect_page assert_nil WikiPage.find_by_id(2) assert_nil WikiPage.find_by_id(5) end @@ -382,7 +388,7 @@ class WikiControllerTest < ActionController::TestCase assert_difference('WikiPage.count', -1) do delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'reassign', :reassign_to_id => 1 end - assert_redirected_to :action => 'index', :project_id => 'ecookbook' + assert_redirected_to action: 'index', project_id: 'ecookbook', id: redirect_page assert_nil WikiPage.find_by_id(2) assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent end @@ -393,7 +399,7 @@ class WikiControllerTest < ActionController::TestCase assert_template 'index' pages = assigns(:pages) assert_not_nil pages - assert_equal Project.find(1).wiki.pages.size, pages.size + assert_equal wiki.pages.size, pages.size assert_equal pages.first.content.updated_on, pages.first.updated_on assert_tag :ul, :attributes => { :class => 'pages-hierarchy' }, diff --git a/test/unit/lib/redmine/wiki_formatting/macros_test.rb b/test/unit/lib/redmine/wiki_formatting/macros_test.rb index 0785687970..af82dfc59d 100644 --- a/test/unit/lib/redmine/wiki_formatting/macros_test.rb +++ b/test/unit/lib/redmine/wiki_formatting/macros_test.rb @@ -73,10 +73,10 @@ class Redmine::WikiFormatting::MacrosTest < HelperTestCase end def test_macro_child_pages - expected = "

\n

" + expected = "

" @project = Project.find(1) # child pages of the current wiki page @@ -89,12 +89,12 @@ class Redmine::WikiFormatting::MacrosTest < HelperTestCase end def test_macro_child_pages_with_option - expected = "

\n

" + expected = "

" @project = Project.find(1) # child pages of the current wiki page From 564f6a0d29b638465a3e6dddfe24776714df3ed4 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 15:35:41 +0100 Subject: [PATCH 34/40] Fix wiki menu index after escaping page titles in links --- app/helpers/application_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c9d804807a..4d0c17b012 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -240,6 +240,8 @@ module ApplicationHelper end def render_page_hierarchy(pages, node=nil, options={}) + return '' unless pages[node] + content_tag :ul, class: 'pages-hierarchy' do pages[node].collect do |page| content_tag :li do From 6854a274f7c2791e02b667f8385081bf45511c16 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 15:35:56 +0100 Subject: [PATCH 35/40] Add change log entry (#2699) --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 29401e4a92..7c1dda3996 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -70,6 +70,7 @@ See doc/COPYRIGHT.rdoc for more details. * `#2586` Query available custom fields in REST API v2 * `#2637` Migrated timestamps to UTC * `#2696` CustomValues still associated to issues +* `#2699` [Wiki] 400 error when entering special character in wiki title * Reverts `#2645` Remove usage of eval() * Fix work package filter query validations From 806c7ffcd2e1073736721aca3c5b82b4f20ff66d Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 16:14:02 +0100 Subject: [PATCH 36/40] Fix 404 on exports of wiki pages with special characters... TODO Add a wiki_pages_controller, handle things more RESTful --- app/views/wiki/show.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/wiki/show.html.erb b/app/views/wiki/show.html.erb index 9a850f2f7d..2112950bf2 100644 --- a/app/views/wiki/show.html.erb +++ b/app/views/wiki/show.html.erb @@ -98,8 +98,8 @@ See doc/COPYRIGHT.rdoc for more details. :action => 'index', :show_wiki_edits => 1, :key => User.current.rss_key } %> - <%= f.link_to 'HTML', :url => { :version => @content.version } %> - <%= f.link_to 'TXT', :url => { :version => @content.version } %> + <%= f.link_to 'HTML', :url => { version: @content.version, id: @page } %> + <%= f.link_to 'TXT', :url => { version: @content.version, id: @page } %> <%= call_hook(:view_wiki_show_other_formats, {:link_builder => f, :url_params => {:id => @page, :version => @content.version}}) %> <% end if User.current.allowed_to?(:export_wiki_pages, @project) %> From 9bf0235d1e4a70f5c8543903e42d96e0b1a2a9d9 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Thu, 7 Nov 2013 16:17:29 +0100 Subject: [PATCH 37/40] Use escaped page title for anchor in wiki export --- app/views/wiki/export_multiple.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/wiki/export_multiple.html.erb b/app/views/wiki/export_multiple.html.erb index e2e8f049dd..5511104a7c 100644 --- a/app/views/wiki/export_multiple.html.erb +++ b/app/views/wiki/export_multiple.html.erb @@ -55,7 +55,7 @@ h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display <% @pages.each do |page| %>
- + <%= textilizable page.content ,:text, :wiki_links => :anchor %> <% end %> From 15982898d9f72eadad8e48e9e300934863fc9d57 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Fri, 8 Nov 2013 14:16:07 +0100 Subject: [PATCH 38/40] Add integration test for wiki child page creation with special characters in title --- app/controllers/wiki_controller.rb | 1 + features/wiki/wiki_create_child.feature | 56 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 features/wiki/wiki_create_child.feature diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index c63c6f4119..7f0ccdebcf 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -120,6 +120,7 @@ class WikiController < ApplicationController attachments = Attachment.attach_files(@page, params[:attachments]) render_attachment_warning_if_needed(@page) call_hook(:controller_wiki_edit_after_save, :params => params, :page => @page) + flash[:notice] = l(:notice_successful_create) redirect_to_show else render :action => 'new' diff --git a/features/wiki/wiki_create_child.feature b/features/wiki/wiki_create_child.feature new file mode 100644 index 0000000000..fa542278ad --- /dev/null +++ b/features/wiki/wiki_create_child.feature @@ -0,0 +1,56 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2013 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +Feature: Creating a wiki child page + + Background: + Given there are no wiki menu items + And there is 1 user with the following: + | login | bob | + And there is a role "member" + And the role "member" may have the following rights: + | view_wiki_pages | + | edit_wiki_pages | + And there is 1 project with the following: + | name | project1 | + | identifier | project1 | + And the user "bob" is a "member" in the project "project1" + And I am already logged in as "bob" + + @javascript + Scenario: Creating a wiki child page the title of which contains special characters + Given the project "project1" has 1 wiki page with the following: + | title | ParentWikiPage | + And the project "project1" has 1 wiki menu item with the following: + | title | ParentWikiPage | + | new_wiki_page | true | + When I go to the wiki new child page below the "ParentWikiPage" page of the project called "project1" + And I click "Create new child page" + And I fill in "page_title" with "Child Page !@#{$%^&*()_},./<>?;':" + And I click "Save" + Then I should see "Successful creation." From 476914cca6b9da24809d6f18619752070c8211cf Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Wed, 13 Nov 2013 10:19:29 +0100 Subject: [PATCH 39/40] added changelog entry for issue 2 wp redirect --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 29401e4a92..ac95ca123a 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -33,6 +33,7 @@ See doc/COPYRIGHT.rdoc for more details. ## 3.0.0pre30 +* Redirect old issue links to new work package URIs * `#2721` Fix: Fix: Fix: Fix: Missing journal entries for customizable_journals * `#2731` Migrated serialized yaml from syck to psych From 51a4d504c321cf242317d67add35eed88bb50de3 Mon Sep 17 00:00:00 2001 From: jwollert Date: Wed, 13 Nov 2013 10:20:14 +0100 Subject: [PATCH 40/40] move changelog entry for consistency with release tags --- doc/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 7c1dda3996..aacb0e8179 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -30,6 +30,7 @@ See doc/COPYRIGHT.rdoc for more details. # Changelog * Fix mysql data migrations +* `#2699` [Wiki] 400 error when entering special character in wiki title ## 3.0.0pre30 @@ -70,7 +71,6 @@ See doc/COPYRIGHT.rdoc for more details. * `#2586` Query available custom fields in REST API v2 * `#2637` Migrated timestamps to UTC * `#2696` CustomValues still associated to issues -* `#2699` [Wiki] 400 error when entering special character in wiki title * Reverts `#2645` Remove usage of eval() * Fix work package filter query validations