summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml16
-rw-r--r--CHANGELOG33
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock10
-rw-r--r--LICENSE2
-rw-r--r--app/assets/javascripts/api.js.coffee67
-rw-r--r--app/assets/javascripts/calendar.js.coffee8
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee6
-rw-r--r--app/assets/javascripts/notes.js.coffee10
-rw-r--r--app/assets/javascripts/project_users_select.js.coffee56
-rw-r--r--app/assets/javascripts/protected_branches.js.coffee6
-rw-r--r--app/assets/javascripts/sidebar.js.coffee11
-rw-r--r--app/assets/javascripts/users_select.js.coffee71
-rw-r--r--app/assets/stylesheets/base/gl_bootstrap.scss1
-rw-r--r--app/assets/stylesheets/base/gl_variables.scss6
-rw-r--r--app/assets/stylesheets/generic/filters.scss55
-rw-r--r--app/assets/stylesheets/generic/mobile.scss7
-rw-r--r--app/assets/stylesheets/generic/selects.scss18
-rw-r--r--app/assets/stylesheets/pages/issues.scss35
-rw-r--r--app/assets/stylesheets/pages/notes.scss9
-rw-r--r--app/assets/stylesheets/pages/projects.scss48
-rw-r--r--app/controllers/application_controller.rb12
-rw-r--r--app/controllers/autocomplete_controller.rb30
-rw-r--r--app/finders/issuable_finder.rb8
-rw-r--r--app/helpers/application_helper.rb10
-rw-r--r--app/helpers/blob_helper.rb8
-rw-r--r--app/helpers/diff_helper.rb4
-rw-r--r--app/helpers/gitlab_markdown_helper.rb7
-rw-r--r--app/helpers/icons_helper.rb44
-rw-r--r--app/helpers/labels_helper.rb4
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/helpers/milestones_helper.rb11
-rw-r--r--app/helpers/projects_helper.rb34
-rw-r--r--app/helpers/selects_helper.rb29
-rw-r--r--app/helpers/submodule_helper.rb7
-rw-r--r--app/helpers/tree_helper.rb9
-rw-r--r--app/mailers/notify.rb2
-rw-r--r--app/models/project_services/asana_service.rb2
-rw-r--r--app/models/project_services/buildbox_service.rb26
-rw-r--r--app/services/issues/update_service.rb4
-rw-r--r--app/services/merge_requests/update_service.rb4
-rw-r--r--app/services/projects/transfer_service.rb3
-rw-r--r--app/views/admin/projects/show.html.haml7
-rw-r--r--app/views/admin/services/_form.html.haml2
-rw-r--r--app/views/groups/group_members/_new_group_member.html.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml3
-rw-r--r--app/views/import/github/status.html.haml3
-rw-r--r--app/views/import/gitlab/status.html.haml3
-rw-r--r--app/views/import/gitorious/status.html.haml3
-rw-r--r--app/views/layouts/doorkeeper/admin.html.haml22
-rw-r--r--app/views/layouts/doorkeeper/application.html.haml15
-rw-r--r--app/views/projects/_home_panel.html.haml35
-rw-r--r--app/views/projects/_issuable_form.html.haml4
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml6
-rw-r--r--app/views/projects/commits/_head.html.haml4
-rw-r--r--app/views/projects/edit.html.haml13
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/issues/_issue_context.html.haml7
-rw-r--r--app/views/projects/issues/index.html.haml10
-rw-r--r--app/views/projects/issues/update.js.haml2
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_context.html.haml7
-rw-r--r--app/views/projects/merge_requests/show/_state_widget.html.haml2
-rw-r--r--app/views/projects/merge_requests/update.js.haml2
-rw-r--r--app/views/projects/new.html.haml13
-rw-r--r--app/views/projects/project_members/_new_project_member.html.haml2
-rw-r--r--app/views/projects/protected_branches/index.html.haml2
-rw-r--r--app/views/projects/repositories/_download_archive.html.haml4
-rw-r--r--app/views/projects/services/_form.html.haml2
-rw-r--r--app/views/projects/show.html.haml11
-rw-r--r--app/views/projects/tree/_blob_item.html.haml2
-rw-r--r--app/views/projects/tree/_submodule_item.html.haml2
-rw-r--r--app/views/projects/tree/_tree_item.html.haml2
-rw-r--r--app/views/shared/_issuable_filter.html.haml137
-rw-r--r--app/views/users/calendar.html.haml2
-rw-r--r--app/views/users/show.html.haml3
-rw-r--r--app/workers/emails_on_push_worker.rb10
-rw-r--r--config/gitlab.yml.example34
-rw-r--r--config/initializers/acts_as_taggable_on_patch.rb131
-rw-r--r--config/routes.rb5
-rw-r--r--doc/markdown/markdown.md9
-rw-r--r--doc/permissions/permissions.md7
-rw-r--r--doc/ssh/README.md2
-rw-r--r--docker/Dockerfile6
-rw-r--r--docker/README.md38
-rw-r--r--docker/data/Dockerfile8
-rw-r--r--docker/data/assets/gitlab.rb (renamed from docker/assets/gitlab.rb)0
-rw-r--r--features/dashboard/issues.feature2
-rw-r--r--features/dashboard/merge_requests.feature2
-rw-r--r--features/groups.feature8
-rw-r--r--features/project/commits/commits.feature3
-rw-r--r--features/project/issues/filter_labels.feature6
-rw-r--r--features/project/merge_requests.feature7
-rw-r--r--features/project/star.feature2
-rw-r--r--features/steps/dashboard/issues.rb17
-rw-r--r--features/steps/dashboard/merge_requests.rb17
-rw-r--r--features/steps/groups.rb26
-rw-r--r--features/steps/project/commits/commits.rb12
-rw-r--r--features/steps/project/issues/filter_labels.rb23
-rw-r--r--features/steps/project/merge_requests.rb14
-rw-r--r--features/steps/project/star.rb8
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/projects.rb7
-rw-r--r--lib/api/users.rb4
-rw-r--r--lib/backup/manager.rb15
-rw-r--r--lib/gitlab/git_access.rb2
-rw-r--r--lib/gitlab/markdown.rb34
-rw-r--r--lib/gitlab/popen.rb2
-rw-r--r--lib/gitlab/satellite/merge_action.rb2
-rw-r--r--lib/gitlab/satellite/satellite.rb6
-rw-r--r--lib/gitlab/theme.rb2
-rw-r--r--lib/tasks/gitlab/test.rake1
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb51
-rw-r--r--spec/features/issues_spec.rb4
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb30
-rw-r--r--spec/helpers/icons_helper_spec.rb109
-rw-r--r--spec/models/project_services/asana_service_spec.rb (renamed from spec/models/asana_service_spec.rb)0
-rw-r--r--spec/models/project_services/buildbox_service_spec.rb12
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/select2_helper.rb4
-rw-r--r--spec/support/test_env.rb8
122 files changed, 981 insertions, 794 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 53ca2ca2191..7290d627d24 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -355,7 +355,7 @@ Style/MultilineBlockChain:
Style/MultilineBlockLayout:
Description: 'Ensures newlines after multiline block do statements.'
- Enabled: false
+ Enabled: true
Style/MultilineIfThen:
Description: 'Do not use then for multi-line if/unless.'
@@ -390,7 +390,7 @@ Style/NegatedWhile:
Style/NestedTernaryOperator:
Description: 'Use one expression per branch in a ternary operator.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary'
- Enabled: false
+ Enabled: true
Style/Next:
Description: 'Use `next` to skip iteration instead of a condition at the end.'
@@ -400,17 +400,17 @@ Style/Next:
Style/NilComparison:
Description: 'Prefer x.nil? to x == nil.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
- Enabled: false
+ Enabled: true
Style/NonNilCheck:
Description: 'Checks for redundant nil checks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
- Enabled: false
+ Enabled: true
Style/Not:
Description: 'Use ! instead of not.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
- Enabled: false
+ Enabled: true
Style/NumericLiterals:
Description: >-
@@ -424,7 +424,7 @@ Style/OneLineConditional:
Favor the ternary operator(?:) over
if/then/else/end constructs.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
- Enabled: false
+ Enabled: true
Style/OpMethod:
Description: 'When defining binary operators, name the argument other.'
@@ -436,7 +436,7 @@ Style/ParenthesesAroundCondition:
Don't use parentheses around the condition of an
if/unless/while.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if'
- Enabled: false
+ Enabled: true
Style/PercentLiteralDelimiters:
Description: 'Use `%`-literal delimiters consistently'
@@ -480,7 +480,7 @@ Style/RedundantException:
Style/RedundantReturn:
Description: "Don't use return where it's not required."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
- Enabled: false
+ Enabled: true
Style/RedundantSelf:
Description: "Don't use self where it's not needed."
diff --git a/CHANGELOG b/CHANGELOG
index d4a1346c481..37054da46b8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,11 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.10.0 (unreleased)
- - Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- - Fix "Import projects from" button to show the correct instructions (Stan Hu)
+ - Fix broken side-by-side diff view on merge request page (Stan Hu)
+ - Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu)
+ - Allow HTML tags in Markdown input
+ - Fix code unfold not working on Compare commits page (Stan Hu)
- Fix dots in Wiki slugs causing errors (Stan Hu)
- - Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
- Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg)
- Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu)
- Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu)
@@ -13,6 +14,7 @@ v 7.10.0 (unreleased)
- extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger)
- Fix a link in the patch update guide
- Add a service to support external wikis (Hannes Rosenögger)
+ - Omit the "email patches" link and fix plain diff view for merge commits
- List new commits for newly pushed branch in activity view.
- Add sidetiq gem dependency to match EE
- Add changelog, license and contribution guide links to project sidebar.
@@ -20,11 +22,13 @@ v 7.10.0 (unreleased)
- Fix alignment of navbar toggle button (Cody Mize)
- Fix checkbox rendering for nested task lists
- Identical look of selectboxes in UI
+ - Upgrade the gitlab_git gem to version 7.1.3
- Move "Import existing repository by URL" option to button.
- Improve error message when save profile has error.
- Passing the name of pushed ref to CI service (requires GitLab CI 7.9+)
- Add location field to user profile
- Fix print view for markdown files and wiki pages
+ - Fix errors when deleting old backups
- Improve GitLab performance when working with git repositories
- Add tag message and last commit to tag hook (Kamil Trzciński)
- Restrict permissions on backup files
@@ -32,12 +36,33 @@ v 7.10.0 (unreleased)
- Add ability to unlink connected accounts
- Replace commits calendar with faster contribution calendar that includes issues and merge requests
- Add inifinite scroll to user page activity
- - Don't show commit comment button when user is not signed in.
- Don't include system notes in issue/MR comment count.
- Don't mark merge request as updated when merge status relative to target branch changes.
- Link note avatar to user.
- Make Git-over-SSH errors more descriptive.
+ - Fix EmailsOnPush.
+ - Refactor issue filtering
+ - AJAX selectbox for issue assignee and author filters
+ - Fix issue with missing options in issue filtering dropdown if selected one
+ - Prevent holding Control-Enter or Command-Enter from posting comment multiple times.
+ - Prevent note form from being cleared when submitting failed.
+ - Improve file icons rendering on tree (Sullivan Sénéchal)
+ - API: Add pagination to project events
+
+v 7.9.0
- Send EmailsOnPush email when branch or tag is created or deleted.
+ - Faster merge request processing for large repository
+ - Prevent doubling AJAX request with each commit visit via Turbolink
+ - Prevent unnecessary doubling of js events on import pages and user calendar
+
+v 7.9.1
+ - Include missing events and fix save functionality in admin service template settings form (Stan Hu)
+ - Fix "Import projects from" button to show the correct instructions (Stan Hu)
+ - Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
+ - Fix for LDAP with commas in DN
+ - Fix missing events and in admin Slack service template settings form (Stan Hu)
+ - Don't show commit comment button when user is not signed in.
+ - Downgrade gemnasium-gitlab-service gem
v 7.9.0
- Add HipChat integration documentation (Stan Hu)
diff --git a/Gemfile b/Gemfile
index e7f75055f3f..e767aec5053 100644
--- a/Gemfile
+++ b/Gemfile
@@ -39,7 +39,7 @@ gem "browser"
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.1.2'
+gem "gitlab_git", '~> 7.1.3'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack'
@@ -115,7 +115,7 @@ end
gem "state_machine"
# Issue tags
-gem "acts-as-taggable-on"
+gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'slim'
diff --git a/Gemfile.lock b/Gemfile.lock
index 7da4d3c3583..ed8663b358b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -33,8 +33,8 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
- acts-as-taggable-on (2.4.1)
- rails (>= 3, < 5)
+ acts-as-taggable-on (3.5.0)
+ activerecord (>= 3.2, < 5)
addressable (2.3.5)
annotate (2.6.0)
activerecord (>= 2.3.0)
@@ -212,7 +212,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
- gitlab_git (7.1.2)
+ gitlab_git (7.1.3)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
@@ -662,7 +662,7 @@ PLATFORMS
DEPENDENCIES
RedCloth
ace-rails-ap
- acts-as-taggable-on
+ acts-as-taggable-on (~> 3.4)
addressable
annotate (~> 2.6.0.beta2)
asana (~> 0.0.6)
@@ -703,7 +703,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.rc2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
- gitlab_git (~> 7.1.2)
+ gitlab_git (~> 7.1.3)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
diff --git a/LICENSE b/LICENSE
index d11b8730bf1..d8cb29f3638 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2014 GitLab B.V.
+Copyright (c) 2011-2015 GitLab B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 27d04e7cac6..9e5d594c861 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -1,57 +1,7 @@
@Api =
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
- users_path: "/api/:version/users.json"
- user_path: "/api/:version/users/:id.json"
- notes_path: "/api/:version/projects/:id/notes.json"
namespaces_path: "/api/:version/namespaces.json"
- project_users_path: "/api/:version/projects/:id/users.json"
-
- # Get 20 (depends on api) recent notes
- # and sort the ascending from oldest to newest
- notes: (project_id, callback) ->
- url = Api.buildUrl(Api.notes_path)
- url = url.replace(':id', project_id)
-
- $.ajax(
- url: url,
- data:
- private_token: gon.api_token
- gfm: true
- recent: true
- dataType: "json"
- ).done (notes) ->
- notes.sort (a, b) ->
- return a.id - b.id
- callback(notes)
-
- user: (user_id, callback) ->
- url = Api.buildUrl(Api.user_path)
- url = url.replace(':id', user_id)
-
- $.ajax(
- url: url
- data:
- private_token: gon.api_token
- dataType: "json"
- ).done (user) ->
- callback(user)
-
- # Return users list. Filtered by query
- # Only active users retrieved
- users: (query, callback) ->
- url = Api.buildUrl(Api.users_path)
-
- $.ajax(
- url: url
- data:
- private_token: gon.api_token
- search: query
- per_page: 20
- active: true
- dataType: "json"
- ).done (users) ->
- callback(users)
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
@@ -80,23 +30,6 @@
).done (groups) ->
callback(groups)
- # Return project users list. Filtered by query
- # Only active users retrieved
- projectUsers: (project_id, query, callback) ->
- url = Api.buildUrl(Api.project_users_path)
- url = url.replace(':id', project_id)
-
- $.ajax(
- url: url
- data:
- private_token: gon.api_token
- search: query
- per_page: 20
- active: true
- dataType: "json"
- ).done (users) ->
- callback(users)
-
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path)
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
index d08ef9361a6..44d75bd694f 100644
--- a/app/assets/javascripts/calendar.js.coffee
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -1,4 +1,4 @@
-class @calendar
+class @Calendar
options =
month: "short"
day: "numeric"
@@ -20,9 +20,9 @@ class @calendar
position: "top"
legend: [
0
- 1
- 4
- 7
+ 10
+ 20
+ 30
]
legendCellPadding: 3
onClick: (date, count) ->
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index deabaf8a784..821712f7512 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -28,6 +28,8 @@ class Dispatcher
new Milestone()
when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
+ when 'projects:compare:show'
+ new Diff()
when 'projects:issues:new','projects:issues:edit'
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation()
@@ -115,6 +117,8 @@ class Dispatcher
new Project()
new ProjectAvatar()
switch path[1]
+ when 'compare'
+ shortcut_handler = new ShortcutsNavigation()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
@@ -123,7 +127,7 @@ class Dispatcher
when 'show'
new ProjectShow()
when 'issues', 'merge_requests'
- new ProjectUsersSelect()
+ new UsersSelect()
when 'wikis'
new Wikis()
shortcut_handler = new ShortcutsNavigation()
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index c366c98cf54..6dfe10f0006 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -37,7 +37,8 @@ class @Notes
$(document).on "click", ".js-note-attachment-delete", @removeAttachment
# reset main target form after submit
- $(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
+ $(document).on "ajax:complete", ".js-main-target-form", @reenableTargetFormSubmitButton
+ $(document).on "ajax:success", ".js-main-target-form", @resetMainTargetForm
# update the file name when an attachment is selected
$(document).on "change", ".js-note-attachment-input", @updateFormAttachment
@@ -57,6 +58,7 @@ class @Notes
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
# Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
$(document).on('keydown', @notes_forms, (e) ->
+ return if e.originalEvent.repeat
if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
$(@).parents('form').submit()
)
@@ -70,6 +72,7 @@ class @Notes
$(document).off "click", ".js-note-delete"
$(document).off "click", ".js-note-attachment-delete"
$(document).off "ajax:complete", ".js-main-target-form"
+ $(document).off "ajax:success", ".js-main-target-form"
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
@@ -169,6 +172,11 @@ class @Notes
form.find(".js-note-text").data("autosave").reset()
+ reenableTargetFormSubmitButton: ->
+ form = $(".js-main-target-form")
+
+ form.find(".js-note-text").trigger "input"
+
###
Shows the main form and does some setup on it.
diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee
deleted file mode 100644
index 80ab1a61ab9..00000000000
--- a/app/assets/javascripts/project_users_select.js.coffee
+++ /dev/null
@@ -1,56 +0,0 @@
-class @ProjectUsersSelect
- constructor: ->
- $('.ajax-project-users-select').each (i, select) =>
- project_id = $(select).data('project-id') || $('body').data('project-id')
-
- $(select).select2
- placeholder: $(select).data('placeholder') || "Search for a user"
- multiple: $(select).hasClass('multiselect')
- minimumInputLength: 0
- query: (query) ->
- Api.projectUsers project_id, query.term, (users) ->
- data = { results: users }
-
- if query.term.length == 0
- nullUser = {
- name: 'Unassigned',
- avatar: null,
- username: 'none',
- id: -1
- }
-
- data.results.unshift(nullUser)
-
- query.callback(data)
-
- initSelection: (element, callback) ->
- id = $(element).val()
- if id != "" && id != "-1"
- Api.user(id, callback)
-
-
- formatResult: (args...) =>
- @formatResult(args...)
- formatSelection: (args...) =>
- @formatSelection(args...)
- dropdownCssClass: "ajax-project-users-dropdown"
- dropdownAutoWidth: true
- escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
- m
-
- formatResult: (user) ->
- if user.avatar_url
- avatar = user.avatar_url
- else
- avatar = gon.default_avatar_url
-
- avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
-
- "<div class='user-result'>
- #{avatarMarkup}
- <div class='user-name'>#{user.name}</div>
- <div class='user-username'>#{user.username}</div>
- </div>"
-
- formatSelection: (user) ->
- user.name
diff --git a/app/assets/javascripts/protected_branches.js.coffee b/app/assets/javascripts/protected_branches.js.coffee
index 691fd4f10d8..5753c9d4e72 100644
--- a/app/assets/javascripts/protected_branches.js.coffee
+++ b/app/assets/javascripts/protected_branches.js.coffee
@@ -1,5 +1,5 @@
$ ->
- $(":checkbox").change ->
+ $(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name")
if name == "developers_can_push"
id = $(this).val()
@@ -14,8 +14,8 @@ $ ->
developers_can_push: checked
success: ->
- new Flash("Branch updated.", "notice")
- location.reload true
+ row = $(e.target)
+ row.closest('tr').effect('highlight')
error: ->
new Flash("Failed to update branch!", "alert")
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index 7febcba0e94..2e3f5608257 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -3,12 +3,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) ->
collapsed = 'page-sidebar-collapsed'
expanded = 'page-sidebar-expanded'
- if $('.page-with-sidebar').hasClass(collapsed)
- $('.page-with-sidebar').removeClass(collapsed).addClass(expanded)
- $('.toggle-nav-collapse i').removeClass('fa-angle-right').addClass('fa-angle-left')
- $.cookie("collapsed_nav", "false", { path: '/' })
- else
- $('.page-with-sidebar').removeClass(expanded).addClass(collapsed)
- $('.toggle-nav-collapse i').removeClass('fa-angle-left').addClass('fa-angle-right')
- $.cookie("collapsed_nav", "true", { path: '/' })
+ $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
+ $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
+ $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
)
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 9eee7406511..f464067686e 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -1,20 +1,48 @@
class @UsersSelect
constructor: ->
+ @usersPath = "/autocomplete/users.json"
+ @userPath = "/autocomplete/users/:id.json"
+
$('.ajax-users-select').each (i, select) =>
+ @projectId = $(select).data('project-id')
+ @groupId = $(select).data('group-id')
+ showNullUser = $(select).data('null-user')
+ showAnyUser = $(select).data('any-user')
+
$(select).select2
placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
- query: (query) ->
- Api.users query.term, (users) ->
+ query: (query) =>
+ @users query.term, (users) =>
data = { results: users }
+
+ if query.term.length == 0
+ anyUser = {
+ name: 'Any',
+ avatar: null,
+ username: 'none',
+ id: null
+ }
+
+ nullUser = {
+ name: 'Unassigned',
+ avatar: null,
+ username: 'none',
+ id: 0
+ }
+
+ if showNullUser
+ data.results.unshift(nullUser)
+ if showAnyUser
+ data.results.unshift(anyUser)
+
query.callback(data)
- initSelection: (element, callback) ->
+ initSelection: (element, callback) =>
id = $(element).val()
- if id isnt ""
- Api.user(id, callback)
-
+ if id != "" && id != "0"
+ @user(id, callback)
formatResult: (args...) =>
@formatResult(args...)
@@ -38,3 +66,34 @@ class @UsersSelect
formatSelection: (user) ->
user.name
+
+ user: (user_id, callback) =>
+ url = @buildUrl(@userPath)
+ url = url.replace(':id', user_id)
+
+ $.ajax(
+ url: url
+ dataType: "json"
+ ).done (user) ->
+ callback(user)
+
+ # Return users list. Filtered by query
+ # Only active users retrieved
+ users: (query, callback) =>
+ url = @buildUrl(@usersPath)
+
+ $.ajax(
+ url: url
+ data:
+ search: query
+ per_page: 20
+ active: true
+ project_id: @projectId
+ group_id: @groupId
+ dataType: "json"
+ ).done (users) ->
+ callback(users)
+
+ buildUrl: (url) ->
+ url = gon.relative_url_root + url if gon.relative_url_root?
+ return url
diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss
index e176cce5c69..62a3eade5c7 100644
--- a/app/assets/stylesheets/base/gl_bootstrap.scss
+++ b/app/assets/stylesheets/base/gl_bootstrap.scss
@@ -182,6 +182,7 @@
.panel-heading {
padding: 6px 15px;
font-size: 13px;
+ font-weight: normal;
a {
color: #777;
}
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss
index 17b5622d74a..56f4c794e1b 100644
--- a/app/assets/stylesheets/base/gl_variables.scss
+++ b/app/assets/stylesheets/base/gl_variables.scss
@@ -115,6 +115,12 @@ $panel-default-border: $border-color;
$panel-default-heading-bg: $background-color;
+//== Wells
+//
+//##
+
+$well-bg: #F9F9F9;
+$well-border: #EEE;
//== Code
//
diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/generic/filters.scss
new file mode 100644
index 00000000000..bd93a79722d
--- /dev/null
+++ b/app/assets/stylesheets/generic/filters.scss
@@ -0,0 +1,55 @@
+.filter-item {
+ margin-right: 15px;
+}
+
+.issues-state-filters {
+ li.active a {
+ border-color: #DDD !important;
+
+ &, &:hover, &:active, &.active {
+ background: #f5f5f5 !important;
+ border-bottom: 1px solid #f5f5f5 !important;
+ }
+ }
+}
+
+.issues-details-filters {
+ font-size: 13px;
+ background: #f5f5f5;
+ margin: -10px 0;
+ padding: 10px 15px;
+ margin-top: -15px;
+ border-left: 1px solid #DDD;
+ border-right: 1px solid #DDD;
+
+ .btn {
+ font-size: 13px;
+ }
+}
+
+@media (min-width: 800px) {
+ .issues-filters,
+ .issues_bulk_update {
+ select, .select2-container {
+ width: 120px !important;
+ display: inline-block;
+ }
+ }
+}
+
+@media (min-width: 1200px) {
+ .issues-filters,
+ .issues_bulk_update {
+ select, .select2-container {
+ width: 150px !important;
+ display: inline-block;
+ }
+ }
+}
+
+.issues-filters,
+.issues_bulk_update {
+ .select2-container .select2-choice {
+ color: #444 !important;
+ }
+}
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
index 1b0e056216f..71a1fc4493f 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -24,13 +24,6 @@
display: none !important;
}
- .project-home-panel {
- .star-fork-buttons {
- padding-top: 10px;
- padding-right: 15px;
- }
- }
-
.project-home-links {
display: none;
}
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
index 7557f411111..d8e0dc028d1 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -2,20 +2,25 @@
.select2-container, .select2-container.select2-drop-above {
.select2-choice {
background: #FFF;
- border-color: #CCC;
+ border-color: #DDD;
+ height: 34px;
padding: 6px 14px;
+ font-size: 14px;
line-height: 1.42857143;
- height: auto;
+
+ @include border-radius(4px);
.select2-arrow {
background: #FFF;
- border-left: 1px solid #DDD;
+ border-left: none;
+ padding-top: 3px;
}
}
}
.select2-container-multi .select2-choices {
- @include border-radius(4px)
+ @include border-radius(4px);
+ border-color: #CCC;
}
.select2-container-multi .select2-choices .select2-search-field input {
@@ -28,6 +33,7 @@
.select2-drop-active {
border: 1px solid #BBB !important;
margin-top: 4px;
+ font-size: 13px;
&.select2-drop-above {
margin-bottom: 8px;
@@ -106,3 +112,7 @@
font-weight: bolder;
}
}
+
+.ajax-users-dropdown {
+ min-width: 225px !important;
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 6c1dd4f7e9f..cd86a9be8b2 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -41,12 +41,9 @@
}
.check-all-holder {
- height: 36px;
+ line-height: 36px;
float: left;
- margin-right: 12px;
- padding: 6px 15px;
- border: 1px solid #ccc;
- @include border-radius(4px);
+ margin-right: 15px;
}
.issues_content {
@@ -59,30 +56,6 @@
}
}
-@media (min-width: 800px) {
- .issues_bulk_update {
- select, .select2-container {
- width: 120px !important;
- display: inline-block;
- }
- }
-}
-
-@media (min-width: 1200px) {
- .issues_bulk_update {
- select, .select2-container {
- width: 160px !important;
- display: inline-block;
- }
- }
-}
-
-.issues_bulk_update {
- .select2-container .select2-choice {
- color: #444 !important;
- }
-}
-
.participants {
margin-bottom: 20px;
}
@@ -120,12 +93,12 @@ form.edit-issue {
}
&.closed {
- background: #F5f5f5;
+ background: #F9F9F9;
border-color: #E5E5E5;
}
&.merged {
- background: #F5f5f5;
+ background: #F9F9F9;
border-color: #E5E5E5;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 70505dc4300..d66093bc2e5 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -90,8 +90,13 @@ ul.notes {
}
// Diff code in discussion view
-.discussion-body .diff-file .line_content {
- white-space: pre-wrap;
+.discussion-body .diff-file {
+ .diff-header > span {
+ margin-right: 10px;
+ }
+ .line_content {
+ white-space: pre-wrap;
+ }
}
.diff-file .notes_holder {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 6d55a5fa66e..5bd725d1222 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -49,35 +49,20 @@
@extend .clearfix;
margin-bottom: 15px;
- .project-home-desc,
- .star-fork-buttons {
+ &.project-home-row-top {
+ margin-bottom: 15px;
+ }
+
+ .project-home-desc {
font-size: 16px;
line-height: 1.3;
+ margin-right: 215px;
}
.project-home-desc {
float: left;
color: #666;
}
-
- .star-fork-buttons {
- float: right;
- min-width: 200px;
- font-weight: bold;
-
- .star-buttons, .fork-buttons {
- float: right;
- margin-left: 20px;
-
- a:hover {
- text-decoration: none;
- }
-
- .count {
- margin-left: 5px;
- }
- }
- }
}
.visibility-level-label {
@@ -87,6 +72,27 @@
color: inherit;
}
}
+
+ .project-repo-buttons {
+ margin-top: -3px;
+ position: absolute;
+ right: 0;
+ width: 260px;
+ text-align: right;
+
+ .btn {
+ font-weight: bold;
+ font-size: 14px;
+ line-height: 16px;
+
+ .count {
+ padding-left: 10px;
+ border-left: 1px solid #ccc;
+ display: inline-block;
+ margin-left: 10px;
+ }
+ }
+ }
}
.project-home-links {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e284f31f7ee..2809f90c0d5 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -178,6 +178,18 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
+ def default_url_options
+ if !Rails.env.test?
+ port = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
+ { host: Gitlab.config.gitlab.host,
+ protocol: Gitlab.config.gitlab.protocol,
+ port: port,
+ script_name: Gitlab.config.gitlab.relative_url_root }
+ else
+ super
+ end
+ end
+
def default_headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
new file mode 100644
index 00000000000..11af9895261
--- /dev/null
+++ b/app/controllers/autocomplete_controller.rb
@@ -0,0 +1,30 @@
+class AutocompleteController < ApplicationController
+ def users
+ @users =
+ if params[:project_id].present?
+ project = Project.find(params[:project_id])
+
+ if can?(current_user, :read_project, project)
+ project.team.users
+ end
+ elsif params[:group_id]
+ group = Group.find(params[:group_id])
+
+ if can?(current_user, :read_group, group)
+ group.users
+ end
+ else
+ User.all
+ end
+
+ @users = @users.search(params[:search]) if params[:search].present?
+ @users = @users.active
+ @users = @users.page(params[:page]).per(PER_PAGE)
+ render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
+ end
+
+ def user
+ @user = User.find(params[:id])
+ render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
+ end
+end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 088a766ed3a..2c0702073d4 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -19,6 +19,8 @@
require_relative 'projects_finder'
class IssuableFinder
+ NONE = '0'
+
attr_accessor :current_user, :params
def execute(current_user, params)
@@ -112,7 +114,7 @@ class IssuableFinder
def by_milestone(items)
if params[:milestone_id].present?
- items = items.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
+ items = items.where(milestone_id: (params[:milestone_id] == NONE ? nil : params[:milestone_id]))
end
items
@@ -120,7 +122,7 @@ class IssuableFinder
def by_assignee(items)
if params[:assignee_id].present?
- items = items.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
+ items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id]))
end
items
@@ -128,7 +130,7 @@ class IssuableFinder
def by_author(items)
if params[:author_id].present?
- items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id]))
+ items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id]))
end
items
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 38b5fc4a011..3f3509bb18a 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -275,7 +275,9 @@ module ApplicationHelper
'https://' + promo_host
end
- def page_filter_path(options={})
+ def page_filter_path(options = {})
+ without = options.delete(:without)
+
exist_opts = {
state: params[:state],
scope: params[:scope],
@@ -288,6 +290,12 @@ module ApplicationHelper
options = exist_opts.merge(options)
+ if without.present?
+ without.each do |key|
+ options.delete(key)
+ end
+ end
+
path = request.path
path << "?#{options.to_param}"
path
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 798d62b3a09..4ea838ca447 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -61,4 +61,12 @@ module BlobHelper
'Preview changes'
end
end
+
+ # Return an image icon depending on the file mode and extension
+ #
+ # mode - File unix mode
+ # mode - File name
+ def blob_icon(mode, name)
+ icon("#{file_type_icon_class('file', mode, name)} fw")
+ end
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index f81504991d3..b56f21c7a18 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -121,6 +121,8 @@ module DiffHelper
def inline_diff_btn
params_copy = params.dup
params_copy[:view] = 'inline'
+ # Always use HTML to handle case where JSON diff rendered this button
+ params_copy.delete(:format)
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
'Inline'
@@ -130,6 +132,8 @@ module DiffHelper
def parallel_diff_btn
params_copy = params.dup
params_copy[:view] = 'parallel'
+ # Always use HTML to handle case where JSON diff rendered this button
+ params_copy.delete(:format)
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
'Side-by-side'
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index f8e104b0827..7ca3f058636 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -29,13 +29,12 @@ module GitlabMarkdownHelper
end
def markdown(text, options={})
- unless (@markdown and options == @options)
+ unless @markdown && options == @options
@options = options
gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
user_color_scheme_class,
{
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
- filter_html: true,
with_toc_data: true,
safe_links_only: true
}.merge(options))
@@ -182,7 +181,7 @@ module GitlabMarkdownHelper
def file_exists?(path)
return false if path.nil?
- return @repository.blob_at(current_sha, path).present? || @repository.tree(current_sha, path).entries.any?
+ @repository.blob_at(current_sha, path).present? || @repository.tree(current_sha, path).entries.any?
end
# Check if the path is pointing to a directory(tree) or a file(blob)
@@ -190,7 +189,7 @@ module GitlabMarkdownHelper
def local_path(path)
return "tree" if @repository.tree(current_sha, path).entries.any?
return "raw" if @repository.blob_at(current_sha, path).image?
- return "blob"
+ "blob"
end
def current_sha
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 18260f0ed4d..187e21832f0 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -36,4 +36,48 @@ module IconsHelper
def private_icon
icon('lock')
end
+
+ def file_type_icon_class(type, mode, name)
+ if type == 'folder'
+ icon_class = 'folder'
+ elsif mode == 0120000
+ icon_class = 'share'
+ else
+ # Guess which icon to choose based on file extension.
+ # If you think a file extension is missing, feel free to add it on PR
+
+ case File.extname(name).downcase
+ when '.pdf'
+ icon_class = 'file-pdf-o'
+ when '.jpg', '.jpeg', '.jif', '.jfif',
+ '.jp2', '.jpx', '.j2k', '.j2c',
+ '.png', '.gif', '.tif', '.tiff',
+ '.svg', '.ico', '.bmp'
+ icon_class = 'file-image-o'
+ when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip',
+ '.xz', '.rar', '.7z'
+ icon_class = 'file-archive-o'
+ when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac'
+ icon_class = 'file-audio-o'
+ when '.mp4', '.m4p', '.m4v',
+ '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv',
+ '.mpg', '.mpeg', '.m2v',
+ '.avi', '.mkv', '.flv', '.ogv', '.mov',
+ '.3gp', '.3g2'
+ icon_class = 'file-video-o'
+ when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb'
+ icon_class = 'file-word-o'
+ when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm',
+ '.xlsb', '.xla', '.xlam', '.xll', '.xlw'
+ icon_class = 'file-excel-o'
+ when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm',
+ '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm'
+ icon_class = 'file-powerpoint-o'
+ else
+ icon_class = 'file-text-o'
+ end
+ end
+
+ icon_class
+ end
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 49063491abf..32ef2e7ca84 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -47,4 +47,8 @@ module LabelsHelper
"#FFF"
end
end
+
+ def project_labels_options(project)
+ options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
+ end
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 51b60770e0b..54462fd00e3 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -17,7 +17,7 @@ module MergeRequestsHelper
end
def new_mr_from_push_event(event, target_project)
- return {
+ {
merge_request: {
source_project_id: event.project.id,
target_project_id: target_project.id,
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 59fdc0d49cc..282bdf744d2 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -19,4 +19,15 @@ module MilestonesHelper
content_tag :div, nil, options
end
end
+
+ def projects_milestones_options
+ milestones =
+ if @project
+ @project.milestones
+ else
+ Milestone.where(project_id: @projects)
+ end.active
+
+ options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 7bf51b5b8e8..e3734023be3 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -80,17 +80,17 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC")
end
- def link_to_toggle_star(title, starred, signed_in)
- cls = 'star-btn'
- cls << ' disabled' unless signed_in
+ def link_to_toggle_star(title, starred)
+ cls = 'star-btn btn btn-sm btn-default'
+
+ toggle_text =
+ if starred
+ ' Unstar'
+ else
+ ' Star'
+ end
toggle_html = content_tag('span', class: 'toggle') do
- toggle_text = if starred
- ' Unstar'
- else
- ' Star'
- end
-
icon('star') + toggle_text
end
@@ -106,23 +106,25 @@ module ProjectsHelper
data: { type: 'json' }
}
+ path = toggle_star_namespace_project_path(@project.namespace, @project)
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
- link_to(
- toggle_star_namespace_project_path(@project.namespace, @project),
- link_opts
- ) do
+ link_to(path, link_opts) do
toggle_html + ' ' + count_html
end
end
end
def link_to_toggle_fork
- out = icon('code-fork')
- out << ' Fork'
- out << content_tag(:span, class: 'count') do
+ html = content_tag('span') do
+ icon('code-fork') + ' Fork'
+ end
+
+ count_html = content_tag(:span, class: 'count') do
@project.forks_count.to_s
end
+
+ html + count_html
end
private
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index 796d805f219..457cd3fa46b 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -4,18 +4,27 @@ module SelectsHelper
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
+ placeholder = opts[:placeholder] || 'Search for a user'
- hidden_field_tag(id, value, class: css_class)
- end
+ null_user = opts[:null_user] || false
+ any_user = opts[:any_user] || false
- def project_users_select_tag(id, opts = {})
- css_class = "ajax-project-users-select "
- css_class << "multiselect " if opts[:multiple]
- css_class << (opts[:class] || '')
- value = opts[:selected] || ''
- placeholder = opts[:placeholder] || 'Select user'
- project_id = opts[:project_id] || @project.id
- hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
+ html = {
+ class: css_class,
+ 'data-placeholder' => placeholder,
+ 'data-null-user' => null_user,
+ 'data-any-user' => any_user,
+ }
+
+ unless opts[:scope] == :all
+ if @project
+ html['data-project-id'] = @project.id
+ elsif @group
+ html['data-group-id'] = @group.id
+ end
+ end
+
+ hidden_field_tag(id, value, html)
end
def groups_select_tag(id, opts = {})
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 525266fb3b5..241462e5e4c 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -49,7 +49,7 @@ module SubmoduleHelper
def standard_links(host, namespace, project, commit)
base = [ 'https://', host, '/', namespace, '/', project ].join('')
- return base, [ base, '/tree/', commit ].join('')
+ [base, [ base, '/tree/', commit ].join('')]
end
def relative_self_links(url, commit)
@@ -58,7 +58,10 @@ module SubmoduleHelper
else
base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('')
end
- return namespace_project_path(base.namespace, base),
+
+ [
+ namespace_project_path(base.namespace, base),
namespace_project_tree_path(base.namespace, base, commit)
+ ]
end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index bf6726574ec..6dd9b6f017c 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -34,12 +34,13 @@ module TreeHelper
end
end
- # Return an image icon depending on the file type
+ # Return an image icon depending on the file type and mode
#
# type - String type of the tree item; either 'folder' or 'file'
- def tree_icon(type)
- icon_class = type == 'folder' ? 'folder' : 'file-o'
- icon(icon_class)
+ # mode - File unix mode
+ # name - File name
+ def tree_icon(type, mode, name)
+ icon("#{file_type_icon_class(type, mode, name)} fw")
end
def tree_hex_class(content)
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index ee27879cf40..8fcdd3bc853 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -148,7 +148,7 @@ class Notify < ActionMailer::Base
headers['References'] = message_id(model)
headers['X-GitLab-Project'] = "#{@project.name} | " if @project
- if (headers[:subject])
+ if headers[:subject]
headers[:subject].prepend('Re: ')
end
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index d52214cdd69..e6e16058d41 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -82,7 +82,7 @@ automatically inspected. Leave blank to include all branches.'
branch_restriction = restrict_to_branch.to_s
# check the branch restriction is poplulated and branch is not included
- if branch_restriction.length > 0 && branch_restriction.index(branch) == nil
+ if branch_restriction.length > 0 && branch_restriction.index(branch).nil?
return
end
diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb
index fef1c9b7349..3a381ff11b8 100644
--- a/app/models/project_services/buildbox_service.rb
+++ b/app/models/project_services/buildbox_service.rb
@@ -20,7 +20,11 @@
require "addressable/uri"
+# Buildbox renamed to Buildkite, but for backwards compatability with the STI
+# of Services, the class name is kept as "Buildbox"
class BuildboxService < CiService
+ ENDPOINT = "https://buildkite.com"
+
prop_accessor :project_url, :token
validates :project_url, presence: true, if: :activated?
@@ -29,7 +33,7 @@ class BuildboxService < CiService
after_save :compose_service_hook, if: :activated?
def webhook_url
- "#{buildbox_endpoint('webhook')}/deliver/#{webhook_token}"
+ "#{buildkite_endpoint('webhook')}/deliver/#{webhook_token}"
end
def compose_service_hook
@@ -59,7 +63,7 @@ class BuildboxService < CiService
end
def commit_status_path(sha)
- "#{buildbox_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}"
+ "#{buildkite_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}"
end
def build_page(sha, ref)
@@ -71,11 +75,11 @@ class BuildboxService < CiService
end
def status_img_path
- "#{buildbox_endpoint('badge')}/#{status_token}.svg"
+ "#{buildkite_endpoint('badge')}/#{status_token}.svg"
end
def title
- 'Buildbox'
+ 'Buildkite'
end
def description
@@ -83,18 +87,18 @@ class BuildboxService < CiService
end
def to_param
- 'buildbox'
+ 'buildkite'
end
def fields
[
{ type: 'text',
name: 'token',
- placeholder: 'Buildbox project GitLab token' },
+ placeholder: 'Buildkite project GitLab token' },
{ type: 'text',
name: 'project_url',
- placeholder: 'https://buildbox.io/example/project' }
+ placeholder: "#{ENDPOINT}/example/project" }
]
end
@@ -116,11 +120,9 @@ class BuildboxService < CiService
end
end
- def buildbox_endpoint(subdomain = nil)
- endpoint = 'https://buildbox.io'
-
+ def buildkite_endpoint(subdomain = nil)
if subdomain.present?
- uri = Addressable::URI.parse(endpoint)
+ uri = Addressable::URI.parse(ENDPOINT)
new_endpoint = "#{uri.scheme || 'http'}://#{subdomain}.#{uri.host}"
if uri.port.present?
@@ -129,7 +131,7 @@ class BuildboxService < CiService
new_endpoint
end
else
- endpoint
+ ENDPOINT
end
end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 3371fe7d5ef..8f04a69287a 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -14,8 +14,8 @@ module Issues
issue.update_nth_task(params[:task_num].to_i, false)
end
- params[:assignee_id] = "" if params[:assignee_id] == "-1"
- params[:milestone_id] = "" if params[:milestone_id] == "-1"
+ params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
+ params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = issue.labels.to_a
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 0ac6dfea6fd..23af2656c37 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -23,8 +23,8 @@ module MergeRequests
merge_request.update_nth_task(params[:task_num].to_i, false)
end
- params[:assignee_id] = "" if params[:assignee_id] == "-1"
- params[:milestone_id] = "" if params[:milestone_id] == "-1"
+ params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
+ params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = merge_request.labels.to_a
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 3372cfc11d0..489e03bd5ef 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -43,6 +43,9 @@ module Projects
project.namespace = new_namespace
project.save!
+ # Notifications
+ project.send_move_instructions
+
# Move main repository
unless gitlab_shell.mv_repository(old_path, new_path)
raise TransferError.new('Cannot move project')
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 077ee569085..05372f4124f 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -68,6 +68,11 @@
%strong.cred
does not exist
+ - if @project.archived?
+ %li
+ %span.light archived:
+ %strong repository is read-only
+
%li
%span.light access:
%strong
@@ -97,7 +102,7 @@
%strong #{@group.name}
group members (#{@group.group_members.count})
.pull-right
- = link_to admin_group_path(@group), class: 'btn btn-sm' do
+ = link_to admin_group_path(@group), class: 'btn btn-xs' do
%i.fa.fa-pencil-square-o
%ul.well-list
- @group_members.each do |member|
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
index 18b7e8ba270..eb7a099bfe2 100644
--- a/app/views/admin/services/_form.html.haml
+++ b/app/views/admin/services/_form.html.haml
@@ -10,7 +10,7 @@
- @service.errors.full_messages.each do |msg|
%p= msg
- if @service.help.present?
- .alert.alert-info
+ .well
= preserve do
= markdown @service.help
diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml
index c4c29bb2e8d..a52b8197384 100644
--- a/app/views/groups/group_members/_new_group_member.html.haml
+++ b/app/views/groups/group_members/_new_group_member.html.haml
@@ -1,7 +1,7 @@
= form_for @group_member, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
- .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
+ .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all)
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 9da3c920c62..4e49bbbc7fa 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
- $ ->
- new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
+ new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 9c4d91013ec..f0bc3e6b1ac 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
- $ ->
- new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
+ new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index e809643d8d4..33b0a21acf3 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
- $ ->
- new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
+ new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index 645241a6c69..78c5e957be0 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -42,5 +42,4 @@
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
- $ ->
- new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
+ new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
diff --git a/app/views/layouts/doorkeeper/admin.html.haml b/app/views/layouts/doorkeeper/admin.html.haml
deleted file mode 100644
index bd9adfab66d..00000000000
--- a/app/views/layouts/doorkeeper/admin.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-!!!
-%html
- %head
- %meta{:charset => "utf-8"}
- %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
- %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
- %title Doorkeeper
- = stylesheet_link_tag "doorkeeper/admin/application"
- = csrf_meta_tags
- %body
- .navbar.navbar-inverse.navbar-fixed-top{:role => "navigation"}
- .container
- .navbar-header
- = link_to 'OAuth2 Provider', oauth_applications_path, class: 'navbar-brand'
- %ul.nav.navbar-nav
- = content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do
- = link_to 'Applications', oauth_applications_path
- .container
- - if flash[:notice].present?
- .alert.alert-info
- = flash[:notice]
- = yield \ No newline at end of file
diff --git a/app/views/layouts/doorkeeper/application.html.haml b/app/views/layouts/doorkeeper/application.html.haml
deleted file mode 100644
index e5f37fad1f4..00000000000
--- a/app/views/layouts/doorkeeper/application.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-!!!
-%html
- %head
- %title OAuth authorize required
- %meta{:charset => "utf-8"}
- %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
- %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
- = stylesheet_link_tag "doorkeeper/application"
- = csrf_meta_tags
- %body
- #container
- - if flash[:notice].present?
- .alert.alert-info
- = flash[:notice]
- = yield \ No newline at end of file
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index a295a0d6cdc..5689bdee1c6 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -2,7 +2,7 @@
.project-home-panel{:class => ("empty-project" if empty_repo)}
.project-identicon-holder
= project_icon(@project, alt: '', class: 'avatar project-avatar')
- .project-home-row
+ .project-home-row.project-home-row-top
.project-home-desc
- if @project.description.present?
= escaped_autolink(@project.description)
@@ -14,31 +14,30 @@
&ndash;
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
= readme.name
- .star-fork-buttons
+ .project-repo-buttons
+ .inline.star.js-toggler-container{class: @show_star ? 'on' : ''}
+ - if current_user
+ = link_to_toggle_star('Star this project.', false)
+ = link_to_toggle_star('Unstar this project.', true)
+ - else
+ = link_to new_user_session_path, class: 'btn star-btn has_tooltip', title: 'You must sign in to star a project' do
+ %span
+ = icon('star')
+ Star
+ %span.count
+ = @project.star_count
- unless @project.empty_repo?
- .fork-buttons
- - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
+ - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
+ .inline.fork-buttons.prepend-left-10
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork' do
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-sm btn-default' do
= link_to_toggle_fork
- else
- = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project" do
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-sm btn-default' do
= link_to_toggle_fork
- .star-buttons
- %span.star.js-toggler-container{class: @show_star ? 'on' : ''}
- - if current_user
- = link_to_toggle_star('Star this project.', false, true)
- = link_to_toggle_star('Unstar this project.', true, true)
- - else
- = link_to_toggle_star('You must sign in to star a project.', false, false)
-
.project-home-row.hidden-xs
- if current_user && !empty_repo
.project-home-dropdown
= render "dropdown"
- - unless @project.empty_repo?
- - if can? current_user, :download_code, @project
- .pull-right.prepend-left-10
- = render 'projects/repositories/download_archive', split_button: true
= render "shared/clone_panel"
diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml
index 7fd5fe8a6e1..e321a84974e 100644
--- a/app/views/projects/_issuable_form.html.haml
+++ b/app/views/projects/_issuable_form.html.haml
@@ -35,8 +35,8 @@
%i.fa.fa-user
Assign to
.col-sm-10
- = project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- placeholder: 'Select a user', class: 'custom-form-control',
+ = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
+ placeholder: 'Select a user', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index ba60bd92869..65c3ab10e02 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -22,7 +22,7 @@
%div#tree-content-holder.tree-content-holder
%article.file-holder
.file-title
- %i.fa.fa-file
+ = blob_icon blob.mode, blob.name
%strong
= blob.name
%small
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 7409f702c5d..3f645b81397 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,7 +10,8 @@
Download as
%span.caret
%ul.dropdown-menu
- %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
+ - unless @commit.parents.length > 1
+ %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
%li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
= link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do
%span Browse Code »
@@ -48,5 +49,4 @@
= preserve(gfm(escape_once(@commit.description)))
:coffeescript
- $ ->
- $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
+ $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 83e4d24cf5f..a714f5f79e0 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,6 +1,8 @@
%ul.nav.nav-tabs
= nav_link(controller: [:commit, :commits]) do
- = link_to 'Commits', namespace_project_commits_path(@project.namespace, @project, @repository.root_ref)
+ = link_to namespace_project_commits_path(@project.namespace, @project, @repository.root_ref) do
+ Commits
+ %span.badge= number_with_precision(@repository.commit_count, precision: 0, delimiter: ',')
= nav_link(controller: :compare) do
= link_to 'Compare', namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index e0d75113a5e..fbf04847e48 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -31,14 +31,11 @@
= render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
- %fieldset.features
- %legend
- Tags:
- .form-group
- = f.label :tag_list, "Tags", class: 'control-label'
- .col-sm-10
- = f.text_field :tag_list, maxlength: 2000, class: "form-control"
- %p.hint Separate tags with commas.
+ .form-group
+ = f.label :tag_list, "Tags", class: 'control-label'
+ .col-sm-10
+ = f.text_field :tag_list, maxlength: 2000, class: "form-control"
+ %p.help-block Separate tags with commas.
%fieldset.features
%legend
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index f1248ac2af5..934b6b8c017 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -12,7 +12,7 @@
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
- .alert.alert-info
+ .well.prepend-top-20
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml
index 91fe0b68371..52e38050419 100644
--- a/app/views/projects/issues/_issue_context.html.haml
+++ b/app/views/projects/issues/_issue_context.html.haml
@@ -8,7 +8,7 @@
- else
none
- if can?(current_user, :modify_issue, @issue)
- = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id)
+ = users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true)
%div.prepend-top-20.clearfix
.issuable-context-title
@@ -43,7 +43,4 @@
You're receiving notifications because you're subscribed to this thread.
:coffeescript
- $ ->
- new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
-
-
+ new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 54e3009cca2..d3c7ae24a75 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -15,15 +15,5 @@
= render 'shared/issuable_filter'
- .clearfix
- .issues_bulk_update.hide
- = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
- = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
- = project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
- = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
- = hidden_field_tag 'update[issues_ids]', []
- = hidden_field_tag :state_event, params[:state_event]
- = button_tag "Update issues", class: "btn update_selected_issues btn-save"
-
.issues-holder
= render "issues"
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index 82c0e653759..1d38662bff8 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -13,5 +13,5 @@
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$('.edit-issue.inline-update input[type="submit"]').hide();
-new ProjectUsersSelect();
+new UsersSelect()
new Issue();
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 1d8eef4e8ce..d986ce67c0c 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -39,7 +39,7 @@
%i.fa.fa-user
Assign to
.col-sm-10
- = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
+ = users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml
index 14ad89a2000..105562fb05e 100644
--- a/app/views/projects/merge_requests/show/_context.html.haml
+++ b/app/views/projects/merge_requests/show/_context.html.haml
@@ -9,7 +9,7 @@
none
.issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
- = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id)
+ = users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, null_user: true)
%div.prepend-top-20.clearfix
.issuable-context-title
@@ -45,7 +45,4 @@
You're receiving notifications because you're subscribed to this thread.
:coffeescript
- $ ->
- new Subscription("#{toggle_subscription_namespace_project_merge_request_path(@merge_request.project.namespace, @project, @merge_request)}")
-
- \ No newline at end of file
+ new Subscription("#{toggle_subscription_namespace_project_merge_request_path(@merge_request.project.namespace, @project, @merge_request)}")
diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml
index a4f2a890969..44bd9347f51 100644
--- a/app/views/projects/merge_requests/show/_state_widget.html.haml
+++ b/app/views/projects/merge_requests/show/_state_widget.html.haml
@@ -29,7 +29,7 @@
%h4
Merge in progress...
%p
- GitLab tries to merge it right now. During this time merge request is locked and can not be closed.
+ Merging is in progress. While merging this request is locked and cannot be closed.
- unless @commits.any?
%h4 Nothing to merge
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index f5cc98c7fa4..b4df1d20737 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -2,7 +2,7 @@
$('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}");
$('.context').effect('highlight');
- new ProjectUsersSelect();
+ new UsersSelect()
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true});
merge_request = new MergeRequest();
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 9687c8ad87c..69909a8554e 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -72,7 +72,7 @@
%span Git repository URL
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
- .alert.alert-info.prepend-top-10
+ .well.prepend-top-20
%ul
%li
The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
@@ -109,9 +109,8 @@
%p Please wait a moment, this page will automatically refresh when ready.
:coffeescript
- $ ->
- $('.how_to_import_link').bind 'click', (e) ->
- e.preventDefault()
- import_modal = $(this).next(".modal").show()
- $('.modal-header .close').bind 'click', ->
- $(".modal").hide()
+ $('.how_to_import_link').bind 'click', (e) ->
+ e.preventDefault()
+ import_modal = $(this).next(".modal").show()
+ $('.modal-header .close').bind 'click', ->
+ $(".modal").hide()
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 0f824bdabf8..5daae2708e6 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -1,7 +1,7 @@
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
- .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
+ .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all)
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 4db71ce8ff9..a3464c0e5e1 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -2,7 +2,7 @@
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
-.alert.alert-info
+.well.append-bottom-20
%p Protected branches are designed to
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index 26669fb00a9..1ba7a1f2060 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -3,10 +3,10 @@
- split_button = split_button || false
- if split_button == true
%span.btn-group{class: btn_class}
- = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-sm', rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
- %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ %a.btn-sm.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Archive Format
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 32e97a754cb..ce6b7a0737a 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -18,7 +18,7 @@
%li= msg
- if @service.help.present?
- .alert.alert-info
+ .well
= preserve do
= markdown @service.help
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index cfa6f558dd6..85113ffa7e2 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -68,7 +68,7 @@
= link_to license_url(@project), class: 'btn btn-block' do
View license
- .prepend-top-10
+ .prepend-top-10.append-bottom-10
%p
%span.light Created on
#{@project.created_at.stamp('Aug 22, 2013')}
@@ -79,8 +79,16 @@
- else
#{link_to @project.owner_name, @project.owner}
+ - unless @project.empty_repo?
+ - if can? current_user, :download_code, @project
+ %hr
+ .prepend-top-10.append-bottom-10
+ = render 'projects/repositories/download_archive', split_button: true
+
+ .prepend-top-10
- @project.ci_services.each do |ci_service|
- if ci_service.active? && ci_service.respond_to?(:builds_path)
+ %hr
- if ci_service.respond_to?(:status_img_path)
= link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
= image_tag ci_service.status_img_path, alt: "build status"
@@ -97,4 +105,3 @@
= readme.name
.wiki
= render_readme(readme)
-
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index b253fe896e3..02ecbade219 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -1,6 +1,6 @@
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
- = tree_icon(type)
+ = tree_icon(type, blob_item.mode, blob_item.name)
%span.str-truncated
= link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 20c70cac699..2b5f671c09e 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,6 +1,6 @@
%tr{ class: "tree-item" }
%td.tree-item-file-name
- %i.fa.fa-archive
+ %i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
%td
%td.hidden-xs
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index 94342bc9b2b..e87138bf980 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -1,6 +1,6 @@
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
- = tree_icon(type)
+ = tree_icon(type, tree_item.mode, tree_item.name)
%span.str-truncated
- path = flatten_tree(tree_item)
= link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml
index 5412b9ef0f4..f169733f2e9 100644
--- a/app/views/shared/_issuable_filter.html.haml
+++ b/app/views/shared/_issuable_filter.html.haml
@@ -14,106 +14,45 @@
%i.fa.fa-compass
All
- %div
- - if controller.controller_name == 'issues'
- .check-all-holder
- = check_box_tag "check_all_issues", nil, false,
- class: "check_all_issues left",
- disabled: !can?(current_user, :modify_issue, @project)
- .issues-other-filters
- .dropdown.inline.assignee-filter
- %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light assignee:
- - if @assignee.present?
- %strong= @assignee.name
- - elsif params[:assignee_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to page_filter_path(assignee_id: nil) do
- Any
- = link_to page_filter_path(assignee_id: 0) do
- Unassigned
- - @assignees.sort_by(&:name).each do |user|
- %li
- = link_to page_filter_path(assignee_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
+ .issues-details-filters
+ = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
+ - if controller.controller_name == 'issues'
+ .check-all-holder
+ = check_box_tag "check_all_issues", nil, false,
+ class: "check_all_issues left",
+ disabled: !can?(current_user, :modify_issue, @project)
+ .issues-other-filters
+ .filter-item.inline
+ = users_select_tag(:assignee_id, selected: params[:assignee_id],
+ placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true)
+
+ .filter-item.inline
+ = users_select_tag(:author_id, selected: params[:author_id],
+ placeholder: 'Author', class: 'trigger-submit', any_user: true)
+
+ .filter-item.inline.milestone-filter
+ = select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone')
- .dropdown.inline.prepend-left-10.author-filter
- %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light author:
- - if @author.present?
- %strong= @author.name
- - elsif params[:author_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to page_filter_path(author_id: nil) do
- Any
- = link_to page_filter_path(author_id: 0) do
- Unassigned
- - @authors.sort_by(&:name).each do |user|
- %li
- = link_to page_filter_path(author_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
+ - if @project
+ .filter-item.inline.labels-filter
+ = select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit", prompt: 'Label')
- .dropdown.inline.prepend-left-10.milestone-filter
- %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
- %i.fa.fa-clock-o
- %span.light milestone:
- - if @milestone.present?
- %strong= @milestone.title
- - elsif params[:milestone_id] == "0"
- None (backlog)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to page_filter_path(milestone_id: nil) do
- Any
- = link_to page_filter_path(milestone_id: 0) do
- None (backlog)
- - @milestones.each do |milestone|
- %li
- = link_to page_filter_path(milestone_id: milestone.id) do
- %strong= milestone.title
- %small.light= milestone.expires_at
+ .pull-right
+ = render 'shared/sort_dropdown'
+
+ - if controller.controller_name == 'issues'
+ .issues_bulk_update.hide
+ = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
+ = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
+ = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true)
+ = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
+ = hidden_field_tag 'update[issues_ids]', []
+ = hidden_field_tag :state_event, params[:state_event]
+ = button_tag "Update issues", class: "btn update_selected_issues btn-save"
- - if @project
- .dropdown.inline.prepend-left-10.labels-filter
- %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
- %i.fa.fa-tags
- %span.light label:
- - if params[:label_name].present?
- %strong= params[:label_name]
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to page_filter_path(label_name: nil) do
- Any
- - if @project.labels.any?
- - @project.labels.each do |label|
- %li
- = link_to page_filter_path(label_name: label.name) do
- = render_colored_label(label)
- - else
- %li
- = link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do
- %i.fa.fa-plus-circle
- Create default labels
+:coffeescript
+ new UsersSelect()
- .pull-right
- = render 'shared/sort_dropdown'
+ $('form.filter-form').on 'submit', (event) ->
+ event.preventDefault()
+ Turbolinks.visit @.action + '&' + $(@).serialize()
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 488f49267c7..922b0c6cebf 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -4,7 +4,7 @@
%small Issues, merge requests and push events
#cal-heatmap.calendar
:javascript
- new calendar(
+ new Calendar(
#{@timestamps.to_json},
#{@starting_year},
#{@starting_month},
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 0653fb871ae..5e1d65e2ed8 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -47,5 +47,4 @@
= render 'projects'
:coffeescript
- $ ->
- $(".user-calendar").load("#{user_calendar_path}")
+ $(".user-calendar").load("#{user_calendar_path}")
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 89fa2117dd2..1d21addece6 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,7 +1,15 @@
class EmailsOnPushWorker
include Sidekiq::Worker
- def perform(project_id, recipients, push_data, send_from_committer_email: false, disable_diffs: false)
+ def perform(project_id, recipients, push_data, options = {})
+ options.symbolize_keys!
+ options.reverse_merge!(
+ send_from_committer_email: false,
+ disable_diffs: false
+ )
+ send_from_committer_email = options[:send_from_committer_email]
+ disable_diffs = options[:disable_diffs]
+
project = Project.find(project_id)
before_sha = push_data["before"]
after_sha = push_data["after"]
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index c4a0fefb7ab..3f1ca34a667 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -2,6 +2,11 @@
# GitLab application config file #
# # # # # # # # # # # # # # # # # #
#
+########################### NOTE #####################################
+# This file should not receive new settings. All configuration options #
+# are being moved to ApplicationSetting model! #
+########################################################################
+#
# How to use:
# 1. Copy file as gitlab.yml
# 2. Update gitlab -> host with your fully qualified domain name
@@ -81,35 +86,6 @@ production: &base
# The default is 'tmp/repositories' relative to the root of the Rails app.
# repository_downloads_path: tmp/repositories
- ## External issues trackers
- issues_tracker:
- # redmine:
- # title: "Redmine"
- # ## If not nil, link 'Issues' on project page will be replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # project_url: "http://redmine.sample/projects/:issues_tracker_id"
- #
- # ## If not nil, links from /#\d/ entities from commit messages will replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # ## :id - Issue id (from commit messages)
- # issues_url: "http://redmine.sample/issues/:id"
- #
- # ## If not nil, links to creating new issues will be replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new"
- #
- # jira:
- # title: "Atlassian Jira"
- # project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id"
- # issues_url: "http://jira.sample/browse/:id"
- # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa"
-
## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
gravatar:
diff --git a/config/initializers/acts_as_taggable_on_patch.rb b/config/initializers/acts_as_taggable_on_patch.rb
deleted file mode 100644
index 0d535cb5cac..00000000000
--- a/config/initializers/acts_as_taggable_on_patch.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-# This is a patch to address the issue in https://github.com/mbleigh/acts-as-taggable-on/issues/427 caused by
-# https://github.com/rails/rails/commit/31a43ebc107fbd50e7e62567e5208a05909ec76c
-# gem 'acts-as-taggable-on' has the fix included https://github.com/mbleigh/acts-as-taggable-on/commit/89bbed3864a9252276fb8dd7d535fce280454b90
-# but not in the currently used version of gem ('2.4.1')
-# With replacement of 'acts-as-taggable-on' gem this file will become obsolete
-
-module ActsAsTaggableOn::Taggable
- module Core
- module ClassMethods
- def tagged_with(tags, options = {})
- tag_list = ActsAsTaggableOn::TagList.from(tags)
- empty_result = where("1 = 0")
-
- return empty_result if tag_list.empty?
-
- joins = []
- conditions = []
- having = []
- select_clause = []
-
- context = options.delete(:on)
- owned_by = options.delete(:owned_by)
- alias_base_name = undecorated_table_name.gsub('.','_')
- quote = ActsAsTaggableOn::Tag.using_postgresql? ? '"' : ''
-
- if options.delete(:exclude)
- if options.delete(:wild)
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ? ESCAPE '!'", "%#{escape_like(t)}%"]) }.join(" OR ")
- else
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ?", t]) }.join(" OR ")
- end
-
- conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
-
- if owned_by
- joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
- " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{owned_by.id}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
- end
-
- elsif options.delete(:any)
- # get tags, drop out if nothing returned (we need at least one)
- tags =
- if options.delete(:wild)
- ActsAsTaggableOn::Tag.named_like_any(tag_list)
- else
- ActsAsTaggableOn::Tag.named_any(tag_list)
- end
-
- return empty_result unless tags.length > 0
-
- # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
- # avoid ambiguous column name
- taggings_context = context ? "_#{context}" : ''
-
- taggings_alias = adjust_taggings_alias(
- "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
- )
-
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
-
- # don't need to sanitize sql, map all ids and join with OR logic
- conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
- select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
-
- if owned_by
- tagging_join << " AND " +
- sanitize_sql([
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
- owned_by.id,
- owned_by.class.base_class.to_s
- ])
- end
-
- joins << tagging_join
- else
- tags = ActsAsTaggableOn::Tag.named_any(tag_list)
-
- return empty_result unless tags.length == tag_list.length
-
- tags.each do |tag|
- taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
- " AND #{taggings_alias}.tag_id = #{tag.id}"
-
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
-
- if owned_by
- tagging_join << " AND " +
- sanitize_sql([
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
- owned_by.id,
- owned_by.class.base_class.to_s
- ])
- end
-
- joins << tagging_join
- end
- end
-
- taggings_alias, tags_alias = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
-
- if options.delete(:match_all)
- joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
-
-
- group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
- group = group_columns
- having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
- end
-
- select(select_clause) \
- .joins(joins.join(" ")) \
- .where(conditions.join(" AND ")) \
- .group(group) \
- .having(having) \
- .order(options[:order]) \
- .readonly(false)
- end
- end
- end
-end
diff --git a/config/routes.rb b/config/routes.rb
index c30cd768572..388858d2670 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -8,6 +8,11 @@ Gitlab::Application.routes.draw do
authorizations: 'oauth/authorizations'
end
+ # Autocomplete
+ get '/autocomplete/users' => 'autocomplete#users'
+ get '/autocomplete/users/:id' => 'autocomplete#user'
+
+
# Search
get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index b66583bb363..965d8fc313f 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -46,14 +46,15 @@ You can also use other rich text files in GitLab. You might have to install a de
GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p).
-A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.:
+A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
+Line-breaks, or softreturns, are rendered if you end a line with two or more spaces
- Roses are red
+ Roses are red [followed by two or more spaces]
Violets are blue
Sugar is sweet
-Roses are red
+Roses are red
Violets are blue
Sugar is sweet
@@ -440,6 +441,8 @@ Note that inline HTML is disabled in the default Gitlab configuration, although
<dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
</dl>
+See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows the `class`, `id`, and `style` attributes.
+
## Horizontal Rule
```
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index c9928e11b2e..8cfa7f9c876 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -41,6 +41,11 @@ If a user is a GitLab administrator they receive all permissions.
## Group
+In order for a group to appear as public and be browsable, it must contain at
+least one public project.
+
+Any user can remove themselves from a group, unless they are the last Owner of the group.
+
| Action | Guest | Reporter | Developer | Master | Owner |
|-------------------------|-------|----------|-----------|--------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -48,5 +53,3 @@ If a user is a GitLab administrator they receive all permissions.
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
-
-Any user can remove themselves from a group, unless they are the last Owner of the group.
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 66941521c2e..0acb15896d3 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -68,6 +68,6 @@ You can't add the same deploy key twice with the 'New Deploy Key' option.
If you want to add the same key to another project, please enable it in the
list that says 'Deploy keys from projects available to you'. All the deploy
keys of all the projects you have access to are available. This project
-access can happen through being a direct member of the projecti, or through
+access can happen through being a direct member of the project, or through
a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more
information.
diff --git a/docker/Dockerfile b/docker/Dockerfile
index f34cbc38a54..f0a8b9f53df 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \
- wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.9.0-omnibus.2-1_amd64.deb \
+ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.9.1-omnibus.1-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
@@ -26,11 +26,7 @@ RUN mkdir -p /opt/gitlab/sv/sshd/supervise \
# Expose web & ssh
EXPOSE 80 22
-# Declare volumes
-VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"]
-
# Copy assets
-COPY assets/gitlab.rb /etc/gitlab/
COPY assets/wrapper /usr/local/bin/
# Wrapper to handle signal, trigger runit and reconfigure GitLab
diff --git a/docker/README.md b/docker/README.md
index 58982a238a8..b7e8b0db7e7 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -8,14 +8,15 @@ GitLab offers git repository management, code reviews, issue tracking, activity
![GitLab Logo](https://gitlab.com/uploads/appearance/logo/1/brand_logo-c37eb221b456bb4b472cc1084480991f.png)
-How to use this image
+How to use these images
======================
-At this moment GitLab doesn't have official Docker images.
-Build your own based on the Omnibus packages with the following command (it assumes you're in the GitLab repo root directory):
+At this moment GitLab doesn't have official Docker images. For convinience we will use suffix _xy where xy is current version of GitLab.
+Build your own based on the Omnibus packages with the following commands (it assumes you're in the GitLab repo root directory):
```bash
-sudo docker build --tag gitlab_image docker/
+sudo docker build --tag gitlab_data_image docker/data/
+sudo docker build --tag gitlab_app_image_xy docker/
```
We assume using a data volume container, this will simplify migrations and backups.
@@ -30,16 +31,16 @@ The directories on data container are:
Create the data container with:
```bash
-sudo docker run --name gitlab_data gitlab_image /bin/true
+sudo docker run --name gitlab_data gitlab_data_image /bin/true
```
-After creating this run GitLab:
+After creating data container run GitLab container:
```bash
-sudo docker run --detach --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_image
+sudo docker run --detach --name gitlab_app_xy --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_app_image_xy
```
-It might take a while before the docker container is responding to queries. You can follow the configuration process with `docker logs -f gitlab_app`.
+It might take a while before the docker container is responding to queries. You can follow the configuration process with `sudo docker logs -f gitlab_app_xy`.
You can then go to `http://localhost:8080/` (or `http://192.168.59.103:8080/` if you use boot2docker).
You can login with username `root` and password `5iveL!fe`.
@@ -54,7 +55,7 @@ This container uses the official Omnibus GitLab distribution, so all configurati
To access GitLab configuration, you can start an interactive command line in a new container using the shared data volume container, you will be able to browse the 3 directories and use your favorite text editor:
```bash
-docker run -ti -e TERM=linux --rm --volumes-from gitlab_data ubuntu
+sudo docker run -ti -e TERM=linux --rm --volumes-from gitlab_data ubuntu
vi /etc/gitlab/gitlab.rb
```
@@ -62,6 +63,25 @@ vi /etc/gitlab/gitlab.rb
You can find all available options in [Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#configuration).
+How to upgrade GitLab
+========================
+
+To updgrade GitLab to new versions, stop running container, create new docker image and container from that image.
+
+It Assumes that you're upgrading from 7.8 to 7.9 and you're in the updated GitLab repo root directory:
+
+```bash
+sudo docker stop gitlab_app_78
+sudo docker build --tag gitlab_app_image_79 docker/
+sudo docker run --detach --name gitlab_app_79 --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_app_image_79
+```
+
+On the first run GitLab will reconfigure and update itself. If everything runs OK don't forget to cleanup old container and image:
+
+```bash
+sudo docker rm gitlab_app_78
+sudo docker rmi gitlab_app_image_78
+```
Troubleshooting
=========================
diff --git a/docker/data/Dockerfile b/docker/data/Dockerfile
new file mode 100644
index 00000000000..ea0175c4aa2
--- /dev/null
+++ b/docker/data/Dockerfile
@@ -0,0 +1,8 @@
+FROM busybox
+
+# Declare volumes
+VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"]
+# Copy assets
+COPY assets/gitlab.rb /etc/gitlab/
+
+CMD /bin/sh
diff --git a/docker/assets/gitlab.rb b/docker/data/assets/gitlab.rb
index 7fddf309c01..7fddf309c01 100644
--- a/docker/assets/gitlab.rb
+++ b/docker/data/assets/gitlab.rb
diff --git a/features/dashboard/issues.feature b/features/dashboard/issues.feature
index 72627e43e05..99dad88a402 100644
--- a/features/dashboard/issues.feature
+++ b/features/dashboard/issues.feature
@@ -10,10 +10,12 @@ Feature: Dashboard Issues
Scenario: I should see assigned issues
Then I should see issues assigned to me
+ @javascript
Scenario: I should see authored issues
When I click "Authored by me" link
Then I should see issues authored by me
+ @javascript
Scenario: I should see all issues
When I click "All" link
Then I should see all issues
diff --git a/features/dashboard/merge_requests.feature b/features/dashboard/merge_requests.feature
index dcef1290e7e..4a2c997d707 100644
--- a/features/dashboard/merge_requests.feature
+++ b/features/dashboard/merge_requests.feature
@@ -10,10 +10,12 @@ Feature: Dashboard Merge Requests
Scenario: I should see assigned merge_requests
Then I should see merge requests assigned to me
+ @javascript
Scenario: I should see authored merge_requests
When I click "Authored by me" link
Then I should see merge requests authored by me
+ @javascript
Scenario: I should see all merge_requests
When I click "All" link
Then I should see all merge requests
diff --git a/features/groups.feature b/features/groups.feature
index 05546e0d6ef..65d06a0daf9 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -47,6 +47,14 @@ Feature: Groups
Then I should not see group "Owned" avatar
And I should not see the "Remove avatar" button
+ @javascript
+ Scenario: Add user to group
+ Given gitlab user "Mike"
+ When I visit group "Owned" members page
+ And I click link "Add members"
+ When I select "Mike" as "Reporter"
+ Then I should see "Mike" in team list as "Reporter"
+
# Leave
@javascript
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index 46076b6f3e6..c4b206edc95 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -21,10 +21,13 @@ Feature: Project Commits
And I click side-by-side diff button
Then I see inline diff button
+ @javascript
Scenario: I compare refs
Given I visit compare refs page
And I fill compare fields with refs
Then I see compared refs
+ And I unfold diff
+ Then I should see additional file lines
Scenario: I browse commits for a specific path
Given I visit my project's commits page for a specific path
diff --git a/features/project/issues/filter_labels.feature b/features/project/issues/filter_labels.feature
index 2c69a78a749..e316f519861 100644
--- a/features/project/issues/filter_labels.feature
+++ b/features/project/issues/filter_labels.feature
@@ -8,11 +8,7 @@ Feature: Project Issues Filter Labels
And project "Shop" has issue "Feature1" with labels: "feature"
Given I visit project "Shop" issues page
- Scenario: I should see project issues
- Then I should see "bug" in labels filter
- And I should see "feature" in labels filter
- And I should see "enhancement" in labels filter
-
+ @javascript
Scenario: I filter by one label
Given I click link "bug"
Then I should see "Bugfix1" in issues list
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 91dc576f8b4..cbb5c8eb39b 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -166,6 +166,13 @@ Feature: Project Merge Requests
And I click Side-by-side Diff tab
Then I should see comments on the side-by-side diff page
+ @javascript
+ Scenario: I view diffs on a merge request
+ Given project "Shop" have "Bug NS-05" open merge request with diffs inside
+ And I visit merge request page "Bug NS-05"
+ And I click on the Changes tab via Javascript
+ Then I should see the proper Inline and Side-by-side links
+
# Task status in issues list
Scenario: Merge requests list should display task status
diff --git a/features/project/star.feature b/features/project/star.feature
index 3322f891805..a45f9c470ea 100644
--- a/features/project/star.feature
+++ b/features/project/star.feature
@@ -13,7 +13,7 @@ Feature: Project Star
Given public project "Community"
And I visit project "Community" page
When I click on the star toggle button
- Then The project has 0 stars
+ Then I redirected to sign in page
@javascript
Scenario: Signed in users can toggle star
diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb
index b77113e3974..60da36e86de 100644
--- a/features/steps/dashboard/issues.rb
+++ b/features/steps/dashboard/issues.rb
@@ -1,6 +1,7 @@
class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
+ include Select2Helper
step 'I should see issues assigned to me' do
should_see(assigned_issue)
@@ -35,21 +36,13 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end
step 'I click "Authored by me" link' do
- within ".assignee-filter" do
- click_link "Any"
- end
- within ".author-filter" do
- click_link current_user.name
- end
+ select2(current_user.id, from: "#author_id")
+ select2(nil, from: "#assignee_id")
end
step 'I click "All" link' do
- within ".author-filter" do
- click_link "Any"
- end
- within ".assignee-filter" do
- click_link "Any"
- end
+ select2(nil, from: "#author_id")
+ select2(nil, from: "#assignee_id")
end
def should_see(issue)
diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb
index 6261c89924c..9d92082bb83 100644
--- a/features/steps/dashboard/merge_requests.rb
+++ b/features/steps/dashboard/merge_requests.rb
@@ -1,6 +1,7 @@
class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
+ include Select2Helper
step 'I should see merge requests assigned to me' do
should_see(assigned_merge_request)
@@ -39,21 +40,13 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end
step 'I click "Authored by me" link' do
- within ".assignee-filter" do
- click_link "Any"
- end
- within ".author-filter" do
- click_link current_user.name
- end
+ select2(current_user.id, from: "#author_id")
+ select2(nil, from: "#assignee_id")
end
step 'I click "All" link' do
- within ".author-filter" do
- click_link "Any"
- end
- within ".assignee-filter" do
- click_link "Any"
- end
+ select2(nil, from: "#author_id")
+ select2(nil, from: "#assignee_id")
end
def should_see(merge_request)
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 91921f5e21c..ec5213e4b93 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -5,6 +5,32 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
include SharedUser
include Select2Helper
+ step 'gitlab user "Mike"' do
+ create(:user, name: "Mike")
+ end
+
+ step 'I click link "Add members"' do
+ find(:css, 'button.btn-new').click
+ end
+
+ step 'I select "Mike" as "Reporter"' do
+ user = User.find_by(name: "Mike")
+
+ within ".users-group-form" do
+ select2(user.id, from: "#user_ids", multiple: true)
+ select "Reporter", from: "access_level"
+ end
+
+ click_button "Add users to group"
+ end
+
+ step 'I should see "Mike" in team list as "Reporter"' do
+ within '.well-list' do
+ page.should have_content('Mike')
+ page.should have_content('Reporter')
+ end
+ end
+
step 'I should see group "Owned" projects list' do
Group.find_by(name: "Owned").projects.each do |project|
page.should have_link project.name
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index b2dccf868b0..57b727f837e 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -38,6 +38,18 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
click_button "Compare"
end
+ step 'I unfold diff' do
+ @diff = first('.js-unfold')
+ @diff.click
+ sleep 2
+ end
+
+ step 'I should see additional file lines' do
+ within @diff.parent do
+ first('.new_line').text.should_not have_content "..."
+ end
+ end
+
step 'I see compared refs' do
page.should have_content "Compare View"
page.should have_content "Commits (1)"
diff --git a/features/steps/project/issues/filter_labels.rb b/features/steps/project/issues/filter_labels.rb
index e62fa9c84c8..5740bd12837 100644
--- a/features/steps/project/issues/filter_labels.rb
+++ b/features/steps/project/issues/filter_labels.rb
@@ -2,24 +2,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
-
- step 'I should see "bug" in labels filter' do
- within ".labels-filter" do
- page.should have_content "bug"
- end
- end
-
- step 'I should see "feature" in labels filter' do
- within ".labels-filter" do
- page.should have_content "feature"
- end
- end
-
- step 'I should see "enhancement" in labels filter' do
- within ".labels-filter" do
- page.should have_content "enhancement"
- end
- end
+ include Select2Helper
step 'I should see "Bugfix1" in issues list' do
within ".issues-list" do
@@ -46,9 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
end
step 'I click link "bug"' do
- within ".labels-filter" do
- click_link "bug"
- end
+ select2('bug', from: "#label_name")
end
step 'I click link "feature"' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 6f6ce439f3e..40c102833a4 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -117,6 +117,20 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
end
+ step 'I click on the Changes tab via Javascript' do
+ find('.diffs-tab').click
+ sleep 2
+ end
+
+ step 'I should see the proper Inline and Side-by-side links' do
+ buttons = all('#commit-diff-viewtype')
+ expect(buttons.count).to eq(2)
+
+ buttons.each do |b|
+ expect(b['href']).should_not have_content('json')
+ end
+ end
+
step 'I switch to the merge request\'s comments tab' do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/features/steps/project/star.rb b/features/steps/project/star.rb
index ae2e4c7a201..50cdfd73c34 100644
--- a/features/steps/project/star.rb
+++ b/features/steps/project/star.rb
@@ -22,12 +22,16 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
# Requires @javascript
step "I click on the star toggle button" do
- find(".star .toggle", visible: true).click
+ find(".star-btn", visible: true).click
+ end
+
+ step 'I redirected to sign in page' do
+ current_path.should == new_user_session_path
end
protected
def has_n_stars(n)
- expect(page).to have_css(".star .count", text: /^#{n}$/, visible: true)
+ expect(page).to have_css(".star-btn .count", text: n, visible: true)
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index a6e77002a01..be133a2920b 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -20,7 +20,7 @@ module API
identifier = sudo_identifier()
# If the sudo is the current user do nothing
- if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
+ if identifier && !(@current_user.id == identifier || @current_user.username == identifier)
render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
@current_user = User.by_username_or_id(identifier)
not_found!("No user id or username for: #{identifier}") if @current_user.nil?
@@ -33,7 +33,7 @@ module API
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
# Regex for integers
- if (!!(identifier =~ /^[0-9]+$/))
+ if !!(identifier =~ /^[0-9]+$/)
identifier.to_i
else
identifier
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 83f65eec6cc..e3fff79d68f 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -88,17 +88,14 @@ module API
present user_project, with: Entities::ProjectWithAccess, user: current_user
end
- # Get a single project events
+ # Get events for a single project
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/events
get ":id/events" do
- limit = (params[:per_page] || 20).to_i
- offset = (params[:page] || 0).to_i * limit
- events = user_project.events.recent.limit(limit).offset(offset)
-
+ events = paginate user_project.events.recent
present events, with: Entities::Event
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 7c8b3250cd0..032a5d76e43 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -61,10 +61,10 @@ module API
authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin, :confirm]
- user = User.build_user(attrs)
admin = attrs.delete(:admin)
- user.admin = admin unless admin.nil?
confirm = !(attrs.delete(:confirm) =~ (/(false|f|no|0)$/i))
+ user = User.build_user(attrs)
+ user.admin = admin unless admin.nil?
user.skip_confirmation! unless confirm
identity_attrs = attributes_for_keys [:provider, :extern_uid]
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index c6087830b40..afd05897509 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -70,16 +70,17 @@ module Backup
# delete backups
$progress.print "Deleting old backups ... "
keep_time = Gitlab.config.backup.keep_time.to_i
- path = Gitlab.config.backup.path
if keep_time > 0
removed = 0
- file_list = Dir.glob(Rails.root.join(path, "*_gitlab_backup.tar"))
- file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
- file_list.sort.each do |timestamp|
- if Time.at(timestamp) < (Time.now - keep_time)
- if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
- removed += 1
+ Dir.chdir(Gitlab.config.backup.path) do
+ file_list = Dir.glob('*_gitlab_backup.tar')
+ file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
+ file_list.sort.each do |timestamp|
+ if Time.at(timestamp) < (Time.now - keep_time)
+ if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
+ removed += 1
+ end
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index d6e609e2c44..bc72b7528d5 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -30,7 +30,7 @@ module Gitlab
def can_push_to_branch?(ref)
return false unless user
-
+
if project.protected_branch?(ref) &&
!(project.developers_can_push_to_protected_branch?(ref) && project.team.developer?(user))
user.can?(:push_code_to_protected_branches, project)
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index f5e8267031c..41bb8d08924 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -79,15 +79,35 @@ module Gitlab
# Used markdown pipelines in GitLab:
# GitlabEmojiFilter - performs emoji replacement.
+ # SanitizationFilter - remove unsafe HTML tags and attributes
#
# see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
filters = [
- HTML::Pipeline::Gitlab::GitlabEmojiFilter
+ HTML::Pipeline::Gitlab::GitlabEmojiFilter,
+ HTML::Pipeline::SanitizationFilter
]
+ whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
+ whitelist[:attributes][:all].push('class', 'id')
+ whitelist[:elements].push('span')
+
+ # Remove the rel attribute that the sanitize gem adds, and remove the
+ # href attribute if it contains inline javascript
+ fix_anchors = lambda do |env|
+ name, node = env[:node_name], env[:node]
+ if name == 'a'
+ node.remove_attribute('rel')
+ if node['href'] && node['href'].match('javascript:')
+ node.remove_attribute('href')
+ end
+ end
+ end
+ whitelist[:transformers].push(fix_anchors)
+
markdown_context = {
asset_root: Gitlab.config.gitlab.url,
- asset_host: Gitlab::Application.config.asset_host
+ asset_host: Gitlab::Application.config.asset_host,
+ whitelist: whitelist
}
markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
@@ -97,18 +117,14 @@ module Gitlab
if options[:xhtml]
saveoptions |= Nokogiri::XML::Node::SaveOptions::AS_XHTML
end
- text = result[:output].to_html(save_with: saveoptions)
- allowed_attributes = ActionView::Base.sanitized_allowed_attributes
- allowed_tags = ActionView::Base.sanitized_allowed_tags
+ text = result[:output].to_html(save_with: saveoptions)
- text = sanitize text.html_safe,
- attributes: allowed_attributes + %w(id class style),
- tags: allowed_tags + %w(table tr td th)
if options[:parse_tasks]
text = parse_tasks(text)
end
- text
+
+ text.html_safe
end
private
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index fea4d2d55d2..43e07e09160 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -29,7 +29,7 @@ module Gitlab
@cmd_status = wait_thr.value.exitstatus
end
- return @cmd_output, @cmd_status
+ [@cmd_output, @cmd_status]
end
end
end
diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb
index 25122666f5e..1f2e5f82dd5 100644
--- a/lib/gitlab/satellite/merge_action.rb
+++ b/lib/gitlab/satellite/merge_action.rb
@@ -97,7 +97,7 @@ module Gitlab
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
update_satellite_source_and_target!(merge_repo)
- if (merge_request.for_fork?)
+ if merge_request.for_fork?
repository = Gitlab::Git::Repository.new(merge_repo.path)
commits = Gitlab::Git::Commit.between(
repository,
diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb
index 70125d539da..f24c6199c44 100644
--- a/lib/gitlab/satellite/satellite.rb
+++ b/lib/gitlab/satellite/satellite.rb
@@ -99,11 +99,7 @@ module Gitlab
heads = repo.heads.map(&:name)
# update or create the parking branch
- if heads.include? PARKING_BRANCH
- repo.git.checkout({}, PARKING_BRANCH)
- else
- repo.git.checkout(default_options({ b: true }), PARKING_BRANCH)
- end
+ repo.git.checkout(default_options({ B: true }), PARKING_BRANCH)
# remove the parking branch from the list of heads ...
heads.delete(PARKING_BRANCH)
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index 9799e54de5d..43093c7d27e 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -19,7 +19,7 @@ module Gitlab
id ||= Gitlab.config.gitlab.default_theme
- return themes[id]
+ themes[id]
end
def self.type_css_class_by_id(id)
diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake
index b4076f8238f..b4c0ae3ff79 100644
--- a/lib/tasks/gitlab/test.rake
+++ b/lib/tasks/gitlab/test.rake
@@ -2,6 +2,7 @@ namespace :gitlab do
desc "GITLAB | Run all tests"
task :test do
cmds = [
+ %W(rake brakeman),
%W(rake rubocop),
%W(rake spinach),
%W(rake spec),
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
new file mode 100644
index 00000000000..a0909cec3bd
--- /dev/null
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe AutocompleteController do
+ let!(:project) { create(:project) }
+ let!(:user) { create(:user) }
+ let!(:user2) { create(:user) }
+
+ context 'project members' do
+ before do
+ sign_in(user)
+ project.team << [user, :master]
+
+ get(:users, project_id: project.id)
+ end
+
+ let(:body) { JSON.parse(response.body) }
+
+ it { body.should be_kind_of(Array) }
+ it { body.size.should eq(1) }
+ it { body.first["username"].should == user.username }
+ end
+
+ context 'group members' do
+ let(:group) { create(:group) }
+
+ before do
+ sign_in(user)
+ group.add_owner(user)
+
+ get(:users, group_id: group.id)
+ end
+
+ let(:body) { JSON.parse(response.body) }
+
+ it { body.should be_kind_of(Array) }
+ it { body.size.should eq(1) }
+ it { body.first["username"].should == user.username }
+ end
+
+ context 'all users' do
+ before do
+ sign_in(user)
+ get(:users)
+ end
+
+ let(:body) { JSON.parse(response.body) }
+
+ it { body.should be_kind_of(Array) }
+ it { body.size.should eq(User.count) }
+ end
+end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index a2db57ad908..e5f33d5a25a 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -95,7 +95,7 @@ describe 'Issues', feature: true do
let(:issue) { @issue }
it 'should allow filtering by issues with no specified milestone' do
- visit namespace_project_issues_path(project.namespace, project, milestone_id: '0')
+ visit namespace_project_issues_path(project.namespace, project, milestone_id: IssuableFinder::NONE)
expect(page).not_to have_content 'foobar'
expect(page).to have_content 'barbaz'
@@ -111,7 +111,7 @@ describe 'Issues', feature: true do
end
it 'should allow filtering by issues with no specified assignee' do
- visit namespace_project_issues_path(project.namespace, project, assignee_id: '0')
+ visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE)
expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz'
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index ddbb4467f10..c631acc591d 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -727,6 +727,36 @@ describe GitlabMarkdownHelper do
expected = ""
expect(markdown(actual)).to match(expected)
end
+
+ it 'should allow whitelisted HTML tags from the user' do
+ actual = '<dl><dt>Term</dt><dd>Definition</dd></dl>'
+ expect(markdown(actual)).to match(actual)
+ end
+
+ it 'should sanitize tags that are not whitelisted' do
+ actual = '<textarea>no inputs allowed</textarea> <blink>no blinks</blink>'
+ expected = 'no inputs allowed no blinks'
+ expect(markdown(actual)).to match(expected)
+ expect(markdown(actual)).not_to match('<.textarea>')
+ expect(markdown(actual)).not_to match('<.blink>')
+ end
+
+ it 'should allow whitelisted tag attributes from the user' do
+ actual = '<a class="custom">link text</a>'
+ expect(markdown(actual)).to match(actual)
+ end
+
+ it 'should sanitize tag attributes that are not whitelisted' do
+ actual = '<a href="http://example.com/bar.html" foo="bar">link text</a>'
+ expected = '<a href="http://example.com/bar.html">link text</a>'
+ expect(markdown(actual)).to match(expected)
+ end
+
+ it 'should sanitize javascript in attributes' do
+ actual = %q(<a href="javascript:alert('foo')">link text</a>)
+ expected = '<a>link text</a>'
+ expect(markdown(actual)).to match(expected)
+ end
end
describe 'markdown for empty repository' do
diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb
new file mode 100644
index 00000000000..0b1cf07b7b0
--- /dev/null
+++ b/spec/helpers/icons_helper_spec.rb
@@ -0,0 +1,109 @@
+require 'spec_helper'
+
+describe IconsHelper do
+ describe 'file_type_icon_class' do
+ it 'returns folder class' do
+ expect(file_type_icon_class('folder', 0, 'folder_name')).to eq 'folder'
+ end
+
+ it 'returns share class' do
+ expect(file_type_icon_class('file', 0120000, 'link')).to eq 'share'
+ end
+
+ it 'returns file-pdf-o class with .pdf' do
+ expect(file_type_icon_class('file', 0, 'filename.pdf')).to eq 'file-pdf-o'
+ end
+
+ it 'returns file-image-o class with .jpg' do
+ expect(file_type_icon_class('file', 0, 'filename.jpg')).to eq 'file-image-o'
+ end
+
+ it 'returns file-image-o class with .JPG' do
+ expect(file_type_icon_class('file', 0, 'filename.JPG')).to eq 'file-image-o'
+ end
+
+ it 'returns file-image-o class with .png' do
+ expect(file_type_icon_class('file', 0, 'filename.png')).to eq 'file-image-o'
+ end
+
+ it 'returns file-archive-o class with .tar' do
+ expect(file_type_icon_class('file', 0, 'filename.tar')).to eq 'file-archive-o'
+ end
+
+ it 'returns file-archive-o class with .TAR' do
+ expect(file_type_icon_class('file', 0, 'filename.TAR')).to eq 'file-archive-o'
+ end
+
+ it 'returns file-archive-o class with .tar.gz' do
+ expect(file_type_icon_class('file', 0, 'filename.tar.gz')).to eq 'file-archive-o'
+ end
+
+ it 'returns file-audio-o class with .mp3' do
+ expect(file_type_icon_class('file', 0, 'filename.mp3')).to eq 'file-audio-o'
+ end
+
+ it 'returns file-audio-o class with .MP3' do
+ expect(file_type_icon_class('file', 0, 'filename.MP3')).to eq 'file-audio-o'
+ end
+
+ it 'returns file-audio-o class with .wav' do
+ expect(file_type_icon_class('file', 0, 'filename.wav')).to eq 'file-audio-o'
+ end
+
+ it 'returns file-video-o class with .avi' do
+ expect(file_type_icon_class('file', 0, 'filename.avi')).to eq 'file-video-o'
+ end
+
+ it 'returns file-video-o class with .AVI' do
+ expect(file_type_icon_class('file', 0, 'filename.AVI')).to eq 'file-video-o'
+ end
+
+ it 'returns file-video-o class with .mp4' do
+ expect(file_type_icon_class('file', 0, 'filename.mp4')).to eq 'file-video-o'
+ end
+
+ it 'returns file-word-o class with .doc' do
+ expect(file_type_icon_class('file', 0, 'filename.doc')).to eq 'file-word-o'
+ end
+
+ it 'returns file-word-o class with .DOC' do
+ expect(file_type_icon_class('file', 0, 'filename.DOC')).to eq 'file-word-o'
+ end
+
+ it 'returns file-word-o class with .docx' do
+ expect(file_type_icon_class('file', 0, 'filename.docx')).to eq 'file-word-o'
+ end
+
+ it 'returns file-excel-o class with .xls' do
+ expect(file_type_icon_class('file', 0, 'filename.xls')).to eq 'file-excel-o'
+ end
+
+ it 'returns file-excel-o class with .XLS' do
+ expect(file_type_icon_class('file', 0, 'filename.XLS')).to eq 'file-excel-o'
+ end
+
+ it 'returns file-excel-o class with .xlsx' do
+ expect(file_type_icon_class('file', 0, 'filename.xlsx')).to eq 'file-excel-o'
+ end
+
+ it 'returns file-excel-o class with .ppt' do
+ expect(file_type_icon_class('file', 0, 'filename.ppt')).to eq 'file-powerpoint-o'
+ end
+
+ it 'returns file-excel-o class with .PPT' do
+ expect(file_type_icon_class('file', 0, 'filename.PPT')).to eq 'file-powerpoint-o'
+ end
+
+ it 'returns file-excel-o class with .pptx' do
+ expect(file_type_icon_class('file', 0, 'filename.pptx')).to eq 'file-powerpoint-o'
+ end
+
+ it 'returns file-text-o class with .unknow' do
+ expect(file_type_icon_class('file', 0, 'filename.unknow')).to eq 'file-text-o'
+ end
+
+ it 'returns file-text-o class with no extension' do
+ expect(file_type_icon_class('file', 0, 'CHANGELOG')).to eq 'file-text-o'
+ end
+ end
+end
diff --git a/spec/models/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb
index 13c8d54a2af..13c8d54a2af 100644
--- a/spec/models/asana_service_spec.rb
+++ b/spec/models/project_services/asana_service_spec.rb
diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb
index fcbf3e45b9a..9f29fbe12b0 100644
--- a/spec/models/project_services/buildbox_service_spec.rb
+++ b/spec/models/project_services/buildbox_service_spec.rb
@@ -36,7 +36,7 @@ describe BuildboxService do
@service.stub(
project: @project,
service_hook: true,
- project_url: 'https://buildbox.io/account-name/example-project',
+ project_url: 'https://buildkite.com/account-name/example-project',
token: 'secret-sauce-webhook-token:secret-sauce-status-token'
)
end
@@ -44,7 +44,7 @@ describe BuildboxService do
describe :webhook_url do
it 'returns the webhook url' do
expect(@service.webhook_url).to eq(
- 'https://webhook.buildbox.io/deliver/secret-sauce-webhook-token'
+ 'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
)
end
end
@@ -52,7 +52,7 @@ describe BuildboxService do
describe :commit_status_path do
it 'returns the correct status page' do
expect(@service.commit_status_path('2ab7834c')).to eq(
- 'https://gitlab.buildbox.io/status/secret-sauce-status-token.json?commit=2ab7834c'
+ 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
)
end
end
@@ -60,7 +60,7 @@ describe BuildboxService do
describe :build_page do
it 'returns the correct build page' do
expect(@service.build_page('2ab7834c', nil)).to eq(
- 'https://buildbox.io/account-name/example-project/builds?commit=2ab7834c'
+ 'https://buildkite.com/account-name/example-project/builds?commit=2ab7834c'
)
end
end
@@ -68,14 +68,14 @@ describe BuildboxService do
describe :builds_page do
it 'returns the correct path to the builds page' do
expect(@service.builds_path).to eq(
- 'https://buildbox.io/account-name/example-project/builds?branch=default-brancho'
+ 'https://buildkite.com/account-name/example-project/builds?branch=default-brancho'
)
end
end
describe :status_img_path do
it 'returns the correct path to the status image' do
- expect(@service.status_img_path).to eq('https://badge.buildbox.io/secret-sauce-status-token.svg')
+ expect(@service.status_img_path).to eq('https://badge.buildkite.com/secret-sauce-status-token.svg')
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index eaec2198dc8..53ccaa4fd67 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -44,3 +44,5 @@ RSpec.configure do |config|
TestEnv.init
end
end
+
+ActiveRecord::Migration.maintain_test_schema!
diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb
index c7cf109a7bb..691f84f39d4 100644
--- a/spec/support/select2_helper.rb
+++ b/spec/support/select2_helper.rb
@@ -17,9 +17,9 @@ module Select2Helper
selector = options[:from]
if options[:multiple]
- execute_script("$('#{selector}').select2('val', ['#{value}']);")
+ execute_script("$('#{selector}').select2('val', ['#{value}'], true);")
else
- execute_script("$('#{selector}').select2('val', '#{value}');")
+ execute_script("$('#{selector}').select2('val', '#{value}', true);")
end
end
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index f869488d8d8..44d70e741b2 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -85,7 +85,7 @@ module TestEnv
end
# We must copy bare repositories because we will push to them.
- system(*%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare}))
+ system(git_env, *%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare}))
end
def copy_repo(project)
@@ -113,4 +113,10 @@ module TestEnv
def factory_repo_name
'gitlab-test'
end
+
+ # Prevent developer git configurations from being persisted to test
+ # repositories
+ def git_env
+ {'GIT_TEMPLATE_DIR' => ''}
+ end
end