From 2b73aaa15ad9f651f51f8c71de461da6664a4fbb Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Date: Sun, 14 Aug 2016 13:49:08 -0400 Subject: Allow to add deploy keys with write-access --- CHANGELOG | 1 + app/controllers/projects/deploy_keys_controller.rb | 2 +- app/views/projects/deploy_keys/_form.html.haml | 9 ++++++++ db/migrate/20160811172945_add_can_push_to_keys.rb | 27 ++++++++++++++++++++++ db/schema.rb | 21 +++++++++-------- lib/gitlab/git_access.rb | 17 ++++++++++---- spec/lib/gitlab/git_access_spec.rb | 11 +++++---- 7 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 db/migrate/20160811172945_add_can_push_to_keys.rb diff --git a/CHANGELOG b/CHANGELOG index 837e9e27aba..d3d0f79039f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -126,6 +126,7 @@ v 8.10.6 - Restore "Largest repository" sort option on Admin > Projects page. !5797 - Fix privilege escalation via project export. - Require administrator privileges to perform a project import. + - Allow to add deploy keys with write-access !5807 (Ali Ibrahim) v 8.10.5 - Add a data migration to fix some missing timestamps in the members table. !5670 diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 529e0aa2d33..77bd883a5bd 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -53,6 +53,6 @@ class Projects::DeployKeysController < Projects::ApplicationController end def deploy_key_params - params.require(:deploy_key).permit(:key, :title) + params.require(:deploy_key).permit(:key, :title, :can_push) end end diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 901605f7ca3..68f472e8886 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -10,4 +10,13 @@ %p.light.append-bottom-0 Paste a machine public key here. Read more about how to generate it = link_to "here", help_page_path("ssh/README") + .form-group + .checkbox + = f.label :can_push do + = f.check_box :can_push + %strong Allow write access + .form-group + %p.light.append-bottom-0 + Can this key be used to push to this repository? Deploy keys always have pull access + = f.submit "Add key", class: "btn-create btn" diff --git a/db/migrate/20160811172945_add_can_push_to_keys.rb b/db/migrate/20160811172945_add_can_push_to_keys.rb new file mode 100644 index 00000000000..de7d07ccabb --- /dev/null +++ b/db/migrate/20160811172945_add_can_push_to_keys.rb @@ -0,0 +1,27 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddCanPushToKeys < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + disable_ddl_transaction! + + def change + add_column_with_default(:keys, :can_push, :boolean, default: false, allow_null: false) + end +end diff --git a/db/schema.rb b/db/schema.rb index 445f484a8c7..d8c6922fb1d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160810142633) do +ActiveRecord::Schema.define(version: 20160811172945) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -484,6 +484,7 @@ ActiveRecord::Schema.define(version: 20160810142633) do t.string "type" t.string "fingerprint" t.boolean "public", default: false, null: false + t.boolean "can_push", default: false, null: false end add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree @@ -589,12 +590,12 @@ ActiveRecord::Schema.define(version: 20160810142633) do t.datetime "locked_at" t.integer "updated_by_id" t.string "merge_error" + t.text "merge_params" t.boolean "merge_when_build_succeeds", default: false, null: false t.integer "merge_user_id" t.string "merge_commit_sha" t.datetime "deleted_at" t.string "in_progress_merge_commit_sha" - t.text "merge_params" end add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree @@ -930,8 +931,8 @@ ActiveRecord::Schema.define(version: 20160810142633) do t.string "noteable_type" t.string "title" t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "submitted_as_ham", default: false, null: false end @@ -1001,13 +1002,13 @@ ActiveRecord::Schema.define(version: 20160810142633) do add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree create_table "user_agent_details", force: :cascade do |t| - t.string "user_agent", null: false - t.string "ip_address", null: false - t.integer "subject_id", null: false - t.string "subject_type", null: false + t.string "user_agent", null: false + t.string "ip_address", null: false + t.integer "subject_id", null: false + t.string "subject_type", null: false t.boolean "submitted", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 1882eb8d050..f208df3cdce 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -50,10 +50,13 @@ module Gitlab end def push_access_check(changes) + unless project.repository.exists? + return build_status_object(false, "A repository for this project does not exist yet.") + end if user user_push_access_check(changes) elsif deploy_key - build_status_object(false, "Deploy keys are not allowed to push code.") + deploy_key_push_access_check(changes) else raise 'Wrong actor' end @@ -72,10 +75,6 @@ module Gitlab return build_status_object(true) end - unless project.repository.exists? - return build_status_object(false, "A repository for this project does not exist yet.") - end - changes_list = Gitlab::ChangesList.new(changes) # Iterate over all changes to find if user allowed all of them to be applied @@ -90,6 +89,14 @@ module Gitlab build_status_object(true) end + def deploy_key_push_access_check(changes) + if actor.can_push? + build_status_object(true) + else + build_status_object(false, "The deploy key does not have write access to the project.") + end + end + def change_access_check(change) Checks::ChangeAccess.new(change, user_access: user_access, project: project).exec end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index f12c9a370f7..d0e73d70e6e 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -284,19 +284,22 @@ describe Gitlab::GitAccess, lib: true do end describe 'deploy key permissions' do - let(:key) { create(:deploy_key) } - let(:actor) { key } - context 'push code' do subject { access.check('git-receive-pack', '_any') } context 'when project is authorized' do + let(:key) { create(:deploy_key, can_push: true) } + let(:actor) { key } + before { key.projects << project } - it { expect(subject).not_to be_allowed } + it { expect(subject).to be_allowed } end context 'when unauthorized' do + let(:key) { create(:deploy_key, can_push: false) } + let(:actor) { key } + context 'to public project' do let(:project) { create(:project, :public) } -- cgit v1.2.1 From fcc2c43ebb32c0e9a3ae636e66460b42cbae4d53 Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Date: Thu, 18 Aug 2016 03:27:45 -0400 Subject: Added can_push attribute to deploy keys and update docs for API --- doc/api/deploy_keys.md | 19 +++++++++++++------ lib/api/entities.rb | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md index ca44afbf355..b3bcf0dec42 100644 --- a/doc/api/deploy_keys.md +++ b/doc/api/deploy_keys.md @@ -20,12 +20,14 @@ Example response: "id": 1, "title": "Public key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "can_push": false, "created_at": "2013-10-02T10:12:29Z" }, { "id": 3, "title": "Another Public key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "can_push": true, "created_at": "2013-10-02T11:12:29Z" } ] @@ -55,12 +57,14 @@ Example response: "id": 1, "title": "Public key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "can_push": false, "created_at": "2013-10-02T10:12:29Z" }, { "id": 3, "title": "Another Public key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "can_push": false, "created_at": "2013-10-02T11:12:29Z" } ] @@ -92,6 +96,7 @@ Example response: "id": 1, "title": "Public key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "can_push": false, "created_at": "2013-10-02T10:12:29Z" } ``` @@ -107,14 +112,15 @@ project only if original one was is accessible by the same user. POST /projects/:id/deploy_keys ``` -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of the project | -| `title` | string | yes | New deploy key's title | -| `key` | string | yes | New deploy key | +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project | +| `title` | string | yes | New deploy key's title | +| `key` | string | yes | New deploy key | +| `can_push` | boolean | no | Can deploy key push to the project's repository | ```bash -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA..."}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/" +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA...", "can_push": "true"}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/" ``` Example response: @@ -124,6 +130,7 @@ Example response: "key" : "ssh-rsa AAAA...", "id" : 12, "title" : "My deploy key", + "can_push": true, "created_at" : "2015-08-29T12:44:31.550Z" } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 055716ab1e3..5df65a2327d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -234,7 +234,7 @@ module API end class SSHKey < Grape::Entity - expose :id, :title, :key, :created_at + expose :id, :title, :key, :created_at, :can_push end class SSHKeyWithUser < SSHKey -- cgit v1.2.1 From 8bc7ffd4dceec118319aed680f41a8dbda275a06 Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Date: Fri, 19 Aug 2016 13:39:13 -0400 Subject: Rephrase wording for pull/push access --- app/views/projects/deploy_keys/_form.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 68f472e8886..757260aa5b5 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -14,9 +14,9 @@ .checkbox = f.label :can_push do = f.check_box :can_push - %strong Allow write access + %strong Write access allowed? .form-group %p.light.append-bottom-0 - Can this key be used to push to this repository? Deploy keys always have pull access + Allow this key to push to repository as well? (Default only allows pull access.) = f.submit "Add key", class: "btn-create btn" -- cgit v1.2.1 From 2811a213a6199978ca1f3eea002a7a77f8b87554 Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Date: Fri, 19 Aug 2016 14:40:45 -0400 Subject: added spacing --- lib/gitlab/git_access.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index f208df3cdce..f7432df5557 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -53,6 +53,7 @@ module Gitlab unless project.repository.exists? return build_status_object(false, "A repository for this project does not exist yet.") end + if user user_push_access_check(changes) elsif deploy_key -- cgit v1.2.1 From c3508851bff289fdaaa114298b3ae13513646775 Mon Sep 17 00:00:00 2001 From: Ali Ibrahim Date: Fri, 19 Aug 2016 19:31:40 -0400 Subject: Set current user as deploy key user --- app/controllers/projects/deploy_keys_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 77bd883a5bd..b094491e006 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -16,7 +16,7 @@ class Projects::DeployKeysController < Projects::ApplicationController end def create - @key = DeployKey.new(deploy_key_params) + @key = DeployKey.new(deploy_key_params.merge(user: current_user)) set_index_vars if @key.valid? && @project.deploy_keys << @key -- cgit v1.2.1 From 633f901d67e7a0c755b9fac92518ff1476595a11 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 10 Nov 2016 23:24:56 +0800 Subject: Update CHANGELOG --- changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml diff --git a/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml b/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml new file mode 100644 index 00000000000..0fd590a877b --- /dev/null +++ b/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml @@ -0,0 +1,4 @@ +--- +title: Allow to add deploy keys with write-access +merge_request: 5807 +author: Ali Ibrahim -- cgit v1.2.1 From 5b5722e9099d63652ec47fc5599217c348e0f9dc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 10 Nov 2016 23:28:15 +0800 Subject: Cleanup migration --- db/migrate/20160811172945_add_can_push_to_keys.rb | 25 ++++++----------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/db/migrate/20160811172945_add_can_push_to_keys.rb b/db/migrate/20160811172945_add_can_push_to_keys.rb index de7d07ccabb..5fd303fe8fb 100644 --- a/db/migrate/20160811172945_add_can_push_to_keys.rb +++ b/db/migrate/20160811172945_add_can_push_to_keys.rb @@ -1,27 +1,14 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. - class AddCanPushToKeys < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! - # Set this constant to true if this migration requires downtime. DOWNTIME = false - # When a migration requires downtime you **must** uncomment the following - # constant and define a short and easy to understand explanation as to why the - # migration requires downtime. - # DOWNTIME_REASON = '' - - # When using the methods "add_concurrent_index" or "add_column_with_default" - # you must disable the use of transactions as these methods can not run in an - # existing transaction. When using "add_concurrent_index" make sure that this - # method is the _only_ method called in the migration, any other changes - # should go in a separate migration. This ensures that upon failure _only_ the - # index creation fails and can be retried or reverted easily. - # - disable_ddl_transaction! - - def change + def up add_column_with_default(:keys, :can_push, :boolean, default: false, allow_null: false) end + + def down + remove_column(:keys, :can_push) + end end -- cgit v1.2.1 From 05cc87052a7755da3d352409ef3ab024921593c4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 20:40:28 +0800 Subject: Improve write access check for deploy key --- lib/gitlab/git_access.rb | 81 +++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 64b5c4b98dc..819e0657bdd 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -7,7 +7,7 @@ module Gitlab ERROR_MESSAGES = { upload: 'You are not allowed to upload code for this project.', download: 'You are not allowed to download code from this project.', - deploy_key: 'Deploy keys are not allowed to push code.', + deploy_key: 'This deploy key does not have write access to this project.', no_repo: 'A repository for this project does not exist yet.' } @@ -46,22 +46,18 @@ module Gitlab def download_access_check if user user_download_access_check - elsif deploy_key.nil? && !Guest.can?(:download_code, project) + elsif !Guest.can?(:download_code, project) raise UnauthorizedError, ERROR_MESSAGES[:download] end end def push_access_check(changes) - unless project.repository.exists? - return build_status_object(false, "A repository for this project does not exist yet.") - end - - if user - user_push_access_check(changes) - elsif deploy_key + if deploy_key deploy_key_push_access_check(changes) + elsif user + user_push_access_check(changes) else - raise UnauthorizedError, ERROR_MESSAGES[deploy_key ? :deploy_key : :upload] + raise UnauthorizedError, ERROR_MESSAGES[:upload] end end @@ -88,34 +84,19 @@ module Gitlab return # Allow access. end - unless project.repository.exists? - raise UnauthorizedError, ERROR_MESSAGES[:no_repo] - end - - changes_list = Gitlab::ChangesList.new(changes) - - # Iterate over all changes to find if user allowed all of them to be applied - changes_list.each do |change| - status = change_access_check(change) - unless status.allowed? - # If user does not have access to make at least one change - cancel all push - raise UnauthorizedError, status.message - end - end + check_repository_existence! + check_change_access!(changes) end def deploy_key_push_access_check(changes) - if actor.can_push? - build_status_object(true) + if deploy_key.can_push? + check_repository_existence! + check_change_access!(changes) else - build_status_object(false, "The deploy key does not have write access to the project.") + raise UnauthorizedError, ERROR_MESSAGES[:deploy_key] end end - def change_access_check(change) - Checks::ChangeAccess.new(change, user_access: user_access, project: project).exec - end - def protocol_allowed? Gitlab::ProtocolAccess.allowed?(protocol) end @@ -146,6 +127,27 @@ module Gitlab end end + def check_repository_existence! + unless project.repository.exists? + raise UnauthorizedError, ERROR_MESSAGES[:no_repo] + end + end + + def check_change_access!(changes) + changes_list = Gitlab::ChangesList.new(changes) + + # Iterate over all changes to find if user allowed all of them to be applied + changes_list.each do |change| + status = Checks::ChangeAccess.new(change, + user_access: user_access, + project: project).exec + unless status.allowed? + # If user does not have access to make at least one change - cancel all push + raise UnauthorizedError, status.message + end + end + end + def matching_merge_request?(newrev, branch_name) Checks::MatchingMergeRequest.new(newrev, branch_name, project).match? end @@ -154,20 +156,11 @@ module Gitlab actor if actor.is_a?(DeployKey) end - def deploy_key_can_read_project? - if deploy_key - return true if project.public? - deploy_key.projects.include?(project) - else - false - end - end - def can_read_project? - if user + if deploy_key + project.public? || deploy_key.projects.include?(project) + elsif user user_access.can_read_project? - elsif deploy_key - deploy_key_can_read_project? else Guest.can?(:read_project, project) end @@ -182,8 +175,6 @@ module Gitlab case actor when User actor - when DeployKey - nil when Key actor.user end -- cgit v1.2.1 From d0df9b7a4c1b246f4ddf22ce7609357f9c07da23 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 20:45:10 +0800 Subject: Remove old entry in CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e77cda75ccb..20907eef17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -879,7 +879,6 @@ entry. - Restore "Largest repository" sort option on Admin > Projects page. !5797 - Fix privilege escalation via project export. - Require administrator privileges to perform a project import. - - Allow to add deploy keys with write-access !5807 (Ali Ibrahim) ## 8.10.5 -- cgit v1.2.1 From 8b2426f898fdda47fc53f2c3d31c2060329430d4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 20:47:47 +0800 Subject: Mark DeployKey#can_push a safe attribute --- spec/lib/gitlab/import_export/safe_model_attributes.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 07a2c316899..aac61d404a8 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -246,6 +246,7 @@ DeployKey: - type - fingerprint - public +- can_push Service: - id - type @@ -339,4 +340,4 @@ LabelPriority: - label_id - priority - created_at -- updated_at \ No newline at end of file +- updated_at -- cgit v1.2.1 From 24d9f51e7bfbfa4a063b068377b100f6cde2b971 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 21:21:43 +0800 Subject: Correct the test. Not sure why change it in the first place --- spec/lib/gitlab/git_access_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 502ee9ce209..f1d0a190002 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -366,7 +366,7 @@ describe Gitlab::GitAccess, lib: true do context 'to public project' do let(:project) { create(:project, :public) } - it { expect(subject).to be_allowed } + it { expect(subject).not_to be_allowed } end context 'to internal project' do -- cgit v1.2.1 From 38fbcb99dba61cfae1b788e0ec947911c4d14dd8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 21:24:21 +0800 Subject: So deploy key might not have a corresponding user --- lib/gitlab/git_access.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 819e0657bdd..78f562821ea 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -46,7 +46,7 @@ module Gitlab def download_access_check if user user_download_access_check - elsif !Guest.can?(:download_code, project) + elsif deploy_key.nil? && !Guest.can?(:download_code, project) raise UnauthorizedError, ERROR_MESSAGES[:download] end end @@ -91,7 +91,7 @@ module Gitlab def deploy_key_push_access_check(changes) if deploy_key.can_push? check_repository_existence! - check_change_access!(changes) + check_change_access!(changes) if user else raise UnauthorizedError, ERROR_MESSAGES[:deploy_key] end -- cgit v1.2.1 From 71ae01fefe62caf396640affb8ca618fe68db5a0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 21:44:33 +0800 Subject: Add more tests and fix write to project check --- app/models/deploy_key.rb | 4 ++++ lib/gitlab/git_access.rb | 2 +- spec/lib/gitlab/git_access_spec.rb | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 2c525d4cd7a..503398f5cca 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -20,4 +20,8 @@ class DeployKey < Key def destroyed_when_orphaned? self.private? end + + def can_push_to?(project) + can_push? && projects.include?(project) + end end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 78f562821ea..96979398c83 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -89,7 +89,7 @@ module Gitlab end def deploy_key_push_access_check(changes) - if deploy_key.can_push? + if deploy_key.can_push_to?(project) check_repository_existence! check_change_access!(changes) if user else diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index f1d0a190002..ac5352a9561 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -353,13 +353,13 @@ describe Gitlab::GitAccess, lib: true do end end - shared_examples 'can not push code' do + shared_examples 'pushing code' do |can| subject { access.check('git-receive-pack', '_any') } context 'when project is authorized' do before { authorize } - it { expect(subject).not_to be_allowed } + it { expect(subject).public_send(can, be_allowed) } end context 'when unauthorized' do @@ -383,10 +383,20 @@ describe Gitlab::GitAccess, lib: true do end end + describe 'full authentication abilities' do + let(:authentication_abilities) { full_authentication_abilities } + + it_behaves_like 'pushing code', :to do + def authorize + project.team << [user, :developer] + end + end + end + describe 'build authentication abilities' do let(:authentication_abilities) { build_authentication_abilities } - it_behaves_like 'can not push code' do + it_behaves_like 'pushing code', :not_to do def authorize project.team << [user, :reporter] end @@ -394,12 +404,26 @@ describe Gitlab::GitAccess, lib: true do end describe 'deploy key permissions' do - let(:key) { create(:deploy_key) } + let(:key) { create(:deploy_key, can_push: can_push) } let(:actor) { key } - it_behaves_like 'can not push code' do - def authorize - key.projects << project + context 'when deploy_key can push' do + let(:can_push) { true } + + it_behaves_like 'pushing code', :to do + def authorize + key.projects << project + end + end + end + + context 'when deploy_key cannot push' do + let(:can_push) { false } + + it_behaves_like 'pushing code', :not_to do + def authorize + key.projects << project + end end end end -- cgit v1.2.1 From 40632455b83e78ec30af2eb93ced02afc5c4d4a0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 21:53:43 +0800 Subject: Fix a typo: acccess -> access --- spec/lib/gitlab/git_access_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index ac5352a9561..3c1df2199b2 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -82,7 +82,7 @@ describe Gitlab::GitAccess, lib: true do end end - describe 'without acccess to project' do + describe 'without access to project' do context 'pull code' do it { expect(subject.allowed?).to be_falsey } end -- cgit v1.2.1 From 5da9bfa453268474b3bff13c63e55b29bbcdf016 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 Nov 2016 22:26:05 +0800 Subject: Fix test for GitAccessWiki, it's overriding change_access_check --- lib/gitlab/git_access.rb | 9 ++++++--- lib/gitlab/git_access_wiki.rb | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 96979398c83..b462cffeaf6 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -138,9 +138,7 @@ module Gitlab # Iterate over all changes to find if user allowed all of them to be applied changes_list.each do |change| - status = Checks::ChangeAccess.new(change, - user_access: user_access, - project: project).exec + status = check_single_change_access(change) unless status.allowed? # If user does not have access to make at least one change - cancel all push raise UnauthorizedError, status.message @@ -148,6 +146,11 @@ module Gitlab end end + def check_single_change_access(change) + Checks::ChangeAccess.new( + change, user_access: user_access, project: project).exec + end + def matching_merge_request?(newrev, branch_name) Checks::MatchingMergeRequest.new(newrev, branch_name, project).match? end diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb index f71d3575909..f7976a64ef5 100644 --- a/lib/gitlab/git_access_wiki.rb +++ b/lib/gitlab/git_access_wiki.rb @@ -1,6 +1,6 @@ module Gitlab class GitAccessWiki < GitAccess - def change_access_check(change) + def check_single_change_access(change) if user_access.can_do_action?(:create_wiki) build_status_object(true) else -- cgit v1.2.1 From 721478123068c6718ec73c72a7b7d32c00c816df Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 16 Nov 2016 19:48:54 +0800 Subject: Also need to check against push rules: Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_18440615 --- lib/gitlab/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index b462cffeaf6..19bdfc878b1 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -91,7 +91,7 @@ module Gitlab def deploy_key_push_access_check(changes) if deploy_key.can_push_to?(project) check_repository_existence! - check_change_access!(changes) if user + check_change_access!(changes) else raise UnauthorizedError, ERROR_MESSAGES[:deploy_key] end -- cgit v1.2.1 From a9765fb47fbbd1e1070434fc06cc76b25a42caa6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 16 Nov 2016 20:31:08 +0800 Subject: Introduce has_access_to? so that we could reuse it Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_18439108 --- app/helpers/projects_helper.rb | 4 ++-- app/models/deploy_key.rb | 6 +++++- app/models/user.rb | 4 ++++ lib/gitlab/auth/result.rb | 3 +-- lib/gitlab/git_access.rb | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 42c00ec3cd5..5b08fc78cfc 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -93,10 +93,10 @@ module ProjectsHelper end def project_for_deploy_key(deploy_key) - if deploy_key.projects.include?(@project) + if deploy_key.has_access_to?(@project) @project else - deploy_key.projects.find { |project| can?(current_user, :read_project, project) } + deploy_key.projects.find(¤t_user.method(:has_access_to?)) end end diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 503398f5cca..aaacbd28470 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -21,7 +21,11 @@ class DeployKey < Key self.private? end + def has_access_to?(project) + projects.include?(project) + end + def can_push_to?(project) - can_push? && projects.include?(project) + can_push? && has_access_to?(project) end end diff --git a/app/models/user.rb b/app/models/user.rb index 3813df6684e..0e96ad88638 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -501,6 +501,10 @@ class User < ActiveRecord::Base several_namespaces? || admin end + def has_access_to?(project) + can?(:read_project, project) + end + def can?(action, subject) Ability.allowed?(self, action, subject) end diff --git a/lib/gitlab/auth/result.rb b/lib/gitlab/auth/result.rb index 6be7f690676..39b86c61a18 100644 --- a/lib/gitlab/auth/result.rb +++ b/lib/gitlab/auth/result.rb @@ -9,8 +9,7 @@ module Gitlab def lfs_deploy_token?(for_project) type == :lfs_deploy_token && - actor && - actor.projects.include?(for_project) + actor.try(:has_access_to?, for_project) end def success? diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 19bdfc878b1..a7ad944e79e 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -161,7 +161,7 @@ module Gitlab def can_read_project? if deploy_key - project.public? || deploy_key.projects.include?(project) + project.public? || deploy_key.has_access_to?(project) elsif user user_access.can_read_project? else -- cgit v1.2.1 From 48090a9188e13e3ddaffb5957a7b5a264024f060 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 16 Nov 2016 22:07:04 +0800 Subject: Introduce no_user_or_blocked? and fix tests due to checking user permission. --- lib/gitlab/user_access.rb | 16 ++++++++++++---- spec/lib/gitlab/git_access_spec.rb | 12 ++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb index 9858d2e7d83..6c7e673fb9f 100644 --- a/lib/gitlab/user_access.rb +++ b/lib/gitlab/user_access.rb @@ -8,6 +8,8 @@ module Gitlab end def can_do_action?(action) + return false if no_user_or_blocked? + @permission_cache ||= {} @permission_cache[action] ||= user.can?(action, project) end @@ -17,7 +19,7 @@ module Gitlab end def allowed? - return false if user.blank? || user.blocked? + return false if no_user_or_blocked? if user.requires_ldap_check? && user.try_obtain_ldap_lease return false unless Gitlab::LDAP::Access.allowed?(user) @@ -27,7 +29,7 @@ module Gitlab end def can_push_to_branch?(ref) - return false unless user + return false if no_user_or_blocked? if project.protected_branch?(ref) return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user) @@ -40,7 +42,7 @@ module Gitlab end def can_merge_to_branch?(ref) - return false unless user + return false if no_user_or_blocked? if project.protected_branch?(ref) access_levels = project.protected_branches.matching(ref).map(&:merge_access_levels).flatten @@ -51,9 +53,15 @@ module Gitlab end def can_read_project? - return false unless user + return false if no_user_or_blocked? user.can?(:read_project, project) end + + private + + def no_user_or_blocked? + user.nil? || user.blocked? + end end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 3c1df2199b2..9c19ea2d862 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -112,9 +112,13 @@ describe Gitlab::GitAccess, lib: true do end describe 'deploy key permissions' do - let(:key) { create(:deploy_key) } + let(:key) { create(:deploy_key, user: user) } let(:actor) { key } + before do + project.team << [user, :master] + end + context 'pull code' do context 'when project is authorized' do before { key.projects << project } @@ -404,9 +408,13 @@ describe Gitlab::GitAccess, lib: true do end describe 'deploy key permissions' do - let(:key) { create(:deploy_key, can_push: can_push) } + let(:key) { create(:deploy_key, user: user, can_push: can_push) } let(:actor) { key } + before do + project.team << [user, :master] + end + context 'when deploy_key can push' do let(:can_push) { true } -- cgit v1.2.1 From 2489332297b441b3ebc0c3df2e8ff14dc88a72cf Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 01:23:04 +0800 Subject: Don't notify user for deploy keys, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_18517263 --- app/controllers/admin/deploy_keys_controller.rb | 2 +- app/models/deploy_key.rb | 6 ++++++ app/models/key.rb | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb index 285e8495342..6b146712940 100644 --- a/app/controllers/admin/deploy_keys_controller.rb +++ b/app/controllers/admin/deploy_keys_controller.rb @@ -10,7 +10,7 @@ class Admin::DeployKeysController < Admin::ApplicationController end def create - @deploy_key = deploy_keys.new(deploy_key_params) + @deploy_key = deploy_keys.new(deploy_key_params.merge(user: current_user)) if @deploy_key.save redirect_to admin_deploy_keys_path diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index aaacbd28470..053f2a11aa0 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -28,4 +28,10 @@ class DeployKey < Key def can_push_to?(project) can_push? && has_access_to?(project) end + + private + + # we don't want to notify the user for deploy keys + def notify_user + end end diff --git a/app/models/key.rb b/app/models/key.rb index ff8dda2dc89..c0a64cfb5fc 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -49,10 +49,6 @@ class Key < ActiveRecord::Base ) end - def notify_user - run_after_commit { NotificationService.new.new_key(self) } - end - def post_create_hook SystemHooksService.new.execute_hooks_for(self, :create) end @@ -78,4 +74,8 @@ class Key < ActiveRecord::Base self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint end + + def notify_user + run_after_commit { NotificationService.new.new_key(self) } + end end -- cgit v1.2.1 From 8c1a01e05fd3c6e1621242aaf31a0ce2789ad546 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 03:48:23 +0800 Subject: We never check user privilege if it's a deploy key --- app/models/user.rb | 4 ---- lib/gitlab/checks/change_access.rb | 11 +++++++++-- lib/gitlab/git_access.rb | 29 +++++++++++++++++++---------- spec/lib/gitlab/git_access_spec.rb | 18 ------------------ 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 40130b8b25c..5a2b232c4ed 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -501,10 +501,6 @@ class User < ActiveRecord::Base several_namespaces? || admin end - def has_access_to?(project) - can?(:read_project, project) - end - def can?(action, subject) Ability.allowed?(self, action, subject) end diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb index cb1065223d4..6b6a86ffde9 100644 --- a/lib/gitlab/checks/change_access.rb +++ b/lib/gitlab/checks/change_access.rb @@ -1,13 +1,15 @@ module Gitlab module Checks class ChangeAccess - attr_reader :user_access, :project + attr_reader :user_access, :project, :skip_authorization - def initialize(change, user_access:, project:) + def initialize( + change, user_access:, project:, skip_authorization: false) @oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref) @branch_name = Gitlab::Git.branch_name(@ref) @user_access = user_access @project = project + @skip_authorization = skip_authorization end def exec @@ -23,6 +25,7 @@ module Gitlab protected def protected_branch_checks + return if skip_authorization return unless @branch_name return unless project.protected_branch?(@branch_name) @@ -48,6 +51,8 @@ module Gitlab end def tag_checks + return if skip_authorization + tag_ref = Gitlab::Git.tag_name(@ref) if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project) @@ -56,6 +61,8 @@ module Gitlab end def push_checks + return if skip_authorization + if user_access.cannot_do_action?(:push_code) "You are not allowed to push code to this project." end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index a7ad944e79e..6be0ab08a1f 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -27,7 +27,7 @@ module Gitlab def check(cmd, changes) check_protocol! - check_active_user! + check_active_user! unless deploy_key? check_project_accessibility! check_command_existence!(cmd) @@ -44,9 +44,13 @@ module Gitlab end def download_access_check - if user + if deploy_key + true + elsif user user_download_access_check - elsif deploy_key.nil? && !Guest.can?(:download_code, project) + elsif Guest.can?(:download_code, project) + true + else raise UnauthorizedError, ERROR_MESSAGES[:download] end end @@ -148,7 +152,10 @@ module Gitlab def check_single_change_access(change) Checks::ChangeAccess.new( - change, user_access: user_access, project: project).exec + change, + user_access: user_access, + project: project, + skip_authorization: deploy_key?).exec end def matching_merge_request?(newrev, branch_name) @@ -156,17 +163,19 @@ module Gitlab end def deploy_key - actor if actor.is_a?(DeployKey) + actor if deploy_key? + end + + def deploy_key? + actor.is_a?(DeployKey) end def can_read_project? if deploy_key - project.public? || deploy_key.has_access_to?(project) + deploy_key.has_access_to?(project) elsif user - user_access.can_read_project? - else - Guest.can?(:read_project, project) - end + user.can?(:read_project, project) + end || Guest.can?(:read_project, project) end protected diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 9c19ea2d862..9810d79f8b4 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -115,10 +115,6 @@ describe Gitlab::GitAccess, lib: true do let(:key) { create(:deploy_key, user: user) } let(:actor) { key } - before do - project.team << [user, :master] - end - context 'pull code' do context 'when project is authorized' do before { key.projects << project } @@ -387,16 +383,6 @@ describe Gitlab::GitAccess, lib: true do end end - describe 'full authentication abilities' do - let(:authentication_abilities) { full_authentication_abilities } - - it_behaves_like 'pushing code', :to do - def authorize - project.team << [user, :developer] - end - end - end - describe 'build authentication abilities' do let(:authentication_abilities) { build_authentication_abilities } @@ -411,10 +397,6 @@ describe Gitlab::GitAccess, lib: true do let(:key) { create(:deploy_key, user: user, can_push: can_push) } let(:actor) { key } - before do - project.team << [user, :master] - end - context 'when deploy_key can push' do let(:can_push) { true } -- cgit v1.2.1 From af0242b3d604cc14302f91f0dbe75af0048862d7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 03:56:35 +0800 Subject: We removed User#has_access_to? --- app/helpers/projects_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 144ac7a0d02..77075e49b17 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -93,7 +93,9 @@ module ProjectsHelper if deploy_key.has_access_to?(@project) @project else - deploy_key.projects.find(¤t_user.method(:has_access_to?)) + deploy_key.projects.find do |project| + can?(current_user, :read_project, project) + end end end -- cgit v1.2.1 From 0c532dbb243bf9bb5bf77248ce87a2a0e4478421 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 04:08:24 +0800 Subject: Check if the key could really download, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_18518792 --- lib/gitlab/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 6be0ab08a1f..d5690f870e9 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -45,7 +45,7 @@ module Gitlab def download_access_check if deploy_key - true + deploy_key.has_access_to?(project) elsif user user_download_access_check elsif Guest.can?(:download_code, project) -- cgit v1.2.1 From e72e2f9ba0a160960f68035fbbdbe3f0f86b0dba Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 04:11:04 +0800 Subject: Still grant :download_code if guest could do that Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_18518792 --- lib/gitlab/git_access.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index d5690f870e9..3f674532488 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -48,11 +48,9 @@ module Gitlab deploy_key.has_access_to?(project) elsif user user_download_access_check - elsif Guest.can?(:download_code, project) - true - else - raise UnauthorizedError, ERROR_MESSAGES[:download] - end + end || + Guest.can?(:download_code, project) || + raise(UnauthorizedError, ERROR_MESSAGES[:download]) end def push_access_check(changes) -- cgit v1.2.1 From 8dbea582497bbc45735cf145a3da4c88c9e0e78d Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 17:28:05 +0800 Subject: Check download privilege more specifically and add another error message for the new error. --- lib/gitlab/git_access.rb | 58 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 3f674532488..b87ca316240 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -7,7 +7,10 @@ module Gitlab ERROR_MESSAGES = { upload: 'You are not allowed to upload code for this project.', download: 'You are not allowed to download code from this project.', - deploy_key: 'This deploy key does not have write access to this project.', + deploy_key_upload: + 'This deploy key does not have write access to this project.', + deploy_key: + 'This deploy key does not have access to this project.', no_repo: 'A repository for this project does not exist yet.' } @@ -44,29 +47,36 @@ module Gitlab end def download_access_check - if deploy_key - deploy_key.has_access_to?(project) - elsif user - user_download_access_check - end || - Guest.can?(:download_code, project) || - raise(UnauthorizedError, ERROR_MESSAGES[:download]) + passed = if deploy_key + deploy_key.has_access_to?(project) + elsif user + user_can_download_code? || build_can_download_code? + end || Guest.can?(:download_code, project) + + unless passed + message = if deploy_key + ERROR_MESSAGES[:deploy_key] + else + ERROR_MESSAGES[:download] + end + + raise UnauthorizedError, message + end end def push_access_check(changes) if deploy_key - deploy_key_push_access_check(changes) + deploy_key_push_access_check elsif user - user_push_access_check(changes) + user_push_access_check else raise UnauthorizedError, ERROR_MESSAGES[:upload] end - end - def user_download_access_check - unless user_can_download_code? || build_can_download_code? - raise UnauthorizedError, ERROR_MESSAGES[:download] - end + return if changes.blank? # Allow access. + + check_repository_existence! + check_change_access!(changes) end def user_can_download_code? @@ -77,25 +87,15 @@ module Gitlab authentication_abilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code) end - def user_push_access_check(changes) + def user_push_access_check unless authentication_abilities.include?(:push_code) raise UnauthorizedError, ERROR_MESSAGES[:upload] end - - if changes.blank? - return # Allow access. - end - - check_repository_existence! - check_change_access!(changes) end - def deploy_key_push_access_check(changes) - if deploy_key.can_push_to?(project) - check_repository_existence! - check_change_access!(changes) - else - raise UnauthorizedError, ERROR_MESSAGES[:deploy_key] + def deploy_key_push_access_check + unless deploy_key.can_push_to?(project) + raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload] end end -- cgit v1.2.1 From a41ee7eb5eea96277ee18d474dc2defecb7f7596 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 18:37:32 +0800 Subject: Fix an old copypasta: internal -> private --- spec/lib/gitlab/git_access_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 9810d79f8b4..2c90397cc78 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -136,7 +136,7 @@ describe Gitlab::GitAccess, lib: true do end context 'from private project' do - let(:project) { create(:project, :internal) } + let(:project) { create(:project, :private) } it { expect(subject).not_to be_allowed } end -- cgit v1.2.1 From 428061678eda85a65ce6a9ee15ac520af45f021a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 18 Nov 2016 19:09:03 +0800 Subject: Add tests for key/deploy key notifications --- spec/models/deploy_key_spec.rb | 12 ++++++++++++ spec/models/key_spec.rb | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 93623e8e99b..3a2252ea40c 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -5,4 +5,16 @@ describe DeployKey, models: true do it { is_expected.to have_many(:deploy_keys_projects) } it { is_expected.to have_many(:projects) } end + + describe 'notification' do + let(:user) { create(:user) } + + it 'does not send a notification' do + perform_enqueued_jobs do + create(:deploy_key, user: user) + end + + should_not_email(user) + end + end end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 1a26cee9f3d..1260f0b5f42 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -92,4 +92,16 @@ describe Key, models: true do expect(described_class.new(key: " #{valid_key} ").key).to eq(valid_key) end end + + describe 'notification' do + let(:user) { create(:user) } + + it 'sends a notification' do + perform_enqueued_jobs do + create(:key, user: user) + end + + should_email(user) + end + end end -- cgit v1.2.1 From 28102ec28e1ef3d3203db3d05aa89ab3da234e70 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 22 Nov 2016 16:06:42 +0800 Subject: Allow admin to set keys with write access, and show write access information when showing the key. TODO: It's ugly right now, need help! --- app/controllers/admin/deploy_keys_controller.rb | 2 +- app/views/admin/deploy_keys/index.html.haml | 6 ++++++ app/views/admin/deploy_keys/new.html.haml | 8 ++++++++ app/views/projects/deploy_keys/_deploy_key.html.haml | 3 +++ features/admin/deploy_keys.feature | 9 ++++++++- features/steps/admin/deploy_keys.rb | 15 ++++++++++++++- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb index 6b146712940..4f6a7e9e2cb 100644 --- a/app/controllers/admin/deploy_keys_controller.rb +++ b/app/controllers/admin/deploy_keys_controller.rb @@ -39,6 +39,6 @@ class Admin::DeployKeysController < Admin::ApplicationController end def deploy_key_params - params.require(:deploy_key).permit(:key, :title) + params.require(:deploy_key).permit(:key, :title, :can_push) end end diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 149593e7f46..dee611ae014 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -11,6 +11,7 @@ %tr %th Title %th Fingerprint + %th Write access %th Added at %th %tbody @@ -20,6 +21,11 @@ %strong= deploy_key.title %td %code.key-fingerprint= deploy_key.fingerprint + %td + - if deploy_key.can_push? + Yes + - else + No %td %span.cgray added #{time_ago_with_tooltip(deploy_key.created_at)} diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml index 5c410a695bf..96055174ad0 100644 --- a/app/views/admin/deploy_keys/new.html.haml +++ b/app/views/admin/deploy_keys/new.html.haml @@ -16,6 +16,14 @@ Paste a machine public key here. Read more about how to generate it = link_to "here", help_page_path("ssh/README") = f.text_area :key, class: "form-control thin_area", rows: 5 + .form-group + .control-label + .col-sm-10 + = f.label :can_push do + = f.check_box :can_push + %strong Write access allowed? + %p.light.append-bottom-0 + Allow this key to push to repository as well? (Default only allows pull access.) .form-actions = f.submit 'Create', class: "btn-create btn" diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml index 450aaeb367c..d360f1bab28 100644 --- a/app/views/projects/deploy_keys/_deploy_key.html.haml +++ b/app/views/projects/deploy_keys/_deploy_key.html.haml @@ -6,6 +6,9 @@ = deploy_key.title .description = deploy_key.fingerprint + - if deploy_key.can_push? + .can-write + Can write .deploy-key-content.prepend-left-default.deploy-key-projects - deploy_key.projects.each do |project| - if can?(current_user, :read_project, project) diff --git a/features/admin/deploy_keys.feature b/features/admin/deploy_keys.feature index 33439cd1e85..95ac77cddd2 100644 --- a/features/admin/deploy_keys.feature +++ b/features/admin/deploy_keys.feature @@ -13,4 +13,11 @@ Feature: Admin Deploy Keys And I click 'New Deploy Key' And I submit new deploy key Then I should be on admin deploy keys page - And I should see newly created deploy key + And I should see newly created deploy key without write access + + Scenario: Deploy Keys new with write access + When I visit admin deploy keys page + And I click 'New Deploy Key' + And I submit new deploy key with write access + Then I should be on admin deploy keys page + And I should see newly created deploy key with write access diff --git a/features/steps/admin/deploy_keys.rb b/features/steps/admin/deploy_keys.rb index 56787eeb6b3..79312a5d1c5 100644 --- a/features/steps/admin/deploy_keys.rb +++ b/features/steps/admin/deploy_keys.rb @@ -32,12 +32,25 @@ class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps click_button "Create" end + step 'I submit new deploy key with write access' do + fill_in "deploy_key_title", with: "server" + fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop" + check "deploy_key_can_push" + click_button "Create" + end + step 'I should be on admin deploy keys page' do expect(current_path).to eq admin_deploy_keys_path end - step 'I should see newly created deploy key' do + step 'I should see newly created deploy key without write access' do + expect(page).to have_content(deploy_key.title) + expect(page).to have_content('No') + end + + step 'I should see newly created deploy key with write access' do expect(page).to have_content(deploy_key.title) + expect(page).to have_content('Yes') end def deploy_key -- cgit v1.2.1 From 86016960811e90ceecdd0353753107943cdea1d8 Mon Sep 17 00:00:00 2001 From: Pier Paolo Ramon Date: Fri, 2 Dec 2016 13:28:35 +0000 Subject: Minor white-space source formatting --- app/assets/stylesheets/pages/boards.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 82f36f24867..c55cecd57c8 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -31,7 +31,7 @@ .dropdown-content { max-height: 150px; - } + } } .issue-board-dropdown-content { -- cgit v1.2.1 From e19d4ce269b77f48a00547971ad3796affea9005 Mon Sep 17 00:00:00 2001 From: Pier Paolo Ramon Date: Fri, 2 Dec 2016 13:33:39 +0000 Subject: Extend the list header top border to the edge of the box With border board lists (those created from labels) the top border created bad visual artifacts with the .board-inner gray border. With this change the colored border overflows the padding-box of .board-inner and extends to the edges. It works very well with most colors, excepts for very light ones (such as `#ff0` yellow for instance). --- app/assets/stylesheets/pages/boards.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index c55cecd57c8..bff84b36eec 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -109,6 +109,12 @@ &.has-border { border-top: 3px solid; + margin-top: -1px; + margin-right: -1px; + margin-left: -1px; + padding-top: 1px; + padding-right: 1px; + padding-left: 1px; .board-title { padding-top: ($gl-padding - 3px); -- cgit v1.2.1 From 23f1f7f49fd404a7617f7e8ab5754c19099a3725 Mon Sep 17 00:00:00 2001 From: Pier Paolo Ramon Date: Fri, 2 Dec 2016 19:44:20 +0000 Subject: Add Changelog entry for !7898 --- .../7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml diff --git a/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml new file mode 100644 index 00000000000..e763194baf9 --- /dev/null +++ b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml @@ -0,0 +1,4 @@ +--- +title: Fixes issue boards list colored top border visual glitch +merge_request: 7898 +author: Pier Paolo Ramon \ No newline at end of file -- cgit v1.2.1 From c5664761778a5f530ee7a738f25da3c6742e3ab2 Mon Sep 17 00:00:00 2001 From: BM5k Date: Fri, 2 Dec 2016 18:05:02 -0700 Subject: rename charcoal theme variables for consistency --- app/assets/stylesheets/framework/gitlab-theme.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 5cd242af91d..642d1d15c14 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -84,10 +84,10 @@ } } -$theme-charcoal: #3d454d; -$theme-charcoal-light: #485157; -$theme-charcoal-dark: #383f45; -$theme-charcoal-text: #b9bbbe; +$theme-charcoal-light: #b9bbbe; +$theme-charcoal: #485157; +$theme-charcoal-dark: #3d454d; +$theme-charcoal-darker: #383f45; $theme-blue-light: #becde9; $theme-blue: #2980b9; @@ -120,7 +120,7 @@ body { } &.ui_charcoal { - @include gitlab-theme($theme-charcoal-text, $theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark); + @include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker); } &.ui_graphite { -- cgit v1.2.1 From bb1fd0566fc00a9c699a23dceb1e5f0aa7e9dbbe Mon Sep 17 00:00:00 2001 From: Pier Paolo Ramon Date: Sat, 3 Dec 2016 11:39:36 +0000 Subject: Fix Changelog entry for !7898 --- .../7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml index e763194baf9..74412c32375 100644 --- a/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml +++ b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml @@ -1,4 +1,4 @@ --- title: Fixes issue boards list colored top border visual glitch merge_request: 7898 -author: Pier Paolo Ramon \ No newline at end of file +author: Pier Paolo Ramon -- cgit v1.2.1 From 1b150576d9de9096796f76e1635d27ddaa2b1dd5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 6 Dec 2016 21:09:23 +0800 Subject: Prefer guest_can_downlod_code? --- lib/gitlab/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 9563fa7cafb..d42879e38cd 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -51,7 +51,7 @@ module Gitlab deploy_key.has_access_to?(project) elsif user user_can_download_code? || build_can_download_code? - end || Guest.can?(:download_code, project) + end || guest_can_downlod_code? unless passed message = if deploy_key -- cgit v1.2.1 From 46d7c1d2b3cf87023cb6bcf40dd53aa79606d203 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 6 Dec 2016 21:10:27 +0800 Subject: Prefer guest_can_download_code? and fix typo --- lib/gitlab/git_access.rb | 4 ++-- lib/gitlab/git_access_wiki.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index d42879e38cd..412f42c6320 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -51,7 +51,7 @@ module Gitlab deploy_key.has_access_to?(project) elsif user user_can_download_code? || build_can_download_code? - end || guest_can_downlod_code? + end || guest_can_download_code? unless passed message = if deploy_key @@ -79,7 +79,7 @@ module Gitlab check_change_access!(changes) end - def guest_can_downlod_code? + def guest_can_download_code? Guest.can?(:download_code, project) end diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb index 74171f4f90e..67eaa5e088d 100644 --- a/lib/gitlab/git_access_wiki.rb +++ b/lib/gitlab/git_access_wiki.rb @@ -1,6 +1,6 @@ module Gitlab class GitAccessWiki < GitAccess - def guest_can_downlod_code? + def guest_can_download_code? Guest.can?(:download_wiki_code, project) end -- cgit v1.2.1 From 5430122c56928849df44520353342150d8f37d51 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 6 Dec 2016 21:33:55 +0800 Subject: Now we need to include EmailHelpers for each test --- spec/models/deploy_key_spec.rb | 2 ++ spec/models/key_spec.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 3a2252ea40c..8ef8218cf74 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe DeployKey, models: true do + include EmailHelpers + describe "Associations" do it { is_expected.to have_many(:deploy_keys_projects) } it { is_expected.to have_many(:projects) } diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 0c0444e1f2a..c5a5467ca99 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Key, models: true do + include EmailHelpers + describe "Associations" do it { is_expected.to belong_to(:user) } end -- cgit v1.2.1 From 20b5f5af275a2b482baa0d2ff26e806517586c79 Mon Sep 17 00:00:00 2001 From: BM5k Date: Fri, 2 Dec 2016 18:06:30 -0700 Subject: rename graphite theme variables for consistency --- app/assets/stylesheets/framework/gitlab-theme.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 642d1d15c14..a8789ad9bf7 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -94,10 +94,10 @@ $theme-blue: #2980b9; $theme-blue-dark: #1970a9; $theme-blue-darker: #096099; -$theme-graphite-lighter: #ccc; -$theme-graphite-light: #777; -$theme-graphite: #666; -$theme-graphite-dark: #555; +$theme-graphite-light: #ccc; +$theme-graphite: #777; +$theme-graphite-dark: #666; +$theme-graphite-darker: #555; $theme-gray-light: #979797; $theme-gray: #373737; @@ -124,7 +124,7 @@ body { } &.ui_graphite { - @include gitlab-theme($theme-graphite-lighter, $theme-graphite-light, $theme-graphite, $theme-graphite-dark); + @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker); } &.ui_gray { -- cgit v1.2.1 From 6cb6f58a6259c91f79489b7f5a71f413d0e0d81c Mon Sep 17 00:00:00 2001 From: BM5k Date: Fri, 2 Dec 2016 18:44:06 -0700 Subject: rename theme to match actual colors --- app/assets/stylesheets/framework/gitlab-theme.scss | 12 ++++++------ app/assets/stylesheets/pages/profiles/preferences.scss | 4 ++-- lib/gitlab/themes.rb | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index a8789ad9bf7..1b52a6a8e71 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -99,10 +99,10 @@ $theme-graphite: #777; $theme-graphite-dark: #666; $theme-graphite-darker: #555; -$theme-gray-light: #979797; -$theme-gray: #373737; -$theme-gray-dark: #272727; -$theme-gray-darker: #222; +$theme-black-light: #979797; +$theme-black: #373737; +$theme-black-dark: #272727; +$theme-black-darker: #222; $theme-green-light: #adc; $theme-green: #019875; @@ -127,8 +127,8 @@ body { @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker); } - &.ui_gray { - @include gitlab-theme($theme-gray-light, $theme-gray, $theme-gray-dark, $theme-gray-darker); + &.ui_black { + @include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker); } &.ui_green { diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index f8da0983b77..100ace41f2a 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -22,8 +22,8 @@ background: $theme-graphite; } - &.ui_gray { - background: $theme-gray; + &.ui_black { + background: $theme-black; } &.ui_green { diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb index d4020af76f9..19ab76ae80f 100644 --- a/lib/gitlab/themes.rb +++ b/lib/gitlab/themes.rb @@ -15,7 +15,7 @@ module Gitlab Theme.new(1, 'Graphite', 'ui_graphite'), Theme.new(2, 'Charcoal', 'ui_charcoal'), Theme.new(3, 'Green', 'ui_green'), - Theme.new(4, 'Gray', 'ui_gray'), + Theme.new(4, 'Black', 'ui_black'), Theme.new(5, 'Violet', 'ui_violet'), Theme.new(6, 'Blue', 'ui_blue') ].freeze -- cgit v1.2.1 From 23f063e444ed308decf3ea42ef1d7fef76020fd6 Mon Sep 17 00:00:00 2001 From: BM5k Date: Sat, 3 Dec 2016 14:46:00 -0700 Subject: add changelog entry --- changelogs/unreleased/issues-8081.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/issues-8081.yml diff --git a/changelogs/unreleased/issues-8081.yml b/changelogs/unreleased/issues-8081.yml new file mode 100644 index 00000000000..82f746937bc --- /dev/null +++ b/changelogs/unreleased/issues-8081.yml @@ -0,0 +1,4 @@ +--- +title: change 'gray' color theme name to 'black' to match the actual color +merge_request: 7908 +author: BM5k -- cgit v1.2.1 From 57e3e942de1adef2c8621905370f07d7da7870c4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 10 Dec 2016 01:45:13 +0800 Subject: Don't pass the actor for deploy key, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_19579483 --- lib/gitlab/git_access.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 412f42c6320..d483038e8e9 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -189,6 +189,8 @@ module Gitlab case actor when User actor + when DeployKey + nil when Key actor.user end -- cgit v1.2.1 From 8ac50d78eb921989d100a91aad9eb6e59cbf43dc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 10 Dec 2016 02:04:36 +0800 Subject: Check project existence for push too, and we don't have to check for deploy key for downloading because deploy key could certainly download when it could already read the project. Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_19578626 --- lib/gitlab/git_access.rb | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index d483038e8e9..13efc1ed73d 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -9,8 +9,6 @@ module Gitlab download: 'You are not allowed to download code from this project.', deploy_key_upload: 'This deploy key does not have write access to this project.', - deploy_key: - 'This deploy key does not have access to this project.', no_repo: 'A repository for this project does not exist yet.' } @@ -33,10 +31,11 @@ module Gitlab check_active_user! unless deploy_key? check_project_accessibility! check_command_existence!(cmd) + check_repository_existence! case cmd when *DOWNLOAD_COMMANDS - download_access_check + download_access_check unless deploy_key? when *PUSH_COMMANDS push_access_check(changes) end @@ -47,20 +46,12 @@ module Gitlab end def download_access_check - passed = if deploy_key - deploy_key.has_access_to?(project) - elsif user - user_can_download_code? || build_can_download_code? - end || guest_can_download_code? + passed = user_can_download_code? || + build_can_download_code? || + guest_can_download_code? unless passed - message = if deploy_key - ERROR_MESSAGES[:deploy_key] - else - ERROR_MESSAGES[:download] - end - - raise UnauthorizedError, message + raise UnauthorizedError, ERROR_MESSAGES[:download] end end @@ -75,7 +66,6 @@ module Gitlab return if changes.blank? # Allow access. - check_repository_existence! check_change_access!(changes) end -- cgit v1.2.1 From f97965395198ef1da892dde246edb1e6ef480127 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 10 Dec 2016 02:25:31 +0800 Subject: Use consistent words, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_19581826 --- app/views/admin/deploy_keys/index.html.haml | 2 +- app/views/admin/deploy_keys/new.html.haml | 2 +- app/views/projects/deploy_keys/_deploy_key.html.haml | 4 ++-- app/views/projects/deploy_keys/_form.html.haml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index dee611ae014..0ba8e087e08 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -11,7 +11,7 @@ %tr %th Title %th Fingerprint - %th Write access + %th Write access allowed %th Added at %th %tbody diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml index 96055174ad0..a064efc231f 100644 --- a/app/views/admin/deploy_keys/new.html.haml +++ b/app/views/admin/deploy_keys/new.html.haml @@ -21,7 +21,7 @@ .col-sm-10 = f.label :can_push do = f.check_box :can_push - %strong Write access allowed? + %strong Write access allowed %p.light.append-bottom-0 Allow this key to push to repository as well? (Default only allows pull access.) diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml index d360f1bab28..d1e3cb14022 100644 --- a/app/views/projects/deploy_keys/_deploy_key.html.haml +++ b/app/views/projects/deploy_keys/_deploy_key.html.haml @@ -7,8 +7,8 @@ .description = deploy_key.fingerprint - if deploy_key.can_push? - .can-write - Can write + .write-access-allowed + Write access allowed .deploy-key-content.prepend-left-default.deploy-key-projects - deploy_key.projects.each do |project| - if can?(current_user, :read_project, project) diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 757260aa5b5..c91bb9c255a 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -14,7 +14,7 @@ .checkbox = f.label :can_push do = f.check_box :can_push - %strong Write access allowed? + %strong Write access allowed .form-group %p.light.append-bottom-0 Allow this key to push to repository as well? (Default only allows pull access.) -- cgit v1.2.1 From 6269f523f0f47e98d581d62e543b84185255454a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 10 Dec 2016 03:46:50 +0800 Subject: Fix tests and also add tests for non-existing repo --- spec/requests/git_http_spec.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index f1728d61def..c701b9a1202 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -371,12 +371,26 @@ describe 'Git HTTP requests', lib: true do shared_examples 'can download code only' do it 'downloads get status 200' do - clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token + allow_any_instance_of(Repository). + to receive(:exists?).and_return(true) + + clone_get "#{project.path_with_namespace}.git", + user: 'gitlab-ci-token', password: build.token expect(response).to have_http_status(200) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) end + it 'downloads from non-existing repository and gets 403' do + allow_any_instance_of(Repository). + to receive(:exists?).and_return(false) + + clone_get "#{project.path_with_namespace}.git", + user: 'gitlab-ci-token', password: build.token + + expect(response).to have_http_status(403) + end + it 'uploads get status 403' do push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token -- cgit v1.2.1 From f726dfba6269c68ce17a81a7f973e6b5e23964cb Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 12 Dec 2016 18:47:12 +0800 Subject: Use btn-inverted for New Deploy Key --- app/views/admin/deploy_keys/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 0ba8e087e08..646c9815cfe 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -3,7 +3,7 @@ .panel-heading Public deploy keys (#{@deploy_keys.count}) .controls - = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm" + = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm btn-inverted" - if @deploy_keys.any? .table-holder %table.table -- cgit v1.2.1 From ce867db6b8b1b317ebe864d36d50fde5aad787d4 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 12 Dec 2016 16:00:50 +0000 Subject: Adds CSS to match the mockups and makes table responsive --- app/assets/stylesheets/pages/admin.scss | 14 +++++++ app/views/admin/deploy_keys/index.html.haml | 63 +++++++++++++++-------------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index 44eac21b143..23b69e4d16f 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -166,3 +166,17 @@ min-width: 120px; } } + +.deploy-keys-list { + width: 100%; + overflow: auto; + + table { + border: 1px solid $table-border-color; + } +} + +.deploy-keys-title { + padding-bottom: 2px; + line-height: 2; +} diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 646c9815cfe..7b71bb5b287 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -1,33 +1,34 @@ - page_title "Deploy Keys" -.panel.panel-default.prepend-top-default - .panel-heading - Public deploy keys (#{@deploy_keys.count}) - .controls - = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm btn-inverted" - - if @deploy_keys.any? - .table-holder - %table.table - %thead.panel-heading + +%h3.page-title.deploy-keys-title + Public deploy keys (#{@deploy_keys.count}) + .pull-right + = link_to 'New Deploy Key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted' + +- if @deploy_keys.any? + .table-holder.deploy-keys-list + %table.table + %thead + %tr + %th.col-sm-2 Title + %th.col-sm-4 Fingerprint + %th.col-sm-2 Write access allowed + %th.col-sm-2 Added at + %th.col-sm-2 + %tbody + - @deploy_keys.each do |deploy_key| %tr - %th Title - %th Fingerprint - %th Write access allowed - %th Added at - %th - %tbody - - @deploy_keys.each do |deploy_key| - %tr - %td - %strong= deploy_key.title - %td - %code.key-fingerprint= deploy_key.fingerprint - %td - - if deploy_key.can_push? - Yes - - else - No - %td - %span.cgray - added #{time_ago_with_tooltip(deploy_key.created_at)} - %td - = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right" + %td + %strong= deploy_key.title + %td + %code.key-fingerprint= deploy_key.fingerprint + %td + - if deploy_key.can_push? + Yes + - else + No + %td + %span.cgray + added #{time_ago_with_tooltip(deploy_key.created_at)} + %td + = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key pull-right' -- cgit v1.2.1 From aac4aa10949b89278ea40dcfb2b45ded1f24f167 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 14 Dec 2016 00:51:53 -0600 Subject: ensure classes declared outside an IIFE are properties of window --- app/assets/javascripts/boards/models/issue.js.es6 | 2 ++ app/assets/javascripts/boards/models/label.js.es6 | 2 ++ app/assets/javascripts/boards/models/list.js.es6 | 2 ++ app/assets/javascripts/boards/models/milestone.js.es6 | 2 ++ app/assets/javascripts/boards/models/user.js.es6 | 2 ++ app/assets/javascripts/boards/services/board_service.js.es6 | 4 +++- app/assets/javascripts/diff_notes/models/discussion.js.es6 | 2 ++ app/assets/javascripts/diff_notes/models/note.js.es6 | 2 ++ .../javascripts/environments/services/environments_service.js.es6 | 2 ++ .../javascripts/protected_branches/protected_branch_dropdown.js.es6 | 2 ++ 10 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index 21d735e8231..c1af4954af1 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -66,3 +66,5 @@ class ListIssue { return Vue.http.patch(url, data); } } + +window.ListIssue = ListIssue; diff --git a/app/assets/javascripts/boards/models/label.js.es6 b/app/assets/javascripts/boards/models/label.js.es6 index 0910fe9a854..dcb78ce86f9 100644 --- a/app/assets/javascripts/boards/models/label.js.es6 +++ b/app/assets/javascripts/boards/models/label.js.es6 @@ -9,3 +9,5 @@ class ListLabel { this.priority = (obj.priority !== null) ? obj.priority : Infinity; } } + +window.ListLabel = ListLabel; diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6 index 429bd27c3fb..967b6fbd08f 100644 --- a/app/assets/javascripts/boards/models/list.js.es6 +++ b/app/assets/javascripts/boards/models/list.js.es6 @@ -145,3 +145,5 @@ class List { }); } } + +window.List = List; diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6 index a48969e19c9..2121e512796 100644 --- a/app/assets/javascripts/boards/models/milestone.js.es6 +++ b/app/assets/javascripts/boards/models/milestone.js.es6 @@ -5,3 +5,5 @@ class ListMilestone { this.title = obj.title; } } + +window.ListMilestone = ListMilestone; diff --git a/app/assets/javascripts/boards/models/user.js.es6 b/app/assets/javascripts/boards/models/user.js.es6 index 583a973fc46..92d6b913d94 100644 --- a/app/assets/javascripts/boards/models/user.js.es6 +++ b/app/assets/javascripts/boards/models/user.js.es6 @@ -7,3 +7,5 @@ class ListUser { this.avatar = user.avatar_url; } } + +window.ListUser = ListUser; diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6 index f59a2ed7937..64532b8f75c 100644 --- a/app/assets/javascripts/boards/services/board_service.js.es6 +++ b/app/assets/javascripts/boards/services/board_service.js.es6 @@ -63,4 +63,6 @@ class BoardService { issue }); } -}; +} + +window.BoardService = BoardService; diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6 index badcdccc840..f4bae5e216a 100644 --- a/app/assets/javascripts/diff_notes/models/discussion.js.es6 +++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6 @@ -89,3 +89,5 @@ class DiscussionModel { return false; } } + +window.DiscussionModel = DiscussionModel; diff --git a/app/assets/javascripts/diff_notes/models/note.js.es6 b/app/assets/javascripts/diff_notes/models/note.js.es6 index d0541b02632..4bbb06059a5 100644 --- a/app/assets/javascripts/diff_notes/models/note.js.es6 +++ b/app/assets/javascripts/diff_notes/models/note.js.es6 @@ -8,3 +8,5 @@ class NoteModel { this.resolved_by = resolved_by; } } + +window.NoteModel = NoteModel; diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6 index 15ec7b76c3d..575a45d9802 100644 --- a/app/assets/javascripts/environments/services/environments_service.js.es6 +++ b/app/assets/javascripts/environments/services/environments_service.js.es6 @@ -20,3 +20,5 @@ class EnvironmentsService { return this.environments.get(); } } + +window.EnvironmentsService = EnvironmentsService; diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 index e3f226e9a2a..5a928631a93 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 +++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 @@ -75,3 +75,5 @@ class ProtectedBranchDropdown { this.$dropdownFooter.toggleClass('hidden', !branchName); } } + +window.ProtectedBranchDropdown = ProtectedBranchDropdown; -- cgit v1.2.1 From 75060b93a6ea958b0c61a3c276d009026eef9dbd Mon Sep 17 00:00:00 2001 From: Jacopo Date: Wed, 14 Dec 2016 21:32:01 +0100 Subject: Go to a project order The order of "Go to a project" dropdown is based on last_activity_at --- app/assets/javascripts/project_select.js | 3 ++- app/helpers/projects_helper.rb | 2 +- changelogs/unreleased/18786-go-to-a-project-order.yml | 4 ++++ spec/javascripts/fixtures/project_title.html.haml | 2 +- spec/javascripts/project_title_spec.js | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/18786-go-to-a-project-order.yml diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index fe1f96872f3..17887a6e449 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -13,6 +13,7 @@ }, data: function(term, callback) { var finalCallback, projectsCallback; + var orderBy = $dropdown.data('order-by'); finalCallback = function(projects) { return callback(projects); }; @@ -32,7 +33,7 @@ if (this.groupId) { return Api.groupProjects(this.groupId, term, projectsCallback); } else { - return Api.projects(term, this.orderBy, projectsCallback); + return Api.projects(term, orderBy, projectsCallback); } }, url: function(project) { diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9cda3b78761..0f3546ebec7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -61,7 +61,7 @@ module ProjectsHelper project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" } if current_user - project_link << button_tag(type: 'button', class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) do + project_link << button_tag(type: 'button', class: 'dropdown-toggle-caret js-projects-dropdown-toggle', aria: { label: 'Toggle switch project dropdown' }, data: { target: '.js-dropdown-menu-projects', toggle: 'dropdown', order_by: 'last_activity_at' }) do icon("chevron-down") end end diff --git a/changelogs/unreleased/18786-go-to-a-project-order.yml b/changelogs/unreleased/18786-go-to-a-project-order.yml new file mode 100644 index 00000000000..1b9e246d1a7 --- /dev/null +++ b/changelogs/unreleased/18786-go-to-a-project-order.yml @@ -0,0 +1,4 @@ +--- +title: Go to a project order +merge_request: 7737 +author: Jacopo Beschi @jacopo-beschi diff --git a/spec/javascripts/fixtures/project_title.html.haml b/spec/javascripts/fixtures/project_title.html.haml index 4547feeb212..9d1f7877116 100644 --- a/spec/javascripts/fixtures/project_title.html.haml +++ b/spec/javascripts/fixtures/project_title.html.haml @@ -4,7 +4,7 @@ GitLab Org %a.project-item-select-holder{href: "/gitlab-org/gitlab-test"} GitLab Test - %i.fa.chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle{ "data-toggle" => "dropdown", "data-target" => ".header-content" } + %i.fa.chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle{ "data-toggle" => "dropdown", "data-target" => ".header-content", "data-order-by" => "last_activity_at" } .js-dropdown-menu-projects .dropdown-menu.dropdown-select.dropdown-menu-projects .dropdown-title diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js index 65de1756201..6085955465c 100644 --- a/spec/javascripts/project_title_spec.js +++ b/spec/javascripts/project_title_spec.js @@ -24,6 +24,7 @@ var fakeAjaxResponse = function fakeAjaxResponse(req) { var d; expect(req.url).toBe('/api/v3/projects.json?simple=true'); + expect(req.data).toEqual({ search: '', order_by: 'last_activity_at', per_page: 20 }); d = $.Deferred(); d.resolve(this.projects_data); return d.promise(); -- cgit v1.2.1 From c2410c48a19cf541832cb726262907670ca7860a Mon Sep 17 00:00:00 2001 From: Allison Whilden Date: Fri, 16 Dec 2016 09:01:53 -0800 Subject: [ci skip] UX Guide: add moving transition animation guidance --- doc/development/ux_guide/animation.md | 17 ++++++++++++++++- doc/development/ux_guide/img/animation-autoscroll.gif | Bin 0 -> 302217 bytes doc/development/ux_guide/img/animation-reorder.gif | Bin 0 -> 70515 bytes 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 doc/development/ux_guide/img/animation-autoscroll.gif create mode 100644 doc/development/ux_guide/img/animation-reorder.gif diff --git a/doc/development/ux_guide/animation.md b/doc/development/ux_guide/animation.md index daeb15460c2..903e54bf9dc 100644 --- a/doc/development/ux_guide/animation.md +++ b/doc/development/ux_guide/animation.md @@ -39,4 +39,19 @@ When information is updating in place, a quick, subtle animation is needed. The ![Quick update animation](img/animation-quickupdate.gif) -> TODO: Add guidance for other kinds of animation \ No newline at end of file +### Moving transitions + +When elements move on screen, there should be a quick animation so it is clear to users what moved where. The timing of this animation differs based on the amount of movement and change. Consider animations between `200ms` and `400ms`. + +#### Shifting elements on reorder +An example of a moving transition is when elements have to move out of the way when you drag an element. + +View the [interactive example](http://codepen.io/awhildy/full/ALyKPE/) here. + +![Reorder animation](img/animation-reorder.gif) + +#### Autoscroll the page +Another example of a moving transition is when you have to autoscroll the page to keep an active element visible. + +View the [interactive example](http://codepen.io/awhildy/full/PbxgVo/) here. +![Autoscroll animation](img/animation-autoscroll.gif) \ No newline at end of file diff --git a/doc/development/ux_guide/img/animation-autoscroll.gif b/doc/development/ux_guide/img/animation-autoscroll.gif new file mode 100644 index 00000000000..155b0234c64 Binary files /dev/null and b/doc/development/ux_guide/img/animation-autoscroll.gif differ diff --git a/doc/development/ux_guide/img/animation-reorder.gif b/doc/development/ux_guide/img/animation-reorder.gif new file mode 100644 index 00000000000..ccdeb3d396f Binary files /dev/null and b/doc/development/ux_guide/img/animation-reorder.gif differ -- cgit v1.2.1 From 884f57c9102416805427d773eb21e09fd30c2452 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 20 Dec 2016 21:19:07 +0800 Subject: Use consistent names and move checks to the method, and move those checks to be private. Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_20285012 https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7383#note_20285279 --- lib/gitlab/git_access.rb | 82 +++++++++++++++++---------------- spec/lib/gitlab/git_access_spec.rb | 6 +-- spec/lib/gitlab/git_access_wiki_spec.rb | 2 +- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 545506f3dfd..f0b241fb5e6 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -29,16 +29,16 @@ module Gitlab def check(cmd, changes) check_protocol! - check_active_user! unless deploy_key? + check_active_user! check_project_accessibility! check_command_existence!(cmd) check_repository_existence! case cmd when *DOWNLOAD_COMMANDS - download_access_check unless deploy_key? + check_download_access! when *PUSH_COMMANDS - push_access_check(changes) + check_push_access!(changes) end build_status_object(true) @@ -46,30 +46,6 @@ module Gitlab build_status_object(false, ex.message) end - def download_access_check - passed = user_can_download_code? || - build_can_download_code? || - guest_can_download_code? - - unless passed - raise UnauthorizedError, ERROR_MESSAGES[:download] - end - end - - def push_access_check(changes) - if deploy_key - deploy_key_push_access_check - elsif user - user_push_access_check - else - raise UnauthorizedError, ERROR_MESSAGES[:upload] - end - - return if changes.blank? # Allow access. - - check_change_access!(changes) - end - def guest_can_download_code? Guest.can?(:download_code, project) end @@ -82,18 +58,6 @@ module Gitlab authentication_abilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code) end - def user_push_access_check - unless authentication_abilities.include?(:push_code) - raise UnauthorizedError, ERROR_MESSAGES[:upload] - end - end - - def deploy_key_push_access_check - unless deploy_key.can_push_to?(project) - raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload] - end - end - def protocol_allowed? Gitlab::ProtocolAccess.allowed?(protocol) end @@ -107,6 +71,8 @@ module Gitlab end def check_active_user! + return if deploy_key? + if user && !user_access.allowed? raise UnauthorizedError, "Your account has been blocked." end @@ -130,6 +96,44 @@ module Gitlab end end + def check_download_access! + return if deploy_key? + + passed = user_can_download_code? || + build_can_download_code? || + guest_can_download_code? + + unless passed + raise UnauthorizedError, ERROR_MESSAGES[:download] + end + end + + def check_push_access!(changes) + if deploy_key + check_deploy_key_push_access! + elsif user + check_user_push_access! + else + raise UnauthorizedError, ERROR_MESSAGES[:upload] + end + + return if changes.blank? # Allow access. + + check_change_access!(changes) + end + + def check_user_push_access! + unless authentication_abilities.include?(:push_code) + raise UnauthorizedError, ERROR_MESSAGES[:upload] + end + end + + def check_deploy_key_push_access! + unless deploy_key.can_push_to?(project) + raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload] + end + end + def check_change_access!(changes) changes_list = Gitlab::ChangesList.new(changes) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 2c90397cc78..44b84afde47 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -50,7 +50,7 @@ describe Gitlab::GitAccess, lib: true do end end - describe 'download_access_check' do + describe '#check_download_access!' do subject { access.check('git-upload-pack', '_any') } describe 'master permissions' do @@ -183,7 +183,7 @@ describe Gitlab::GitAccess, lib: true do end end - describe 'push_access_check' do + describe '#check_push_access!' do before { merge_into_protected_branch } let(:unprotected_branch) { FFaker::Internet.user_name } @@ -231,7 +231,7 @@ describe Gitlab::GitAccess, lib: true do permissions_matrix[role].each do |action, allowed| context action do - subject { access.push_access_check(changes[action]) } + subject { access.send(:check_push_access!, changes[action]) } it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey } end end diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 578db51631e..a5d172233cc 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::GitAccessWiki, lib: true do ['6f6d7e7ed 570e7b2ab refs/heads/master'] end - describe '#download_access_check' do + describe '#access_check_download!' do subject { access.check('git-upload-pack', '_any') } before do -- cgit v1.2.1 From c1d11bf57c3091aa4695e302e21c39b9ec723f54 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 20 Dec 2016 23:30:01 +0800 Subject: Rubocop prefers to indent this way --- lib/gitlab/git_access.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index f0b241fb5e6..7e1484613f2 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -100,8 +100,8 @@ module Gitlab return if deploy_key? passed = user_can_download_code? || - build_can_download_code? || - guest_can_download_code? + build_can_download_code? || + guest_can_download_code? unless passed raise UnauthorizedError, ERROR_MESSAGES[:download] -- cgit v1.2.1 From 77deeb12f74b857f9356168ccdf92612fc85fe84 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 14 Dec 2016 17:57:14 -0200 Subject: Fix issuable assignee update bug when previous assignee is null --- app/models/concerns/issuable.rb | 7 ++++--- changelogs/unreleased/issue_22664.yml | 4 ++++ spec/models/concerns/issuable_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/issue_22664.yml diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 0ea7b1b1098..ecbfb625c5e 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -92,9 +92,10 @@ module Issuable after_save :record_metrics def update_assignee_cache_counts - # make sure we flush the cache for both the old *and* new assignee - User.find(assignee_id_was).update_cache_counts if assignee_id_was - assignee.update_cache_counts if assignee + # make sure we flush the cache for both the old *and* new assignees(if they exist) + previous_assignee = User.find_by_id(assignee_id_was) + previous_assignee.try(:update_cache_counts) + assignee.try(:update_cache_counts) end # We want to use optimistic lock for cases when only title or description are involved diff --git a/changelogs/unreleased/issue_22664.yml b/changelogs/unreleased/issue_22664.yml new file mode 100644 index 00000000000..28d3e74b1f8 --- /dev/null +++ b/changelogs/unreleased/issue_22664.yml @@ -0,0 +1,4 @@ +--- +title: Fix issuable assignee update bug when previous assignee is null +merge_request: +author: diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 4fa06a8c60a..3cc96816cb0 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -44,6 +44,26 @@ describe Issue, "Issuable" do it { expect(described_class).to respond_to(:assigned) } end + describe "after_save" do + describe "#update_cache_counts" do + context "when previous assignee exists" do + it "user updates cache counts" do + expect(user).to receive(:update_cache_counts) + + issue.update(assignee: user) + end + end + + context "when previous assignee does not exist" do + it "does not raise error" do + issue.update(assignee_id: "") + + expect { issue.update(assignee_id: user) }.not_to raise_error + end + end + end + end + describe ".search" do let!(:searchable_issue) { create(:issue, title: "Searchable issue") } -- cgit v1.2.1 From d05dd81b99b897bcf41bfa4055d1f42bb9669af9 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Tue, 29 Nov 2016 20:25:41 +0100 Subject: Don't expose all namespace fields in API --- lib/api/entities.rb | 4 ++-- spec/requests/api/projects_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index dfbb3ab86dd..c7530d9eb05 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -88,7 +88,7 @@ module API expose :shared_runners_enabled expose :lfs_enabled?, as: :lfs_enabled expose :creator_id - expose :namespace + expose :namespace, using: 'API::Entities::Namespace' expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } expose :avatar_url expose :star_count, :forks_count @@ -391,7 +391,7 @@ module API end class Namespace < Grape::Entity - expose :id, :path, :kind + expose :id, :name, :path, :kind end class MemberAccess < Grape::Entity diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 8304c408064..a626b41845f 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -630,6 +630,18 @@ describe API::Projects, api: true do expect(json_response['name']).to eq(project.name) end + it 'exposes namespace fields' do + get api("/projects/#{project.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['namespace']).to eq({ + 'id' => user.namespace.id, + 'name' => user.namespace.name, + 'path' => user.namespace.path, + 'kind' => user.namespace.kind, + }) + end + describe 'permissions' do context 'all projects' do before { project.team << [user, :master] } -- cgit v1.2.1 From d5c49779125f70c49ff8b160355d4999d27091ee Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Tue, 29 Nov 2016 20:21:39 +0100 Subject: Consistently use current_user in API entities --- lib/api/entities.rb | 16 ++++++++-------- lib/api/groups.rb | 14 +++++++------- lib/api/projects.rb | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index c7530d9eb05..c1e42fb7d47 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -78,11 +78,11 @@ module API expose :container_registry_enabled # Expose old field names with the new permissions methods to keep API compatible - expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:user]) } - expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:user]) } - expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:user]) } - expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:user]) } - expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:user]) } + expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } + expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } + expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } + expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } + expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose :created_at, :last_activity_at expose :shared_runners_enabled @@ -92,7 +92,7 @@ module API expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } expose :avatar_url expose :star_count, :forks_count - expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:user]) && project.default_issues_tracker? } + expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :public_builds expose :shared_with_groups do |project, options| @@ -440,12 +440,12 @@ module API class ProjectWithAccess < Project expose :permissions do expose :project_access, using: Entities::ProjectAccess do |project, options| - project.project_members.find_by(user_id: options[:user].id) + project.project_members.find_by(user_id: options[:current_user].id) end expose :group_access, using: Entities::GroupAccess do |project, options| if project.group - project.group.group_members.find_by(user_id: options[:user].id) + project.group.group_members.find_by(user_id: options[:current_user].id) end end end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 9b9d3df7435..a9ae2977dc5 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -38,7 +38,7 @@ module API groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? groups = groups.reorder(params[:order_by] => params[:sort]) - present paginate(groups), with: Entities::Group + present paginate(groups), with: Entities::Group, current_user: current_user end desc 'Get list of owned groups for authenticated user' do @@ -49,7 +49,7 @@ module API end get '/owned' do groups = current_user.owned_groups - present paginate(groups), with: Entities::Group, user: current_user + present paginate(groups), with: Entities::Group, current_user: current_user end desc 'Create a group. Available only for users who can create groups.' do @@ -66,7 +66,7 @@ module API group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute if group.persisted? - present group, with: Entities::Group + present group, with: Entities::Group, current_user: current_user else render_api_error!("Failed to save group #{group.errors.messages}", 400) end @@ -92,7 +92,7 @@ module API authorize! :admin_group, group if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute - present group, with: Entities::GroupDetail + present group, with: Entities::GroupDetail, current_user: current_user else render_validation_error!(group) end @@ -103,7 +103,7 @@ module API end get ":id" do group = find_group!(params[:id]) - present group, with: Entities::GroupDetail + present group, with: Entities::GroupDetail, current_user: current_user end desc 'Remove a group.' @@ -134,7 +134,7 @@ module API projects = GroupProjectsFinder.new(group).execute(current_user) projects = filter_projects(projects) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project - present paginate(projects), with: entity, user: current_user + present paginate(projects), with: entity, current_user: current_user end desc 'Transfer a project to the group namespace. Available only for admin.' do @@ -150,7 +150,7 @@ module API result = ::Projects::TransferService.new(project, current_user).execute(group) if result - present group, with: Entities::GroupDetail + present group, with: Entities::GroupDetail, current_user: current_user else render_api_error!("Failed to transfer project #{project.errors.messages}", 400) end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 2929d2157dc..f5609d878f8 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -75,7 +75,7 @@ module API projects = filter_projects(projects) entity = params[:simple] || !current_user ? Entities::BasicProjectDetails : Entities::ProjectWithAccess - present paginate(projects), with: entity, user: current_user + present paginate(projects), with: entity, current_user: current_user end desc 'Get a projects list for authenticated user' do @@ -94,7 +94,7 @@ module API projects = filter_projects(projects) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess - present paginate(projects), with: entity, user: current_user + present paginate(projects), with: entity, current_user: current_user end desc 'Get an owned projects list for authenticated user' do @@ -110,7 +110,7 @@ module API projects = current_user.owned_projects projects = filter_projects(projects) - present paginate(projects), with: Entities::ProjectWithAccess, user: current_user + present paginate(projects), with: Entities::ProjectWithAccess, current_user: current_user end desc 'Gets starred project for the authenticated user' do @@ -126,7 +126,7 @@ module API projects = current_user.viewable_starred_projects projects = filter_projects(projects) - present paginate(projects), with: Entities::Project, user: current_user + present paginate(projects), with: Entities::Project, current_user: current_user end desc 'Get all projects for admin user' do @@ -142,7 +142,7 @@ module API projects = Project.all projects = filter_projects(projects) - present paginate(projects), with: Entities::ProjectWithAccess, user: current_user + present paginate(projects), with: Entities::ProjectWithAccess, current_user: current_user end desc 'Search for projects the current user has access to' do @@ -221,7 +221,7 @@ module API end get ":id" do entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails - present user_project, with: entity, user: current_user, + present user_project, with: entity, current_user: current_user, user_can_admin_project: can?(current_user, :admin_project, user_project) end -- cgit v1.2.1 From b8d8fd70d53a90fba6631d9cce573fcfdc24a270 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sat, 3 Dec 2016 16:59:24 +0100 Subject: Remove unused ProjectsHelper#round_commit_count --- app/helpers/projects_helper.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d2177f683a1..9c9e38c4ed7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -396,20 +396,6 @@ module ProjectsHelper [@project.path_with_namespace, sha, "readme"].join('-') end - def round_commit_count(project) - count = project.commit_count - - if count > 10000 - '10000+' - elsif count > 5000 - '5000+' - elsif count > 1000 - '1000+' - else - count - end - end - def current_ref @ref || @repository.try(:root_ref) end -- cgit v1.2.1 From 6fd58ee48dcfbca49c609c45004d6c25035af2eb Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sat, 3 Dec 2016 17:00:04 +0100 Subject: Remove rake task update_commit_count --- lib/tasks/gitlab/update_commit_count.rake | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 lib/tasks/gitlab/update_commit_count.rake diff --git a/lib/tasks/gitlab/update_commit_count.rake b/lib/tasks/gitlab/update_commit_count.rake deleted file mode 100644 index 3bd10b0208b..00000000000 --- a/lib/tasks/gitlab/update_commit_count.rake +++ /dev/null @@ -1,20 +0,0 @@ -namespace :gitlab do - desc "GitLab | Update commit count for projects" - task update_commit_count: :environment do - projects = Project.where(commit_count: 0) - puts "#{projects.size} projects need to be updated. This might take a while." - ask_to_continue unless ENV['force'] == 'yes' - - projects.find_each(batch_size: 100) do |project| - print "#{project.name_with_namespace.color(:yellow)} ... " - - unless project.repo_exists? - puts "skipping, because the repo is empty".color(:magenta) - next - end - - project.update_commit_count - puts project.commit_count.to_s.color(:green) - end - end -end -- cgit v1.2.1 From 3ef4f74b1acc9399db320b53dffc592542de0126 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Tue, 22 Nov 2016 17:58:10 +0100 Subject: Add more storage statistics This adds counters for build artifacts and LFS objects, and moves the preexisting repository_size and commit_count from the projects table into a new project_statistics table. The counters are displayed in the administration area for projects and groups, and also available through the API for admins (on */all) and normal users (on */owned) The statistics are updated through ProjectCacheWorker, which can now do more granular updates with the new :statistics argument. --- app/controllers/admin/groups_controller.rb | 7 +- app/controllers/admin/projects_controller.rb | 2 +- app/controllers/groups_controller.rb | 2 +- app/helpers/projects_helper.rb | 5 - app/helpers/sorting_helper.rb | 11 +- app/helpers/storage_helper.rb | 7 + app/models/ci/build.rb | 6 + app/models/group.rb | 8 +- app/models/lfs_objects_project.rb | 9 ++ app/models/namespace.rb | 13 ++ app/models/project.rb | 22 +-- app/models/project_statistics.rb | 43 ++++++ app/services/git_push_service.rb | 2 +- app/services/git_tag_push_service.rb | 2 +- app/views/admin/groups/_group.html.haml | 3 + app/views/admin/groups/index.html.haml | 2 + app/views/admin/groups/show.html.haml | 20 ++- app/views/admin/projects/index.html.haml | 4 +- app/views/admin/projects/show.html.haml | 13 +- app/views/groups/projects.html.haml | 4 +- app/views/projects/show.html.haml | 4 +- app/workers/project_cache_worker.rb | 23 +-- .../unreleased/feature-more-storage-statistics.yml | 4 + config/initializers/inflections.rb | 2 +- .../20161201155511_create_project_statistics.rb | 20 +++ .../20161201160452_migrate_project_statistics.rb | 23 +++ db/schema.rb | 16 ++- doc/administration/build_artifacts.md | 6 + doc/api/groups.md | 8 +- doc/api/projects.md | 4 + doc/workflow/lfs/lfs_administration.md | 8 ++ lib/api/entities.rb | 19 +++ lib/api/groups.rb | 21 ++- lib/api/helpers.rb | 2 +- lib/api/projects.rb | 80 ++++++----- lib/tasks/gitlab/import.rake | 3 +- spec/factories/lfs_objects.rb | 2 +- spec/factories/project_statistics.rb | 6 + spec/helpers/storage_helper_spec.rb | 21 +++ spec/lib/gitlab/import_export/all_models.yml | 1 + spec/models/ci/build_spec.rb | 26 ++++ spec/models/lfs_objects_project_spec.rb | 36 +++++ spec/models/namespace_spec.rb | 45 ++++++ spec/models/project_spec.rb | 21 +++ spec/models/project_statistics_spec.rb | 160 +++++++++++++++++++++ spec/requests/api/groups_spec.rb | 33 +++++ spec/requests/api/projects_spec.rb | 44 +++++- spec/services/git_push_service_spec.rb | 4 +- spec/workers/project_cache_worker_spec.rb | 42 +++--- 49 files changed, 748 insertions(+), 121 deletions(-) create mode 100644 app/helpers/storage_helper.rb create mode 100644 app/models/project_statistics.rb create mode 100644 changelogs/unreleased/feature-more-storage-statistics.yml create mode 100644 db/migrate/20161201155511_create_project_statistics.rb create mode 100644 db/migrate/20161201160452_migrate_project_statistics.rb create mode 100644 spec/factories/project_statistics.rb create mode 100644 spec/helpers/storage_helper_spec.rb create mode 100644 spec/models/lfs_objects_project_spec.rb create mode 100644 spec/models/project_statistics_spec.rb diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 1e3d194e9f9..61a3a03182a 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,17 +1,18 @@ class Admin::GroupsController < Admin::ApplicationController - before_action :group, only: [:edit, :show, :update, :destroy, :project_update, :members_update] + before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update] def index - @groups = Group.all + @groups = Group.with_statistics @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.search(params[:name]) if params[:name].present? @groups = @groups.page(params[:page]) end def show + @group = Group.with_statistics.find_by_full_path(params[:id]) @members = @group.members.order("access_level DESC").page(params[:members_page]) @requesters = AccessRequestsFinder.new(@group).execute(current_user) - @projects = @group.projects.page(params[:projects_page]) + @projects = @group.projects.with_statistics.page(params[:projects_page]) end def new diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 1d963bdf7d5..b09ae423096 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -3,7 +3,7 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :group, only: [:show, :transfer] def index - @projects = Project.all + @projects = Project.with_statistics @projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.with_push if params[:with_push].present? diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index efe9c001bcf..01c8fa2739f 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -75,7 +75,7 @@ class GroupsController < Groups::ApplicationController end def projects - @projects = @group.projects.page(params[:page]) + @projects = @group.projects.with_statistics.page(params[:page]) end def update diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9c9e38c4ed7..fc9eccd2942 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -246,11 +246,6 @@ module ProjectsHelper end end - def repository_size(project = @project) - size_in_bytes = project.repository_size * 1.megabyte - number_to_human_size(size_in_bytes, delimiter: ',', precision: 2) - end - def default_url_to_repo(project = @project) case default_clone_protocol when 'ssh' diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index f03c4627050..ff787fb4131 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -11,6 +11,7 @@ module SortingHelper sort_value_due_date_soon => sort_title_due_date_soon, sort_value_due_date_later => sort_title_due_date_later, sort_value_largest_repo => sort_title_largest_repo, + sort_value_largest_group => sort_title_largest_group, sort_value_recently_signin => sort_title_recently_signin, sort_value_oldest_signin => sort_title_oldest_signin, sort_value_downvotes => sort_title_downvotes, @@ -92,6 +93,10 @@ module SortingHelper 'Largest repository' end + def sort_title_largest_group + 'Largest group' + end + def sort_title_recently_signin 'Recent sign in' end @@ -193,7 +198,11 @@ module SortingHelper end def sort_value_largest_repo - 'repository_size_desc' + 'storage_size_desc' + end + + def sort_value_largest_group + 'storage_size_desc' end def sort_value_recently_signin diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb new file mode 100644 index 00000000000..e19c67a37ca --- /dev/null +++ b/app/helpers/storage_helper.rb @@ -0,0 +1,7 @@ +module StorageHelper + def storage_counter(size_in_bytes) + precision = size_in_bytes < 1.megabyte ? 0 : 1 + + number_to_human_size(size_in_bytes, delimiter: ',', precision: precision, significant: false) + end +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index cb76cdf5981..27042798741 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -43,6 +43,8 @@ module Ci before_destroy { project } after_create :execute_hooks + after_save :update_project_statistics, if: :artifacts_size_changed? + after_destroy :update_project_statistics class << self def first_pending @@ -584,5 +586,9 @@ module Ci Ci::MaskSecret.mask!(trace, token) trace end + + def update_project_statistics + ProjectCacheWorker.perform_async(project_id, [], [:build_artifacts_size]) + end end end diff --git a/app/models/group.rb b/app/models/group.rb index ac8a82c8c1e..85696ad9747 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -48,7 +48,13 @@ class Group < Namespace end def sort(method) - order_by(method) + if method == 'storage_size_desc' + # storage_size is a virtual column so we need to + # pass a string to avoid AR adding the table name + reorder('storage_size DESC, namespaces.id DESC') + else + order_by(method) + end end def reference_prefix diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb index 0fd5f089db9..007eed5600a 100644 --- a/app/models/lfs_objects_project.rb +++ b/app/models/lfs_objects_project.rb @@ -5,4 +5,13 @@ class LfsObjectsProject < ActiveRecord::Base validates :lfs_object_id, presence: true validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" } validates :project_id, presence: true + + after_create :update_project_statistics + after_destroy :update_project_statistics + + private + + def update_project_statistics + ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size]) + end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index b52f08c7081..d41833de66f 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -9,6 +9,7 @@ class Namespace < ActiveRecord::Base cache_markdown_field :description, pipeline: :description has_many :projects, dependent: :destroy + has_many :project_statistics belongs_to :owner, class_name: "User" belongs_to :parent, class_name: "Namespace" @@ -38,6 +39,18 @@ class Namespace < ActiveRecord::Base scope :root, -> { where('type IS NULL') } + scope :with_statistics, -> do + joins('LEFT JOIN project_statistics ps ON ps.namespace_id = namespaces.id') + .group('namespaces.id') + .select( + 'namespaces.*', + 'COALESCE(SUM(ps.storage_size), 0) AS storage_size', + 'COALESCE(SUM(ps.repository_size), 0) AS repository_size', + 'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size', + 'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size', + ) + end + class << self def by_path(path) find_by('lower(path) = :value', value: path.downcase) diff --git a/app/models/project.rb b/app/models/project.rb index 26fa20f856d..19bbe65b01d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -44,6 +44,7 @@ class Project < ActiveRecord::Base after_create :ensure_dir_exist after_create :create_project_feature, unless: :project_feature after_save :ensure_dir_exist, if: :namespace_id_changed? + after_save :update_project_statistics, if: :namespace_id_changed? # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -151,6 +152,7 @@ class Project < ActiveRecord::Base has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :project_feature, dependent: :destroy + has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id @@ -220,6 +222,7 @@ class Project < ActiveRecord::Base scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) } scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') } + scope :with_statistics, -> { includes(:statistics) } # "enabled" here means "not disabled". It includes private features! scope :with_feature_enabled, ->(feature) { @@ -332,8 +335,10 @@ class Project < ActiveRecord::Base end def sort(method) - if method == 'repository_size_desc' - reorder(repository_size: :desc, id: :desc) + if method == 'storage_size_desc' + # storage_size is a joined column so we need to + # pass a string to avoid AR adding the table name + reorder('project_statistics.storage_size DESC, projects.id DESC') else order_by(method) end @@ -1036,14 +1041,6 @@ class Project < ActiveRecord::Base forked? && project == forked_from_project end - def update_repository_size - update_attribute(:repository_size, repository.size) - end - - def update_commit_count - update_attribute(:commit_count, repository.commit_count) - end - def forks_count forks.count end @@ -1322,4 +1319,9 @@ class Project < ActiveRecord::Base def full_path_changed? path_changed? || namespace_id_changed? end + + def update_project_statistics + stats = statistics || build_statistics + stats.update(namespace_id: namespace_id) + end end diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb new file mode 100644 index 00000000000..2270ac75071 --- /dev/null +++ b/app/models/project_statistics.rb @@ -0,0 +1,43 @@ +class ProjectStatistics < ActiveRecord::Base + belongs_to :project + belongs_to :namespace + + before_save :update_storage_size + + STORAGE_COLUMNS = [:repository_size, :lfs_objects_size, :build_artifacts_size] + STATISTICS_COLUMNS = [:commit_count] + STORAGE_COLUMNS + + def total_repository_size + repository_size + lfs_objects_size + end + + def refresh!(only: nil) + STATISTICS_COLUMNS.each do |column, generator| + if only.blank? || only.include?(column) + public_send("update_#{column}") + end + end + + save! + end + + def update_commit_count + self.commit_count = project.repository.commit_count + end + + def update_repository_size + self.repository_size = project.repository.size + end + + def update_lfs_objects_size + self.lfs_objects_size = project.lfs_objects.sum(:size) + end + + def update_build_artifacts_size + self.build_artifacts_size = project.builds.sum(:artifacts_size) + end + + def update_storage_size + self.storage_size = STORAGE_COLUMNS.sum(&method(:read_attribute)) + end +end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 185556c12cc..f603036cf03 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -74,7 +74,7 @@ class GitPushService < BaseService types = [] end - ProjectCacheWorker.perform_async(@project.id, types) + ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size]) end protected diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 20a4445bddf..96432837481 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -12,7 +12,7 @@ class GitTagPushService < BaseService project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) Ci::CreatePipelineService.new(project, current_user, @push_data).execute - ProjectCacheWorker.perform_async(project.id) + ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size]) true end diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index cf28f92853e..6fc212119c4 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -5,6 +5,9 @@ = link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn' = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove' .stats + %span.badge + = storage_counter(group.storage_size) + %span = icon('bookmark') = number_with_delimiter(group.projects.count) diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 794f910a61f..07775247cfd 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -27,6 +27,8 @@ = sort_title_recently_updated = link_to admin_groups_path(sort: sort_value_oldest_updated, name: project_name) do = sort_title_oldest_updated + = link_to admin_groups_path(sort: sort_value_largest_group, name: project_name) do + = sort_title_largest_group = link_to new_admin_group_path, class: "btn btn-new" do New Group %ul.content-list diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 7b0175af214..ab9c79f6add 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -38,6 +38,18 @@ %strong = @group.created_at.to_s(:medium) + %li + %span.light Storage: + %strong= storage_counter(@group.storage_size) + ( + = storage_counter(@group.repository_size) + repositories, + = storage_counter(@group.build_artifacts_size) + build artifacts, + = storage_counter(@group.lfs_objects_size) + LFS + ) + %li %span.light Group Git LFS status: %strong @@ -55,8 +67,8 @@ %li %strong = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] - %span.label.label-gray - = repository_size(project) + %span.badge + = storage_counter(project.statistics.storage_size) %span.pull-right.light %span.monospace= project.path_with_namespace + ".git" .panel-footer @@ -73,8 +85,8 @@ %li %strong = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] - %span.label.label-gray - = repository_size(project) + %span.badge + = storage_counter(project.statistics.storage_size) %span.pull-right.light %span.monospace= project.path_with_namespace + ".git" diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 8bc7dc7dd51..2e6f03fcde0 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -69,8 +69,8 @@ .controls - if project.archived %span.label.label-warning archived - %span.label.label-gray - = repository_size(project) + %span.badge + = storage_counter(project.statistics.storage_size) = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn" = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove" .title diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 6c7c3c48604..2967da6e692 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -65,9 +65,16 @@ = @project.repository.path_to_repo %li - %span.light Size - %strong - = repository_size(@project) + %span.light Storage: + %strong= storage_counter(@project.statistics.storage_size) + ( + = storage_counter(@project.statistics.repository_size) + repository, + = storage_counter(@project.statistics.build_artifacts_size) + build artifacts, + = storage_counter(@project.statistics.lfs_objects_size) + LFS + ) %li %span.light last commit: diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 33fee334d93..2e7e5e5c309 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -18,8 +18,8 @@ .pull-right - if project.archived %span.label.label-warning archived - %span.label.label-gray - = repository_size(project) + %span.badge + = storage_counter(project.statistics.storage_size) = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 8a214e1de58..eb31fe430b6 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -17,10 +17,10 @@ %ul.nav %li = link_to project_files_path(@project) do - Files (#{repository_size}) + Files (#{storage_counter(@project.statistics.total_repository_size)}) %li = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do - #{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)}) + #{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)}) %li = link_to namespace_project_branches_path(@project.namespace, @project) do #{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index 27d7e652721..8ff9d07860f 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -6,26 +6,27 @@ class ProjectCacheWorker LEASE_TIMEOUT = 15.minutes.to_i # project_id - The ID of the project for which to flush the cache. - # refresh - An Array containing extra types of data to refresh such as - # `:readme` to flush the README and `:changelog` to flush the - # CHANGELOG. - def perform(project_id, refresh = []) + # files - An Array containing extra types of files to refresh such as + # `:readme` to flush the README and `:changelog` to flush the + # CHANGELOG. + # statistics - An Array containing columns from ProjectStatistics to + # refresh, if empty all columns will be refreshed + def perform(project_id, files = [], statistics = []) project = Project.find_by(id: project_id) return unless project && project.repository.exists? - update_repository_size(project) - project.update_commit_count + update_statistics(project, statistics.map(&:to_sym)) - project.repository.refresh_method_caches(refresh.map(&:to_sym)) + project.repository.refresh_method_caches(files.map(&:to_sym)) end - def update_repository_size(project) - return unless try_obtain_lease_for(project.id, :update_repository_size) + def update_statistics(project, statistics = []) + return unless try_obtain_lease_for(project.id, :update_statistics) - Rails.logger.info("Updating repository size for project #{project.id}") + Rails.logger.info("Updating statistics for project #{project.id}") - project.update_repository_size + project.statistics.refresh!(only: statistics) end private diff --git a/changelogs/unreleased/feature-more-storage-statistics.yml b/changelogs/unreleased/feature-more-storage-statistics.yml new file mode 100644 index 00000000000..824fd36dc34 --- /dev/null +++ b/changelogs/unreleased/feature-more-storage-statistics.yml @@ -0,0 +1,4 @@ +--- +title: Add more storage statistics +merge_request: 7754 +author: Markus Koller diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 3d1a41a4652..d4197da3fa9 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -10,5 +10,5 @@ # end # ActiveSupport::Inflector.inflections do |inflect| - inflect.uncountable %w(award_emoji) + inflect.uncountable %w(award_emoji project_statistics) end diff --git a/db/migrate/20161201155511_create_project_statistics.rb b/db/migrate/20161201155511_create_project_statistics.rb new file mode 100644 index 00000000000..26e6d3623eb --- /dev/null +++ b/db/migrate/20161201155511_create_project_statistics.rb @@ -0,0 +1,20 @@ +class CreateProjectStatistics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + # use bigint columns to support values >2GB + counter_column = { limit: 8, null: false, default: 0 } + + create_table :project_statistics do |t| + t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade } + t.references :namespace, null: false, index: true + t.integer :commit_count, counter_column + t.integer :storage_size, counter_column + t.integer :repository_size, counter_column + t.integer :lfs_objects_size, counter_column + t.integer :build_artifacts_size, counter_column + end + end +end diff --git a/db/migrate/20161201160452_migrate_project_statistics.rb b/db/migrate/20161201160452_migrate_project_statistics.rb new file mode 100644 index 00000000000..3ae3f2c159b --- /dev/null +++ b/db/migrate/20161201160452_migrate_project_statistics.rb @@ -0,0 +1,23 @@ +class MigrateProjectStatistics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + DOWNTIME_REASON = 'Removes two columns from the projects table' + + def up + # convert repository_size in float (megabytes) to integer (bytes), + # initialize total storage_size with repository_size + execute <<-EOF + INSERT INTO project_statistics (project_id, namespace_id, commit_count, storage_size, repository_size) + SELECT id, namespace_id, commit_count, (repository_size * 1024 * 1024), (repository_size * 1024 * 1024) FROM projects + EOF + + remove_column :projects, :repository_size + remove_column :projects, :commit_count + end + + def down + add_column_with_default :projects, :repository_size, :float, default: 0.0 + add_column_with_default :projects, :commit_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 13a847827cc..95a4f7e8ee0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -901,6 +901,19 @@ ActiveRecord::Schema.define(version: 20161220141214) do add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree + create_table "project_statistics", force: :cascade do |t| + t.integer "project_id", null: false + t.integer "namespace_id", null: false + t.integer "commit_count", limit: 8, default: 0, null: false + t.integer "storage_size", limit: 8, default: 0, null: false + t.integer "repository_size", limit: 8, default: 0, null: false + t.integer "lfs_objects_size", limit: 8, default: 0, null: false + t.integer "build_artifacts_size", limit: 8, default: 0, null: false + end + + add_index "project_statistics", ["namespace_id"], name: "index_project_statistics_on_namespace_id", using: :btree + add_index "project_statistics", ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree + create_table "projects", force: :cascade do |t| t.string "name" t.string "path" @@ -915,11 +928,9 @@ ActiveRecord::Schema.define(version: 20161220141214) do t.boolean "archived", default: false, null: false t.string "avatar" t.string "import_status" - t.float "repository_size", default: 0.0 t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 t.text "import_error" t.integer "ci_id" t.boolean "shared_runners_enabled", default: true, null: false @@ -1288,6 +1299,7 @@ ActiveRecord::Schema.define(version: 20161220141214) do add_foreign_key "personal_access_tokens", "users" add_foreign_key "project_authorizations", "projects", on_delete: :cascade add_foreign_key "project_authorizations", "users", on_delete: :cascade + add_foreign_key "project_statistics", "projects", on_delete: :cascade add_foreign_key "protected_branch_merge_access_levels", "protected_branches" add_foreign_key "protected_branch_push_access_levels", "protected_branches" add_foreign_key "subscriptions", "projects", on_delete: :cascade diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md index 3ba8387c7f0..cca422892ec 100644 --- a/doc/administration/build_artifacts.md +++ b/doc/administration/build_artifacts.md @@ -88,3 +88,9 @@ artifacts through the [Admin area settings](../user/admin_area/settings/continuo [reconfigure gitlab]: restart_gitlab.md "How to restart GitLab" [restart gitlab]: restart_gitlab.md "How to restart GitLab" + +## Storage statistics + +You can see the total storage used for build artifacts on groups and projects +in the administration area, as well as through the [groups](../api/groups.md) +and [projects APIs](../api/projects.md). diff --git a/doc/api/groups.md b/doc/api/groups.md index 134d7bda22f..bc737bff8ee 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -13,6 +13,7 @@ Parameters: | `search` | string | no | Return list of authorized groups matching the search criteria | | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | +| `statistics` | boolean | no | Include group statistics (admins only) | ``` GET /groups @@ -31,7 +32,6 @@ GET /groups You can search for groups by name or path, see below. -======= ## List owned groups Get a list of groups which are owned by the authenticated user. @@ -40,6 +40,12 @@ Get a list of groups which are owned by the authenticated user. GET /groups/owned ``` +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `statistics` | boolean | no | Include group statistics | + ## List a group's projects Get a list of projects in this group. diff --git a/doc/api/projects.md b/doc/api/projects.md index edffad555a5..122075bbd11 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -307,6 +307,8 @@ Parameters: | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of authorized projects matching the search criteria | +| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | +| `statistics` | boolean | no | Include project statistics | ### List starred projects @@ -325,6 +327,7 @@ Parameters: | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of authorized projects matching the search criteria | +| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | ### List ALL projects @@ -343,6 +346,7 @@ Parameters: | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of authorized projects matching the search criteria | +| `statistics` | boolean | no | Include project statistics | ### Get single project diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md index b3c73e947f0..5f6a718135d 100644 --- a/doc/workflow/lfs/lfs_administration.md +++ b/doc/workflow/lfs/lfs_administration.md @@ -40,6 +40,12 @@ In `config/gitlab.yml`: storage_path: /mnt/storage/lfs-objects ``` +## Storage statistics + +You can see the total storage used for LFS objects on groups and projects +in the administration area, as well as through the [groups](../api/groups.md) +and [projects APIs](../api/projects.md). + ## Known limitations * Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) @@ -47,3 +53,5 @@ In `config/gitlab.yml`: * Currently, removing LFS objects from GitLab Git LFS storage is not supported * LFS authentications via SSH was added with GitLab 8.12 * Only compatible with the GitLFS client versions 1.1.0 and up, or 1.0.2. +* The storage statistics currently count each LFS object multiple times for + every project linking to it diff --git a/lib/api/entities.rb b/lib/api/entities.rb index c1e42fb7d47..9f15c08f472 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -101,6 +101,16 @@ module API expose :only_allow_merge_if_build_succeeds expose :request_access_enabled expose :only_allow_merge_if_all_discussions_are_resolved + + expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + end + + class ProjectStatistics < Grape::Entity + expose :commit_count + expose :storage_size + expose :repository_size + expose :lfs_objects_size + expose :build_artifacts_size end class Member < UserBasic @@ -127,6 +137,15 @@ module API expose :avatar_url expose :web_url expose :request_access_enabled + + expose :statistics, if: :statistics do + with_options format_with: -> (value) { value.to_i } do + expose :storage_size + expose :repository_size + expose :lfs_objects_size + expose :build_artifacts_size + end + end end class GroupDetail < Group diff --git a/lib/api/groups.rb b/lib/api/groups.rb index a9ae2977dc5..e04d2e40fb6 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -11,6 +11,20 @@ module API optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' end + + params :statistics_params do + optional :statistics, type: Boolean, default: false, desc: 'Include project statistics' + end + + def present_groups(groups, options = {}) + options = options.reverse_merge( + with: Entities::Group, + current_user: current_user, + ) + + groups = groups.with_statistics if options[:statistics] + present paginate(groups), options + end end resource :groups do @@ -18,6 +32,7 @@ module API success Entities::Group end params do + use :statistics_params optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list' optional :all_available, type: Boolean, desc: 'Show all group that you have access to' optional :search, type: String, desc: 'Search for a specific group' @@ -38,7 +53,7 @@ module API groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? groups = groups.reorder(params[:order_by] => params[:sort]) - present paginate(groups), with: Entities::Group, current_user: current_user + present_groups groups, statistics: params[:statistics] && current_user.is_admin? end desc 'Get list of owned groups for authenticated user' do @@ -46,10 +61,10 @@ module API end params do use :pagination + use :statistics_params end get '/owned' do - groups = current_user.owned_groups - present paginate(groups), with: Entities::Group, current_user: current_user + present_groups current_user.owned_groups, statistics: params[:statistics] end desc 'Create a group. Available only for users who can create groups.' do diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 4be659fc20b..fe00c83bff3 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -248,7 +248,7 @@ module API rack_response({ 'message' => '500 Internal Server Error' }.to_json, 500) end - # Projects helpers + # project helpers def filter_projects(projects) if params[:search].present? diff --git a/lib/api/projects.rb b/lib/api/projects.rb index f5609d878f8..3be14e8eb76 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -40,6 +40,15 @@ module API resource :projects do helpers do + params :collection_params do + use :sort_params + use :filter_params + use :pagination + + optional :simple, type: Boolean, default: false, + desc: 'Return only the ID, URL, name, and path of each project' + end + params :sort_params do optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at], default: 'created_at', desc: 'Return projects ordered by field' @@ -52,97 +61,94 @@ module API optional :visibility, type: String, values: %w[public internal private], desc: 'Limit by visibility' optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' - use :sort_params + end + + params :statistics_params do + optional :statistics, type: Boolean, default: false, desc: 'Include project statistics' end params :create_params do optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.' optional :import_url, type: String, desc: 'URL from which the project is imported' end + + def present_projects(projects, options = {}) + options = options.reverse_merge( + with: Entities::Project, + current_user: current_user, + simple: params[:simple], + ) + + projects = filter_projects(projects) + projects = projects.with_statistics if options[:statistics] + options[:with] = Entities::BasicProjectDetails if options[:simple] + + present paginate(projects), options + end end desc 'Get a list of visible projects for authenticated user' do success Entities::BasicProjectDetails end params do - optional :simple, type: Boolean, default: false, - desc: 'Return only the ID, URL, name, and path of each project' - use :filter_params - use :pagination + use :collection_params end get '/visible' do - projects = ProjectsFinder.new.execute(current_user) - projects = filter_projects(projects) - entity = params[:simple] || !current_user ? Entities::BasicProjectDetails : Entities::ProjectWithAccess - - present paginate(projects), with: entity, current_user: current_user + entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails + present_projects ProjectsFinder.new.execute(current_user), with: entity end desc 'Get a projects list for authenticated user' do success Entities::BasicProjectDetails end params do - optional :simple, type: Boolean, default: false, - desc: 'Return only the ID, URL, name, and path of each project' - use :filter_params - use :pagination + use :collection_params end get do authenticate! - projects = current_user.authorized_projects - projects = filter_projects(projects) - entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess - - present paginate(projects), with: entity, current_user: current_user + present_projects current_user.authorized_projects, + with: Entities::ProjectWithAccess end desc 'Get an owned projects list for authenticated user' do success Entities::BasicProjectDetails end params do - use :filter_params - use :pagination + use :collection_params + use :statistics_params end get '/owned' do authenticate! - projects = current_user.owned_projects - projects = filter_projects(projects) - - present paginate(projects), with: Entities::ProjectWithAccess, current_user: current_user + present_projects current_user.owned_projects, + with: Entities::ProjectWithAccess, + statistics: params[:statistics] end desc 'Gets starred project for the authenticated user' do success Entities::BasicProjectDetails end params do - use :filter_params - use :pagination + use :collection_params end get '/starred' do authenticate! - projects = current_user.viewable_starred_projects - projects = filter_projects(projects) - - present paginate(projects), with: Entities::Project, current_user: current_user + present_projects current_user.viewable_starred_projects end desc 'Get all projects for admin user' do success Entities::BasicProjectDetails end params do - use :filter_params - use :pagination + use :collection_params + use :statistics_params end get '/all' do authenticated_as_admin! - projects = Project.all - projects = filter_projects(projects) - - present paginate(projects), with: Entities::ProjectWithAccess, current_user: current_user + present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics] end desc 'Search for projects the current user has access to' do diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index dbdd4e977e8..a2eca74a3c8 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -63,8 +63,7 @@ namespace :gitlab do if project.persisted? puts " * Created #{project.name} (#{repo_path})".color(:green) - project.update_repository_size - project.update_commit_count + ProjectCacheWorker.perform(project.id) else puts " * Failed trying to create #{project.name} (#{repo_path})".color(:red) puts " Errors: #{project.errors.messages}".color(:red) diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index a81645acd2b..477fab9e964 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -2,7 +2,7 @@ include ActionDispatch::TestProcess FactoryGirl.define do factory :lfs_object do - oid "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" + sequence(:oid) { |n| "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a%05x" % n } size 499013 end diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb new file mode 100644 index 00000000000..72d43096216 --- /dev/null +++ b/spec/factories/project_statistics.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :project_statistics do + project { create :project } + namespace { project.namespace } + end +end diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb new file mode 100644 index 00000000000..4627a1e1872 --- /dev/null +++ b/spec/helpers/storage_helper_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe StorageHelper do + describe '#storage_counter' do + it 'formats bytes to one decimal place' do + expect(helper.storage_counter(1.23.megabytes)).to eq '1.2 MB' + end + + it 'does not add decimals for sizes < 1 MB' do + expect(helper.storage_counter(23.5.kilobytes)).to eq '24 KB' + end + + it 'does not add decimals for zeroes' do + expect(helper.storage_counter(2.megabytes)).to eq '2 MB' + end + + it 'uses commas as thousands separator' do + expect(helper.storage_counter(100_000_000_000_000_000)).to eq '90,949.5 TB' + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index f420d71dee2..ceed9c942c1 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -192,6 +192,7 @@ project: - authorized_users - project_authorizations - route +- statistics award_emoji: - awardable - user diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index a7e90c8a381..7e1d1126b97 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -85,4 +85,30 @@ describe Ci::Build, models: true do it { expect(build.trace_file_path).to eq(build.old_path_to_trace) } end end + + describe '#update_project_statistics' do + let!(:build) { create(:ci_build, artifacts_size: 23) } + + it 'updates project statistics when the artifact size changes' do + expect(ProjectCacheWorker).to receive(:perform_async) + .with(build.project_id, [], [:build_artifacts_size]) + + build.artifacts_size = 42 + build.save! + end + + it 'does not update project statistics when the artifact size stays the same' do + expect(ProjectCacheWorker).not_to receive(:perform_async) + + build.name = 'changed' + build.save! + end + + it 'updates project statistics when the build is destroyed' do + expect(ProjectCacheWorker).to receive(:perform_async) + .with(build.project_id, [], [:build_artifacts_size]) + + build.destroy + end + end end diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb new file mode 100644 index 00000000000..7bc278e350f --- /dev/null +++ b/spec/models/lfs_objects_project_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe LfsObjectsProject, models: true do + subject { create(:lfs_objects_project, project: project) } + let(:project) { create(:empty_project) } + + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:lfs_object) } + end + + describe 'validation' do + it { is_expected.to validate_presence_of(:lfs_object_id) } + it { is_expected.to validate_uniqueness_of(:lfs_object_id).scoped_to(:project_id).with_message("already exists in project") } + + it { is_expected.to validate_presence_of(:project_id) } + end + + describe '#update_project_statistics' do + it 'updates project statistics when the object is added' do + expect(ProjectCacheWorker).to receive(:perform_async) + .with(project.id, [], [:lfs_objects_size]) + + subject.save! + end + + it 'updates project statistics when the object is removed' do + subject.save! + + expect(ProjectCacheWorker).to receive(:perform_async) + .with(project.id, [], [:lfs_objects_size]) + + subject.destroy + end + end +end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 9fd06bb6b23..600538ff5f4 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -4,6 +4,7 @@ describe Namespace, models: true do let!(:namespace) { create(:namespace) } it { is_expected.to have_many :projects } + it { is_expected.to have_many :project_statistics } it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) } @@ -57,6 +58,50 @@ describe Namespace, models: true do end end + describe '.with_statistics' do + let(:namespace) { create :namespace } + + let(:project1) do + create(:empty_project, + namespace: namespace, + statistics: build(:project_statistics, + storage_size: 606, + repository_size: 101, + lfs_objects_size: 202, + build_artifacts_size: 303)) + end + + let(:project2) do + create(:empty_project, + namespace: namespace, + statistics: build(:project_statistics, + storage_size: 60, + repository_size: 10, + lfs_objects_size: 20, + build_artifacts_size: 30)) + end + + it "sums all project storage counters in the namespace" do + project1 + project2 + statistics = Namespace.with_statistics.find(namespace.id) + + expect(statistics.storage_size).to eq 666 + expect(statistics.repository_size).to eq 111 + expect(statistics.lfs_objects_size).to eq 222 + expect(statistics.build_artifacts_size).to eq 333 + end + + it "correctly handles namespaces without projects" do + statistics = Namespace.with_statistics.find(namespace.id) + + expect(statistics.storage_size).to eq 0 + expect(statistics.repository_size).to eq 0 + expect(statistics.lfs_objects_size).to eq 0 + expect(statistics.build_artifacts_size).to eq 0 + end + end + describe '#move_dir' do before do @namespace = create :namespace diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 88d5d14f855..fb225eb7625 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -49,6 +49,7 @@ describe Project, models: true do it { is_expected.to have_one(:gitlab_issue_tracker_service).dependent(:destroy) } it { is_expected.to have_one(:external_wiki_service).dependent(:destroy) } it { is_expected.to have_one(:project_feature).dependent(:destroy) } + it { is_expected.to have_one(:statistics).class_name('ProjectStatistics').dependent(:delete) } it { is_expected.to have_one(:import_data).class_name('ProjectImportData').dependent(:destroy) } it { is_expected.to have_one(:last_event).class_name('Event') } it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) } @@ -1729,6 +1730,26 @@ describe Project, models: true do end end + describe '#update_project_statistics' do + let(:project) { create(:empty_project) } + + it "is called after creation" do + expect(project.statistics).to be_a ProjectStatistics + expect(project.statistics).to be_persisted + end + + it "copies the namespace_id" do + expect(project.statistics.namespace_id).to eq project.namespace_id + end + + it "updates the namespace_id when changed" do + namespace = create(:namespace) + project.update(namespace: namespace) + + expect(project.statistics.namespace_id).to eq namespace.id + end + end + def enable_lfs allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb new file mode 100644 index 00000000000..77403cc9eb0 --- /dev/null +++ b/spec/models/project_statistics_spec.rb @@ -0,0 +1,160 @@ +require 'rails_helper' + +describe ProjectStatistics, models: true do + let(:project) { create :empty_project } + let(:statistics) { project.statistics } + + describe 'constants' do + describe 'STORAGE_COLUMNS' do + it 'is an array of symbols' do + expect(described_class::STORAGE_COLUMNS).to be_kind_of Array + expect(described_class::STORAGE_COLUMNS.map(&:class).uniq).to eq [Symbol] + end + end + + describe 'STATISTICS_COLUMNS' do + it 'is an array of symbols' do + expect(described_class::STATISTICS_COLUMNS).to be_kind_of Array + expect(described_class::STATISTICS_COLUMNS.map(&:class).uniq).to eq [Symbol] + end + + it 'includes all storage columns' do + expect(described_class::STATISTICS_COLUMNS & described_class::STORAGE_COLUMNS).to eq described_class::STORAGE_COLUMNS + end + end + end + + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:namespace) } + end + + describe 'statistics columns' do + it "support values up to 8 exabytes" do + statistics.update!( + commit_count: 8.exabytes - 1, + repository_size: 2.exabytes, + lfs_objects_size: 2.exabytes, + build_artifacts_size: 4.exabytes - 1, + ) + + statistics.reload + + expect(statistics.commit_count).to eq(8.exabytes - 1) + expect(statistics.repository_size).to eq(2.exabytes) + expect(statistics.lfs_objects_size).to eq(2.exabytes) + expect(statistics.build_artifacts_size).to eq(4.exabytes - 1) + expect(statistics.storage_size).to eq(8.exabytes - 1) + end + end + + describe '#total_repository_size' do + it "sums repository and LFS object size" do + statistics.repository_size = 2 + statistics.lfs_objects_size = 3 + statistics.build_artifacts_size = 4 + + expect(statistics.total_repository_size).to eq 5 + end + end + + describe '#refresh!' do + before do + allow(statistics).to receive(:update_commit_count) + allow(statistics).to receive(:update_repository_size) + allow(statistics).to receive(:update_lfs_objects_size) + allow(statistics).to receive(:update_build_artifacts_size) + allow(statistics).to receive(:update_storage_size) + end + + context "without arguments" do + before do + statistics.refresh! + end + + it "sums all counters" do + expect(statistics).to have_received(:update_commit_count) + expect(statistics).to have_received(:update_repository_size) + expect(statistics).to have_received(:update_lfs_objects_size) + expect(statistics).to have_received(:update_build_artifacts_size) + end + end + + context "when passing an only: argument" do + before do + statistics.refresh! only: [:lfs_objects_size] + end + + it "only updates the given columns" do + expect(statistics).to have_received(:update_lfs_objects_size) + expect(statistics).not_to have_received(:update_commit_count) + expect(statistics).not_to have_received(:update_repository_size) + expect(statistics).not_to have_received(:update_build_artifacts_size) + end + end + end + + describe '#update_commit_count' do + before do + allow(project.repository).to receive(:commit_count).and_return(23) + statistics.update_commit_count + end + + it "stores the number of commits in the repository" do + expect(statistics.commit_count).to eq 23 + end + end + + describe '#update_repository_size' do + before do + allow(project.repository).to receive(:size).and_return(12.megabytes) + statistics.update_repository_size + end + + it "stores the size of the repository" do + expect(statistics.repository_size).to eq 12.megabytes + end + end + + describe '#update_lfs_objects_size' do + let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) } + let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) } + let!(:lfs_objects_project1) { create(:lfs_objects_project, project: project, lfs_object: lfs_object1) } + let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) } + + before do + statistics.update_lfs_objects_size + end + + it "stores the size of related LFS objects" do + expect(statistics.lfs_objects_size).to eq 57.megabytes + end + end + + describe '#update_build_artifacts_size' do + let!(:pipeline) { create(:ci_pipeline, project: project) } + let!(:build1) { create(:ci_build, pipeline: pipeline, artifacts_size: 45.megabytes) } + let!(:build2) { create(:ci_build, pipeline: pipeline, artifacts_size: 56.megabytes) } + + before do + statistics.update_build_artifacts_size + end + + it "stores the size of related build artifacts" do + expect(statistics.build_artifacts_size).to eq 101.megabytes + end + end + + describe '#update_storage_size' do + it "sums all storage counters" do + statistics.update!( + repository_size: 2, + lfs_objects_size: 3, + ) + + statistics.reload + + expect(statistics.storage_size).to eq 5 + end + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index cdeb965b413..0e8d6faea27 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -35,6 +35,14 @@ describe API::Groups, api: true do expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(group1.name) end + + it "does not include statistics" do + get api("/groups", user1), statistics: true + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).not_to include 'statistics' + end end context "when authenticated as admin" do @@ -44,6 +52,31 @@ describe API::Groups, api: true do expect(json_response).to be_an Array expect(json_response.length).to eq(2) end + + it "does not include statistics by default" do + get api("/groups", admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).not_to include('statistics') + end + + it "includes statistics if requested" do + attributes = { + storage_size: 702, + repository_size: 123, + lfs_objects_size: 234, + build_artifacts_size: 345, + } + + project1.statistics.update!(attributes) + + get api("/groups", admin), statistics: true + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['statistics']).to eq attributes.stringify_keys + end end context "when using skip_groups in request" do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index a626b41845f..f5788d15f93 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -49,7 +49,7 @@ describe API::Projects, api: true do end end - context 'when authenticated' do + context 'when authenticated as regular user' do it 'returns an array of projects' do get api('/projects', user) expect(response).to have_http_status(200) @@ -172,6 +172,22 @@ describe API::Projects, api: true do end end end + + it "does not include statistics by default" do + get api('/projects/all', admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).not_to include('statistics') + end + + it "includes statistics if requested" do + get api('/projects/all', admin), statistics: true + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).to include 'statistics' + end end end @@ -196,6 +212,32 @@ describe API::Projects, api: true do expect(json_response.first['name']).to eq(project4.name) expect(json_response.first['owner']['username']).to eq(user4.username) end + + it "does not include statistics by default" do + get api('/projects/owned', user4) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).not_to include('statistics') + end + + it "includes statistics if requested" do + attributes = { + commit_count: 23, + storage_size: 702, + repository_size: 123, + lfs_objects_size: 234, + build_artifacts_size: 345, + } + + project4.statistics.update!(attributes) + + get api('/projects/owned', user4), statistics: true + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['statistics']).to eq attributes.stringify_keys + end end end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index e7624e70725..6ed08accb6d 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -583,7 +583,7 @@ describe GitPushService, services: true do service.push_commits = [commit] expect(ProjectCacheWorker).to receive(:perform_async). - with(project.id, %i(readme)) + with(project.id, %i(readme), %i(commit_count repository_size)) service.update_caches end @@ -596,7 +596,7 @@ describe GitPushService, services: true do it 'does not flush any conditional caches' do expect(ProjectCacheWorker).to receive(:perform_async). - with(project.id, []). + with(project.id, [], %i(commit_count repository_size)). and_call_original service.update_caches diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index 855c28b584e..f4f63b57a5f 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -1,8 +1,9 @@ require 'spec_helper' describe ProjectCacheWorker do - let(:project) { create(:project) } let(:worker) { described_class.new } + let(:project) { create(:project) } + let(:statistics) { project.statistics } describe '#perform' do before do @@ -12,7 +13,7 @@ describe ProjectCacheWorker do context 'with a non-existing project' do it 'does nothing' do - expect(worker).not_to receive(:update_repository_size) + expect(worker).not_to receive(:update_statistics) worker.perform(-1) end @@ -22,24 +23,19 @@ describe ProjectCacheWorker do it 'does nothing' do allow_any_instance_of(Repository).to receive(:exists?).and_return(false) - expect(worker).not_to receive(:update_repository_size) + expect(worker).not_to receive(:update_statistics) worker.perform(project.id) end end context 'with an existing project' do - it 'updates the repository size' do - expect(worker).to receive(:update_repository_size).and_call_original - - worker.perform(project.id) - end - - it 'updates the commit count' do - expect_any_instance_of(Project).to receive(:update_commit_count). - and_call_original + it 'updates the project statistics' do + expect(worker).to receive(:update_statistics) + .with(kind_of(Project), %i(repository_size)) + .and_call_original - worker.perform(project.id) + worker.perform(project.id, [], %w(repository_size)) end it 'refreshes the method caches' do @@ -47,33 +43,35 @@ describe ProjectCacheWorker do with(%i(readme)). and_call_original - worker.perform(project.id, %i(readme)) + worker.perform(project.id, %w(readme)) end end end - describe '#update_repository_size' do + describe '#update_statistics' do context 'when a lease could not be obtained' do it 'does not update the repository size' do allow(worker).to receive(:try_obtain_lease_for). - with(project.id, :update_repository_size). + with(project.id, :update_statistics). and_return(false) - expect(project).not_to receive(:update_repository_size) + expect(statistics).not_to receive(:refresh!) - worker.update_repository_size(project) + worker.update_statistics(project) end end context 'when a lease could be obtained' do - it 'updates the repository size' do + it 'updates the project statistics' do allow(worker).to receive(:try_obtain_lease_for). - with(project.id, :update_repository_size). + with(project.id, :update_statistics). and_return(true) - expect(project).to receive(:update_repository_size).and_call_original + expect(statistics).to receive(:refresh!) + .with(only: %i(repository_size)) + .and_call_original - worker.update_repository_size(project) + worker.update_statistics(project, %i(repository_size)) end end end -- cgit v1.2.1 From a3bb2463c41124d6d7f5927bbff578a4991c15cb Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Fri, 11 Nov 2016 14:09:49 +0900 Subject: switch to email_reply_trimmer from discourse --- Gemfile | 2 +- Gemfile.lock | 4 ++-- lib/gitlab/email/reply_parser.rb | 30 ++++-------------------------- spec/lib/gitlab/email/reply_parser_spec.rb | 4 +--- 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/Gemfile b/Gemfile index 9dfaf7a48a2..fff808e9805 100644 --- a/Gemfile +++ b/Gemfile @@ -332,7 +332,7 @@ gem 'octokit', '~> 4.3.0' gem 'mail_room', '~> 0.9.0' -gem 'email_reply_parser', '~> 0.5.8' +gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' gem 'ruby-prof', '~> 0.16.2' diff --git a/Gemfile.lock b/Gemfile.lock index 9f8367b420a..765d57c6238 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,7 +167,7 @@ GEM railties (>= 4.2) dropzonejs-rails (0.7.2) rails (> 3.1) - email_reply_parser (0.5.8) + email_reply_trimmer (0.1.6) email_spec (1.6.0) launchy (~> 2.1) mail (~> 2.2) @@ -839,7 +839,7 @@ DEPENDENCIES diffy (~> 3.1.0) doorkeeper (~> 4.2.0) dropzonejs-rails (~> 0.7.1) - email_reply_parser (~> 0.5.8) + email_reply_trimmer (~> 0.1) email_spec (~> 1.6.0) factory_girl_rails (~> 4.7.0) ffaker (~> 2.0.0) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index f586c5ab062..e53a99cd8e0 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -13,9 +13,11 @@ module Gitlab encoding = body.encoding - body = discourse_email_trimmer(body) + body = EmailReplyTrimmer.trim(body) - body = EmailReplyParser.parse_reply(body) + # TODO [jneen]: do we want to allow empty-quoting? (replies only containing a blockquote) + # EmailReplyTrimmer allows this as a special case, so we detect it manually here. + return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } body.force_encoding(encoding).encode("UTF-8") end @@ -57,30 +59,6 @@ module Gitlab rescue nil end - - REPLYING_HEADER_LABELS = %w(From Sent To Subject Reply To Cc Bcc Date) - REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |label| "#{label}:" }) - - def discourse_email_trimmer(body) - lines = body.scrub.lines.to_a - range_end = 0 - - lines.each_with_index do |l, idx| - # This one might be controversial but so many reply lines have years, times and end with a colon. - # Let's try it and see how well it works. - break if (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) || - (l =~ /On \w+ \d+,? \d+,?.*wrote:/) - - # Headers on subsequent lines - break if (0..2).all? { |off| lines[idx + off] =~ REPLYING_HEADER_REGEX } - # Headers on the same line - break if REPLYING_HEADER_LABELS.count { |label| l.include?(label) } >= 3 - - range_end = idx - end - - lines[0..range_end].join.strip - end end end end diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index c7a0139d32a..106f2b0f10a 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -88,8 +88,6 @@ describe Gitlab::Email::ReplyParser, lib: true do expect(test_parse_body(fixture_file("emails/inline_reply.eml"))). to eq( <<-BODY.strip_heredoc.chomp - On Wed, Oct 8, 2014 at 11:12 AM, techAPJ wrote: - > techAPJ > November 28 > @@ -158,7 +156,7 @@ describe Gitlab::Email::ReplyParser, lib: true do <<-BODY.strip_heredoc.chomp ### this is a reply from iOS default mail - The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. Here's some **bold** markdown text. -- cgit v1.2.1 From 03753ff146a448eb66e7af12f56dcc4cf11922d9 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Tue, 15 Nov 2016 16:58:37 +0900 Subject: remove trailing whitespace from email bodies --- lib/gitlab/email/reply_parser.rb | 3 +++ spec/lib/gitlab/email/reply_parser_spec.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index e53a99cd8e0..ed8c1cd09f8 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -15,6 +15,9 @@ module Gitlab body = EmailReplyTrimmer.trim(body) + # [jneen] not using /\s+$/ here because that deletes empty lines + body = body.gsub(/[ \t]$/, '') + # TODO [jneen]: do we want to allow empty-quoting? (replies only containing a blockquote) # EmailReplyTrimmer allows this as a special case, so we detect it manually here. return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 106f2b0f10a..28698e89c33 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -156,7 +156,7 @@ describe Gitlab::Email::ReplyParser, lib: true do <<-BODY.strip_heredoc.chomp ### this is a reply from iOS default mail - The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. Here's some **bold** markdown text. -- cgit v1.2.1 From 017579fe149e976fb0b2b21b234ea923913cadb2 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Fri, 18 Nov 2016 18:57:37 +0900 Subject: protect against EmailReplyTrimmer returning nil --- lib/gitlab/email/reply_parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index ed8c1cd09f8..e714923c4d8 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -13,7 +13,7 @@ module Gitlab encoding = body.encoding - body = EmailReplyTrimmer.trim(body) + body = EmailReplyTrimmer.trim(body) or return "" # [jneen] not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') -- cgit v1.2.1 From 602c3198626ae2a23f8f2962963c18f3b3bf1664 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Tue, 22 Nov 2016 15:25:09 +0900 Subject: allow empty-quotes --- lib/gitlab/email/reply_parser.rb | 4 ---- spec/fixtures/emails/empty_quote.eml | 34 ++++++++++++++++++++++++++++++ spec/fixtures/emails/no_content_reply.eml | 34 ------------------------------ spec/lib/gitlab/email/reply_parser_spec.rb | 19 +++++++++++++++-- 4 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 spec/fixtures/emails/empty_quote.eml delete mode 100644 spec/fixtures/emails/no_content_reply.eml diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index e714923c4d8..5957cbbad7e 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -18,10 +18,6 @@ module Gitlab # [jneen] not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') - # TODO [jneen]: do we want to allow empty-quoting? (replies only containing a blockquote) - # EmailReplyTrimmer allows this as a special case, so we detect it manually here. - return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } - body.force_encoding(encoding).encode("UTF-8") end diff --git a/spec/fixtures/emails/empty_quote.eml b/spec/fixtures/emails/empty_quote.eml new file mode 100644 index 00000000000..95eb2055ce6 --- /dev/null +++ b/spec/fixtures/emails/empty_quote.eml @@ -0,0 +1,34 @@ +Return-Path: +Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +Date: Thu, 13 Jun 2013 17:03:48 -0400 +From: Jake the Dog +To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo +Message-ID: +Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Mime-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +X-Sieve: CMU Sieve 2.2 +X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, + 13 Jun 2013 14:03:48 -0700 (PDT) +X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 + +On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta + wrote: +> +> +> +> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: +> +> --- +> hey guys everyone knows adventure time sucks! +> +> --- +> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 +> +> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). +> \ No newline at end of file diff --git a/spec/fixtures/emails/no_content_reply.eml b/spec/fixtures/emails/no_content_reply.eml deleted file mode 100644 index 95eb2055ce6..00000000000 --- a/spec/fixtures/emails/no_content_reply.eml +++ /dev/null @@ -1,34 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> \ No newline at end of file diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 28698e89c33..138ad814262 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -15,8 +15,23 @@ describe Gitlab::Email::ReplyParser, lib: true do expect(test_parse_body("asdf" * 30)).to eq("") end - it "returns an empty string if there is no reply content" do - expect(test_parse_body(fixture_file("emails/no_content_reply.eml"))).to eq("") + it "returns the quote for an empty-quoted comment" do + expect(test_parse_body(fixture_file("emails/empty_quote.eml"))) + .to eq(<<-BODY.strip_heredoc.chomp) + > + > + > + > eviltrout posted in 'Adventure Time Sux' on Discourse Meta: + > + > --- + > hey guys everyone knows adventure time sucks! + > + > --- + > Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 + > + > To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). + > + BODY end it "properly renders plaintext-only email" do -- cgit v1.2.1 From 56031276993347a7fba542c67c640fdae79df716 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Tue, 22 Nov 2016 15:37:30 +0900 Subject: remove comment attribution --- lib/gitlab/email/reply_parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 5957cbbad7e..96d0a4f5153 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -15,7 +15,7 @@ module Gitlab body = EmailReplyTrimmer.trim(body) or return "" - # [jneen] not using /\s+$/ here because that deletes empty lines + # not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') body.force_encoding(encoding).encode("UTF-8") -- cgit v1.2.1 From f02f238d52f061ee386438fb8bbe14b379e41f42 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Tue, 13 Dec 2016 12:14:38 -0800 Subject: Revert "allow empty-quotes" This reverts commit 4f2f678aff8d7dfcac96c47cf7eb480a5707ddaf. --- lib/gitlab/email/reply_parser.rb | 4 ++++ spec/fixtures/emails/empty_quote.eml | 34 ------------------------------ spec/fixtures/emails/no_content_reply.eml | 34 ++++++++++++++++++++++++++++++ spec/lib/gitlab/email/reply_parser_spec.rb | 19 ++--------------- 4 files changed, 40 insertions(+), 51 deletions(-) delete mode 100644 spec/fixtures/emails/empty_quote.eml create mode 100644 spec/fixtures/emails/no_content_reply.eml diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 96d0a4f5153..971afe4a878 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -18,6 +18,10 @@ module Gitlab # not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') + # TODO [jneen]: do we want to allow empty-quoting? (replies only containing a blockquote) + # EmailReplyTrimmer allows this as a special case, so we detect it manually here. + return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } + body.force_encoding(encoding).encode("UTF-8") end diff --git a/spec/fixtures/emails/empty_quote.eml b/spec/fixtures/emails/empty_quote.eml deleted file mode 100644 index 95eb2055ce6..00000000000 --- a/spec/fixtures/emails/empty_quote.eml +++ /dev/null @@ -1,34 +0,0 @@ -Return-Path: -Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 -Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 -Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 -Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 -Date: Thu, 13 Jun 2013 17:03:48 -0400 -From: Jake the Dog -To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo -Message-ID: -Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' -Mime-Version: 1.0 -Content-Type: text/plain; - charset=ISO-8859-1 -Content-Transfer-Encoding: 7bit -X-Sieve: CMU Sieve 2.2 -X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, - 13 Jun 2013 14:03:48 -0700 (PDT) -X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 - -On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta - wrote: -> -> -> -> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: -> -> --- -> hey guys everyone knows adventure time sucks! -> -> --- -> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 -> -> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). -> \ No newline at end of file diff --git a/spec/fixtures/emails/no_content_reply.eml b/spec/fixtures/emails/no_content_reply.eml new file mode 100644 index 00000000000..95eb2055ce6 --- /dev/null +++ b/spec/fixtures/emails/no_content_reply.eml @@ -0,0 +1,34 @@ +Return-Path: +Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +Date: Thu, 13 Jun 2013 17:03:48 -0400 +From: Jake the Dog +To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo +Message-ID: +Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Mime-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +X-Sieve: CMU Sieve 2.2 +X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, + 13 Jun 2013 14:03:48 -0700 (PDT) +X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 + +On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta + wrote: +> +> +> +> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: +> +> --- +> hey guys everyone knows adventure time sucks! +> +> --- +> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 +> +> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). +> \ No newline at end of file diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 138ad814262..28698e89c33 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -15,23 +15,8 @@ describe Gitlab::Email::ReplyParser, lib: true do expect(test_parse_body("asdf" * 30)).to eq("") end - it "returns the quote for an empty-quoted comment" do - expect(test_parse_body(fixture_file("emails/empty_quote.eml"))) - .to eq(<<-BODY.strip_heredoc.chomp) - > - > - > - > eviltrout posted in 'Adventure Time Sux' on Discourse Meta: - > - > --- - > hey guys everyone knows adventure time sucks! - > - > --- - > Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 - > - > To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). - > - BODY + it "returns an empty string if there is no reply content" do + expect(test_parse_body(fixture_file("emails/no_content_reply.eml"))).to eq("") end it "properly renders plaintext-only email" do -- cgit v1.2.1 From 1b8a576d3fe9d283ad20ebb863bd11ec58eb5fb5 Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Tue, 13 Dec 2016 12:15:42 -0800 Subject: Be more certain in the comment --- lib/gitlab/email/reply_parser.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 971afe4a878..7dedd5ec4aa 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -18,8 +18,9 @@ module Gitlab # not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') - # TODO [jneen]: do we want to allow empty-quoting? (replies only containing a blockquote) - # EmailReplyTrimmer allows this as a special case, so we detect it manually here. + # NOTE: We currently don't support empty quotes. + # EmailReplyTrimmer allows this as a special case, + # so we detect it manually here. return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } body.force_encoding(encoding).encode("UTF-8") -- cgit v1.2.1 From 9b66aa6e04ab66af7ce11e26076ebf431ca938c5 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Mon, 19 Dec 2016 10:26:12 +0000 Subject: Prevent empty pagination when list is not empty --- app/controllers/concerns/kaminari_pagination.rb | 8 ++++++++ app/controllers/dashboard/todos_controller.rb | 4 +++- app/controllers/projects/issues_controller.rb | 3 ++- app/controllers/projects/merge_requests_controller.rb | 3 ++- app/controllers/projects/snippets_controller.rb | 3 ++- .../19988-prevent-empty-pagination-when-list-not-empty.yml | 4 ++++ 6 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 app/controllers/concerns/kaminari_pagination.rb create mode 100644 changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml diff --git a/app/controllers/concerns/kaminari_pagination.rb b/app/controllers/concerns/kaminari_pagination.rb new file mode 100644 index 00000000000..b69340cac0e --- /dev/null +++ b/app/controllers/concerns/kaminari_pagination.rb @@ -0,0 +1,8 @@ +module KaminariPagination + extend ActiveSupport::Concern + + def bounded_pagination(items, page_number) + items = items.page(page_number) + items.to_a.empty? ? items.page(items.total_pages) : items + end +end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index d425d0f9014..b2333a02392 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -1,9 +1,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController + include KaminariPagination + before_action :find_todos, only: [:index, :destroy_all] def index @sort = params[:sort] - @todos = @todos.page(params[:page]) + @todos = bounded_pagination(@todos, params[:page]) end def destroy diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4f66e01e0f7..ec09a5297fc 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -5,6 +5,7 @@ class Projects::IssuesController < Projects::ApplicationController include ToggleAwardEmoji include IssuableCollections include SpammableActions + include KaminariPagination before_action :redirect_to_external_issue_tracker, only: [:index, :new] before_action :module_enabled @@ -24,7 +25,7 @@ class Projects::IssuesController < Projects::ApplicationController def index @issues = issues_collection - @issues = @issues.page(params[:page]) + @issues = bounded_pagination(@issues, params[:page]) if params[:label_name].present? @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 3abebdfd032..e25eea52723 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -6,6 +6,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController include NotesHelper include ToggleAwardEmoji include IssuableCollections + include KaminariPagination before_action :module_enabled before_action :merge_request, only: [ @@ -37,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController def index @merge_requests = merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]) + @merge_requests = bounded_pagination(@merge_requests, params[:page]) if params[:label_name].present? labels_params = { project_id: @project.id, title: params[:label_name] } diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 0720be2e55d..7083b29b6a3 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -1,5 +1,6 @@ class Projects::SnippetsController < Projects::ApplicationController include ToggleAwardEmoji + include KaminariPagination before_action :module_enabled before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji] @@ -25,7 +26,7 @@ class Projects::SnippetsController < Projects::ApplicationController project: @project, scope: params[:scope] ) - @snippets = @snippets.page(params[:page]) + @snippets = bounded_pagination(@snippets, params[:page]) end def new diff --git a/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml b/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml new file mode 100644 index 00000000000..5570ede4a9a --- /dev/null +++ b/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml @@ -0,0 +1,4 @@ +--- +title: Prevent empty pagination when list is not empty +merge_request: 8172 +author: -- cgit v1.2.1 From 805bbe889328bff55b49290294721405148cc980 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Tue, 20 Dec 2016 18:52:09 +0000 Subject: adds specs for respective behaviour --- app/controllers/concerns/kaminari_pagination.rb | 8 ----- app/controllers/dashboard/todos_controller.rb | 7 +++-- app/controllers/projects/issues_controller.rb | 6 ++-- .../projects/merge_requests_controller.rb | 6 ++-- app/controllers/projects/snippets_controller.rb | 6 ++-- .../controllers/dashboard/todos_controller_spec.rb | 36 ++++++++++++++++++++++ .../controllers/projects/issues_controller_spec.rb | 29 +++++++++++++++++ .../projects/merge_requests_controller_spec.rb | 22 +++++++++++-- .../projects/snippets_controller_spec.rb | 22 +++++++++++++ 9 files changed, 123 insertions(+), 19 deletions(-) delete mode 100644 app/controllers/concerns/kaminari_pagination.rb create mode 100644 spec/controllers/dashboard/todos_controller_spec.rb diff --git a/app/controllers/concerns/kaminari_pagination.rb b/app/controllers/concerns/kaminari_pagination.rb deleted file mode 100644 index b69340cac0e..00000000000 --- a/app/controllers/concerns/kaminari_pagination.rb +++ /dev/null @@ -1,8 +0,0 @@ -module KaminariPagination - extend ActiveSupport::Concern - - def bounded_pagination(items, page_number) - items = items.page(page_number) - items.to_a.empty? ? items.page(items.total_pages) : items - end -end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index b2333a02392..40652129f4c 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -1,11 +1,12 @@ class Dashboard::TodosController < Dashboard::ApplicationController - include KaminariPagination - before_action :find_todos, only: [:index, :destroy_all] def index @sort = params[:sort] - @todos = bounded_pagination(@todos, params[:page]) + @todos = @todos.page(params[:page]) + if @todos.out_of_range? && @todos.total_pages != 0 + redirect_to dashboard_todos_path(page: @todos.total_pages) + end end def destroy diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ec09a5297fc..0efeec98570 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -5,7 +5,6 @@ class Projects::IssuesController < Projects::ApplicationController include ToggleAwardEmoji include IssuableCollections include SpammableActions - include KaminariPagination before_action :redirect_to_external_issue_tracker, only: [:index, :new] before_action :module_enabled @@ -25,7 +24,10 @@ class Projects::IssuesController < Projects::ApplicationController def index @issues = issues_collection - @issues = bounded_pagination(@issues, params[:page]) + @issues = @issues.page(params[:page]) + if @issues.out_of_range? && @issues.total_pages != 0 + return redirect_to namespace_project_issues_path(page: @issues.total_pages) + end if params[:label_name].present? @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index e25eea52723..050d0ca77ae 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -6,7 +6,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController include NotesHelper include ToggleAwardEmoji include IssuableCollections - include KaminariPagination before_action :module_enabled before_action :merge_request, only: [ @@ -38,7 +37,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController def index @merge_requests = merge_requests_collection - @merge_requests = bounded_pagination(@merge_requests, params[:page]) + @merge_requests = @merge_requests.page(params[:page]) + if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 + return redirect_to namespace_project_merge_requests_path(page: @merge_requests.total_pages) + end if params[:label_name].present? labels_params = { project_id: @project.id, title: params[:label_name] } diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 7083b29b6a3..02a97c1c574 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -1,6 +1,5 @@ class Projects::SnippetsController < Projects::ApplicationController include ToggleAwardEmoji - include KaminariPagination before_action :module_enabled before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji] @@ -26,7 +25,10 @@ class Projects::SnippetsController < Projects::ApplicationController project: @project, scope: params[:scope] ) - @snippets = bounded_pagination(@snippets, params[:page]) + @snippets = @snippets.page(params[:page]) + if @snippets.out_of_range? && @snippets.total_pages != 0 + redirect_to namespace_project_snippets_path(page: @snippets.total_pages) + end end def new diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb new file mode 100644 index 00000000000..a8c08a6e87b --- /dev/null +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Dashboard::TodosController do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:todo_service) { TodoService.new } + + describe 'GET #index' do + before do + sign_in(user) + project.team << [user, :developer] + end + + context 'when using pagination' do + let(:last_page) { user.todos.page().total_pages } + let!(:issues) { create_list(:issue, 30, project: project, assignee: user) } + + before do + issues.each { |issue| todo_service.new_issue(issue, user) } + end + + it 'redirects to last_page if page number is larger than number of pages' do + get :index, page: (last_page + 1).to_param + + expect(response).to redirect_to(dashboard_todos_path(page: last_page)) + end + + it 'redirects to correspondent page' do + get :index, page: last_page + + expect(assigns(:todos).current_page).to eq(last_page) + expect(response).to have_http_status(200) + end + end + end +end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index dbe5ddccbcf..60b21cb4ace 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -52,6 +52,35 @@ describe Projects::IssuesController do expect(response).to have_http_status(404) end end + + context 'with page param' do + let(:last_page) { project.issues.page().total_pages } + let!(:issue_list) { create_list(:issue, 30, project: project) } + + before do + sign_in(user) + project.team << [user, :developer] + end + + it 'redirects to last_page if page number is larger than number of pages' do + get :index, + namespace_id: project.namespace.path.to_param, + project_id: project.path.to_param, + page: (last_page + 1).to_param + + expect(response).to redirect_to(namespace_project_issues_path(page: last_page)) + end + + it 'redirects to specified page' do + get :index, + namespace_id: project.namespace.path.to_param, + project_id: project.path.to_param, + page: last_page.to_param + + expect(assigns(:issues).current_page).to eq(last_page) + expect(response).to have_http_status(200) + end + end end describe 'GET #new' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 440b897ddc6..92392172af0 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -127,11 +127,29 @@ describe Projects::MergeRequestsController do end describe 'GET index' do - def get_merge_requests + let(:last_page) { project.merge_requests.page().total_pages } + let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + + def get_merge_requests(page = nil) get :index, namespace_id: project.namespace.to_param, project_id: project.to_param, - state: 'opened' + state: 'opened', page: page.to_param + end + + context 'when page param' do + it 'redirects to last_page if page number is larger than number of pages' do + get_merge_requests(last_page + 1) + + expect(response).to redirect_to(namespace_project_merge_requests_path(page: last_page)) + end + + it 'redirects to specified page' do + get_merge_requests(last_page) + + expect(assigns(:merge_requests).current_page).to eq(last_page) + expect(response).to have_http_status(200) + end end context 'when filtering by opened state' do diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 72a3ebf2ebd..82e57e4d7dc 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -11,6 +11,28 @@ describe Projects::SnippetsController do end describe 'GET #index' do + context 'when page param' do + let(:last_page) { project.snippets.page().total_pages } + let!(:project_snippet) { create(:project_snippet, :public, project: project, author: user) } + + it 'redirects to last_page if page number is larger than number of pages' do + get :index, + namespace_id: project.namespace.path, + project_id: project.path, page: (last_page + 1).to_param + + expect(response).to redirect_to(namespace_project_snippets_path(page: last_page)) + end + + it 'redirects to specified page' do + get :index, + namespace_id: project.namespace.path, + project_id: project.path, page: (last_page).to_param + + expect(assigns(:snippets).current_page).to eq(last_page) + expect(response).to have_http_status(200) + end + end + context 'when the project snippet is private' do let!(:project_snippet) { create(:project_snippet, :private, project: project, author: user) } -- cgit v1.2.1 From 7218daaa96badbaadf6e94b6729a74a15273935d Mon Sep 17 00:00:00 2001 From: "http://jneen.net/" Date: Thu, 22 Dec 2016 00:10:35 -0800 Subject: fix guard style --- lib/gitlab/email/reply_parser.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 7dedd5ec4aa..8c8dd1b9cef 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -13,7 +13,9 @@ module Gitlab encoding = body.encoding - body = EmailReplyTrimmer.trim(body) or return "" + body = EmailReplyTrimmer.trim(body) + + return '' unless body # not using /\s+$/ here because that deletes empty lines body = body.gsub(/[ \t]$/, '') -- cgit v1.2.1 From a548b70851813f71c57da59769fade9495a0d2a9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 22 Dec 2016 10:03:30 +0000 Subject: Fixed discussion note resolve button SVG color --- app/assets/stylesheets/pages/notes.scss | 8 ++------ changelogs/unreleased/resolve-note-svg-color.yml | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/resolve-note-svg-color.yml diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 6ac4ec6ea0d..c3edf8a65b5 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -566,18 +566,14 @@ ul.notes { &.is-active { color: $gl-text-green; - svg path { + svg { fill: $gl-text-green; } } svg { position: relative; - color: $gray-darkest; - - path { - fill: $gray-darkest; - } + fill: $gray-darkest; } } diff --git a/changelogs/unreleased/resolve-note-svg-color.yml b/changelogs/unreleased/resolve-note-svg-color.yml new file mode 100644 index 00000000000..500d4e9d5b1 --- /dev/null +++ b/changelogs/unreleased/resolve-note-svg-color.yml @@ -0,0 +1,4 @@ +--- +title: Fixed resolve discussion note button color +merge_request: +author: -- cgit v1.2.1 From e008e14c7b2d3d5596d767dfb51a693f0746dfbb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 22 Dec 2016 11:59:41 +0100 Subject: Fix single team selection --- app/views/projects/mattermosts/_team_selection.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index 7980f7c9a72..24e86b8497f 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -8,7 +8,9 @@ = @teams.one? ? 'The team' : 'Select the team' where the slash commands will be used in - selected_id = @teams.keys.first if @teams.one? - = f.select(:team_id, mattermost_teams_options(@teams), {}, { class: 'form-control', selected: "#{selected_id}", disabled: @teams.one? }) + - options = mattermost_teams_options(@teams) + - options = options_for_select(options, selected_id) + = f.select(:team_id, options, {}, { class: 'form-control', selected: "#{selected_id}" }) .help-block - if @teams.one? This is the only team where you are an administrator. -- cgit v1.2.1 From c6f0f8f0d4ab44f5f770b248383c783aee306192 Mon Sep 17 00:00:00 2001 From: Allison Whilden Date: Tue, 20 Dec 2016 11:52:05 -0800 Subject: [ci skip] UX Guide: add comment and discussion to copy [ci skip] UX Guide: refine discussion definition --- doc/development/ux_guide/copy.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md index 8896d450f14..e48897426cb 100644 --- a/doc/development/ux_guide/copy.md +++ b/doc/development/ux_guide/copy.md @@ -101,3 +101,11 @@ The form should be titled `Edit issue`. The submit button should be labeled `Sav | Approve | Approve an open merge request || | Remove approval, unapproved | Remove approval of an open merge request | Do not use `unapprove` as that is not an English word| | Merge | Merge an open merge request || + +### Comments & Discussions + +#### Comment +A **comment** is a written piece of text that users of GitLab can create. Comments have the meta data of author and time stamp. Comments can be added in a variety of contexts, such as issues, merge requests, and discussions. + +#### Dicussion +A **discussion** is a group of 1 or more comments. A discussion can include sub discussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved. \ No newline at end of file -- cgit v1.2.1 From 0b21d6748be2438c5016299025bd99500409d094 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 23 Dec 2016 00:24:51 +0600 Subject: fixes timestamp text alignemnt in mr-list and issue-list --- app/assets/stylesheets/framework/lists.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index e96cd671e34..28a0f9871a2 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -239,7 +239,6 @@ ul.content-list { } ul.controls { - padding-top: 1px; float: right; list-style: none; -- cgit v1.2.1 From 439d913c22871601a701f57f5bb1000a4f91460c Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 23 Dec 2016 00:39:33 +0600 Subject: changelog added --- changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml diff --git a/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml b/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml new file mode 100644 index 00000000000..454d8799432 --- /dev/null +++ b/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml @@ -0,0 +1,4 @@ +--- +title: Fix mr list timestamp alignment +merge_request: 8271 +author: -- cgit v1.2.1 From 77da936b73947a224d5179bec787dee72266555c Mon Sep 17 00:00:00 2001 From: Allison Whilden Date: Tue, 20 Dec 2016 11:15:43 -0800 Subject: [ci skip] UX Guide: split state and action guidance for color [ci skip] UX Guide: Add retry to blue --- doc/development/ux_guide/basics.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/development/ux_guide/basics.md b/doc/development/ux_guide/basics.md index e81729556d8..76ec7fd466b 100644 --- a/doc/development/ux_guide/basics.md +++ b/doc/development/ux_guide/basics.md @@ -50,13 +50,13 @@ GitLab uses Font Awesome icons throughout our interface. ## Color -| | | -| :------: | :------- | -| ![Blue](img/color-blue.png) | Blue is used to highlight primary active elements (such as the current tab), as well as other organizational and managing commands.| -| ![Green](img/color-green.png) | Green is for actions that create new objects. | -| ![Orange](img/color-orange.png) | Orange is used for warnings | -| ![Red](img/color-red.png) | Red is reserved for delete and other destructive commands | -| ![Grey](img/color-grey.png) | Grey is used for neutral secondary elements. Depending on context, white is sometimes used instead. | +| | State | Action | +| :------: | :------- | :------- | +| ![Blue](img/color-blue.png) | Primary and active (such as the current tab) | Organizational, managing, and retry commands| +| ![Green](img/color-green.png) | Opened | Create new objects | +| ![Orange](img/color-orange.png) | Warning | Non destructive action | +| ![Red](img/color-red.png) | Closed | Delete and other destructive commands | +| ![Grey](img/color-grey.png) | Neutral | Neutral secondary commands | > TODO: Establish a perspective for color in terms of our personality and rationalize with Marketing usage. -- cgit v1.2.1 From c46022413585e14013da4575b05dd094439c4cd7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 23 Dec 2016 09:05:01 +0000 Subject: Fixed error with GFM autocomplete when no data exists --- app/assets/javascripts/gfm_auto_complete.js.es6 | 2 +- changelogs/unreleased/label-gfm-error-fix.yml | 4 ++++ spec/features/issues/gfm_autocomplete_spec.rb | 13 ++++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/label-gfm-error-fix.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 12875eaa1c3..3857bbb743b 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -367,7 +367,7 @@ return $input.trigger('keyup'); }, isLoading(data) { - if (!data) return false; + if (!data || !data.length) return false; if (Array.isArray(data)) data = data[0]; return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0]; }, diff --git a/changelogs/unreleased/label-gfm-error-fix.yml b/changelogs/unreleased/label-gfm-error-fix.yml new file mode 100644 index 00000000000..37f311d4790 --- /dev/null +++ b/changelogs/unreleased/label-gfm-error-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed GFM autocomplete error when no data exists +merge_request: +author: diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index d0294908d2c..3489331a1b6 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -47,7 +47,7 @@ feature 'GFM autocomplete', feature: true, js: true do expect_to_wrap(true, label_item, note, label.title) end - it "does not show drpdown when preceded with a special character" do + it "does not show dropdown when preceded with a special character" do note = find('#note_note') page.within '.timeline-content-form' do note.native.send_keys('') @@ -65,6 +65,17 @@ feature 'GFM autocomplete', feature: true, js: true do expect(page).to have_selector('.atwho-container', visible: false) end + it "does not throw an error if no labels exist" do + note = find('#note_note') + page.within '.timeline-content-form' do + note.native.send_keys('') + note.native.send_keys('~') + note.click + end + + expect(page).to have_selector('.atwho-container', visible: false) + end + it 'doesn\'t wrap for assignee values' do note = find('#note_note') page.within '.timeline-content-form' do -- cgit v1.2.1 From db8ee6672cf49a2eed4e244080246f21a9e14dd9 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 23 Dec 2016 11:08:09 +0100 Subject: Rename "autodeploy" to "auto deploy" --- app/views/projects/show.html.haml | 4 ++-- changelogs/unreleased/auto-deploy-with-space.yml | 4 ++++ doc/ci/README.md | 2 +- doc/ci/autodeploy/img/auto_deploy_button.png | Bin 0 -> 43441 bytes doc/ci/autodeploy/img/auto_deploy_dropdown.png | Bin 0 -> 44380 bytes doc/ci/autodeploy/img/autodeploy_button.png | Bin 41799 -> 0 bytes doc/ci/autodeploy/img/autodeploy_dropdown.png | Bin 51420 -> 0 bytes doc/ci/autodeploy/index.md | 14 +++++++------- lib/gitlab/template/gitlab_ci_yml_template.rb | 4 ++-- spec/features/auto_deploy_spec.rb | 14 +++++++------- 10 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/auto-deploy-with-space.yml create mode 100644 doc/ci/autodeploy/img/auto_deploy_button.png create mode 100644 doc/ci/autodeploy/img/auto_deploy_dropdown.png delete mode 100644 doc/ci/autodeploy/img/autodeploy_button.png delete mode 100644 doc/ci/autodeploy/img/autodeploy_dropdown.png diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a915c159cb4..f9776628c77 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -70,8 +70,8 @@ = link_to 'Set up Koding', add_koding_stack_path(@project) - if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? %li.missing - = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up autodeploy', target_branch: 'autodeploy', context: 'autodeploy') do - Set up autodeploy + = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do + Set up auto deploy - if @repository.commit .project-last-commit{ class: container_class } diff --git a/changelogs/unreleased/auto-deploy-with-space.yml b/changelogs/unreleased/auto-deploy-with-space.yml new file mode 100644 index 00000000000..a206e3ae85c --- /dev/null +++ b/changelogs/unreleased/auto-deploy-with-space.yml @@ -0,0 +1,4 @@ +--- +title: Rename "autodeploy" to "auto deploy" +merge_request: +author: diff --git a/doc/ci/README.md b/doc/ci/README.md index 6a9495f8892..dd14698e9cd 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -23,7 +23,7 @@ - [CI/CD pipelines settings](../user/project/pipelines/settings.md) - [Review Apps](review_apps/index.md) - [Git submodules](git_submodules.md) Using Git submodules in your CI jobs -- [Autodeploy](autodeploy/index.md) +- [Auto deploy](autodeploy/index.md) ## Breaking changes diff --git a/doc/ci/autodeploy/img/auto_deploy_button.png b/doc/ci/autodeploy/img/auto_deploy_button.png new file mode 100644 index 00000000000..423e76a6cda Binary files /dev/null and b/doc/ci/autodeploy/img/auto_deploy_button.png differ diff --git a/doc/ci/autodeploy/img/auto_deploy_dropdown.png b/doc/ci/autodeploy/img/auto_deploy_dropdown.png new file mode 100644 index 00000000000..957870ec8c7 Binary files /dev/null and b/doc/ci/autodeploy/img/auto_deploy_dropdown.png differ diff --git a/doc/ci/autodeploy/img/autodeploy_button.png b/doc/ci/autodeploy/img/autodeploy_button.png deleted file mode 100644 index 9e2cd57a0ba..00000000000 Binary files a/doc/ci/autodeploy/img/autodeploy_button.png and /dev/null differ diff --git a/doc/ci/autodeploy/img/autodeploy_dropdown.png b/doc/ci/autodeploy/img/autodeploy_dropdown.png deleted file mode 100644 index d2733de83df..00000000000 Binary files a/doc/ci/autodeploy/img/autodeploy_dropdown.png and /dev/null differ diff --git a/doc/ci/autodeploy/index.md b/doc/ci/autodeploy/index.md index 9c79d8c457e..630207ffa09 100644 --- a/doc/ci/autodeploy/index.md +++ b/doc/ci/autodeploy/index.md @@ -1,8 +1,8 @@ -# Autodeploy +# Auto deploy > [Introduced][mr-8135] in GitLab 8.15. -Autodeploy is an easy way to configure GitLab CI for the deployment of your +Auto deploy is an easy way to configure GitLab CI for the deployment of your application. GitLab Community maintains a list of `.gitlab-ci.yml` templates for various infrastructure providers and deployment scripts powering them. These scripts are responsible for packaging your application, @@ -15,7 +15,7 @@ deployment. ## Supported templates -The list of supported autodeploy templates is available [here][autodeploy-templates]. +The list of supported auto deploy templates is available [here][auto-deploy-templates]. ## Configuration @@ -24,17 +24,17 @@ credentials. For example, if you want to deploy to OpenShift you have to enable [Kubernetes service][kubernetes-service]. 1. Configure GitLab Runner to use Docker or Kubernetes executor with [privileged mode enabled][docker-in-docker]. -1. Navigate to the "Project" tab and click "Set up autodeploy" button. - ![Autodeploy button](img/autodeploy_button.png) +1. Navigate to the "Project" tab and click "Set up auto deploy" button. + ![Auto deploy button](img/auto_deploy_button.png) 1. Select a template. - ![Dropdown with autodeploy templates](img/autodeploy_dropdown.png) + ![Dropdown with auto deploy templates](img/auto_deploy_dropdown.png) 1. Commit your changes and create a merge request. 1. Test your deployment configuration using a [Review App][review-app] that was created automatically for you. [mr-8135]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8135 [project-services]: ../../project_services/project_services.md -[autodeploy-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml/tree/master/autodeploy +[auto-deploy-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml/tree/master/autodeploy [kubernetes-service]: ../../project_services/kubernetes.md [docker-in-docker]: ../docker/using_docker_build.md#use-docker-in-docker-executor [review-app]: ../review_apps/index.md diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb index d19b0a52043..9d2ecee9756 100644 --- a/lib/gitlab/template/gitlab_ci_yml_template.rb +++ b/lib/gitlab/template/gitlab_ci_yml_template.rb @@ -15,7 +15,7 @@ module Gitlab { 'General' => '', 'Pages' => 'Pages', - 'Autodeploy' => 'autodeploy' + 'Auto deploy' => 'autodeploy' } end @@ -28,7 +28,7 @@ module Gitlab end def dropdown_names(context) - categories = context == 'autodeploy' ? ['Autodeploy'] : ['General', 'Pages'] + categories = context == 'autodeploy' ? ['Auto deploy'] : ['General', 'Pages'] super().slice(*categories) end end diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb index 92f1ab90881..ea7a97d1d4f 100644 --- a/spec/features/auto_deploy_spec.rb +++ b/spec/features/auto_deploy_spec.rb @@ -26,7 +26,7 @@ describe 'Auto deploy' do it 'does not show a button to set up auto deploy' do visit namespace_project_path(project.namespace, project) - expect(page).to have_no_content('Set up autodeploy') + expect(page).to have_no_content('Set up auto deploy') end end @@ -37,11 +37,11 @@ describe 'Auto deploy' do end it 'shows a button to set up auto deploy' do - expect(page).to have_link('Set up autodeploy') + expect(page).to have_link('Set up auto deploy') end - it 'includes Kubernetes as an available template', js: true do - click_link 'Set up autodeploy' + it 'includes OpenShift as an available template', js: true do + click_link 'Set up auto deploy' click_button 'Choose a GitLab CI Yaml template' within '.gitlab-ci-yml-selector' do @@ -49,8 +49,8 @@ describe 'Auto deploy' do end end - it 'creates a merge request using "autodeploy" branch', js: true do - click_link 'Set up autodeploy' + it 'creates a merge request using "auto-deploy" branch', js: true do + click_link 'Set up auto deploy' click_button 'Choose a GitLab CI Yaml template' within '.gitlab-ci-yml-selector' do click_on 'OpenShift' @@ -58,7 +58,7 @@ describe 'Auto deploy' do wait_for_ajax click_button 'Commit Changes' - expect(page).to have_content('New Merge Request From autodeploy into master') + expect(page).to have_content('New Merge Request From auto-deploy into master') end end end -- cgit v1.2.1 From 5507c7c66bd76030494d3fc5cc449cd9dc487077 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 23 Dec 2016 12:36:26 +0000 Subject: Fix line breaking in nodes of the pipeline graph in firefox Adds MR ID to CHANGELOG entry --- app/assets/stylesheets/pages/pipelines.scss | 7 +++---- changelogs/unreleased/26026-pipeline-overflow-firefox.yml | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/26026-pipeline-overflow-firefox.yml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 93c8b6c9a1e..799dfe908e0 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -335,7 +335,6 @@ width: 100%; background-color: $gray-light; padding: $gl-padding; - overflow: auto; white-space: nowrap; transition: max-height 0.3s, padding 0.3s; @@ -621,14 +620,14 @@ } .dropdown-counter-badge { - float: right; color: $border-color; font-weight: 100; font-size: 15px; - margin-right: 2px; + position: absolute; + right: 5px; + top: 8px; } - .grouped-pipeline-dropdown { padding: 0; width: 191px; diff --git a/changelogs/unreleased/26026-pipeline-overflow-firefox.yml b/changelogs/unreleased/26026-pipeline-overflow-firefox.yml new file mode 100644 index 00000000000..bf11d877c7e --- /dev/null +++ b/changelogs/unreleased/26026-pipeline-overflow-firefox.yml @@ -0,0 +1,4 @@ +--- +title: Fix line breaking in nodes of the pipeline graph in firefox +merge_request: 8292 +author: -- cgit v1.2.1 From 3f6edaeae892d38df22f397c7079c09c4b279f5c Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 23 Dec 2016 19:04:00 +0600 Subject: fixes confedential warning alignment on comment box --- app/assets/stylesheets/pages/note_form.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 074abec7692..e54e12be82f 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -109,7 +109,7 @@ margin: auto; margin-top: 0; text-align: center; - font-size: 13px; + font-size: 12px; @media (max-width: $screen-sm-max) { // On smaller devices the warning becomes the fourth item in the list, -- cgit v1.2.1 From 56fb1beb4e9e98affd6f18345643a370bc766857 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 23 Dec 2016 19:13:59 +0600 Subject: adds changelog --- .../unreleased/26038-fix-confedential-warning-text-alignment.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml diff --git a/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml b/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml new file mode 100644 index 00000000000..420be5a3b10 --- /dev/null +++ b/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml @@ -0,0 +1,4 @@ +--- +title: Fixes confendential warning text alignment +merge_request: 8293 +author: -- cgit v1.2.1 From c21df973b0ffc1559ddef37fe63f41b80ca90788 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 18:55:49 +0530 Subject: Hide Scroll Top for failed build --- app/assets/javascripts/build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 5e449170cd3..bc13c46443a 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -92,8 +92,8 @@ success: function(buildData) { $('.js-build-output').html(buildData.trace_html); if (removeRefreshStatuses.indexOf(buildData.status) >= 0) { - this.initScrollMonitor(); - return this.$buildRefreshAnimation.remove(); + this.$buildRefreshAnimation.remove(); + return this.initScrollMonitor(); } }.bind(this) }); -- cgit v1.2.1 From b5d6d107af29a82b5d1b13f9e031ab5ef2ca829c Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 19:09:09 +0530 Subject: Changelog entry for MR !8295 --- changelogs/unreleased/26040-hide-scroll-top.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/26040-hide-scroll-top.yml diff --git a/changelogs/unreleased/26040-hide-scroll-top.yml b/changelogs/unreleased/26040-hide-scroll-top.yml new file mode 100644 index 00000000000..f68cb1dd51d --- /dev/null +++ b/changelogs/unreleased/26040-hide-scroll-top.yml @@ -0,0 +1,4 @@ +--- +title: Hide Scroll Top button for failed build page +merge_request: 8295 +author: -- cgit v1.2.1 From d7a2e92ca0ae7fba4898f2f8ab722033b0721ec9 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Wed, 21 Dec 2016 15:02:05 +0000 Subject: applies url_for so that we dont lose filters when redirecting --- app/controllers/dashboard/todos_controller.rb | 2 +- app/controllers/projects/issues_controller.rb | 2 +- app/controllers/projects/merge_requests_controller.rb | 2 +- spec/controllers/dashboard/todos_controller_spec.rb | 3 ++- spec/controllers/projects/issues_controller_spec.rb | 5 +++-- spec/controllers/projects/merge_requests_controller_spec.rb | 8 ++++---- spec/controllers/projects/snippets_controller_spec.rb | 2 +- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 40652129f4c..e3933e3d7b1 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -5,7 +5,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController @sort = params[:sort] @todos = @todos.page(params[:page]) if @todos.out_of_range? && @todos.total_pages != 0 - redirect_to dashboard_todos_path(page: @todos.total_pages) + redirect_to url_for(params.merge(page: @todos.total_pages)) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 0efeec98570..2beb0df8a07 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController @issues = issues_collection @issues = @issues.page(params[:page]) if @issues.out_of_range? && @issues.total_pages != 0 - return redirect_to namespace_project_issues_path(page: @issues.total_pages) + return redirect_to url_for(params.merge(page: @issues.total_pages)) end if params[:label_name].present? diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 050d0ca77ae..fc8a289d49d 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 - return redirect_to namespace_project_merge_requests_path(page: @merge_requests.total_pages) + return redirect_to url_for(params.merge(page: @merge_requests.total_pages)) end if params[:label_name].present? diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb index a8c08a6e87b..288984cfba9 100644 --- a/spec/controllers/dashboard/todos_controller_spec.rb +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -13,10 +13,11 @@ describe Dashboard::TodosController do context 'when using pagination' do let(:last_page) { user.todos.page().total_pages } - let!(:issues) { create_list(:issue, 30, project: project, assignee: user) } + let!(:issues) { create_list(:issue, 2, project: project, assignee: user) } before do issues.each { |issue| todo_service.new_issue(issue, user) } + allow(Kaminari.config).to receive(:default_per_page).and_return(1) end it 'redirects to last_page if page number is larger than number of pages' do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 60b21cb4ace..e2321f2034b 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -55,11 +55,12 @@ describe Projects::IssuesController do context 'with page param' do let(:last_page) { project.issues.page().total_pages } - let!(:issue_list) { create_list(:issue, 30, project: project) } + let!(:issue_list) { create_list(:issue, 2, project: project) } before do sign_in(user) project.team << [user, :developer] + allow(Kaminari.config).to receive(:default_per_page).and_return(1) end it 'redirects to last_page if page number is larger than number of pages' do @@ -68,7 +69,7 @@ describe Projects::IssuesController do project_id: project.path.to_param, page: (last_page + 1).to_param - expect(response).to redirect_to(namespace_project_issues_path(page: last_page)) + expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope])) end it 'redirects to specified page' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 92392172af0..2a411d78395 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -127,9 +127,6 @@ describe Projects::MergeRequestsController do end describe 'GET index' do - let(:last_page) { project.merge_requests.page().total_pages } - let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } - def get_merge_requests(page = nil) get :index, namespace_id: project.namespace.to_param, @@ -138,10 +135,13 @@ describe Projects::MergeRequestsController do end context 'when page param' do + let(:last_page) { project.merge_requests.page().total_pages } + let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + it 'redirects to last_page if page number is larger than number of pages' do get_merge_requests(last_page + 1) - expect(response).to redirect_to(namespace_project_merge_requests_path(page: last_page)) + expect(response).to redirect_to(namespace_project_merge_requests_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope])) end it 'redirects to specified page' do diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 82e57e4d7dc..32b0e42c3cd 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -26,7 +26,7 @@ describe Projects::SnippetsController do it 'redirects to specified page' do get :index, namespace_id: project.namespace.path, - project_id: project.path, page: (last_page).to_param + project_id: project.path, page: last_page.to_param expect(assigns(:snippets).current_page).to eq(last_page) expect(response).to have_http_status(200) -- cgit v1.2.1 From 1b109c99a4802e2cc6598d19248013c0dc152d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 23 Dec 2016 17:03:25 +0100 Subject: Fix a Grape deprecation, use `#request_method` instead of `#route_method` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- changelogs/unreleased/fix-api-deprecation.yml | 4 ++++ lib/api/helpers.rb | 2 +- spec/requests/api/helpers_spec.rb | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/fix-api-deprecation.yml diff --git a/changelogs/unreleased/fix-api-deprecation.yml b/changelogs/unreleased/fix-api-deprecation.yml new file mode 100644 index 00000000000..90285ddf058 --- /dev/null +++ b/changelogs/unreleased/fix-api-deprecation.yml @@ -0,0 +1,4 @@ +--- +title: Fix a Grape deprecation, use `#request_method` instead of `#route_method` +merge_request: +author: diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 4be659fc20b..151d7400549 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -96,7 +96,7 @@ module API end def authenticate_non_get! - authenticate! unless %w[GET HEAD].include?(route.route_method) + authenticate! unless %w[GET HEAD].include?(route.request_method) end def authenticate_by_gitlab_shell_token! diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index c3d7ac3eef8..b8ee2293a33 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -396,7 +396,7 @@ describe API::Helpers, api: true do %w[HEAD GET].each do |method_name| context "method is #{method_name}" do before do - expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name)) + expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name)) end it 'does not raise an error' do @@ -410,7 +410,7 @@ describe API::Helpers, api: true do %w[POST PUT PATCH DELETE].each do |method_name| context "method is #{method_name}" do before do - expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name)) + expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name)) end it 'calls authenticate!' do -- cgit v1.2.1 From d72b40423c6736fd24ef4371604897871a1b3acc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 21 Dec 2016 19:31:30 +0200 Subject: Rename projects with reserved path names We cant have project with name 'project' or 'tree' anymore. This merge request containts a migration that will find and rename all projects using reserved names by adding N digit to the end of the name. Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 4 +- .../dz-rename-reserved-project-names.yml | 4 ++ ...20161221153951_rename_reserved_project_names.rb | 76 ++++++++++++++++++++++ db/schema.rb | 2 +- .../rename_reserved_project_names_spec.rb | 45 +++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/dz-rename-reserved-project-names.yml create mode 100644 db/post_migrate/20161221153951_rename_reserved_project_names.rb create mode 100644 spec/migrations/rename_reserved_project_names_spec.rb diff --git a/app/models/project.rb b/app/models/project.rb index 26fa20f856d..5fd6c86fd70 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -921,7 +921,7 @@ class Project < ActiveRecord::Base Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present" # we currently doesn't support renaming repository if it contains tags in container registry - raise Exception.new('Project cannot be renamed, because tags are present in its container registry') + raise StandardError.new('Project cannot be renamed, because tags are present in its container registry') end if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) @@ -948,7 +948,7 @@ class Project < ActiveRecord::Base # if we cannot move namespace directory we should rollback # db changes in order to prevent out of sync between db and fs - raise Exception.new('repository cannot be renamed') + raise StandardError.new('repository cannot be renamed') end Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" diff --git a/changelogs/unreleased/dz-rename-reserved-project-names.yml b/changelogs/unreleased/dz-rename-reserved-project-names.yml new file mode 100644 index 00000000000..30bcc1a0396 --- /dev/null +++ b/changelogs/unreleased/dz-rename-reserved-project-names.yml @@ -0,0 +1,4 @@ +--- +title: Rename projects wth reserved names +merge_request: 8234 +author: diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb new file mode 100644 index 00000000000..7e7342d80f9 --- /dev/null +++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb @@ -0,0 +1,76 @@ +class RenameReservedProjectNames < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + include Gitlab::ShellAdapter + + DOWNTIME = false + + class Project < ActiveRecord::Base; end + + def up + threads = reserved_projects.each_slice(100).map do |slice| + Thread.new do + rename_projects(slice) + end + end + + threads.each(&:join) + end + + def down + # nothing to do here + end + + private + + def reserved_projects + select_all("SELECT p.id, p.path, p.repository_storage, n.path AS namespace_path, n.id AS namespace_id FROM projects p + INNER JOIN namespaces n ON n.id = p.namespace_id + WHERE p.path IN ( + '.well-known', 'all', 'assets', 'files', 'groups', 'hooks', 'issues', + 'merge_requests', 'new', 'profile', 'projects', 'public', 'repository', + 'robots.txt', 's', 'snippets', 'teams', 'u', 'unsubscribes', 'users', + 'tree', 'commits', 'wikis', 'new', 'edit', 'create', 'update', 'logs_tree', + 'preview', 'blob', 'blame', 'raw', 'files', 'create_dir', 'find_file')") + end + + def route_exists?(full_path) + select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(full_path)}'").present? + end + + # Adds number to the end of the path that is not taken by other route + def rename_path(namespace_path, path_was) + counter = 0 + path = "#{path_was}#{counter}" + + while route_exists?("#{namespace_path}/#{path}") + counter += 1 + path = "#{path_was}#{counter}" + end + + path + end + + def rename_projects(projects) + projects.each do |row| + id = row['id'] + path_was = row['path'] + namespace_path = row['namespace_path'] + path = rename_path(namespace_path, path_was) + project = Project.find_by(id: id) + + begin + # Because project path update is quite complex operation we can't safely + # copy-paste all code from GitLab. As exception we use Rails code here + if project && + project.respond_to?(:update_attributes) && + project.update_attributes(path: path) && + project.respond_to?(:rename_repo) + + project.rename_repo + end + rescue => e + Rails.logger.error "Exception when rename project #{id}: #{e.message}" + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 05b6c807660..e48ce9f4ca5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161221140236) do +ActiveRecord::Schema.define(version: 20161221153951) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb new file mode 100644 index 00000000000..66f68570b50 --- /dev/null +++ b/spec/migrations/rename_reserved_project_names_spec.rb @@ -0,0 +1,45 @@ +# encoding: utf-8 + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb') + +describe RenameReservedProjectNames do + let(:migration) { described_class.new } + let!(:project) { create(:project) } + + before do + project.path = 'projects' + project.save!(validate: false) + allow(Thread).to receive(:new).and_yield + end + + describe '#up' do + context 'when project repository exists' do + before { project.create_repository } + + context 'when no exception is raised' do + it 'renames project with reserved names' do + migration.up + + expect(project.reload.path).to eq('projects0') + end + end + + context 'when exception is raised during rename' do + before do + allow(project).to receive(:rename_repo).and_raise(StandardError) + end + + it 'captures exception from project rename' do + expect { migration.up }.not_to raise_error + end + end + end + + context 'when project repository does not exist' do + it 'does not raise error' do + expect { migration.up }.not_to raise_error + end + end + end +end -- cgit v1.2.1 From dcea45015c4b8f9ea36fac02a2ff9817f0c01339 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 23 Dec 2016 16:18:36 +0100 Subject: Fix parallel renaming of reserved projects This ensures threads don't re-use the same connection object, and use fewer queries to perform the work. --- ...20161221153951_rename_reserved_project_names.rb | 113 ++++++++++++++++----- 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb index 7e7342d80f9..46ae434bf70 100644 --- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb +++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb @@ -1,18 +1,79 @@ +require 'thread' + class RenameReservedProjectNames < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers include Gitlab::ShellAdapter DOWNTIME = false - class Project < ActiveRecord::Base; end + THREAD_COUNT = 8 + + KNOWN_PATHS = %w(.well-known + all + assets + blame + blob + commits + create + create_dir + edit + files + files + find_file + groups + hooks + issues + logs_tree + merge_requests + new + new + preview + profile + projects + public + raw + repository + robots.txt + s + snippets + teams + tree + u + unsubscribes + update + users + wikis) def up - threads = reserved_projects.each_slice(100).map do |slice| + queues = Array.new(THREAD_COUNT) { Queue.new } + start = false + + threads = Array.new(THREAD_COUNT) do |index| Thread.new do - rename_projects(slice) + queue = queues[index] + + # Wait until we have input to process. + until start; end + + rename_projects(queue.pop) until queue.empty? + end + end + + enum = queues.each + + reserved_projects.each_slice(100) do |slice| + begin + queue = enum.next + rescue StopIteration + enum.rewind + retry end + + queue << slice end + start = true + threads.each(&:join) end @@ -23,18 +84,17 @@ class RenameReservedProjectNames < ActiveRecord::Migration private def reserved_projects - select_all("SELECT p.id, p.path, p.repository_storage, n.path AS namespace_path, n.id AS namespace_id FROM projects p - INNER JOIN namespaces n ON n.id = p.namespace_id - WHERE p.path IN ( - '.well-known', 'all', 'assets', 'files', 'groups', 'hooks', 'issues', - 'merge_requests', 'new', 'profile', 'projects', 'public', 'repository', - 'robots.txt', 's', 'snippets', 'teams', 'u', 'unsubscribes', 'users', - 'tree', 'commits', 'wikis', 'new', 'edit', 'create', 'update', 'logs_tree', - 'preview', 'blob', 'blame', 'raw', 'files', 'create_dir', 'find_file')") + Project.unscoped. + includes(:namespace). + where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)'). + where('projects.path' => KNOWN_PATHS) end def route_exists?(full_path) - select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(full_path)}'").present? + quoted_path = ActiveRecord::Base.connection.quote_string(full_path) + + ActiveRecord::Base.connection. + select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present? end # Adds number to the end of the path that is not taken by other route @@ -51,26 +111,27 @@ class RenameReservedProjectNames < ActiveRecord::Migration end def rename_projects(projects) - projects.each do |row| - id = row['id'] - path_was = row['path'] - namespace_path = row['namespace_path'] + projects.each do |project| + next unless project.namespace + + id = project.id + path_was = project.path + namespace_path = project.namespace.path path = rename_path(namespace_path, path_was) - project = Project.find_by(id: id) begin # Because project path update is quite complex operation we can't safely # copy-paste all code from GitLab. As exception we use Rails code here - if project && - project.respond_to?(:update_attributes) && - project.update_attributes(path: path) && - project.respond_to?(:rename_repo) - - project.rename_repo - end - rescue => e - Rails.logger.error "Exception when rename project #{id}: #{e.message}" + project.rename_repo if rename_project_row(project, path) + rescue Exception => e # rubocop: disable Lint/RescueException + Rails.logger.error "Exception when renaming project #{id}: #{e.message}" end end end + + def rename_project_row(project, path) + project.respond_to?(:update_attributes) && + project.update_attributes(path: path) && + project.respond_to?(:rename_repo) + end end -- cgit v1.2.1 From 2f93259c6b3a1c543a6ea089f8e84257c7ab6ba5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 23 Dec 2016 16:25:35 +0100 Subject: Removed return from reserved project migration This return is redundant as the query now uses "WHERE EXISTS (...)" to filter out projects without a namespace. --- db/post_migrate/20161221153951_rename_reserved_project_names.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb index 46ae434bf70..7f7c2424a5c 100644 --- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb +++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb @@ -112,8 +112,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration def rename_projects(projects) projects.each do |project| - next unless project.namespace - id = project.id path_was = project.path namespace_path = project.namespace.path -- cgit v1.2.1 From 655289dd337588ad55f41a156cdc2ac318227979 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 24 Dec 2016 12:24:00 +0800 Subject: Order only for latest_successful_for Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8286#note_20461082 --- app/models/ci/pipeline.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index f2f6453b3b9..abbbddaa4f6 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -93,11 +93,8 @@ module Ci .select("max(#{quoted_table_name}.id)") .group(:ref, :sha) - if ref - where(id: max_id, ref: ref) - else - where(id: max_id) - end + relation = ref ? where(ref: ref) : self + relation.where(id: max_id) end def self.latest_status(ref = nil) @@ -105,7 +102,7 @@ module Ci end def self.latest_successful_for(ref) - success.latest(ref).first + success.latest(ref).order(id: :desc).first end def self.truncate_sha(sha) -- cgit v1.2.1 From e60bf0d6c895bd16919bf22da0829055845574e4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 26 Dec 2016 14:33:08 +0800 Subject: Add a test for latest_successful_for --- changelogs/unreleased/fix-latest-pipeine-ordering.yml | 4 ++++ spec/models/ci/pipeline_spec.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 changelogs/unreleased/fix-latest-pipeine-ordering.yml diff --git a/changelogs/unreleased/fix-latest-pipeine-ordering.yml b/changelogs/unreleased/fix-latest-pipeine-ordering.yml new file mode 100644 index 00000000000..b155d51741b --- /dev/null +++ b/changelogs/unreleased/fix-latest-pipeine-ordering.yml @@ -0,0 +1,4 @@ +--- +title: Fix finding the latest pipeline +merge_request: 8301 +author: diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index dc377d15f15..cebaa157ef3 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -464,6 +464,19 @@ describe Ci::Pipeline, models: true do end end + describe '.latest_successful_for' do + include_context 'with some outdated pipelines' + + let!(:latest_successful_pipeline) do + create_pipeline(:success, 'ref', 'D') + end + + it 'returns the latest successful pipeline' do + expect(described_class.latest_successful_for('ref')). + to eq(latest_successful_pipeline) + end + end + describe '#status' do let!(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') } -- cgit v1.2.1 From 7b4b3d5f268534c028f55ef1014a84fe6a916cb0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Dec 2016 20:59:39 +0200 Subject: Include group parents into read access for project and group Signed-off-by: Dmitriy Zaporozhets --- app/models/group.rb | 18 ++++++++--- app/policies/group_policy.rb | 2 +- app/policies/project_policy.rb | 2 +- spec/models/group_spec.rb | 11 +++++++ spec/policies/group_policy_spec.rb | 66 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index ac8a82c8c1e..50c949d84aa 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -155,15 +155,17 @@ class Group < Namespace end def has_owner?(user) - owners.include?(user) + members_with_parents.owners.where(user_id: user).any? end def has_master?(user) - members.masters.where(user_id: user).any? + members_with_parents.masters.where(user_id: user).any? end + # Check if user is a last owner of the group. + # Parent owners are ignored for nested groups. def last_owner?(user) - has_owner?(user) && owners.size == 1 + owners.include?(user) && owners.size == 1 end def avatar_type @@ -189,6 +191,14 @@ class Group < Namespace end def refresh_members_authorized_projects - UserProjectAccessChangedService.new(users.pluck(:id)).execute + UserProjectAccessChangedService.new(users_with_parents.pluck(:id)).execute + end + + def members_with_parents + GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id)) + end + + def users_with_parents + User.where(id: members_with_parents.pluck(:user_id)) end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 6f943feb2a7..0be6e113655 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -4,7 +4,7 @@ class GroupPolicy < BasePolicy return unless @user globally_viewable = @subject.public? || (@subject.internal? && !@user.external?) - member = @subject.users.include?(@user) + member = @subject.users_with_parents.include?(@user) owner = @user.admin? || @subject.has_owner?(@user) master = owner || @subject.has_master?(@user) diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index b5db9c12622..eaf3035dfe1 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -245,7 +245,7 @@ class ProjectPolicy < BasePolicy def project_group_member?(user) project.group && ( - project.group.members.exists?(user_id: user.id) || + project.group.members_with_parents.exists?(user_id: user.id) || project.group.requesters.exists?(user_id: user.id) ) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 7d5ecfbaa64..45fe927202b 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -277,4 +277,15 @@ describe Group, models: true do it { is_expected.to be_valid } it { expect(subject.parent).to be_kind_of(Group) } end + + describe '#members_with_parents' do + let!(:group) { create(:group, :nested) } + let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) } + let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) } + + it 'returns parents members' do + expect(group.members_with_parents).to include(developer) + expect(group.members_with_parents).to include(master) + end + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index a20ac303a53..5c34ff04152 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -105,4 +105,70 @@ describe GroupPolicy, models: true do is_expected.to include(*owner_permissions) end end + + describe 'private nested group inherit permissions' do + let(:nested_group) { create(:group, :private, parent: group) } + + subject { described_class.abilities(current_user, nested_group).to_set } + + context 'with no user' do + let(:current_user) { nil } + + it do + is_expected.not_to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'guests' do + let(:current_user) { guest } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'reporter' do + let(:current_user) { reporter } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'developer' do + let(:current_user) { developer } + + it do + is_expected.to include(:read_group) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'master' do + let(:current_user) { master } + + it do + is_expected.to include(:read_group) + is_expected.to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'owner' do + let(:current_user) { owner } + + it do + is_expected.to include(:read_group) + is_expected.to include(*master_permissions) + is_expected.to include(*owner_permissions) + end + end + end end -- cgit v1.2.1 From 9410f215eab0ba49051d2a00f0b4174f5dc13a6f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Dec 2016 11:56:19 +0200 Subject: Add nested groups support to the Groups::CreateService Signed-off-by: Dmitriy Zaporozhets --- app/services/groups/create_service.rb | 13 ++++++++++++ spec/services/groups/create_service_spec.rb | 31 +++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 2bccd584dde..9a630aee626 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -12,6 +12,19 @@ module Groups return @group end + parent_id = params[:parent_id] + + if parent_id + parent = Group.find(parent_id) + + unless can?(current_user, :admin_group, parent) + @group.parent_id = nil + @group.errors.add(:parent_id, 'manage access required to create subgroup') + + return @group + end + end + @group.name ||= @group.path.dup @group.save @group.add_owner(current_user) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 71a0b8e2a12..14717a7455d 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,11 +1,12 @@ require 'spec_helper' -describe Groups::CreateService, services: true do - let!(:user) { create(:user) } +describe Groups::CreateService, '#execute', services: true do + let!(:user) { create(:user) } let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } - describe "execute" do - let!(:service) { described_class.new(user, group_params ) } + describe 'visibility level restrictions' do + let!(:service) { described_class.new(user, group_params) } + subject { service.execute } context "create groups without restricted visibility level" do @@ -14,7 +15,29 @@ describe Groups::CreateService, services: true do context "cannot create group with restricted visibility level" do before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } + it { is_expected.not_to be_persisted } end end + + describe 'creating subgroup' do + let!(:group) { create(:group) } + let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) } + + subject { service.execute } + + context 'as group owner' do + before { group.add_owner(user) } + + it { is_expected.to be_persisted } + end + + context 'as guest' do + it 'does not save group and returns an error' do + is_expected.not_to be_persisted + expect(subject.errors[:parent_id].first).to eq('manage access required to create subgroup') + expect(subject.parent_id).to be_nil + end + end + end end -- cgit v1.2.1 From 10de4e3bb612a529480c93da53849d95e98a8633 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Dec 2016 12:51:48 +0200 Subject: Show nested groups tab on group page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups_controller.rb | 2 ++ app/views/groups/show.html.haml | 10 ++++++++++ changelogs/unreleased/dz-nested-group-misc.yml | 4 ++++ spec/features/groups_spec.rb | 13 +++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 changelogs/unreleased/dz-nested-group-misc.yml diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index efe9c001bcf..e061df2f819 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -42,6 +42,8 @@ class GroupsController < Groups::ApplicationController @notification_setting = current_user.notification_settings_for(group) end + @nested_groups = group.children + setup_projects respond_to do |format| diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 52ce26a20b1..a3cd333373e 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -32,6 +32,10 @@ %li = link_to "#shared", 'data-toggle' => 'tab' do Shared Projects + - if @nested_groups.present? + %li + = link_to "#groups", 'data-toggle' => 'tab' do + Nested Groups .nav-controls = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false @@ -47,3 +51,9 @@ - if @shared_projects.present? .tab-pane#shared = render "shared_projects", projects: @shared_projects + + - if @nested_groups.present? + .tab-pane#groups + %ul.content-list + - @nested_groups.each do |group| + = render 'shared/groups/group', group: group diff --git a/changelogs/unreleased/dz-nested-group-misc.yml b/changelogs/unreleased/dz-nested-group-misc.yml new file mode 100644 index 00000000000..9c9d0b1c644 --- /dev/null +++ b/changelogs/unreleased/dz-nested-group-misc.yml @@ -0,0 +1,4 @@ +--- +title: Show nested groups tab on group page +merge_request: 8308 +author: diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 4b19886274e..d865a71f04b 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -107,4 +107,17 @@ feature 'Group', feature: true do expect(page).to have_css('.group-home-desc a[rel]') end end + + describe 'group page with nested groups', js: true do + let!(:group) { create(:group) } + let!(:nested_group) { create(:group, parent: group) } + let!(:path) { group_path(group) } + + it 'has nested groups tab with nested groups inside' do + visit path + click_link 'Nested Groups' + + expect(page).to have_content(nested_group.full_name) + end + end end -- cgit v1.2.1 From dd178bbe6ab851da9dceb2acf2f0336166f08b48 Mon Sep 17 00:00:00 2001 From: victorwu Date: Fri, 9 Dec 2016 22:37:28 -0600 Subject: In issue view, remove checking branches and new branch unavailable states --- app/assets/javascripts/issue.js | 3 --- app/views/projects/issues/_new_branch.html.haml | 3 --- changelogs/unreleased/re-style-issue-new-branch.yml | 3 +++ 3 files changed, 3 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/re-style-issue-new-branch.yml diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 63b70d4be17..61e8531153b 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -139,15 +139,12 @@ return; } return $.getJSON($container.data('path')).error(function() { - $container.find('.checking').hide(); $container.find('.unavailable').show(); return new Flash('Failed to check if a new branch can be created.', 'alert'); }).success(function(data) { if (data.can_create_branch) { - $container.find('.checking').hide(); $container.find('.available').show(); } else { - $container.find('.checking').hide(); return $container.find('.unavailable').show(); } }); diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index c56b6cc11f5..4589e112279 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -1,9 +1,6 @@ - if can?(current_user, :push_code, @project) .pull-right #new-branch.new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)} - = link_to '#', class: 'checking btn btn-grouped', disabled: 'disabled' do - = icon('spinner spin') - Checking branches = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), method: :post, class: 'btn btn-new btn-inverted btn-grouped has-tooltip available hide', title: @issue.to_branch_name do New branch diff --git a/changelogs/unreleased/re-style-issue-new-branch.yml b/changelogs/unreleased/re-style-issue-new-branch.yml new file mode 100644 index 00000000000..977a54ff2ae --- /dev/null +++ b/changelogs/unreleased/re-style-issue-new-branch.yml @@ -0,0 +1,3 @@ +--- +title: Remove checking branches state in issue new branch button +merge_request: 8023 -- cgit v1.2.1 From 8cc5333420230d6a6f0a2c9844af9a1f1dc955ec Mon Sep 17 00:00:00 2001 From: victorwu Date: Thu, 15 Dec 2016 13:54:43 -0600 Subject: Replace wording for slash command confirmation message --- app/services/notes/create_service.rb | 2 +- ...our-commands-have-been-executed-is-overkill.yml | 3 +++ .../issues/user_uses_slash_commands_spec.rb | 8 ++++---- .../user_uses_slash_commands_spec.rb | 6 +++--- .../issuable_slash_commands_shared_examples.rb | 22 +++++++++++----------- 5 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index d75592e31f3..1beca9f4109 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -41,7 +41,7 @@ module Notes # We must add the error after we call #save because errors are reset # when #save is called if only_commands - note.errors.add(:commands_only, 'Your commands have been executed!') + note.errors.add(:commands_only, 'Commands applied') end note.commands_changes = command_params.keys diff --git a/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml b/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml new file mode 100644 index 00000000000..850e98518a6 --- /dev/null +++ b/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml @@ -0,0 +1,3 @@ +--- +title: Replace wording for slash command confirmation message +merge_request: 8123 diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index 3f2da1c380c..31f75512f4a 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -30,7 +30,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do write_note("/due 2016-08-28") expect(page).not_to have_content '/due 2016-08-28' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' issue.reload @@ -51,7 +51,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do write_note("/due 2016-08-28") expect(page).to have_content '/due 2016-08-28' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' issue.reload @@ -70,7 +70,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do write_note("/remove_due_date") expect(page).not_to have_content '/remove_due_date' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' issue.reload @@ -91,7 +91,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do write_note("/remove_due_date") expect(page).to have_content '/remove_due_date' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' issue.reload diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb index 7b8af555f0e..b1b3a47a1ce 100644 --- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb @@ -31,7 +31,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do write_note("/wip") expect(page).not_to have_content '/wip' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(merge_request.reload.work_in_progress?).to eq true end @@ -42,7 +42,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do write_note("/wip") expect(page).not_to have_content '/wip' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(merge_request.reload.work_in_progress?).to eq false end @@ -61,7 +61,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do write_note("/wip") expect(page).not_to have_content '/wip' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' expect(merge_request.reload.work_in_progress?).to eq false end diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb index 194620d0a68..a4713e53f63 100644 --- a/spec/support/features/issuable_slash_commands_shared_examples.rb +++ b/spec/support/features/issuable_slash_commands_shared_examples.rb @@ -76,7 +76,7 @@ shared_examples 'issuable record that supports slash commands in its description expect(page).not_to have_content '/assign @bob' expect(page).not_to have_content '/label ~bug' expect(page).not_to have_content '/milestone %"ASAP"' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' issuable.reload @@ -97,7 +97,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/close") expect(page).not_to have_content '/close' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(issuable.reload).to be_closed end @@ -114,7 +114,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/close") expect(page).not_to have_content '/close' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' expect(issuable).to be_open end @@ -132,7 +132,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/reopen") expect(page).not_to have_content '/reopen' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(issuable.reload).to be_open end @@ -149,7 +149,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/reopen") expect(page).not_to have_content '/reopen' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' expect(issuable).to be_closed end @@ -162,7 +162,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/title Awesome new title") expect(page).not_to have_content '/title' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(issuable.reload.title).to eq 'Awesome new title' end @@ -179,7 +179,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/title Awesome new title") expect(page).not_to have_content '/title' - expect(page).not_to have_content 'Your commands have been executed!' + expect(page).not_to have_content 'Commands applied' expect(issuable.reload.title).not_to eq 'Awesome new title' end @@ -191,7 +191,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/todo") expect(page).not_to have_content '/todo' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' todos = TodosFinder.new(master).execute todo = todos.first @@ -222,7 +222,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/done") expect(page).not_to have_content '/done' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(todo.reload).to be_done end @@ -235,7 +235,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/subscribe") expect(page).not_to have_content '/subscribe' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(issuable.subscribed?(master, project)).to be_truthy end @@ -252,7 +252,7 @@ shared_examples 'issuable record that supports slash commands in its description write_note("/unsubscribe") expect(page).not_to have_content '/unsubscribe' - expect(page).to have_content 'Your commands have been executed!' + expect(page).to have_content 'Commands applied' expect(issuable.subscribed?(master, project)).to be_falsy end -- cgit v1.2.1 From dbfac0de066c4770a15d4e9a96dbc01edf9add3f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Dec 2016 16:14:41 +0200 Subject: Rename users with namespace ending with .git Signed-off-by: Dmitriy Zaporozhets --- changelogs/unreleased/dz-rename-invalid-users.yml | 4 + ...20161226122833_remove_dot_git_from_usernames.rb | 87 ++++++++++++++++++++++ db/schema.rb | 2 +- spec/migrations/remove_dot_git_from_usernames.rb | 29 ++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/dz-rename-invalid-users.yml create mode 100644 db/migrate/20161226122833_remove_dot_git_from_usernames.rb create mode 100644 spec/migrations/remove_dot_git_from_usernames.rb diff --git a/changelogs/unreleased/dz-rename-invalid-users.yml b/changelogs/unreleased/dz-rename-invalid-users.yml new file mode 100644 index 00000000000..f420b069531 --- /dev/null +++ b/changelogs/unreleased/dz-rename-invalid-users.yml @@ -0,0 +1,4 @@ +--- +title: Rename users with namespace ending with .git +merge_request: 8309 +author: diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb new file mode 100644 index 00000000000..809b09feb84 --- /dev/null +++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb @@ -0,0 +1,87 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveDotGitFromUsernames < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + include Gitlab::ShellAdapter + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def up + invalid_users.each do |user| + id = user['id'] + namespace_id = user['namespace_id'] + path_was = user['username'] + path_was_wildcard = quote_string("#{path_was}/%") + path = quote_string(rename_path(path_was)) + + move_namespace(namespace_id, path_was, path) + + execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}" + execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}" + execute "UPDATE users SET username = '#{path}' WHERE id = #{id}" + + select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route| + new_path = "#{path}/#{route['path'].split('/').last}" + execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}" + end + end + end + + def down + # nothing to do here + end + + private + + def invalid_users + select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u + INNER JOIN namespaces n ON n.owner_id = u.id + WHERE n.type is NULL AND n.path LIKE '%.git'") + end + + def route_exists?(path) + select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present? + end + + # Accepts invalid path like test.git and returns test_git or + # test_git1 if test_git already taken + def rename_path(path) + # To stay closer with original name and reduce risk of duplicates + # we rename suffix instead of removing it + path = path.sub(/\.git\z/, '_git') + + counter = 0 + base = path + + while route_exists?(path) + counter += 1 + path = "#{base}#{counter}" + end + + path + end + + def move_namespace(namespace_id, path_was, path) + repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_id}").map do |row| + Gitlab.config.repositories.storages[row['repository_storage']] + end.compact + + # Move the namespace directory in all storages paths used by member projects + repository_storage_paths.each do |repository_storage_path| + # Ensure old directory exists before moving it + gitlab_shell.add_namespace(repository_storage_path, path_was) + + unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path) + Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise Exception.new('namespace directory cannot be moved') + end + end + + Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) + end +end diff --git a/db/schema.rb b/db/schema.rb index 05b6c807660..1fcb6bcfe60 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161221140236) do +ActiveRecord::Schema.define(version: 20161226122833) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/migrations/remove_dot_git_from_usernames.rb b/spec/migrations/remove_dot_git_from_usernames.rb new file mode 100644 index 00000000000..1b1d2adc463 --- /dev/null +++ b/spec/migrations/remove_dot_git_from_usernames.rb @@ -0,0 +1,29 @@ +# encoding: utf-8 + +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb') + +describe RemoveDotGitFromUsernames do + let(:user) { create(:user) } + + describe '#up' do + let(:migration) { described_class.new } + + before do + namespace = user.namespace + namespace.path = 'test.git' + namespace.save!(validate: false) + + user.username = 'test.git' + user.save!(validate: false) + end + + it 'renames user with .git in username' do + migration.up + + expect(user.reload.username).to eq('test_git') + expect(user.namespace.reload.path).to eq('test_git') + expect(user.namespace.route.path).to eq('test_git') + end + end +end -- cgit v1.2.1 From ed6900caf107ad0fc4580ad7dc000284d91683d4 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 26 Dec 2016 14:27:20 +0000 Subject: Use raw_diffs to calculate MR files changed We don't need to create the intermediate FileCollection object with its associated highlighting in order to count how many files changed. Both the compare object and the MR diff object have a raw_diffs method that is perfectly fine for this case. --- app/models/merge_request.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 5ce20cc43d6..22490d121c7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -198,7 +198,9 @@ class MergeRequest < ActiveRecord::Base end def diff_size - diffs(diff_options).size + opts = diff_options || {} + + raw_diffs(opts).size end def diff_base_commit -- cgit v1.2.1 From 1e5e56c698740e049a3fcf7b578ac006d5deef42 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 26 Dec 2016 15:03:05 +0000 Subject: Fix MR with files hidden by .gitattributes Don't try to highlight and cache files hidden by .gitattributes entries. --- ...-view-crash-when-commiting-a-js-sourcemap-file.yml | 4 ++++ lib/gitlab/diff/file_collection/merge_request_diff.rb | 5 ++++- .../diff/file_collection/merge_request_diff_spec.rb | 19 ++++++++++++++----- .../merge_request_diff_cache_service_spec.rb | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml diff --git a/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml b/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml new file mode 100644 index 00000000000..023ec1abcfa --- /dev/null +++ b/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml @@ -0,0 +1,4 @@ +--- +title: Fix timeout when MR contains large files marked as binary by .gitattributes +merge_request: +author: diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index 56530448f36..329d12f13d1 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -61,7 +61,10 @@ module Gitlab end def cacheable?(diff_file) - @merge_request_diff.present? && diff_file.blob && diff_file.blob.text? + @merge_request_diff.present? && + diff_file.blob && + diff_file.blob.text? && + @project.repository.diffable?(diff_file.blob) end def cache_key diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb index 2a680f03476..f2bc15d39d7 100644 --- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb +++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb @@ -1,21 +1,30 @@ require 'spec_helper' describe Gitlab::Diff::FileCollection::MergeRequestDiff do - let(:merge_request) { create :merge_request } + let(:merge_request) { create(:merge_request) } + let(:diff_files) { described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files } - it 'does not hightlight binary files' do + it 'does not highlight binary files' do allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => false)) expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines) - described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files + diff_files end - it 'does not hightlight file if blob is not accessable' do + it 'does not highlight file if blob is not accessable' do allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil) expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines) - described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files + diff_files + end + + it 'does not files marked as undiffable in .gitattributes' do + allow_any_instance_of(Repository).to receive(:diffable?).and_return(false) + + expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines) + + diff_files end end diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb index 05cdbe5287a..35804d41b46 100644 --- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb +++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb @@ -11,6 +11,7 @@ describe MergeRequests::MergeRequestDiffCacheService do expect(Rails.cache).to receive(:read).with(cache_key).and_return({}) expect(Rails.cache).to receive(:write).with(cache_key, anything) allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => true)) + allow_any_instance_of(Repository).to receive(:diffable?).and_return(true) subject.execute(merge_request) end -- cgit v1.2.1 From 738fe41437f66f499c862c9acd0abeed51d20c42 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 26 Dec 2016 17:10:18 +0000 Subject: Fix build failure links in Slack --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b256e8a2a5f..e0e780e1e6b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -425,7 +425,7 @@ notify:slack: SETUP_DB: "false" USE_BUNDLE_INSTALL: "false" script: - - ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See " + - ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See " when: on_failure only: - master@gitlab-org/gitlab-ce -- cgit v1.2.1 From b5f3c85458bc49522405e3cc19618df1ab6e6546 Mon Sep 17 00:00:00 2001 From: Victor Wu Date: Mon, 26 Dec 2016 23:15:10 +0000 Subject: Rename filename to file path in tooltip of file header in merge request diff --- app/views/projects/diffs/_file_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml index d3ed8e1bf38..90c9a0c6c2b 100644 --- a/app/views/projects/diffs/_file_header.html.haml +++ b/app/views/projects/diffs/_file_header.html.haml @@ -21,7 +21,7 @@ - if diff_file.deleted_file deleted - = clipboard_button(clipboard_text: diff_file.new_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy filename to clipboard') + = clipboard_button(clipboard_text: diff_file.new_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard') - if diff_file.mode_changed? %small -- cgit v1.2.1 From b285abeccc3c466b8501d1333f7391be5d6f4334 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Sat, 24 Dec 2016 16:53:13 +0000 Subject: Improved the u2f flow Added tests --- app/assets/javascripts/dispatcher.js.es6 | 11 +++ app/assets/javascripts/u2f/authenticate.js | 113 ----------------------- app/assets/javascripts/u2f/authenticate.js.es6 | 120 +++++++++++++++++++++++++ app/views/devise/sessions/two_factor.html.haml | 2 +- app/views/u2f/_authenticate.html.haml | 17 +--- spec/features/u2f_spec.rb | 119 +++++++++++++++--------- spec/javascripts/u2f/authenticate_spec.js | 23 ++--- spec/support/fake_u2f_device.rb | 3 +- 8 files changed, 226 insertions(+), 182 deletions(-) delete mode 100644 app/assets/javascripts/u2f/authenticate.js create mode 100644 app/assets/javascripts/u2f/authenticate.js.es6 diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 78f68a247a2..1c1b6cd2dad 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -64,6 +64,17 @@ new UsernameValidator(); new ActiveTabMemoizer(); break; + case 'sessions:create': + if (!gon.u2f) break; + window.gl.u2fAuthenticate = new gl.U2FAuthenticate( + $("#js-authenticate-u2f"), + '#js-login-u2f-form', + gon.u2f, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form'), + ); + window.gl.u2fAuthenticate.start(); + break; case 'projects:boards:show': case 'projects:boards:index': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js deleted file mode 100644 index e407b856e10..00000000000 --- a/app/assets/javascripts/u2f/authenticate.js +++ /dev/null @@ -1,113 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ -/* global u2f */ -/* global U2FError */ -/* global U2FUtil */ - -// Authenticate U2F (universal 2nd factor) devices for users to authenticate with. -// -// State Flow #1: setup -> in_progress -> authenticated -> POST to server -// State Flow #2: setup -> in_progress -> error -> setup -(function() { - var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - this.U2FAuthenticate = (function() { - function U2FAuthenticate(container, u2fParams) { - this.container = container; - this.renderNotSupported = bind(this.renderNotSupported, this); - this.renderAuthenticated = bind(this.renderAuthenticated, this); - this.renderError = bind(this.renderError, this); - this.renderInProgress = bind(this.renderInProgress, this); - this.renderSetup = bind(this.renderSetup, this); - this.renderTemplate = bind(this.renderTemplate, this); - this.authenticate = bind(this.authenticate, this); - this.start = bind(this.start, this); - this.appId = u2fParams.app_id; - this.challenge = u2fParams.challenge; - this.signRequests = u2fParams.sign_requests.map(function(request) { - // The U2F Javascript API v1.1 requires a single challenge, with - // _no challenges per-request_. The U2F Javascript API v1.0 requires a - // challenge per-request, which is done by copying the single challenge - // into every request. - // - // In either case, we don't need the per-request challenges that the server - // has generated, so we can remove them. - // - // Note: The server library fixes this behaviour in (unreleased) version 1.0.0. - // This can be removed once we upgrade. - // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4 - return _(request).omit('challenge'); - }); - } - - U2FAuthenticate.prototype.start = function() { - if (U2FUtil.isU2FSupported()) { - return this.renderSetup(); - } else { - return this.renderNotSupported(); - } - }; - - U2FAuthenticate.prototype.authenticate = function() { - return u2f.sign(this.appId, this.challenge, this.signRequests, (function(_this) { - return function(response) { - var error; - if (response.errorCode) { - error = new U2FError(response.errorCode); - return _this.renderError(error); - } else { - return _this.renderAuthenticated(JSON.stringify(response)); - } - }; - })(this), 10); - }; - - // Rendering # - U2FAuthenticate.prototype.templates = { - "notSupported": "#js-authenticate-u2f-not-supported", - "setup": '#js-authenticate-u2f-setup', - "inProgress": '#js-authenticate-u2f-in-progress', - "error": '#js-authenticate-u2f-error', - "authenticated": '#js-authenticate-u2f-authenticated' - }; - - U2FAuthenticate.prototype.renderTemplate = function(name, params) { - var template, templateString; - templateString = $(this.templates[name]).html(); - template = _.template(templateString); - return this.container.html(template(params)); - }; - - U2FAuthenticate.prototype.renderSetup = function() { - this.renderTemplate('setup'); - return this.container.find('#js-login-u2f-device').on('click', this.renderInProgress); - }; - - U2FAuthenticate.prototype.renderInProgress = function() { - this.renderTemplate('inProgress'); - return this.authenticate(); - }; - - U2FAuthenticate.prototype.renderError = function(error) { - this.renderTemplate('error', { - error_message: error.message(), - error_code: error.errorCode - }); - return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); - }; - - U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) { - this.renderTemplate('authenticated'); - // Prefer to do this instead of interpolating using Underscore templates - // because of JSON escaping issues. - return this.container.find("#js-device-response").val(deviceResponse); - }; - - U2FAuthenticate.prototype.renderNotSupported = function() { - return this.renderTemplate('notSupported'); - }; - - return U2FAuthenticate; - - })(); - -}).call(this); diff --git a/app/assets/javascripts/u2f/authenticate.js.es6 b/app/assets/javascripts/u2f/authenticate.js.es6 new file mode 100644 index 00000000000..2b992109a8c --- /dev/null +++ b/app/assets/javascripts/u2f/authenticate.js.es6 @@ -0,0 +1,120 @@ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ +/* global u2f */ +/* global U2FError */ +/* global U2FUtil */ + +// Authenticate U2F (universal 2nd factor) devices for users to authenticate with. +// +// State Flow #1: setup -> in_progress -> authenticated -> POST to server +// State Flow #2: setup -> in_progress -> error -> setup +(function() { + const global = window.gl || (window.gl = {}); + + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + global.U2FAuthenticate = (function() { + function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) { + this.container = container; + this.renderNotSupported = bind(this.renderNotSupported, this); + this.renderAuthenticated = bind(this.renderAuthenticated, this); + this.renderError = bind(this.renderError, this); + this.renderInProgress = bind(this.renderInProgress, this); + this.renderTemplate = bind(this.renderTemplate, this); + this.authenticate = bind(this.authenticate, this); + this.start = bind(this.start, this); + this.appId = u2fParams.app_id; + this.challenge = u2fParams.challenge; + this.form = form; + this.fallbackButton = fallbackButton; + this.fallbackUI = fallbackUI; + if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this)); + this.signRequests = u2fParams.sign_requests.map(function(request) { + // The U2F Javascript API v1.1 requires a single challenge, with + // _no challenges per-request_. The U2F Javascript API v1.0 requires a + // challenge per-request, which is done by copying the single challenge + // into every request. + // + // In either case, we don't need the per-request challenges that the server + // has generated, so we can remove them. + // + // Note: The server library fixes this behaviour in (unreleased) version 1.0.0. + // This can be removed once we upgrade. + // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4 + return _(request).omit('challenge'); + }); + } + + U2FAuthenticate.prototype.start = function() { + if (U2FUtil.isU2FSupported()) { + return this.renderInProgress(); + } else { + return this.renderNotSupported(); + } + }; + + U2FAuthenticate.prototype.authenticate = function() { + return u2f.sign(this.appId, this.challenge, this.signRequests, (function(_this) { + return function(response) { + var error; + if (response.errorCode) { + error = new U2FError(response.errorCode); + return _this.renderError(error); + } else { + return _this.renderAuthenticated(JSON.stringify(response)); + } + }; + })(this), 10); + }; + + // Rendering # + U2FAuthenticate.prototype.templates = { + "notSupported": "#js-authenticate-u2f-not-supported", + "setup": '#js-authenticate-u2f-setup', + "inProgress": '#js-authenticate-u2f-in-progress', + "error": '#js-authenticate-u2f-error', + "authenticated": '#js-authenticate-u2f-authenticated' + }; + + U2FAuthenticate.prototype.renderTemplate = function(name, params) { + var template, templateString; + templateString = $(this.templates[name]).html(); + template = _.template(templateString); + return this.container.html(template(params)); + }; + + U2FAuthenticate.prototype.renderInProgress = function() { + this.renderTemplate('inProgress'); + return this.authenticate(); + }; + + U2FAuthenticate.prototype.renderError = function(error) { + this.renderTemplate('error', { + error_message: error.message(), + error_code: error.errorCode + }); + return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress); + }; + + U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) { + this.renderTemplate('authenticated'); + const container = this.container[0]; + container.querySelector('#js-device-response').value = deviceResponse; + container.querySelector(this.form).submit(); + this.fallbackButton.classList.add('hidden'); + }; + + U2FAuthenticate.prototype.renderNotSupported = function() { + return this.renderTemplate('notSupported'); + }; + + U2FAuthenticate.prototype.switchToFallbackUI = function() { + this.fallbackButton.classList.add('hidden'); + this.container[0].classList.add('hidden'); + this.fallbackUI.classList.remove('hidden'); + }; + + return U2FAuthenticate; + + })(); + +})(); diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 2cadc424668..951f03083bf 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -7,7 +7,7 @@ .login-box .login-body - if @user.two_factor_otp_enabled? - = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user gl-show-field-errors' }) do |f| + = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: "edit_user gl-show-field-errors js-2fa-form #{'hidden' if @user.two_factor_u2f_enabled?}" }) do |f| - resource_params = params[resource_name].presence || params = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) %div diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml index fa998c91f72..f878bece2fa 100644 --- a/app/views/u2f/_authenticate.html.haml +++ b/app/views/u2f/_authenticate.html.haml @@ -1,30 +1,21 @@ #js-authenticate-u2f +%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code %script#js-authenticate-u2f-not-supported{ type: "text/template" } %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer). -%script#js-authenticate-u2f-setup{ type: "text/template" } - %div - %p Insert your security key (if you haven't already), and press the button below. - %a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Sign in via U2F device - %script#js-authenticate-u2f-in-progress{ type: "text/template" } %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now. %script#js-authenticate-u2f-error{ type: "text/template" } %div %p <%= error_message %> (error code: <%= error_code %>) - %a.btn.btn-warning#js-u2f-try-again Try again? + %a.btn.btn-block.btn-warning#js-u2f-try-again Try again? %script#js-authenticate-u2f-authenticated{ type: "text/template" } %div - %p We heard back from your U2F device. Click this button to authenticate with the GitLab server. - = form_tag(new_user_session_path, method: :post) do |f| + %p We heard back from your U2F device. You have been authenticated. + = form_tag(new_user_session_path, method: :post, id: 'js-login-u2f-form') do |f| - resource_params = params[resource_name].presence || params = hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0) = hidden_field_tag 'user[device_response]', nil, class: 'form-control', required: true, id: "js-device-response" - = submit_tag "Authenticate via U2F Device", class: "btn btn-success" - -:javascript - var u2fAuthenticate = new U2FAuthenticate($("#js-authenticate-u2f"), gon.u2f); - u2fAuthenticate.start(); diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb index be21b403084..a8d00bb8e5a 100644 --- a/spec/features/u2f_spec.rb +++ b/spec/features/u2f_spec.rb @@ -45,12 +45,12 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: it 'allows registering a new device with a name' do visit profile_account_path manage_two_factor_authentication - expect(page.body).to match("You've already enabled two-factor authentication using mobile") + expect(page).to have_content("You've already enabled two-factor authentication using mobile") u2f_device = register_u2f_device - expect(page.body).to match(u2f_device.name) - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content(u2f_device.name) + expect(page).to have_content('Your U2F device was registered') end it 'allows registering more than one device' do @@ -59,30 +59,30 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: # First device manage_two_factor_authentication first_device = register_u2f_device - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content('Your U2F device was registered') # Second device second_device = register_u2f_device - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content('Your U2F device was registered') - expect(page.body).to match(first_device.name) - expect(page.body).to match(second_device.name) + expect(page).to have_content(first_device.name) + expect(page).to have_content(second_device.name) expect(U2fRegistration.count).to eq(2) end it 'allows deleting a device' do visit profile_account_path manage_two_factor_authentication - expect(page.body).to match("You've already enabled two-factor authentication using mobile") + expect(page).to have_content("You've already enabled two-factor authentication using mobile") first_u2f_device = register_u2f_device second_u2f_device = register_u2f_device click_on "Delete", match: :first - expect(page.body).to match('Successfully deleted') + expect(page).to have_content('Successfully deleted') expect(page.body).not_to match(first_u2f_device.name) - expect(page.body).to match(second_u2f_device.name) + expect(page).to have_content(second_u2f_device.name) end end @@ -91,7 +91,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: visit profile_account_path manage_two_factor_authentication u2f_device = register_u2f_device - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content('Your U2F device was registered') logout # Second user @@ -100,7 +100,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: visit profile_account_path manage_two_factor_authentication register_u2f_device(u2f_device) - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content('Your U2F device was registered') expect(U2fRegistration.count).to eq(2) end @@ -117,8 +117,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: click_on 'Register U2F Device' expect(U2fRegistration.count).to eq(0) - expect(page.body).to match("The form contains the following error") - expect(page.body).to match("did not send a valid JSON response") + expect(page).to have_content("The form contains the following error") + expect(page).to have_content("did not send a valid JSON response") end it "allows retrying registration" do @@ -130,12 +130,12 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: click_on 'Setup New U2F Device' expect(page).to have_content('Your device was successfully set up') click_on 'Register U2F Device' - expect(page.body).to match("The form contains the following error") + expect(page).to have_content("The form contains the following error") # Successful registration register_u2f_device - expect(page.body).to match('Your U2F device was registered') + expect(page).to have_content('Your U2F device was registered') expect(U2fRegistration.count).to eq(1) end end @@ -160,10 +160,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: login_with(user) @u2f_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" - expect(page.body).to match('href="/users/sign_out"') + + expect(page).to have_content('We heard back from your U2F device') + expect(page).to have_css('.sign-out-link', visible: false) end end @@ -173,11 +172,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: login_with(user) @u2f_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" - expect(page.body).to match('href="/users/sign_out"') + expect(page).to have_content('We heard back from your U2F device') + expect(page).to have_css('.sign-out-link', visible: false) end end @@ -185,8 +182,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: login_with(user, remember: true) @u2f_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') + expect(page).to have_content('We heard back from your U2F device') within 'div#js-authenticate-u2f' do field = first('input#user_remember_me', visible: false) @@ -208,11 +204,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: # Try authenticating user with the old U2F device login_as(current_user) @u2f_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" - - expect(page.body).to match('Authentication via U2F device failed') + expect(page).to have_content('We heard back from your U2F device') + expect(page).to have_content('Authentication via U2F device failed') end end @@ -229,11 +222,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: # Try authenticating user with the same U2F device login_as(current_user) @u2f_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" + expect(page).to have_content('We heard back from your U2F device') - expect(page.body).to match('href="/users/sign_out"') + expect(page).to have_css('.sign-out-link', visible: false) end end end @@ -243,11 +234,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: unregistered_device = FakeU2fDevice.new(page, FFaker::Name.first_name) login_as(user) unregistered_device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" + expect(page).to have_content('We heard back from your U2F device') - expect(page.body).to match('Authentication via U2F device failed') + expect(page).to have_content('Authentication via U2F device failed') end end @@ -270,11 +259,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: [first_device, second_device].each do |device| login_as(user) device.respond_to_u2f_authentication - click_on "Sign in via U2F device" - expect(page.body).to match('We heard back from your U2F device') - click_on "Authenticate via U2F Device" + expect(page).to have_content('We heard back from your U2F device') - expect(page.body).to match('href="/users/sign_out"') + expect(page).to have_css('.sign-out-link', visible: false) logout end @@ -299,4 +286,50 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: end end end + + describe 'fallback code authentication' do + let(:user) { create(:user) } + + def assert_fallback_ui(page) + expect(page).to have_button('Verify code') + expect(page).to have_css('#user_otp_attempt') + expect(page).not_to have_link('Sign in via 2FA code') + expect(page).not_to have_css('#js-authenticate-u2f') + end + + before do + # Register and logout + login_as(user) + user.update_attribute(:otp_required_for_login, true) + visit profile_account_path + end + + describe 'when no u2f device is registered' do + before do + logout + login_with(user) + end + + it 'shows the fallback otp code UI' do + assert_fallback_ui(page) + end + end + + describe 'when a u2f device is registered' do + before do + manage_two_factor_authentication + @u2f_device = register_u2f_device + logout + login_with(user) + end + + it 'provides a button that shows the fallback otp code UI' do + expect(page).to have_link('Sign in via 2FA code') + + click_link('Sign in via 2FA code') + + assert_fallback_ui(page) + end + end + end end diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index a8874ab12d3..064d18519ea 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -14,18 +14,19 @@ beforeEach(function() { this.u2fDevice = new MockU2FDevice; this.container = $("#js-authenticate-u2f"); - this.component = new U2FAuthenticate(this.container, { - sign_requests: [] - }, "token"); + this.component = new window.gl.U2FAuthenticate( + this.container, + '#js-login-u2f-form', + { + sign_requests: [] + }, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form') + ); return this.component.start(); }); it('allows authenticating via a U2F device', function() { - var authenticatedMessage, deviceResponse, inProgressMessage, setupButton, setupMessage; - setupButton = this.container.find("#js-login-u2f-device"); - setupMessage = this.container.find("p"); - expect(setupMessage.text()).toContain('Insert your security key'); - expect(setupButton.text()).toBe('Sign in via U2F device'); - setupButton.trigger('click'); + var authenticatedMessage, deviceResponse, inProgressMessage; inProgressMessage = this.container.find("p"); expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); this.u2fDevice.respondToAuthenticateRequest({ @@ -33,7 +34,7 @@ }); authenticatedMessage = this.container.find("p"); deviceResponse = this.container.find('#js-device-response'); - expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server"); + expect(authenticatedMessage.text()).toContain('We heard back from your U2F device. You have been authenticated.'); return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); }); return describe("errors", function() { @@ -62,7 +63,7 @@ deviceData: "this is data from the device" }); authenticatedMessage = this.container.find("p"); - return expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server"); + return expect(authenticatedMessage.text()).toContain("We heard back from your U2F device. You have been authenticated."); }); }); }); diff --git a/spec/support/fake_u2f_device.rb b/spec/support/fake_u2f_device.rb index 8c407b867fe..a7605cd483a 100644 --- a/spec/support/fake_u2f_device.rb +++ b/spec/support/fake_u2f_device.rb @@ -5,7 +5,7 @@ class FakeU2fDevice @page = page @name = name end - + def respond_to_u2f_registration app_id = @page.evaluate_script('gon.u2f.app_id') challenges = @page.evaluate_script('gon.u2f.challenges') @@ -28,6 +28,7 @@ class FakeU2fDevice u2f.sign = function(appId, challenges, signRequests, callback) { callback(#{json_response}); }; + window.gl.u2fAuthenticate.start(); ") end -- cgit v1.2.1 From d56d2a02c3542f09093e7c904b658dd3f6dc45dc Mon Sep 17 00:00:00 2001 From: Victor Wu Date: Tue, 27 Dec 2016 03:06:05 +0000 Subject: Add changelog --- changelogs/unreleased/filename-to-file-path.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/unreleased/filename-to-file-path.yml diff --git a/changelogs/unreleased/filename-to-file-path.yml b/changelogs/unreleased/filename-to-file-path.yml new file mode 100644 index 00000000000..3c6c838595a --- /dev/null +++ b/changelogs/unreleased/filename-to-file-path.yml @@ -0,0 +1,3 @@ +--- +title: Rename filename to file path in tooltip of file header in merge request diff +merge_request: 8314 \ No newline at end of file -- cgit v1.2.1 From 80a1d74c7fabcdf8929c84a5be7bfcbbff629c8c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 27 Dec 2016 06:05:29 +0000 Subject: Update vmstat to version 2.3.0 Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/26114 vmstat 2.3.0 fixes an issue with reporting the incorrect # of CPUs. This is used in the GitLab Admin Dashboard. --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 9dfaf7a48a2..c570759bb72 100644 --- a/Gemfile +++ b/Gemfile @@ -347,5 +347,5 @@ gem 'paranoia', '~> 2.2' gem 'health_check', '~> 2.2.0' # System information -gem 'vmstat', '~> 2.2' +gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' diff --git a/Gemfile.lock b/Gemfile.lock index 9f8367b420a..a68d6bc7115 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -773,7 +773,7 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - vmstat (2.2.0) + vmstat (2.3.0) warden (1.2.6) rack (>= 1.0) web-console (2.3.0) @@ -982,7 +982,7 @@ DEPENDENCIES unicorn-worker-killer (~> 0.4.4) version_sorter (~> 2.1.0) virtus (~> 1.0.1) - vmstat (~> 2.2) + vmstat (~> 2.3.0) web-console (~> 2.0) webmock (~> 1.21.0) wikicloth (= 0.8.1) -- cgit v1.2.1 From 269bc745063319cf0004de445c540af8d8c61ce5 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 27 Dec 2016 11:10:13 +0100 Subject: Disable timeout while removing services On GitLab.com this migration could take about 3 minutes. Disabling the statement we know we have enough time to complete this. Fixes #25976 --- db/post_migrate/20161221140236_remove_unneeded_services.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/db/post_migrate/20161221140236_remove_unneeded_services.rb b/db/post_migrate/20161221140236_remove_unneeded_services.rb index a94ccc43a41..6b7e94c8641 100644 --- a/db/post_migrate/20161221140236_remove_unneeded_services.rb +++ b/db/post_migrate/20161221140236_remove_unneeded_services.rb @@ -4,6 +4,8 @@ class RemoveUnneededServices < ActiveRecord::Migration DOWNTIME = false def up + disable_statement_timeout + execute("DELETE FROM services WHERE active = false AND properties = '{}';") end -- cgit v1.2.1 From 7daa541668de012b1c232763df0d03e29b5deb7c Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 27 Dec 2016 13:22:02 +0100 Subject: Link to k8s contributing doc. --- CONTRIBUTING.md | 5 +++-- vendor/gitignore/Clojure.gitignore | 14 +++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) mode change 120000 => 100644 vendor/gitignore/Clojure.gitignore diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 659871a06a4..b68c4a67826 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -217,8 +217,8 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce] and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note -that if an issue is marked for the current milestone either before or while you -are working on it, a team member may take over the merge request in order to +that if an issue is marked for the current milestone either before or while you +are working on it, a team member may take over the merge request in order to ensure the work is finished before the release date. If you want to add a new feature that is not labeled it is best to first create @@ -300,6 +300,7 @@ you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MRs that leads to higher code quality is more important to us than having a minimal commit log. The smaller an MR is the more likely it is it will be merged (quickly). After that you can send more MRs to enhance it. +The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this. For examples of feedback on merge requests please look at already [closed merge requests][closed-merge-requests]. If you would like quick feedback diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore deleted file mode 120000 index 7657a270c45..00000000000 --- a/vendor/gitignore/Clojure.gitignore +++ /dev/null @@ -1 +0,0 @@ -Leiningen.gitignore \ No newline at end of file diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore new file mode 100644 index 00000000000..a9fe6fba80d --- /dev/null +++ b/vendor/gitignore/Clojure.gitignore @@ -0,0 +1,13 @@ +pom.xml +pom.xml.asc +*.jar +*.class +/lib/ +/classes/ +/target/ +/checkouts/ +.lein-deps-sum +.lein-repl-history +.lein-plugins/ +.lein-failures +.nrepl-port -- cgit v1.2.1 From 123bc1d5ebc102c75456b54cbce8f1ec1bfe0eff Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 27 Dec 2016 13:45:44 +0100 Subject: Fix rename projects spec so it doesn't hang This spec would hang due to the use of transactions, and would incorrectly stub Thread.new. --- spec/migrations/rename_reserved_project_names_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb index 66f68570b50..4fb7ed36884 100644 --- a/spec/migrations/rename_reserved_project_names_spec.rb +++ b/spec/migrations/rename_reserved_project_names_spec.rb @@ -3,14 +3,16 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb') -describe RenameReservedProjectNames do +# This migration uses multiple threads, and thus different transactions. This +# means data created in this spec may not be visible to some threads. To work +# around this we use the TRUNCATE cleaning strategy. +describe RenameReservedProjectNames, truncate: true do let(:migration) { described_class.new } - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } before do project.path = 'projects' project.save!(validate: false) - allow(Thread).to receive(:new).and_yield end describe '#up' do -- cgit v1.2.1 From ab30273e94d36f4699bea2116f6183244018a2bf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 27 Dec 2016 13:52:13 +0100 Subject: Added changelog for MR !8322 [ci skip] --- changelogs/unreleased/changelog-mr8322.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/changelog-mr8322.yml diff --git a/changelogs/unreleased/changelog-mr8322.yml b/changelogs/unreleased/changelog-mr8322.yml new file mode 100644 index 00000000000..c645ed418af --- /dev/null +++ b/changelogs/unreleased/changelog-mr8322.yml @@ -0,0 +1,4 @@ +--- +title: Disable PostgreSQL statement timeouts when removing unneeded services +merge_request: 8322 +author: -- cgit v1.2.1 From 6c953fe12c7b2fcabbef83850aeccb6cc94d6fed Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Thu, 22 Dec 2016 13:22:57 -0600 Subject: Changed the label font size from 13px to 12px odd sizes don't mix well with windows and some linux system fonts --- app/assets/stylesheets/framework/tw_bootstrap.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 55bc325b858..28cbae9a449 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -91,7 +91,7 @@ // Labels .label { padding: 4px 5px; - font-size: 13px; + font-size: 12px; font-style: normal; font-weight: normal; display: inline-block; -- cgit v1.2.1 From dca0a42a95d4757f6caeb1fc8866c7ebc570744b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 20 Dec 2016 17:58:04 -0500 Subject: Truncate tag description and fix mobile for inidividual tag --- app/assets/stylesheets/framework/common.scss | 16 +++++++++ app/assets/stylesheets/framework/lists.scss | 5 +++ app/assets/stylesheets/framework/nav.scss | 47 ++++++++++++++++++++------- app/views/projects/tags/_tag.html.haml | 41 +++++++++++------------ app/views/projects/tags/index.html.haml | 6 ++-- app/views/projects/tags/show.html.haml | 16 +++++---- spec/features/tags/master_creates_tag_spec.rb | 2 +- 7 files changed, 91 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 251e43d2edd..5e3a91af86e 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -57,6 +57,11 @@ pre { border-radius: 0; color: $well-pre-color; } + + &.wrap { + word-break: break-word; + white-space: pre-wrap; + } } hr { @@ -67,6 +72,17 @@ hr { @include str-truncated; } +.block-truncated { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + > div, + .str-truncated { + display: inline; + } +} + .item-title { font-weight: 600; } /** FLASH message **/ diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index e96cd671e34..edc0c4ff094 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -208,6 +208,11 @@ ul.content-list { padding-right: 8px; } + .row-fixed-content { + flex: 0 0 auto; + margin-left: auto; + } + .row-title { font-weight: 600; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index bbf9de06630..4f84bc37f90 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -165,6 +165,8 @@ padding: 11px 0; margin-bottom: 0; + > .btn, + > .btn-container, > .dropdown { margin-right: $gl-padding-top; display: inline-block; @@ -172,16 +174,7 @@ &:last-child { margin-right: 0; - } - } - - > .btn { - margin-right: $gl-padding-top; - display: inline-block; - vertical-align: top; - - &:last-child { - margin-right: 0; + float: right; } } @@ -427,4 +420,36 @@ border-bottom: none; } } -} \ No newline at end of file +} + +@media (max-width: $screen-xs-max) { + .top-area .nav-controls { + $controls-margin: $btn-xs-side-margin - 2px; + + &.controls-flex { + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: center; + padding: 0 0 $gl-padding-top; + } + + .controls-item, + .controls-item:last-child { + flex: 1 1 35%; + display: block; + width: 100%; + margin: $controls-margin; + + .btn, + .dropdown { + margin: 0; + } + } + + .controls-item-full { + @extend .controls-item; + flex: 1 1 100%; + } + } +} diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index c42641afea0..8ef069b9e05 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -1,7 +1,7 @@ - commit = @repository.commit(tag.dereferenced_target) - release = @releases.find { |release| release.tag == tag.name } -%li - %div +%li.flex-row + .row-main-content.str-truncated = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do %span.item-title = icon('tag') @@ -10,24 +10,25 @@   = strip_gpg_signature(tag.message) - .controls - = render 'projects/buttons/download', project: @project, ref: tag.name + - if commit + .block-truncated + = render 'projects/branches/commit', commit: commit, project: @project + - else + %p + Cant find HEAD commit for this tag + - if release && release.description.present? + .description.prepend-top-default + .wiki + = preserve do + = markdown_field(release, :description) - - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do - = icon("pencil") + .row-fixed-content.controls + = render 'projects/buttons/download', project: @project, ref: tag.name - - if can?(current_user, :admin_project, @project) - = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do - = icon("trash-o") + - if can?(current_user, :push_code, @project) + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do + = icon("pencil") - - if commit - = render 'projects/branches/commit', commit: commit, project: @project - - else - %p - Cant find HEAD commit for this tag - - if release && release.description.present? - .description.prepend-top-default - .wiki - = preserve do - = markdown_field(release, :description) + - if can?(current_user, :admin_project, @project) + = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do + = icon("trash-o") diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 1d39f3a7534..d52162a5de8 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -11,8 +11,8 @@ = form_tag(filter_tags_path, method: :get) do = search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } - .dropdown.inline - %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} } + .dropdown + %button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} } %span.light = projects_sort_options_hash[@sort] = icon('chevron-down') @@ -30,7 +30,7 @@ .tags - if @tags.any? - %ul.content-list + %ul.flex-list.content-list = render partial: 'tag', collection: @tags = paginate @tags, theme: 'gitlab' diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 12facb6eb73..a8d462132c6 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -12,21 +12,23 @@ - else Cant find HEAD commit for this tag - .nav-controls + .nav-controls.controls-flex - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Edit release notes' do = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse files' do = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse commits' do = icon('history') - = render 'projects/buttons/download', project: @project, ref: @tag.name + .btn-container.controls-item + = render 'projects/buttons/download', project: @project, ref: @tag.name - if can?(current_user, :admin_project, @project) - .pull-right + .btn-container.controls-item-full = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o + - if @tag.message.present? - %pre.body + %pre.wrap = strip_gpg_signature(@tag.message) .append-bottom-default.prepend-top-default diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb index 08a97085a9c..ca25c696f75 100644 --- a/spec/features/tags/master_creates_tag_spec.rb +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -34,7 +34,7 @@ feature 'Master creates tag', feature: true do expect(current_path).to eq( namespace_project_tag_path(project.namespace, project, 'v3.0')) expect(page).to have_content 'v3.0' - page.within 'pre.body' do + page.within 'pre.wrap' do expect(page).to have_content "Awesome tag message\n\n- hello\n- world" end end -- cgit v1.2.1 From 6328ec3ab2595722afb780a9b60b44ef4c0bcac2 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 27 Dec 2016 11:26:25 -0600 Subject: Only limit container width on issues & MRs within fixed-width container --- app/assets/stylesheets/pages/issuable.scss | 60 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index eeb5b590625..9478b66f536 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -1,48 +1,50 @@ // Limit MR description for side-by-side diff view -.limit-container-width { - .detail-page-header { - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - - .issuable-details { - .detail-page-description, - .mr-source-target, - .mr-state-widget, - .merge-manually { +.container-limited { + &.limit-container-width { + .detail-page-header { max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); margin-left: auto; margin-right: auto; } - .merge-request-tabs-holder { - &.affix { - border-bottom: 1px solid $border-color; + .issuable-details { + .detail-page-description, + .mr-source-target, + .mr-state-widget, + .merge-manually { + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; + } + + .merge-request-tabs-holder { + &.affix { + border-bottom: 1px solid $border-color; + + .nav-links { + border: 0; + } + } - .nav-links { - border: 0; + .container-fluid { + padding-left: 0; + padding-right: 0; + max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); + margin-left: auto; + margin-right: auto; } } + } - .container-fluid { - padding-left: 0; - padding-right: 0; + .diffs { + .mr-version-controls, + .files-changed { max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); margin-left: auto; margin-right: auto; } } } - - .diffs { - .mr-version-controls, - .files-changed { - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - } } .issuable-details { -- cgit v1.2.1 From 55dc0941473ab61a3e6c050e362dc3975ba76aab Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 27 Dec 2016 14:33:01 -0500 Subject: Update CHANGELOG.md for 8.15.2 [ci skip] --- CHANGELOG.md | 15 +++++++++++++++ .../unreleased/25909-fix-mr-list-timestamp-alignment.yml | 4 ---- .../25930-discussion-actions-overlap-header-text.yml | 4 ---- ...uest-view-crash-when-commiting-a-js-sourcemap-file.yml | 4 ---- .../26018-mini-pipeline-hover-cross-broswer.yml | 4 ---- changelogs/unreleased/26026-pipeline-overflow-firefox.yml | 4 ---- .../26038-fix-confedential-warning-text-alignment.yml | 4 ---- changelogs/unreleased/26040-hide-scroll-top.yml | 4 ---- changelogs/unreleased/auto-deploy-with-space.yml | 4 ---- changelogs/unreleased/changelog-mr8322.yml | 4 ---- changelogs/unreleased/fix-latest-pipeine-ordering.yml | 4 ---- changelogs/unreleased/label-gfm-error-fix.yml | 4 ---- changelogs/unreleased/resolve-note-svg-color.yml | 4 ---- 13 files changed, 15 insertions(+), 48 deletions(-) delete mode 100644 changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml delete mode 100644 changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml delete mode 100644 changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml delete mode 100644 changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml delete mode 100644 changelogs/unreleased/26026-pipeline-overflow-firefox.yml delete mode 100644 changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml delete mode 100644 changelogs/unreleased/26040-hide-scroll-top.yml delete mode 100644 changelogs/unreleased/auto-deploy-with-space.yml delete mode 100644 changelogs/unreleased/changelog-mr8322.yml delete mode 100644 changelogs/unreleased/fix-latest-pipeine-ordering.yml delete mode 100644 changelogs/unreleased/label-gfm-error-fix.yml delete mode 100644 changelogs/unreleased/resolve-note-svg-color.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index c57ba82e38c..d4b2f041ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.15.2 (2016-12-27) + +- Fix mr list timestamp alignment. !8271 +- Fix discussion overlap text in regular screens. !8273 +- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282 +- Fix line breaking in nodes of the pipeline graph in firefox. !8292 +- Fixes confendential warning text alignment. !8293 +- Hide Scroll Top button for failed build page. !8295 +- Fix finding the latest pipeline. !8301 +- Disable PostgreSQL statement timeouts when removing unneeded services. !8322 +- Fix timeout when MR contains large files marked as binary by .gitattributes. +- Rename "autodeploy" to "auto deploy". +- Fixed GFM autocomplete error when no data exists. +- Fixed resolve discussion note button color. + ## 8.15.1 (2016-12-23) - Push payloads schedule at most 100 commits, instead of all commits. diff --git a/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml b/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml deleted file mode 100644 index 454d8799432..00000000000 --- a/changelogs/unreleased/25909-fix-mr-list-timestamp-alignment.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix mr list timestamp alignment -merge_request: 8271 -author: diff --git a/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml b/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml deleted file mode 100644 index 54a461c24ed..00000000000 --- a/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix discussion overlap text in regular screens -merge_request: 8273 -author: diff --git a/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml b/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml deleted file mode 100644 index 023ec1abcfa..00000000000 --- a/changelogs/unreleased/25931-gitlab-merge-request-view-crash-when-commiting-a-js-sourcemap-file.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix timeout when MR contains large files marked as binary by .gitattributes -merge_request: -author: diff --git a/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml b/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml deleted file mode 100644 index 501f0b25a21..00000000000 --- a/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari -merge_request: 8282 -author: diff --git a/changelogs/unreleased/26026-pipeline-overflow-firefox.yml b/changelogs/unreleased/26026-pipeline-overflow-firefox.yml deleted file mode 100644 index bf11d877c7e..00000000000 --- a/changelogs/unreleased/26026-pipeline-overflow-firefox.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix line breaking in nodes of the pipeline graph in firefox -merge_request: 8292 -author: diff --git a/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml b/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml deleted file mode 100644 index 420be5a3b10..00000000000 --- a/changelogs/unreleased/26038-fix-confedential-warning-text-alignment.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes confendential warning text alignment -merge_request: 8293 -author: diff --git a/changelogs/unreleased/26040-hide-scroll-top.yml b/changelogs/unreleased/26040-hide-scroll-top.yml deleted file mode 100644 index f68cb1dd51d..00000000000 --- a/changelogs/unreleased/26040-hide-scroll-top.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Hide Scroll Top button for failed build page -merge_request: 8295 -author: diff --git a/changelogs/unreleased/auto-deploy-with-space.yml b/changelogs/unreleased/auto-deploy-with-space.yml deleted file mode 100644 index a206e3ae85c..00000000000 --- a/changelogs/unreleased/auto-deploy-with-space.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Rename "autodeploy" to "auto deploy" -merge_request: -author: diff --git a/changelogs/unreleased/changelog-mr8322.yml b/changelogs/unreleased/changelog-mr8322.yml deleted file mode 100644 index c645ed418af..00000000000 --- a/changelogs/unreleased/changelog-mr8322.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Disable PostgreSQL statement timeouts when removing unneeded services -merge_request: 8322 -author: diff --git a/changelogs/unreleased/fix-latest-pipeine-ordering.yml b/changelogs/unreleased/fix-latest-pipeine-ordering.yml deleted file mode 100644 index b155d51741b..00000000000 --- a/changelogs/unreleased/fix-latest-pipeine-ordering.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix finding the latest pipeline -merge_request: 8301 -author: diff --git a/changelogs/unreleased/label-gfm-error-fix.yml b/changelogs/unreleased/label-gfm-error-fix.yml deleted file mode 100644 index 37f311d4790..00000000000 --- a/changelogs/unreleased/label-gfm-error-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed GFM autocomplete error when no data exists -merge_request: -author: diff --git a/changelogs/unreleased/resolve-note-svg-color.yml b/changelogs/unreleased/resolve-note-svg-color.yml deleted file mode 100644 index 500d4e9d5b1..00000000000 --- a/changelogs/unreleased/resolve-note-svg-color.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed resolve discussion note button color -merge_request: -author: -- cgit v1.2.1 From a6ed18cc714f43eea9ecbd5060ad6c26060a628e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:15:03 -0600 Subject: remove superfluous return statements generated by coffeescript --- app/assets/javascripts/preview_markdown.js | 51 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 1e261cd49c2..2526cd391f0 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -22,14 +22,14 @@ mdText = form.find('textarea.markdown-area').val(); if (mdText.trim().length === 0) { preview.text('Nothing to preview.'); - return this.hideReferencedUsers(form); + this.hideReferencedUsers(form); } else { preview.text('Loading...'); - return this.renderMarkdown(mdText, (function(_this) { + this.renderMarkdown(mdText, (function(_this) { return function(response) { preview.html(response.body); preview.renderGFM(); - return _this.renderReferencedUsers(response.references.users, form); + _this.renderReferencedUsers(response.references.users, form); }; })(this)); } @@ -40,9 +40,10 @@ return; } if (text === this.ajaxCache.text) { - return success(this.ajaxCache.response); + success(this.ajaxCache.response); + return; } - return $.ajax({ + $.ajax({ type: 'POST', url: window.preview_markdown_path, data: { @@ -55,16 +56,14 @@ text: text, response: response }; - return success(response); + success(response); }; })(this) }); }; MarkdownPreview.prototype.hideReferencedUsers = function(form) { - var referencedUsers; - referencedUsers = form.find('.referenced-users'); - return referencedUsers.hide(); + form.find('.referenced-users').hide(); }; MarkdownPreview.prototype.renderReferencedUsers = function(users, form) { @@ -73,15 +72,14 @@ if (referencedUsers.length) { if (users.length >= this.referenceThreshold) { referencedUsers.show(); - return referencedUsers.find('.js-referenced-users-count').text(users.length); + referencedUsers.find('.js-referenced-users-count').text(users.length); } else { - return referencedUsers.hide(); + referencedUsers.hide(); } } }; return MarkdownPreview; - })(); markdownPreview = new window.MarkdownPreview(); @@ -93,15 +91,14 @@ lastTextareaPreviewed = null; $.fn.setupMarkdownPreview = function() { - var $form, form_textarea; - $form = $(this); - form_textarea = $form.find('textarea.markdown-area'); - form_textarea.on('input', function() { - return markdownPreview.hideReferencedUsers($form); - }); - return form_textarea.on('blur', function() { - return markdownPreview.showPreview($form); - }); + var $form = $(this); + $form.find('textarea.markdown-area') + .on('input', function() { + markdownPreview.hideReferencedUsers($form); + }) + .on('blur', function() { + markdownPreview.showPreview($form); + }); }; $(document).on('markdown-preview:show', function(e, $form) { @@ -115,7 +112,7 @@ // toggle content $form.find('.md-write-holder').hide(); $form.find('.md-preview-holder').show(); - return markdownPreview.showPreview($form); + markdownPreview.showPreview($form); }); $(document).on('markdown-preview:hide', function(e, $form) { @@ -129,7 +126,7 @@ // toggle content $form.find('.md-write-holder').show(); $form.find('textarea.markdown-area').focus(); - return $form.find('.md-preview-holder').hide(); + $form.find('.md-preview-holder').hide(); }); $(document).on('markdown-preview:toggle', function(e, keyboardEvent) { @@ -137,11 +134,11 @@ $target = $(keyboardEvent.target); if ($target.is('textarea.markdown-area')) { $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]); - return keyboardEvent.preventDefault(); + keyboardEvent.preventDefault(); } else if (lastTextareaPreviewed) { $target = lastTextareaPreviewed; $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]); - return keyboardEvent.preventDefault(); + keyboardEvent.preventDefault(); } }); @@ -149,14 +146,14 @@ var $form; e.preventDefault(); $form = $(this).closest('form'); - return $(document).triggerHandler('markdown-preview:show', [$form]); + $(document).triggerHandler('markdown-preview:show', [$form]); }); $(document).on('click', writeButtonSelector, function(e) { var $form; e.preventDefault(); $form = $(this).closest('form'); - return $(document).triggerHandler('markdown-preview:hide', [$form]); + $(document).triggerHandler('markdown-preview:hide', [$form]); }); }).call(this); -- cgit v1.2.1 From 4f8087d62b646f99e42d2d745ea9c39fdbfe0bfb Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:18:40 -0600 Subject: refactor _this usage into proper function binding --- app/assets/javascripts/preview_markdown.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 2526cd391f0..e2260b37f91 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -25,13 +25,11 @@ this.hideReferencedUsers(form); } else { preview.text('Loading...'); - this.renderMarkdown(mdText, (function(_this) { - return function(response) { - preview.html(response.body); - preview.renderGFM(); - _this.renderReferencedUsers(response.references.users, form); - }; - })(this)); + this.renderMarkdown(mdText, (function(response) { + preview.html(response.body); + preview.renderGFM(); + this.renderReferencedUsers(response.references.users, form); + }).bind(this)); } }; @@ -50,15 +48,13 @@ text: text }, dataType: 'json', - success: (function(_this) { - return function(response) { - _this.ajaxCache = { - text: text, - response: response - }; - success(response); + success: (function(response) { + this.ajaxCache = { + text: text, + response: response }; - })(this) + success(response); + }).bind(this) }); }; -- cgit v1.2.1 From de427dc58959775f104776b4316349a90190fd1d Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:35:19 -0600 Subject: use $ sign to make it clear when dealing with jQuery object --- app/assets/javascripts/preview_markdown.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index e2260b37f91..6d9e40d4e91 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -16,19 +16,19 @@ MarkdownPreview.prototype.ajaxCache = {}; - MarkdownPreview.prototype.showPreview = function(form) { + MarkdownPreview.prototype.showPreview = function($form) { var mdText, preview; - preview = form.find('.js-md-preview'); - mdText = form.find('textarea.markdown-area').val(); + preview = $form.find('.js-md-preview'); + mdText = $form.find('textarea.markdown-area').val(); if (mdText.trim().length === 0) { preview.text('Nothing to preview.'); - this.hideReferencedUsers(form); + this.hideReferencedUsers($form); } else { preview.text('Loading...'); this.renderMarkdown(mdText, (function(response) { preview.html(response.body); preview.renderGFM(); - this.renderReferencedUsers(response.references.users, form); + this.renderReferencedUsers(response.references.users, $form); }).bind(this)); } }; @@ -58,13 +58,13 @@ }); }; - MarkdownPreview.prototype.hideReferencedUsers = function(form) { - form.find('.referenced-users').hide(); + MarkdownPreview.prototype.hideReferencedUsers = function($form) { + $form.find('.referenced-users').hide(); }; - MarkdownPreview.prototype.renderReferencedUsers = function(users, form) { + MarkdownPreview.prototype.renderReferencedUsers = function(users, $form) { var referencedUsers; - referencedUsers = form.find('.referenced-users'); + referencedUsers = $form.find('.referenced-users'); if (referencedUsers.length) { if (users.length >= this.referenceThreshold) { referencedUsers.show(); -- cgit v1.2.1 From 2b6f6e7b655e9a5b5d58b41added3da48b1cc639 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:37:11 -0600 Subject: rename renderMarkdown to fetchMarkdownPreview to be less misleading --- app/assets/javascripts/preview_markdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 6d9e40d4e91..bbdb1c10f33 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -25,7 +25,7 @@ this.hideReferencedUsers($form); } else { preview.text('Loading...'); - this.renderMarkdown(mdText, (function(response) { + this.fetchMarkdownPreview(mdText, (function(response) { preview.html(response.body); preview.renderGFM(); this.renderReferencedUsers(response.references.users, $form); @@ -33,7 +33,7 @@ } }; - MarkdownPreview.prototype.renderMarkdown = function(text, success) { + MarkdownPreview.prototype.fetchMarkdownPreview = function(text, success) { if (!window.preview_markdown_path) { return; } -- cgit v1.2.1 From 948fd40ccdca048ad9bc8342a31bd2061f0707fd Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:48:49 -0600 Subject: add loading class to preview element and check it to prevent multiple ajax requests --- app/assets/javascripts/preview_markdown.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index bbdb1c10f33..b1a29b46133 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -23,10 +23,10 @@ if (mdText.trim().length === 0) { preview.text('Nothing to preview.'); this.hideReferencedUsers($form); - } else { - preview.text('Loading...'); + } else if (!preview.hasClass('md-preview-loading')) { + preview.addClass('md-preview-loading').text('Loading...'); this.fetchMarkdownPreview(mdText, (function(response) { - preview.html(response.body); + preview.removeClass('md-preview-loading').html(response.body); preview.renderGFM(); this.renderReferencedUsers(response.references.users, $form); }).bind(this)); -- cgit v1.2.1 From 75ef84df95a9cae0c164994f88ceb53c833fee10 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:55:17 -0600 Subject: fix some eslint rules --- app/assets/javascripts/preview_markdown.js | 37 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index b1a29b46133..efe2351652f 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -1,14 +1,14 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, camelcase, prefer-arrow-callback, max-len */ +/* eslint-disable func-names, no-var, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, max-len */ // MarkdownPreview // // Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, // and showing a warning when more than `x` users are referenced. // -(function() { +(function () { var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector; - window.MarkdownPreview = (function() { + window.MarkdownPreview = (function () { function MarkdownPreview() {} // Minimum number of users referenced before triggering a warning @@ -16,7 +16,7 @@ MarkdownPreview.prototype.ajaxCache = {}; - MarkdownPreview.prototype.showPreview = function($form) { + MarkdownPreview.prototype.showPreview = function ($form) { var mdText, preview; preview = $form.find('.js-md-preview'); mdText = $form.find('textarea.markdown-area').val(); @@ -25,7 +25,7 @@ this.hideReferencedUsers($form); } else if (!preview.hasClass('md-preview-loading')) { preview.addClass('md-preview-loading').text('Loading...'); - this.fetchMarkdownPreview(mdText, (function(response) { + this.fetchMarkdownPreview(mdText, (function (response) { preview.removeClass('md-preview-loading').html(response.body); preview.renderGFM(); this.renderReferencedUsers(response.references.users, $form); @@ -33,7 +33,7 @@ } }; - MarkdownPreview.prototype.fetchMarkdownPreview = function(text, success) { + MarkdownPreview.prototype.fetchMarkdownPreview = function (text, success) { if (!window.preview_markdown_path) { return; } @@ -48,7 +48,7 @@ text: text }, dataType: 'json', - success: (function(response) { + success: (function (response) { this.ajaxCache = { text: text, response: response @@ -58,11 +58,11 @@ }); }; - MarkdownPreview.prototype.hideReferencedUsers = function($form) { + MarkdownPreview.prototype.hideReferencedUsers = function ($form) { $form.find('.referenced-users').hide(); }; - MarkdownPreview.prototype.renderReferencedUsers = function(users, $form) { + MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) { var referencedUsers; referencedUsers = $form.find('.referenced-users'); if (referencedUsers.length) { @@ -76,7 +76,7 @@ }; return MarkdownPreview; - })(); + }()); markdownPreview = new window.MarkdownPreview(); @@ -86,18 +86,18 @@ lastTextareaPreviewed = null; - $.fn.setupMarkdownPreview = function() { + $.fn.setupMarkdownPreview = function () { var $form = $(this); $form.find('textarea.markdown-area') - .on('input', function() { + .on('input', function () { markdownPreview.hideReferencedUsers($form); }) - .on('blur', function() { + .on('blur', function () { markdownPreview.showPreview($form); }); }; - $(document).on('markdown-preview:show', function(e, $form) { + $(document).on('markdown-preview:show', function (e, $form) { if (!$form) { return; } @@ -111,7 +111,7 @@ markdownPreview.showPreview($form); }); - $(document).on('markdown-preview:hide', function(e, $form) { + $(document).on('markdown-preview:hide', function (e, $form) { if (!$form) { return; } @@ -125,7 +125,7 @@ $form.find('.md-preview-holder').hide(); }); - $(document).on('markdown-preview:toggle', function(e, keyboardEvent) { + $(document).on('markdown-preview:toggle', function (e, keyboardEvent) { var $target; $target = $(keyboardEvent.target); if ($target.is('textarea.markdown-area')) { @@ -138,18 +138,17 @@ } }); - $(document).on('click', previewButtonSelector, function(e) { + $(document).on('click', previewButtonSelector, function (e) { var $form; e.preventDefault(); $form = $(this).closest('form'); $(document).triggerHandler('markdown-preview:show', [$form]); }); - $(document).on('click', writeButtonSelector, function(e) { + $(document).on('click', writeButtonSelector, function (e) { var $form; e.preventDefault(); $form = $(this).closest('form'); $(document).triggerHandler('markdown-preview:hide', [$form]); }); - }).call(this); -- cgit v1.2.1 From aa1c3ab227c0a2dd7d0a3d777f4516f6d619d788 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Sun, 11 Dec 2016 21:57:36 -0600 Subject: fix some more eslint rules --- app/assets/javascripts/preview_markdown.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index efe2351652f..d22b67724b2 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, max-len */ +/* eslint-disable func-names, no-var, object-shorthand, comma-dangle, prefer-arrow-callback */ // MarkdownPreview // @@ -6,7 +6,10 @@ // and showing a warning when more than `x` users are referenced. // (function () { - var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector; + var lastTextareaPreviewed; + var markdownPreview; + var previewButtonSelector; + var writeButtonSelector; window.MarkdownPreview = (function () { function MarkdownPreview() {} @@ -17,9 +20,8 @@ MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.showPreview = function ($form) { - var mdText, preview; - preview = $form.find('.js-md-preview'); - mdText = $form.find('textarea.markdown-area').val(); + var preview = $form.find('.js-md-preview'); + var mdText = $form.find('textarea.markdown-area').val(); if (mdText.trim().length === 0) { preview.text('Nothing to preview.'); this.hideReferencedUsers($form); @@ -151,4 +153,4 @@ $form = $(this).closest('form'); $(document).triggerHandler('markdown-preview:hide', [$form]); }); -}).call(this); +}()); -- cgit v1.2.1 From bdf3a2f48f0e78cfc1796505fdd5dc06e029c9b5 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 27 Dec 2016 20:53:21 +0000 Subject: Revert "Merge branch 'increase-left-padding-of-filter-row-labels' into 'master'" This reverts merge request !8258 --- app/assets/stylesheets/pages/labels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 237869aa544..d129eb12a45 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -98,7 +98,7 @@ } .label { - padding: 8px 9px 9px $gl-padding; + padding: 8px 9px 9px; font-size: 14px; } } -- cgit v1.2.1 From 1b082a4c338d7575e15d7450906801db59873441 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 14 Dec 2016 19:39:53 -0200 Subject: Check if user can read issue before being assigned --- app/models/concerns/issuable.rb | 6 +-- app/services/issuable_base_service.rb | 41 ++++++++++++----- app/services/issues/base_service.rb | 4 -- app/services/merge_requests/base_service.rb | 4 -- changelogs/unreleased/issue_22664.yml | 2 +- spec/models/concerns/issuable_spec.rb | 29 +++++++++--- spec/services/issuable/bulk_update_service_spec.rb | 12 +++-- spec/services/issues/create_service_spec.rb | 2 + spec/services/issues/update_service_spec.rb | 11 +++++ .../services/merge_requests/create_service_spec.rb | 2 + spec/services/notes/slash_commands_service_spec.rb | 2 + .../issuable_create_service_shared_examples.rb | 52 ++++++++++++++++++++++ ...reate_service_slash_commands_shared_examples.rb | 4 +- .../issuable_update_service_shared_examples.rb | 52 ++++++++++++++++++++++ 14 files changed, 189 insertions(+), 34 deletions(-) create mode 100644 spec/support/services/issuable_create_service_shared_examples.rb diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index ecbfb625c5e..5e63825bf99 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -93,9 +93,9 @@ module Issuable def update_assignee_cache_counts # make sure we flush the cache for both the old *and* new assignees(if they exist) - previous_assignee = User.find_by_id(assignee_id_was) - previous_assignee.try(:update_cache_counts) - assignee.try(:update_cache_counts) + previous_assignee = User.find_by_id(assignee_id_was) if assignee_id_was + previous_assignee.update_cache_counts if previous_assignee + assignee.update_cache_counts if assignee end # We want to use optimistic lock for cases when only title or description are involved diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index ab3d2a9a0cd..4ce5fd993d9 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -36,14 +36,10 @@ class IssuableBaseService < BaseService end end - def filter_params(issuable_ability_name = :issue) - filter_assignee - filter_milestone - filter_labels + def filter_params(issuable) + ability_name = :"admin_#{issuable.to_ability_name}" - ability = :"admin_#{issuable_ability_name}" - - unless can?(current_user, ability, project) + unless can?(current_user, ability_name, project) params.delete(:milestone_id) params.delete(:labels) params.delete(:add_label_ids) @@ -52,14 +48,35 @@ class IssuableBaseService < BaseService params.delete(:assignee_id) params.delete(:due_date) end + + filter_assignee(issuable) + filter_milestone + filter_labels end - def filter_assignee - if params[:assignee_id] == IssuableFinder::NONE - params[:assignee_id] = '' + def filter_assignee(issuable) + return unless params[:assignee_id].present? + + assignee_id = params[:assignee_id] + + if assignee_id.to_s == IssuableFinder::NONE + params[:assignee_id] = "" + else + params.delete(:assignee_id) unless assignee_can_read?(issuable, assignee_id) end end + def assignee_can_read?(issuable, assignee_id) + new_assignee = User.find_by_id(assignee_id) + + return false unless new_assignee.present? + + ability_name = :"read_#{issuable.to_ability_name}" + resource = issuable.persisted? ? issuable : project + + can?(new_assignee, ability_name, resource) + end + def filter_milestone milestone_id = params[:milestone_id] return unless milestone_id @@ -138,7 +155,7 @@ class IssuableBaseService < BaseService def create(issuable) merge_slash_commands_into_params!(issuable) - filter_params + filter_params(issuable) params.delete(:state_event) params[:author] ||= current_user @@ -180,7 +197,7 @@ class IssuableBaseService < BaseService change_state(issuable) change_subscription(issuable) change_todo(issuable) - filter_params + filter_params(issuable) old_labels = issuable.labels.to_a old_mentioned_users = issuable.mentioned_users.to_a diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 742e834df97..35af867a098 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -17,10 +17,6 @@ module Issues private - def filter_params - super(:issue) - end - def execute_hooks(issue, action = 'open') issue_data = hook_data(issue, action) hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 800fd39c424..70e25956dc7 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -38,10 +38,6 @@ module MergeRequests private - def filter_params - super(:merge_request) - end - def merge_requests_for(branch) origin_merge_requests = @project.origin_merge_requests .opened.where(source_branch: branch).to_a diff --git a/changelogs/unreleased/issue_22664.yml b/changelogs/unreleased/issue_22664.yml index 28d3e74b1f8..18a8d9ec6be 100644 --- a/changelogs/unreleased/issue_22664.yml +++ b/changelogs/unreleased/issue_22664.yml @@ -1,4 +1,4 @@ --- -title: Fix issuable assignee update bug when previous assignee is null +title: Check if user can read project before being assigned to issue merge_request: author: diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 3cc96816cb0..1078c959419 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -44,21 +44,40 @@ describe Issue, "Issuable" do it { expect(described_class).to respond_to(:assigned) } end - describe "after_save" do + describe "before_save" do describe "#update_cache_counts" do context "when previous assignee exists" do - it "user updates cache counts" do + before do + assignee = create(:user) + issue.project.team << [assignee, :developer] + issue.update(assignee: assignee) + end + + it "updates cache counts for new assignee" do + user = create(:user) + expect(user).to receive(:update_cache_counts) issue.update(assignee: user) end + + it "updates cache counts for previous assignee" do + old_assignee = issue.assignee + allow(User).to receive(:find_by_id).with(old_assignee.id).and_return(old_assignee) + + expect(old_assignee).to receive(:update_cache_counts) + + issue.update(assignee: nil) + end end context "when previous assignee does not exist" do - it "does not raise error" do - issue.update(assignee_id: "") + before{ issue.update(assignee: nil) } - expect { issue.update(assignee_id: user) }.not_to raise_error + it "updates cache count for the new assignee" do + expect_any_instance_of(User).to receive(:update_cache_counts) + + issue.update(assignee: user) end end end diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb index 5f3020b6525..0475f38fe5e 100644 --- a/spec/services/issuable/bulk_update_service_spec.rb +++ b/spec/services/issuable/bulk_update_service_spec.rb @@ -52,7 +52,10 @@ describe Issuable::BulkUpdateService, services: true do context 'when the new assignee ID is a valid user' do it 'succeeds' do - result = bulk_update(issue, assignee_id: create(:user).id) + new_assignee = create(:user) + project.team << [new_assignee, :developer] + + result = bulk_update(issue, assignee_id: new_assignee.id) expect(result[:success]).to be_truthy expect(result[:count]).to eq(1) @@ -60,15 +63,16 @@ describe Issuable::BulkUpdateService, services: true do it 'updates the assignee to the use ID passed' do assignee = create(:user) + project.team << [assignee, :developer] expect { bulk_update(issue, assignee_id: assignee.id) } .to change { issue.reload.assignee }.from(user).to(assignee) end end - context 'when the new assignee ID is -1' do - it 'unassigns the issues' do - expect { bulk_update(issue, assignee_id: -1) } + context "when the new assignee ID is #{IssuableFinder::NONE}" do + it "unassigns the issues" do + expect { bulk_update(issue, assignee_id: IssuableFinder::NONE) } .to change { issue.reload.assignee }.to(nil) end end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 8bde61ee336..ac3834c32ff 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -135,6 +135,8 @@ describe Issues::CreateService, services: true do end end + it_behaves_like 'issuable create service' + it_behaves_like 'new issuable record that supports slash commands' context 'for a merge request' do diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index eafbea46905..d83b09fd32c 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -142,6 +142,17 @@ describe Issues::UpdateService, services: true do update_issue(confidential: true) end + + it 'does not update assignee_id with unauthorized users' do + project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + update_issue(confidential: true) + non_member = create(:user) + original_assignee = issue.assignee + + update_issue(assignee_id: non_member.id) + + expect(issue.reload.assignee_id).to eq(original_assignee.id) + end end context 'todos' do diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index b8142889075..673c0bd6c9c 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -84,6 +84,8 @@ describe MergeRequests::CreateService, services: true do end end + it_behaves_like 'issuable create service' + context 'while saving references to issues that the created merge request closes' do let(:first_issue) { create(:issue, project: project) } let(:second_issue) { create(:issue, project: project) } diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/slash_commands_service_spec.rb index d1099884a02..960b5cd5e6f 100644 --- a/spec/services/notes/slash_commands_service_spec.rb +++ b/spec/services/notes/slash_commands_service_spec.rb @@ -5,6 +5,8 @@ describe Notes::SlashCommandsService, services: true do let(:project) { create(:empty_project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:assignee) { create(:user) } + + before { project.team << [assignee, :master] } end shared_examples 'note on noteable that does not support slash commands' do diff --git a/spec/support/services/issuable_create_service_shared_examples.rb b/spec/support/services/issuable_create_service_shared_examples.rb new file mode 100644 index 00000000000..93c0267d2db --- /dev/null +++ b/spec/support/services/issuable_create_service_shared_examples.rb @@ -0,0 +1,52 @@ +shared_examples 'issuable create service' do + context 'asssignee_id' do + let(:assignee) { create(:user) } + + before { project.team << [user, :master] } + + it 'removes assignee_id when user id is invalid' do + opts = { title: 'Title', description: 'Description', assignee_id: -1 } + + issuable = described_class.new(project, user, opts).execute + + expect(issuable.assignee_id).to be_nil + end + + it 'removes assignee_id when user id is 0' do + opts = { title: 'Title', description: 'Description', assignee_id: 0 } + + issuable = described_class.new(project, user, opts).execute + + expect(issuable.assignee_id).to be_nil + end + + it 'saves assignee when user id is valid' do + project.team << [assignee, :master] + opts = { title: 'Title', description: 'Description', assignee_id: assignee.id } + + issuable = described_class.new(project, user, opts).execute + + expect(issuable.assignee_id).to eq(assignee.id) + end + + context "when issuable feature is private" do + before do + project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE) + project.project_feature.update(merge_requests_access_level: ProjectFeature::PRIVATE) + end + + levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC] + + levels.each do |level| + it "removes not authorized assignee when project is #{Gitlab::VisibilityLevel.level_name(level)}" do + project.update(visibility_level: level) + opts = { title: 'Title', description: 'Description', assignee_id: assignee.id } + + issuable = described_class.new(project, user, opts).execute + + expect(issuable.assignee_id).to be_nil + end + end + end + end +end diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index 5f9645ed44f..dd54b0addda 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -11,6 +11,8 @@ shared_examples 'new issuable record that supports slash commands' do let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) } let(:issuable) { described_class.new(project, user, params).execute } + before { project.team << [assignee, :master ] } + context 'with labels in command only' do let(:example_params) do { @@ -55,7 +57,7 @@ shared_examples 'new issuable record that supports slash commands' do context 'with assignee and milestone in params and command' do let(:example_params) do { - assignee: build_stubbed(:user), + assignee: create(:user), milestone_id: double(:milestone), description: %(/assign @#{assignee.username}\n/milestone %"#{milestone.name}") } diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb index a3336755773..49cea1e608c 100644 --- a/spec/support/services/issuable_update_service_shared_examples.rb +++ b/spec/support/services/issuable_update_service_shared_examples.rb @@ -1,4 +1,8 @@ shared_examples 'issuable update service' do + def update_issuable(opts) + described_class.new(project, user, opts).execute(open_issuable) + end + context 'changing state' do before { expect(project).to receive(:execute_hooks).once } @@ -14,4 +18,52 @@ shared_examples 'issuable update service' do end end end + + context 'asssignee_id' do + it 'does not update assignee when assignee_id is invalid' do + open_issuable.update(assignee_id: user.id) + + update_issuable(assignee_id: -1) + + expect(open_issuable.reload.assignee).to eq(user) + end + + it 'unassigns assignee when user id is 0' do + open_issuable.update(assignee_id: user.id) + + update_issuable(assignee_id: 0) + + expect(open_issuable.assignee_id).to be_nil + end + + it 'saves assignee when user id is valid' do + update_issuable(assignee_id: user.id) + + expect(open_issuable.assignee_id).to eq(user.id) + end + + it 'does not update assignee_id when user cannot read issue' do + non_member = create(:user) + original_assignee = open_issuable.assignee + + update_issuable(assignee_id: non_member.id) + + expect(open_issuable.assignee_id).to eq(original_assignee.id) + end + + context "when issuable feature is private" do + levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC] + + levels.each do |level| + it "does not update with unauthorized assignee when project is #{Gitlab::VisibilityLevel.level_name(level)}" do + assignee = create(:user) + project.update(visibility_level: level) + feature_visibility_attr = :"#{open_issuable.model_name.plural}_access_level" + project.project_feature.update_attribute(feature_visibility_attr, ProjectFeature::PRIVATE) + + expect{ update_issuable(assignee_id: assignee) }.not_to change{ open_issuable.assignee } + end + end + end + end end -- cgit v1.2.1 From 1521e2ce012a2555b06e217de2fcd8f5ac54ce87 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 27 Dec 2016 15:43:54 -0600 Subject: re-enable submit button after username change failure --- app/assets/javascripts/profile/profile.js.es6 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6 index eb2fe3477a2..aef2e3a3fa8 100644 --- a/app/assets/javascripts/profile/profile.js.es6 +++ b/app/assets/javascripts/profile/profile.js.es6 @@ -41,15 +41,12 @@ } beforeUpdateUsername() { - $('.loading-username').show(); - $(this).find('.update-success').hide(); - return $(this).find('.update-failed').hide(); + $('.loading-username', this).removeClass('hidden'); } afterUpdateUsername() { - $('.loading-username').hide(); - $(this).find('.btn-save').enable(); - return $(this).find('.loading-gif').hide(); + $('.loading-username', this).addClass('hidden'); + $('button[type=submit]', this).enable(); } onUpdateNotifs(e, data) { -- cgit v1.2.1 From 3b33ddac5b110d99f417ad72384e24c80f0df3b2 Mon Sep 17 00:00:00 2001 From: dimitrieh Date: Tue, 27 Dec 2016 22:54:56 +0100 Subject: darkened hr border color in descriptions because of update of bootstrap --- app/assets/stylesheets/framework/common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 251e43d2edd..a32e8aeeafb 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -61,6 +61,7 @@ pre { hr { margin: $gl-padding 0; + border-top: 1px solid darken($gray-normal, 8%); } .str-truncated { -- cgit v1.2.1 From 7e3b63e1b3a7272e5dcb240e200a400d8b62f223 Mon Sep 17 00:00:00 2001 From: dimitrieh Date: Tue, 27 Dec 2016 22:59:20 +0100 Subject: changelog entry --- changelogs/unreleased/fix-light-hr-in-descriptions.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-light-hr-in-descriptions.yml diff --git a/changelogs/unreleased/fix-light-hr-in-descriptions.yml b/changelogs/unreleased/fix-light-hr-in-descriptions.yml new file mode 100644 index 00000000000..8efd471e416 --- /dev/null +++ b/changelogs/unreleased/fix-light-hr-in-descriptions.yml @@ -0,0 +1,4 @@ +--- +title: Darkened hr border color in descriptions because of update of bootstrap +merge_request: 8333 +author: -- cgit v1.2.1 From a6e71b9fc90dc57edb3a5bd2bd1a2351f668ced1 Mon Sep 17 00:00:00 2001 From: dimitrieh Date: Tue, 27 Dec 2016 23:22:50 +0100 Subject: Change status colors of runners to better defaults --- app/assets/stylesheets/framework/variables.scss | 6 +++--- .../25430-make-colors-in-runner-status-more-colorblind-friendly.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3e1fded6b6b..b0c4a6edf57 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -489,9 +489,9 @@ $project-network-controls-color: #888; */ $runner-state-shared-bg: #32b186; $runner-state-specific-bg: #3498db; -$runner-status-online-color: green; -$runner-status-offline-color: gray; -$runner-status-paused-color: red; +$runner-status-online-color: $green-normal; +$runner-status-offline-color: $gray-darkest; +$runner-status-paused-color: $red-normal; /* Stat Graph diff --git a/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml b/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml new file mode 100644 index 00000000000..e60c42cdfba --- /dev/null +++ b/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml @@ -0,0 +1,4 @@ +--- +title: Change status colors of runners to better defaults +merge_request: +author: -- cgit v1.2.1 From 8c1f287cb70b958ed5e3b9f2f3b31c531f8a8405 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 27 Dec 2016 16:23:57 -0600 Subject: remove preview markdown ajax trigger on textarea blur --- app/assets/javascripts/preview_markdown.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index d22b67724b2..c06fdf0aa65 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -90,13 +90,9 @@ $.fn.setupMarkdownPreview = function () { var $form = $(this); - $form.find('textarea.markdown-area') - .on('input', function () { - markdownPreview.hideReferencedUsers($form); - }) - .on('blur', function () { - markdownPreview.showPreview($form); - }); + $form.find('textarea.markdown-area').on('input', function () { + markdownPreview.hideReferencedUsers($form); + }); }; $(document).on('markdown-preview:show', function (e, $form) { -- cgit v1.2.1 From dbf4d80c329c8c4f4d00412b795d0ec48c35e6e9 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 27 Dec 2016 16:30:51 -0600 Subject: move check for preview loading class to top of method --- app/assets/javascripts/preview_markdown.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index c06fdf0aa65..89f7e976934 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -20,12 +20,17 @@ MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.showPreview = function ($form) { + var mdText; var preview = $form.find('.js-md-preview'); - var mdText = $form.find('textarea.markdown-area').val(); + if (preview.hasClass('md-preview-loading')) { + return; + } + mdText = $form.find('textarea.markdown-area').val(); + if (mdText.trim().length === 0) { preview.text('Nothing to preview.'); this.hideReferencedUsers($form); - } else if (!preview.hasClass('md-preview-loading')) { + } else { preview.addClass('md-preview-loading').text('Loading...'); this.fetchMarkdownPreview(mdText, (function (response) { preview.removeClass('md-preview-loading').html(response.body); -- cgit v1.2.1 From cf74f0af544e96b0784266ee15e3a05c6f987569 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 28 Dec 2016 11:15:13 +0100 Subject: Fix grammar error in text about mentioned issues --- app/views/projects/merge_requests/widget/_open.html.haml | 2 +- spec/features/merge_requests/closes_issues_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index f4aa1609a1b..40fbac7025a 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -42,6 +42,6 @@ - if mr_issues_mentioned_but_not_closing.present? #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author - #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not closed. + #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not be closed. diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb index dc32c8f7373..c73065cdce1 100644 --- a/spec/features/merge_requests/closes_issues_spec.rb +++ b/spec/features/merge_requests/closes_issues_spec.rb @@ -41,7 +41,7 @@ feature 'Merge Request closing issues message', feature: true do let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not closed.") + expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.") end end @@ -49,7 +49,7 @@ feature 'Merge Request closing issues message', feature: true do let(:merge_request_description) { "Description\n\ncloses #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not closed.") + expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.") end end end -- cgit v1.2.1 From c5757e9afb29f094a37e3ac19f22db4b90fc7670 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 28 Dec 2016 10:19:24 +0000 Subject: Fix GFM dropdown not showing at beginning of new lines Closes #26145 --- app/assets/javascripts/gfm_auto_complete.js.es6 | 2 +- changelogs/unreleased/gfm-new-line-fix.yml | 4 ++++ spec/features/issues/gfm_autocomplete_spec.rb | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/gfm-new-line-fix.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 3857bbb743b..87c579ac757 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -77,7 +77,7 @@ var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar; atSymbolsWithBar = Object.keys(this.app.controllers).join('|'); atSymbolsWithoutBar = Object.keys(this.app.controllers).join(''); - subtext = subtext.split(' ').pop(); + subtext = subtext.split(/\s+/g).pop(); flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); _a = decodeURI("%C3%80"); diff --git a/changelogs/unreleased/gfm-new-line-fix.yml b/changelogs/unreleased/gfm-new-line-fix.yml new file mode 100644 index 00000000000..751bb356b5f --- /dev/null +++ b/changelogs/unreleased/gfm-new-line-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed GFM dropdown not showing on new lines +merge_request: +author: diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 3489331a1b6..82c9bd0e6e6 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -47,6 +47,18 @@ feature 'GFM autocomplete', feature: true, js: true do expect_to_wrap(true, label_item, note, label.title) end + it "shows dropdown after a new line" do + note = find('#note_note') + page.within '.timeline-content-form' do + note.native.send_keys('test') + note.native.send_keys(:enter) + note.native.send_keys(:enter) + note.native.send_keys('@') + end + + expect(page).to have_selector('.atwho-container') + end + it "does not show dropdown when preceded with a special character" do note = find('#note_note') page.within '.timeline-content-form' do -- cgit v1.2.1 From 47689c16263586f09e42c83ef7a2445e6f5b50d4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 28 Dec 2016 11:20:27 +0100 Subject: Add changelog for typo fix in merge request widget --- changelogs/unreleased/fix-mentioned-issue-text-grammar.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-mentioned-issue-text-grammar.yml diff --git a/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml b/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml new file mode 100644 index 00000000000..1d001e6b568 --- /dev/null +++ b/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml @@ -0,0 +1,4 @@ +--- +title: Fix a minor grammar error in merge request widget +merge_request: 8337 +author: -- cgit v1.2.1 From 7422ea7a3e2ff8b3f15eeceffbb8232061233c2b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 28 Dec 2016 11:22:21 +0000 Subject: Set SIDEKIQ_LOG_ARGUMENTS in gitlab.rb --- doc/development/sidekiq_debugging.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md index cea11e5f126..d6d770e27c1 100644 --- a/doc/development/sidekiq_debugging.md +++ b/doc/development/sidekiq_debugging.md @@ -3,12 +3,15 @@ ## Log arguments to Sidekiq jobs If you want to see what arguments are being passed to Sidekiq jobs you can set -the SIDEKIQ_LOG_ARGUMENTS environment variable. +the `SIDEKIQ_LOG_ARGUMENTS` [environment variable] +(https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true). + +Example: ``` -SIDEKIQ_LOG_ARGUMENTS=1 bundle exec foreman start +gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "1"} ``` -It is not recommend to enable this setting in production because some Sidekiq -jobs (such as sending a password reset email) take secret arguments (for -example the password reset token). +Please note: It is not recommend to enable this setting in production because some +Sidekiq jobs (such as sending a password reset email) take secret arguments (for +example the password reset token). \ No newline at end of file -- cgit v1.2.1 From fac9efd216af36369b22ac3768b85c1de683bfb5 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 28 Dec 2016 11:27:51 +0000 Subject: Remove "up-for-grabs" label from PROCESS.md --- PROCESS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PROCESS.md b/PROCESS.md index 8af660fbdd1..cbeb781cd3c 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -69,7 +69,8 @@ to add details to the issue. - ~UX needs help from a UX designer - ~Frontend needs help from a Front-end engineer. Please follow the ["Implement design & UI elements" guidelines]. -- ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. +- ~"Accepting Merge Requests" is a low priority, well-defined issue that we + encourage people to contribute to. Not exclusive with other labels. - ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote in support or comment for further detail. Do not use `feature request`. - ~bug is an issue reporting undesirable or incorrect behavior. -- cgit v1.2.1 From 8aba0b0ed0ba93c59296f707f8c9a7d8ab1b498f Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 28 Dec 2016 12:31:37 +0100 Subject: Update CHANGELOG.md for 8.15.2 [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b2f041ab5..1baf5b3257c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ entry. ## 8.15.2 (2016-12-27) +- Fix finding the latest pipeline. !8301 - Fix mr list timestamp alignment. !8271 - Fix discussion overlap text in regular screens. !8273 - Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282 -- cgit v1.2.1 From 2ac92662ea1577f4a71751ded4dafbd90d74bd2b Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 28 Dec 2016 12:40:39 +0100 Subject: Parameter already enforced via grape --- lib/api/notes.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/api/notes.rb b/lib/api/notes.rb index d0faf17714b..284e4cf549a 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -69,8 +69,6 @@ module API optional :created_at, type: String, desc: 'The creation date of the note' end post ":id/#{noteables_str}/:noteable_id/notes" do - required_attributes! [:body] - opts = { note: params[:body], noteable_type: noteables_str.classify, -- cgit v1.2.1 From 7f1d954873263b5406bdd25afa02b06013cb70e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 28 Dec 2016 13:43:08 +0200 Subject: Fix 500 error when visit group from admin area if group name contains dot Signed-off-by: Dmitriy Zaporozhets --- changelogs/unreleased/dz-improve-admin-group-routing.yml | 4 ++++ config/routes/admin.rb | 2 +- spec/routing/admin_routing_spec.rb | 10 ++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/dz-improve-admin-group-routing.yml diff --git a/changelogs/unreleased/dz-improve-admin-group-routing.yml b/changelogs/unreleased/dz-improve-admin-group-routing.yml new file mode 100644 index 00000000000..2360b965c90 --- /dev/null +++ b/changelogs/unreleased/dz-improve-admin-group-routing.yml @@ -0,0 +1,4 @@ +--- +title: Fix 500 error when visit group from admin area if group name contains dot +merge_request: +author: diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 0dd2c8f7aef..23295b43c92 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -32,7 +32,7 @@ namespace :admin do scope(path: 'groups/*id', controller: :groups, - constraints: { id: Gitlab::Regex.namespace_route_regex }) do + constraints: { id: Gitlab::Regex.namespace_route_regex, format: /(html|json|atom)/ }) do scope(as: :group) do put :members_update diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 661b671301e..99c44bde151 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -122,12 +122,18 @@ describe Admin::HealthCheckController, "routing" do end describe Admin::GroupsController, "routing" do + let(:name) { 'complex.group-namegit' } + it "to #index" do expect(get("/admin/groups")).to route_to('admin/groups#index') end it "to #show" do - expect(get("/admin/groups/gitlab")).to route_to('admin/groups#show', id: 'gitlab') - expect(get("/admin/groups/gitlab/subgroup")).to route_to('admin/groups#show', id: 'gitlab/subgroup') + expect(get("/admin/groups/#{name}")).to route_to('admin/groups#show', id: name) + expect(get("/admin/groups/#{name}/subgroup")).to route_to('admin/groups#show', id: "#{name}/subgroup") + end + + it "to #edit" do + expect(get("/admin/groups/#{name}/edit")).to route_to('admin/groups#edit', id: name) end end -- cgit v1.2.1 From 38dd9b545cda01df61c055dc18698a38a84174d9 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Wed, 28 Dec 2016 23:22:31 +1000 Subject: use MergeRequest#closes_issues instead of rewriting code to retrieve closing issues on MergeRequest#issues_mentioned_but_not_closing --- app/models/merge_request.rb | 6 +----- spec/models/merge_request_spec.rb | 10 +++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 22490d121c7..61845bf4036 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -576,11 +576,7 @@ class MergeRequest < ActiveRecord::Base ext = Gitlab::ReferenceExtractor.new(project, current_user) ext.analyze(description) - issues = ext.issues - closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user). - closed_by_message(description) - - issues - closing_issues + ext.issues - closes_issues end def target_project_path diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 5da00a8636a..646e6c6dbb3 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -283,12 +283,16 @@ describe MergeRequest, models: true do end describe '#issues_mentioned_but_not_closing' do - it 'detects issues mentioned in description but not closed' do - mentioned_issue = create(:issue, project: subject.project) + let(:closing_issue) { create :issue, project: subject.project } + let(:mentioned_issue) { create :issue, project: subject.project } + + let(:commit) { double('commit', safe_message: "Fixes #{closing_issue.to_reference}") } + it 'detects issues mentioned in description but not closed' do subject.project.team << [subject.author, :developer] - subject.description = "Is related to #{mentioned_issue.to_reference}" + subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}" + allow(subject).to receive(:commits).and_return([commit]) allow(subject.project).to receive(:default_branch). and_return(subject.target_branch) -- cgit v1.2.1 From 684536321c9c847a8a39d0adc95e0c17be355c72 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 28 Dec 2016 13:10:34 +0200 Subject: Fix: Admin group show page does not work --- app/controllers/admin/groups_controller.rb | 2 +- spec/features/admin/admin_groups_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 61a3a03182a..add1c819adf 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -9,7 +9,7 @@ class Admin::GroupsController < Admin::ApplicationController end def show - @group = Group.with_statistics.find_by_full_path(params[:id]) + @group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id]) @members = @group.members.order("access_level DESC").page(params[:members_page]) @requesters = AccessRequestsFinder.new(@group).execute(current_user) @projects = @group.projects.with_statistics.page(params[:projects_page]) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 0aa01fc499a..9c19db6b420 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -17,6 +17,16 @@ feature 'Admin Groups', feature: true do end end + describe 'show a group' do + scenario 'shows the group' do + group = create(:group, :private) + + visit admin_group_path(group) + + expect(page).to have_content("Group: #{group.name}") + end + end + describe 'group edit' do scenario 'shows the visibility level radio populated with the group visibility_level value' do group = create(:group, :private) -- cgit v1.2.1 From f0ba001877e074eb8046f476c9972a47fe108f1b Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 28 Dec 2016 14:41:30 +0100 Subject: Cache project authorizations even when user has access to zero projects --- .../users/refresh_authorized_projects_service.rb | 2 +- .../26126-cache-even-when-no-projects.yml | 4 +++ .../refresh_authorized_projects_service_spec.rb | 37 ++++++++++++++++++---- 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/26126-cache-even-when-no-projects.yml diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb index 7d38ac3a374..8559908e0c3 100644 --- a/app/services/users/refresh_authorized_projects_service.rb +++ b/app/services/users/refresh_authorized_projects_service.rb @@ -74,7 +74,7 @@ module Users # remove - The IDs of the authorization rows to remove. # add - Rows to insert in the form `[user id, project id, access level]` def update_authorizations(remove = [], add = []) - return if remove.empty? && add.empty? + return if remove.empty? && add.empty? && user.authorized_projects_populated User.transaction do user.remove_project_authorizations(remove) unless remove.empty? diff --git a/changelogs/unreleased/26126-cache-even-when-no-projects.yml b/changelogs/unreleased/26126-cache-even-when-no-projects.yml new file mode 100644 index 00000000000..53e14ac9edf --- /dev/null +++ b/changelogs/unreleased/26126-cache-even-when-no-projects.yml @@ -0,0 +1,4 @@ +--- +title: Cache project authorizations even when user has access to zero projects +merge_request: 8327 +author: diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index 72c8f7cd8ec..1f6919151de 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -54,12 +54,37 @@ describe Users::RefreshAuthorizedProjectsService do end describe '#update_authorizations' do - it 'does nothing when there are no rows to add and remove' do - expect(user).not_to receive(:remove_project_authorizations) - expect(ProjectAuthorization).not_to receive(:insert_authorizations) - expect(user).not_to receive(:set_authorized_projects_column) + context 'when there are no rows to add and remove' do + it 'does not change authorizations' do + expect(user).not_to receive(:remove_project_authorizations) + expect(ProjectAuthorization).not_to receive(:insert_authorizations) - service.update_authorizations([], []) + service.update_authorizations([], []) + end + + context 'when the authorized projects column is not set' do + before do + user.update!(authorized_projects_populated: nil) + end + + it 'populates the authorized projects column' do + service.update_authorizations([], []) + + expect(user.authorized_projects_populated).to eq true + end + end + + context 'when the authorized projects column is set' do + before do + user.update!(authorized_projects_populated: true) + end + + it 'does nothing' do + expect(user).not_to receive(:set_authorized_projects_column) + + service.update_authorizations([], []) + end + end end it 'removes authorizations that should be removed' do @@ -84,7 +109,7 @@ describe Users::RefreshAuthorizedProjectsService do it 'populates the authorized projects column' do # make sure we start with a nil value no matter what the default in the # factory may be. - user.update(authorized_projects_populated: nil) + user.update!(authorized_projects_populated: nil) service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]]) -- cgit v1.2.1 From a051d32645a22e0da5834e3df6c281b51f63701c Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 08:05:17 -0600 Subject: fix left border in session tabs --- app/assets/stylesheets/pages/login.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index dd27a06fcd2..712bd3da22f 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -105,19 +105,19 @@ li { flex: 1; text-align: center; + border-left: 1px solid $border-color; &:first-of-type { + border-left: none; border-top-left-radius: $border-radius-default; } &:last-of-type { - border-left: 1px solid $border-color; border-top-right-radius: $border-radius-default; } &:not(.active) { background-color: $gray-light; - border-left: 1px solid $border-color; } a { -- cgit v1.2.1 From 7510d97f617ff45916c9fdace998f0e5a18f65f5 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 08:13:29 -0600 Subject: add CHANGELOG.md entry for !8346 --- changelogs/unreleased/24941-login-tabs-border.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/24941-login-tabs-border.yml diff --git a/changelogs/unreleased/24941-login-tabs-border.yml b/changelogs/unreleased/24941-login-tabs-border.yml new file mode 100644 index 00000000000..b06c21ad71a --- /dev/null +++ b/changelogs/unreleased/24941-login-tabs-border.yml @@ -0,0 +1,4 @@ +--- +title: fix border in login session tabs +merge_request: 8346 +author: -- cgit v1.2.1 From 1b647486570d61d03eebdf30651136a8d53d6e40 Mon Sep 17 00:00:00 2001 From: Gabriel Gizotti Date: Thu, 29 Dec 2016 00:39:53 +1000 Subject: add changelog entry --- changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml diff --git a/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml b/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml new file mode 100644 index 00000000000..ba3b13bcdb7 --- /dev/null +++ b/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml @@ -0,0 +1,4 @@ +--- +title: Fix unclear closing issue behaviour on Merge Request show page +merge_request: 8345 +author: Gabriel Gizotti -- cgit v1.2.1 From 822534c4c4c05cc53606fc45ca002d4a3d218256 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 21 Dec 2016 10:29:47 -0500 Subject: Replace link to Resolve conflicts with buttons Use variables for display logic conditional checks --- .../widget/open/_conflicts.html.haml | 30 ++++++++++++---------- ...-resolve-these-conflicts-link-is-too-subtle.yml | 4 +++ 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml diff --git a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml index af3096f04d9..c98b2c42597 100644 --- a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml +++ b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml @@ -1,21 +1,23 @@ +- can_resolve = @merge_request.conflicts_can_be_resolved_by?(current_user) +- can_resolve_in_ui = @merge_request.conflicts_can_be_resolved_in_ui? +- can_merge = @merge_request.can_be_merged_via_command_line_by?(current_user) + %h4.has-conflicts = icon("exclamation-triangle") This merge request contains merge conflicts %p - Please - - if @merge_request.conflicts_can_be_resolved_by?(current_user) - - if @merge_request.conflicts_can_be_resolved_in_ui? - = link_to "resolve these conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) - - else - %span.has-tooltip{title: "These conflicts cannot be resolved through GitLab"} - resolve these conflicts locally - - else - resolve these conflicts - + To merge this request, resolve these conflicts + - if can_resolve && !can_resolve_in_ui + locally or + - unless can_merge + ask someone with write access to this repository to + merge it locally. - - if @merge_request.can_be_merged_via_command_line_by?(current_user) - #{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}. - - else - ask someone with write access to this repository to merge this request manually. +- if (can_resolve && can_resolve_in_ui) || can_merge + .btn-group + - if can_resolve && can_resolve_in_ui + = link_to "Resolve conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn" + - if can_merge + = link_to "Merge locally", "#modal_merge_info", class: "btn how_to_merge_link vlink", "data-toggle" => "modal" diff --git a/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml b/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml new file mode 100644 index 00000000000..574c322803c --- /dev/null +++ b/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml @@ -0,0 +1,4 @@ +--- +title: Improve visibility of "Resolve conflicts" and "Merge locally" actions +merge_request: 8229 +author: -- cgit v1.2.1 From 56465687511e19f5b7d7fe91f2bb4e2ae51de390 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 21 Dec 2016 17:41:46 -0200 Subject: Revert MattermostNotificationService and SlackNotificationService to MattermostService and SlackService --- ...e_slack_and_mattermost_notification_services.rb | 25 ++++++++++++++++++++++ db/schema.rb | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb diff --git a/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb b/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb new file mode 100644 index 00000000000..50ad7437227 --- /dev/null +++ b/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb @@ -0,0 +1,25 @@ +class RenameSlackAndMattermostNotificationServices < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + update_column_in_batches(:services, :type, 'SlackService') do |table, query| + query.where(table[:type].eq('SlackNotificationService')) + end + + update_column_in_batches(:services, :type, 'MattermostService') do |table, query| + query.where(table[:type].eq('MattermostNotificationService')) + end + end + + def down + update_column_in_batches(:services, :type, 'SlackNotificationService') do |table, query| + query.where(table[:type].eq('SlackService')) + end + + update_column_in_batches(:services, :type, 'MattermostNotificationService') do |table, query| + query.where(table[:type].eq('MattermostService')) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index e1e72bdae8f..923ece86edb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161226122833) do +ActiveRecord::Schema.define(version: 20161227192806) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 36d6af4307c0863a43fbdfd291d6b8cd49169ecd Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 22 Dec 2016 17:18:01 -0600 Subject: allow header buttons to take more than 50% of the screen width --- app/views/projects/branches/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 5fd664c7a93..0a84793cd12 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -3,7 +3,7 @@ = render "projects/commits/head" %div{ class: container_class } - .top-area + .top-area.adjust .nav-text Protected branches can be managed in project settings -- cgit v1.2.1 From da44d84da07fbacb6f2e27dbf619860f0010f711 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 12 Dec 2016 12:30:55 -0600 Subject: catch instances where hashchange event is not fired yet we still want to adjust scroll position --- app/assets/javascripts/application.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 043c6a11c4f..302f108ce17 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -76,6 +76,14 @@ gl.utils.handleLocationHash(); }, false); + // `hashchange` is not triggered when link target is already in window.location + $('body').on('click', 'a', function() { + var href = this.getAttribute('href'); + if (href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { + setTimeout(gl.utils.handleLocationHash, 1); + } + }); + $(function () { var $body = $('body'); var $document = $(document); -- cgit v1.2.1 From 682b71eae692400b08ed73c76119eb0b6a462d17 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 09:54:51 -0600 Subject: move body click binding inside the onready callback --- app/assets/javascripts/application.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 302f108ce17..7024691f9f7 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -76,14 +76,6 @@ gl.utils.handleLocationHash(); }, false); - // `hashchange` is not triggered when link target is already in window.location - $('body').on('click', 'a', function() { - var href = this.getAttribute('href'); - if (href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { - setTimeout(gl.utils.handleLocationHash, 1); - } - }); - $(function () { var $body = $('body'); var $document = $(document); @@ -97,6 +89,14 @@ // Set the default path for all cookies to GitLab's root directory Cookies.defaults.path = gon.relative_url_root || '/'; + // `hashchange` is not triggered when link target is already in window.location + $body.on('click', 'a', function() { + var href = this.getAttribute('href'); + if (href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { + setTimeout(gl.utils.handleLocationHash, 1); + } + }); + // prevent default action for disabled buttons $('.btn').click(function(e) { if ($(this).hasClass('disabled')) { -- cgit v1.2.1 From 4704a59768dd34c588c50f83206f4f9a1a1dc71f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 09:59:35 -0600 Subject: add CHANGELOG.md entry for !8046 --- ...76-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml diff --git a/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml b/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml new file mode 100644 index 00000000000..c31c89dc4bc --- /dev/null +++ b/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml @@ -0,0 +1,4 @@ +--- +title: ensure permalinks scroll to correct position on multiple clicks +merge_request: 8046 +author: -- cgit v1.2.1 From 33251af3ab22f7df9d0f811f9f7c1f1a6577784b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 27 Dec 2016 21:02:17 -0500 Subject: Defer tooltip update on Resolve Comment button after DOM cycle --- .../javascripts/diff_notes/components/resolve_btn.js.es6 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 index 88a19fc6e1d..5852b8bbdb7 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 @@ -59,9 +59,11 @@ }, methods: { updateTooltip: function () { - $(this.$refs.button) - .tooltip('hide') - .tooltip('fixTitle'); + this.$nextTick(() => { + $(this.$refs.button) + .tooltip('hide') + .tooltip('fixTitle'); + }); }, resolve: function () { if (!this.canResolve) return; @@ -90,7 +92,7 @@ new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert'); } - this.$nextTick(this.updateTooltip); + this.updateTooltip(); }); } }, -- cgit v1.2.1 From 5fe5f33d6ba40ef4f934d60575910f6999534042 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 11:28:52 -0600 Subject: don't take for granted that all anchor tags have an href attribute --- app/assets/javascripts/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7024691f9f7..13d5c6e1e0b 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -92,7 +92,7 @@ // `hashchange` is not triggered when link target is already in window.location $body.on('click', 'a', function() { var href = this.getAttribute('href'); - if (href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { + if (href && href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { setTimeout(gl.utils.handleLocationHash, 1); } }); -- cgit v1.2.1 From 49d288499c554cc0fe3f8a957dee3bb7ea7368f7 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 11:55:22 -0600 Subject: limit body click selector to tags with a hash target --- app/assets/javascripts/application.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 13d5c6e1e0b..e43afbb4cc9 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -90,9 +90,9 @@ Cookies.defaults.path = gon.relative_url_root || '/'; // `hashchange` is not triggered when link target is already in window.location - $body.on('click', 'a', function() { + $body.on('click', 'a[href^="#"]', function() { var href = this.getAttribute('href'); - if (href && href.indexOf('#') === 0 && href.substr(1) === gl.utils.getLocationHash()) { + if (href.substr(1) === gl.utils.getLocationHash()) { setTimeout(gl.utils.handleLocationHash, 1); } }); -- cgit v1.2.1 From 4052869a72a4ca3fc1e0f94a5a57b187459ba94b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 28 Dec 2016 12:03:57 -0600 Subject: Refactor line length css --- app/assets/stylesheets/pages/issuable.scss | 49 ------------------------ app/assets/stylesheets/pages/merge_requests.scss | 4 ++ 2 files changed, 4 insertions(+), 49 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9478b66f536..6b4d1f85564 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -1,52 +1,3 @@ -// Limit MR description for side-by-side diff view -.container-limited { - &.limit-container-width { - .detail-page-header { - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - - .issuable-details { - .detail-page-description, - .mr-source-target, - .mr-state-widget, - .merge-manually { - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - - .merge-request-tabs-holder { - &.affix { - border-bottom: 1px solid $border-color; - - .nav-links { - border: 0; - } - } - - .container-fluid { - padding-left: 0; - padding-right: 0; - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - } - } - - .diffs { - .mr-version-controls, - .files-changed { - max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2)); - margin-left: auto; - margin-right: auto; - } - } - } -} - .issuable-details { section { .issuable-discussion { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 41b1b47713d..4cb53ab6fce 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -424,6 +424,10 @@ .merge-request-tabs-holder { background-color: $white-light; + .container-limited { + max-width: $limited-layout-width; + } + &.affix { top: 100px; left: 0; -- cgit v1.2.1 From bff6ca63cf100f6194090f39a2590f3ede6d6fa7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 21 Dec 2016 18:18:47 -0500 Subject: Set default sort for tags to Last updated Fix spacing between nav control elements Wrap navigation text on Tags list page --- app/assets/stylesheets/framework/lists.scss | 1 + app/assets/stylesheets/framework/nav.scss | 75 +++++++++++++++++------------ app/controllers/projects/tags_controller.rb | 2 +- app/views/projects/tags/index.html.haml | 10 ++-- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index b34f3bf6abc..277d4202950 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -199,6 +199,7 @@ ul.content-list { display: -webkit-flex; display: -ms-flexbox; display: flex; + align-items: center; white-space: nowrap; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 4f84bc37f90..a87e4f2f299 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -116,8 +116,8 @@ padding-top: 16px; padding-bottom: 11px; display: inline-block; - width: 50%; line-height: 28px; + white-space: normal; /* Small devices (phones, tablets, 768px and lower) */ @media (max-width: $screen-xs-max) { @@ -158,7 +158,6 @@ } .nav-controls { - width: 50%; display: inline-block; float: right; text-align: right; @@ -167,7 +166,9 @@ > .btn, > .btn-container, - > .dropdown { + > .dropdown, + > input, + > form { margin-right: $gl-padding-top; display: inline-block; vertical-align: top; @@ -182,19 +183,21 @@ float: none; } - > form { - display: inline-block; - } - .icon-label { display: none; } - input { + .btn, + .dropdown, + .dropdown-toggle, + input, + form { height: 35px; + } + + input { display: inline-block; position: relative; - margin-right: $gl-padding-top; /* Medium devices (desktops, 992px and up) */ @media (min-width: $screen-md-min) { width: 200px; } @@ -218,6 +221,7 @@ .btn, form, .dropdown, + .dropdown-toggle, .dropdown-menu-toggle, .form-control { margin: 0 0 10px; @@ -268,6 +272,10 @@ padding: 17px 0; } } + + pre { + width: 100%; + } } .layout-nav { @@ -423,33 +431,38 @@ } @media (max-width: $screen-xs-max) { - .top-area .nav-controls { - $controls-margin: $btn-xs-side-margin - 2px; + .top-area { + flex-flow: row wrap; - &.controls-flex { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: center; - padding: 0 0 $gl-padding-top; - } + .nav-controls { + $controls-margin: $btn-xs-side-margin - 2px; + flex: 0 0 100%; + + &.controls-flex { + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: center; + padding: 0 0 $gl-padding-top; + } - .controls-item, - .controls-item:last-child { - flex: 1 1 35%; - display: block; - width: 100%; - margin: $controls-margin; + .controls-item, + .controls-item-full, + .controls-item:last-child { + flex: 1 1 35%; + display: block; + width: 100%; + margin: $controls-margin; - .btn, - .dropdown { - margin: 0; + .btn, + .dropdown { + margin: 0; + } } - } - .controls-item-full { - @extend .controls-item; - flex: 1 1 100%; + .controls-item-full { + flex: 1 1 100%; + } } } } diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 953091492ae..e2d9d5ed460 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -8,7 +8,7 @@ class Projects::TagsController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:destroy] def index - params[:sort] = params[:sort].presence || 'name' + params[:sort] = params[:sort].presence || sort_value_recently_updated @sort = params[:sort] @tags = TagsFinder.new(@repository, params).execute diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index d52162a5de8..92a023a5c85 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -2,17 +2,17 @@ - page_title "Tags" = render "projects/commits/head" -%div{ class: container_class } - .top-area - .nav-text +%div.flex-list{ class: container_class } + .top-area.flex-row + .nav-text.row-main-content Tags give the ability to mark specific points in history as being important - .nav-controls + .nav-controls.row-fixed-content = form_tag(filter_tags_path, method: :get) do = search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } .dropdown - %button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} } + %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} } %span.light = projects_sort_options_hash[@sort] = icon('chevron-down') -- cgit v1.2.1 From ba2bbd4bf8011d31dd79355115f4b23330157c0e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 22 Dec 2016 17:22:27 -0600 Subject: fix mobile view for branch index header --- app/assets/stylesheets/framework/nav.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 4f84bc37f90..e8314872822 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -256,6 +256,10 @@ .nav-text, .nav-controls { width: auto; + + @media (max-width: $screen-xs-max) { + width: 100%; + } } } -- cgit v1.2.1 From 5a91daec8951aed64de1ecce48171c0e51420351 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 22 Dec 2016 17:30:49 -0600 Subject: add CHANGELOG.md entry for !8074 --- ...stacking-on-top-of-each-other-depending-on-the-selected-filter.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml diff --git a/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml b/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml new file mode 100644 index 00000000000..fac7697ede1 --- /dev/null +++ b/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml @@ -0,0 +1,4 @@ +--- +title: fix button layout issue on branches page +merge_request: 8074 +author: -- cgit v1.2.1 From b69dec2a10299f1250d25abf4e5e5940bdb97c55 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Dec 2016 14:33:42 -0600 Subject: change chevron position for branch sort order dropdown menu --- app/views/projects/branches/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 0a84793cd12..c2a5e725efc 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -12,7 +12,7 @@ = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } .dropdown.inline - %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} %span.light = projects_sort_options_hash[@sort] = icon('chevron-down') -- cgit v1.2.1 From 4815314b5c239134f5af44459888f63813ae7775 Mon Sep 17 00:00:00 2001 From: victorwu Date: Tue, 27 Dec 2016 00:25:04 -0500 Subject: About GitLab link in sidebar that links to help page --- app/assets/stylesheets/framework/gitlab-theme.scss | 3 +++ app/assets/stylesheets/framework/sidebar.scss | 11 +++++++++++ app/views/layouts/nav/_dashboard.html.haml | 4 ++++ changelogs/unreleased/view-ce-vs-ee.yml | 4 ++++ 4 files changed, 22 insertions(+) create mode 100644 changelogs/unreleased/view-ce-vs-ee.yml diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 1b52a6a8e71..d6566dc4ec9 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -80,6 +80,9 @@ } } + .about-gitlab { + color: $color-light; + } } } } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 46a06cd7eab..a8641e83154 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -101,6 +101,17 @@ padding: 0 8px; border-radius: 6px; } + + .about-gitlab { + padding: 7px $gl-sidebar-padding; + font-size: $gl-font-size; + line-height: 24px; + display: block; + text-decoration: none; + font-weight: normal; + position: absolute; + bottom: 10px; + } } .sidebar-action-buttons { diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 817e4bebb05..205d23178d2 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -35,3 +35,7 @@ = link_to dashboard_snippets_path, title: 'Snippets' do %span Snippets + + = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do + %span + About GitLab CE diff --git a/changelogs/unreleased/view-ce-vs-ee.yml b/changelogs/unreleased/view-ce-vs-ee.yml new file mode 100644 index 00000000000..38bce4ac7c3 --- /dev/null +++ b/changelogs/unreleased/view-ce-vs-ee.yml @@ -0,0 +1,4 @@ +--- +title: About GitLab link in sidebar that links to help page +merge_request: 8316 +author: -- cgit v1.2.1 From 72e94991e5199a1230b55591e9e872a93f4fd975 Mon Sep 17 00:00:00 2001 From: dimitrieh Date: Wed, 28 Dec 2016 23:13:23 +0100 Subject: Move award emoji's out of the discussion tab for merge requests --- app/views/projects/merge_requests/_show.html.haml | 6 +++--- .../25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index d1fa51ae7ee..393ab1149bc 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -47,6 +47,9 @@ = succeed '.' do = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" + .content-block.content-block-small + = render 'award_emoji/awards_block', awardable: @merge_request, inline: true + - if @commits_count.nonzero? .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } %div{ class: container_class } @@ -83,9 +86,6 @@ .tab-content#diff-notes-app #notes.notes.tab-pane.voting_notes - .content-block.content-block-small - = render 'award_emoji/awards_block', awardable: @merge_request, inline: true - .row %section.col-md-12 .issuable-discussion diff --git a/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml b/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml new file mode 100644 index 00000000000..e05e2dd6fed --- /dev/null +++ b/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml @@ -0,0 +1,4 @@ +--- +title: Move award emoji's out of the discussion tab for merge requests +merge_request: +author: -- cgit v1.2.1 From 9f39953eaf5568eb75bd2ecf1bab230bbf13f330 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Dec 2016 00:29:33 +0200 Subject: Improve Group#users_with_parents method Signed-off-by: Dmitriy Zaporozhets --- app/models/group.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/group.rb b/app/models/group.rb index 50c949d84aa..21c9047c98a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -199,6 +199,6 @@ class Group < Namespace end def users_with_parents - User.where(id: members_with_parents.pluck(:user_id)) + User.where(id: members_with_parents.select(:user_id)) end end -- cgit v1.2.1 From f7d6fbf641dd496c1748e8f9e56c968221435f71 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Dec 2016 11:06:22 +0200 Subject: Wait for ajax request to complete when testing milestont drang-n-drop feature Signed-off-by: Dmitriy Zaporozhets --- spec/features/milestones/milestones_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb index 8b603f51545..aadd72a9f8e 100644 --- a/spec/features/milestones/milestones_spec.rb +++ b/spec/features/milestones/milestones_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe 'Milestone draggable', feature: true, js: true do + include WaitForAjax + let(:milestone) { create(:milestone, project: project, title: 8.14) } let(:project) { create(:empty_project, :public) } let(:user) { create(:user) } @@ -74,6 +76,8 @@ describe 'Milestone draggable', feature: true, js: true do visit namespace_project_milestone_path(project.namespace, project, milestone) issue.drag_to(issue_target) + + wait_for_ajax end def create_and_drag_merge_request(params = {}) @@ -82,5 +86,7 @@ describe 'Milestone draggable', feature: true, js: true do visit namespace_project_milestone_path(project.namespace, project, milestone) page.find("a[href='#tab-merge-requests']").click merge_request.drag_to(merge_request_target) + + wait_for_ajax end end -- cgit v1.2.1 From ec7485de216695b53c26fb36fedba9f7d702acaa Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 13 Dec 2016 09:00:41 +0100 Subject: Grapify the settings API --- doc/api/settings.md | 1 + lib/api/settings.rb | 118 +++++++++++++++++++++++++++++++++---- spec/requests/api/settings_spec.rb | 3 +- 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/doc/api/settings.md b/doc/api/settings.md index 218546aafea..0bd38a6e664 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -79,6 +79,7 @@ PUT /application/settings | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. | | `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. | | `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. | +| `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources | ```bash curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1 diff --git a/lib/api/settings.rb b/lib/api/settings.rb index c4cb1c7924a..9eb9a105bde 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -9,23 +9,117 @@ module API end end - # Get current applicaiton settings - # - # Example Request: - # GET /application/settings + desc 'Get the current application settings' do + success Entities::ApplicationSetting + end get "application/settings" do present current_settings, with: Entities::ApplicationSetting end - # Modify application settings - # - # Example Request: - # PUT /application/settings + desc 'Modify application settings' do + success Entities::ApplicationSetting + end + params do + optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master' + optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility' + optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility' + optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility' + optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.' + optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project], + desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com' + optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources' + optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.' + optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled' + optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects' + optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB' + optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.' + optional :user_oauth_applications, type: Boolean, desc: 'Allow users to register any application to use GitLab as an OAuth provider' + optional :user_default_external, type: Boolean, desc: 'Newly registered users will by default be external' + optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled' + optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up' + optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com' + optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups' + given domain_blacklist_enabled: ->(val) { val } do + requires :domain_blacklist, type: String, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com' + end + optional :after_sign_up_text, type: String, desc: 'Text shown after sign up' + optional :signin_enabled, type: Boolean, desc: 'Flag indicating if sign in is enabled' + optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication' + given require_two_factor_authentication: ->(val) { val } do + requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication' + end + optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page' + optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out' + optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application' + optional :help_page_text, type: String, desc: 'Custom text displayed on the help page' + optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects' + given shared_runners_enabled: ->(val) { val } do + requires :shared_runners_text, type: String, desc: 'Shared runners text ' + end + optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have" + optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' + optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics' + given metrics_enabled: ->(val) { val } do + requires :metrics_host, type: String, desc: 'The InfluxDB host' + requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB' + requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open' + requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out' + requires :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.' + requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds' + requires :metrics_packet_size, type: Integer, desc: 'The amount of points to store in a single UDP packet' + end + optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling' + given sidekiq_throttling_enabled: ->(val) { val } do + requires :sidekiq_throttling_queus, type: Array[String], desc: 'Choose which queues you wish to throttle' + requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.' + end + optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts' + given recaptcha_enabled: ->(val) { val } do + requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha' + requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha' + end + optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues' + given akismet_enabled: ->(val) { val } do + requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com' + end + optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.' + optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com' + given sentry_enabled: ->(val) { val } do + requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name' + end + optional :repository_storage, type: String, desc: 'Storage paths for new projects' + optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues." + optional :koding_enabled, type: Boolean, desc: 'Enable Koding' + given koding_enabled: ->(val) { val } do + requires :koding_url, type: String, desc: 'The Koding team URL' + end + optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.' + optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.' + optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.' + optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)' + given housekeeping_enabled: ->(val) { val } do + requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance." + requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run." + requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run." + requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run." + end + at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility, + :default_group_visibility, :restricted_visibility_levels, :import_sources, + :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit, + :max_attachment_size, :session_expire_delay, :disabled_oauth_sign_in_sources, + :user_oauth_applications, :user_default_external, :signup_enabled, + :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled, + :after_sign_up_text, :signin_enabled, :require_two_factor_authentication, + :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text, + :shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay, + :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled, + :akismet_enabled, :admin_notification_email, :sentry_enabled, + :repository_storage, :repository_checks_enabled, :koding_enabled, + :version_check_enabled, :email_author_in_body, :html_emails_enabled, + :housekeeping_enabled + end put "application/settings" do - attributes = ["repository_storage"] + current_settings.attributes.keys - ["id"] - attrs = attributes_for_keys(attributes) - - if current_settings.update_attributes(attrs) + if current_settings.update_attributes(declared_params(include_missing: false)) present current_settings, with: Entities::ApplicationSetting else render_validation_error!(current_settings) diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 9a8d633d657..ad9d8a25af4 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -44,8 +44,7 @@ describe API::Settings, 'Settings', api: true do put api("/application/settings", admin), koding_enabled: true expect(response).to have_http_status(400) - expect(json_response['message']).to have_key('koding_url') - expect(json_response['message']['koding_url']).to include "can't be blank" + expect(json_response['error']).to eq('koding_url is missing') end end end -- cgit v1.2.1 From e74b8bd14873fc5a3586bc41b262f4965f14b43f Mon Sep 17 00:00:00 2001 From: Saad Shahd Date: Thu, 29 Dec 2016 02:16:42 +0200 Subject: CTRL+Enter does not submit a new merge request --- app/views/projects/merge_requests/_new_submit.html.haml | 2 +- .../26134-ctrl-enter-does-not-submit-a-new-merge-request.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 349181be784..34ead6427e0 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -10,7 +10,7 @@ %span.pull-right = link_to 'Change branches', mr_change_branches_path(@merge_request) %hr -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input js-quick-submit' } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request = f.hidden_field :source_project_id = f.hidden_field :source_branch diff --git a/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml b/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml new file mode 100644 index 00000000000..40183f8d3fa --- /dev/null +++ b/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml @@ -0,0 +1,4 @@ +--- +title: Make CTRL+Enter submits a new merge request +merge_request: 8360 +author: Saad Shahd -- cgit v1.2.1 From 42cf8b84db81d5e83f5ecce4cb1eebee8ee59e2c Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Thu, 29 Dec 2016 21:18:51 +0600 Subject: fixes too short input for placeholder message in commit listing page --- app/assets/stylesheets/pages/projects.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d6aa4c4c032..b99be02ab0c 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -627,6 +627,12 @@ pre.light-well { } } +.commits-search-form { + .input-short { + min-width: 200px; + } +} + .project-last-commit { @media (min-width: $screen-sm-min) { margin-top: $gl-padding; -- cgit v1.2.1 From 77ee5e0ed58649dab6c282b3529bbe70b9bfe229 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Thu, 29 Dec 2016 21:42:53 +0600 Subject: changelog file added --- changelogs/unreleased/26192-fixes-too-short-input.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/26192-fixes-too-short-input.yml diff --git a/changelogs/unreleased/26192-fixes-too-short-input.yml b/changelogs/unreleased/26192-fixes-too-short-input.yml new file mode 100644 index 00000000000..ff707f4694d --- /dev/null +++ b/changelogs/unreleased/26192-fixes-too-short-input.yml @@ -0,0 +1,4 @@ +--- +title: Fixes too short input for placeholder message in commit listing page +merge_request: 8367 +author: -- cgit v1.2.1 From 0321c86b922b07a5ad7f5ecee1f6e69a336579eb Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 29 Dec 2016 09:43:44 -0600 Subject: add CHANGELOG.md entry for !8332 --- .../25829-update-username-button-remains-disabled-upon-failure.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml diff --git a/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml b/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml new file mode 100644 index 00000000000..c82bacd8bcd --- /dev/null +++ b/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml @@ -0,0 +1,4 @@ +--- +title: re-enable change username button after failure +merge_request: 8332 +author: -- cgit v1.2.1 From 69ca159797980d933628e375e59be16bdfd9cd4c Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Thu, 29 Dec 2016 10:02:08 -0600 Subject: Fix MR versions spec --- spec/features/merge_requests/merge_request_versions_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/merge_request_versions_spec.rb index 09451f41de4..cd2272dd38f 100644 --- a/spec/features/merge_requests/merge_request_versions_spec.rb +++ b/spec/features/merge_requests/merge_request_versions_spec.rb @@ -24,7 +24,7 @@ feature 'Merge Request versions', js: true, feature: true do before do page.within '.mr-version-dropdown' do find('.btn-default').click - click_link 'version 1' + find(:link, 'version 1').trigger('click') end end @@ -45,7 +45,7 @@ feature 'Merge Request versions', js: true, feature: true do before do page.within '.mr-version-compare-dropdown' do find('.btn-default').click - click_link 'version 1' + find(:link, 'version 1').trigger('click') end end -- cgit v1.2.1 From 283e868ef523185b0ee314b9e2164599780d888b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Dec 2016 19:18:05 +0200 Subject: Refactor nested group related code * Simplify code around group parent access check * Rename 'Nested groups' to 'Subgroups' tab at group#show page Signed-off-by: Dmitriy Zaporozhets --- app/services/groups/create_service.rb | 14 ++++---------- app/views/groups/show.html.haml | 5 ++--- spec/features/groups_spec.rb | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 9a630aee626..febeb661fb5 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -12,17 +12,11 @@ module Groups return @group end - parent_id = params[:parent_id] + if @group.parent && !can?(current_user, :admin_group, @group.parent) + @group.parent = nil + @group.errors.add(:parent_id, 'manage access required to create subgroup') - if parent_id - parent = Group.find(parent_id) - - unless can?(current_user, :admin_group, parent) - @group.parent_id = nil - @group.errors.add(:parent_id, 'manage access required to create subgroup') - - return @group - end + return @group end @group.name ||= @group.path.dup diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index a3cd333373e..9ef88f233c5 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -35,7 +35,7 @@ - if @nested_groups.present? %li = link_to "#groups", 'data-toggle' => 'tab' do - Nested Groups + Subgroups .nav-controls = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false @@ -55,5 +55,4 @@ - if @nested_groups.present? .tab-pane#groups %ul.content-list - - @nested_groups.each do |group| - = render 'shared/groups/group', group: group + = render partial: 'shared/groups/group', collection: @nested_groups diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index d865a71f04b..a515c92db37 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -115,7 +115,7 @@ feature 'Group', feature: true do it 'has nested groups tab with nested groups inside' do visit path - click_link 'Nested Groups' + click_link 'Subgroups' expect(page).to have_content(nested_group.full_name) end -- cgit v1.2.1 From f9963fe029122c9d7e866368b1fc1218e071f9d6 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 21 Dec 2016 16:36:24 -0200 Subject: Parse JIRA issue references even if Issue Tracker is disabled --- app/helpers/projects_helper.rb | 53 +++++++--------------- app/policies/project_policy.rb | 13 ++++-- changelogs/unreleased/issue_25682.yml | 4 ++ spec/features/projects/features_visibility_spec.rb | 11 +++++ .../reference_parser/external_issue_parser_spec.rb | 12 ++++- 5 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 changelogs/unreleased/issue_25682.yml diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d2177f683a1..8a28b6c5d40 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -171,48 +171,27 @@ module ProjectsHelper nav_tabs << :merge_requests end - if can?(current_user, :read_pipeline, project) - nav_tabs << :pipelines - end - - if can?(current_user, :read_build, project) - nav_tabs << :builds - end - if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project) nav_tabs << :container_registry end - if can?(current_user, :read_environment, project) - nav_tabs << :environments - end - - if can?(current_user, :admin_project, project) - nav_tabs << :settings - end - - if can?(current_user, :read_project_member, project) - nav_tabs << :team - end - - if can?(current_user, :read_issue, project) - nav_tabs << :issues - end - - if can?(current_user, :read_wiki, project) - nav_tabs << :wiki - end - - if can?(current_user, :read_project_snippet, project) - nav_tabs << :snippets - end - - if can?(current_user, :read_label, project) - nav_tabs << :labels - end + tab_ability_map = { + environments: :read_environment, + milestones: :read_milestone, + pipelines: :read_pipeline, + snippets: :read_project_snippet, + settings: :admin_project, + builds: :read_build, + labels: :read_label, + issues: :read_issue, + team: :read_project_member, + wiki: :read_wiki + } - if can?(current_user, :read_milestone, project) - nav_tabs << :milestones + tab_ability_map.each do |tab, ability| + if can?(current_user, ability, project) + nav_tabs << tab + end end nav_tabs.flatten diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index b5db9c12622..e06818a0456 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -171,9 +171,7 @@ class ProjectPolicy < BasePolicy def disabled_features! repository_enabled = project.feature_available?(:repository, user) - unless project.feature_available?(:issues, user) - cannot!(*named_abilities(:issue)) - end + block_issues_abilities unless project.feature_available?(:merge_requests, user) && repository_enabled cannot!(*named_abilities(:merge_request)) @@ -250,6 +248,15 @@ class ProjectPolicy < BasePolicy ) end + def block_issues_abilities + unless project.feature_available?(:issues, user) + cannot! :read_issue if project.default_issues_tracker? + cannot! :create_issue + cannot! :update_issue + cannot! :admin_issue + end + end + def named_abilities(name) [ :"read_#{name}", diff --git a/changelogs/unreleased/issue_25682.yml b/changelogs/unreleased/issue_25682.yml new file mode 100644 index 00000000000..a50138756ba --- /dev/null +++ b/changelogs/unreleased/issue_25682.yml @@ -0,0 +1,4 @@ +--- +title: Parse JIRA issue references even if Issue Tracker is disabled +merge_request: +author: diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index 3bb33394be7..9079350186d 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -42,6 +42,17 @@ describe 'Edit Project Settings', feature: true do end end + context "When external issue tracker is enabled" do + it "does not hide issues tab" do + project.project_feature.update(issues_access_level: ProjectFeature::DISABLED) + allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(JiraService.new) + + visit namespace_project_path(project.namespace, project) + + expect(page).to have_selector(".shortcuts-issues") + end + end + context "pipelines subtabs" do it "shows builds when enabled" do visit namespace_project_pipelines_path(project.namespace, project) diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb index 50a5d1a19ba..0af36776a54 100644 --- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb @@ -12,7 +12,17 @@ describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do context 'when the link has a data-issue attribute' do before { link['data-external-issue'] = 123 } - it_behaves_like "referenced feature visibility", "issues" + levels = [ProjectFeature::DISABLED, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] + + levels.each do |level| + it "creates reference when the feature is #{level}" do + project.project_feature.update(issues_access_level: level) + + visible_nodes = subject.nodes_visible_to_user(user, [link]) + + expect(visible_nodes).to include(link) + end + end end end -- cgit v1.2.1 From e8a568abfbd0bf1099b0d5c9f3d576cdbdd7c91f Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Thu, 29 Dec 2016 15:04:26 -0600 Subject: Make tabs full-width at small viewport --- app/assets/stylesheets/pages/merge_requests.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 4cb53ab6fce..1b1126695a1 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -433,6 +433,10 @@ left: 0; z-index: 10; transition: right .15s; + + @media (max-width: $screen-xs-max) { + right: 0; + } } &:not(.affix) .container-fluid { -- cgit v1.2.1 From 091d21d539e634c998bf79d7281bbbeee911a5d0 Mon Sep 17 00:00:00 2001 From: victorwu Date: Thu, 29 Dec 2016 17:14:45 -0500 Subject: Fix typo: seach to search --- app/assets/javascripts/boards/boards_bundle.js.es6 | 2 +- app/views/shared/issuable/_filter.html.haml | 2 +- changelogs/unreleased/fix-boards-search-typo.yml | 4 ++++ spec/features/boards/boards_spec.rb | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/fix-boards-search-typo.yml diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index ab2343c72fc..2c9ab61c94d 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -73,7 +73,7 @@ $(() => { }); gl.IssueBoardsSearch = new Vue({ - el: '#js-boards-seach', + el: '#js-boards-search', data: { filters: Store.state.filters }, diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index e3503981afe..d6c67c523e2 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -35,7 +35,7 @@ .pull-right - if boards_page - #js-boards-seach.issue-boards-search + #js-boards-search.issue-boards-search %input.pull-left.form-control{ type: "search", placeholder: "Filter by name...", "v-model" => "filters.search", "debounce" => "250" } - if can?(current_user, :admin_list, @project) .dropdown.pull-right diff --git a/changelogs/unreleased/fix-boards-search-typo.yml b/changelogs/unreleased/fix-boards-search-typo.yml new file mode 100644 index 00000000000..0c083fc0d10 --- /dev/null +++ b/changelogs/unreleased/fix-boards-search-typo.yml @@ -0,0 +1,4 @@ +--- +title: 'Fix typo: seach to search' +merge_request: 8370 +author: diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 973d5b286e9..bfac5a1b8ab 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -109,7 +109,7 @@ describe 'Issue Boards', feature: true, js: true do end it 'search backlog list' do - page.within('#js-boards-seach') do + page.within('#js-boards-search') do find('.form-control').set(issue1.title) end @@ -122,7 +122,7 @@ describe 'Issue Boards', feature: true, js: true do end it 'search done list' do - page.within('#js-boards-seach') do + page.within('#js-boards-search') do find('.form-control').set(issue8.title) end @@ -135,7 +135,7 @@ describe 'Issue Boards', feature: true, js: true do end it 'search list' do - page.within('#js-boards-seach') do + page.within('#js-boards-search') do find('.form-control').set(issue5.title) end -- cgit v1.2.1 From b21024684b7c8f9fdef524873434e057de7a2713 Mon Sep 17 00:00:00 2001 From: James Gregory Date: Wed, 28 Dec 2016 22:22:57 +0000 Subject: Merged the 'groups' and 'projects' tabs when viewing user profiles --- app/assets/stylesheets/framework/common.scss | 1 + app/controllers/admin/users_controller.rb | 3 --- app/views/admin/users/_head.html.haml | 4 +--- app/views/admin/users/groups.html.haml | 20 -------------------- app/views/admin/users/projects.html.haml | 16 +++++++++++----- .../feature-admin-merge-groups-and-projects.yml | 4 ++++ config/routes/admin.rb | 1 - spec/features/admin/admin_users_spec.rb | 17 +++++++++++++++++ 8 files changed, 34 insertions(+), 32 deletions(-) delete mode 100644 app/views/admin/users/groups.html.haml create mode 100644 changelogs/unreleased/feature-admin-merge-groups-and-projects.yml diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 34757c57acf..67b5aa37ae7 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -26,6 +26,7 @@ .append-bottom-default { margin-bottom: $gl-padding; } .inline { display: inline-block; } .center { text-align: center; } +.vertical-align-middle { vertical-align: middle; } .underlined-link { text-decoration: underline; } .hint { font-style: italic; color: $hint-color; } diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index bb912ed10cc..df9039b16b2 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -16,9 +16,6 @@ class Admin::UsersController < Admin::ApplicationController @joined_projects = user.projects.joined(@user) end - def groups - end - def keys @keys = user.keys end diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index ce5e21e54cc..9984e733956 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -15,10 +15,8 @@ %ul.nav-links = nav_link(path: 'users#show') do = link_to "Account", admin_user_path(@user) - = nav_link(path: 'users#groups') do - = link_to "Groups", groups_admin_user_path(@user) = nav_link(path: 'users#projects') do - = link_to "Projects", projects_admin_user_path(@user) + = link_to "Groups and projects", projects_admin_user_path(@user) = nav_link(path: 'users#keys') do = link_to "SSH keys", keys_admin_user_path(@user) = nav_link(controller: :identities) do diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml deleted file mode 100644 index 8f6d13b881a..00000000000 --- a/app/views/admin/users/groups.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -- page_title "Groups", @user.name, "Users" -= render 'admin/users/head' - -- group_members = @user.group_members.includes(:source) -- if group_members.any? - .panel.panel-default - .panel-heading Groups: - %ul.well-list - - group_members.each do |group_member| - - group = group_member.group - %li.group_member - %span{class: ("list-item-name" unless group_member.owner?)} - %strong= link_to group.name, admin_group_path(group) - .pull-right - %span.light= group_member.human_access - - unless group_member.owner? - = link_to group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do - %i.fa.fa-times.fa-inverse -- else - .nothing-here-block This user has no groups. diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml index dd6b7303493..15eaf1c0e67 100644 --- a/app/views/admin/users/projects.html.haml +++ b/app/views/admin/users/projects.html.haml @@ -1,15 +1,21 @@ -- page_title "Projects", @user.name, "Users" +- page_title "Groups and projects", @user.name, "Users" = render 'admin/users/head' - if @user.groups.any? .panel.panel-default .panel-heading Group projects %ul.well-list - - @user.groups.each do |group| - %li + - @user.group_members.includes(:source).each do |group_member| + - group = group_member.group + %li.group_member %strong= link_to group.name, admin_group_path(group) – access to #{pluralize(group.projects.count, 'project')} + .pull-right + %span.light.vertical-align-middle= group_member.human_access + - unless group_member.owner? + = link_to group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from group' do + %i.fa.fa-times.fa-inverse .row .col-md-6 @@ -35,8 +41,8 @@ - if member.owner? %span.light Owner - else - %span.light= member.human_access + %span.light.vertical-align-middle= member.human_access - if member.respond_to? :project - = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do + = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from project' do %i.fa.fa-times diff --git a/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml b/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml new file mode 100644 index 00000000000..0c0b74b686a --- /dev/null +++ b/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml @@ -0,0 +1,4 @@ +--- +title: Merged the 'Groups' and 'Projects' tabs when viewing user profiles +merge_request: 8323 +author: James Gregory diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 23295b43c92..8e99239f350 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -6,7 +6,6 @@ namespace :admin do member do get :projects get :keys - get :groups put :block put :unblock put :unlock diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index e31325ce47b..55ffc6761f8 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Admin::Users", feature: true do + include WaitForAjax + before { login_as :admin } describe "GET /admin/users" do @@ -252,5 +254,20 @@ describe "Admin::Users", feature: true do end expect(page).to have_content @project.name end + + it 'shows the group access level' do + within(:css, '.append-bottom-default + .panel') do + expect(page).to have_content 'Developer' + end + end + + it 'allows group membership to be revoked', js: true do + page.within(first('.group_member')) do + find('.btn-remove').click + end + wait_for_ajax + + expect(page).not_to have_selector('.group_member') + end end end -- cgit v1.2.1 From 781cca8d4549ffb55f6b594c0c740f630d5531c7 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 29 Dec 2016 21:09:13 -0200 Subject: Fix redirect after update file when user has forked project --- app/controllers/concerns/creates_commit.rb | 2 +- changelogs/unreleased/issue_25578.yml | 4 +++ spec/controllers/projects/blob_controller_spec.rb | 32 ++++++++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/issue_25578.yml diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index 936d9bab57e..6f43ce5226d 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -82,7 +82,7 @@ module CreatesCommit return @merge_request if defined?(@merge_request) @merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened. - find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch) + find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch, source_project_id: @mr_source_project) end def different_project? diff --git a/changelogs/unreleased/issue_25578.yml b/changelogs/unreleased/issue_25578.yml new file mode 100644 index 00000000000..e10f1d232af --- /dev/null +++ b/changelogs/unreleased/issue_25578.yml @@ -0,0 +1,4 @@ +--- +title: Fix redirect after update file when user has forked project +merge_request: +author: diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 3efef757ae2..f35c5d992d9 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe Projects::BlobController do - let(:project) { create(:project) } + let(:project) { create(:project, :public) } let(:user) { create(:user) } before do @@ -84,5 +84,35 @@ describe Projects::BlobController do end end end + + context 'when user has forked project' do + let(:guest) { create(:user) } + let!(:forked_project) { Projects::ForkService.new(project, guest).execute } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: "fork-test-1", target_branch: "master") } + + before { sign_in(guest) } + + it "redirects to forked project new merge request" do + default_params[:target_branch] = "fork-test-1" + default_params[:create_merge_request] = 1 + + allow_any_instance_of(Files::UpdateService).to receive(:commit).and_return(:success) + + put :update, default_params + + expect(response).to redirect_to( + new_namespace_project_merge_request_path( + forked_project.namespace, + forked_project, + merge_request: { + source_project_id: forked_project.id, + target_project_id: project.id, + source_branch: "fork-test-1", + target_branch: "master" + } + ) + ) + end + end end end -- cgit v1.2.1 From d69b7bec9583fcbddaf563c44b2483408b459a07 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 30 Dec 2016 11:32:44 +0100 Subject: Remove time zone information from assertion in timeago tooltips spec This allows the test to pass when your local time zone is different than UTC. "Date Format" JavaScript library uses a custom, non-standard format when displaying time zone part of the datetime. There is no point in trying to replicate this behavior in Ruby. --- spec/features/dashboard/datetime_on_tooltips_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb index 44dfc2dff45..dc9d09fa396 100644 --- a/spec/features/dashboard/datetime_on_tooltips_spec.rb +++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb @@ -6,7 +6,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do let(:user) { create(:user) } let(:project) { create(:project, name: 'test', namespace: user.namespace) } let(:created_date) { Date.yesterday.to_time } - let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P UTC') } + let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P') } context 'on the activity tab' do before do -- cgit v1.2.1 From ff4d5fc1d0832922b7a39d4f617ee424e606e446 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 30 Dec 2016 16:44:16 +0100 Subject: Fix failures in default sort order spec Test cases were not waiting for page load after clicking. This caused failures in test cases that were executed right after these incorrect ones, because of requests hitting server after a test was finished. We do not have to click links, we can go directly to a correct page by passing parameters in the URL. --- spec/features/issuables/default_sort_order_spec.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/spec/features/issuables/default_sort_order_spec.rb b/spec/features/issuables/default_sort_order_spec.rb index 9a2b879e789..73553f97d6f 100644 --- a/spec/features/issuables/default_sort_order_spec.rb +++ b/spec/features/issuables/default_sort_order_spec.rb @@ -180,16 +180,10 @@ describe 'Projects > Issuables > Default sort order', feature: true do end def visit_merge_requests_with_state(project, state) - visit_merge_requests project - visit_issuables_with_state state + visit_merge_requests project, state: state end def visit_issues_with_state(project, state) - visit_issues project - visit_issuables_with_state state - end - - def visit_issuables_with_state(state) - within('.issues-state-filters') { find("span", text: state.titleize).click } + visit_issues project, state: state end end -- cgit v1.2.1 From 21777aa1ff9f4b54258d8091b3864a1a08f73cb9 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 15:00:00 +0530 Subject: HAMLLint: Enable `AltText` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index da9a43d9c6d..a8f724072e4 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -7,7 +7,7 @@ exclude: linters: AltText: - enabled: false + enabled: true ClassAttributeWithStaticValue: enabled: false -- cgit v1.2.1 From 71dc50f048ce5604e331f69cb50c4fa6b4a73bbb Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 15:00:56 +0530 Subject: HAMLLint: fix `AltText` offences --- app/views/notify/pipeline_failed_email.html.haml | 6 +++--- app/views/notify/pipeline_success_email.html.haml | 6 +++--- app/views/profiles/show.html.haml | 2 +- app/views/projects/blob/_image.html.haml | 4 ++-- app/views/projects/boards/components/_card.html.haml | 2 +- .../projects/boards/components/sidebar/_assignee.html.haml | 2 +- app/views/projects/diffs/_image.html.haml | 14 +++++++------- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index 001d9c48555..751f81db1cb 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -90,7 +90,7 @@ %tbody %tr %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13"}/ + %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"} = @pipeline.ref @@ -101,7 +101,7 @@ %tbody %tr %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/ + %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} = @pipeline.short_sha @@ -119,7 +119,7 @@ %tr - commit = @pipeline.commit %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24"}/ + %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - if commit.author %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"} diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index 56c1949ab2b..d64232ca8f2 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -90,7 +90,7 @@ %tbody %tr %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13"}/ + %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"} = @pipeline.ref @@ -101,7 +101,7 @@ %tbody %tr %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/ + %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} = @pipeline.short_sha @@ -119,7 +119,7 @@ %tr - commit = @pipeline.commit %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24"}/ + %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar"}/ %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - if commit.author %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"} diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 578af9fe98d..859273ae94a 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -108,7 +108,7 @@ Position and size your new avatar .modal-body .profile-crop-image-container - %img.modal-profile-crop-image + %img.modal-profile-crop-image{ alt: "Avatar cropper" } .crop-controls .btn-group %button.btn.btn-primary{ data: { method: "zoom", option: "0.1" } } diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml index 4c356d1f07f..3d47351cec8 100644 --- a/app/views/projects/blob/_image.html.haml +++ b/app/views/projects/blob/_image.html.haml @@ -5,11 +5,11 @@ - # be wrong/strange if RawController modified the data. - blob.load_all_data!(@repository) - blob = sanitize_svg(blob) - %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"} + %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}"} - else .nothing-here-block The SVG could not be displayed as it is too large, you can #{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank')} instead. - else - %img{src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path))} + %img{src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}"} diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index 1f31496e73f..e4c2aff46ec 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -17,7 +17,7 @@ ":title" => '"Assigned to " + issue.assignee.name', "v-if" => "issue.assignee", data: { container: 'body' } } - %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20 } + %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20, alt: "Avatar" } %button.label.color-label.has-tooltip{ "v-for" => "label in issue.labels", type: "button", "v-if" => "(!list.label || label.id !== list.label.id)", diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 8fe1b832071..e75ce305440 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -14,7 +14,7 @@ %a.author_link.bold{ ":href" => "'#{root_url}' + issue.assignee.username", "v-if" => "issue.assignee" } %img.avatar.avatar-inline.s32{ ":src" => "issue.assignee.avatar", - width: "32" } + width: "32", alt: "Avatar" } %span.author {{ issue.assignee.name }} %span.username diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 9ec6a7aa5cd..28fee887f4d 100644 --- a/app/views/projects/diffs/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -8,7 +8,7 @@ .image %span.wrap .frame{class: image_diff_class(diff)} - %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path} + %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path, alt: diff.new_path} %p.image-info= "#{number_to_human_size file.size}" - else .image @@ -16,7 +16,7 @@ %span.wrap .frame.deleted %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path))} - %img{src: old_file_raw_path} + %img{src: old_file_raw_path, alt: diff.old_path} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size old_file.size}" | @@ -28,7 +28,7 @@ %span.wrap .frame.added %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path))} - %img{src: file_raw_path} + %img{src: file_raw_path, alt: diff.new_path} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size file.size}" | @@ -41,10 +41,10 @@ %div.swipe.view.hide .swipe-frame .frame.deleted - %img{src: old_file_raw_path} + %img{src: old_file_raw_path, alt: diff.old_path} .swipe-wrap .frame.added - %img{src: file_raw_path} + %img{src: file_raw_path, alt: diff.new_path} %span.swipe-bar %span.top-handle %span.bottom-handle @@ -52,9 +52,9 @@ %div.onion-skin.view.hide .onion-skin-frame .frame.deleted - %img{src: old_file_raw_path} + %img{src: old_file_raw_path, alt: diff.old_path} .frame.added - %img{src: file_raw_path} + %img{src: file_raw_path, alt: diff.new_path} .controls .transparent .drag-track -- cgit v1.2.1 From ef3744d0cd644d75b7fa573777baf29950172623 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 16:27:04 +0530 Subject: HAMLLint: Enable `ClassAttributeWithStaticValue` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index a8f724072e4..442128a8e94 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -10,7 +10,7 @@ linters: enabled: true ClassAttributeWithStaticValue: - enabled: false + enabled: true ClassesBeforeIds: enabled: false -- cgit v1.2.1 From 7b1944f96c6513486cc4a7be30f912e2f7a1110b Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 16:30:09 +0530 Subject: HAMLLint: Fix `ClassAttributeWithStaticValue` offences --- app/views/devise/shared/_signin_box.html.haml | 2 +- app/views/devise/shared/_signup_box.html.haml | 2 +- app/views/help/_shortcuts.html.haml | 8 ++++---- app/views/kaminari/gitlab/_next_page.html.haml | 4 ++-- app/views/kaminari/gitlab/_prev_page.html.haml | 4 ++-- app/views/layouts/devise.html.haml | 2 +- app/views/layouts/header/_default.html.haml | 4 ++-- app/views/projects/blob/_upload.html.haml | 2 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/builds/show.html.haml | 4 ++-- .../projects/merge_requests/conflicts/_submit_form.html.haml | 2 +- app/views/projects/new.html.haml | 2 +- app/views/projects/notes/_note.html.haml | 2 +- app/views/projects/tags/new.html.haml | 2 +- app/views/projects/tree/_submodule_item.html.haml | 2 +- app/views/shared/members/_group.html.haml | 2 +- app/views/shared/milestones/_issuable.html.haml | 6 +++--- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index 86edaf14e43..e5c65a10e05 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -1,6 +1,6 @@ - if form_based_providers.any? - if crowd_enabled? - .login-box.tab-pane.active{id: "crowd", role: 'tabpanel', class: 'tab-pane'} + .login-box.tab-pane.active{id: "crowd", role: 'tabpanel'} .login-body = render 'devise/sessions/new_crowd' - @ldap_servers.each_with_index do |server, i| diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 3133f6de2e8..683a38b9ec6 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -1,4 +1,4 @@ -#register-pane.login-box{ role: 'tabpanel', class: 'tab-pane' } +#register-pane.tab-pane.login-box{ role: 'tabpanel' } .login-body = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f| .devise-errors diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 65842a0479b..eb2eecab4e3 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -82,7 +82,7 @@ .col-lg-4 %table.shortcut-mappings - %tbody{ class: 'hidden-shortcut project', style: 'display:none' } + %tbody.hidden-shortcut.project{ style: 'display:none' } %tr %th %th Global Dashboard @@ -190,7 +190,7 @@ %td New issue .col-lg-4 %table.shortcut-mappings - %tbody{ class: 'hidden-shortcut network', style: 'display:none' } + %tbody.hidden-shortcut.network{ style: 'display:none' } %tr %th %th Network Graph @@ -240,7 +240,7 @@ .key shift j %td Scroll to bottom - %tbody{ class: 'hidden-shortcut issues', style: 'display:none' } + %tbody.hidden-shortcut.issues{ style: 'display:none' } %tr %th %th Issues @@ -264,7 +264,7 @@ %td.shortcut .key l %td Change Label - %tbody{ class: 'hidden-shortcut merge_requests', style: 'display:none' } + %tbody.hidden-shortcut.merge_requests{ style: 'display:none' } %tr %th %th Merge Requests diff --git a/app/views/kaminari/gitlab/_next_page.html.haml b/app/views/kaminari/gitlab/_next_page.html.haml index 125f09777ba..c93dc7a50e8 100644 --- a/app/views/kaminari/gitlab/_next_page.html.haml +++ b/app/views/kaminari/gitlab/_next_page.html.haml @@ -6,8 +6,8 @@ -# per_page: number of items to fetch per page -# remote: data-remote - if current_page.last? - %li{ class: "next disabled" } + %li.next.disabled %span= raw(t 'views.pagination.next') - else - %li{ class: "next" } + %li.next = link_to raw(t 'views.pagination.next'), url, rel: 'next', remote: remote diff --git a/app/views/kaminari/gitlab/_prev_page.html.haml b/app/views/kaminari/gitlab/_prev_page.html.haml index 7edf10498a8..b7c6caf7ff4 100644 --- a/app/views/kaminari/gitlab/_prev_page.html.haml +++ b/app/views/kaminari/gitlab/_prev_page.html.haml @@ -6,8 +6,8 @@ -# per_page: number of items to fetch per page -# remote: data-remote - if current_page.first? - %li{ class: "prev disabled" } + %li.prev.disabled %span= raw(t 'views.pagination.previous') - else - %li{ class: "prev" } + %li.prev = link_to raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index afd9958f073..a72ecec579f 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,5 +1,5 @@ !!! 5 -%html{ lang: "en", class: "devise-layout-html"} +%html.devise-layout-html = render "layouts/head" %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page }} .page-wrap diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 5456be77aab..63ae5ff0628 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,6 +1,6 @@ %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } - %a{ href: "#content-body", tabindex: "1", class: "sr-only gl-accessibility" } Skip to content - %div{ class: "container-fluid" } + %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content + %div.container-fluid .header-content %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" } %span.sr-only Toggle navigation diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 57a27ec904e..fbcd7d3a83d 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -12,7 +12,7 @@ Attach a file by drag & drop or = link_to 'click to upload', '#', class: "markdown-selector" %br - .dropzone-alerts{class: "alert alert-danger data", style: "display:none"} + .dropzone-alerts.alert.alert-danger.data{style: "display:none"} = render 'shared/new_commit_form', placeholder: placeholder diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 5a6c8c243fa..bd1d3947d7e 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -2,7 +2,7 @@ - if @error .alert.alert-danger - %button{ type: "button", class: "close", "data-dismiss" => "alert"} × + %button.close{ type: "button", "data-dismiss" => "alert"} × = @error %h3.page-title New Branch diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index c69c53b656f..54724ef5cab 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -56,10 +56,10 @@ - else #js-build-scroll.scroll-controls .scroll-step - %a{ href: '#up-build-trace', id: 'scroll-top', class: 'scroll-link scroll-top', title: 'Scroll to top' } + %a.scroll-link.scroll-top{ href: '#up-build-trace', id: 'scroll-top', title: 'Scroll to top' } = custom_icon('scroll_up') = custom_icon('scroll_up_hover_active') - %a{ href: '#down-build-trace', id: 'scroll-bottom', class: 'scroll-link scroll-bottom', title: 'Scroll to bottom' } + %a.scroll-link.scroll-bottom{ href: '#down-build-trace', id: 'scroll-bottom', title: 'Scroll to bottom' } = custom_icon('scroll_down') = custom_icon('scroll_down_hover_active') - if @build.active? diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml index 6ffaa9ad4d2..62c9748c510 100644 --- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml +++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml @@ -10,7 +10,7 @@ .col-sm-offset-2.col-sm-10 .row .col-xs-6 - %button{ type: "button", class: "btn btn-success js-submit-button", "@click" => "commit()", ":disabled" => "!readyToCommit" } + %button.btn.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" } %span {{commitButtonText}} .col-xs-6.text-right = link_to "Cancel", namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request), class: "btn btn-cancel" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 866b278ce57..6a23cfc1dfc 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -77,7 +77,7 @@ - if git_import_enabled? = link_to "#", class: 'btn js-toggle-button import_git' do = icon('git', text: 'Repo by URL') - %div{ class: 'import_gitlab_project' } + %div.import_gitlab_project - if gitlab_project_import_enabled? = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = icon('gitlab', text: 'GitLab export') diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 399cf85cd0f..3aecb04fb50 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -15,7 +15,7 @@ - unless note.system commented - if note.system - %span{class: 'system-note-message'} + %span.system-note-message = note.redacted_note_html %a{ href: "##{dom_id(note)}" } = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index c06a413eb2f..9b87696addf 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -2,7 +2,7 @@ - if @error .alert.alert-danger - %button{ type: "button", class: "close", "data-dismiss" => "alert"} × + %button.close{ type: "button", "data-dismiss" => "alert"} × = @error %h3.page-title diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml index 2b5f671c09e..04d52361db0 100644 --- a/app/views/projects/tree/_submodule_item.html.haml +++ b/app/views/projects/tree/_submodule_item.html.haml @@ -1,4 +1,4 @@ -%tr{ class: "tree-item" } +%tr.tree-item %td.tree-item-file-name %i.fa.fa-archive.fa-fw = submodule_link(submodule_item, @ref) diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml index 8928de9097b..a46ba3b0605 100644 --- a/app/views/shared/members/_group.html.haml +++ b/app/views/shared/members/_group.html.haml @@ -3,7 +3,7 @@ - can_admin_member = can?(current_user, :admin_project_member, @project) - dom_id = "group_member_#{group_link.id}" %li.member.group_member{ id: dom_id } - %span{ class: "list-item-name" } + %span.list-item-name = image_tag group_icon(group), class: "avatar s40", alt: '' %strong = link_to group.name, group_path(group) diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 9e1b0379428..7bf8b5d8c58 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -14,15 +14,15 @@ - if issuable.is_a?(Issue) = confidential_icon(issuable) = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title - %div{class: 'issuable-detail'} + %div.issuable-detail = link_to [project.namespace.becomes(Namespace), project, issuable] do - %span{ class: 'issuable-number' }>= issuable.to_reference + %span.issuable-number >= issuable.to_reference - issuable.labels.each do |label| = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do - render_colored_label(label) - %span{ class: "assignee-icon" } + %span.assignee-icon - if assignee = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do -- cgit v1.2.1 From 08e083db1c004f9bec076c910bd5240d282bfc68 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 17:13:11 +0530 Subject: HAMLLint: Enable `FinalNewline` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index 442128a8e94..09f3ad41578 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -29,7 +29,7 @@ linters: enabled: true FinalNewline: - enabled: false + enabled: true present: true HtmlAttributes: -- cgit v1.2.1 From 1974deeffb3ddc88d027cc6fb8f992af7b91b2a7 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 17:13:54 +0530 Subject: HAMLLint: Fix `FinalNewline` offences --- app/views/groups/_group_lfs_settings.html.haml | 2 +- app/views/notify/project_was_not_exported_email.text.haml | 2 +- app/views/projects/tree/_blob_item.html.haml | 2 +- app/views/projects/tree/_tree_item.html.haml | 2 +- app/views/shared/_nav_scroll.html.haml | 2 +- app/views/users/calendar.html.haml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/groups/_group_lfs_settings.html.haml b/app/views/groups/_group_lfs_settings.html.haml index af57065f0fc..3c622ca5c3c 100644 --- a/app/views/groups/_group_lfs_settings.html.haml +++ b/app/views/groups/_group_lfs_settings.html.haml @@ -8,4 +8,4 @@ Allow projects within this group to use Git LFS = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') %br/ - %span.descr This setting can be overridden in each project. \ No newline at end of file + %span.descr This setting can be overridden in each project. diff --git a/app/views/notify/project_was_not_exported_email.text.haml b/app/views/notify/project_was_not_exported_email.text.haml index b27cb620b9e..27785165c2d 100644 --- a/app/views/notify/project_was_not_exported_email.text.haml +++ b/app/views/notify/project_was_not_exported_email.text.haml @@ -3,4 +3,4 @@ = "The errors we encountered were:" - @errors.each do |error| - #{error} \ No newline at end of file + #{error} diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index ee417b58cbf..425b460eb09 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -6,4 +6,4 @@ %span.str-truncated= file_name %td.hidden-xs.tree-commit %td.tree-time-ago.cgray.text-right - = render 'projects/tree/spinner' \ No newline at end of file + = render 'projects/tree/spinner' diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml index 1ccef6d52ab..15c9536133c 100644 --- a/app/views/projects/tree/_tree_item.html.haml +++ b/app/views/projects/tree/_tree_item.html.haml @@ -6,4 +6,4 @@ %span.str-truncated= path %td.hidden-xs.tree-commit %td.tree-time-ago.text-right - = render 'projects/tree/spinner' \ No newline at end of file + = render 'projects/tree/spinner' diff --git a/app/views/shared/_nav_scroll.html.haml b/app/views/shared/_nav_scroll.html.haml index 4e3b1b3a571..61646f150c1 100644 --- a/app/views/shared/_nav_scroll.html.haml +++ b/app/views/shared/_nav_scroll.html.haml @@ -1,4 +1,4 @@ .fade-left = icon('angle-left') .fade-right - = icon('angle-right') \ No newline at end of file + = icon('angle-right') diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 09ff8a76d27..6228245d8d0 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -6,4 +6,4 @@ new Calendar( #{@activity_dates.to_json}, '#{user_calendar_activities_path}' - ); \ No newline at end of file + ); -- cgit v1.2.1 From fa432f0c07ff0add713288be3d03c26915ecef47 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 17:51:27 +0530 Subject: HAMLLint: Enable `HtmlAttributes` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index 09f3ad41578..83d3043b38d 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -33,7 +33,7 @@ linters: present: true HtmlAttributes: - enabled: false + enabled: true ImplicitDiv: enabled: false -- cgit v1.2.1 From 71000b24a48e4e6d15a37bb92ccc4758ff106c3e Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 22 Dec 2016 17:52:05 +0530 Subject: HAMLLint: Fix `HtmlAttributes` offences --- app/views/layouts/devise_mailer.html.haml | 2 +- app/views/projects/branches/_branch.html.haml | 4 ++-- app/views/sherlock/queries/show.html.haml | 4 ++-- app/views/sherlock/transactions/show.html.haml | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml index c258eafdd51..a16c31a9dd9 100644 --- a/app/views/layouts/devise_mailer.html.haml +++ b/app/views/layouts/devise_mailer.html.haml @@ -1,7 +1,7 @@ !!! 5 %html %head - %meta(content='text/html; charset=UTF-8' http-equiv='Content-Type') + %meta{ content: 'text/html; charset=UTF-8', 'http-equiv'=> 'Content-Type' } = stylesheet_link_tag 'mailers/devise' %body diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 9135cee8364..2eb49685f08 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -4,7 +4,7 @@ - number_commits_behind = diverging_commit_counts[:behind] - number_commits_ahead = diverging_commit_counts[:ahead] - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) -%li(class="js-branch-#{branch.name}") +%li{ class: "js-branch-#{branch.name}" } %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name), class: 'item-title str-truncated' do = branch.name @@ -12,7 +12,7 @@ - if branch.name == @repository.root_ref %span.label.label-primary default - elsif @repository.merged_to_root_ref? branch.name - %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}") + %span.label.label-info.has-tooltip{ title: "Merged into #{@repository.root_ref}" } merged - if @project.protected_branch? branch.name diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml index fc2863dca8e..c45da6ee9a4 100644 --- a/app/views/sherlock/queries/show.html.haml +++ b/app/views/sherlock/queries/show.html.haml @@ -3,10 +3,10 @@ %ul.nav-links %li.active - %a(href="#tab-general" data-toggle="tab") + %a{ href: "#tab-general", data: { toggle: "tab" } } = t('sherlock.general') %li - %a(href="#tab-backtrace" data-toggle="tab") + %a{ href: "#tab-backtrace", data: { toggle: "tab" } } = t('sherlock.backtrace') .row-content-block diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml index 8aa6b437d95..eab91e8fbe4 100644 --- a/app/views/sherlock/transactions/show.html.haml +++ b/app/views/sherlock/transactions/show.html.haml @@ -3,15 +3,15 @@ %ul.nav-links %li.active - %a(href="#tab-general" data-toggle="tab") + %a{ href: "#tab-general", data: { toggle: "tab" } } = t('sherlock.general') %li - %a(href="#tab-queries" data-toggle="tab") + %a{ href: "#tab-queries", data: { toggle: "tab" } } = t('sherlock.queries') %span.badge #{@transaction.queries.length} %li - %a(href="#tab-file-samples" data-toggle="tab") + %a{ href: "#tab-file-samples", data: { toggle: "tab" } } = t('sherlock.file_samples') %span.badge #{@transaction.file_samples.length} -- cgit v1.2.1 From 7157f5857a187cfa7011ef7e0077d599e846d452 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 15:02:07 +0530 Subject: HAMLLint: Enable `ImplicitDiv` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index 83d3043b38d..1dac5c06a4c 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -36,7 +36,7 @@ linters: enabled: true ImplicitDiv: - enabled: false + enabled: true LeadingCommentSpace: enabled: false -- cgit v1.2.1 From dd5ffd9ca648dba4c0b58be2172cd14c428fa654 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 15:07:12 +0530 Subject: HAMLLint: Fix `ImplicitDiv` offences --- app/views/admin/groups/show.html.haml | 2 +- app/views/admin/hooks/index.html.haml | 3 +-- app/views/devise/sessions/_new_base.html.haml | 6 +++--- app/views/devise/shared/_omniauth_box.html.haml | 2 +- app/views/devise/shared/_signup_box.html.haml | 6 +++--- app/views/doorkeeper/applications/index.html.haml | 2 +- app/views/groups/show.html.haml | 2 +- app/views/kaminari/gitlab/_paginator.html.haml | 3 +-- app/views/layouts/header/_default.html.haml | 2 +- app/views/layouts/notify.html.haml | 4 ++-- app/views/profiles/keys/index.html.haml | 2 +- app/views/profiles/notifications/show.html.haml | 2 +- app/views/projects/_bitbucket_import_modal.html.haml | 2 +- app/views/projects/_customize_workflow.html.haml | 2 +- app/views/projects/_gitlab_import_modal.html.haml | 2 +- app/views/projects/_md_preview.html.haml | 2 +- app/views/projects/_wiki.html.haml | 2 +- app/views/projects/artifacts/browse.html.haml | 2 +- app/views/projects/blob/_blob.html.haml | 2 +- app/views/projects/blob/show.html.haml | 2 +- app/views/projects/builds/index.html.haml | 2 +- app/views/projects/buttons/_fork.html.haml | 2 +- app/views/projects/buttons/_star.html.haml | 4 ++-- app/views/projects/commit/show.html.haml | 2 +- app/views/projects/commits/_commit_list.html.haml | 2 +- app/views/projects/deployments/_commit.html.haml | 2 +- app/views/projects/diffs/_image.html.haml | 6 +++--- app/views/projects/diffs/_parallel_view.html.haml | 2 +- app/views/projects/empty.html.haml | 2 +- app/views/projects/find_file/show.html.haml | 2 +- app/views/projects/merge_requests/show/_how_to_merge.html.haml | 2 +- app/views/projects/merge_requests/show/_versions.html.haml | 2 +- app/views/projects/new.html.haml | 2 +- app/views/projects/pipelines/index.html.haml | 2 +- app/views/projects/pipelines/show.html.haml | 2 +- app/views/projects/snippets/show.html.haml | 2 +- app/views/projects/tree/_tree_content.html.haml | 4 ++-- app/views/projects/wikis/_new.html.haml | 2 +- app/views/shared/issuable/_participants.html.haml | 2 +- app/views/shared/milestones/_issuable.html.haml | 2 +- app/views/u2f/_register.html.haml | 2 +- 41 files changed, 50 insertions(+), 52 deletions(-) diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index ab9c79f6add..30b3fabdd7e 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -103,7 +103,7 @@ = form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do %div = users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all) - %div.prepend-top-10 + .prepend-top-10 = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr = button_tag 'Add users to group', class: "btn btn-create" diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index c217490963f..78ba8c39c67 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -29,7 +29,7 @@ System hook will be triggered on set of events like creating project or adding ssh key. But you can also enable extra triggers like Push events. - %div.prepend-top-default + .prepend-top-default = f.check_box :push_events, class: 'pull-left' .prepend-left-20 = f.label :push_events, class: 'list-label' do @@ -70,4 +70,3 @@ - if hook.send(trigger) %span.label.label-gray= trigger.titleize %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} - diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 84e13693dfd..dd790bf043d 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -1,8 +1,8 @@ = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f| - %div.form-group + .form-group = f.label "Username or email", for: :login = f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required." - %div.form-group + .form-group = f.label :password = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required." - if devise_mapping.rememberable? @@ -12,5 +12,5 @@ %span Remember me .pull-right.forgot-password = link_to "Forgot your password?", new_password_path(resource_name) - %div.submit-container.move-submit-down + .submit-container.move-submit-down = f.submit "Sign in", class: "btn btn-save" diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index 8908b64cdac..e87a16a5157 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -1,4 +1,4 @@ -%div.omniauth-container +.omniauth-container %p %span.light Sign in with   diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 683a38b9ec6..545a938f4be 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -3,16 +3,16 @@ = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f| .devise-errors = devise_error_messages! - %div.form-group + .form-group = f.label :name = f.text_field :name, class: "form-control top", required: true, title: "This field is required." - %div.username.form-group + .username.form-group = f.label :username = f.text_field :username, class: "form-control middle", pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, required: true, title: 'Please create a username with only alphanumeric characters.' %p.validation-error.hide Username is already taken. %p.validation-success.hide Username is available. %p.validation-pending.hide Checking username availability... - %div.form-group + .form-group = f.label :email = f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address." .form-group.append-bottom-20#password-strength diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml index 3998e66f40d..028398f1a6a 100644 --- a/app/views/doorkeeper/applications/index.html.haml +++ b/app/views/doorkeeper/applications/index.html.haml @@ -72,7 +72,7 @@ %tr %td Anonymous - %div.help-block + .help-block %em Authorization was granted by entering your username and password in the application. %td= token.created_at %td= token.scopes diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 9ef88f233c5..d256d14609e 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -22,7 +22,7 @@ = render 'shared/members/access_request_buttons', source: @group = render 'shared/notifications/button', notification_setting: @notification_setting -%div.groups-header{ class: container_class } +.groups-header{ class: container_class } .top-area %ul.nav-links %li.active diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml index f5e0d2ed3f3..8fe6bd653ae 100644 --- a/app/views/kaminari/gitlab/_paginator.html.haml +++ b/app/views/kaminari/gitlab/_paginator.html.haml @@ -6,7 +6,7 @@ -# remote: data-remote -# paginator: the paginator that renders the pagination tags inside = paginator.render do - %div.gl-pagination + .gl-pagination %ul.pagination.clearfix - unless current_page.first? = first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages @@ -19,4 +19,3 @@ = next_page_tag - unless current_page.last? = last_page_tag unless total_pages < 5 - diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 63ae5ff0628..ad60db9427f 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,6 +1,6 @@ %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content - %div.container-fluid + .container-fluid .header-content %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" } %span.sr-only Toggle navigation diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 1ec4c3f0c67..12356c3641c 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -6,9 +6,9 @@ = stylesheet_link_tag 'notify' = yield :head %body - %div.content + .content = yield - %div.footer{style: "margin-top: 10px;"} + .footer{style: "margin-top: 10px;"} %p — %br diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml index 93187873501..71b224a413b 100644 --- a/app/views/profiles/keys/index.html.haml +++ b/app/views/profiles/keys/index.html.haml @@ -17,5 +17,5 @@ %hr %h5 Your SSH keys (#{@keys.count}) - %div.append-bottom-default + .append-bottom-default = render 'key_table' diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index d79a1a9f368..5c5e5940365 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -3,7 +3,7 @@ %div - if @user.errors.any? - %div.alert.alert-danger + .alert.alert-danger %ul - @user.errors.full_messages.each do |msg| %li= msg diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml index e74fd5b93ea..e4bf70a6b0b 100644 --- a/app/views/projects/_bitbucket_import_modal.html.haml +++ b/app/views/projects/_bitbucket_import_modal.html.haml @@ -1,4 +1,4 @@ -%div#bitbucket_import_modal.modal +#bitbucket_import_modal.modal .modal-dialog .modal-content .modal-header diff --git a/app/views/projects/_customize_workflow.html.haml b/app/views/projects/_customize_workflow.html.haml index d2c1e943db1..e2b73cee5a9 100644 --- a/app/views/projects/_customize_workflow.html.haml +++ b/app/views/projects/_customize_workflow.html.haml @@ -1,5 +1,5 @@ .row-content-block.project-home-empty - %div.text-center{ class: container_class } + .text-center{ class: container_class } %h4 Customize your workflow! %p diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml index e9f39b16aa7..db5f9f552ea 100644 --- a/app/views/projects/_gitlab_import_modal.html.haml +++ b/app/views/projects/_gitlab_import_modal.html.haml @@ -1,4 +1,4 @@ -%div#gitlab_import_modal.modal +#gitlab_import_modal.modal .modal-dialog .modal-content .modal-header diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 58d961d93ca..a7976741e00 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -31,7 +31,7 @@ .md.md-preview-holder.js-md-preview.hide{class: (preview_class if defined?(preview_class))} - if defined?(referenced_users) && referenced_users - %div.referenced-users.hide + .referenced-users.hide %span = icon("exclamation-triangle") You are about to add diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml index f00422dd7c0..41d42740f61 100644 --- a/app/views/projects/_wiki.html.haml +++ b/app/views/projects/_wiki.html.haml @@ -7,7 +7,7 @@ - else - can_create_wiki = can?(current_user, :create_wiki, @project) .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] } - %div.text-center{ class: container_class } + .text-center{ class: container_class } %h4 This project does not have a wiki homepage yet - if can_create_wiki diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index ede01dcc1aa..d0ff14e45e6 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -8,7 +8,7 @@ Download artifacts archive .tree-holder - %div.tree-content-holder + .tree-content-holder %table.table.tree-table %thead %tr diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 149ee7c59d6..350bdf5f836 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -22,7 +22,7 @@ - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) = render blob_commit, project: @project, ref: @ref -%div#blob-content-holder.blob-content-holder +#blob-content-holder.blob-content-holder %article.file-holder .file-title = blob_icon blob.mode, blob.name diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 0ab78a39cf9..b6738c3380f 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -5,7 +5,7 @@ %div{ class: container_class } = render 'projects/last_push' - %div#tree-holder.tree-holder + #tree-holder.tree-holder = render 'blob', blob: @blob - if can_edit_blob?(@blob) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 06070f12bbd..c623e39b21f 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -19,5 +19,5 @@ = link_to ci_lint_path, class: 'btn btn-default' do %span CI Lint - %div.content-list.builds-content-list + .content-list.builds-content-list = render "table", builds: @builds, project: @project diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 27da86b9efe..851fe44a86d 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -8,7 +8,7 @@ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: 'Fork project', class: 'btn' do = custom_icon('icon_fork') %span Fork - %div.count-with-arrow + .count-with-arrow %span.arrow = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count' do = @project.forks_count diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 12d35101770..d57eb2cbfbc 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -6,7 +6,7 @@ - else = icon('star-o') %span Star - %div.count-with-arrow + .count-with-arrow %span.arrow %span.count.star-count = @project.star_count @@ -15,7 +15,7 @@ = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do = icon('star') Star - %div.count-with-arrow + .count-with-arrow %span.arrow %span.count = @project.star_count diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index b8c64d1f13e..7afd3d80ef5 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -8,7 +8,7 @@ - if @commit.status = render "ci_menu" - else - %div.block-connector + .block-connector = render "projects/diffs/diffs", diffs: @diffs = render "projects/notes/notes_with_form" - if can_collaborate_with_project? diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index ce416caa494..6f5835cb9be 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -1,7 +1,7 @@ - commits, hidden = limited_commits(@commits) - commits = Commit.decorate(commits, @project) -%div.panel.panel-default +.panel.panel-default .panel-heading Commits (#{@commits.count}) - if hidden > 0 diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml index ff250eeca50..170d786ecbf 100644 --- a/app/views/projects/deployments/_commit.html.haml +++ b/app/views/projects/deployments/_commit.html.haml @@ -1,4 +1,4 @@ -%div.branch-commit +.branch-commit - if deployment.ref .icon-container = deployment.tag? ? icon('tag') : icon('code-fork') diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 28fee887f4d..81883182833 100644 --- a/app/views/projects/diffs/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -12,7 +12,7 @@ %p.image-info= "#{number_to_human_size file.size}" - else .image - %div.two-up.view + .two-up.view %span.wrap .frame.deleted %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path))} @@ -38,7 +38,7 @@ %b H: %span.meta-height - %div.swipe.view.hide + .swipe.view.hide .swipe-frame .frame.deleted %img{src: old_file_raw_path, alt: diff.old_path} @@ -49,7 +49,7 @@ %span.top-handle %span.bottom-handle - %div.onion-skin.view.hide + .onion-skin.view.hide .onion-skin-frame .frame.deleted %img{src: old_file_raw_path, alt: diff.old_path} diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index 78aa9fb7391..401cbc16117 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -1,5 +1,5 @@ / Side-by-side diff view -%div.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data } +.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data } %table - last_line = 0 - diff_file.parallel_diff_lines.each do |line| diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index c0a83091c8c..3525a07a687 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -32,7 +32,7 @@ .empty_wrapper %h3.page-title-empty Command line instructions - %div.git-empty + .git-empty %fieldset %h5 Git global setup %pre.light-well diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 9322c82904f..b949795f582 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -11,7 +11,7 @@ %li.file-finder %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path', autocomplete: 'off'} - %div.tree-content-holder + .tree-content-holder .table-holder %table.table.files-slider{class: "table_#{@hex_path} tree-table table-striped" } %tbody diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index f1d5441f9dd..e5bbb42ea70 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -1,4 +1,4 @@ -%div#modal_merge_info.modal +#modal_merge_info.modal .modal-dialog .modal-content .modal-header diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml index 5cc92595fe0..d32bb0fd656 100644 --- a/app/views/projects/merge_requests/show/_versions.html.haml +++ b/app/views/projects/merge_requests/show/_versions.html.haml @@ -1,6 +1,6 @@ - if @merge_request_diffs.size > 1 .mr-version-controls - %div.mr-version-menus-container.content-block + .mr-version-menus-container.content-block Changes between %span.dropdown.inline.mr-version-dropdown %a.dropdown-toggle.btn.btn-default{ data: {toggle: :dropdown} } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 6a23cfc1dfc..064e92b15eb 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -77,7 +77,7 @@ - if git_import_enabled? = link_to "#", class: 'btn js-toggle-button import_git' do = icon('git', text: 'Repo by URL') - %div.import_gitlab_project + .import_gitlab_project - if gitlab_project_import_enabled? = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = icon('gitlab', text: 'GitLab export') diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 28026ccf861..7adac9b39e2 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -36,7 +36,7 @@ = link_to ci_lint_path, class: 'btn btn-default' do %span CI Lint - %div.content-list.pipelines + .content-list.pipelines - if @pipelines.blank? %div .nothing-here-block No pipelines to show diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 29a41bc664b..49c1d886423 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -2,7 +2,7 @@ - page_title "Pipeline" = render "projects/pipelines/head" -%div.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } } +.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } } - if @commit = render "projects/pipelines/info" diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 79d87b7db12..485b23815bc 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -15,4 +15,4 @@ .row-content-block.top-block.content-component-block = render 'award_emoji/awards_block', awardable: @snippet, inline: true - %div#notes= render "projects/notes/notes_with_form" + #notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index d37c376c36b..a019578bdf8 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,4 +1,4 @@ -%div.tree-content-holder +.tree-content-holder .table-holder %table.table#tree-slider{class: "table_#{@hex_path} tree-table" } %thead @@ -7,7 +7,7 @@ %th.hidden-xs .pull-left Last commit .last-commit.hidden-sm.pull-left - %small.light + %small.light = clipboard_button(clipboard_text: @commit.id) = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" = time_ago_with_tooltip(@commit.committed_date) diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index c32cb122c26..baa03a84559 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -1,7 +1,7 @@ - @no_container = true %div{ class: container_class } - %div#modal-new-wiki.modal + #modal-new-wiki.modal .modal-dialog .modal-content .modal-header diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index 33a9a494857..2ec19f74be6 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -13,7 +13,7 @@ .participants-author.js-participants-author = link_to_member(@project, participant, name: false, size: 24) - if participants_extra > 0 - %div.participants-more + .participants-more %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}} + #{participants_extra} more :javascript diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 7bf8b5d8c58..51c195ffbcd 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -14,7 +14,7 @@ - if issuable.is_a?(Issue) = confidential_icon(issuable) = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title - %div.issuable-detail + .issuable-detail = link_to [project.namespace.becomes(Namespace), project, issuable] do %span.issuable-number >= issuable.to_reference diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index fcc33f04237..adc07bcba73 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -27,7 +27,7 @@ %a.btn.btn-warning#js-u2f-try-again Try again? %script#js-register-u2f-registered{ type: "text/template" } - %div.row.append-bottom-10 + .row.append-bottom-10 .col-md-12 %p Your device was successfully set up! Give it a name and register it with the GitLab server. = form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do -- cgit v1.2.1 From 6f6f546bc23fa6d5b2ee6a41cf8cded243b8d962 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 15:56:15 +0530 Subject: HAMLLint: Enable `SpaceBeforeScript` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index 1dac5c06a4c..eaa7f2ab547 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -80,7 +80,7 @@ linters: enabled: false SpaceBeforeScript: - enabled: false + enabled: true SpaceInsideHashAttributes: enabled: false -- cgit v1.2.1 From 598d8cab324782906c84e830b5e880949c0c7a83 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 15:57:41 +0530 Subject: HAMLLint: Fix `SpaceBeforeScript` offences --- app/views/admin/broadcast_messages/index.html.haml | 2 +- app/views/admin/hooks/index.html.haml | 2 +- app/views/ci/lints/_create.html.haml | 2 +- app/views/dashboard/projects/_zero_authorized_projects.html.haml | 2 +- app/views/notify/new_issue_email.html.haml | 2 +- app/views/notify/new_mention_in_issue_email.html.haml | 2 +- app/views/notify/new_mention_in_merge_request_email.html.haml | 2 +- app/views/notify/new_merge_request_email.html.haml | 2 +- app/views/projects/_bitbucket_import_modal.html.haml | 2 +- app/views/projects/_gitlab_import_modal.html.haml | 2 +- app/views/projects/_md_preview.html.haml | 2 +- app/views/projects/forks/index.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 2 +- app/views/shared/_clone_panel.html.haml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index c05538a393c..4f2ae081d7a 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -10,7 +10,7 @@ %br.clearfix --if @broadcast_messages.any? +- if @broadcast_messages.any? %table.table %thead %tr diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index 78ba8c39c67..551edf14361 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -54,7 +54,7 @@ = f.submit "Add System Hook", class: "btn btn-create" %hr --if @hooks.any? +- if @hooks.any? .panel.panel-default .panel-heading System hooks (#{@hooks.count}) diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml index 61c7cce20b2..c91602fcff7 100644 --- a/app/views/ci/lints/_create.html.haml +++ b/app/views/ci/lints/_create.html.haml @@ -36,7 +36,7 @@ - if build[:allow_failure] %b Allowed to fail --else +- else %p %b Status: syntax is incorrect diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml index 4a55aac0df6..1bbd4602ecf 100644 --- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml @@ -33,7 +33,7 @@ = link_to new_project_path, class: "btn btn-new" do New project --if publicish_project_count > 0 +- if publicish_project_count > 0 .blank-state .blank-state-icon = icon("globe") diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index f42b150c0d6..d1855568215 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,7 +1,7 @@ - if current_application_settings.email_author_in_body %div #{link_to @issue.author_name, user_url(@issue.author)} wrote: --if @issue.description +- if @issue.description = markdown(@issue.description, pipeline: :email, author: @issue.author) - if @issue.assignee_id.present? diff --git a/app/views/notify/new_mention_in_issue_email.html.haml b/app/views/notify/new_mention_in_issue_email.html.haml index 4f3d36bd9ca..02f21baa368 100644 --- a/app/views/notify/new_mention_in_issue_email.html.haml +++ b/app/views/notify/new_mention_in_issue_email.html.haml @@ -4,7 +4,7 @@ - if current_application_settings.email_author_in_body %div #{link_to @issue.author_name, user_url(@issue.author)} wrote: --if @issue.description +- if @issue.description = markdown(@issue.description, pipeline: :email, author: @issue.author) - if @issue.assignee_id.present? diff --git a/app/views/notify/new_mention_in_merge_request_email.html.haml b/app/views/notify/new_mention_in_merge_request_email.html.haml index 32aedb9e6b9..cbd434be02a 100644 --- a/app/views/notify/new_mention_in_merge_request_email.html.haml +++ b/app/views/notify/new_mention_in_merge_request_email.html.haml @@ -11,5 +11,5 @@ %p Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} --if @merge_request.description +- if @merge_request.description = markdown(@merge_request.description, pipeline: :email, author: @merge_request.author) diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 158404de396..8890b300f7d 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -8,5 +8,5 @@ %p Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} --if @merge_request.description +- if @merge_request.description = markdown(@merge_request.description, pipeline: :email, author: @merge_request.author) diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml index e4bf70a6b0b..899601b8a0a 100644 --- a/app/views/projects/_bitbucket_import_modal.html.haml +++ b/app/views/projects/_bitbucket_import_modal.html.haml @@ -10,4 +10,4 @@ as administrator you need to configure - else ask your GitLab administrator to configure - == #{link_to 'OAuth integration', help_page_path("integration/bitbucket")}. + = link_to 'OAuth integration', help_page_path("integration/bitbucket") diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml index db5f9f552ea..06f7c684d2b 100644 --- a/app/views/projects/_gitlab_import_modal.html.haml +++ b/app/views/projects/_gitlab_import_modal.html.haml @@ -10,4 +10,4 @@ as administrator you need to configure - else ask your GitLab administrator to configure - == #{link_to 'OAuth integration', help_page_path("integration/gitlab")}. + = link_to 'OAuth integration', help_page_path("integration/gitlab") diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index a7976741e00..eccf020f25a 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -24,7 +24,7 @@ = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) .toolbar-group %button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } } - =icon("arrows-alt fw") + = icon("arrows-alt fw") .md-write-holder = yield diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 5ee3979c7e7..1f2b0a89afe 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -1,7 +1,7 @@ .top-area .nav-text - full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private" - == #{pluralize(@total_forks_count, 'fork')}: #{full_count_title} + = "#{pluralize(@total_forks_count, 'fork')}: #{full_count_title}" .nav-controls = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 513710e8e66..0f4a8508751 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -20,6 +20,6 @@ - if @milestone.new_record? = f.submit 'Create milestone', class: "btn-create btn" = link_to "Cancel", namespace_project_milestones_path(@project.namespace, @project), class: "btn btn-cancel" - -else + - else = f.submit 'Save changes', class: "btn-save btn" = link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel" diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 3b82d8e686f..b315cd06f35 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -2,7 +2,7 @@ .git-clone-holder.input-group .input-group-btn - -if allowed_protocols_present? + - if allowed_protocols_present? .clone-dropdown-btn.btn.btn-static %span = enabled_project_button(project, enabled_protocol) -- cgit v1.2.1 From df4f896bcb86a53711725e8b6b62bfad894bc1e1 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 17:18:23 +0530 Subject: HAMLLint: Enable `TrailingWhitespace` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index eaa7f2ab547..ee3da0d5822 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -94,7 +94,7 @@ linters: enabled: true TrailingWhitespace: - enabled: false + enabled: true UnnecessaryInterpolation: enabled: false -- cgit v1.2.1 From 97f3c8f3c9480f92375aa3fbb09138bdf43792c6 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 23 Dec 2016 17:20:31 +0530 Subject: HAMLLint: Fix `TrailingWhitespace` offences --- app/views/abuse_report_mailer/notify.html.haml | 6 +++--- app/views/doorkeeper/authorized_applications/_delete_form.html.haml | 2 +- app/views/projects/_find_file_link.html.haml | 6 +++--- app/views/projects/forks/index.html.haml | 2 +- app/views/projects/merge_requests/_merge_request.html.haml | 2 +- app/views/projects/merge_requests/widget/_closed.html.haml | 2 +- app/views/projects/merge_requests/widget/_open.html.haml | 2 -- app/views/projects/merge_requests/widget/open/_archived.html.haml | 2 +- app/views/projects/merge_requests/widget/open/_nothing.html.haml | 2 +- 9 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml index 2741eb44357..d50b4071745 100644 --- a/app/views/abuse_report_mailer/notify.html.haml +++ b/app/views/abuse_report_mailer/notify.html.haml @@ -1,7 +1,7 @@ %p - #{link_to @abuse_report.user.name, user_url(@abuse_report.user)} - (@#{@abuse_report.user.username}) was reported for abuse by - #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)} + #{link_to @abuse_report.user.name, user_url(@abuse_report.user)} + (@#{@abuse_report.user.username}) was reported for abuse by + #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)} (@#{@abuse_report.reporter.username}). %blockquote diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml index 9f02a8d2ed9..7f161c050b1 100644 --- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml +++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml @@ -3,7 +3,7 @@ - path = oauth_authorized_application_path(0, token_id: token) - else - path = oauth_authorized_application_path(application) - + = form_tag path do %input{:name => "_method", :type => "hidden", :value => "delete"}/ = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm' diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml index 08e2fc48be7..dbb33090670 100644 --- a/app/views/projects/_find_file_link.html.haml +++ b/app/views/projects/_find_file_link.html.haml @@ -1,3 +1,3 @@ -= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do - = icon('search') - %span Find File += link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do + = icon('search') + %span Find File diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 1f2b0a89afe..8657d73b881 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -1,7 +1,7 @@ .top-area .nav-text - full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private" - = "#{pluralize(@total_forks_count, 'fork')}: #{full_count_title}" + = "#{pluralize(@total_forks_count, 'fork')}: #{full_count_title}" .nav-controls = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index b3c43286a50..e3b0aa7e644 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -65,7 +65,7 @@   - merge_request.labels.each do |label| = link_to_label(label, subject: merge_request.project, type: :merge_request) - + - if merge_request.tasks?   %span.task-status diff --git a/app/views/projects/merge_requests/widget/_closed.html.haml b/app/views/projects/merge_requests/widget/_closed.html.haml index f3cc0e7e8a1..15f47ecf210 100644 --- a/app/views/projects/merge_requests/widget/_closed.html.haml +++ b/app/views/projects/merge_requests/widget/_closed.html.haml @@ -6,7 +6,7 @@ - if @merge_request.closed_event by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)} #{time_ago_with_tooltip(@merge_request.closed_event.created_at)} - %p + %p = succeed '.' do The changes were not merged into %span.label-branch= @merge_request.target_branch diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 40fbac7025a..c0d6ab669b8 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -43,5 +43,3 @@ #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not be closed. - - diff --git a/app/views/projects/merge_requests/widget/open/_archived.html.haml b/app/views/projects/merge_requests/widget/open/_archived.html.haml index ab30fa6b243..0d61e56d8fb 100644 --- a/app/views/projects/merge_requests/widget/open/_archived.html.haml +++ b/app/views/projects/merge_requests/widget/open/_archived.html.haml @@ -1,4 +1,4 @@ -%h4 +%h4 Project is archived %p This merge request cannot be merged because archived projects cannot be written to. diff --git a/app/views/projects/merge_requests/widget/open/_nothing.html.haml b/app/views/projects/merge_requests/widget/open/_nothing.html.haml index 35626b624b7..7af8c01c134 100644 --- a/app/views/projects/merge_requests/widget/open/_nothing.html.haml +++ b/app/views/projects/merge_requests/widget/open/_nothing.html.haml @@ -1,4 +1,4 @@ -%h4 +%h4 = icon("exclamation-triangle") Nothing to merge from %span.label-branch= source_branch_with_namespace(@merge_request) -- cgit v1.2.1 From 8e2a76d2dee86ed05bc5ed144424a33b45f92da7 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Mon, 26 Dec 2016 15:57:58 +0530 Subject: HAMLLint: Enable `SpaceInsideHashAttributes` rule --- .haml-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.haml-lint.yml b/.haml-lint.yml index ee3da0d5822..7c8a9c4fd17 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -83,7 +83,7 @@ linters: enabled: true SpaceInsideHashAttributes: - enabled: false + enabled: true style: space Indentation: -- cgit v1.2.1 From fb3e365412954b5d23a048518d70c15894cb9686 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Mon, 26 Dec 2016 16:17:16 +0530 Subject: HAMLLint: Fix `SpaceInsideHashAttributes` offences --- .../admin/application_settings/_form.html.haml | 4 +- .../admin/applications/_delete_form.html.haml | 2 +- app/views/admin/applications/index.html.haml | 2 +- app/views/admin/background_jobs/show.html.haml | 2 +- app/views/admin/groups/_group.html.haml | 2 +- app/views/admin/labels/_label.html.haml | 2 +- app/views/admin/runners/_runner.html.haml | 2 +- app/views/admin/users/_user.html.haml | 2 +- app/views/admin/users/show.html.haml | 2 +- app/views/dashboard/todos/_todo.html.haml | 2 +- app/views/dashboard/todos/index.html.haml | 6 +- app/views/devise/sessions/_new_base.html.haml | 2 +- app/views/devise/sessions/_new_crowd.html.haml | 4 +- app/views/devise/sessions/_new_ldap.html.haml | 4 +- app/views/devise/shared/_signin_box.html.haml | 8 +- app/views/devise/shared/_tabs_ldap.html.haml | 2 +- app/views/devise/shared/_tabs_normal.html.haml | 8 +- app/views/discussions/_diff_discussion.html.haml | 2 +- app/views/discussions/_jump_to_next.html.haml | 2 +- .../_parallel_diff_discussion.html.haml | 6 +- .../doorkeeper/applications/_delete_form.html.haml | 2 +- app/views/doorkeeper/applications/index.html.haml | 4 +- .../doorkeeper/authorizations/error.html.haml | 2 +- app/views/doorkeeper/authorizations/new.html.haml | 2 +- app/views/doorkeeper/authorizations/show.html.haml | 2 +- .../authorized_applications/_delete_form.html.haml | 2 +- .../authorized_applications/index.html.haml | 2 +- app/views/emojis/index.html.haml | 2 +- app/views/errors/access_denied.html.haml | 3 +- app/views/errors/encoding.html.haml | 3 +- app/views/errors/git_not_found.html.haml | 3 +- app/views/errors/not_found.html.haml | 3 +- app/views/errors/omniauth_error.html.haml | 3 +- app/views/events/_event_issue.atom.haml | 2 +- app/views/events/_event_merge_request.atom.haml | 2 +- app/views/events/_event_note.atom.haml | 2 +- app/views/events/_event_push.atom.haml | 2 +- app/views/events/event/_common.html.haml | 2 +- app/views/events/event/_created_project.html.haml | 2 +- app/views/explore/groups/index.html.haml | 2 +- app/views/explore/projects/_filter.html.haml | 4 +- app/views/help/_shortcuts.html.haml | 4 +- app/views/help/ui.html.haml | 124 +++++++++---------- app/views/import/_githubish_status.html.haml | 4 +- app/views/import/bitbucket/status.html.haml | 6 +- app/views/import/fogbugz/status.html.haml | 4 +- app/views/import/gitlab/status.html.haml | 4 +- app/views/import/google_code/new.html.haml | 2 +- app/views/import/google_code/status.html.haml | 6 +- app/views/kaminari/gitlab/_page.html.haml | 4 +- app/views/layouts/_head.html.haml | 40 +++--- app/views/layouts/_search.html.haml | 4 +- app/views/layouts/application.html.haml | 2 +- app/views/layouts/devise.html.haml | 2 +- app/views/layouts/devise_empty.html.haml | 2 +- app/views/layouts/devise_mailer.html.haml | 2 +- app/views/layouts/errors.html.haml | 4 +- app/views/layouts/header/_default.html.haml | 2 +- app/views/layouts/nav/_admin_settings.html.haml | 2 +- app/views/layouts/nav/_group_settings.html.haml | 2 +- app/views/layouts/nav/_project.html.haml | 2 +- app/views/layouts/notify.html.haml | 6 +- app/views/notify/build_fail_email.html.haml | 2 +- app/views/notify/build_success_email.html.haml | 2 +- app/views/notify/links/ci/builds/_build.html.haml | 2 +- app/views/notify/pipeline_failed_email.html.haml | 136 ++++++++++----------- app/views/notify/pipeline_success_email.html.haml | 112 ++++++++--------- app/views/notify/repository_push_email.html.haml | 6 +- app/views/profiles/chat_names/new.html.haml | 2 +- app/views/profiles/preferences/show.html.haml | 4 +- app/views/profiles/show.html.haml | 8 +- app/views/projects/_activity.html.haml | 2 +- .../projects/_bitbucket_import_modal.html.haml | 2 +- app/views/projects/_gitlab_import_modal.html.haml | 2 +- app/views/projects/_home_panel.html.haml | 2 +- app/views/projects/_md_preview.html.haml | 16 +-- .../projects/artifacts/_tree_directory.html.haml | 2 +- app/views/projects/blame/show.html.haml | 2 +- app/views/projects/blob/_image.html.haml | 4 +- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/blob/_remove.html.haml | 2 +- app/views/projects/blob/_upload.html.haml | 4 +- app/views/projects/blob/diff.html.haml | 8 +- app/views/projects/blob/preview.html.haml | 2 +- app/views/projects/branches/index.html.haml | 2 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/builds/_sidebar.html.haml | 8 +- app/views/projects/buttons/_dropdown.html.haml | 2 +- app/views/projects/ci/builds/_build.html.haml | 2 +- .../projects/ci/pipelines/_pipeline.html.haml | 6 +- app/views/projects/commit/_change.html.haml | 6 +- app/views/projects/commit/_commit_box.html.haml | 2 +- app/views/projects/commits/show.html.haml | 4 +- app/views/projects/cycle_analytics/show.html.haml | 8 +- app/views/projects/deployments/_actions.haml | 2 +- app/views/projects/diffs/_content.html.haml | 2 +- app/views/projects/diffs/_file.html.haml | 4 +- app/views/projects/diffs/_image.html.haml | 28 ++--- app/views/projects/diffs/_line.html.haml | 4 +- app/views/projects/diffs/_parallel_view.html.haml | 12 +- app/views/projects/diffs/_stats.html.haml | 9 +- app/views/projects/environments/index.html.haml | 2 +- app/views/projects/environments/terminal.html.haml | 6 +- app/views/projects/find_file/show.html.haml | 4 +- app/views/projects/forks/index.html.haml | 2 +- .../_generic_commit_status.html.haml | 2 +- app/views/projects/graphs/ci/_build_times.haml | 2 +- app/views/projects/graphs/ci/_builds.haml | 6 +- app/views/projects/graphs/show.html.haml | 2 +- app/views/projects/issues/_issue.html.haml | 2 +- app/views/projects/issues/_new_branch.html.haml | 2 +- app/views/projects/merge_requests/_show.html.haml | 6 +- .../projects/merge_requests/conflicts.html.haml | 18 +-- .../conflicts/_commit_stats.html.haml | 8 +- .../conflicts/_file_actions.html.haml | 4 +- .../components/_diff_file_editor.html.haml | 2 +- .../components/_inline_conflict_lines.html.haml | 18 +-- .../merge_requests/show/_how_to_merge.html.haml | 2 +- .../projects/merge_requests/show/_mr_box.html.haml | 2 +- .../merge_requests/show/_versions.html.haml | 4 +- .../merge_requests/widget/_heading.html.haml | 6 +- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/notes/_note.html.haml | 4 +- app/views/projects/pipelines/_info.html.haml | 2 +- app/views/projects/pipelines/index.html.haml | 8 +- app/views/projects/runners/_runner.html.haml | 2 +- app/views/projects/stage/_graph.html.haml | 5 +- app/views/projects/stage/_in_stage_group.html.haml | 2 +- app/views/projects/stage/_stage.html.haml | 6 +- app/views/projects/tags/new.html.haml | 2 +- app/views/projects/tree/_tree_content.html.haml | 2 +- app/views/projects/tree/_tree_header.html.haml | 8 +- app/views/projects/variables/index.html.haml | 2 +- app/views/projects/wikis/_new.html.haml | 2 +- app/views/search/_category.html.haml | 26 ++-- app/views/shared/_clone_panel.html.haml | 2 +- app/views/shared/_confirm_modal.html.haml | 4 +- app/views/shared/_file_highlight.html.haml | 4 +- app/views/shared/_label.html.haml | 4 +- app/views/shared/_milestones_filter.html.haml | 6 +- app/views/shared/_sort_dropdown.html.haml | 2 +- app/views/shared/groups/_group.html.haml | 2 +- app/views/shared/issuable/_filter.html.haml | 10 +- app/views/shared/issuable/_form.html.haml | 2 +- .../shared/issuable/_label_dropdown.html.haml | 2 +- .../shared/issuable/_label_page_default.html.haml | 2 +- app/views/shared/issuable/_nav.html.haml | 10 +- app/views/shared/issuable/_participants.html.haml | 2 +- app/views/shared/issuable/_sidebar.html.haml | 14 +-- app/views/shared/milestones/_milestone.html.haml | 2 +- .../notifications/_custom_notifications.html.haml | 2 +- app/views/shared/projects/_project.html.haml | 2 +- app/views/shared/snippets/_blob.html.haml | 2 +- app/views/sherlock/file_samples/show.html.haml | 2 +- app/views/sherlock/queries/_general.html.haml | 4 +- app/views/sherlock/transactions/index.html.haml | 2 +- app/views/users/show.html.haml | 8 +- 157 files changed, 514 insertions(+), 521 deletions(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 7accd2529af..4612a7a058a 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -321,7 +321,7 @@ = f.text_field :recaptcha_site_key, class: 'form-control' .help-block Generate site and private keys at - %a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha + %a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha .form-group = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2' @@ -342,7 +342,7 @@ = f.text_field :akismet_api_key, class: 'form-control' .help-block Generate API key at - %a{ href: 'http://www.akismet.com', target: 'blank'} http://www.akismet.com + %a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com %fieldset %legend Abuse reports diff --git a/app/views/admin/applications/_delete_form.html.haml b/app/views/admin/applications/_delete_form.html.haml index 042971e1eed..82781f6716d 100644 --- a/app/views/admin/applications/_delete_form.html.haml +++ b/app/views/admin/applications/_delete_form.html.haml @@ -1,4 +1,4 @@ - submit_btn_css ||= 'btn btn-link btn-remove btn-sm' = form_tag admin_application_path(application) do - %input{:name => "_method", :type => "hidden", :value => "delete"}/ + %input{ :name => "_method", :type => "hidden", :value => "delete" }/ = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index f8cd98f0ec4..b3a3b4c1d45 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -15,7 +15,7 @@ %th %tbody.oauth-applications - @applications.each do |application| - %tr{:id => "application_#{application.id}"} + %tr{ :id => "application_#{application.id}" } %td= link_to application.name, admin_application_path(application) %td= application.redirect_uri %td= application.access_tokens.map(&:resource_owner_id).uniq.count diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index 05855db963a..4f982a6e369 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -43,4 +43,4 @@ .panel.panel-default - %iframe{src: sidekiq_path, width: '100%', height: 970, style: "border: none"} + %iframe{ src: sidekiq_path, width: '100%', height: 970, style: "border: none" } diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index 6fc212119c4..e3a77dfdf10 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -16,7 +16,7 @@ = icon('users') = number_with_delimiter(group.users.count) - %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)} + %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) } = visibility_level_icon(group.visibility_level, fw: false) .avatar-container.s40 diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index be224d66855..77b174fbb27 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -1,4 +1,4 @@ -%li{id: dom_id(label)} +%li{ id: dom_id(label) } .label-row = render_colored_label(label, tooltip: false) = markdown_field(label, :description) diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 64893b38c58..975bd950ae1 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -1,4 +1,4 @@ -%tr{id: dom_id(runner)} +%tr{ id: dom_id(runner) } %td - if runner.shared? %span.label.label-success shared diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 2d9588f9d27..3b5c713ac2d 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -18,7 +18,7 @@ = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn' - unless user == current_user .dropdown.inline - %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', data: { toggle: 'dropdown' } } + %a.dropdown-new.btn.btn-default#project-settings-button{ href: '#', data: { toggle: 'dropdown' } } = icon('cog') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 76c9ed0ee8b..a71240986c9 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -40,7 +40,7 @@ %li.two-factor-status %span.light Two-factor Authentication: - %strong{class: @user.two_factor_enabled? ? 'cgreen' : 'cred'} + %strong{ class: @user.two_factor_enabled? ? 'cgreen' : 'cred' } - if @user.two_factor_enabled? Enabled = link_to 'Disable', disable_two_factor_admin_user_path(@user), data: {confirm: 'Are you sure?'}, method: :patch, class: 'btn btn-xs btn-remove pull-right', title: 'Disable Two-factor Authentication' diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index cc077fad32a..b084685b388 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -1,4 +1,4 @@ -%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data:{url: todo_target_path(todo)} } +%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data:{url: todo_target_path(todo) } } = author_avatar(todo, size: 40) .todo-item.todo-block diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index e13f404fee2..f4efcfb27b2 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -5,14 +5,14 @@ .top-area %ul.nav-links - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending') - %li{class: "todos-pending #{todo_pending_active}"}> + %li{ class: "todos-pending #{todo_pending_active}" }> = link_to todos_filter_path(state: 'pending') do %span To do %span.badge = number_with_delimiter(todos_pending_count) - todo_done_active = ('active' if params[:state] == 'done') - %li{class: "todos-done #{todo_done_active}"}> + %li{ class: "todos-done #{todo_done_active}" }> = link_to todos_filter_path(state: 'done') do %span Done @@ -50,7 +50,7 @@ data: { data: todo_actions_options, default_label: 'Action' } }) .pull-right .dropdown.inline.prepend-left-10 - %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.light - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index dd790bf043d..5d359538efe 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -7,7 +7,7 @@ = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required." - if devise_mapping.rememberable? .remember-me.checkbox - %label{for: "user_remember_me"} + %label{ for: "user_remember_me" } = f.check_box :remember_me %span Remember me .pull-right.forgot-password diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml index a6cadbcbdff..2556cb6f59b 100644 --- a/app/views/devise/sessions/_new_crowd.html.haml +++ b/app/views/devise/sessions/_new_crowd.html.haml @@ -1,13 +1,13 @@ = form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'gl-show-field-errors') do .form-group = label_tag :username, 'Username or email' - = text_field_tag :username, nil, {class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true } + = text_field_tag :username, nil, { class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true } .form-group = label_tag :password = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true } - if devise_mapping.rememberable? .remember-me.checkbox - %label{for: "remember_me"} + %label{ for: "remember_me" } = check_box_tag :remember_me, '1', false, id: 'remember_me' %span Remember me = submit_tag "Sign in", class: "btn-save btn" diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 3ab5461f929..3159d21598a 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -1,13 +1,13 @@ = form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do .form-group = label_tag :username, "#{server['label']} Username" - = text_field_tag :username, nil, {class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true } + = text_field_tag :username, nil, { class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true } .form-group = label_tag :password = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true } - if devise_mapping.rememberable? .remember-me.checkbox - %label{for: "remember_me"} + %label{ for: "remember_me" } = check_box_tag :remember_me, '1', false, id: 'remember_me' %span Remember me = submit_tag "Sign in", class: "btn-save btn" diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index e5c65a10e05..eddfce363a7 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -1,18 +1,18 @@ - if form_based_providers.any? - if crowd_enabled? - .login-box.tab-pane.active{id: "crowd", role: 'tabpanel'} + .login-box.tab-pane.active{ id: "crowd", role: 'tabpanel' } .login-body = render 'devise/sessions/new_crowd' - @ldap_servers.each_with_index do |server, i| - .login-box.tab-pane{id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?)} + .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?) } .login-body = render 'devise/sessions/new_ldap', server: server - if signin_enabled? - .login-box.tab-pane{id: 'ldap-standard', role: 'tabpanel'} + .login-box.tab-pane{ id: 'ldap-standard', role: 'tabpanel' } .login-body = render 'devise/sessions/new_base' - elsif signin_enabled? - .login-box.tab-pane.active{id: 'login-pane', role: 'tabpanel'} + .login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' } .login-body = render 'devise/sessions/new_base' diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index aec1b31ce62..8c4ad30c832 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -3,7 +3,7 @@ %li.active = link_to "Crowd", "#crowd", 'data-toggle' => 'tab' - @ldap_servers.each_with_index do |server, i| - %li{class: (:active if i.zero? && !crowd_enabled?)} + %li{ class: (:active if i.zero? && !crowd_enabled?) } = link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab' - if signin_enabled? %li diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml index 05246303fb6..c225d800a98 100644 --- a/app/views/devise/shared/_tabs_normal.html.haml +++ b/app/views/devise/shared/_tabs_normal.html.haml @@ -1,6 +1,6 @@ -%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'} +%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist' } %li.active{ role: 'presentation' } - %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in + %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in - if signin_enabled? && signup_enabled? - %li{ role: 'presentation'} - %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register + %li{ role: 'presentation' } + %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register diff --git a/app/views/discussions/_diff_discussion.html.haml b/app/views/discussions/_diff_discussion.html.haml index 1411daeb4a6..2deadbeeceb 100644 --- a/app/views/discussions/_diff_discussion.html.haml +++ b/app/views/discussions/_diff_discussion.html.haml @@ -1,5 +1,5 @@ - expanded = local_assigns.fetch(:expanded, true) -%tr.notes_holder{class: ('hide' unless expanded)} +%tr.notes_holder{ class: ('hide' unless expanded) } %td.notes_line{ colspan: 2 } %td.notes_content .content diff --git a/app/views/discussions/_jump_to_next.html.haml b/app/views/discussions/_jump_to_next.html.haml index 7ed09dd1a98..69bd416c4de 100644 --- a/app/views/discussions/_jump_to_next.html.haml +++ b/app/views/discussions/_jump_to_next.html.haml @@ -5,5 +5,5 @@ %button.btn.btn-default.discussion-next-btn.has-tooltip{ "@click" => "jumpToNextUnresolvedDiscussion", title: "Jump to next unresolved discussion", "aria-label" => "Jump to next unresolved discussion", - data: { container: "body" }} + data: { container: "body" } } = custom_icon("next_discussion") diff --git a/app/views/discussions/_parallel_diff_discussion.html.haml b/app/views/discussions/_parallel_diff_discussion.html.haml index f1072ce0feb..ef16b516e2c 100644 --- a/app/views/discussions/_parallel_diff_discussion.html.haml +++ b/app/views/discussions/_parallel_diff_discussion.html.haml @@ -1,9 +1,9 @@ - expanded = discussion_left.try(:expanded?) || discussion_right.try(:expanded?) -%tr.notes_holder{class: ('hide' unless expanded)} +%tr.notes_holder{ class: ('hide' unless expanded) } - if discussion_left %td.notes_line.old %td.notes_content.parallel.old - .content{class: ('hide' unless discussion_left.expanded?)} + .content{ class: ('hide' unless discussion_left.expanded?) } = render "discussions/notes", discussion: discussion_left, line_type: 'old' - else %td.notes_line.old= "" @@ -13,7 +13,7 @@ - if discussion_right %td.notes_line.new %td.notes_content.parallel.new - .content{class: ('hide' unless discussion_right.expanded?)} + .content{ class: ('hide' unless discussion_right.expanded?) } = render "discussions/notes", discussion: discussion_right, line_type: 'new' - else %td.notes_line.new= "" diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml index 001a711b1dd..84b4ce5b606 100644 --- a/app/views/doorkeeper/applications/_delete_form.html.haml +++ b/app/views/doorkeeper/applications/_delete_form.html.haml @@ -1,6 +1,6 @@ - submit_btn_css ||= 'btn btn-link btn-remove btn-sm' = form_tag oauth_application_path(application) do - %input{:name => "_method", :type => "hidden", :value => "delete"}/ + %input{ :name => "_method", :type => "hidden", :value => "delete" }/ - if defined? small = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: "Are you sure?" } do %span.sr-only diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml index 028398f1a6a..aa271150b07 100644 --- a/app/views/doorkeeper/applications/index.html.haml +++ b/app/views/doorkeeper/applications/index.html.haml @@ -31,7 +31,7 @@ %th.last-heading %tbody - @applications.each do |application| - %tr{id: "application_#{application.id}"} + %tr{ id: "application_#{application.id}" } %td= link_to application.name, oauth_application_path(application) %td - application.redirect_uri.split.each do |uri| @@ -63,7 +63,7 @@ %tbody - @authorized_apps.each do |app| - token = app.authorized_tokens.order('created_at desc').first - %tr{id: "application_#{app.id}"} + %tr{ id: "application_#{app.id}" } %td= app.name %td= token.created_at %td= token.scopes diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml index a4c607cea60..6117b00149f 100644 --- a/app/views/doorkeeper/authorizations/error.html.haml +++ b/app/views/doorkeeper/authorizations/error.html.haml @@ -1,3 +1,3 @@ %h3.page-title An error has occurred -%main{:role => "main"} +%main{ :role => "main" } %pre= @pre_auth.error_response.body[:error_description] diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml index ce050007204..2a0e301c8dd 100644 --- a/app/views/doorkeeper/authorizations/new.html.haml +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -1,5 +1,5 @@ %h3.page-title Authorization required -%main{:role => "main"} +%main{ :role => "main" } %p.h4 Authorize %strong.text-info= @pre_auth.client.name diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml index 01f9e46f142..44e868e6782 100644 --- a/app/views/doorkeeper/authorizations/show.html.haml +++ b/app/views/doorkeeper/authorizations/show.html.haml @@ -1,3 +1,3 @@ %h3.page-title Authorization code: -%main{:role => "main"} +%main{ :role => "main" } %code#authorization_code= params[:code] diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml index 7f161c050b1..11c1e67878e 100644 --- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml +++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml @@ -5,5 +5,5 @@ - path = oauth_authorized_application_path(application) = form_tag path do - %input{:name => "_method", :type => "hidden", :value => "delete"}/ + %input{ :name => "_method", :type => "hidden", :value => "delete" }/ = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm' diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml index b184b9c01d4..c8a585560a2 100644 --- a/app/views/doorkeeper/authorized_applications/index.html.haml +++ b/app/views/doorkeeper/authorized_applications/index.html.haml @@ -1,6 +1,6 @@ %header.page-header %h1 Your authorized applications -%main{:role => "main"} +%main{ :role => "main" } .table-holder %table.table.table-striped %thead diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml index 790d90ad3ee..49bd9acd2db 100644 --- a/app/views/emojis/index.html.haml +++ b/app/views/emojis/index.html.haml @@ -7,5 +7,5 @@ %ul.clearfix.emoji-menu-list - emojis.each do |emoji| %li.pull-left.text-center.emoji-menu-list-item - %button.emoji-menu-btn.text-center.js-emoji-btn{type: "button"} + %button.emoji-menu-btn.text-center.js-emoji-btn{ type: "button" } = emoji_icon(emoji["name"], emoji["unicode"], emoji["aliases"]) diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml index 8bddbef3562..a97cbd4d4b3 100644 --- a/app/views/errors/access_denied.html.haml +++ b/app/views/errors/access_denied.html.haml @@ -1,6 +1,5 @@ - content_for(:title, 'Access Denied') -%img{:alt => "GitLab Logo", - :src => image_path('logo.svg')} +%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') } %h1 403 .container diff --git a/app/views/errors/encoding.html.haml b/app/views/errors/encoding.html.haml index 064ff14ad2c..64f7f8e0836 100644 --- a/app/views/errors/encoding.html.haml +++ b/app/views/errors/encoding.html.haml @@ -1,6 +1,5 @@ - content_for(:title, 'Encoding Error') -%img{:alt => "GitLab Logo", - :src => image_path('logo.svg')} +%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') } %h1 500 .container diff --git a/app/views/errors/git_not_found.html.haml b/app/views/errors/git_not_found.html.haml index c5c12a410ac..d860957665b 100644 --- a/app/views/errors/git_not_found.html.haml +++ b/app/views/errors/git_not_found.html.haml @@ -1,6 +1,5 @@ - content_for(:title, 'Git Resource Not Found') -%img{:alt => "GitLab Logo", - :src => image_path('logo.svg')} +%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') } %h1 404 .container diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml index 50a54a93cb5..a0b9a632e22 100644 --- a/app/views/errors/not_found.html.haml +++ b/app/views/errors/not_found.html.haml @@ -1,6 +1,5 @@ - content_for(:title, 'Not Found') -%img{:alt => "GitLab Logo", - :src => image_path('logo.svg')} +%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') } %h1 404 .container diff --git a/app/views/errors/omniauth_error.html.haml b/app/views/errors/omniauth_error.html.haml index d91f1878cb6..72508b91134 100644 --- a/app/views/errors/omniauth_error.html.haml +++ b/app/views/errors/omniauth_error.html.haml @@ -1,6 +1,5 @@ - content_for(:title, 'Auth Error') -%img{:alt => "GitLab Logo", - :src => image_path('logo.svg')} +%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') } %h1 422 .container diff --git a/app/views/events/_event_issue.atom.haml b/app/views/events/_event_issue.atom.haml index 083c3936212..51585314a62 100644 --- a/app/views/events/_event_issue.atom.haml +++ b/app/views/events/_event_issue.atom.haml @@ -1,2 +1,2 @@ -%div{xmlns: "http://www.w3.org/1999/xhtml"} +%div{ xmlns: "http://www.w3.org/1999/xhtml" } = markdown(issue.description, pipeline: :atom, project: issue.project, author: issue.author) diff --git a/app/views/events/_event_merge_request.atom.haml b/app/views/events/_event_merge_request.atom.haml index d7e05600627..56fc8b86217 100644 --- a/app/views/events/_event_merge_request.atom.haml +++ b/app/views/events/_event_merge_request.atom.haml @@ -1,2 +1,2 @@ -%div{xmlns: "http://www.w3.org/1999/xhtml"} +%div{ xmlns: "http://www.w3.org/1999/xhtml" } = markdown(merge_request.description, pipeline: :atom, project: merge_request.project, author: merge_request.author) diff --git a/app/views/events/_event_note.atom.haml b/app/views/events/_event_note.atom.haml index 1154f982821..6fa2f9bd4db 100644 --- a/app/views/events/_event_note.atom.haml +++ b/app/views/events/_event_note.atom.haml @@ -1,2 +1,2 @@ -%div{xmlns: "http://www.w3.org/1999/xhtml"} +%div{ xmlns: "http://www.w3.org/1999/xhtml" } = markdown(note.note, pipeline: :atom, project: note.project, author: note.author) diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index 28bee1d0a33..f8f0bcb7608 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -1,4 +1,4 @@ -%div{xmlns: "http://www.w3.org/1999/xhtml"} +%div{ xmlns: "http://www.w3.org/1999/xhtml" } - event.commits.first(15).each do |commit| %p %strong= commit[:author][:name] diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index bba6e0d2c20..2fb6b5647da 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -1,6 +1,6 @@ .event-title %span.author_name= link_to_author event - %span{class: event.action_name} + %span{ class: event.action_name } - if event.target = event.action_name %strong diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml index aba64dd17d0..80cf2344fe1 100644 --- a/app/views/events/event/_created_project.html.haml +++ b/app/views/events/event/_created_project.html.haml @@ -1,6 +1,6 @@ .event-title %span.author_name= link_to_author event - %span{class: event.action_name} + %span{ class: event.action_name } = event_action_name(event) - if event.project diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 4e5d965ccbe..73cf6e87eb4 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -17,7 +17,7 @@ .pull-right .dropdown.inline - %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.light - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 5ea154c36b4..e3088848492 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -1,6 +1,6 @@ - if current_user .dropdown - %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" } = icon('globe') %span.light Visibility: - if params[:visibility_level].present? @@ -20,7 +20,7 @@ - if @tags.present? .dropdown - %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" } = icon('tags') %span.light Tags: - if params[:tag].present? diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index eb2eecab4e3..b74cc822295 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -1,8 +1,8 @@ -#modal-shortcuts.modal{tabindex: -1} +#modal-shortcuts.modal{ tabindex: -1 } .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h4 Keyboard Shortcuts %small diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 070ed90da6d..dd1df46792b 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -182,7 +182,7 @@ .nav-controls = text_field_tag 'sample', nil, class: 'form-control' .dropdown - %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span Sort by name = icon('chevron-down') %ul.dropdown-menu @@ -205,121 +205,121 @@ %h2#buttons Buttons .example - %button.btn.btn-default{:type => "button"} Default - %button.btn.btn-gray{:type => "button"} Gray - %button.btn.btn-primary{:type => "button"} Primary - %button.btn.btn-success{:type => "button"} Success - %button.btn.btn-info{:type => "button"} Info - %button.btn.btn-warning{:type => "button"} Warning - %button.btn.btn-danger{:type => "button"} Danger - %button.btn.btn-link{:type => "button"} Link + %button.btn.btn-default{ :type => "button" } Default + %button.btn.btn-gray{ :type => "button" } Gray + %button.btn.btn-primary{ :type => "button" } Primary + %button.btn.btn-success{ :type => "button" } Success + %button.btn.btn-info{ :type => "button" } Info + %button.btn.btn-warning{ :type => "button" } Warning + %button.btn.btn-danger{ :type => "button" } Danger + %button.btn.btn-link{ :type => "button" } Link %h2#dropdowns Dropdowns .example .clearfix .dropdown.inline.pull-left - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown = icon('chevron-down') %ul.dropdown-menu %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option .dropdown.inline.pull-right - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown = icon('chevron-down') %ul.dropdown-menu.dropdown-menu-align-right %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option .example %div .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown = icon('chevron-down') %ul.dropdown-menu.dropdown-menu-selectable %li - %a.is-active{href: "#"} + %a.is-active{ href: "#" } Dropdown Option .example %div .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-title %span Dropdown Title - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Filter results"} + %input.dropdown-input-field{ type: "search", placeholder: "Filter results" } = icon('search') .dropdown-content %ul %li - %a.is-active{href: "#"} + %a.is-active{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li.divider %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option .dropdown-footer %strong Tip: If an author is not a member of this project, you can still filter by his name while using the search field. .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown loading = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading .dropdown-title %span Dropdown Title - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Filter results"} + %input.dropdown-input-field{ type: "search", placeholder: "Filter results" } = icon('search') .dropdown-content %ul %li - %a.is-active{href: "#"} + %a.is-active{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li.divider %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option %li - %a{href: "#"} + %a{ href: "#" } Dropdown Option .dropdown-footer %strong Tip: @@ -330,21 +330,21 @@ .example %div .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: {toggle: 'dropdown' } } Dropdown user = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user .dropdown-title %span Dropdown Title - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Filter results"} + %input.dropdown-input-field{ type: "search", placeholder: "Filter results" } = icon('search') .dropdown-content %ul %li - %a.dropdown-menu-user-link.is-active{href: "#"} + %a.dropdown-menu-user-link.is-active{ href: "#" } = link_to_member_avatar(@user, size: 30) %strong.dropdown-menu-user-full-name = @user.name @@ -354,24 +354,24 @@ .example %div .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Dropdown page 2 = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user.dropdown-menu-paging.is-page-two .dropdown-page-one .dropdown-title - %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}} + %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } } = icon('arrow-left') %span Dropdown Title - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Filter results"} + %input.dropdown-input-field{ type: "search", placeholder: "Filter results" } = icon('search') .dropdown-content %ul %li - %a.dropdown-menu-user-link.is-active{href: "#"} + %a.dropdown-menu-user-link.is-active{ href: "#" } = link_to_member_avatar(@user, size: 30) %strong.dropdown-menu-user-full-name = @user.name @@ -379,13 +379,13 @@ = @user.to_reference .dropdown-page-two .dropdown-title - %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}} + %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } } = icon('arrow-left') %span Create label - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Name new label"} + %input.dropdown-input-field{ type: "search", placeholder: "Name new label" } .dropdown-content %button.btn.btn-primary Create @@ -393,16 +393,16 @@ .example %div .dropdown.inline - %button#js-project-dropdown.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button#js-project-dropdown.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } Projects = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-title %span Go to project - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input - %input.dropdown-input-field{type: "search", placeholder: "Filter results"} + %input.dropdown-input-field{ type: "search", placeholder: "Filter results" } = icon('search') .dropdown-content .dropdown-loading @@ -486,22 +486,22 @@ .example %form.form-horizontal .form-group - %label.col-sm-2.control-label{:for => "inputEmail3"} Email + %label.col-sm-2.control-label{ :for => "inputEmail3" } Email .col-sm-10 - %input#inputEmail3.form-control{:placeholder => "Email", :type => "email"}/ + %input#inputEmail3.form-control{ :placeholder => "Email", :type => "email" }/ .form-group - %label.col-sm-2.control-label{:for => "inputPassword3"} Password + %label.col-sm-2.control-label{ :for => "inputPassword3" } Password .col-sm-10 - %input#inputPassword3.form-control{:placeholder => "Password", :type => "password"}/ + %input#inputPassword3.form-control{ :placeholder => "Password", :type => "password" }/ .form-group .col-sm-offset-2.col-sm-10 .checkbox %label - %input{:type => "checkbox"}/ + %input{ :type => "checkbox" }/ Remember me .form-group .col-sm-offset-2.col-sm-10 - %button.btn.btn-default{:type => "submit"} Sign in + %button.btn.btn-default{ :type => "submit" } Sign in .lead Form when label rendered above input @@ -510,16 +510,16 @@ .example %form .form-group - %label{:for => "exampleInputEmail1"} Email address - %input#exampleInputEmail1.form-control{:placeholder => "Enter email", :type => "email"}/ + %label{ :for => "exampleInputEmail1" } Email address + %input#exampleInputEmail1.form-control{ :placeholder => "Enter email", :type => "email" }/ .form-group - %label{:for => "exampleInputPassword1"} Password - %input#exampleInputPassword1.form-control{:placeholder => "Password", :type => "password"}/ + %label{ :for => "exampleInputPassword1" } Password + %input#exampleInputPassword1.form-control{ :placeholder => "Password", :type => "password" }/ .checkbox %label - %input{:type => "checkbox"}/ + %input{ :type => "checkbox" }/ Remember me - %button.btn.btn-default{:type => "submit"} Sign in + %button.btn.btn-default{ :type => "submit" } Sign in %h2#file File %h4 diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index f12f9482a51..864c5c0ff95 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -21,7 +21,7 @@ %th Status %tbody - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %td = provider_project_link(provider, project.import_source) %td @@ -38,7 +38,7 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} + %tr{ id: "repo_#{repo.id}" } %td = provider_project_link(provider, repo.full_name) %td.import-target diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index ac09b71ae89..7f1b9ee7141 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -31,7 +31,7 @@ %th Status %tbody - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %td = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank' %td @@ -48,7 +48,7 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo.owner}___#{repo.slug}"} + %tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %td = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: "_blank" %td.import-target @@ -68,7 +68,7 @@ Import = icon('spinner spin', class: 'loading-icon') - @incompatible_repos.each do |repo| - %tr{id: "repo_#{repo.owner}___#{repo.slug}"} + %tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %td = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank' %td.import-target diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index c8a6fa1aa9e..97e5e51abe0 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -29,7 +29,7 @@ %th Status %tbody - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %td = project.import_source %td @@ -46,7 +46,7 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} + %tr{ id: "repo_#{repo.id}" } %td = repo.name %td.import-target diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index d31fc2e6adb..d5b88709a34 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -24,7 +24,7 @@ %th Status %tbody - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %td = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" %td @@ -41,7 +41,7 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo["id"]}"} + %tr{ id: "repo_#{repo["id"]}" } %td = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" %td.import-target diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml index 5d2f149cd5f..336becd229e 100644 --- a/app/views/import/google_code/new.html.haml +++ b/app/views/import/google_code/new.html.haml @@ -45,7 +45,7 @@ %p Upload GoogleCodeProjectHosting.json here: %p - %input{type: "file", name: "dump_file", id: "dump_file"} + %input{ type: "file", name: "dump_file", id: "dump_file" } %li %p Do you want to customize how Google Code email addresses and usernames are imported into GitLab? diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index e79f122940a..9f1507cade6 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -34,7 +34,7 @@ %th Status %tbody - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %td = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank" %td @@ -51,7 +51,7 @@ = project.human_import_status_name - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} + %tr{ id: "repo_#{repo.id}" } %td = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" %td.import-target @@ -61,7 +61,7 @@ Import = icon("spinner spin", class: "loading-icon") - @incompatible_repos.each do |repo| - %tr{id: "repo_#{repo.id}"} + %tr{ id: "repo_#{repo.id}" } %td = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" %td.import-target diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml index 750aed8f329..cefe0066a8f 100644 --- a/app/views/kaminari/gitlab/_page.html.haml +++ b/app/views/kaminari/gitlab/_page.html.haml @@ -6,5 +6,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li{class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}"} - = link_to page, url, {remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil} +%li{ class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}" } + = link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil } diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 3e488cf73b9..3096f0ee19e 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,27 +1,27 @@ - page_description brand_title unless page_description - site_name = "GitLab" -%head{prefix: "og: http://ogp.me/ns#"} - %meta{charset: "utf-8"} - %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} +%head{ prefix: "og: http://ogp.me/ns#" } + %meta{ charset: "utf-8" } + %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' } -# Open Graph - http://ogp.me/ - %meta{property: 'og:type', content: "object"} - %meta{property: 'og:site_name', content: site_name} - %meta{property: 'og:title', content: page_title} - %meta{property: 'og:description', content: page_description} - %meta{property: 'og:image', content: page_image} - %meta{property: 'og:url', content: request.base_url + request.fullpath} + %meta{ property: 'og:type', content: "object" } + %meta{ property: 'og:site_name', content: site_name } + %meta{ property: 'og:title', content: page_title } + %meta{ property: 'og:description', content: page_description } + %meta{ property: 'og:image', content: page_image } + %meta{ property: 'og:url', content: request.base_url + request.fullpath } -# Twitter Card - https://dev.twitter.com/cards/types/summary - %meta{property: 'twitter:card', content: "summary"} - %meta{property: 'twitter:title', content: page_title} - %meta{property: 'twitter:description', content: page_description} - %meta{property: 'twitter:image', content: page_image} + %meta{ property: 'twitter:card', content: "summary" } + %meta{ property: 'twitter:title', content: page_title } + %meta{ property: 'twitter:description', content: page_description } + %meta{ property: 'twitter:image', content: page_image } = page_card_meta_tags %title= page_title(site_name) - %meta{name: "description", content: page_description} + %meta{ name: "description", content: page_description } = favicon_link_tag 'favicon.ico' @@ -36,20 +36,20 @@ = csrf_meta_tags - unless browser.safari? - %meta{name: 'referrer', content: 'origin-when-cross-origin'} - %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} - %meta{name: 'theme-color', content: '#474D57'} + %meta{ name: 'referrer', content: 'origin-when-cross-origin' } + %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' } + %meta{ name: 'theme-color', content: '#474D57' } -# Apple Safari/iOS home screen icons = favicon_link_tag 'touch-icon-iphone.png', rel: 'apple-touch-icon' = favicon_link_tag 'touch-icon-ipad.png', rel: 'apple-touch-icon', sizes: '76x76' = favicon_link_tag 'touch-icon-iphone-retina.png', rel: 'apple-touch-icon', sizes: '120x120' = favicon_link_tag 'touch-icon-ipad-retina.png', rel: 'apple-touch-icon', sizes: '152x152' - %link{rel: 'mask-icon', href: image_path('logo.svg'), color: 'rgb(226, 67, 41)'} + %link{ rel: 'mask-icon', href: image_path('logo.svg'), color: 'rgb(226, 67, 41)' } -# Windows 8 pinned site tile - %meta{name: 'msapplication-TileImage', content: image_path('msapplication-tile.png')} - %meta{name: 'msapplication-TileColor', content: '#30353E'} + %meta{ name: 'msapplication-TileImage', content: image_path('msapplication-tile.png') } + %meta{ name: 'msapplication-TileColor', content: '#30353E' } = yield :meta_tags diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 8e65bd12c56..0e64ebd71b8 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -6,7 +6,7 @@ - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) } - if @project && @project.persisted? - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: namespace_project_issues_path(@project.namespace, @project), mr_path: namespace_project_merge_requests_path(@project.namespace, @project) } -.search.search-form{class: "#{'has-location-badge' if label.present?}"} +.search.search-form{ class: "#{'has-location-badge' if label.present?}" } = form_tag search_path, method: :get, class: 'navbar-form' do |f| .search-input-container - if label.present? @@ -44,4 +44,4 @@ = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref = button_tag 'Go' if ENV['RAILS_ENV'] == 'test' - .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } + .search-autocomplete-opts.hide{ :'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 6c2285fa2b6..935517d4913 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en", class: "#{page_class}" } = render "layouts/head" - %body{class: "#{user_application_theme}", data: {page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}"}} + %body{ class: "#{user_application_theme}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } = Gon::Base.render_data -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body. diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index a72ecec579f..3368a9beb29 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,7 +1,7 @@ !!! 5 %html.devise-layout-html = render "layouts/head" - %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page }} + %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page } } .page-wrap = Gon::Base.render_data = render "layouts/header/empty" diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml index 6bd427b02ac..7466423a934 100644 --- a/app/views/layouts/devise_empty.html.haml +++ b/app/views/layouts/devise_empty.html.haml @@ -1,5 +1,5 @@ !!! 5 -%html{ lang: "en"} +%html{ lang: "en" } = render "layouts/head" %body.ui_charcoal.login-page.application.navless = Gon::Base.render_data diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml index a16c31a9dd9..e1e1f9ae516 100644 --- a/app/views/layouts/devise_mailer.html.haml +++ b/app/views/layouts/devise_mailer.html.haml @@ -9,7 +9,7 @@ %tr %td %table#header - %td{valign: "top"} + %td{ valign: "top" } = image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark') %table#body diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index a3b925f6afd..6d9ec043590 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,7 +1,7 @@ !!! 5 -%html{ lang: "en"} +%html{ lang: "en" } %head - %meta{:content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport"} + %meta{ :content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport" } %title= yield(:title) :css body { diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index ad60db9427f..f4e0244596c 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -5,7 +5,7 @@ %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" } %span.sr-only Toggle navigation = icon('bars') - %button.navbar-toggle{type: 'button'} + %button.navbar-toggle{ type: 'button' } %span.sr-only Toggle navigation = icon('ellipsis-v') diff --git a/app/views/layouts/nav/_admin_settings.html.haml b/app/views/layouts/nav/_admin_settings.html.haml index 38e9b80d129..9de0e12a826 100644 --- a/app/views/layouts/nav/_admin_settings.html.haml +++ b/app/views/layouts/nav/_admin_settings.html.haml @@ -1,6 +1,6 @@ .controls .dropdown.admin-settings-dropdown - %a.dropdown-new.btn.btn-default{href: '#', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-default{ href: '#', 'data-toggle' => 'dropdown' } = icon('cog') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index 1579d8f1662..30feb6813b4 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -5,7 +5,7 @@ - if can_admin_group || can_edit .controls .dropdown.group-settings-dropdown - %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-default#group-settings-button{ href: '#', 'data-toggle' => 'dropdown' } = icon('cog') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index cc1571cbb4f..3c8c7b8f25e 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,7 +1,7 @@ - if current_user .controls .dropdown.project-settings-dropdown - %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-default#project-settings-button{ href: '#', 'data-toggle' => 'dropdown' } = icon('cog') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 12356c3641c..76268c1b705 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -1,6 +1,6 @@ -%html{lang: "en"} +%html{ lang: "en" } %head - %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} + %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" } %title GitLab = stylesheet_link_tag 'notify' @@ -8,7 +8,7 @@ %body .content = yield - .footer{style: "margin-top: 10px;"} + .footer{ style: "margin-top: 10px;" } %p — %br diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml index 4bf7c1f4d64..a744c4be9d6 100644 --- a/app/views/notify/build_fail_email.html.haml +++ b/app/views/notify/build_fail_email.html.haml @@ -1,5 +1,5 @@ - content_for :header do - %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + %h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" } GitLab (build failed) %h3 diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml index 252a5b7152c..8c2e6db1426 100644 --- a/app/views/notify/build_success_email.html.haml +++ b/app/views/notify/build_success_email.html.haml @@ -1,5 +1,5 @@ - content_for :header do - %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + %h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" } GitLab (build successful) %h3 diff --git a/app/views/notify/links/ci/builds/_build.html.haml b/app/views/notify/links/ci/builds/_build.html.haml index 38cd4e5e145..d35b3839171 100644 --- a/app/views/notify/links/ci/builds/_build.html.haml +++ b/app/views/notify/links/ci/builds/_build.html.haml @@ -1,2 +1,2 @@ -%a{href: pipeline_build_url(pipeline, build), style: "color:#3777b0;text-decoration:none;"} +%a{ href: pipeline_build_url(pipeline, build), style: "color:#3777b0;text-decoration:none;" } = build.name diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index 751f81db1cb..82c7fe229b8 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -1,9 +1,9 @@ -%html{lang: "en"} +%html{ lang: "en" } %head - %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ - %meta{content: "width=device-width, initial-scale=1", name: "viewport"}/ - %meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/ + %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ + %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ + %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ %title= message.subject :css /* CLIENT-SPECIFIC STYLES */ @@ -41,139 +41,139 @@ padding-right: 10px !important; } } - %body{style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"} - %table#body{border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;"} + %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" } %tbody %tr.line - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;"}   + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   %tr.header - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} - %img{alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55"}/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/ %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"} - %table.wrapper{border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"} - %table.content{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } %tbody %tr.alert - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;"} - %img{alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } + %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } Your pipeline has failed. %tr.spacer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }   %tr.section - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"} - %table.info{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;"} Project - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) - %a.muted{href: namespace_url, style: "color:#333333;text-decoration:none;"} + %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } = namespace_name \/ - %a.muted{href: project_url(@project), style: "color:#333333;text-decoration:none;"} + %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } = @project.name %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Branch - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } = @pipeline.ref %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Commit - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } = @pipeline.short_sha - if @merge_request in - %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"} + %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } = @merge_request.to_reference - .commit{style: "color:#5c5c5c;font-weight:300;"} + .commit{ style: "color:#5c5c5c;font-weight:300;" } = @pipeline.git_commit_message.truncate(50) %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Author - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - commit = @pipeline.commit - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.author - %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"} + %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } = commit.author.name - else %span = commit.author_name %tr.spacer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }   - failed = @pipeline.statuses.latest.failed %tr.pre-section - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" } Pipeline - %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} + %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } = "\##{@pipeline.id}" had = failed.size failed = "#{'build'.pluralize(failed.size)}." %tr.warning - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" } Logs may contain sensitive data. Please consider before forwarding this email. %tr.section - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;"} - %table.builds{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" } + %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" } %tbody - failed.each do |build| %tr.build-state - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;"} - %img{alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" } + %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" } = build.stage - %td{align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"} + %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %tr.build-log - if build.has_trace? - %td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"} - %pre{style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;"} + %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" } + %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" } = build.trace_html(last_lines: 10).html_safe - else - %td{colspan: "2"} + %td{ colspan: "2" } %tr.footer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} - %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ %div - %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications + %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications · - %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help + %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help %div You're receiving this email because of your account on = succeed "." do - %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host + %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index d64232ca8f2..6dddb3b6373 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -1,9 +1,9 @@ -%html{lang: "en"} +%html{ lang: "en" } %head - %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ - %meta{content: "width=device-width, initial-scale=1", name: "viewport"}/ - %meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/ + %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ + %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ + %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ %title= message.subject :css /* CLIENT-SPECIFIC STYLES */ @@ -41,114 +41,114 @@ padding-right: 10px !important; } } - %body{style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"} - %table#body{border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;"} + %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" } %tbody %tr.line - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;"}   + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   %tr.header - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} - %img{alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55"}/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/ %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"} - %table.wrapper{border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"} - %table.content{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } %tbody %tr.success - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;"} - %img{alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } + %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } Your pipeline has passed. %tr.spacer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }   %tr.section - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"} - %table.info{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;"} Project - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) - %a.muted{href: namespace_url, style: "color:#333333;text-decoration:none;"} + %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } = namespace_name \/ - %a.muted{href: project_url(@project), style: "color:#333333;text-decoration:none;"} + %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } = @project.name %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Branch - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } = @pipeline.ref %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Commit - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} - %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } = @pipeline.short_sha - if @merge_request in - %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"} + %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } = @merge_request.to_reference - .commit{style: "color:#5c5c5c;font-weight:300;"} + .commit{ style: "color:#5c5c5c;font-weight:300;" } = @pipeline.git_commit_message.truncate(50) %tr - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Author - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"} - %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - commit = @pipeline.commit - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} - %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar"}/ - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.author - %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"} + %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } = commit.author.name - else %span = commit.author_name %tr.spacer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }   %tr.success-message - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;"} + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" } - build_count = @pipeline.statuses.latest.size - stage_count = @pipeline.stages_count Pipeline - %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} + %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } = "\##{@pipeline.id}" successfully completed = "#{build_count} #{'build'.pluralize(build_count)}" in = "#{stage_count} #{'stage'.pluralize(stage_count)}." %tr.footer - %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} - %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ %div - %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications + %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications · - %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help + %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help %div You're receiving this email because of your account on = succeed "." do - %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host + %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index 25883de257c..36858fa6f34 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -29,7 +29,7 @@ %ul - @message.diffs.each do |diff| %li.file-stats - %a{href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" } + %a{ href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" } - if diff.deleted_file %span.deleted-file − @@ -54,8 +54,8 @@ %h4 Changes: - diff_files.each do |diff_file| - file_hash = hexdigest(diff_file.file_path) - %li{id: file_hash} - %a{href: @message.target_url + "##{file_hash}"}< + %li{ id: file_hash } + %a{ href: @message.target_url + "##{file_hash}" }< - if diff_file.deleted_file %strong< = diff_file.old_path diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml index f635acf96e2..5bf22aa94f1 100644 --- a/app/views/profiles/chat_names/new.html.haml +++ b/app/views/profiles/chat_names/new.html.haml @@ -1,5 +1,5 @@ %h3.page-title Authorization required -%main{:role => "main"} +%main{ :role => "main" } %p.h4 Authorize %strong.text-info= @chat_name_params[:chat_name] diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 2afa026847a..feadd863b00 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -1,7 +1,7 @@ - page_title 'Preferences' = render 'profiles/head' -= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f| += form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f| .col-lg-3.profile-settings-sidebar %h4.prepend-top-0 Application theme @@ -10,7 +10,7 @@ .col-lg-9.application-theme - Gitlab::Themes.each do |theme| = label_tag do - .preview{class: theme.css_class} + .preview{ class: theme.css_class } = f.radio_button :theme_id, theme.id = theme.name .col-sm-12 diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 859273ae94a..2385a90401e 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -30,7 +30,7 @@ The maximum file size allowed is 200KB. - if @user.avatar? %hr - = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-gray" + = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?" }, method: :delete, class: "btn btn-gray" %hr .row .col-lg-3.profile-settings-sidebar @@ -69,7 +69,7 @@ %span.help-block We also use email for avatar detection if no avatar is uploaded. .form-group = f.label :public_email, class: "label-light" - = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2" + = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), { include_blank: 'Do not show on profile' }, class: "select2" %span.help-block This email will be displayed on your public profile. .form-group = f.label :skype, class: "label-light" @@ -101,7 +101,7 @@ .modal-dialog .modal-content .modal-header - %button.close{:type => "button", :'data-dismiss' => "modal"} + %button.close{ :type => "button", :'data-dismiss' => "modal" } %span × %h4.modal-title @@ -116,5 +116,5 @@ %button.btn.btn-primary{ data: { method: "zoom", option: "-0.1" } } %span.fa.fa-search-minus .modal-footer - %button.btn.btn-primary.js-upload-user-avatar{:type => "button"} + %button.btn.btn-primary.js-upload-user-avatar{ :type => "button" } Set new profile picture diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 4f15f2997fb..0ea733cb978 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -9,7 +9,7 @@ = render 'shared/event_filter' - .content_list.project-activity{:"data-href" => activity_project_path(@project)} + .content_list.project-activity{ :"data-href" => activity_project_path(@project) } = spinner :javascript diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml index 899601b8a0a..c24a496486c 100644 --- a/app/views/projects/_bitbucket_import_modal.html.haml +++ b/app/views/projects/_bitbucket_import_modal.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3 Import projects from Bitbucket .modal-body To enable importing projects from Bitbucket, diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml index 06f7c684d2b..00aef66e1f8 100644 --- a/app/views/projects/_gitlab_import_modal.html.haml +++ b/app/views/projects/_gitlab_import_modal.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3 Import projects from GitLab.com .modal-body To enable importing projects from GitLab.com, diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 0d1f2b70018..1b9d87e9969 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -5,7 +5,7 @@ = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile') %h1.project-title = @project.name - %span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)} + %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) } = visibility_level_icon(@project.visibility_level, fw: false) .project-home-desc diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index eccf020f25a..085f79de785 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -15,20 +15,20 @@ %li.pull-right .toolbar-group - = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" }) - = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" }) - = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) - = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" }) - = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" }) - = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" }) - = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) + = markdown_toolbar_button({ icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" }) + = markdown_toolbar_button({ icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" }) + = markdown_toolbar_button({ icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) + = markdown_toolbar_button({ icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" }) + = markdown_toolbar_button({ icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" }) + = markdown_toolbar_button({ icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" }) + = markdown_toolbar_button({ icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) .toolbar-group %button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } } = icon("arrows-alt fw") .md-write-holder = yield - .md.md-preview-holder.js-md-preview.hide{class: (preview_class if defined?(preview_class))} + .md.md-preview-holder.js-md-preview.hide{ class: (preview_class if defined?(preview_class)) } - if defined?(referenced_users) && referenced_users .referenced-users.hide diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml index def493c56f5..9e49c93388a 100644 --- a/app/views/projects/artifacts/_tree_directory.html.haml +++ b/app/views/projects/artifacts/_tree_directory.html.haml @@ -1,6 +1,6 @@ - path_to_directory = browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: directory.path) -%tr.tree-item{ 'data-link' => path_to_directory} +%tr.tree-item{ 'data-link' => path_to_directory } %td.tree-item-file-name = tree_icon('folder', '755', directory.name) %span.str-truncated diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index f63802ac88b..23f54553014 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -36,7 +36,7 @@ %td.line-numbers - line_count = blame_group[:lines].count - (current_line...(current_line + line_count)).each do |i| - %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} + %a.diff-line-num{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i } = icon("link") = i \ diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml index 3d47351cec8..a486b2fe491 100644 --- a/app/views/projects/blob/_image.html.haml +++ b/app/views/projects/blob/_image.html.haml @@ -5,11 +5,11 @@ - # be wrong/strange if RawController modified the data. - blob.load_all_data!(@repository) - blob = sanitize_svg(blob) - %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}"} + %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}" } - else .nothing-here-block The SVG could not be displayed as it is too large, you can #{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank')} instead. - else - %img{src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}"} + %img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" } diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 84694203d7d..7f470b890ba 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title Create New Directory .modal-body = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index 2e1f32fd15e..db6662a95ac 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title Delete #{@blob.name} .modal-body diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index fbcd7d3a83d..61a7ffdd0ab 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title #{title} .modal-body = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal' do @@ -12,7 +12,7 @@ Attach a file by drag & drop or = link_to 'click to upload', '#', class: "markdown-selector" %br - .dropzone-alerts.alert.alert-danger.data{style: "display:none"} + .dropzone-alerts.alert.alert-danger.data{ style: "display:none" } = render 'shared/new_commit_form', placeholder: placeholder diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index a79ae53c780..538f8591f13 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -13,15 +13,15 @@ - case diff_view - when :inline %td.old_line.diff-line-num{ data: { linenumber: line_old } } - %a{href: "##{line_old}", data: { linenumber: line_old }} + %a{ href: "##{line_old}", data: { linenumber: line_old } } %td.new_line.diff-line-num{ data: { linenumber: line_new } } - %a{href: "##{line_new}", data: { linenumber: line_new }} + %a{ href: "##{line_new}", data: { linenumber: line_new } } = line_content - when :parallel - %td.old_line.diff-line-num{data: { linenumber: line_old }} + %td.old_line.diff-line-num{ data: { linenumber: line_old } } = link_to raw(line_old), "##{line_old}" = line_content - %td.new_line.diff-line-num{data: { linenumber: line_new }} + %td.new_line.diff-line-num{ data: { linenumber: line_new } } = link_to raw(line_new), "##{line_new}" = line_content diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml index 541dc96c45f..5cafb644b40 100644 --- a/app/views/projects/blob/preview.html.haml +++ b/app/views/projects/blob/preview.html.haml @@ -20,6 +20,6 @@ - else %td.old_line.diff-line-num %td.new_line.diff-line-num - %td.line_content{class: "#{line.type}"}= diff_line_content(line.text) + %td.line_content{ class: "#{line.type}" }= diff_line_content(line.text) - else .nothing-here-block No changes. diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index c2a5e725efc..ecd812312c0 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -12,7 +12,7 @@ = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } .dropdown.inline - %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.light = projects_sort_options_hash[@sort] = icon('chevron-down') diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index bd1d3947d7e..e63bdb38bd8 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -2,7 +2,7 @@ - if @error .alert.alert-danger - %button.close{ type: "button", "data-dismiss" => "alert"} × + %button.close{ type: "button", "data-dismiss" => "alert" } × = @error %h3.page-title New Branch diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index ce8b66b1945..0b3adcbe121 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -114,7 +114,7 @@ - if @build.pipeline.stages_count > 1 .dropdown.build-dropdown .title Stage - %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.stage-selection More = icon('chevron-down') %ul.dropdown-menu @@ -125,10 +125,10 @@ .builds-container - HasStatus::ORDERED_STATUSES.each do |build_status| - builds.select{|build| build.status == build_status}.each do |build| - .build-job{class: sidebar_build_class(build, @build), data: {stage: build.stage}} + .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } = link_to namespace_project_build_path(@project.namespace, @project, build) do = icon('arrow-right') - %span{class: "ci-status-icon-#{build.status}"} + %span{ class: "ci-status-icon-#{build.status}" } = ci_icon_for_status(build.status) %span - if build.name @@ -136,4 +136,4 @@ - else = build.id - if build.retried? - %i.fa.fa-refresh.has-tooltip{data: { container: 'body', placement: 'bottom' }, title: 'Build was retried'} + %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Build was retried' } diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index f5659be25f0..67de8699b2e 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -1,6 +1,6 @@ - if current_user .project-action-button.dropdown.inline - %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} + %a.btn.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" } = icon('plus') = icon("caret-down") %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index f1cb0201032..520113639b7 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -7,7 +7,7 @@ - coverage = local_assigns.fetch(:coverage, false) - allow_retry = local_assigns.fetch(:allow_retry, false) -%tr.build.commit{class: ('retried' if retried)} +%tr.build.commit{ class: ('retried' if retried) } %td.status = render "ci/status/badge", status: build.detailed_status(current_user) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 2f8f153f9a9..e67492a36d1 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -49,7 +49,7 @@ .stage-container.mini-pipeline-graph .dropdown.inline.build-content - %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}} + %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name) } } %span.has-tooltip{ class: status_klass } %span.mini-pipeline-graph-icon-container %span{ class: status_klass }= custom_icon(icon_status) @@ -81,7 +81,7 @@ .btn-group.inline - if actions.any? .btn-group - %a.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{type: 'button', 'data-toggle' => 'dropdown'} + %a.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{ type: 'button', 'data-toggle' => 'dropdown' } = custom_icon('icon_play') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right @@ -92,7 +92,7 @@ %span= build.name.humanize - if artifacts.present? .btn-group - %a.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{type: 'button', 'data-toggle' => 'dropdown'} + %a.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{ type: 'button', 'data-toggle' => 'dropdown' } = icon("download") = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml index 782f558e8b0..12e4280d344 100644 --- a/app/views/projects/commit/_change.html.haml +++ b/app/views/projects/commit/_change.html.haml @@ -6,11 +6,11 @@ - label = 'Cherry-pick' - target_label = 'Pick into branch' -.modal{id: "modal-#{type}-commit"} +.modal{ id: "modal-#{type}-commit" } .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title== #{label} this #{commit.change_type_title(current_user)} .modal-body = form_tag send("#{type.underscore}_namespace_project_commit_path", @project.namespace, @project, commit.id), method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do @@ -18,7 +18,7 @@ = label_tag 'target_branch', target_label, class: 'control-label' .col-sm-10 = hidden_field_tag :target_branch, @project.default_branch, id: 'target_branch' - = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "target_branch", selected: @project.default_branch, target_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false }}) + = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "target_branch", selected: @project.default_branch, target_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } }) - if can?(current_user, :push_code, @project) .js-create-merge-request-container diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index c08ed8f6c16..a9ee9230076 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -63,7 +63,7 @@ - if @commit.status .well-segment.pipeline-info - %div{class: "icon-container ci-status-icon-#{@commit.status}"} + %div{ class: "icon-container ci-status-icon-#{@commit.status}" } = ci_icon_for_status(@commit.status) Pipeline = link_to "##{@commit.pipelines.last.id}", pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "monospace" diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 9628cbd1634..e77f23c7fd8 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -28,12 +28,12 @@ = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } - if current_user && current_user.private_token .control - = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do + = link_to namespace_project_commits_path(@project.namespace, @project, @ref, { format: :atom, private_token: current_user.private_token }), title: "Commits Feed", class: 'btn' do = icon("rss") %ul.breadcrumb.repo-breadcrumb = commits_breadcrumbs - %div{id: dom_id(@project)} + %div{ id: dom_id(@project) } %ol#commits-list.list-unstyled.content_list = render 'commits', project: @project, ref: @ref = spinner diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index ef1b38d5e21..89e2e162b5b 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -7,7 +7,7 @@ #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data - .bordered-box.landing.content-block{"v-if" => "!isOverviewDialogDismissed"} + .bordered-box.landing.content-block{ "v-if" => "!isOverviewDialogDismissed" } = icon("times", class: "dismiss-icon", "@click" => "dismissOverviewDialog()") .row .col-sm-3.col-xs-12.svg-container @@ -20,19 +20,19 @@ = link_to "Read more", help_page_path('user/project/cycle_analytics'), target: '_blank', class: 'btn' = icon("spinner spin", "v-show" => "isLoading") - .wrapper{"v-show" => "!isLoading && !hasError"} + .wrapper{ "v-show" => "!isLoading && !hasError" } .panel.panel-default .panel-heading Pipeline Health .content-block .container-fluid .row - .col-sm-3.col-xs-12.column{"v-for" => "item in state.summary"} + .col-sm-3.col-xs-12.column{ "v-for" => "item in state.summary" } %h3.header {{item.value}} %p.text {{item.title}} .col-sm-3.col-xs-12.column .dropdown.inline.js-ca-dropdown - %button.dropdown-menu-toggle{"data-toggle" => "dropdown", :type => "button"} + %button.dropdown-menu-toggle{ "data-toggle" => "dropdown", :type => "button" } %span.dropdown-label Last 30 days %i.fa.fa-chevron-down %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml index 58a214bdbd1..a680b1ca017 100644 --- a/app/views/projects/deployments/_actions.haml +++ b/app/views/projects/deployments/_actions.haml @@ -3,7 +3,7 @@ - if actions.present? .inline .dropdown - %a.dropdown-new.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' } = custom_icon('icon_play') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml index 6120b2191dd..52a1ece7d60 100644 --- a/app/views/projects/diffs/_content.html.haml +++ b/app/views/projects/diffs/_content.html.haml @@ -10,7 +10,7 @@ .nothing-here-block This diff was suppressed by a .gitattributes entry. - elsif diff_file.collapsed? - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier)) - .nothing-here-block.diff-collapsed{data: { diff_for_path: url } } + .nothing-here-block.diff-collapsed{ data: { diff_for_path: url } } This diff is collapsed. %a.click-to-expand Click to expand it. diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 6c33d80becd..15df2edefc7 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,5 +1,5 @@ -.diff-file.file-holder{id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)} - .file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"} +.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) } + .file-title{ id: "file-path-#{hexdigest(diff_file.file_path)}" } = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" - unless diff_file.submodule? diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 81883182833..1bccaaf5273 100644 --- a/app/views/projects/diffs/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -7,16 +7,16 @@ - if diff.renamed_file || diff.new_file || diff.deleted_file .image %span.wrap - .frame{class: image_diff_class(diff)} - %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path, alt: diff.new_path} + .frame{ class: image_diff_class(diff) } + %img{ src: diff.deleted_file ? old_file_raw_path : file_raw_path, alt: diff.new_path } %p.image-info= "#{number_to_human_size file.size}" - else .image .two-up.view %span.wrap .frame.deleted - %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path))} - %img{src: old_file_raw_path, alt: diff.old_path} + %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path)) } + %img{ src: old_file_raw_path, alt: diff.old_path } %p.image-info.hide %span.meta-filesize= "#{number_to_human_size old_file.size}" | @@ -27,8 +27,8 @@ %span.meta-height %span.wrap .frame.added - %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path))} - %img{src: file_raw_path, alt: diff.new_path} + %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path)) } + %img{ src: file_raw_path, alt: diff.new_path } %p.image-info.hide %span.meta-filesize= "#{number_to_human_size file.size}" | @@ -41,10 +41,10 @@ .swipe.view.hide .swipe-frame .frame.deleted - %img{src: old_file_raw_path, alt: diff.old_path} + %img{ src: old_file_raw_path, alt: diff.old_path } .swipe-wrap .frame.added - %img{src: file_raw_path, alt: diff.new_path} + %img{ src: file_raw_path, alt: diff.new_path } %span.swipe-bar %span.top-handle %span.bottom-handle @@ -52,18 +52,18 @@ .onion-skin.view.hide .onion-skin-frame .frame.deleted - %img{src: old_file_raw_path, alt: diff.old_path} + %img{ src: old_file_raw_path, alt: diff.old_path } .frame.added - %img{src: file_raw_path, alt: diff.new_path} + %img{ src: file_raw_path, alt: diff.new_path } .controls .transparent .drag-track - .dragger{:style => "left: 0px;"} + .dragger{ :style => "left: 0px;" } .opaque .view-modes.hide %ul.view-modes-menu - %li.two-up{data: {mode: 'two-up'}} 2-up - %li.swipe{data: {mode: 'swipe'}} Swipe - %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin + %li.two-up{ data: { mode: 'two-up' } } 2-up + %li.swipe{ data: { mode: 'swipe' } } Swipe + %li.onion-skin{ data: { mode: 'onion-skin' } } Onion skin diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 16c96b66714..cd18ba2ed00 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -16,13 +16,13 @@ - if plain = link_text - else - %a{href: "##{line_code}", data: { linenumber: link_text }} + %a{ href: "##{line_code}", data: { linenumber: link_text } } %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } - link_text = type == "old" ? " " : line.new_pos - if plain = link_text - else - %a{href: "##{line_code}", data: { linenumber: link_text }} + %a{ href: "##{line_code}", data: { linenumber: link_text } } %td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }< - if email %pre= line.text diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index 401cbc16117..b087485aa17 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -13,9 +13,9 @@ - else - left_line_code = diff_file.line_code(left) - left_position = diff_file.position(left) - %td.old_line.diff-line-num{id: left_line_code, class: left.type, data: { linenumber: left.old_pos }} - %a{href: "##{left_line_code}" }= raw(left.old_pos) - %td.line_content.parallel.noteable_line{class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old')}= diff_line_content(left.text) + %td.old_line.diff-line-num{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } } + %a{ href: "##{left_line_code}" }= raw(left.old_pos) + %td.line_content.parallel.noteable_line{ class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old') }= diff_line_content(left.text) - else %td.old_line.diff-line-num.empty-cell %td.line_content.parallel @@ -26,9 +26,9 @@ - else - right_line_code = diff_file.line_code(right) - right_position = diff_file.position(right) - %td.new_line.diff-line-num{id: right_line_code, class: right.type, data: { linenumber: right.new_pos }} - %a{href: "##{right_line_code}" }= raw(right.new_pos) - %td.line_content.parallel.noteable_line{class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new')}= diff_line_content(right.text) + %td.new_line.diff-line-num{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } } + %a{ href: "##{right_line_code}" }= raw(right.new_pos) + %td.line_content.parallel.noteable_line{ class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new') }= diff_line_content(right.text) - else %td.old_line.diff-line-num.empty-cell %td.line_content.parallel diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index 66d6254aa1e..290f696d582 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -14,24 +14,23 @@ %li - if diff_file.deleted_file %span.deleted-file - %a{href: "##{file_hash}"} + %a{ href: "##{file_hash}" } %i.fa.fa-minus = diff_file.old_path - elsif diff_file.renamed_file %span.renamed-file - %a{href: "##{file_hash}"} + %a{ href: "##{file_hash}" } %i.fa.fa-minus = diff_file.old_path → = diff_file.new_path - elsif diff_file.new_file %span.new-file - %a{href: "##{file_hash}"} + %a{ href: "##{file_hash}" } %i.fa.fa-plus = diff_file.new_path - else %span.edit-file - %a{href: "##{file_hash}"} + %a{ href: "##{file_hash}" } %i.fa.fa-adjust = diff_file.new_path - diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 0c6f696f5b9..8c728eb0f6a 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -16,4 +16,4 @@ "css-class" => container_class, "commit-icon-svg" => custom_icon("icon_commit"), "terminal-icon-svg" => custom_icon("icon_terminal"), - "play-icon-svg" => custom_icon("icon_play")}} + "play-icon-svg" => custom_icon("icon_play") } } diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index a6726e509e0..431253c1299 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -6,7 +6,7 @@ = stylesheet_link_tag "xterm/xterm" = page_specific_javascript_tag("terminal/terminal_bundle.js") -%div{class: container_class} +%div{ class: container_class } .top-area .row .col-sm-6 @@ -18,5 +18,5 @@ .nav-controls = render 'projects/deployments/actions', deployment: @environment.last_deployment -.terminal-container{class: container_class} - #terminal{data:{project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws"}} +.terminal-container{ class: container_class } + #terminal{ data: { project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws" } } diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index b949795f582..4cdb44325b3 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -9,11 +9,11 @@ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do = @project.path %li.file-finder - %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path', autocomplete: 'off'} + %input#file_find.form-control.file-finder-input{ type: "text", placeholder: 'Find by path', autocomplete: 'off' } .tree-content-holder .table-holder - %table.table.files-slider{class: "table_#{@hex_path} tree-table table-striped" } + %table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" } %tbody = spinner nil, true diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 8657d73b881..6c8a6f051a9 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -9,7 +9,7 @@ spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' } .dropdown - %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.light sort: - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 9f444f076c0..6d7af1685fd 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -6,7 +6,7 @@ - stage = local_assigns.fetch(:stage, false) - coverage = local_assigns.fetch(:coverage, false) -%tr.generic_commit_status{class: ('retried' if retried)} +%tr.generic_commit_status{ class: ('retried' if retried) } %td.status = render 'ci/status/badge', status: generic_commit_status.detailed_status(current_user) diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml index 195f18afc76..bb0975a9535 100644 --- a/app/views/projects/graphs/ci/_build_times.haml +++ b/app/views/projects/graphs/ci/_build_times.haml @@ -2,7 +2,7 @@ %p.light Commit duration in minutes for last 30 commits - %canvas#build_timesChart{height: 200} + %canvas#build_timesChart{ height: 200 } :javascript var data = { diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml index 1fbf6ca2c1c..431657c4dcb 100644 --- a/app/views/projects/graphs/ci/_builds.haml +++ b/app/views/projects/graphs/ci/_builds.haml @@ -13,18 +13,18 @@ %p.light Builds for last week (#{date_from_to(Date.today - 7.days, Date.today)}) - %canvas#weekChart{height: 200} + %canvas#weekChart{ height: 200 } .prepend-top-default %p.light Builds for last month (#{date_from_to(Date.today - 30.days, Date.today)}) - %canvas#monthChart{height: 200} + %canvas#monthChart{ height: 200 } .prepend-top-default %p.light Builds for last year - %canvas#yearChart.padded{height: 250} + %canvas#yearChart.padded{ height: 250 } - [:week, :month, :year].each do |scope| :javascript diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index ac5f792d140..5ebb939a109 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -21,7 +21,7 @@ %h3#date_header.page-title %p.light Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits. - %input#brush_change{:type => "hidden"} + %input#brush_change{ :type => "hidden" } .graphs.row #contributors-master #contributors.clearfix diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index c80210d6ff4..bd46af339cf 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -43,7 +43,7 @@ = icon('clock-o') = issue.milestone.title - if issue.due_date - %span{class: "#{'cred' if issue.overdue?}"} + %span{ class: "#{'cred' if issue.overdue?}" }   = icon('calendar') = issue.due_date.to_s(:medium) diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 4589e112279..13e2150f997 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -1,6 +1,6 @@ - if can?(current_user, :push_code, @project) .pull-right - #new-branch.new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)} + #new-branch.new-branch{ 'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue) } = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), method: :post, class: 'btn btn-new btn-inverted btn-grouped has-tooltip available hide', title: @issue.to_branch_name do New branch diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 393ab1149bc..633701c6f40 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -5,10 +5,10 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('diff_notes/diff_notes_bundle.js') -.merge-request{'data-url' => merge_request_path(@merge_request)} +.merge-request{ 'data-url' => merge_request_path(@merge_request) } = render "projects/merge_requests/show/mr_title" - .merge-request-details.issuable-details{data: {id: @merge_request.project.id}} + .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } } = render "projects/merge_requests/show/mr_box" .append-bottom-default.mr-source-target.prepend-top-default - if @merge_request.open? @@ -73,7 +73,7 @@ Changes %span.badge= @merge_request.diff_size %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } - %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } + %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } %div .line-resolve-all{ "v-show" => "discussionCount > 0", ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml index 16789f68f70..b8b87dcdcaf 100644 --- a/app/views/projects/merge_requests/conflicts.html.haml +++ b/app/views/projects/merge_requests/conflicts.html.haml @@ -9,29 +9,29 @@ = render 'shared/issuable/sidebar', issuable: @merge_request -#conflicts{"v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json), +#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json), resolve_conflicts_path: resolve_conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request) } } - .loading{"v-if" => "isLoading"} + .loading{ "v-if" => "isLoading" } %i.fa.fa-spinner.fa-spin - .nothing-here-block{"v-if" => "hasError"} + .nothing-here-block{ "v-if" => "hasError" } {{conflictsData.errorMessage}} = render partial: "projects/merge_requests/conflicts/commit_stats" - .files-wrapper{"v-if" => "!isLoading && !hasError"} + .files-wrapper{ "v-if" => "!isLoading && !hasError" } .files - .diff-file.file-holder.conflict{"v-for" => "file in conflictsData.files"} + .diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" } .file-title - %i.fa.fa-fw{":class" => "file.iconClass"} + %i.fa.fa-fw{ ":class" => "file.iconClass" } %strong {{file.filePath}} = render partial: 'projects/merge_requests/conflicts/file_actions' .diff-content.diff-wrap-lines - .diff-wrap-lines.code.file-content.js-syntax-highlight{"v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } + .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } = render partial: "projects/merge_requests/conflicts/components/inline_conflict_lines" - .diff-wrap-lines.code.file-content.js-syntax-highlight{"v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } + .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } %parallel-conflict-lines{ ":file" => "file" } - %div{"v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'"} + %div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" } = render partial: "projects/merge_requests/conflicts/components/diff_file_editor" = render partial: "projects/merge_requests/conflicts/submit_form" diff --git a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml index 5ab3cd96163..964dc40a213 100644 --- a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml +++ b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml @@ -1,9 +1,9 @@ -.content-block.oneline-block.files-changed{"v-if" => "!isLoading && !hasError"} - .inline-parallel-buttons{"v-if" => "showDiffViewTypeSwitcher"} +.content-block.oneline-block.files-changed{ "v-if" => "!isLoading && !hasError" } + .inline-parallel-buttons{ "v-if" => "showDiffViewTypeSwitcher" } .btn-group - %button.btn{":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')"} + %button.btn{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" } Inline - %button.btn{":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')"} + %button.btn{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" } Side-by-side .js-toggle-container diff --git a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml index 05af57acf03..2595ce74ac0 100644 --- a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml +++ b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml @@ -1,5 +1,5 @@ .file-actions - .btn-group{"v-if" => "file.type === 'text'"} + .btn-group{ "v-if" => "file.type === 'text'" } %button.btn{ ":class" => "{ 'active': file.resolveMode == 'interactive' }", '@click' => "onClickResolveModeButton(file, 'interactive')", type: 'button' } @@ -8,5 +8,5 @@ '@click' => "onClickResolveModeButton(file, 'edit')", type: 'button' } Edit inline - %a.btn.view-file.btn-file-option{":href" => "file.blobPath"} + %a.btn.view-file.btn-file-option{ ":href" => "file.blobPath" } View file @{{conflictsData.shortCommitSha}} diff --git a/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml b/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml index 3c927d362c2..aff3fb82fa6 100644 --- a/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml +++ b/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml @@ -1,4 +1,4 @@ -%diff-file-editor{"inline-template" => "true", ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation"} +%diff-file-editor{ "inline-template" => "true", ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" } .diff-editor-wrap{ "v-show" => "file.showEditor" } .discard-changes-alert-wrap{ "v-if" => "file.promptDiscardConfirmation" } .discard-changes-alert diff --git a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml index d35c7bee163..d828cb6cf9e 100644 --- a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml +++ b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml @@ -1,14 +1,14 @@ -%inline-conflict-lines{ "inline-template" => "true", ":file" => "file"} +%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" } %table - %tr.line_holder.diff-inline{"v-for" => "line in file.inlineLines"} - %td.diff-line-num.new_line{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader"} + %tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" } + %td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" } %a {{line.new_line}} - %td.diff-line-num.old_line{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader"} + %td.diff-line-num.old_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" } %a {{line.old_line}} - %td.line_content{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader", "v-html" => "line.richText"} - %td.diff-line-num.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"} - %td.diff-line-num.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"} - %td.line_content.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"} - %strong{"v-html" => "line.richText"} + %td.line_content{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader", "v-html" => "line.richText" } + %td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" } + %td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" } + %td.line_content.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" } + %strong{ "v-html" => "line.richText" } %button.btn{ "@click" => "handleSelected(file, line.id, line.section)" } {{line.buttonTitle}} diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index e5bbb42ea70..ec76c6a5417 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3 Check out, review, and merge locally .modal-body %p diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index ed23d06ee5e..683cb8a5a27 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -4,7 +4,7 @@ %div - if @merge_request.description.present? - .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''} + .description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' } .wiki = preserve do = markdown_field(@merge_request, :description) diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml index d32bb0fd656..ac4a03220b9 100644 --- a/app/views/projects/merge_requests/show/_versions.html.haml +++ b/app/views/projects/merge_requests/show/_versions.html.haml @@ -13,7 +13,7 @@ .dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-title %span Version: - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times', class: 'dropdown-menu-close-icon') .dropdown-content %ul @@ -43,7 +43,7 @@ .dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-title %span Compared with: - %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}} + %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times', class: 'dropdown-menu-close-icon') .dropdown-content %ul diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 5bc417d1760..c80dc33058d 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -17,7 +17,7 @@ - # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API .mr-widget-heading - %w[success skipped canceled failed running pending].each do |status| - .ci_widget{class: "ci-#{status} ci-status-icon-#{status}", style: "display:none"} + .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" } = ci_icon_for_status(status) %span CI build @@ -32,11 +32,11 @@ = icon("spinner spin") Checking CI status for #{@merge_request.diff_head_commit.short_id}… - .ci_widget.ci-not_found{style: "display:none"} + .ci_widget.ci-not_found{ style: "display:none" } = icon("times-circle") Could not find CI status for #{@merge_request.diff_head_commit.short_id}. - .ci_widget.ci-error{style: "display:none"} + .ci_widget.ci-error{ style: "display:none" } = icon("times-circle") Could not connect to the CI server. Please check your settings and try again. diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 46b402545cd..39731668a61 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -23,5 +23,5 @@ .note-form-actions.clearfix = f.submit 'Comment', class: "btn btn-nr btn-create append-right-10 comment-btn js-comment-button" = yield(:note_actions) - %a.btn.btn-cancel.js-note-discard{role: "button", data: {cancel_text: "Cancel"}} + %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Cancel" } } Discard draft diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 3aecb04fb50..eb869ea85bf 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -5,7 +5,7 @@ %li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} } .timeline-entry-inner .timeline-icon - %a{href: user_path(note.author)} + %a{ href: user_path(note.author) } = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40' .timeline-content .note-header @@ -61,7 +61,7 @@ = icon('pencil', class: 'link-highlight') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do = icon('trash-o', class: 'danger-highlight') - .note-body{class: note_editable ? 'js-task-list-container' : ''} + .note-body{ class: note_editable ? 'js-task-list-container' : '' } .note-text.md = preserve do = note.redacted_note_html diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index b00ba2d5307..ca76f13ef5e 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -23,7 +23,7 @@ .info-well - if @commit.status .well-segment.pipeline-info - %div{class: "icon-container ci-status-icon-#{@commit.status}"} + %div{ class: "icon-container ci-status-icon-#{@commit.status}" } = ci_icon_for_status(@commit.status) = pluralize @pipeline.statuses.count(:id), "build" - if @pipeline.ref diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 7adac9b39e2..4bb3d4d35fb 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -5,23 +5,23 @@ %div{ class: container_class } .top-area %ul.nav-links - %li{class: ('active' if @scope.nil?)}> + %li{ class: ('active' if @scope.nil?) }> = link_to project_pipelines_path(@project) do All %span.badge.js-totalbuilds-count = number_with_delimiter(@pipelines_count) - %li{class: ('active' if @scope == 'running')}> + %li{ class: ('active' if @scope == 'running') }> = link_to project_pipelines_path(@project, scope: :running) do Running %span.badge.js-running-count = number_with_delimiter(@running_or_pending_count) - %li{class: ('active' if @scope == 'branches')}> + %li{ class: ('active' if @scope == 'branches') }> = link_to project_pipelines_path(@project, scope: :branches) do Branches - %li{class: ('active' if @scope == 'tags')}> + %li{ class: ('active' if @scope == 'tags') }> = link_to project_pipelines_path(@project, scope: :tags) do Tags diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 6e58e5a0c78..7036b8a5ccc 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -1,4 +1,4 @@ -%li.runner{id: dom_id(runner)} +%li.runner{ id: dom_id(runner) } %h4 = runner_status_icon(runner) %span.monospace diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index 6919b210a00..d9d392fa02f 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -10,11 +10,10 @@ - status_groups.each do |group_name, grouped_statuses| - if grouped_statuses.one? - status = grouped_statuses.first - %li.build{ 'id' => "ci-badge-#{group_name}" } + %li.build{ 'id' => "ci-badge-#{group_name}" } .curve = render 'ci/status/graph_badge', subject: status - else - %li.build{ 'id' => "ci-badge-#{group_name}" } + %li.build{ 'id' => "ci-badge-#{group_name}" } .curve = render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses - diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml index 65e5f31e86c..c4cb9ab50d0 100644 --- a/app/views/projects/stage/_in_stage_group.html.haml +++ b/app/views/projects/stage/_in_stage_group.html.haml @@ -1,6 +1,6 @@ - group_status = CommitStatus.where(id: subject).status %button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } } - %span{class: "ci-status-icon ci-status-icon-#{group_status}"} + %span{ class: "ci-status-icon ci-status-icon-#{group_status}" } = ci_icon_for_status(group_status) %span.ci-status-text = name diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml index 1684e02fbad..28e1c060875 100644 --- a/app/views/projects/stage/_stage.html.haml +++ b/app/views/projects/stage/_stage.html.haml @@ -1,13 +1,13 @@ %tr - %th{colspan: 10} + %th{ colspan: 10 } %strong %a{ name: stage.name } - %span{class: "ci-status-link ci-status-icon-#{stage.status}"} + %span{ class: "ci-status-link ci-status-icon-#{stage.status}" } = ci_icon_for_status(stage.status)   = stage.name.titleize = render stage.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true = render stage.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true %tr - %td{colspan: 10} + %td{ colspan: 10 }   diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 9b87696addf..160d4c7a223 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -2,7 +2,7 @@ - if @error .alert.alert-danger - %button.close{ type: "button", "data-dismiss" => "alert"} × + %button.close{ type: "button", "data-dismiss" => "alert" } × = @error %h3.page-title diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index a019578bdf8..038a960bd0c 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,6 +1,6 @@ .tree-content-holder .table-holder - %table.table#tree-slider{class: "table_#{@hex_path} tree-table" } + %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } %thead %tr %th Name diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 1c5f8b3928b..259207a6dfd 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -15,11 +15,11 @@ - if current_user %li - if !on_top_of_branch? - %span.btn.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} + %span.btn.add-to-tree.disabled.has-tooltip{ title: "You can only add files when you are on a branch", data: { container: 'body' } } = icon('plus') - else %span.dropdown - %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown" } = icon('plus') %ul.dropdown-menu - if can_edit_tree? @@ -28,11 +28,11 @@ = icon('pencil fw') New file %li - = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do = icon('file fw') Upload file %li - = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do = icon('folder fw') New directory - elsif can?(current_user, :fork_project, @project) diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/index.html.haml index 39303700131..cf7ae0b489f 100644 --- a/app/views/projects/variables/index.html.haml +++ b/app/views/projects/variables/index.html.haml @@ -15,4 +15,4 @@ No variables found, add one with the form above. - else = render "table" - %button.btn.btn-info.js-btn-toggle-reveal-values{"data-status" => 'hidden'} Reveal Values + %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index baa03a84559..c74f53b4c39 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -5,7 +5,7 @@ .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title New Wiki Page .modal-body %form.new-wiki-page diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index 2c378231237..8cbecb725b5 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -1,70 +1,70 @@ %ul.nav-links.search-filter - if @project - %li{class: ("active" if @scope == 'blobs')} + %li{ class: ("active" if @scope == 'blobs') } = link_to search_filter_path(scope: 'blobs') do Code %span.badge = @search_results.blobs_count - %li{class: ("active" if @scope == 'issues')} + %li{ class: ("active" if @scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{class: ("active" if @scope == 'merge_requests')} + %li{ class: ("active" if @scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{class: ("active" if @scope == 'milestones')} + %li{ class: ("active" if @scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge = @search_results.milestones_count - %li{class: ("active" if @scope == 'notes')} + %li{ class: ("active" if @scope == 'notes') } = link_to search_filter_path(scope: 'notes') do Comments %span.badge = @search_results.notes_count - %li{class: ("active" if @scope == 'wiki_blobs')} + %li{ class: ("active" if @scope == 'wiki_blobs') } = link_to search_filter_path(scope: 'wiki_blobs') do Wiki %span.badge = @search_results.wiki_blobs_count - %li{class: ("active" if @scope == 'commits')} + %li{ class: ("active" if @scope == 'commits') } = link_to search_filter_path(scope: 'commits') do Commits %span.badge = @search_results.commits_count - elsif @show_snippets - %li{class: ("active" if @scope == 'snippet_blobs')} + %li{ class: ("active" if @scope == 'snippet_blobs') } = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do Snippet Contents %span.badge = @search_results.snippet_blobs_count - %li{class: ("active" if @scope == 'snippet_titles')} + %li{ class: ("active" if @scope == 'snippet_titles') } = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do Titles and Filenames %span.badge = @search_results.snippet_titles_count - else - %li{class: ("active" if @scope == 'projects')} + %li{ class: ("active" if @scope == 'projects') } = link_to search_filter_path(scope: 'projects') do Projects %span.badge = @search_results.projects_count - %li{class: ("active" if @scope == 'issues')} + %li{ class: ("active" if @scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{class: ("active" if @scope == 'merge_requests')} + %li{ class: ("active" if @scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{class: ("active" if @scope == 'milestones')} + %li{ class: ("active" if @scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index b315cd06f35..96b75440309 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -7,7 +7,7 @@ %span = enabled_project_button(project, enabled_protocol) - else - %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', data: { toggle: 'dropdown' }} + %a#clone-dropdown.clone-dropdown-btn.btn{ href: '#', data: { toggle: 'dropdown' } } %span = default_clone_protocol.upcase = icon('caret-down') diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index b0fc60573f7..e7cb93b17a7 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -1,8 +1,8 @@ -#modal-confirm-danger.modal{tabindex: -1} +#modal-confirm-danger.modal{ tabindex: -1 } .modal-dialog .modal-content .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title Confirmation required diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index e26693bf5b9..8d64cb5d698 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -9,8 +9,8 @@ - offset = defined?(first_line_number) ? first_line_number : 1 - i = index + offset -# We're not using `link_to` because it is too slow once we get to thousands of lines. - %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i} + %a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i } = link_icon = i - .blob-content{data: {blob_id: blob.id}} + .blob-content{ data: { blob_id: blob.id } } = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index db324d8868e..f11f4471a9d 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -4,7 +4,7 @@ - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] -%li{id: label_css_id, data: { id: label.id } } +%li{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label .visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown @@ -56,7 +56,7 @@ = icon('spinner spin', class: 'label-subscribe-button-loading') .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } - %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span Subscribe = icon('chevron-down') %ul.dropdown-menu diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml index 186ed4a7c8b..39294fe1a09 100644 --- a/app/views/shared/_milestones_filter.html.haml +++ b/app/views/shared/_milestones_filter.html.haml @@ -2,17 +2,17 @@ - counts = milestone_counts(@project.milestones) %ul.nav-links - %li{class: milestone_class_for_state(params[:state], 'opened', true)}> + %li{ class: milestone_class_for_state(params[:state], 'opened', true) }> = link_to milestones_filter_path(state: 'opened') do Open - if @project %span.badge #{counts[:opened]} - %li{class: milestone_class_for_state(params[:state], 'closed')}> + %li{ class: milestone_class_for_state(params[:state], 'closed') }> = link_to milestones_filter_path(state: 'closed') do Closed - if @project %span.badge #{counts[:closed]} - %li{class: milestone_class_for_state(params[:state], 'all')}> + %li{ class: milestone_class_for_state(params[:state], 'all') }> = link_to milestones_filter_path(state: 'all') do All - if @project diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index ede3c7090d7..0ce0d759e86 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.inline.prepend-left-10 - %button.dropdown-toggle{type: 'button', data: {toggle: 'dropdown'}} + %button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } } %span.light - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 8164f61797c..f9a7aa4e29b 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -21,7 +21,7 @@ = icon('users') = number_with_delimiter(group.users.count) - %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)} + %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) } = visibility_level_icon(group.visibility_level, fw: false) .avatar-container.s40 diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index d6c67c523e2..b42eaabb111 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -31,7 +31,7 @@ - if issuable_filter_present? .filter-item.inline.reset-filters - %a{href: page_filter_path(without: issuable_filter_params)} Reset filters + %a{ href: page_filter_path(without: issuable_filter_params) } Reset filters .pull-right - if boards_page @@ -56,9 +56,9 @@ = dropdown_tag("Status", options: { toggle_class: "js-issue-status", title: "Change status", dropdown_class: "dropdown-menu-status dropdown-menu-selectable", data: { field_name: "update[state_event]" } } ) do %ul %li - %a{href: "#", data: {id: "reopen"}} Open + %a{ href: "#", data: { id: "reopen" } } Open %li - %a{href: "#", data: {id: "close"}} Closed + %a{ href: "#", data: {id: "close" } } Closed .filter-item.inline = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee js-filter-submit js-filter-bulk-update", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable", placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } }) @@ -70,9 +70,9 @@ = dropdown_tag("Subscription", options: { toggle_class: "js-subscription-event", title: "Change subscription", dropdown_class: "dropdown-menu-selectable", data: { field_name: "update[subscription_event]" } } ) do %ul %li - %a{href: "#", data: {id: "subscribe"}} Subscribe + %a{ href: "#", data: { id: "subscribe" } } Subscribe %li - %a{href: "#", data: {id: "unsubscribe"}} Unsubscribe + %a{ href: "#", data: { id: "unsubscribe" } } Unsubscribe = hidden_field_tag 'update[issuable_ids]', [] = hidden_field_tag :state_event, params[:state_event] diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index bdb00bfa33c..dcc1f3ba676 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -58,7 +58,7 @@ as resolved. Ask someone with sufficient rights to resolve the them. - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) -.row-content-block{class: (is_footer ? "footer-block" : "middle-block")} +.row-content-block{ class: (is_footer ? "footer-block" : "middle-block") } - if issuable.new_record? = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml index 22b5a6aa11b..93c7fa0c7d6 100644 --- a/app/views/shared/issuable/_label_dropdown.html.haml +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -19,7 +19,7 @@ = hidden_field_tag data_options[:field_name], use_id ? label.try(:id) : label.try(:title), id: nil .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} + %button.dropdown-menu-toggle.js-label-select.js-multiselect{ class: classes.join(' '), type: "button", data: dropdown_data } %span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) } = multi_label_name(selected, "Labels") = icon('chevron-down') diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml index a8f01026ca5..9a8529c6cbb 100644 --- a/app/views/shared/issuable/_label_page_default.html.haml +++ b/app/views/shared/issuable/_label_page_default.html.haml @@ -17,7 +17,7 @@ %ul.dropdown-footer-list - if can?(current_user, :admin_label, @project) %li - %a.dropdown-toggle-page{href: "#"} + %a.dropdown-toggle-page{ href: "#" } Create new label %li = link_to namespace_project_labels_path(@project.namespace, @project), :"data-is-link" => true do diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml index d938edf4dbd..1154316c03f 100644 --- a/app/views/shared/issuable/_nav.html.haml +++ b/app/views/shared/issuable/_nav.html.haml @@ -3,23 +3,23 @@ - issuables = @issues || @merge_requests %ul.nav-links.issues-state-filters - %li{class: ("active" if params[:state] == 'opened')}> + %li{ class: ("active" if params[:state] == 'opened') }> = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do #{issuables_state_counter_text(type, :opened)} - if type == :merge_requests - %li{class: ("active" if params[:state] == 'merged')}> + %li{ class: ("active" if params[:state] == 'merged') }> = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do #{issuables_state_counter_text(type, :merged)} - %li{class: ("active" if params[:state] == 'closed')}> + %li{ class: ("active" if params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do #{issuables_state_counter_text(type, :closed)} - else - %li{class: ("active" if params[:state] == 'closed')}> + %li{ class: ("active" if params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do #{issuables_state_counter_text(type, :closed)} - %li{class: ("active" if params[:state] == 'all')}> + %li{ class: ("active" if params[:state] == 'all') }> = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do #{issuables_state_counter_text(type, :all)} diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index 2ec19f74be6..171da899937 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -14,7 +14,7 @@ = link_to_member(@project, participant, name: false, size: 24) - if participants_extra > 0 .participants-more - %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}} + %a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } } + #{participants_extra} more :javascript IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row}; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 9fe1be5a597..bc57d48ae7c 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -17,9 +17,9 @@ Add todo = icon('spin spinner', class: 'hidden js-issuable-todo-loading') - = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| + = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f| .block.assignee - .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)} + .sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) } - if issuable.assignee = link_to_member(@project, issuable.assignee, size: 24) - else @@ -54,7 +54,7 @@ = icon('clock-o') %span - if issuable.milestone - %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}} + %span.has-tooltip{ title: milestone_remaining_days(issuable.milestone), data: { container: 'body', html: 1, placement: 'left' } } = issuable.milestone.title - else None @@ -129,8 +129,8 @@ - selected_labels.each do |label| = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}} - %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?)} + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project) } } + %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) } = multi_label_name(selected_labels, "Labels") = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable @@ -141,7 +141,7 @@ = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user - subscribed = issuable.subscribed?(current_user, @project) - .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} + .block.light.subscription{ data: { url: toggle_subscription_path(issuable) } } .sidebar-collapsed-icon = icon('rss') %span.issuable-header-text.hide-collapsed.pull-left @@ -157,7 +157,7 @@ .cross-project-reference.hide-collapsed %span Reference: - %cite{title: project_ref} + %cite{ title: project_ref } = project_ref = clipboard_button(clipboard_text: project_ref) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 3dccfb147bf..3200aacf542 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -1,7 +1,7 @@ - dashboard = local_assigns[:dashboard] - custom_dom_id = dom_id(@project ? milestone : milestone.milestones.first) -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } +%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } .row .col-sm-6 %strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml index a82fc95df84..b5c0a7fd6d4 100644 --- a/app/views/shared/notifications/_custom_notifications.html.haml +++ b/app/views/shared/notifications/_custom_notifications.html.haml @@ -18,7 +18,7 @@ %p Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out = succeed "." do - %a{ href: "http://docs.gitlab.com/ce/workflow/notifications.html", target: "_blank"} notification emails + %a{ href: "http://docs.gitlab.com/ce/workflow/notifications.html", target: "_blank" } notification emails .col-lg-8 - NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index| - field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]" diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 264391fe84f..4a27965754d 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -25,7 +25,7 @@ %span = icon('star') = number_with_delimiter(project.star_count) - %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)} + %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) } = visibility_level_icon(project.visibility_level, fw: true) .title diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml index dcdba01aee9..ad5c0c2d8c8 100644 --- a/app/views/shared/snippets/_blob.html.haml +++ b/app/views/shared/snippets/_blob.html.haml @@ -1,6 +1,6 @@ - unless @snippet.content.empty? - if markup?(@snippet.file_name) - %textarea.markdown-snippet-copy.blob-content{data: {blob_id: @snippet.id}} + %textarea.markdown-snippet-copy.blob-content{ data: { blob_id: @snippet.id } } = @snippet.content .file-content.wiki - if gitlab_markdown?(@snippet.file_name) diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml index 94d4dd4fa7d..92151176fce 100644 --- a/app/views/sherlock/file_samples/show.html.haml +++ b/app/views/sherlock/file_samples/show.html.haml @@ -41,7 +41,7 @@ %th= t('sherlock.percent') %tbody - @file_sample.line_samples.each_with_index do |sample, index| - %tr{class: sample.majority_of?(@file_sample.duration) ? 'slow' : ''} + %tr{ class: sample.majority_of?(@file_sample.duration) ? 'slow' : '' } %td= index + 1 %td= sample.events %td diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml index 7073c0f4d90..5a447f791dc 100644 --- a/app/views/sherlock/queries/_general.html.haml +++ b/app/views/sherlock/queries/_general.html.haml @@ -26,7 +26,7 @@ .panel.panel-default .panel-heading .pull-right - %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button} + %button.js-clipboard-trigger.btn.btn-xs{ title: t('sherlock.copy_to_clipboard'), type: :button } %i.fa.fa-clipboard %pre.hidden = @query.formatted_query @@ -41,7 +41,7 @@ .panel.panel-default .panel-heading .pull-right - %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button} + %button.js-clipboard-trigger.btn.btn-xs{ title: t('sherlock.copy_to_clipboard'), type: :button } %i.fa.fa-clipboard %pre.hidden = @query.explain diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml index da969c02765..bc05659dfa8 100644 --- a/app/views/sherlock/transactions/index.html.haml +++ b/app/views/sherlock/transactions/index.html.haml @@ -28,7 +28,7 @@ %tr %td= trans.type %td - %span{title: trans.path} + %span{ title: trans.path } = truncate(trans.path, length: 70) %td = trans.duration.round(2) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 1e0752bd3c3..fb25eed4f37 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -18,11 +18,11 @@ - elsif current_user - if @user.abuse_report %button.btn.btn-danger{ title: 'Already reported for abuse', - data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }} + data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } } = icon('exclamation-circle') - else = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray', - title: 'Report abuse', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('exclamation-circle') - if current_user = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do @@ -101,12 +101,12 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - .user-calendar{data: {href: user_calendar_path}} + .user-calendar{ data: { href: user_calendar_path } } %h4.center.light %i.fa.fa-spinner.fa-spin .user-calendar-activities - .content_list{ data: {href: user_path} } + .content_list{ data: { href: user_path } } = spinner #groups.tab-pane -- cgit v1.2.1 From e0f765ae440da26f1aa9b888dbff80e32b3be5ce Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Tue, 27 Dec 2016 08:19:01 +0530 Subject: HAMLLint: Fix `SpaceInsideHashAttributes` offence --- app/views/dashboard/todos/_todo.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index b084685b388..9849b31d7e2 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -1,4 +1,4 @@ -%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data:{url: todo_target_path(todo) } } +%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } } = author_avatar(todo, size: 40) .todo-item.todo-block -- cgit v1.2.1 From a54c9b7f20a30f63eb1feb66b21c2d9eea0bf559 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Sat, 31 Dec 2016 13:40:35 +0530 Subject: HAMLLint: Fix `TrailingWhitespace` offense post-rebase --- app/views/projects/tags/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index a8d462132c6..fad3c5c2173 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -26,7 +26,7 @@ .btn-container.controls-item-full = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o - + - if @tag.message.present? %pre.wrap = strip_gpg_signature(@tag.message) -- cgit v1.2.1 From f79680c53fa93c04e27f8630448d89799cde4289 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Sat, 31 Dec 2016 13:41:30 +0530 Subject: HAMLLint: Fix `ImplicitDiv` offence post-rebase --- app/views/projects/tags/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 92a023a5c85..e2f132f7742 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -2,7 +2,7 @@ - page_title "Tags" = render "projects/commits/head" -%div.flex-list{ class: container_class } +.flex-list{ class: container_class } .top-area.flex-row .nav-text.row-main-content Tags give the ability to mark specific points in history as being important -- cgit v1.2.1 From f4aab83a36433eddc00db468b86583eacd4636c9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 2 Jan 2017 15:30:17 +0000 Subject: Increases pipeline graph drowdown width in order to prevent strange position on chrome on ubuntu Adds MR ID to changelog entry --- app/assets/stylesheets/pages/pipelines.scss | 4 ++-- .../26218-rety-button-pipeline-builds-name-drodown-broken.yml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index c370b6247b0..578003f6d36 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -630,8 +630,8 @@ .grouped-pipeline-dropdown { padding: 0; - width: 191px; - min-width: 191px; + width: 195px; + min-width: 195px; left: auto; right: -195px; top: -4px; diff --git a/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml b/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml new file mode 100644 index 00000000000..ef8581b6fb3 --- /dev/null +++ b/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml @@ -0,0 +1,4 @@ +--- +title: Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu +merge_request: 8399 +author: -- cgit v1.2.1