From 8c8d9b7e57ba262f4f54e8670aa6e7977e71134f Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 28 Apr 2014 13:08:04 +0200 Subject: [PATCH 01/36] Add cukes --- app/views/time_entries/reports/show.html.erb | 3 ++- features/issues/time_entries.feature | 18 ++++++++++++++++++ features/step_definitions/time_entry_steps.rb | 16 +++++++++++++--- features/support/paths.rb | 4 ++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/app/views/time_entries/reports/show.html.erb b/app/views/time_entries/reports/show.html.erb index 9fdb497799..d2afd8f575 100644 --- a/app/views/time_entries/reports/show.html.erb +++ b/app/views/time_entries/reports/show.html.erb @@ -47,7 +47,8 @@ See doc/COPYRIGHT.rdoc for more details. <%= render :partial => 'timelog/date_range' %> <%= render 'timelog/time_entry_tabs' %> -

: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], +

+ : <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], [l(:label_month), 'month'], [l(:label_week), 'week'], [l(:label_day_plural).titleize, 'day']], @columns), diff --git a/features/issues/time_entries.feature b/features/issues/time_entries.feature index 2f87d9945a..dfaf459f50 100644 --- a/features/issues/time_entries.feature +++ b/features/issues/time_entries.feature @@ -65,3 +65,21 @@ Feature: Tracking Time Then I should see a time entry with 4 hours and comment "updated test" And I should see a total spent time of 4 hours + @javascript + Scenario: Selecting time period + When I go to the time entry page of issue "issue1" + And I select "yesterday" from "period" + Then I should not see a total spent time of 0 hours + + When I click "Apply" + Then I should see a total spent time of 0 hours + + @javascript + Scenario: Selecting month for spent time report + When I go to the time entry report page of issue "issue1" + And I select "Year" from "Details" + And I select "Project" from "Add" + Then I should not see a total spent time of 4 hours + + When I click "Apply" within ".timelog-report-selection" + Then I should see a total spent time of 4 hours diff --git a/features/step_definitions/time_entry_steps.rb b/features/step_definitions/time_entry_steps.rb index 99e3df925d..7adf7b71bb 100644 --- a/features/step_definitions/time_entry_steps.rb +++ b/features/step_definitions/time_entry_steps.rb @@ -49,9 +49,19 @@ Then(/^I should see a time entry with (\d+) hours and comment "(.*)"$/) do |hour expect(page).to have_content(comment) end -Then(/^I should see a total spent time of (\d+) hours$/) do |hours| - within('div.total-hours') do - expect(find("span.hours-int")).to have_content hours +Then(/^I should (not )?see a total spent time of (\d+) hours$/) do |negative, hours| + available = find('div.total-hours') rescue false + + if available || !negative + within('div.total-hours') do + element = find("span.hours-int") + + if negative + expect(element).not_to have_content hours + else + expect(element).to have_content hours + end + end end end diff --git a/features/support/paths.rb b/features/support/paths.rb index 2159364587..3825997395 100644 --- a/features/support/paths.rb +++ b/features/support/paths.rb @@ -352,6 +352,10 @@ module NavigationHelpers issue_id = WorkPackage.find_by_subject($1).id "/work_packages/#{issue_id}/time_entries" + when /^the time entry report page of issue "(.+)"$/ + issue_id = WorkPackage.find_by_subject($1).id + "/work_packages/#{issue_id}/time_entries/report" + when /^the move new page of the work package "(.+)"$/ work_package_id = WorkPackage.find_by_subject($1).id "/work_packages/#{work_package_id}/move/new?copy=" From 98c3e37d19b798f6090aa3973db19765704390f9 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 28 Apr 2014 11:10:01 +0200 Subject: [PATCH 02/36] Prevent page reload on time period selection --- app/views/timelog/_date_range.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/timelog/_date_range.html.erb b/app/views/timelog/_date_range.html.erb index 8abd5fd010..f42e1a4da7 100644 --- a/app/views/timelog/_date_range.html.erb +++ b/app/views/timelog/_date_range.html.erb @@ -34,7 +34,6 @@ See doc/COPYRIGHT.rdoc for more details. <%= label_tag "period_type_list", l(:description_date_range_list), :class => "hidden-for-sighted" %> <%= radio_button_tag 'period_type', '1', !@free_period, :onclick => 'Form.Element.disable("from");Form.Element.disable("to");Form.Element.enable("period");', :id => "period_type_list"%> <%= select_tag 'period', options_for_period_select(params[:period]), - :onchange => 'this.form.submit();', :onfocus => '$("period_type_1").checked = true;', :disabled => @free_period %>

From ff046c826de9b8e26af44d1624089694c4f9a5b8 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 28 Apr 2014 11:10:21 +0200 Subject: [PATCH 03/36] Prevent page reload on time/column selection --- app/views/time_entries/reports/show.html.erb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/time_entries/reports/show.html.erb b/app/views/time_entries/reports/show.html.erb index d2afd8f575..23006d9749 100644 --- a/app/views/time_entries/reports/show.html.erb +++ b/app/views/time_entries/reports/show.html.erb @@ -51,14 +51,13 @@ See doc/COPYRIGHT.rdoc for more details. : <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], [l(:label_month), 'month'], [l(:label_week), 'week'], - [l(:label_day_plural).titleize, 'day']], @columns), - :onchange => "this.form.submit();" %> + [l(:label_day_plural).titleize, 'day']], @columns) %> : <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l_or_humanize(@available_criterias[k][:label]), k]}), - :onchange => "this.form.submit();", :style => 'width: 200px', :id => nil, :disabled => (@criterias.length >= 3), :id => "criterias") %> + <%= link_to_function l(:button_apply), '$("query_form").submit()', :class => 'icon icon-yes' %> <%= link_to l(:button_clear), {:project_id => @project, :issue_id => @issue, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns}, :class => 'icon icon-undo' %>

<% end %> From c146860e9288b7876880a0a68d40698b9a25dff0 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Thu, 12 Jun 2014 13:03:53 +0200 Subject: [PATCH 04/36] Fix mouse pointer for directory expander --- app/assets/stylesheets/scm.css.sass | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/scm.css.sass b/app/assets/stylesheets/scm.css.sass index 6f30903442..82414e3502 100644 --- a/app/assets/stylesheets/scm.css.sass +++ b/app/assets/stylesheets/scm.css.sass @@ -418,6 +418,7 @@ tr.dir &.dir-expander @include icon-common margin-right: -15px + cursor: pointer &:before content: "\e089" &.loading From 6a9d565689b00928a83ad6dda6acf2293eb351c7 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Thu, 12 Jun 2014 16:17:28 +0200 Subject: [PATCH 05/36] Created basic queries API endpoint & disabled experimental queries API endpoints --- config/routes.rb | 10 +++++----- lib/api/root.rb | 3 --- lib/api/v3/queries/queries_api.rb | 24 ++++++++++++++++++++++++ lib/api/v3/root.rb | 1 + 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 lib/api/v3/queries/queries_api.rb diff --git a/config/routes.rb b/config/routes.rb index 5d1cfcc9f3..13a21ade4b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -118,11 +118,11 @@ OpenProject::Application.routes.draw do get :column_data, on: :collection get :column_sums, on: :collection end - resources :queries, only: [:show, :create, :update, :destroy] do - get :available_columns, on: :collection - get :custom_field_filters, on: :collection - get :grouped, on: :collection - end + # resources :queries, only: [:show, :create, :update, :destroy] do + # get :available_columns, on: :collection + # get :custom_field_filters, on: :collection + # get :grouped, on: :collection + # end resources :projects, only: [:show, :index] do resources :work_packages, only: [:index] do diff --git a/lib/api/root.rb b/lib/api/root.rb index 983c2d9cfb..b8ccea4f76 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -38,12 +38,9 @@ module API format 'hal+json' helpers do - # Needs refactoring - Will have to find a way how to access sessions in all enviroments def current_user return User.current if Rails.env.test? - user_id = env['rack.session']['user_id'] - User.current = user_id ? User.find(user_id) : User.anonymous end diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb new file mode 100644 index 0000000000..e4adac60db --- /dev/null +++ b/lib/api/v3/queries/queries_api.rb @@ -0,0 +1,24 @@ +module API + module V3 + module Queries + class QueriesAPI < Grape::API + + resources :queries do + + params do + requires :id, desc: 'Query id' + end + namespace ':id' do + + get do + { query: 'query' }.to_json + end + + end + + end + + end + end + end +end diff --git a/lib/api/v3/root.rb b/lib/api/v3/root.rb index 9395a0f4e7..7c90637dae 100644 --- a/lib/api/v3/root.rb +++ b/lib/api/v3/root.rb @@ -37,6 +37,7 @@ module API version 'v3', using: :path mount API::V3::WorkPackages::WorkPackagesAPI + mount API::V3::Queries::QueriesAPI end end end From 49fa88258c6bc6ab66c06315e98d1741dd3b71df Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Thu, 12 Jun 2014 16:30:15 +0200 Subject: [PATCH 06/36] Created API query model --- lib/api/v3/queries/query_model.rb | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 lib/api/v3/queries/query_model.rb diff --git a/lib/api/v3/queries/query_model.rb b/lib/api/v3/queries/query_model.rb new file mode 100644 index 0000000000..a5ba53abaf --- /dev/null +++ b/lib/api/v3/queries/query_model.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'reform' +require 'reform/form/coercion' + +module API + module V3 + module Queries + class QueryModel < Reform::Form + include Composition + include Coercion + + model :query + + property :name, on: :query, type: String + property :project_id, on: :query, type: Integer + property :user_id, on: :query, type: Integer + property :filters, on: :query, type: String + property :is_public, on: :query, type: String + property :column_names, on: :query, type: String + property :sort_criteria, on: :query, type: String + property :group_by, on: :query, type: String + property :display_sums, on: :query, type: String + end + end + end +end From a87b90ad81e6e5f773a3377d6ab2b07dfc859813 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Thu, 12 Jun 2014 16:36:55 +0200 Subject: [PATCH 07/36] Created queries representer --- lib/api/v3/queries/query_representer.rb | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 lib/api/v3/queries/query_representer.rb diff --git a/lib/api/v3/queries/query_representer.rb b/lib/api/v3/queries/query_representer.rb new file mode 100644 index 0000000000..891cbb4831 --- /dev/null +++ b/lib/api/v3/queries/query_representer.rb @@ -0,0 +1,70 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + +require 'roar/decorator' +require 'roar/representer/json/hal' + +module API + module V3 + module Queries + class QueryRepresenter < Roar::Decorator + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include Rails.application.routes.url_helpers + + self.as_strategy = API::Utilities::CamelCasingStrategy.new + + property :_type, exec_context: :decorator + + link :self do + { href: "http://localhost:3000/api/v3/work_packages/#{represented.id}", title: "#{represented.name}" } + end + + property :id, getter: -> (*) { query.id }, render_nil: true + property :name, render_nil: true + property :project_id, getter: -> (*) { query.project.id } + property :project_name, getter: -> (*) { query.project.try(:name) } + property :user_id, getter: -> (*) { query.user.try(:id) }, render_nil: true + property :user_name, getter: -> (*) { query.user.try(:name) }, render_nil: true + property :user_login, getter: -> (*) { query.user.try(:login) }, render_nil: true + property :user_mail, getter: -> (*) { query.user.try(:mail) }, render_nil: true + property :filters, render_nil: true + property :is_public, render_nil: true + property :column_names, render_nil: true + property :sort_criteria, render_nil: true + property :group_by, render_nil: true + property :display_sums, render_nil: true + + def _type + "Query" + end + end + end + end +end From ecce489a9e64464b29b6ffda41145959b1375df7 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Fri, 13 Jun 2014 14:43:19 +0200 Subject: [PATCH 08/36] Created unstar action for queries --- config/routes.rb | 12 ++--- lib/api/v3/queries/queries_api.rb | 24 ++++++++- lib/api/v3/queries/query_representer.rb | 6 +-- lib/redmine.rb | 1 + spec/api/query_resource_spec.rb | 69 +++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 spec/api/query_resource_spec.rb diff --git a/config/routes.rb b/config/routes.rb index 13a21ade4b..233c648dff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -113,16 +113,16 @@ OpenProject::Application.routes.draw do end - namespace :v3 do + namespace :v3_experimental do resources :work_packages, only: [:index] do get :column_data, on: :collection get :column_sums, on: :collection end - # resources :queries, only: [:show, :create, :update, :destroy] do - # get :available_columns, on: :collection - # get :custom_field_filters, on: :collection - # get :grouped, on: :collection - # end + resources :queries, only: [:show, :create, :update, :destroy] do + get :available_columns, on: :collection + get :custom_field_filters, on: :collection + get :grouped, on: :collection + end resources :projects, only: [:show, :index] do resources :work_packages, only: [:index] do diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index e4adac60db..bdb3495c15 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -10,10 +10,30 @@ module API end namespace ':id' do - get do - { query: 'query' }.to_json + before do + @query = Query.find(params[:id]) + model = QueryModel.new(query: @query) + @representer = QueryRepresenter.new(model) end + patch :star do + # authorize(:queries_api, :patch, @query.project) + normalized_query_name = @query.name.parameterize.underscore + query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name + + if query_menu_item.valid? + query_menu_item.save! + @representer.to_json + else + raise ValidationError.new(query_menu_item) + end + end + + patch :unstar do + query_menu_item = @query.query_menu_item + query_menu_item.destroy + @representer.to_json + end end end diff --git a/lib/api/v3/queries/query_representer.rb b/lib/api/v3/queries/query_representer.rb index 891cbb4831..24c93b0216 100644 --- a/lib/api/v3/queries/query_representer.rb +++ b/lib/api/v3/queries/query_representer.rb @@ -43,7 +43,7 @@ module API property :_type, exec_context: :decorator link :self do - { href: "http://localhost:3000/api/v3/work_packages/#{represented.id}", title: "#{represented.name}" } + { href: "http://localhost:3000/api/v3/queries/#{represented.query.id}", title: "#{represented.name}" } end property :id, getter: -> (*) { query.id }, render_nil: true @@ -55,11 +55,11 @@ module API property :user_login, getter: -> (*) { query.user.try(:login) }, render_nil: true property :user_mail, getter: -> (*) { query.user.try(:mail) }, render_nil: true property :filters, render_nil: true - property :is_public, render_nil: true + property :is_public, getter: -> (*) { query.is_public.to_s }, render_nil: true property :column_names, render_nil: true property :sort_criteria, render_nil: true property :group_by, render_nil: true - property :display_sums, render_nil: true + property :display_sums, getter: -> (*) { query.display_sums.to_s }, render_nil: true def _type "Query" diff --git a/lib/redmine.rb b/lib/redmine.rb index e89770f3e6..a1ba536caf 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -109,6 +109,7 @@ Redmine::AccessControl.map do |map| :queries => [:index, :create, :update, :available_columns, :custom_field_filters, :grouped], :work_packages => [:show, :index], :work_packages_api => [:get], + :queries_api => [:get], :'work_packages/reports' => [:report, :report_details], :planning_elements => [:index, :all, :show, :recycle_bin], :planning_element_journals => [:index]} diff --git a/spec/api/query_resource_spec.rb b/spec/api/query_resource_spec.rb new file mode 100644 index 0000000000..12814ddff1 --- /dev/null +++ b/spec/api/query_resource_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' +require 'rack/test' + +describe 'API v3 Query resource' do + include Rack::Test::Methods + + let(:work_package) { FactoryGirl.create(:work_package, :project_id => project.id) } + let(:project) { FactoryGirl.create(:project, :identifier => 'test_project', :is_public => false) } + let(:current_user) { FactoryGirl.create(:user) } + let(:role) { FactoryGirl.create(:role, permissions: [:view_work_packages]) } + let(:unauthorize_user) { FactoryGirl.create(:user) } + let(:query) { FactoryGirl.create(:public_query) } + let(:private_query) { FactoryGirl.create(:private_query, project: project) } + + describe '#get' do + let(:get_path) { "/api/v3/queries/#{private_query.id}" } + let(:expected_response) do + { + "_type" => 'Query', + "_links" => { + "self" => { + "href" => "http://localhost:3000/api/v3/queries/#{private_query.id}", + "title" => private_query.name + } + }, + "id" => private_query.id, + "name" => private_query.name, + "projectId" => private_query.project_id, + "projectName" => private_query.project.name, + "userId" => private_query.user_id, + "userName" => private_query.user.try(:name), + "userLogin" => private_query.user.try(:login), + "userMail" => private_query.user.try(:mail), + "filters" => private_query.filters, + "isPublic" => private_query.is_public.to_s, + "columnNames" => private_query.column_names, + "sortCriteria" => private_query.sort_criteria, + "groupBy" => private_query.group_by, + "displaySums" => private_query.display_sums.to_s + } + end + + context 'accessing private queries' do + context 'when acting as a user with permission to view query' do + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: work_package.project) + member.role_ids = [role.id] + member.save! + get get_path + end + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should respond with work package in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response) + end + end + end + + end + + describe '#star' do + + end +end From 698d4dc7cc8df28548786f6c753db22480988303 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Fri, 13 Jun 2014 15:21:34 +0200 Subject: [PATCH 09/36] Template for tests --- config/routes.rb | 6 ++-- lib/api/v3/queries/queries_api.rb | 3 +- lib/redmine.rb | 2 +- spec/api/query_resource_spec.rb | 51 ++++--------------------------- 4 files changed, 11 insertions(+), 51 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 233c648dff..18f9e78587 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,7 +30,7 @@ OpenProject::Application.routes.draw do root :to => 'welcome#index', :as => 'home' - + mount API::Root => '/' rails_relative_url_root = OpenProject::Configuration['rails_relative_url_root'] || '' # Redirect deprecated issue links to new work packages uris @@ -113,7 +113,7 @@ OpenProject::Application.routes.draw do end - namespace :v3_experimental do + namespace :v3 do resources :work_packages, only: [:index] do get :column_data, on: :collection get :column_sums, on: :collection @@ -519,6 +519,4 @@ OpenProject::Application.routes.draw do match '/:controller(/:action(/:id))' match '/robots' => 'welcome#robots', :defaults => { :format => :txt } root :to => 'account#login' - - mount API::Root => '/' end diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index bdb3495c15..3ce67a060d 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -17,7 +17,7 @@ module API end patch :star do - # authorize(:queries_api, :patch, @query.project) + authorize(:queries_api, :star, @query.project) normalized_query_name = @query.name.parameterize.underscore query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name @@ -30,6 +30,7 @@ module API end patch :unstar do + authorize(:queries_api, :unstar, @query.project) query_menu_item = @query.query_menu_item query_menu_item.destroy @representer.to_json diff --git a/lib/redmine.rb b/lib/redmine.rb index a1ba536caf..3219c1871e 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -109,7 +109,7 @@ Redmine::AccessControl.map do |map| :queries => [:index, :create, :update, :available_columns, :custom_field_filters, :grouped], :work_packages => [:show, :index], :work_packages_api => [:get], - :queries_api => [:get], + :queries_api => [:star, :unstar], :'work_packages/reports' => [:report, :report_details], :planning_elements => [:index, :all, :show, :recycle_bin], :planning_element_journals => [:index]} diff --git a/spec/api/query_resource_spec.rb b/spec/api/query_resource_spec.rb index 12814ddff1..ef496fa5ee 100644 --- a/spec/api/query_resource_spec.rb +++ b/spec/api/query_resource_spec.rb @@ -12,58 +12,19 @@ describe 'API v3 Query resource' do let(:query) { FactoryGirl.create(:public_query) } let(:private_query) { FactoryGirl.create(:private_query, project: project) } - describe '#get' do - let(:get_path) { "/api/v3/queries/#{private_query.id}" } - let(:expected_response) do - { - "_type" => 'Query', - "_links" => { - "self" => { - "href" => "http://localhost:3000/api/v3/queries/#{private_query.id}", - "title" => private_query.name - } - }, - "id" => private_query.id, - "name" => private_query.name, - "projectId" => private_query.project_id, - "projectName" => private_query.project.name, - "userId" => private_query.user_id, - "userName" => private_query.user.try(:name), - "userLogin" => private_query.user.try(:login), - "userMail" => private_query.user.try(:mail), - "filters" => private_query.filters, - "isPublic" => private_query.is_public.to_s, - "columnNames" => private_query.column_names, - "sortCriteria" => private_query.sort_criteria, - "groupBy" => private_query.group_by, - "displaySums" => private_query.display_sums.to_s - } - end + describe '#star' do + context 'anonymous user' do - context 'accessing private queries' do - context 'when acting as a user with permission to view query' do - before(:each) do - allow(User).to receive(:current).and_return current_user - member = FactoryGirl.build(:member, user: current_user, project: work_package.project) - member.role_ids = [role.id] - member.save! - get get_path - end + end - it 'should respond with 200' do - last_response.status.should eq(200) - end + context 'user with permissions for the project' do - it 'should respond with work package in HAL+JSON format' do - parsed_response = JSON.parse(last_response.body) - parsed_response.should eq(expected_response) - end - end end + context 'user without permissions for the project' end - describe '#star' do + describe '#unstar' do end end From e7a3afb08f5cd9e29e8f427da0dabb941ae21b3a Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Fri, 13 Jun 2014 17:38:35 +0200 Subject: [PATCH 10/36] Added 'is_starred' property to the query --- lib/api/v3/queries/query_representer.rb | 6 ++++++ spec/api/query_resource_spec.rb | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/api/v3/queries/query_representer.rb b/lib/api/v3/queries/query_representer.rb index 24c93b0216..a7f68c3fb1 100644 --- a/lib/api/v3/queries/query_representer.rb +++ b/lib/api/v3/queries/query_representer.rb @@ -60,10 +60,16 @@ module API property :sort_criteria, render_nil: true property :group_by, render_nil: true property :display_sums, getter: -> (*) { query.display_sums.to_s }, render_nil: true + property :is_starred, getter: -> (*) { is_starred.to_s }, exec_context: :decorator def _type "Query" end + + def is_starred + return true if !represented.query.query_menu_item.nil? + false + end end end end diff --git a/spec/api/query_resource_spec.rb b/spec/api/query_resource_spec.rb index ef496fa5ee..560582acae 100644 --- a/spec/api/query_resource_spec.rb +++ b/spec/api/query_resource_spec.rb @@ -21,7 +21,9 @@ describe 'API v3 Query resource' do end - context 'user without permissions for the project' + context 'user without permissions for the project' do + + end end describe '#unstar' do From a94fbeb7a1a38d15d93c7a9fec3b8b5d8fe202ec Mon Sep 17 00:00:00 2001 From: Ion Biziiac Date: Fri, 13 Jun 2014 12:18:16 +0300 Subject: [PATCH 11/36] Fix text alignment consistency in the versions tables --- app/assets/stylesheets/default/main.css.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/default/main.css.erb b/app/assets/stylesheets/default/main.css.erb index 45cdb14c9f..21bab0ef38 100644 --- a/app/assets/stylesheets/default/main.css.erb +++ b/app/assets/stylesheets/default/main.css.erb @@ -121,9 +121,9 @@ tr.message td.created_on { white-space: nowrap; } tr.message td.last_message { white-space: nowrap; } tr.version td.closed, tr.version td.closed a { text-decoration: line-through; } -tr.version td.name { padding-left: 20px; } -tr.version.shared td.name { background: url(<%= asset_path 'link.png' %>) no-repeat 0% 70%; } -tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space: nowrap; } +tr.version.shared td.name { background: url(<%= asset_path 'link.png' %>) no-repeat 0% 70%; padding-left: 20px; } +tr.version td.date, tr.version td.status, tr.version td.sharing { white-space: nowrap; } +tr.version td { padding: 3px 6px; text-align: left; } tr.user td { width: 13%; } tr.user td.email { width: 18%; } From b516f6abbc79034c9fe5f76206b9fb64f9700244 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 16 Jun 2014 14:14:27 +0200 Subject: [PATCH 12/36] Fix expander and directory icon padding --- app/views/repositories/_dir_list_content.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/repositories/_dir_list_content.html.erb b/app/views/repositories/_dir_list_content.html.erb index 1ee98a6aef..3772013339 100644 --- a/app/views/repositories/_dir_list_content.html.erb +++ b/app/views/repositories/_dir_list_content.html.erb @@ -35,7 +35,7 @@ See doc/COPYRIGHT.rdoc for more details. <% if entry.is_dir? %> - + :class => (entry.is_dir? ? 'icon-context icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%> <%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> <% changeset = @project.repository.find_changeset_by_name(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> From 3b90a18fe1279f61b755de9ab415b32f5a13226a Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 16 Jun 2014 14:15:38 +0200 Subject: [PATCH 13/36] Fix expander icon's left's left margin --- app/assets/stylesheets/scm.css.sass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/scm.css.sass b/app/assets/stylesheets/scm.css.sass index 82414e3502..7508c51056 100644 --- a/app/assets/stylesheets/scm.css.sass +++ b/app/assets/stylesheets/scm.css.sass @@ -417,10 +417,11 @@ tr.dir span &.dir-expander @include icon-common - margin-right: -15px cursor: pointer &:before content: "\e089" + margin-left: 5px + padding: 0 &.loading span.dir-expander:before content: "\e07a" From d37b33aca5b9f5208c65e21495c3e18e272baf24 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 16 Jun 2014 14:16:07 +0200 Subject: [PATCH 14/36] Make filename indentation themable --- app/assets/stylesheets/default/main.css.erb | 1 - app/assets/stylesheets/global/_variables.sass | 2 ++ app/assets/stylesheets/scm.css.sass | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/default/main.css.erb b/app/assets/stylesheets/default/main.css.erb index 45cdb14c9f..f6b4dcff2b 100644 --- a/app/assets/stylesheets/default/main.css.erb +++ b/app/assets/stylesheets/default/main.css.erb @@ -101,7 +101,6 @@ tr.entry td.filename { width: 30%; } tr.entry td.size { text-align: right; font-size: 90%; } tr.entry td.revision, tr.entry td.author { text-align: center; } tr.entry td.age { text-align: right; } -tr.entry.file td.filename a { margin-left: 16px; } tr span.expander {cursor: pointer;} tr.open span .expand { display:none; } diff --git a/app/assets/stylesheets/global/_variables.sass b/app/assets/stylesheets/global/_variables.sass index 0b4b2b708a..8a90e0fab1 100644 --- a/app/assets/stylesheets/global/_variables.sass +++ b/app/assets/stylesheets/global/_variables.sass @@ -167,3 +167,5 @@ $wiki_toc_header_font_size: 10px !default $wiki_toc_ul_font_size: 0.8em !default $journal_attribute_font_size: 11px !default + +$repository_entry_filename_margin_left: 24px !default diff --git a/app/assets/stylesheets/scm.css.sass b/app/assets/stylesheets/scm.css.sass index 7508c51056..59ef018661 100644 --- a/app/assets/stylesheets/scm.css.sass +++ b/app/assets/stylesheets/scm.css.sass @@ -431,3 +431,11 @@ tr.dir &.open span.dir-expander:before content: "\e082" + +tr + &.entry + &.file + td + &.filename + a + margin-left: $repository_entry_filename_margin_left From 28cf8705a531a1c1aba8aa7ba4f8c936c2e8d889 Mon Sep 17 00:00:00 2001 From: Hagen Schink Date: Mon, 16 Jun 2014 14:31:33 +0200 Subject: [PATCH 15/36] Add changelog entry --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 2d2ecceb08..dc3353b7f1 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -31,6 +31,7 @@ See doc/COPYRIGHT.rdoc for more details. * `#4019` Fix focus on project creation * `#4021` Separate focus for main menu expander +* `#4258` Text alignment consistency in tables * `#6288` Editing relations in modal dialog leads to warning * `#7898` Watchers are not sorted alphabetically in work package screen From 97bfff11d033350a2cd1ca32a85c812eb0c08a40 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Mon, 16 Jun 2014 15:45:20 +0200 Subject: [PATCH 16/36] Added some tests and handled permissions for #star and #unstar methods --- lib/api/v3/queries/queries_api.rb | 12 +- lib/redmine.rb | 5 +- spec/api/query_resource_spec.rb | 361 +++++++++++++++++++++++++++++- 3 files changed, 365 insertions(+), 13 deletions(-) diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index 3ce67a060d..9647dcaad8 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -17,7 +17,10 @@ module API end patch :star do - authorize(:queries_api, :star, @query.project) + authorize(:queries, :star, @query.project) + if !@query.is_public? && @query.user_id != current_user.id + raise API::Errors::Unauthorized.new(current_user) + end normalized_query_name = @query.name.parameterize.underscore query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name @@ -30,9 +33,14 @@ module API end patch :unstar do - authorize(:queries_api, :unstar, @query.project) + authorize(:queries, :unstar, @query.project) + if !@query.is_public? && @query.user_id != current_user.id + raise API::Errors::Unauthorized.new(current_user) + end query_menu_item = @query.query_menu_item + return @representer.to_json if @query.query_menu_item.nil? query_menu_item.destroy + @query.reload @representer.to_json end end diff --git a/lib/redmine.rb b/lib/redmine.rb index 3219c1871e..a5154668bb 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -109,7 +109,6 @@ Redmine::AccessControl.map do |map| :queries => [:index, :create, :update, :available_columns, :custom_field_filters, :grouped], :work_packages => [:show, :index], :work_packages_api => [:get], - :queries_api => [:star, :unstar], :'work_packages/reports' => [:report, :report_details], :planning_elements => [:index, :all, :show, :recycle_bin], :planning_element_journals => [:index]} @@ -138,8 +137,8 @@ Redmine::AccessControl.map do |map| map.permission :manage_work_package_relations, {:work_package_relations => [:create, :destroy]} map.permission :manage_subtasks, {} # Queries - map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member - map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin + map.permission :manage_public_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :member + map.permission :save_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :loggedin # Watchers map.permission :view_work_package_watchers, {} map.permission :add_work_package_watchers, {:watchers => [:new, :create]} diff --git a/spec/api/query_resource_spec.rb b/spec/api/query_resource_spec.rb index 560582acae..706397f8cf 100644 --- a/spec/api/query_resource_spec.rb +++ b/spec/api/query_resource_spec.rb @@ -4,29 +4,374 @@ require 'rack/test' describe 'API v3 Query resource' do include Rack::Test::Methods - let(:work_package) { FactoryGirl.create(:work_package, :project_id => project.id) } let(:project) { FactoryGirl.create(:project, :identifier => 'test_project', :is_public => false) } let(:current_user) { FactoryGirl.create(:user) } - let(:role) { FactoryGirl.create(:role, permissions: [:view_work_packages]) } + let(:manage_public_queries_role) { FactoryGirl.create(:role, permissions: [:manage_public_queries]) } + let(:save_queries_role) { FactoryGirl.create(:role, permissions: [:save_queries]) } + let(:role_without_query_permissions) { FactoryGirl.create(:role, permissions: [:view_work_packages]) } let(:unauthorize_user) { FactoryGirl.create(:user) } - let(:query) { FactoryGirl.create(:public_query) } - let(:private_query) { FactoryGirl.create(:private_query, project: project) } describe '#star' do - context 'anonymous user' do - + let(:star_path) { "/api/v3/queries/#{query.id}/star" } + let(:filters) do + query.filters.map{ |f| {f.field.to_s => { "operator" => f.operator, "values" => f.values }}} + end + let(:expected_response) do + { + "_type" => 'Query', + "_links" => { + "self" => { + "href" => "http://localhost:3000/api/v3/queries/#{query.id}", + "title" => query.name + } + }, + "id" => query.id, + "name" => query.name, + "projectId" => query.project_id, + "projectName" => query.project.name, + "userId" => query.user_id, + "userName" => query.user.try(:name), + "userLogin" => query.user.try(:login), + "userMail" => query.user.try(:mail), + "filters" => filters, + "isPublic" => query.is_public.to_s, + "columnNames" => query.column_names, + "sortCriteria" => query.sort_criteria, + "groupBy" => query.group_by, + "displaySums" => query.display_sums.to_s, + "isStarred" => "true" + } end - context 'user with permissions for the project' do + describe 'public queries' do + let(:query) { FactoryGirl.create(:public_query, project: project) } + + context 'user with permission to manage public queries' do + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [manage_public_queries_role.id] + member.save! + end + + context 'when starring an unstarred query' do + before(:each) { patch star_path } + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response) + end + + it 'should return the query with "isStarred" property set to true' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('true') + end + end + + context 'when starring already starred query' do + before(:each) { patch star_path } + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response) + end + + it 'should return the query with "isStarred" property set to true' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('true') + end + end + + context 'when trying to star nonexistent query' do + let(:star_path) { "/api/v3/queries/999/star" } + before(:each) { patch star_path } + + it 'should respond with 404' do + last_response.status.should eq(404) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_found', 'messages' => ['Couldn\'t find Query with id=999']}]) + end + end + end + + context 'user without permission to manage public queries' do + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [role_without_query_permissions.id] + member.save! + patch star_path + end + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end end - context 'user without permissions for the project' do + describe 'private queries' do + context 'user with permission to save queries' do + let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [save_queries_role.id] + member.save! + patch star_path + end + + context 'starring his own query' do + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response) + end + + it 'should return the query with "isStarred" property set to true' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('true') + end + end + + context 'trying to star somebody else\'s query' do + let(:another_user) { FactoryGirl.create(:user) } + let(:query) { FactoryGirl.create(:private_query, project: project, user: another_user) } + + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end + end + + context 'user without permission to save queries' do + let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [role_without_query_permissions.id] + member.save! + patch star_path + end + + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end end end describe '#unstar' do + let(:unstar_path) { "/api/v3/queries/#{query.id}/unstar" } + let(:filters) do + query.filters.map{ |f| {f.field.to_s => { "operator" => f.operator, "values" => f.values }}} + end + let(:expected_response) do + { + "_type" => 'Query', + "_links" => { + "self" => { + "href" => "http://localhost:3000/api/v3/queries/#{query.id}", + "title" => query.name + } + }, + "id" => query.id, + "name" => query.name, + "projectId" => query.project_id, + "projectName" => query.project.name, + "userId" => query.user_id, + "userName" => query.user.try(:name), + "userLogin" => query.user.try(:login), + "userMail" => query.user.try(:mail), + "filters" => filters, + "isPublic" => query.is_public.to_s, + "columnNames" => query.column_names, + "sortCriteria" => query.sort_criteria, + "groupBy" => query.group_by, + "displaySums" => query.display_sums.to_s, + "isStarred" => "true" + } + end + + describe 'public queries' do + let(:query) { FactoryGirl.create(:public_query, project: project) } + + context 'user with permission to manage public queries' do + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [manage_public_queries_role.id] + member.save! + end + + context 'when unstarring a starred query' do + before(:each) do + FactoryGirl.create(:query_menu_item, query: query) + patch unstar_path + end + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) + end + + it 'should return the query with "isStarred" property set to false' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('false') + end + end + + context 'when unstarring an unstarred query' do + before(:each) { patch unstar_path } + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) + end + + it 'should return the query with "isStarred" property set to true' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('false') + end + + end + + context 'when trying to unstar nonexistent query' do + let(:unstar_path) { "/api/v3/queries/999/unstar" } + before(:each) { patch unstar_path } + + it 'should respond with 404' do + last_response.status.should eq(404) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_found', 'messages' => ['Couldn\'t find Query with id=999']}]) + end + end + end + + context 'user without permission to manage public queries' do + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [role_without_query_permissions.id] + member.save! + patch unstar_path + end + + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end + end + + describe 'private queries' do + context 'user with permission to save queries' do + let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [save_queries_role.id] + member.save! + patch unstar_path + end + + context 'unstarring his own query' do + + it 'should respond with 200' do + last_response.status.should eq(200) + end + + it 'should return the query in HAL+JSON format' do + parsed_response = JSON.parse(last_response.body) + parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) + end + + it 'should return the query with "isStarred" property set to true' do + parsed_response = JSON.parse(last_response.body) + parsed_response['isStarred'].should eq('false') + end + end + + context 'trying to unstar somebody else\'s query' do + let(:another_user) { FactoryGirl.create(:user) } + let(:query) { FactoryGirl.create(:private_query, project: project, user: another_user) } + + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end + end + + context 'user without permission to save queries' do + let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } + before(:each) do + allow(User).to receive(:current).and_return current_user + member = FactoryGirl.build(:member, user: current_user, project: project) + member.role_ids = [role_without_query_permissions.id] + member.save! + patch unstar_path + end + + it 'should respond with 403' do + last_response.status.should eq(403) + end + + it 'should respond with explanatory error message' do + parsed_errors = JSON.parse(last_response.body)['errors'] + parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) + end + end + end end end From 83974f95567deade95047e004c1f0b9cfe0cc10c Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Mon, 16 Jun 2014 17:45:36 +0200 Subject: [PATCH 17/36] Quick & dirty fix for queries starring and unstarring permissions --- lib/api/v3/queries/queries_api.rb | 16 ++++++++++------ lib/redmine.rb | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index 9647dcaad8..ceef4e92aa 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -17,9 +17,11 @@ module API end patch :star do - authorize(:queries, :star, @query.project) - if !@query.is_public? && @query.user_id != current_user.id - raise API::Errors::Unauthorized.new(current_user) + if !@query.is_public? + raise API::Errors::Unauthorized.new(current_user) unless @query.user_id == current_user.id + authorize(:queries, :star_private, @query.project) + else + authorize(:queries, :star, @query.project) end normalized_query_name = @query.name.parameterize.underscore query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name @@ -33,9 +35,11 @@ module API end patch :unstar do - authorize(:queries, :unstar, @query.project) - if !@query.is_public? && @query.user_id != current_user.id - raise API::Errors::Unauthorized.new(current_user) + if !@query.is_public? + raise API::Errors::Unauthorized.new(current_user) unless @query.user_id == current_user.id + authorize(:queries, :unstar_private, @query.project) + else + authorize(:queries, :unstar, @query.project) end query_menu_item = @query.query_menu_item return @representer.to_json if @query.query_menu_item.nil? diff --git a/lib/redmine.rb b/lib/redmine.rb index a5154668bb..b25f08ebe7 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -138,7 +138,7 @@ Redmine::AccessControl.map do |map| map.permission :manage_subtasks, {} # Queries map.permission :manage_public_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :member - map.permission :save_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :loggedin + map.permission :save_queries, {:queries => [:new, :edit, :star_private, :unstar_private, :destroy]}, :require => :loggedin # Watchers map.permission :view_work_package_watchers, {} map.permission :add_work_package_watchers, {:watchers => [:new, :create]} From b1c71ae15fbda9071d1e229cf88b9525d5dceee3 Mon Sep 17 00:00:00 2001 From: Michael Frister Date: Tue, 17 Jun 2014 13:33:44 +0200 Subject: [PATCH 18/36] Gemfile: Fetch representable via https --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index d0bd139c98..eeed1651ff 100644 --- a/Gemfile +++ b/Gemfile @@ -194,7 +194,7 @@ end # API gems gem 'grape', '~> 0.7.0' -gem 'representable', :github => 'finnlabs/representable' +gem 'representable', git: 'https://github.com/finnlabs/representable' gem 'roar', '~> 0.12.6' gem 'reform', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 52e38fcd26..63158834b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,12 +1,3 @@ -GIT - remote: git://github.com/finnlabs/representable.git - revision: 5f8fbcb1e61720699135c39be3f36a725ca870ad - specs: - representable (1.8.1) - multi_json - nokogiri - uber - GIT remote: https://github.com/Compass/compass-rails revision: f97a4f41518204683aeec7037da3d9b8c57ef4cb @@ -22,6 +13,15 @@ GIT rack-protection (1.5.2) rack +GIT + remote: https://github.com/finnlabs/representable + revision: 5f8fbcb1e61720699135c39be3f36a725ca870ad + specs: + representable (1.8.1) + multi_json + nokogiri + uber + GIT remote: https://github.com/finnlabs/rspec-example_disabler.git revision: deb9c38e3f4e3688724583ac1ff58e1ae8aba409 From 3cbe82aabfa4e6a292d1600994f78b3381912887 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Tue, 17 Jun 2014 14:01:45 +0200 Subject: [PATCH 19/36] fixes wiki font-size after bad resolved conflicts in ed93fb982d19efef28d --- app/assets/stylesheets/content/_wiki.sass | 3 ++- app/assets/stylesheets/global/_variables.sass | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/content/_wiki.sass b/app/assets/stylesheets/content/_wiki.sass index d58c026473..fcdbe4ca72 100644 --- a/app/assets/stylesheets/content/_wiki.sass +++ b/app/assets/stylesheets/content/_wiki.sass @@ -28,7 +28,7 @@ @import global/all div.wiki - font-size: $wiki_toc_ul_font_size + font-size: $wiki_default_font_size line-height: 1.6em h1, h2 margin: 1em 0 1em 0 @@ -62,6 +62,7 @@ div.wiki margin-right: 12px margin-left: 0 display: table + font-size: $wiki_toc_ul_font_size &.right float: right margin-left: 12px diff --git a/app/assets/stylesheets/global/_variables.sass b/app/assets/stylesheets/global/_variables.sass index 14384dbbcb..f4e12abe22 100644 --- a/app/assets/stylesheets/global/_variables.sass +++ b/app/assets/stylesheets/global/_variables.sass @@ -174,6 +174,7 @@ $my_page_edit_box_border_color: #06799F !default $action_menu_bg_color: #FFFFFF +$wiki_default_font_size: $global_font_size $wiki_toc_header_font_size: 10px !default $wiki_toc_ul_font_size: 0.8em !default From 7c86e9ee3dc60fea9d4e5c9fa10ffd5e9b30a37d Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Tue, 17 Jun 2014 15:17:35 +0200 Subject: [PATCH 20/36] Add blank option to group dialog drop down --- .../angular/controllers/dialogs/group-by.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/angular/controllers/dialogs/group-by.js b/app/assets/javascripts/angular/controllers/dialogs/group-by.js index b33c8d4c30..f1f3665daf 100644 --- a/app/assets/javascripts/angular/controllers/dialogs/group-by.js +++ b/app/assets/javascripts/angular/controllers/dialogs/group-by.js @@ -59,13 +59,20 @@ angular.module('openproject.workPackages.controllers') $scope.workPackageTableData = WorkPackagesTableService.getWorkPackagesTableData(); + function buildOptions() { + var blankOption = { id: null, label: ' ', other: null }; + + $scope.groupableColumnsData = $scope.groupableColumns.map(function(column){ + return { id: column.name, label: column.title, other: column.title }; + }); + $scope.groupableColumnsData.unshift(blankOption); + } + $scope.$watch('workPackageTableData.groupableColumns', function(groupableColumns){ if (!groupableColumns) return; $scope.groupableColumns = groupableColumns; - $scope.groupableColumnsData = groupableColumns.map(function(column){ - return { id: column.name, label: column.title, other: column.title }; - }); + buildOptions(); var currentGroupBy = $scope.groupableColumnsData.filter(function(column){ return column.id == QueryService.getGroupBy(); From 940655312ab085248ad74dcefb805a26862cd193 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 16:01:35 +0200 Subject: [PATCH 21/36] Refactor API authorize method --- lib/api/root.rb | 12 +++++++++-- lib/api/v3/queries/queries_api.rb | 21 ++++++++----------- lib/api/v3/work_packages/work_packages_api.rb | 2 +- lib/redmine.rb | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/api/root.rb b/lib/api/root.rb index b8ccea4f76..a223599621 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -45,11 +45,19 @@ module API end # Split into two methods: one for authentication, one for authorization - def authorize(api, endpoint, project = nil, projects = nil, global = false) + def authorize(api, endpoint, options) if current_user.nil? || current_user.anonymous? raise API::Errors::Unauthenticated.new end - is_authorized = AuthorizationService.new(api, endpoint, project, projects, global, current_user).perform + + if !options[:allow].nil? + raise API::Errors::Unauthorized.new(current_user) unless options[:allow] + end + + global = options[:global] || false + + is_authorized = AuthorizationService.new(api, endpoint, options[:project], options[:projects], + global, current_user).perform unless is_authorized raise API::Errors::Unauthorized.new(current_user) end diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index ceef4e92aa..b6394d2e83 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -16,13 +16,15 @@ module API @representer = QueryRepresenter.new(model) end - patch :star do - if !@query.is_public? - raise API::Errors::Unauthorized.new(current_user) unless @query.user_id == current_user.id - authorize(:queries, :star_private, @query.project) - else - authorize(:queries, :star, @query.project) + helpers do + def allowed_to_manage_stars? + (@query.is_public? && current_user.allowed_to?(:manage_public_queries, @query.project)) || + (!@query.is_public? && current_user.allowed_to?(:save_queries, @query.project) && @query.user_id == current_user.id) end + end + + patch :star do + authorize(:queries, :star, project: @query.project, allow: allowed_to_manage_stars?) normalized_query_name = @query.name.parameterize.underscore query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name @@ -35,12 +37,7 @@ module API end patch :unstar do - if !@query.is_public? - raise API::Errors::Unauthorized.new(current_user) unless @query.user_id == current_user.id - authorize(:queries, :unstar_private, @query.project) - else - authorize(:queries, :unstar, @query.project) - end + authorize(:queries, :unstar, project: @query.project, allow: allowed_to_manage_stars?) query_menu_item = @query.query_menu_item return @representer.to_json if @query.query_menu_item.nil? query_menu_item.destroy diff --git a/lib/api/v3/work_packages/work_packages_api.rb b/lib/api/v3/work_packages/work_packages_api.rb index 752f863ebf..95836be639 100644 --- a/lib/api/v3/work_packages/work_packages_api.rb +++ b/lib/api/v3/work_packages/work_packages_api.rb @@ -17,7 +17,7 @@ module API end get do - authorize(:work_packages_api, :get, @work_package.project) + authorize(:work_packages_api, :get, project: @work_package.project) @representer.to_json end diff --git a/lib/redmine.rb b/lib/redmine.rb index b25f08ebe7..a5154668bb 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -138,7 +138,7 @@ Redmine::AccessControl.map do |map| map.permission :manage_subtasks, {} # Queries map.permission :manage_public_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :member - map.permission :save_queries, {:queries => [:new, :edit, :star_private, :unstar_private, :destroy]}, :require => :loggedin + map.permission :save_queries, {:queries => [:new, :edit, :star, :unstar, :destroy]}, :require => :loggedin # Watchers map.permission :view_work_package_watchers, {} map.permission :add_work_package_watchers, {:watchers => [:new, :create]} From d01ef0584b507845b377309f6604b6b4a83f482a Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 16:04:24 +0200 Subject: [PATCH 22/36] Extract authenticate method from API authorize method --- lib/api/root.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/api/root.rb b/lib/api/root.rb index a223599621..05d3d4ecb1 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -44,11 +44,13 @@ module API User.current = user_id ? User.find(user_id) : User.anonymous end + def authenticate + raise API::Errors::Unauthenticated.new if current_user.nil? || current_user.anonymous? + end + # Split into two methods: one for authentication, one for authorization def authorize(api, endpoint, options) - if current_user.nil? || current_user.anonymous? - raise API::Errors::Unauthenticated.new - end + authenticate if !options[:allow].nil? raise API::Errors::Unauthorized.new(current_user) unless options[:allow] From d14f0258fc707f3aafd3f4a727cb99cfded8ad53 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 16:16:10 +0200 Subject: [PATCH 23/36] Refactoring of API authorize method --- lib/api/root.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/api/root.rb b/lib/api/root.rb index 05d3d4ecb1..a58f2d39db 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -48,21 +48,13 @@ module API raise API::Errors::Unauthenticated.new if current_user.nil? || current_user.anonymous? end - # Split into two methods: one for authentication, one for authorization def authorize(api, endpoint, options) - authenticate - - if !options[:allow].nil? + unless options[:allow].nil? raise API::Errors::Unauthorized.new(current_user) unless options[:allow] end - - global = options[:global] || false - is_authorized = AuthorizationService.new(api, endpoint, options[:project], options[:projects], - global, current_user).perform - unless is_authorized - raise API::Errors::Unauthorized.new(current_user) - end + !!options[:global], current_user).perform + raise API::Errors::Unauthorized.new(current_user) unless is_authorized is_authorized end end @@ -77,6 +69,11 @@ module API Rack::Response.new(not_found.to_json, not_found.code, not_found.headers).finish end + # run authentication before each request + before do + authenticate + end + mount API::V3::Root end end From 67b33cbb7d5866ea774a7d5543b4e21cece72342 Mon Sep 17 00:00:00 2001 From: Christian Ratz Date: Tue, 17 Jun 2014 16:30:00 +0200 Subject: [PATCH 24/36] change attachment journals description length --- ..._attachment_journals_description_length.rb | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 db/migrate/20130807083716_change_attachment_journals_description_length.rb diff --git a/db/migrate/20130807083716_change_attachment_journals_description_length.rb b/db/migrate/20130807083716_change_attachment_journals_description_length.rb new file mode 100644 index 0000000000..8abff5dc5f --- /dev/null +++ b/db/migrate/20130807083716_change_attachment_journals_description_length.rb @@ -0,0 +1,38 @@ +#-- encoding: UTF-8 +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2014 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. +#++ + + +class ChangeAttachmentJournalsDescriptionLength < ActiveRecord::Migration + def change + change_column :attachment_journals, + :description, + :text + end +end + From 41eb6f6cd4053a9c3fac02c05d43469f0ae25da4 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 16:34:22 +0200 Subject: [PATCH 25/36] Refactored exception handling --- lib/api/root.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/api/root.rb b/lib/api/root.rb index a58f2d39db..31926746b1 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -59,14 +59,14 @@ module API end end - rescue_from API::Errors::Validation, API::Errors::UnwritableProperty, API::Errors::Unauthorized, - API::Errors::Unauthenticated do |e| - Rack::Response.new(e.to_json, e.code, e.headers).finish - end - - rescue_from ActiveRecord::RecordNotFound do |e| - not_found = API::Errors::NotFound.new(e.message) - Rack::Response.new(not_found.to_json, not_found.code, not_found.headers).finish + rescue_from :all do |e| + case e.class.to_s + when 'API::Errors::Validation', 'API::Errors::UnwritableProperty', 'API::Errors::Unauthorized', 'API::Errors::Unauthenticated' + Rack::Response.new(e.to_json, e.code, e.headers).finish + when 'ActiveRecord::RecordNotFound' + not_found = API::Errors::NotFound.new(e.message) + Rack::Response.new(not_found.to_json, not_found.code, not_found.headers).finish + end end # run authentication before each request From 103845acc375c69163bd69ecf4cd93d02bb0563b Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Tue, 17 Jun 2014 16:37:22 +0200 Subject: [PATCH 26/36] Hotfix - always derive unused columns from cached columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit don’t cache them separately as state changes would need to refresh the unused columns array [ci skip] --- app/assets/javascripts/angular/services/query-service.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/assets/javascripts/angular/services/query-service.js b/app/assets/javascripts/angular/services/query-service.js index 7341aa6908..452d574e78 100644 --- a/app/assets/javascripts/angular/services/query-service.js +++ b/app/assets/javascripts/angular/services/query-service.js @@ -145,10 +145,6 @@ angular.module('openproject.services') }, loadAvailableUnusedColumns: function(projectIdentifier) { - if(availableUnusedColumns.length) { - return $q.when(availableUnusedColumns); - } - return QueryService.loadAvailableColumns(projectIdentifier) .then(function(available_columns) { availableUnusedColumns = WorkPackagesTableHelper.getColumnDifference(available_columns, QueryService.getSelectedColumns()); From d637354013ca775448e8985029cef8f9dc7607fe Mon Sep 17 00:00:00 2001 From: Ion Biziiac Date: Tue, 17 Jun 2014 16:30:14 +0300 Subject: [PATCH 27/36] Add specs for Fix: APIv2 does not rewire parents correctly --- .../v2/planning_elements_controller_spec.rb | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/controllers/api/v2/planning_elements_controller_spec.rb b/spec/controllers/api/v2/planning_elements_controller_spec.rb index a84e4c4792..1364bdd829 100644 --- a/spec/controllers/api/v2/planning_elements_controller_spec.rb +++ b/spec/controllers/api/v2/planning_elements_controller_spec.rb @@ -238,6 +238,29 @@ describe Api::V2::PlanningElementsController do end end + describe 'w/ cross-project relations' do + before do + Setting.stub(:cross_project_work_package_relations?).and_return(true) + end + + let!(:project1) { FactoryGirl.create(:project, :identifier => 'project-1') } + let!(:project2) { FactoryGirl.create(:project, :identifier => 'project-2') } + let!(:ticket_a) { FactoryGirl.create(:work_package, :id => 1, :project_id => project1.id) } + let!(:ticket_b) { FactoryGirl.create(:work_package, :id => 2, :project_id => project1.id, :parent_id => ticket_a.id) } + let!(:ticket_c) { FactoryGirl.create(:work_package, :id => 3, :project_id => project1.id, :parent_id => ticket_b.id) } + let!(:ticket_d) { FactoryGirl.create(:work_package, :id => 4, :project_id => project1.id) } + let!(:ticket_e) { FactoryGirl.create(:work_package, :id => 5, :project_id => project2.id, :parent_id => ticket_d.id) } + let!(:ticket_f) { FactoryGirl.create(:work_package, :id => 6, :project_id => project1.id, :parent_id => ticket_e.id) } + + become_admin { [project1, project2] } + + it 'rewires ancestors correctly' do + get 'index', project_id: project1.id, :format => 'xml' + + expect(assigns(:planning_elements).last.parent_id).to eq(ticket_d.id) + end + end + describe 'changed since' do let!(:work_package) do work_package = Timecop.travel(5.hours.ago) do From 4a13ce44c3a04ab60c0a20ae0ce60f994c3d25c7 Mon Sep 17 00:00:00 2001 From: Ion Biziiac Date: Tue, 17 Jun 2014 16:30:30 +0300 Subject: [PATCH 28/36] Fix APIv2 does not rewire parents correctly --- app/controllers/api/v2/planning_elements_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v2/planning_elements_controller.rb b/app/controllers/api/v2/planning_elements_controller.rb index b02d68be00..b7b9985624 100644 --- a/app/controllers/api/v2/planning_elements_controller.rb +++ b/app/controllers/api/v2/planning_elements_controller.rb @@ -321,7 +321,7 @@ module Api # re-wire the parent of this pe to the first ancestor found in the filtered set # re-wiring is only needed, when there is actually a parent, and the parent has been filtered out if pe.parent_id && !filtered_ids.include?(pe.parent_id) - ancestors = @planning_elements.select{|candidate| candidate.lft < pe.lft && candidate.rgt > pe.rgt } + ancestors = @planning_elements.select{|candidate| candidate.lft < pe.lft && candidate.rgt > pe.rgt && candidate.root_id == pe.root_id } # the greatest lower boundary is the first ancestor not filtered pe.parent_id = ancestors.empty? ? nil : ancestors.sort_by{|ancestor| ancestor.lft }.last.id end From 88b1c64b26cfebd2eb859922f3b48372142d08af Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 16:50:49 +0200 Subject: [PATCH 29/36] Allowed for admin to star / unstar any query --- lib/api/v3/queries/queries_api.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index b6394d2e83..01bc35d437 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -19,7 +19,8 @@ module API helpers do def allowed_to_manage_stars? (@query.is_public? && current_user.allowed_to?(:manage_public_queries, @query.project)) || - (!@query.is_public? && current_user.allowed_to?(:save_queries, @query.project) && @query.user_id == current_user.id) + (!@query.is_public? && (current_user.admin? || + (current_user.allowed_to?(:save_queries, @query.project) && @query.user_id == current_user.id))) end end From 751593b9d64facc9a8165d7fbfd358ec353fe75a Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Tue, 17 Jun 2014 17:10:50 +0200 Subject: [PATCH 30/36] Update angular context menu This fixes the column context menu when wp#index is update via state changes (when a query is switched) --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index b564140c32..a50b349091 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,7 @@ "jquery-migrate": "~1.2.1", "momentjs": "~2.6.0", "moment-timezone": "~0.0.6", - "angular-context-menu": "0.1.1" + "angular-context-menu": "0.1.2" }, "devDependencies": { "mocha": "~1.14.0", From 637db9c8dbb103c774e4574275fd078d2c533540 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Tue, 17 Jun 2014 17:18:02 +0200 Subject: [PATCH 31/36] =?UTF-8?q?Hotfix=20-=20Remove=20=E2=80=98Hide=20col?= =?UTF-8?q?umn=E2=80=99=20for=20id=20column=20from=20column=20context=20me?= =?UTF-8?q?nu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the ‘id’ column is not modeled appropriately on the backend-side but hard-coded in the template --- public/templates/work_packages/column_context_menu.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/work_packages/column_context_menu.html b/public/templates/work_packages/column_context_menu.html index afba424dea..7bf00d39d6 100644 --- a/public/templates/work_packages/column_context_menu.html +++ b/public/templates/work_packages/column_context_menu.html @@ -38,7 +38,7 @@ -
  • From 6eb061c01e78caeac7a2688fb7aa188c8f402bf4 Mon Sep 17 00:00:00 2001 From: Marek Takac Date: Tue, 17 Jun 2014 17:23:39 +0200 Subject: [PATCH 32/36] Simplified #star endpoint --- lib/api/root.rb | 3 +++ lib/api/v3/queries/queries_api.rb | 13 +++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/api/root.rb b/lib/api/root.rb index 31926746b1..dd74cc478d 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -66,6 +66,9 @@ module API when 'ActiveRecord::RecordNotFound' not_found = API::Errors::NotFound.new(e.message) Rack::Response.new(not_found.to_json, not_found.code, not_found.headers).finish + when 'ActiveRecord::RecordInvalid' + error = API::Errors::Validation.new(e.record) + Rack::Response.new(error.to_json, error.code, error.headers).finish end end diff --git a/lib/api/v3/queries/queries_api.rb b/lib/api/v3/queries/queries_api.rb index 01bc35d437..898c3012b5 100644 --- a/lib/api/v3/queries/queries_api.rb +++ b/lib/api/v3/queries/queries_api.rb @@ -27,14 +27,11 @@ module API patch :star do authorize(:queries, :star, project: @query.project, allow: allowed_to_manage_stars?) normalized_query_name = @query.name.parameterize.underscore - query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id normalized_query_name, @query.id, title: @query.name - - if query_menu_item.valid? - query_menu_item.save! - @representer.to_json - else - raise ValidationError.new(query_menu_item) - end + query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id( + normalized_query_name, @query.id, title: @query.name + ) + query_menu_item.save! + @representer.to_json end patch :unstar do From bd38e2ebeac61bb7a1c6855ca2b91ad932858895 Mon Sep 17 00:00:00 2001 From: Till Breuer Date: Tue, 17 Jun 2014 17:55:25 +0200 Subject: [PATCH 33/36] =?UTF-8?q?Remove=20drop=20down=20anchor=20right=20f?= =?UTF-8?q?rom=20drop=20down=20associated=20to=20=E2=80=98id=E2=80=99=20co?= =?UTF-8?q?lumn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui_components/has-dropdown-menu-directive.js | 11 +++++++++-- .../templates/work_packages/column_context_menu.html | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js index c3ad1f56b5..39e3d149d4 100644 --- a/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js +++ b/app/assets/javascripts/angular/ui_components/has-dropdown-menu-directive.js @@ -76,8 +76,8 @@ angular.module('openproject.uiComponents') ctrl.open(); contextMenu.open(locals) - .then(function(menuElement) { - menuElement.css(getCssPositionProperties(menuElement, element)); + .then(function(element) { + menuElement = element; }); } @@ -87,6 +87,10 @@ angular.module('openproject.uiComponents') contextMenu.close(); } + function positionDropdown() { + menuElement.css(getCssPositionProperties(menuElement, element)); + } + element.bind(triggerOnEvent, function(event) { event.preventDefault(); event.stopPropagation(); @@ -95,6 +99,9 @@ angular.module('openproject.uiComponents') toggle(); }); + // set css position parameters after the digest has been completed + if (contextMenu.active()) positionDropdown(); + scope.$root.$broadcast('openproject.markDropdownsAsClosed', element); }); diff --git a/public/templates/work_packages/column_context_menu.html b/public/templates/work_packages/column_context_menu.html index 7bf00d39d6..47b27fdece 100644 --- a/public/templates/work_packages/column_context_menu.html +++ b/public/templates/work_packages/column_context_menu.html @@ -1,4 +1,6 @@ -