summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG22
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js.coffee44
-rw-r--r--app/assets/javascripts/dashboard.js.coffee31
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee7
-rw-r--r--app/assets/javascripts/notes.js.coffee31
-rw-r--r--app/assets/javascripts/pager.js.coffee3
-rw-r--r--app/assets/javascripts/profile.js.coffee10
-rw-r--r--app/assets/javascripts/projects_list.js.coffee49
-rw-r--r--app/assets/javascripts/user.js.coffee11
-rw-r--r--app/assets/javascripts/user_tabs.js.coffee146
-rw-r--r--app/assets/stylesheets/framework/buttons.scss13
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/forms.scss12
-rw-r--r--app/assets/stylesheets/framework/lists.scss1
-rw-r--r--app/assets/stylesheets/framework/nav.scss4
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss6
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss9
-rw-r--r--app/assets/stylesheets/pages/notifications.scss18
-rw-r--r--app/assets/stylesheets/pages/profile.scss67
-rw-r--r--app/assets/stylesheets/pages/todos.scss1
-rw-r--r--app/controllers/concerns/issues_action.rb2
-rw-r--r--app/controllers/concerns/merge_requests_action.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/profiles/keys_controller.rb8
-rw-r--r--app/controllers/projects/badges_controller.rb13
-rw-r--r--app/controllers/projects/commit_controller.rb9
-rw-r--r--app/controllers/projects/compare_controller.rb12
-rw-r--r--app/controllers/projects/forks_controller.rb20
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/users_controller.rb60
-rw-r--r--app/helpers/diff_helper.rb34
-rw-r--r--app/helpers/icons_helper.rb11
-rw-r--r--app/models/commit.rb16
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/label.rb5
-rw-r--r--app/models/merge_request.rb9
-rw-r--r--app/models/merge_request_diff.rb86
-rw-r--r--app/models/note.rb26
-rw-r--r--app/models/project.rb20
-rw-r--r--app/models/project_services/jira_service.rb6
-rw-r--r--app/services/compare_service.rb12
-rw-r--r--app/services/merge_requests/build_service.rb20
-rw-r--r--app/views/abuse_reports/new.html.haml4
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml4
-rw-r--r--app/views/admin/users/keys.html.haml2
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/dashboard/projects/_projects.html.haml7
-rw-r--r--app/views/explore/projects/_filter.html.haml73
-rw-r--r--app/views/explore/projects/_projects.html.haml7
-rw-r--r--app/views/explore/projects/index.html.haml4
-rw-r--r--app/views/groups/_projects.html.haml3
-rw-r--r--app/views/groups/group_members/_group_member.html.haml3
-rw-r--r--app/views/groups/milestones/new.html.haml6
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/profiles/_event_table.html.haml28
-rw-r--r--app/views/profiles/audit_log.html.haml13
-rw-r--r--app/views/profiles/emails/index.html.haml91
-rw-r--r--app/views/profiles/keys/_form.html.haml14
-rw-r--r--app/views/profiles/keys/_key.html.haml25
-rw-r--r--app/views/profiles/keys/_key_details.html.haml2
-rw-r--r--app/views/profiles/keys/_key_table.html.haml20
-rw-r--r--app/views/profiles/keys/index.html.haml29
-rw-r--r--app/views/profiles/keys/new.html.haml17
-rw-r--r--app/views/profiles/notifications/_settings.html.haml4
-rw-r--r--app/views/profiles/notifications/show.html.haml120
-rw-r--r--app/views/profiles/passwords/edit.html.haml51
-rw-r--r--app/views/profiles/preferences/show.html.haml98
-rw-r--r--app/views/profiles/show.html.haml167
-rw-r--r--app/views/projects/blob/_editor.html.haml2
-rw-r--r--app/views/projects/blob/_new_dir.html.haml2
-rw-r--r--app/views/projects/blob/_remove.html.haml2
-rw-r--r--app/views/projects/blob/_upload.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/blob/new.html.haml2
-rw-r--r--app/views/projects/diffs/_diffs.html.haml11
-rw-r--r--app/views/projects/diffs/_text_file.html.haml2
-rw-r--r--app/views/projects/diffs/_warning.html.haml2
-rw-r--r--app/views/projects/forks/_projects.html.haml2
-rw-r--r--app/views/projects/forks/index.html.haml25
-rw-r--r--app/views/projects/issues/_form.html.haml2
-rw-r--r--app/views/projects/labels/_form.html.haml4
-rw-r--r--app/views/projects/labels/_label.html.haml4
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml29
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml10
-rw-r--r--app/views/projects/merge_requests/_show.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_diffs.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml2
-rw-r--r--app/views/projects/milestones/_form.html.haml6
-rw-r--r--app/views/projects/notes/_edit_form.html.haml4
-rw-r--r--app/views/projects/notes/_form.html.haml4
-rw-r--r--app/views/projects/releases/edit.html.haml4
-rw-r--r--app/views/projects/tags/new.html.haml4
-rw-r--r--app/views/projects/wikis/_form.html.haml4
-rw-r--r--app/views/search/_results.html.haml4
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/shared/_commit_message_container.html.haml2
-rw-r--r--app/views/shared/_no_ssh.html.haml2
-rw-r--r--app/views/shared/groups/_list.html.haml6
-rw-r--r--app/views/shared/issuable/_form.html.haml4
-rw-r--r--app/views/shared/projects/_list.html.haml26
-rw-r--r--app/views/users/show.html.haml201
-rw-r--r--app/workers/irker_worker.rb2
-rw-r--r--config/routes.rb11
-rw-r--r--db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb5
-rw-r--r--db/schema.rb1
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--features/groups.feature12
-rw-r--r--features/profile/ssh_keys.feature2
-rw-r--r--features/project/badges/build.feature5
-rw-r--r--features/steps/groups.rb29
-rw-r--r--features/steps/profile/profile.rb10
-rw-r--r--features/steps/profile/ssh_keys.rb4
-rw-r--r--features/steps/project/badges/build.rb4
-rw-r--r--features/steps/project/commits/commits.rb7
-rw-r--r--features/steps/shared/user.rb16
-rw-r--r--features/user.feature13
-rw-r--r--lib/api/commits.rb6
-rw-r--r--lib/api/entities.rb6
-rw-r--r--lib/gitlab/compare_result.rb9
-rw-r--r--lib/gitlab/diff/file.rb2
-rw-r--r--lib/gitlab/diff/parser.rb79
-rw-r--r--lib/gitlab/email/message/repository_push.rb2
-rw-r--r--spec/controllers/commit_controller_spec.rb2
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb8
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb72
-rw-r--r--spec/helpers/diff_helper_spec.rb57
-rw-r--r--spec/javascripts/fixtures/behaviors/quick_submit.html.haml6
-rw-r--r--spec/lib/gitlab/diff/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/email/message/repository_push_spec.rb2
-rw-r--r--spec/models/project_spec.rb77
-rw-r--r--spec/routing/routing_spec.rb5
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb2
-rw-r--r--spec/services/system_note_service_spec.rb4
136 files changed, 1489 insertions, 1034 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 6837037a367..8692f61fe36 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,15 +10,33 @@ v 8.6.0 (unreleased)
- Return empty array instead of 404 when commit has no statuses in commit status API
- Update documentation to reflect Guest role not being enforced on internal projects
- Allow search for logged out users
+ - Don't show Issues/MRs from archived projects in Groups view
+ - Increase the notes polling timeout over time (Roberto Dip)
+
+v 8.5.4
+ - Do not cache requests for badges (including builds badge)
+
+v 8.5.3
+ - Flush repository caches before renaming projects
+ - Sort starred projects on dashboard based on last activity by default
+ - Show commit message in JIRA mention comment
v 8.5.2
- Fix sidebar overlapping content when screen width was below 1200px
- - Fix permissions for deprecated CI build status badge
+ - Don't repeat labels listed on Labels tab
+ - Bring the "branded appearance" feature from EE to CE
- Fix error 500 when commenting on a commit
+ - Show days remaining instead of elapsed time for Milestone
- Fix broken icons on installations with relative URL (Artem Sidorenko)
+ - Fix issue where tag list wasn't refreshed after deleting a tag
- Fix import from gitlab.com (KazSawada)
+ - Improve implementation to check read access to forks and add pagination
+ - Don't show any "2FA required" message if it's not actually required
- Fix help keyboard shortcut on relative URL setups (Artem Sidorenko)
+ - Update Rails to 4.2.5.2
+ - Fix permissions for deprecated CI build status badge
- Don't show "Welcome to GitLab" when the search didn't return any projects
+ - Add Todos documentation
v 8.5.1
- Fix group projects styles
@@ -33,7 +51,7 @@ v 8.5.1
- Fix error 500 when trying to mark an already done todo as "done"
- Fix an issue where MRs weren't sortable
- Issues can now be dragged & dropped into empty milestone lists. This is also
- possible with MRs
+ possible with MRs
- Changed padding & background color for highlighted notes
- Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu)
- Update sentry-raven gem to 0.15.6
diff --git a/Gemfile b/Gemfile
index 134646cf800..c646fb1ff7b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -50,7 +50,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 8.2'
+gem "gitlab_git", '~> 9.0'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -278,7 +278,7 @@ group :development, :test do
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine', '~> 2.2.0'
- gem 'spring', '~> 1.3.6'
+ gem 'spring', '~> 1.6.4'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index e048e2f5a56..bdf7dbfd157 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -357,7 +357,7 @@ GEM
posix-spawn (~> 0.3)
gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1)
- gitlab_git (8.2.0)
+ gitlab_git (9.0.0)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
@@ -763,7 +763,7 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
- spring (1.3.6)
+ spring (1.6.4)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
@@ -929,7 +929,7 @@ DEPENDENCIES
github-markup (~> 1.3.1)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_emoji (~> 0.3.0)
- gitlab_git (~> 8.2)
+ gitlab_git (~> 9.0)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0)
@@ -1016,7 +1016,7 @@ DEPENDENCIES
six (~> 0.2.0)
slack-notifier (~> 1.2.0)
spinach-rails (~> 0.2.1)
- spring (~> 1.3.6)
+ spring (~> 1.6.4)
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.0.0)
spring-commands-teaspoon (~> 0.0.2)
diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee
index 4ec8531d580..61c2e4f698c 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js.coffee
+++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee
@@ -1,23 +1,30 @@
# Quick Submit behavior
#
-# When an input field with the `js-quick-submit` class receives a "Meta+Enter"
-# (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, its parent form is
-# submitted.
+# When a child field of a form with a `js-quick-submit` class receives a
+# "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
+# is submitted.
#
#= require extensions/jquery
#
# ### Example Markup
#
-# <form action="/foo">
-# <input type="text" class="js-quick-submit" />
-# <textarea class="js-quick-submit"></textarea>
+# <form action="/foo" class="js-quick-submit">
+# <input type="text" />
+# <textarea></textarea>
+# <input type="submit" value="Submit" />
# </form>
#
+isMac = ->
+ navigator.userAgent.match(/Macintosh/)
+
+keyCodeIs = (e, keyCode) ->
+ return false if (e.originalEvent && e.originalEvent.repeat) || e.repeat
+ return e.keyCode == keyCode
+
$(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
- return if (e.originalEvent && e.originalEvent.repeat) || e.repeat
- return unless e.keyCode == 13 # Enter
+ return unless keyCodeIs(e, 13) # Enter
- if navigator.userAgent.match(/Macintosh/)
+ if isMac()
return unless (e.metaKey && !e.altKey && !e.ctrlKey && !e.shiftKey)
else
return unless (e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey)
@@ -27,3 +34,22 @@ $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
$form = $(e.target).closest('form')
$form.find('input[type=submit], button[type=submit]').disable()
$form.submit()
+
+# If the user tabs to a submit button on a `js-quick-submit` form, display a
+# tooltip to let them know they could've used the hotkey
+$(document).on 'keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', (e) ->
+ return unless keyCodeIs(e, 9) # Tab
+
+ if isMac()
+ title = "You can also press &#8984;-Enter"
+ else
+ title = "You can also press Ctrl-Enter"
+
+ $this = $(@)
+ $this.tooltip(
+ container: 'body'
+ html: 'true'
+ placement: 'auto top'
+ title: title
+ trigger: 'manual'
+ ).tooltip('show').one('blur', -> $this.tooltip('hide'))
diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee
deleted file mode 100644
index 62143e66cfe..00000000000
--- a/app/assets/javascripts/dashboard.js.coffee
+++ /dev/null
@@ -1,31 +0,0 @@
-@Dashboard =
- init: ->
- $(".projects-list-filter").off('keyup')
- this.initSearch()
-
- initSearch: ->
- @timer = null
- $(".projects-list-filter").on('keyup', ->
- clearTimeout(@timer)
- @timer = setTimeout(Dashboard.filterResults, 500)
- )
-
- filterResults: =>
- $('.projects-list-holder').fadeTo(250, 0.5)
-
- form = null
- form = $("form#project-filter-form")
- search = $(".projects-list-filter").val()
- project_filter_url = form.attr('action') + '?' + form.serialize()
-
- $.ajax
- type: "GET"
- url: form.attr('action')
- data: form.serialize()
- complete: ->
- $('.projects-list-holder').fadeTo(250, 1)
- success: (data) ->
- $('.projects-list-holder').replaceWith(data.html)
- # Change url so if user reload a page - search results are saved
- history.replaceState {page: project_filter_url}, document.title, project_filter_url
- dataType: "json"
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 89f1993797f..d7feb5d5c87 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -16,8 +16,6 @@ class Dispatcher
shortcut_handler = null
switch page
- when 'explore:projects:index', 'explore:projects:starred', 'explore:projects:trending'
- Dashboard.init()
when 'projects:issues:index'
Issues.init()
shortcut_handler = new ShortcutsNavigation()
@@ -59,8 +57,6 @@ class Dispatcher
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
MergeRequests.init()
- when 'dashboard:show', 'root:show'
- Dashboard.init()
when 'dashboard:activity'
new Activities()
when 'dashboard:projects:starred'
@@ -107,9 +103,6 @@ class Dispatcher
new ProjectFork()
when 'projects:artifacts:browse'
new BuildArtifacts()
- when 'users:show'
- new User()
- new Activities()
switch path.first()
when 'admin'
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 3347ab65c90..c95ead22e6c 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -16,11 +16,13 @@ class @Notes
@view = view
@noteable_url = document.URL
@notesCountBadge ||= $(".issuable-details").find(".notes-tab .badge")
+ @basePollingInterval = 15000
+ @maxPollingSteps = 4
- @initRefresh()
- @setupMainTargetNoteForm()
@cleanBinding()
@addBinding()
+ @setPollingInterval()
+ @setupMainTargetNoteForm()
@initTaskList()
addBinding: ->
@@ -37,7 +39,7 @@ class @Notes
# Reopen and close actions for Issue/MR combined with note form submit
$(document).on "click", ".js-comment-button", @updateCloseButton
- $(document).on "keyup", ".js-note-text", @updateTargetButtons
+ $(document).on "keyup input", ".js-note-text", @updateTargetButtons
# remove a note (in general)
$(document).on "click", ".js-note-delete", @removeNote
@@ -91,9 +93,11 @@ class @Notes
clearInterval(Notes.interval)
Notes.interval = setInterval =>
@refresh()
- , 15000
+ , @pollingInterval
refresh: ->
+ return if @refreshing is true
+ refreshing = true
if not document.hidden and document.URL.indexOf(@noteable_url) is 0
@getContent()
@@ -105,12 +109,31 @@ class @Notes
success: (data) =>
notes = data.notes
@last_fetched_at = data.last_fetched_at
+ @setPollingInterval(data.notes.length)
$.each notes, (i, note) =>
if note.discussion_with_diff_html?
@renderDiscussionNote(note)
else
@renderNote(note)
+ always: =>
+ @refreshing = false
+
+ ###
+ Increase @pollingInterval up to 120 seconds on every function call,
+ if `shouldReset` has a truthy value, 'null' or 'undefined' the variable
+ will reset to @basePollingInterval.
+
+ Note: this function is used to gradually increase the polling interval
+ if there aren't new notes coming from the server
+ ###
+ setPollingInterval: (shouldReset = true) ->
+ nthInterval = @basePollingInterval * Math.pow(2, @maxPollingSteps - 1)
+ if shouldReset
+ @pollingInterval = @basePollingInterval
+ else if @pollingInterval < nthInterval
+ @pollingInterval *= 2
+ @initRefresh()
###
Render note in main comments area.
diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee
index d639303aed3..0ff83b7f0c8 100644
--- a/app/assets/javascripts/pager.js.coffee
+++ b/app/assets/javascripts/pager.js.coffee
@@ -1,6 +1,7 @@
@Pager =
init: (@limit = 0, preload, @disable = false) ->
- @loading = $(".loading")
+ @loading = $('.loading').first()
+
if preload
@offset = 0
@getOld()
diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee
index 69d590a7533..9110b732adc 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile.js.coffee
@@ -48,7 +48,7 @@ class @Profile
$filename.text($filename.data('label'))
$('.js-upload-user-avatar').on 'click', ->
- $('.edit_user').submit()
+ $('.edit-user').submit()
$avatarInput.on "change", ->
form = $(this).closest("form")
@@ -63,3 +63,11 @@ class @Profile
fileData = reader.readAsDataURL(this.files[0])
+$ ->
+ # Extract the SSH Key title from its comment
+ $(document).on 'focusout.ssh_key', '#key_key', ->
+ $title = $('#key_title')
+ comment = $(@).val().match(/^\S+ \S+ (.+)\n?$/)
+
+ if comment && comment.length > 1 && $title.val() == ''
+ $title.val(comment[1]).change()
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index eab34be652a..ed5206368ce 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -1,26 +1,31 @@
-class @ProjectsList
- constructor: ->
- $(".projects-list .js-expand").on 'click', (e) ->
- e.preventDefault()
- list = $(this).closest('.projects-list')
+@ProjectsList =
+ init: ->
+ $(".projects-list-filter").off('keyup')
+ this.initSearch()
- $("#filter_projects").on 'keyup', ->
- ProjectsList.filter_results($("#filter_projects"))
+ initSearch: ->
+ @timer = null
+ $(".projects-list-filter").on('keyup', ->
+ clearTimeout(@timer)
+ @timer = setTimeout(ProjectsList.filterResults, 500)
+ )
- @filter_results: ($element) ->
- terms = $element.val()
- filterSelector = $element.data('filter-selector') || 'span.filter-title'
+ filterResults: =>
+ $('.projects-list-holder').fadeTo(250, 0.5)
- if not terms
- $(".projects-list li").show()
- $('.gl-pagination').show()
- else
- $(".projects-list li").each (index) ->
- $this = $(this)
- name = $this.find(filterSelector).text()
+ form = null
+ form = $("form#project-filter-form")
+ search = $(".projects-list-filter").val()
+ project_filter_url = form.attr('action') + '?' + form.serialize()
- if name.toLowerCase().indexOf(terms.toLowerCase()) == -1
- $this.hide()
- else
- $this.show()
- $('.gl-pagination').hide()
+ $.ajax
+ type: "GET"
+ url: form.attr('action')
+ data: form.serialize()
+ complete: ->
+ $('.projects-list-holder').fadeTo(250, 1)
+ success: (data) ->
+ $('.projects-list-holder').replaceWith(data.html)
+ # Change url so if user reload a page - search results are saved
+ history.replaceState {page: project_filter_url}, document.title, project_filter_url
+ dataType: "json"
diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee
index ec4271b092c..2882a90d118 100644
--- a/app/assets/javascripts/user.js.coffee
+++ b/app/assets/javascripts/user.js.coffee
@@ -1,10 +1,17 @@
class @User
- constructor: ->
+ constructor: (@opts) ->
$('.profile-groups-avatars').tooltip("placement": "top")
- new ProjectsList()
+
+ @initTabs()
$('.hide-project-limit-message').on 'click', (e) ->
path = '/'
$.cookie('hide_project_limit_message', 'false', { path: path })
$(@).parents('.project-limit-message').remove()
e.preventDefault()
+
+ initTabs: ->
+ new UserTabs(
+ parentEl: '.user-profile'
+ action: @opts.action
+ )
diff --git a/app/assets/javascripts/user_tabs.js.coffee b/app/assets/javascripts/user_tabs.js.coffee
new file mode 100644
index 00000000000..09b7eec9104
--- /dev/null
+++ b/app/assets/javascripts/user_tabs.js.coffee
@@ -0,0 +1,146 @@
+# UserTabs
+#
+# Handles persisting and restoring the current tab selection and lazily-loading
+# content on the Users#show page.
+#
+# ### Example Markup
+#
+# <ul class="nav-links">
+# <li class="activity-tab active">
+# <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
+# Activity
+# </a>
+# </li>
+# <li class="groups-tab">
+# <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
+# Groups
+# </a>
+# </li>
+# <li class="contributed-tab">
+# <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
+# Contributed projects
+# </a>
+# </li>
+# <li class="projects-tab">
+# <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
+# Personal projects
+# </a>
+# </li>
+# </ul>
+#
+# <div class="tab-content">
+# <div class="tab-pane" id="activity">
+# Activity Content
+# </div>
+# <div class="tab-pane" id="groups">
+# Groups Content
+# </div>
+# <div class="tab-pane" id="contributed">
+# Contributed projects content
+# </div>
+# <div class="tab-pane" id="projects">
+# Projects content
+# </div>
+# </div>
+#
+# <div class="loading-status">
+# <div class="loading">
+# Loading Animation
+# </div>
+# </div>
+#
+class @UserTabs
+ constructor: (opts) ->
+ {
+ @action = 'activity'
+ @defaultAction = 'activity'
+ @parentEl = $(document)
+ } = opts
+
+ # Make jQuery object if selector is provided
+ @parentEl = $(@parentEl) if typeof @parentEl is 'string'
+
+ # Store the `location` object, allowing for easier stubbing in tests
+ @_location = location
+
+ # Set tab states
+ @loaded = {}
+ for item in @parentEl.find('.nav-links a')
+ @loaded[$(item).attr 'data-action'] = false
+
+ # Actions
+ @actions = Object.keys @loaded
+
+ @bindEvents()
+
+ # Set active tab
+ @action = @defaultAction if @action is 'show'
+ @activateTab(@action)
+
+ bindEvents: ->
+ # Toggle event listeners
+ @parentEl
+ .off 'shown.bs.tab', '.nav-links a[data-toggle="tab"]'
+ .on 'shown.bs.tab', '.nav-links a[data-toggle="tab"]', @tabShown
+
+ tabShown: (event) =>
+ $target = $(event.target)
+ action = $target.data('action')
+ source = $target.attr('href')
+
+ @setTab(source, action)
+ @setCurrentAction(action)
+
+ activateTab: (action) ->
+ @parentEl.find(".nav-links .#{action}-tab a").tab('show')
+
+ setTab: (source, action) ->
+ return if @loaded[action] is true
+
+ if action is 'activity'
+ @loadActivities(source)
+
+ if action in ['groups', 'contributed', 'projects']
+ @loadTab(source, action)
+
+ loadTab: (source, action) ->
+ $.ajax
+ beforeSend: => @toggleLoading(true)
+ complete: => @toggleLoading(false)
+ dataType: 'json'
+ type: 'GET'
+ url: "#{source}.json"
+ success: (data) =>
+ tabSelector = 'div#' + action
+ @parentEl.find(tabSelector).html(data.html)
+ @loaded[action] = true
+
+ loadActivities: (source) ->
+ return if @loaded['activity'] is true
+
+ $calendarWrap = @parentEl.find('.user-calendar')
+ $calendarWrap.load($calendarWrap.data('href'))
+
+ new Activities()
+ @loaded['activity'] = true
+
+ toggleLoading: (status) ->
+ @parentEl.find('.loading-status .loading').toggle(status)
+
+ setCurrentAction: (action) ->
+ # Remove possible actions from URL
+ regExp = new RegExp('\/(' + @actions.join('|') + ')(\.html)?\/?$')
+ new_state = @_location.pathname
+ new_state = new_state.replace(/\/+$/, "") # remove trailing slashes
+ new_state = new_state.replace(regExp, '')
+
+ # Append the new action if we're on a tab other than 'activity'
+ unless action == @defaultAction
+ new_state += "/#{action}"
+
+ # Ensure parameters and hash come along for the ride
+ new_state += @_location.search + @_location.hash
+
+ history.replaceState {turbolinks: true, url: new_state}, document.title, new_state
+
+ new_state
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 50aa170d24c..8d475137b03 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -143,6 +143,19 @@
}
}
+.btn-transparent {
+ color: $btn-transparent-color;
+ background-color: transparent;
+ border: 0;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: transparent;
+ box-shadow: none;
+ }
+}
+
.btn-block {
width: 100%;
margin: 0;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 9e8506b2a81..1d3c71932a8 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -6,11 +6,15 @@
.cdark { color: #444 }
/** COMMON CLASSES **/
+.prepend-top-0 { margin-top: 0; }
+.prepend-top-5 { margin-top: 5px; }
.prepend-top-10 { margin-top:10px }
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top:20px }
.prepend-left-10 { margin-left:10px }
+.prepend-left-default { margin-left:$gl-padding }
.prepend-left-20 { margin-left:20px }
+.append-right-5 { margin-right: 5px }
.append-right-10 { margin-right:10px }
.append-right-20 { margin-right:20px }
.append-bottom-10 { margin-bottom:10px }
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index d097e4d32f7..6c08005812e 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -28,15 +28,15 @@ input[type='search'].search-input {
}
&.search-input:-moz-placeholder { /* Firefox 18- */
- text-align: center;
+ text-align: center;
}
&.search-input::-moz-placeholder { /* Firefox 19+ */
- text-align: center;
+ text-align: center;
}
- &.search-input:-ms-input-placeholder {
- text-align: center;
+ &.search-input:-ms-input-placeholder {
+ text-align: center;
}
}
@@ -69,6 +69,10 @@ label {
&.inline-label {
margin: 0;
}
+
+ &.label-light {
+ font-weight: 600;
+ }
}
.inline-input-group {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index b6a781f79de..bfec0911b3c 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -3,6 +3,7 @@
*
*/
.well-list {
+ position: relative;
margin: 0;
padding: 0;
list-style: none;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index d24faa897a1..7de874c8bcd 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -79,6 +79,10 @@
> .dropdown {
margin-right: $gl-padding-top;
display: inline-block;
+
+ &:last-child {
+ margin-right: 0;
+ }
}
> .btn {
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 3e709244879..1e9959429cd 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -167,12 +167,6 @@
}
}
-.alert-help {
- background-color: $background-color;
- border: 1px solid $border-color;
- color: $gl-gray;
-}
-
// Typography =================================================================
.text-primary,
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 8d8f41287da..48570abff49 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -196,7 +196,7 @@ body {
h1, h2, h3, h4, h5, h6 {
color: $gl-header-color;
- font-weight: 500;
+ font-weight: 600;
}
/** CODE **/
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 7834cb0bfa5..6fc62f7f201 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -34,6 +34,12 @@ $error-exclamation-point: #E62958;
$border-radius-default: 3px;
$list-title-color: #333333;
$list-text-color: #555555;
+$profile-settings-link-color: $md-link-color;
+
+$btn-transparent-color: #8F8F8F;
+
+$ssh-key-icon-color: #8F8F8F;
+$ssh-key-icon-size: 18px;
/*
* Color schema
@@ -92,6 +98,9 @@ $border-red-light: #E52C5A;
$border-red-normal: #D22852;
$border-red-dark: #CA264F;
+$help-well-bg: #FAFAFA;
+$help-well-border: #E5E5E5;
+
/* header */
$light-grey-header: #faf9f9;
diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss
index cc273f55222..94fbbef3c77 100644
--- a/app/assets/stylesheets/pages/notifications.scss
+++ b/app/assets/stylesheets/pages/notifications.scss
@@ -1,16 +1,18 @@
-.global-notifications-form .level-title {
- font-size: 15px;
- color: #333;
- font-weight: bold;
+.notification-list-item {
+ line-height: 34px;
}
-.notification-icon-holder {
- width: 20px;
- float: left;
+.notification {
+ position: relative;
+ top: 1px;
+
+ > .fa {
+ font-size: 18px;
+ }
}
.ns-part {
- color: $gl-primary;
+ color: $gl-text-green;
}
.ns-watch {
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index de4d9fd80fa..4826b994e37 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -5,12 +5,24 @@
}
}
-.profile-avatar-form-option {
- hr {
- margin: 10px 0;
+.avatar-image {
+ @media (min-width: $screen-sm-min) {
+ float: left;
+ margin-bottom: 0;
}
}
+.avatar-file-name {
+ position: relative;
+ top: 2px;
+ display: inline-block;
+}
+
+.account-btn-link,
+.profile-settings-sidebar a {
+ color: $profile-settings-link-color;
+}
+
.oauth-buttons {
.btn-group {
margin-right: 10px;
@@ -42,6 +54,18 @@
}
}
+.account-well {
+ padding: 10px 10px;
+ background-color: $help-well-bg;
+ border: 1px solid $help-well-border;
+ border-radius: $border-radius-base;
+
+ ul {
+ padding-left: 20px;
+ margin-bottom: 0;
+ }
+}
+
.calendar-hint {
margin-top: -12px;
float: right;
@@ -79,6 +103,13 @@
margin: auto;
}
+.user-avatar-button {
+ .file-name {
+ display: inline-block;
+ padding-left: 10px;
+ }
+}
+
.modal-profile-crop {
.modal-dialog {
width: 500px;
@@ -114,3 +145,33 @@
width: auto;
}
}
+
+.key-list-item {
+ .key-list-item-info {
+ @media (min-width: $screen-sm-min) {
+ float: left;
+ }
+ }
+
+ .description {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+}
+
+.key-icon {
+ color: $ssh-key-icon-color;
+ font-size: $ssh-key-icon-size;
+ line-height: 42px;
+}
+
+.key-created-at {
+ line-height: 42px;
+}
+
+.profile-settings-content {
+ a {
+ color: $profile-settings-link-color;
+ }
+}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 0dc5a905f99..479c3c16d46 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -8,6 +8,7 @@
.badge.todos-pending-count {
background-color: #7f8fa4;
margin-top: -5px;
+ font-weight: normal;
}
}
}
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index 5b098628557..ef8e74a4641 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -2,7 +2,7 @@ module IssuesAction
extend ActiveSupport::Concern
def issues
- @issues = get_issues_collection
+ @issues = get_issues_collection.non_archived
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
@issues = @issues.preload(:author, :project)
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index f6de696e84d..9c49596bd0b 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -2,7 +2,7 @@ module MergeRequestsAction
extend ActiveSupport::Concern
def merge_requests
- @merge_requests = get_merge_requests_collection
+ @merge_requests = get_merge_requests_collection.non_archived
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index aeafbd30143..dc880b634e5 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -31,7 +31,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
def starred
- @projects = current_user.starred_projects
+ @projects = current_user.starred_projects.sorted_by_activity
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort])
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index f3224148fda..b88c080352b 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -3,23 +3,21 @@ class Profiles::KeysController < Profiles::ApplicationController
def index
@keys = current_user.keys
+ @key = Key.new
end
def show
@key = current_user.keys.find(params[:id])
end
- def new
- @key = current_user.keys.new
- end
-
def create
@key = current_user.keys.new(key_params)
if @key.save
redirect_to profile_key_path(@key)
else
- render 'new'
+ @keys = current_user.keys.select(&:persisted?)
+ render :index
end
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index a4dd94b941c..dc9c96df003 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -1,4 +1,6 @@
class Projects::BadgesController < Projects::ApplicationController
+ before_action :set_no_cache
+
def build
respond_to do |format|
format.html { render_404 }
@@ -8,4 +10,15 @@ class Projects::BadgesController < Projects::ApplicationController
end
end
end
+
+ private
+
+ def set_no_cache
+ expires_now
+
+ # Add some deprecated headers for older agents
+ #
+ response.headers['Pragma'] = 'no-cache'
+ response.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
+ end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 97d31a4229a..576fa3cedb2 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -3,6 +3,7 @@
# Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController
include CreatesCommit
+ include DiffHelper
# Authorize
before_action :require_non_empty_project
@@ -100,12 +101,10 @@ class Projects::CommitController < Projects::ApplicationController
def define_show_vars
return git_not_found! unless commit
- if params[:w].to_i == 1
- @diffs = commit.diffs({ ignore_whitespace_change: true })
- else
- @diffs = commit.diffs
- end
+ opts = diff_options
+ opts[:ignore_whitespace_change] = true if params[:format] == 'diff'
+ @diffs = commit.diffs(opts)
@diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index dc5d217f3e4..671d5c23024 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -1,6 +1,8 @@
require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController
+ include DiffHelper
+
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
@@ -11,16 +13,14 @@ class Projects::CompareController < Projects::ApplicationController
end
def show
- diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
-
- compare_result = CompareService.new.
+ compare = CompareService.new.
execute(@project, @head_ref, @project, @base_ref, diff_options)
- if compare_result
- @commits = Commit.decorate(compare_result.commits, @project)
- @diffs = compare_result.diffs
+ if compare
+ @commits = Commit.decorate(compare.commits, @project)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@base_ref, @head_ref)
+ @diffs = compare.diffs(diff_options)
@diff_refs = [@base_commit, @commit]
@line_notes = []
end
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index a0835c9aad0..7b202f3862f 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -6,20 +6,24 @@ class Projects::ForksController < Projects::ApplicationController
def index
base_query = project.forks.includes(:creator)
- @forks = if current_user
- base_query.where('projects.visibility_level IN (?) OR projects.id IN (?)',
- Project.public_and_internal_levels,
- current_user.authorized_projects.pluck(:id))
- else
- base_query.where('projects.visibility_level = ?', Project::PUBLIC)
- end
-
+ @forks = base_query.merge(ProjectsFinder.new.execute(current_user))
@total_forks_count = base_query.size
@private_forks_count = @total_forks_count - @forks.size
@public_forks_count = @total_forks_count - @private_forks_count
@sort = params[:sort] || 'id_desc'
+ @forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present?
@forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE)
+
+ respond_to do |format|
+ format.html
+
+ format.json do
+ render json: {
+ html: view_to_html_string("projects/forks/_projects", projects: @forks)
+ }
+ end
+ end
end
def new
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5fe21694605..03ba289eb94 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,4 +1,6 @@
class Projects::MergeRequestsController < Projects::ApplicationController
+ include DiffHelper
+
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
@@ -111,7 +113,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.last_commit
@base_commit = @merge_request.diff_base_commit
- @diffs = @merge_request.compare_diffs
+ @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@ci_commit = @merge_request.ci_commit
@statuses = @ci_commit.statuses if @ci_commit
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 626213c6728..4b1cf242885 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -3,13 +3,6 @@ class UsersController < ApplicationController
before_action :set_user
def show
- @contributed_projects = contributed_projects.joined(@user).reject(&:forked?)
-
- @projects = PersonalProjectsFinder.new(@user).execute(current_user)
- @projects = @projects.page(params[:page]).per(PER_PAGE)
-
- @groups = @user.groups.order_id_desc
-
respond_to do |format|
format.html
@@ -25,6 +18,45 @@ class UsersController < ApplicationController
end
end
+ def groups
+ load_groups
+
+ respond_to do |format|
+ format.html { render 'show' }
+ format.json do
+ render json: {
+ html: view_to_html_string("shared/groups/_list", groups: @groups)
+ }
+ end
+ end
+ end
+
+ def projects
+ load_projects
+
+ respond_to do |format|
+ format.html { render 'show' }
+ format.json do
+ render json: {
+ html: view_to_html_string("shared/projects/_list", projects: @projects, remote: true)
+ }
+ end
+ end
+ end
+
+ def contributed
+ load_contributed_projects
+
+ respond_to do |format|
+ format.html { render 'show' }
+ format.json do
+ render json: {
+ html: view_to_html_string("shared/projects/_list", projects: @contributed_projects)
+ }
+ end
+ end
+ end
+
def calendar
calendar = contributions_calendar
@timestamps = calendar.timestamps
@@ -69,6 +101,20 @@ class UsersController < ApplicationController
limit_recent(20, params[:offset])
end
+ def load_projects
+ @projects =
+ PersonalProjectsFinder.new(@user).execute(current_user)
+ .page(params[:page]).per(PER_PAGE)
+ end
+
+ def load_contributed_projects
+ @contributed_projects = contributed_projects.joined(@user)
+ end
+
+ def load_groups
+ @groups = @user.groups.order_id_desc
+ end
+
def projects_for_current_user
ProjectsFinder.new.execute(current_user)
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index d76db867c5a..ff32e834499 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -12,40 +12,20 @@ module DiffHelper
params[:view] == 'parallel' ? 'parallel' : 'inline'
end
- def allowed_diff_size
- if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_FILES
- else
- Commit::DIFF_SAFE_FILES
- end
+ def diff_hard_limit_enabled?
+ params[:force_show_diff].present?
end
- def allowed_diff_lines
+ def diff_options
+ options = { ignore_whitespace_change: params[:w] == '1' }
if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_LINES
- else
- Commit::DIFF_SAFE_LINES
+ options.merge!(Commit.max_diff_options)
end
+ options
end
def safe_diff_files(diffs, diff_refs)
- lines = 0
- safe_files = []
- diffs.first(allowed_diff_size).each do |diff|
- lines += diff.diff.lines.count
- break if lines > allowed_diff_lines
- safe_files << Gitlab::Diff::File.new(diff, diff_refs)
- end
- safe_files
- end
-
- def diff_hard_limit_enabled?
- # Enabling hard limit allows user to see more diff information
- if params[:force_show_diff].present?
- true
- else
- false
- end
+ diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs) }
end
def generate_line_code(file_path, line)
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 84c6d0883b0..ab3ef454e1c 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -10,6 +10,15 @@ module IconsHelper
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
+ def audit_icon(names, options = {})
+ case names
+ when "standard"
+ names = "key"
+ end
+
+ options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
+ end
+
def spinner(text = nil, visible = false)
css_class = 'loading'
css_class << ' hide' unless visible
@@ -37,7 +46,7 @@ module IconsHelper
else # Gitlab::VisibilityLevel::PUBLIC
'globe'
end
-
+
name << " fw" if fw
icon(name)
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 3224f5457f0..ce0b85d50cf 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -12,12 +12,7 @@ class Commit
attr_accessor :project
- # Safe amount of changes (files and lines) in one commit to render
- # Used to prevent 500 error on huge commits by suppressing diff
- #
- # User can force display of diff above this size
- DIFF_SAFE_FILES = 100 unless defined?(DIFF_SAFE_FILES)
- DIFF_SAFE_LINES = 5000 unless defined?(DIFF_SAFE_LINES)
+ DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
# Commits above this size will not be rendered in HTML
DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES)
@@ -36,13 +31,20 @@ class Commit
# Calculate number of lines to render for diffs
def diff_line_count(diffs)
- diffs.reduce(0) { |sum, d| sum + d.diff.lines.count }
+ diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) }
end
# Truncate sha to 8 characters
def truncate_sha(sha)
sha[0..7]
end
+
+ def max_diff_options
+ {
+ max_files: DIFF_HARD_LIMIT_FILES,
+ max_lines: DIFF_HARD_LIMIT_LINES,
+ }
+ end
end
attr_accessor :raw
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index e5f089fb8a0..286d6655861 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -38,6 +38,7 @@ module Issuable
scope :join_project, -> { joins(:project) }
scope :references_project, -> { references(:project) }
+ scope :non_archived, -> { join_project.merge(Project.non_archived) }
delegate :name,
:email,
diff --git a/app/models/label.rb b/app/models/label.rb
index 07a1db4abe5..c34f4e4ba60 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -27,6 +27,7 @@ class Label < ActiveRecord::Base
belongs_to :project
has_many :label_links, dependent: :destroy
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
+ has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest'
validates :color, color: true, allow_blank: false
validates :project, presence: true, unless: Proc.new { |service| service.template? }
@@ -90,6 +91,10 @@ class Label < ActiveRecord::Base
issues.closed.count
end
+ def open_merge_requests_count
+ merge_requests.opened.count
+ end
+
def template?
template
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 1543ef311d7..025b522cf66 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -48,7 +48,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff
after_update :update_merge_request_diff
- delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil
+ delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
@@ -56,8 +56,7 @@ class MergeRequest < ActiveRecord::Base
# Temporary fields to store compare vars
# when creating new merge request
- attr_accessor :can_be_created, :compare_failed,
- :compare_commits, :compare_diffs
+ attr_accessor :can_be_created, :compare_commits, :compare
state_machine :state, initial: :opened do
event :close do
@@ -182,6 +181,10 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
+ def diff_size
+ merge_request_diff.size
+ end
+
def diff_base_commit
if merge_request_diff
merge_request_diff.base_commit
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index c95179d6046..df08d3a6dfb 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -19,14 +19,15 @@ class MergeRequestDiff < ActiveRecord::Base
# Prevent store of diff if commits amount more then 500
COMMITS_SAFE_SIZE = 500
- attr_reader :commits, :diffs, :diffs_no_whitespace
-
belongs_to :merge_request
delegate :target_branch, :source_branch, to: :merge_request, prefix: nil
state_machine :state, initial: :empty do
state :collected
+ state :overflow
+ # Deprecated states: these are no longer used but these values may still occur
+ # in the database.
state :timeout
state :overflow_commits_safe_size
state :overflow_diff_files_limit
@@ -43,19 +44,23 @@ class MergeRequestDiff < ActiveRecord::Base
reload_diffs
end
- def diffs
- @diffs ||= (load_diffs(st_diffs) || [])
+ def size
+ real_size.presence || diffs.size
end
- def diffs_no_whitespace
- compare_result = Gitlab::CompareResult.new(
- Gitlab::Git::Compare.new(
- self.repository.raw_repository,
- self.target_branch,
- self.source_sha,
- ), { ignore_whitespace_change: true }
- )
- @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs))
+ def diffs(options={})
+ if options[:ignore_whitespace_change]
+ @diffs_no_whitespace ||= begin
+ compare = Gitlab::Git::Compare.new(
+ self.repository.raw_repository,
+ self.target_branch,
+ self.source_sha,
+ )
+ compare.diffs(options)
+ end
+ else
+ @diffs ||= load_diffs(st_diffs, options)
+ end
end
def commits
@@ -94,16 +99,18 @@ class MergeRequestDiff < ActiveRecord::Base
end
end
- def load_diffs(raw)
- if raw.respond_to?(:map)
- raw.map { |hash| Gitlab::Git::Diff.new(hash) }
+ def load_diffs(raw, options)
+ if raw.respond_to?(:each)
+ Gitlab::Git::DiffCollection.new(raw, options)
+ else
+ Gitlab::Git::DiffCollection.new([])
end
end
# Collect array of Git::Commit objects
# between target and source branches
def unmerged_commits
- commits = compare_result.commits
+ commits = compare.commits
if commits.present?
commits = Commit.decorate(commits, merge_request.source_project).
@@ -133,27 +140,21 @@ class MergeRequestDiff < ActiveRecord::Base
if commits.size.zero?
self.state = :empty
- elsif commits.size > COMMITS_SAFE_SIZE
- self.state = :overflow_commits_safe_size
else
- new_diffs = unmerged_diffs
- end
+ diff_collection = unmerged_diffs
- if new_diffs.any?
- if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES
- self.state = :overflow_diff_files_limit
- new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES)
+ if diff_collection.overflow?
+ # Set our state to 'overflow' to make the #empty? and #collected?
+ # methods (generated by StateMachine) return false.
+ self.state = :overflow
end
- if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES
- self.state = :overflow_diff_lines_limit
- new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES)
- end
- end
+ self.real_size = diff_collection.real_size
- if new_diffs.present?
- new_diffs = dump_commits(new_diffs)
- self.state = :collected
+ if diff_collection.any?
+ new_diffs = dump_diffs(diff_collection)
+ self.state = :collected
+ end
end
self.st_diffs = new_diffs
@@ -166,10 +167,7 @@ class MergeRequestDiff < ActiveRecord::Base
# Collect array of Git::Diff objects
# between target and source branches
def unmerged_diffs
- compare_result.diffs || []
- rescue Gitlab::Git::Diff::TimeoutError
- self.state = :timeout
- []
+ compare.diffs(Commit.max_diff_options)
end
def repository
@@ -181,18 +179,16 @@ class MergeRequestDiff < ActiveRecord::Base
source_commit.try(:sha)
end
- def compare_result
- @compare_result ||=
+ def compare
+ @compare ||=
begin
# Update ref for merge request
merge_request.fetch_ref
- Gitlab::CompareResult.new(
- Gitlab::Git::Compare.new(
- self.repository.raw_repository,
- self.target_branch,
- self.source_sha
- )
+ Gitlab::Git::Compare.new(
+ self.repository.raw_repository,
+ self.target_branch,
+ self.source_sha
)
end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index d287e0f3c6d..1a7b2ba6d42 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -131,9 +131,11 @@ class Note < ActiveRecord::Base
end
def find_diff
- return nil unless noteable && noteable.diffs.present?
+ return nil unless noteable
+ return @diff if defined?(@diff)
- @diff ||= noteable.diffs.find do |d|
+ # Don't use ||= because nil is a valid value for @diff
+ @diff = noteable.diffs(Commit.max_diff_options).find do |d|
Digest::SHA1.hexdigest(d.new_path) == diff_file_index if d.new_path
end
end
@@ -165,20 +167,16 @@ class Note < ActiveRecord::Base
def active?
return true unless self.diff
return false unless noteable
+ return @active if defined?(@active)
- noteable.diffs.each do |mr_diff|
- next unless mr_diff.new_path == self.diff.new_path
+ diffs = noteable.diffs(Commit.max_diff_options)
+ notable_diff = diffs.find { |d| d.new_path == self.diff.new_path }
- lines = Gitlab::Diff::Parser.new.parse(mr_diff.diff.lines.to_a)
+ return @active = false if notable_diff.nil?
- lines.each do |line|
- if line.text == diff_line
- return true
- end
- end
- end
-
- false
+ parsed_lines = Gitlab::Diff::Parser.new.parse(notable_diff.diff.each_line)
+ # We cannot use ||= because @active may be false
+ @active = parsed_lines.any? { |line_obj| line_obj.text == diff_line }
end
def outdated?
@@ -263,7 +261,7 @@ class Note < ActiveRecord::Base
end
def diff_lines
- @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.lines)
+ @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
end
def highlighted_diff_lines
diff --git a/app/models/project.rb b/app/models/project.rb
index 6f5d592755a..148eab692ff 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -278,7 +278,7 @@ class Project < ActiveRecord::Base
end
def search_by_title(query)
- where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
+ non_archived.where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
end
def find_with_namespace(id)
@@ -711,6 +711,8 @@ class Project < ActiveRecord::Base
old_path_with_namespace = File.join(namespace_dir, path_was)
new_path_with_namespace = File.join(namespace_dir, path)
+ expire_caches_before_rename(old_path_with_namespace)
+
if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
@@ -739,6 +741,22 @@ class Project < ActiveRecord::Base
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
end
+ # Expires various caches before a project is renamed.
+ def expire_caches_before_rename(old_path)
+ repo = Repository.new(old_path, self)
+ wiki = Repository.new("#{old_path}.wiki", self)
+
+ if repo.exists?
+ repo.expire_cache
+ repo.expire_emptiness_caches
+ end
+
+ if wiki.exists?
+ wiki.expire_cache
+ wiki.expire_emptiness_caches
+ end
+ end
+
def hook_attrs
{
name: name,
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index f6571fc063e..aba37921c09 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -108,7 +108,8 @@ class JiraService < IssueTrackerService
},
entity: {
name: noteable_name.humanize.downcase,
- url: entity_url
+ url: entity_url,
+ title: noteable.title
}
}
@@ -196,10 +197,11 @@ class JiraService < IssueTrackerService
user_url = data[:user][:url]
entity_name = data[:entity][:name]
entity_url = data[:entity][:url]
+ entity_title = data[:entity][:title]
project_name = data[:project][:name]
message = {
- body: "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]."
+ body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'}
}
unless existing_comment?(issue_name, message[:body])
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index ec581658fc1..e2bccbdbcc3 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -1,7 +1,7 @@
require 'securerandom'
# Compare 2 branches for one repo or between repositories
-# and return Gitlab::CompareResult object that responds to commits and diffs
+# and return Gitlab::Git::Compare object that responds to commits and diffs
class CompareService
def execute(source_project, source_branch, target_project, target_branch, diff_options = {})
source_commit = source_project.commit(source_branch)
@@ -20,12 +20,10 @@ class CompareService
)
end
- Gitlab::CompareResult.new(
- Gitlab::Git::Compare.new(
- target_project.repository.raw_repository,
- target_branch,
- source_sha,
- ), diff_options
+ Gitlab::Git::Compare.new(
+ target_project.repository.raw_repository,
+ target_branch,
+ source_sha,
)
end
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index c0700d953dd..954746a39a5 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -5,9 +5,7 @@ module MergeRequests
# Set MR attributes
merge_request.can_be_created = false
- merge_request.compare_failed = false
merge_request.compare_commits = []
- merge_request.compare_diffs = []
merge_request.source_project = project unless merge_request.source_project
merge_request.target_project ||= (project.forked_from_project || project)
merge_request.target_branch ||= merge_request.target_project.default_branch
@@ -21,35 +19,23 @@ module MergeRequests
return build_failed(merge_request, message)
end
- compare_result = CompareService.new.execute(
+ compare = CompareService.new.execute(
merge_request.source_project,
merge_request.source_branch,
merge_request.target_project,
merge_request.target_branch,
)
- commits = compare_result.commits
+ commits = compare.commits
# At this point we decide if merge request can be created
# If we have at least one commit to merge -> creation allowed
if commits.present?
merge_request.compare_commits = Commit.decorate(commits, merge_request.source_project)
merge_request.can_be_created = true
- merge_request.compare_failed = false
-
- # Try to collect diff for merge request.
- diffs = compare_result.diffs
-
- if diffs.present?
- merge_request.compare_diffs = diffs
-
- elsif diffs == false
- merge_request.can_be_created = false
- merge_request.compare_failed = true
- end
+ merge_request.compare = compare
else
merge_request.can_be_created = false
- merge_request.compare_failed = false
end
commits = merge_request.compare_commits
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index f125ecf7be5..3bc1b24b5e2 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -2,7 +2,7 @@
%h3.page-title Report abuse
%p Please use this form to report users who create spam issues, comments or behave inappropriately.
%hr
-= form_for @abuse_report, html: { class: 'form-horizontal js-requires-input'} do |f|
+= form_for @abuse_report, html: { class: 'form-horizontal js-quick-submit js-requires-input'} do |f|
= f.hidden_field :user_id
- if @abuse_report.errors.any?
.alert.alert-danger
@@ -16,7 +16,7 @@
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
- = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true, value: sanitize(@ref_url)
+ = f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url)
.help-block
Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment.
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 5c9403fa0c2..b748460a9f7 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -3,7 +3,7 @@
.js-broadcast-message-preview
= render_broadcast_message(@broadcast_message.message.presence || "Your message here")
-= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-requires-input'} do |f|
+= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f|
-if @broadcast_message.errors.any?
.alert.alert-danger
- @broadcast_message.errors.full_messages.each do |msg|
@@ -11,7 +11,7 @@
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
- = f.text_area :message, class: "form-control js-quick-submit js-autosize",
+ = f.text_area :message, class: "form-control js-autosize",
required: true,
data: { preview_path: preview_admin_broadcast_messages_path }
.form-group.js-toggle-colors-container
diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml
index 07110717082..0f644121e62 100644
--- a/app/views/admin/users/keys.html.haml
+++ b/app/views/admin/users/keys.html.haml
@@ -1,3 +1,3 @@
-- page_title "Keys", @user.name, "Users"
+- page_title "SSH Keys", @user.name, "Users"
= render 'admin/users/head'
= render 'profiles/keys/key_table', admin: true
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 4bc761b3738..40f88261c10 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -14,7 +14,7 @@
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
- = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field'
+ = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
= render 'explore/projects/dropdown'
- if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do
diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml
index 933a3edd0f0..0ebd7c01bab 100644
--- a/app/views/dashboard/projects/_projects.html.haml
+++ b/app/views/dashboard/projects/_projects.html.haml
@@ -1,6 +1 @@
-.projects-list-holder
-
- = render 'shared/projects/list', projects: @projects, ci: true
-
- :javascript
- Dashboard.init()
+= render 'shared/projects/list', projects: @projects, ci: true
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index c248dbb695f..39e3e8e2738 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,41 +1,40 @@
-.pull-right.hidden-sm.hidden-xs
- - if current_user
- .dropdown.inline.append-right-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-globe
- %span.light Visibility:
- - if params[:visibility_level].present?
- = visibility_level_label(params[:visibility_level].to_i)
- - else
+- if current_user
+ .dropdown
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ = icon('globe')
+ %span.light Visibility:
+ - if params[:visibility_level].present?
+ = visibility_level_label(params[:visibility_level].to_i)
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to explore_projects_filter_path(visibility_level: nil) do
Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to explore_projects_filter_path(visibility_level: nil) do
- Any
- - Gitlab::VisibilityLevel.values.each do |level|
- %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
- = link_to explore_projects_filter_path(visibility_level: level) do
- = visibility_level_icon(level)
- = visibility_level_label(level)
+ - Gitlab::VisibilityLevel.values.each do |level|
+ %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
+ = link_to explore_projects_filter_path(visibility_level: level) do
+ = visibility_level_icon(level)
+ = visibility_level_label(level)
- - if @tags.present?
- .dropdown.inline.append-right-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-tags
- %span.light Tags:
- - if params[:tag].present?
- = params[:tag]
- - else
+- if @tags.present?
+ .dropdown
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ = icon('tags')
+ %span.light Tags:
+ - if params[:tag].present?
+ = params[:tag]
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to explore_projects_filter_path(tag: nil) do
Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to explore_projects_filter_path(tag: nil) do
- Any
- - @tags.each do |tag|
- %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
- = link_to explore_projects_filter_path(tag: tag.name) do
- %i.fa.fa-tag
- = tag.name
+ - @tags.each do |tag|
+ %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
+ = link_to explore_projects_filter_path(tag: tag.name) do
+ %i.fa.fa-tag
+ = tag.name
diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml
index 999a933390b..708fbc27f55 100644
--- a/app/views/explore/projects/_projects.html.haml
+++ b/app/views/explore/projects/_projects.html.haml
@@ -1,6 +1 @@
-- if projects.any?
- .projects-list-holder
- = render 'shared/projects/list', projects: projects
-- else
- .nothing-here-block
- No such projects
+= render 'shared/projects/list', projects: projects
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index dca75498573..42b50481b9d 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -9,7 +9,7 @@
.top-area
= render 'explore/projects/nav'
-.gray-content-block.second-block.clearfix
- = render 'filter'
+ .nav-controls
+ = render 'filter'
= render 'projects', projects: @projects
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 209729dc7ee..794aa57b55a 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -8,5 +8,4 @@
= icon('plus')
New Project
-.projects-list-holder
- = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
+= render 'shared/projects/list', projects: @projects, stars: false, skip_namespace: true
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
index a79a0fcdc8e..60234be8f83 100644
--- a/app/views/groups/group_members/_group_member.html.haml
+++ b/app/views/groups/group_members/_group_member.html.haml
@@ -1,5 +1,6 @@
- user = member.user
- return unless user || member.invite?
+- show_roles = local_assigns.fetch(:show_roles, true)
%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
%span{class: ("list-item-name" if show_controls)}
@@ -28,7 +29,7 @@
= link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
Resend invite
- - if should_user_see_group_roles?(current_user, @group)
+ - if show_roles && should_user_see_group_roles?(current_user, @group)
%span.pull-right
%strong.member-access-level= member.human_access
- if show_controls
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
index 3894a0ece74..a8e1ed77da9 100644
--- a/app/views/groups/milestones/new.html.haml
+++ b/app/views/groups/milestones/new.html.haml
@@ -8,18 +8,18 @@
This will create milestone in every selected project
%hr
-= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' } do |f|
+= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input' } do |f|
.row
.col-md-6
.form-group
= f.label :title, "Title", class: "control-label"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true
+ = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
- = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
.clearfix
.error-alert
.form-group
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 20042e21bf2..54af2c3063c 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,6 +1,6 @@
.search
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
- = search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false
+ = search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false, tabindex: "1"
= hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
diff --git a/app/views/profiles/_event_table.html.haml b/app/views/profiles/_event_table.html.haml
index 58af79716a7..879fc170f92 100644
--- a/app/views/profiles/_event_table.html.haml
+++ b/app/views/profiles/_event_table.html.haml
@@ -1,17 +1,15 @@
-.table-holder
- %table.table#audits
- %thead
- %tr
- %th Action
- %th When
+%h5.prepend-top-0
+ History of authentications
+
+%ul.well-list
+ - events.each do |event|
+ %li
+ %span.description
+ = audit_icon(event.details[:with], class: "append-right-5")
+ Signed in with
+ = event.details[:with]
+ authentication
+ %span.pull-right
+ #{time_ago_in_words event.created_at} ago
- %tbody
- - events.each do |event|
- %tr
- %td
- %span
- Signed in with
- %b= event.details[:with]
- authentication
- %td #{time_ago_in_words event.created_at} ago
= paginate events, theme: "gitlab"
diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml
index 8f45f41cfe3..f630c03e5f6 100644
--- a/app/views/profiles/audit_log.html.haml
+++ b/app/views/profiles/audit_log.html.haml
@@ -1,8 +1,11 @@
- page_title "Audit Log"
- header_title page_title, audit_log_profile_path
-.alert.alert-help.prepend-top-default
- History of authentications
-
-.prepend-top-default
-= render 'event_table', events: @events
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h3.prepend-top-0
+ = page_title
+ %p
+ This is a security log of important events involving your account.
+ .col-lg-9
+ = render 'event_table', events: @events
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 705e1804717..3f328f96cea 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,50 +1,49 @@
- page_title "Emails"
- header_title page_title, profile_emails_path
-.alert.alert-help.prepend-top-default
- %ul
- %li
- Your
- %b Primary Email
- will be used for avatar detection and web based operations, such as edits and merges.
- %li
- Your
- %b Notification Email
- will be used for account notifications.
- %li
- Your
- %b Public Email
- will be displayed on your public profile.
- %li
- All email addresses will be used to identify your commits.
-
-.panel.panel-default
- .panel-heading
- Emails (#{@emails.count + 1})
- %ul.well-list#emails-table
- %li
- %strong= @primary
- %span.label.label-success Primary Email
- - if @primary === current_user.public_email
- %span.label.label-info Public Email
- - if @primary === current_user.notification_email
- %span.label.label-info Notification Email
- - @emails.each do |email|
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = page_title
+ %p
+ Control emails linked to your account
+ .col-lg-9
+ %h4.prepend-top-0
+ Add email address
+ = form_for 'email', url: profile_emails_path do |f|
+ .form-group
+ = f.label :email, class: 'label-light'
+ = f.text_field :email, class: 'form-control'
+ .prepend-top-default
+ = f.submit 'Add email address', class: 'btn btn-create'
+ %hr
+ %h4.prepend-top-0
+ Linked emails (#{@emails.count + 1})
+ .account-well.append-bottom-default
+ %ul
+ %li
+ Your Primary Email will be used for avatar detection and web based operations, such as edits and merges.
+ %li
+ Your Notification Email will be used for account notifications.
+ %li
+ Your Public Email will be displayed on your public profile.
+ %li
+ All email addresses will be used to identify your commits.
+ %ul.well-list
%li
- %strong= email.email
- - if email.email === current_user.public_email
- %span.label.label-info Public Email
- - if email.email === current_user.notification_email
- %span.label.label-info Notification Email
- %span.cgray
- added #{time_ago_with_tooltip(email.created_at)}
- = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove pull-right'
-
-%h4 Add email address
-= form_for 'email', url: profile_emails_path, html: { class: 'form-horizontal' } do |f|
- .form-group
- = f.label :email, class: 'control-label'
- .col-sm-10
- = f.text_field :email, class: 'form-control'
- .form-actions
- = f.submit 'Add email address', class: 'btn btn-create'
+ = @primary
+ %span.pull-right
+ %span.label.label-success Primary Email
+ - if @primary === current_user.public_email
+ %span.label.label-info Public Email
+ - if @primary === current_user.notification_email
+ %span.label.label-info Notification Email
+ - @emails.each do |email|
+ %li
+ = email.email
+ %span.pull-right
+ - if email.email === current_user.public_email
+ %span.label.label-info Public Email
+ - if email.email === current_user.notification_email
+ %span.label.label-info Notification Email
+ = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove pull-right'
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 2a8800de60e..4d78215ed3c 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -1,5 +1,5 @@
%div
- = form_for [:profile, @key], html: { class: 'form-horizontal js-requires-input' } do |f|
+ = form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
- if @key.errors.any?
.alert.alert-danger
%ul
@@ -7,13 +7,11 @@
%li= msg
.form-group
- = f.label :key, class: 'control-label'
- .col-sm-10
- = f.text_area :key, class: "form-control", rows: 8, autofocus: true, required: true
+ = f.label :key, class: 'label-light'
+ = f.text_area :key, class: "form-control", rows: 8, required: true
.form-group
- = f.label :title, class: 'control-label'
- .col-sm-10= f.text_field :title, class: "form-control", required: true
+ = f.label :title, class: 'label-light'
+ = f.text_field :title, class: "form-control", required: true
- .form-actions
+ .prepend-top-default
= f.submit 'Add key', class: "btn btn-create"
- = link_to "Cancel", profile_keys_path, class: "btn btn-cancel"
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index 9bbccbc45ea..25e9e8ff008 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -1,11 +1,14 @@
-%tr
- %td
- = link_to path_to_key(key, is_admin) do
- %strong= key.title
- %td
- %code.key-fingerprint= key.fingerprint
- %td
- %span.cgray
- added #{time_ago_with_tooltip(key.created_at)}
- %td
- = link_to 'Remove', path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
+%li.key-list-item
+ .pull-left.append-right-10
+ = icon 'key', class: "key-icon hidden-xs"
+ .key-list-item-info
+ = link_to path_to_key(key, is_admin), class: "title" do
+ = key.title
+ .description
+ = key.fingerprint
+ .pull-right
+ %span.key-created-at
+ created #{time_ago_with_tooltip(key.created_at)} ago
+ = link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do
+ %span.sr-only Remove
+ = icon('trash')
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 3bd1f1af162..dd7615400dc 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -1,5 +1,5 @@
- is_admin = defined?(admin) ? true : false
-.row
+.row.prepend-top-default
.col-md-4
.panel.panel-default
.panel-heading
diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml
index 8c9d546af4c..296cafa6e31 100644
--- a/app/views/profiles/keys/_key_table.html.haml
+++ b/app/views/profiles/keys/_key_table.html.haml
@@ -1,19 +1,11 @@
-- is_admin = defined?(admin) ? true : false
+- is_admin = local_assigns.fetch(:admin, false)
+
- if @keys.any?
- .table-holder
- %table.table
- %thead.panel-heading
- %tr
- %th Title
- %th Fingerprint
- %th Added at
- %th
- %tbody
- - @keys.each do |key|
- = render 'profiles/keys/key', key: key, is_admin: is_admin
+ %ul.well-list
+ = render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin }
- else
- .nothing-here-block
+ %p.profile-settings-message.text-center
- if is_admin
- User has no ssh keys
+ There are no SSH keys associated with this account.
- else
There are no SSH keys with access to your account.
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index c9a6a93f545..e0f8c9a5733 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,14 +1,21 @@
- page_title "SSH Keys"
- header_title page_title, profile_keys_path
-.top-area
- .nav-text
- Before you can add an SSH key you need to
- = link_to "generate it.", help_page_path("ssh", "README")
- .nav-controls
- = link_to new_profile_key_path, class: "btn btn-new" do
- = icon('plus')
- Add SSH Key
-
-.prepend-top-default
-= render 'key_table'
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = page_title
+ %p
+ SSH keys allow you to establish a secure connection between your computer and GitLab.
+ .col-lg-9
+ %h5.prepend-top-0
+ Add an SSH key
+ %p.profile-settings-content
+ Before you can add an SSH key you need to
+ = link_to "generate it.", help_page_path("ssh", "README")
+ = render 'form'
+ %hr
+ %h5
+ Your SSH keys (#{@keys.count})
+ %div.append-bottom-default
+ = render 'key_table'
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
deleted file mode 100644
index 13a18269d11..00000000000
--- a/app/views/profiles/keys/new.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- page_title "Add SSH Keys"
-%h3.page-title Add an SSH Key
-%p.light
- Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_page_path("ssh", "README")}.
-%hr
-= render 'form'
-
-:javascript
- $('#key_key').on('focusout', function(){
- var title = $('#key_title'),
- val = $('#key_key').val(),
- comment = val.match(/^\S+ \S+ (.+)\n?$/);
-
- if( comment && comment.length > 1 && title.val() == '' ){
- $('#key_title').val( comment[1] ).change();
- }
- });
diff --git a/app/views/profiles/notifications/_settings.html.haml b/app/views/profiles/notifications/_settings.html.haml
index 742c5c4b68d..d0d044136f6 100644
--- a/app/views/profiles/notifications/_settings.html.haml
+++ b/app/views/profiles/notifications/_settings.html.haml
@@ -1,5 +1,5 @@
-%li
- %span.notification.fa.fa-holder
+%li.notification-list-item
+ %span.notification.fa.fa-holder.append-right-5
- if notification.global?
= notification_icon(@notification)
- else
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index d5f61d9f0ca..de80abd7f4d 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,8 +1,7 @@
- page_title "Notifications"
- header_title page_title, profile_notifications_path
-.prepend-top-default
-= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications form-horizontal global-notifications-form' } do |f|
+= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
-if @user.errors.any?
%div.alert.alert-danger
%ul
@@ -10,65 +9,66 @@
%li= msg
= hidden_field_tag :notification_type, 'global'
+ .row
+ .col-lg-3.profile-settings-sidebar
+ %h4
+ = page_title
+ %p
+ You can specify notification level per group or per project.
+ %p
+ By default, all projects and groups will use the global notifications setting.
+ .col-lg-9
+ %h5
+ Global notification settings
+ .form-group
+ = f.label :notification_email, class: "label-light"
+ = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
+ .form-group
+ = f.label :notification_level, class: 'label-light'
+ .radio
+ = f.label :notification_level, value: Notification::N_DISABLED do
+ = f.radio_button :notification_level, Notification::N_DISABLED
+ .level-title
+ Disabled
+ %p You will not get any notifications via email
- .form-group
- = f.label :notification_email, class: "control-label"
- .col-sm-10
- = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "form-control"
+ .radio
+ = f.label :notification_level, value: Notification::N_MENTION do
+ = f.radio_button :notification_level, Notification::N_MENTION
+ .level-title
+ On Mention
+ %p You will receive notifications only for comments in which you were @mentioned
- .form-group
- = f.label :notification_level, class: 'control-label'
- .col-sm-10
- .radio
- = f.label :notification_level, value: Notification::N_DISABLED do
- = f.radio_button :notification_level, Notification::N_DISABLED
- .level-title
- Disabled
- %p You will not get any notifications via email
+ .radio
+ = f.label :notification_level, value: Notification::N_PARTICIPATING do
+ = f.radio_button :notification_level, Notification::N_PARTICIPATING
+ .level-title
+ Participating
+ %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
- .radio
- = f.label :notification_level, value: Notification::N_MENTION do
- = f.radio_button :notification_level, Notification::N_MENTION
- .level-title
- On Mention
- %p You will receive notifications only for comments in which you were @mentioned
+ .radio
+ = f.label :notification_level, value: Notification::N_WATCH do
+ = f.radio_button :notification_level, Notification::N_WATCH
+ .level-title
+ Watch
+ %p You will receive notifications for any activity
- .radio
- = f.label :notification_level, value: Notification::N_PARTICIPATING do
- = f.radio_button :notification_level, Notification::N_PARTICIPATING
- .level-title
- Participating
- %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
-
- .radio
- = f.label :notification_level, value: Notification::N_WATCH do
- = f.radio_button :notification_level, Notification::N_WATCH
- .level-title
- Watch
- %p You will receive notifications for any activity
-
- .gray-content-block
- = f.submit 'Save changes', class: "btn btn-create"
-
-.row.all-notifications.prepend-top-default
- .col-md-6
- %p
- You can also specify notification level per group or per project.
- %br
- By default, all projects and groups will use the notification level set above.
- %h4 Groups:
- %ul.bordered-list
- - @group_members.each do |group_member|
- - notification = Notification.new(group_member)
- = render 'settings', type: 'group', membership: group_member, notification: notification
-
- .col-md-6
- %p
- To specify the notification level per project of a group you belong to,
- %br
- you need to be a member of the project itself, not only its group.
- %h4 Projects:
- %ul.bordered-list
- - @project_members.each do |project_member|
- - notification = Notification.new(project_member)
- = render 'settings', type: 'project', membership: project_member, notification: notification
+ .prepend-top-default
+ = f.submit 'Update settings', class: "btn btn-create"
+ %hr
+ %h5
+ Groups (#{@group_members.count})
+ %div
+ %ul.bordered-list
+ - @group_members.each do |group_member|
+ - notification = Notification.new(group_member)
+ = render 'settings', type: 'group', membership: group_member, notification: notification
+ %h5
+ Projects (#{@project_members.count})
+ %p.account-well
+ To specify the notification level per project of a group you belong to, you need to be a member of the project itself, not only its group.
+ .append-bottom-default
+ %ul.bordered-list
+ - @project_members.each do |project_member|
+ - notification = Notification.new(project_member)
+ = render 'settings', type: 'project', membership: project_member, notification: notification
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index ab070c09beb..afd4f996b62 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -1,20 +1,18 @@
- page_title "Password"
- header_title page_title, edit_profile_password_path
-.alert.alert-help.prepend-top-default
- - if @user.password_automatically_set?
- Set your password.
- - else
- Change your password or recover your current one.
-
-.update-password.prepend-top-default
- = form_for @user, url: profile_password_path, method: :put, html: { class: 'form-horizontal' } do |f|
- %div
- %p.slead
- - unless @user.password_automatically_set?
- You must provide current password in order to change it.
- %br
- After a successful password update, you will be redirected to the login page where you can log in with your new password.
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = page_title
+ %p
+ After a successful password update, you will be redirected to the login page where you can log in with your new password.
+ .col-lg-9
+ %h5.prepend-top-0
+ Change your password
+ - unless @user.password_automatically_set?
+ or recover your current one
+ = form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
-if @user.errors.any?
.alert.alert-danger
%ul
@@ -22,19 +20,16 @@
%li= msg
- unless @user.password_automatically_set?
.form-group
- = f.label :current_password, class: 'control-label'
- .col-sm-10
- = f.password_field :current_password, required: true, class: 'form-control'
- %div
- = link_to "Forgot your password?", reset_profile_password_path, method: :put
-
- .form-group
- = f.label :password, 'New password', class: 'control-label'
- .col-sm-10
+ = f.label :current_password, class: 'label-light'
+ = f.password_field :current_password, required: true, class: 'form-control'
+ %p.help-block
+ You must provide your current password in order to change it.
+ .form-group
+ = f.label :password, 'New password', class: 'label-light'
= f.password_field :password, required: true, class: 'form-control'
- .form-group
- = f.label :password_confirmation, class: 'control-label'
- .col-sm-10
+ .form-group
+ = f.label :password_confirmation, class: 'label-light'
= f.password_field :password_confirmation, required: true, class: 'form-control'
- .form-actions
- = f.submit 'Save password', class: "btn btn-create"
+ .prepend-top-default.append-bottom-default
+ = f.submit 'Save password', class: "btn btn-create append-right-10"
+ = link_to "I forgot my password", reset_profile_password_path, method: :put, class: "account-btn-link"
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 1a53b4393e4..f80211669fb 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -1,56 +1,56 @@
- page_title 'Preferences'
- header_title page_title, profile_preferences_path
-.alert.alert-help.prepend-top-default
- These settings allow you to customize the appearance and behavior of the site.
- They are saved with your account and will persist to any device you use to
- access the site.
-
-= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f|
- .panel.panel-default.application-theme
- .panel-heading
+= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f|
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
Application theme
- .panel-body
- - Gitlab::Themes.each do |theme|
- = label_tag do
- .preview{class: theme.css_class}
- = f.radio_button :theme_id, theme.id
- = theme.name
-
- .panel.panel-default.syntax-theme
- .panel-heading
+ %p
+ This setting allows you to customize the appearance of the site, ex. sidebar.
+ .col-lg-9.application-theme
+ - Gitlab::Themes.each do |theme|
+ = label_tag do
+ .preview{class: theme.css_class}
+ = f.radio_button :theme_id, theme.id
+ = theme.name
+ .col-sm-12
+ %hr
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
Syntax highlighting theme
- .panel-body
- - Gitlab::ColorSchemes.each do |scheme|
- = label_tag do
- .preview= image_tag "#{scheme.css_class}-scheme-preview.png"
- = f.radio_button :color_scheme_id, scheme.id
- = scheme.name
-
- .panel.panel-default
- .panel-heading
+ %p
+ This setting allow you to customize the appearance of the syntax.
+ .col-lg-9.syntax-theme
+ - Gitlab::ColorSchemes.each do |scheme|
+ = label_tag do
+ .preview= image_tag "#{scheme.css_class}-scheme-preview.png"
+ = f.radio_button :color_scheme_id, scheme.id
+ = scheme.name
+ .col-sm-12
+ %hr
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
Behavior
- .panel-body
- .form-group
- = f.label :layout, class: 'control-label' do
- Layout width
- .col-sm-10
- = f.select :layout, layout_choices, {}, class: 'form-control'
- .help-block
- Choose between fixed (max. 1200px) and fluid (100%) application layout.
- .form-group
- = f.label :dashboard, class: 'control-label' do
- Default Dashboard
- = link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
- .col-sm-10
- = f.select :dashboard, dashboard_choices, {}, class: 'form-control'
- .form-group
- = f.label :project_view, class: 'control-label' do
- Project view
- = link_to('(?)', help_page_path('profile', 'preferences') + '#default-project-view', target: '_blank')
- .col-sm-10
- = f.select :project_view, project_view_choices, {}, class: 'form-control'
- .help-block
- Choose what content you want to see on a project's home page.
- .panel-footer
+ %p
+ This setting allows you to customize the behavior of the system layout and default views.
+ .col-lg-9
+ .form-group
+ = f.label :layout, class: 'label-light' do
+ Layout width
+ = f.select :layout, layout_choices, {}, class: 'form-control'
+ .help-block
+ Choose between fixed (max. 1200px) and fluid (100%) application layout.
+ .form-group
+ = f.label :dashboard, class: 'label-light' do
+ Default Dashboard
+ = link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
+ = f.select :dashboard, dashboard_choices, {}, class: 'form-control'
+ .form-group
+ = f.label :project_view, class: 'label-light' do
+ Project view
+ = link_to('(?)', help_page_path('profile', 'preferences') + '#default-project-view', target: '_blank')
+ = f.select :project_view, project_view_choices, {}, class: 'form-control'
+ .help-block
+ Choose what content you want to see on a project's home page.
+ .form-group
= f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 64c4bdceff9..3d1ba49491c 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,107 +1,102 @@
-.alert.alert-help.prepend-top-default
- This information will appear on your profile.
- - if current_user.ldap_user?
- Some options are unavailable for LDAP accounts
-
-.prepend-top-default
-= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" }, authenticity_token: true do |f|
+= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
+ = f.hidden_field :avatar_crop_x
+ = f.hidden_field :avatar_crop_y
+ = f.hidden_field :avatar_crop_size
-if @user.errors.any?
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.row
- .col-md-7
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Public Avatar
+ %p
+ - if @user.avatar?
+ You can change your avatar here
+ - if Gitlab.config.gravatar.enabled
+ or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
+ - else
+ You can upload an avatar here
+ - if Gitlab.config.gravatar.enabled
+ or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
+ .col-lg-9
+ .clearfix.avatar-image.append-bottom-default
+ = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
+ %h5.prepend-top-0
+ Upload new avatar
+ .prepend-top-5.append-bottom-10
+ %a.btn.js-choose-user-avatar-button
+ Browse file...
+ %span.avatar-file-name.prepend-left-default.js-avatar-filename No file chosen
+ = f.file_field :avatar, class: "js-user-avatar-input hidden"
+ .help-block
+ The maximum file size allowed is 200KB.
+ - if @user.avatar?
+ %hr
+ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-gray"
+ %hr
+ .row
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Main settings
+ %p
+ This information will appear on your profile.
+ - if current_user.ldap_user?
+ Some options are unavailable for LDAP accounts
+ .col-lg-9
.form-group
- = f.label :name, class: "control-label"
- .col-sm-10
- = f.text_field :name, class: "form-control", required: true
- %span.help-block Enter your name, so people you know can recognize you.
+ = f.label :name, class: "label-light"
+ = f.text_field :name, class: "form-control", required: true
+ %span.help-block Enter your name, so people you know can recognize you.
.form-group
- = f.label :email, class: "control-label"
- .col-sm-10
- - if @user.ldap_user? && @user.ldap_email?
- = f.text_field :email, class: "form-control", required: true, readonly: true
- %span.help-block.light
- Your email address was automatically set based on the LDAP server.
+ = f.label :email, class: "label-light"
+ - if @user.ldap_user? && @user.ldap_email?
+ = f.text_field :email, class: "form-control", required: true, readonly: true
+ %span.help-block.light
+ Your email address was automatically set based on the LDAP server.
+ - else
+ - if @user.temp_oauth_email?
+ = f.text_field :email, class: "form-control", required: true, value: nil
- else
- - if @user.temp_oauth_email?
- = f.text_field :email, class: "form-control", required: true, value: nil
- - else
- = f.text_field :email, class: "form-control", required: true
- - if @user.unconfirmed_email.present?
- %span.help-block
- Please click the link in the confirmation email before continuing. It was sent to
- = succeed "." do
- %strong #{@user.unconfirmed_email}
- %p
- = link_to "Resend confirmation e-mail", user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
+ = f.text_field :email, class: "form-control", required: true
+ - if @user.unconfirmed_email.present?
+ %span.help-block
+ Please click the link in the confirmation email before continuing. It was sent to
+ = succeed "." do
+ %strong #{@user.unconfirmed_email}
+ %p
+ = link_to "Resend confirmation e-mail", user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
- - else
- %span.help-block We also use email for avatar detection if no avatar is uploaded.
+ - else
+ %span.help-block We also use email for avatar detection if no avatar is uploaded.
.form-group
- = f.label :public_email, class: "control-label"
- .col-sm-10
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2"
- %span.help-block This email will be displayed on your public profile.
+ = f.label :public_email, class: "label-light"
+ = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2"
+ %span.help-block This email will be displayed on your public profile.
.form-group
- = f.label :skype, class: "control-label"
- .col-sm-10= f.text_field :skype, class: "form-control"
+ = f.label :skype, class: "label-light"
+ = f.text_field :skype, class: "form-control"
.form-group
- = f.label :linkedin, class: "control-label"
- .col-sm-10= f.text_field :linkedin, class: "form-control"
+ = f.label :linkedin, class: "label-light"
+ = f.text_field :linkedin, class: "form-control"
.form-group
- = f.label :twitter, class: "control-label"
- .col-sm-10= f.text_field :twitter, class: "form-control"
+ = f.label :twitter, class: "label-light"
+ = f.text_field :twitter, class: "form-control"
.form-group
- = f.label :website_url, 'Website', class: "control-label"
- .col-sm-10= f.text_field :website_url, class: "form-control"
+ = f.label :website_url, 'Website', class: "label-light"
+ = f.text_field :website_url, class: "form-control"
.form-group
- = f.label :location, 'Location', class: "control-label"
- .col-sm-10= f.text_field :location, class: "form-control"
+ = f.label :location, 'Location', class: "label-light"
+ = f.text_field :location, class: "form-control"
.form-group
- = f.label :bio, class: "control-label"
- .col-sm-10
- = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250
- %span.help-block Tell us about yourself in fewer than 250 characters.
-
- .col-md-5
- .light-well
- = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
-
- .clearfix
- .profile-avatar-form-option
- %p.light
- - if @user.avatar?
- You can change your avatar here
- - if Gitlab.config.gravatar.enabled
- %br
- or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
- - else
- You can upload an avatar here
- - if Gitlab.config.gravatar.enabled
- %br
- or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
- %hr
- %a.choose-btn.btn.btn-sm.js-choose-user-avatar-button
- %i.fa.fa-paperclip
- %span Choose File ...
- &nbsp;
- %span.file_name.js-avatar-filename File name...
- = f.file_field :avatar, class: "js-user-avatar-input hidden"
- = f.hidden_field :avatar_crop_x
- = f.hidden_field :avatar_crop_y
- = f.hidden_field :avatar_crop_size
- .light The maximum file size allowed is 200KB.
- - if @user.avatar?
- %hr
- = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
-
-
- .form-actions
- = f.submit 'Save changes', class: "btn btn-success"
- = link_to "Cancel", user_path(current_user), class: "btn btn-cancel"
+ = f.label :bio, class: "label-light"
+ = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250
+ %span.help-block Tell us about yourself in fewer than 250 characters.
+ .prepend-top-default.append-bottom-default
+ = f.submit 'Update profile settings', class: "btn btn-success"
+ = link_to "Cancel", user_path(current_user), class: "btn btn-cancel"
.modal.modal-profile-crop
.modal-dialog
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 10b02813733..f8b6fa253c4 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -10,7 +10,7 @@
%span.editor-file-name
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
- required: true, class: 'form-control new-file-name js-quick-submit'
+ required: true, class: 'form-control new-file-name'
.pull-right
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index 084608bbba3..84694203d7d 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -5,7 +5,7 @@
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Create New Directory
.modal-body
- = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-requires-input' do
+ = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do
.form-group
= label_tag :dir_name, 'Directory name', class: 'control-label'
.col-sm-10
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index 1cf19a7d3db..2e1f32fd15e 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -6,7 +6,7 @@
%h3.page-title Delete #{@blob.name}
.modal-body
- = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-requires-input' do
+ = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-quick-submit js-requires-input' do
= render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}"
.form-group
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 676924dc6ca..b1f50eb5f34 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -5,7 +5,7 @@
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title #{title}
.modal-body
- = form_tag form_path, method: method, class: 'js-upload-blob-form form-horizontal' do
+ = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal' do
.dropzone
.dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index a279e6eda55..effcce5a1c4 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -13,7 +13,7 @@
= icon('eye')
= editing_preview_title(@blob.name)
- = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do
+ = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form') do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 167fa615182..1dd2b5c0af7 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -5,7 +5,7 @@
New File
.file-editor
- = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do
+ = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-quick-submit js-requires-input') do
= render 'projects/blob/editor', ref: @ref
= render 'shared/new_commit_form', placeholder: "Add new file"
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index d668f483bcb..6086ad3661e 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -10,8 +10,8 @@
= parallel_diff_btn
= render 'projects/diffs/stats', diff_files: diff_files
-- if diff_files.count < diffs.size
- = render 'projects/diffs/warning', diffs: diffs, shown_files_count: diff_files.count
+- if diff_files.overflow?
+ = render 'projects/diffs/warning', diff_files: diff_files
.files
- diff_files.each_with_index do |diff_file, index|
@@ -21,10 +21,3 @@
= render 'projects/diffs/file', i: index, project: project,
diff_file: diff_file, diff_commit: diff_commit, blob: blob
-
-- if @diff_timeout
- .alert.alert-danger
- %h4
- Failed to collect changes
- %p
- Maybe diff is really big and operation failed with timeout. Try to get diff locally
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index d75e9ef2a49..9a8208202e4 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -6,7 +6,7 @@
%table.text-file.code.js-syntax-highlight{ class: too_big ? 'hide' : '' }
- last_line = 0
- - raw_diff_lines = diff_file.diff_lines
+ - raw_diff_lines = diff_file.diff_lines.to_a
- diff_file.highlighted_diff_lines.each_with_index do |line, index|
- type = line.type
- last_line = line.new_pos
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index 63ede71e6f1..15536c17f8e 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -14,5 +14,5 @@
= link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm"
%p
To preserve performance only
- %strong #{shown_files_count} of #{diffs.size}
+ %strong #{diff_files.count} of #{diff_files.real_size}
files are displayed.
diff --git a/app/views/projects/forks/_projects.html.haml b/app/views/projects/forks/_projects.html.haml
new file mode 100644
index 00000000000..2946e6dcbd0
--- /dev/null
+++ b/app/views/projects/forks/_projects.html.haml
@@ -0,0 +1,2 @@
+= render 'shared/projects/list', projects: projects, use_creator_avatar: true,
+ forks: true, show_last_commit_as_description: true
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index ace22625d1d..4bcf2d9d533 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -4,8 +4,9 @@
== #{pluralize(@total_forks_count, 'fork')}: #{full_count_title}
.nav-controls
- = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
- spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
+ = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+ = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
+ spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
@@ -38,18 +39,10 @@
Fork
-.projects-list-holder
- - if @forks.blank?
- %ul.content-list
- %li
- .nothing-here-block No forks to show
- - else
- = render 'shared/projects/list', projects: @forks, use_creator_avatar: true,
- forks: true, show_last_commit_as_description: true
+= render 'projects', projects: @forks
- - if @private_forks_count > 0
- %ul.projects-list.private-forks-notice
- %li.project-row
- = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
- %strong= pluralize(@private_forks_count, 'private fork')
- %span you have no access to.
+- if @private_forks_count > 0
+ .private-forks-notice
+ = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
+ %strong= pluralize(@private_forks_count, 'private fork')
+ %span you have no access to.
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 6588d9bdbe1..33c48199ba5 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-requires-input' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-quick-submit js-requires-input' } do |f|
= render 'shared/issuable/form', f: f, issuable: @issue
:javascript
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index d63d3a3ec20..be7a0bb5628 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-quick-submit js-requires-input' } do |f|
-if @label.errors.any?
.row
.col-sm-offset-2.col-sm-10
@@ -10,7 +10,7 @@
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
- = f.text_field :title, class: "form-control js-quick-submit", required: true, autofocus: true
+ = f.text_field :title, class: "form-control", required: true, autofocus: true
.form-group
= f.label :description, class: 'control-label'
.col-sm-10
diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml
index 5b35acc66c0..f7ddd30c5a9 100644
--- a/app/views/projects/labels/_label.html.haml
+++ b/app/views/projects/labels/_label.html.haml
@@ -3,6 +3,10 @@
.pull-right
%strong.append-right-20
+ = link_to_label(label, type: :merge_request) do
+ = pluralize label.open_merge_requests_count, 'open merge request'
+
+ %strong.append-right-20
= link_to_label(label) do
= pluralize label.open_issues_count, 'open issue'
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 236a545c840..01dc7519bee 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -33,23 +33,18 @@
%div= msg
- elsif @merge_request.source_branch.present? && @merge_request.target_branch.present?
- - if @merge_request.compare_failed
- .alert.alert-danger
- %h4 Compare failed
- %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches.
- - else
- .light-well.append-bottom-default
- .center
- %h4
- There isn't anything to merge.
- %p.slead
- - if @merge_request.source_branch == @merge_request.target_branch
- You'll need to use different branch names to get a valid comparison.
- - else
- %span.label-branch #{@merge_request.source_branch}
- and
- %span.label-branch #{@merge_request.target_branch}
- are the same.
+ .light-well.append-bottom-default
+ .center
+ %h4
+ There isn't anything to merge.
+ %p.slead
+ - if @merge_request.source_branch == @merge_request.target_branch
+ You'll need to use different branch names to get a valid comparison.
+ - else
+ %span.label-branch #{@merge_request.source_branch}
+ and
+ %span.label-branch #{@merge_request.target_branch}
+ are the same.
.form-actions
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 4c5a9818e3e..9e59f7df71b 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -31,22 +31,18 @@
%li.diffs-tab.active
= link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
- %span.badge= @diffs.size
+ %span.badge= @diffs.real_size
.tab-content
#commits.commits.tab-pane
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane.active
- - if @diffs.present?
- = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @merge_request.diff_refs
- - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ - if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.alert.alert-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line changes are not shown.
- else
- .alert.alert-danger
- %h4 This comparison includes a huge diff.
- %p To preserve performance the line changes are not shown.
+ = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @merge_request.diff_refs
- if @ci_commit
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index d7bc26e24b9..7b5e2991c09 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -62,7 +62,7 @@
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
- %span.badge= @merge_request.diffs.size
+ %span.badge= @merge_request.diff_size
.tab-content
#notes.notes.tab-pane.voting_notes
diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index 64cd484193e..1b0bae86ad4 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -1,5 +1,5 @@
- if @merge_request_diff.collected?
- = render "projects/diffs/diffs", diffs: params[:w] == '1' ? @merge_request.diffs_no_whitespace : @merge_request.diffs,
+ = render "projects/diffs/diffs", diffs: @merge_request.diffs(diff_options),
project: @merge_request.project, diff_refs: @merge_request.diff_refs
- elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index d9a1730a8bc..807833741af 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -1,6 +1,6 @@
- status_class = @ci_commit ? " ci-#{@ci_commit.status}" : nil
-= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
+= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-quick-submit js-requires-input' } do |f|
= hidden_field_tag :authenticity_token, form_authenticity_token
.accept-merge-holder.clearfix.js-toggle-container
.clearfix
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 39aa2437e18..23f2bca7baf 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-requires-input'} do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input'} do |f|
-if @milestone.errors.any?
.alert.alert-danger
%ul
@@ -9,12 +9,12 @@
.form-group
= f.label :title, "Title", class: "control-label"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true
+ = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
- = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
= render 'projects/notes/hints'
.clearfix
.error-alert
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index 5d78652befa..b5f076088c7 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -1,8 +1,8 @@
.note-edit-form
- = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
+ = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true, class: 'js-quick-submit' do |f|
= note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do
- = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field js-quick-submit'
+ = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints'
.note-form-actions
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index f10a4145d62..09740d8ea12 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form js-quick-submit common-note-form gfm-form" }, authenticity_token: true do |f|
= hidden_field_tag :view, diff_view
= hidden_field_tag :line_type
= note_target_fields(@note)
@@ -8,7 +8,7 @@
= f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-quick-submit'
+ = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
= render 'projects/notes/hints'
.error-alert
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index bc80f2f29ad..c4a3f06ee06 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -9,9 +9,9 @@
%strong #{@tag.name}
.prepend-top-default
- = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f|
+ = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form js-quick-submit' }) do |f|
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control'
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
= render 'projects/notes/hints'
.error-alert
.form-actions.prepend-top-default
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 3a2f75fecaa..77c7c4d23de 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -10,7 +10,7 @@
New Tag
%hr
-= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-requires-input" do
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-quick-submit js-requires-input" do
.form-group
= label_tag :tag_name, nil, class: 'control-label'
.col-sm-10
@@ -30,7 +30,7 @@
= label_tag :release_description, 'Release notes', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control'
+ = render 'projects/zen', attr: :release_description, classes: 'description form-control'
= render 'projects/notes/hints'
.help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.
.form-actions
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 1d257818dcd..f0d1932e23c 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default js-quick-submit' } do |f|
-if @page.errors.any?
#error_explanation
.alert.alert-danger
@@ -15,7 +15,7 @@
= f.label :content, class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
- = render 'projects/zen', f: f, attr: :content, classes: 'description form-control js-quick-submit'
+ = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
= render 'projects/notes/hints'
.clearfix
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index eef7b7b5efd..60df348891c 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -18,7 +18,9 @@
= render 'shared/projects/list', projects: @objects
- else
= render partial: "search/results/#{@scope.singularize}", collection: @objects
- = paginate @objects, theme: 'gitlab'
+
+ - if @scope != 'projects'
+ = paginate @objects, theme: 'gitlab'
:javascript
$(".search-results .term").highlight("#{escape_javascript(params[:search])}");
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index 6b77d24f50c..c9b7bd154af 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -30,7 +30,7 @@
.line-numbers
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
- - chunk[:data].lines.to_a.size.times do |index|
+ - Gitlab::Git::Util.count_lines(chunk[:data]).times do |index|
- offset = defined?(chunk[:start_line]) ? chunk[:start_line] : 1
- i = index + offset
= link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}", class: "diff-line-num" do
diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml
index 7c57924277e..7afbaeddee8 100644
--- a/app/views/shared/_commit_message_container.html.haml
+++ b/app/views/shared/_commit_message_container.html.haml
@@ -7,7 +7,7 @@
.max-width-marker
= text_area_tag 'commit_message',
(params[:commit_message] || local_assigns[:text]),
- class: 'form-control js-commit-message js-quick-submit', placeholder: local_assigns[:placeholder],
+ class: 'form-control js-commit-message', placeholder: local_assigns[:placeholder],
required: true, rows: (local_assigns[:rows] || 3),
id: "commit_message-#{nonce}"
- if local_assigns[:hint]
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index 089179e677a..bb5fff2d3bb 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,6 +1,6 @@
- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
.no-ssh-key-message.alert.alert-warning.hidden-xs
- You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path, class: 'alert-link'} to your profile
+ You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', profile_keys_path, class: 'alert-link'} to your profile
.pull-right
= link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
diff --git a/app/views/shared/groups/_list.html.haml b/app/views/shared/groups/_list.html.haml
new file mode 100644
index 00000000000..1aa7ed1f2eb
--- /dev/null
+++ b/app/views/shared/groups/_list.html.haml
@@ -0,0 +1,6 @@
+- if groups.any?
+ %ul.content-list
+ - groups.each_with_index do |group, i|
+ = render "shared/groups/group", group: group
+- else
+ %h3 No groups found
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 90dc0062481..d5a4aad05d9 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -9,7 +9,7 @@
= f.label :title, class: 'control-label'
.col-sm-10
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
- class: 'form-control pad js-gfm-input js-quick-submit', required: true
+ class: 'form-control pad js-gfm-input', required: true
- if issuable.is_a?(MergeRequest)
%p.help-block
@@ -25,7 +25,7 @@
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control js-quick-submit'
+ classes: 'description form-control'
= render 'projects/notes/hints'
.clearfix
.error-alert
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 2446e6c30ae..2e08bb2ac08 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -6,25 +6,19 @@
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
+- remote = false unless local_assigns[:remote] == true
-%ul.projects-list.content-list
+.projects-list-holder
- if projects.any?
- - projects.each_with_index do |project, i|
- - css_class = (i >= projects_limit) ? 'hide' : nil
- = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
- avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
- forks: forks, show_last_commit_as_description: show_last_commit_as_description
-
- - if projects.size > projects_limit && projects.kind_of?(Array)
- %li.bottom.center
- .light
- #{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
- = link_to '#', class: 'js-expand' do
- Show all
- = paginate projects, theme: "gitlab" if projects.respond_to? :total_pages
+ %ul.projects-list.content-list
+ - projects.each_with_index do |project, i|
+ - css_class = (i >= projects_limit) ? 'hide' : nil
+ = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
+ avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
+ forks: forks, show_last_commit_as_description: show_last_commit_as_description
+ = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
- else
.nothing-here-block No projects found
:javascript
- new ProjectsList();
- Dashboard.init();
+ ProjectsList.init();
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index d109635fa1e..bca816f22cb 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -8,117 +8,110 @@
= render 'shared/show_aside'
-.cover-block
- .cover-controls
- - if @user == current_user
- = link_to profile_path, class: 'btn btn-gray' do
- = icon('pencil')
- - elsif current_user
- %span.report-abuse
- - if @user.abuse_report
- %button.btn.btn-danger{ title: 'Already reported for abuse',
- data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
- = icon('exclamation-circle')
- - else
- = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
- title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
- = icon('exclamation-circle')
- - if current_user
- &nbsp;
- = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
- = icon('rss')
-
- .avatar-holder
- = link_to avatar_icon(@user, 400), target: '_blank' do
- = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
- .cover-title
- = @user.name
-
- .cover-desc
- %span.middle-dot-divider
- @#{@user.username}
- %span.middle-dot-divider
- Member since #{@user.created_at.to_s(:medium)}
-
- - if @user.bio.present?
+.user-profile
+ .cover-block
+ .cover-controls
+ - if @user == current_user
+ = link_to profile_path, class: 'btn btn-gray' do
+ = icon('pencil')
+ - elsif current_user
+ %span.report-abuse
+ - if @user.abuse_report
+ %button.btn.btn-danger{ title: 'Already reported for abuse',
+ data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
+ = icon('exclamation-circle')
+ - else
+ = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
+ title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
+ = icon('exclamation-circle')
+ - if current_user
+ &nbsp;
+ = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
+ = icon('rss')
+
+ .avatar-holder
+ = link_to avatar_icon(@user, 400), target: '_blank' do
+ = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
+ .cover-title
+ = @user.name
+
+ .cover-desc
+ %span.middle-dot-divider
+ @#{@user.username}
+ %span.middle-dot-divider
+ Member since #{@user.created_at.to_s(:medium)}
+
+ - if @user.bio.present?
+ .cover-desc
+ %p.profile-user-bio
+ = @user.bio
+
.cover-desc
- %p.profile-user-bio
- = @user.bio
-
- .cover-desc
- - unless @user.public_email.blank?
- .profile-link-holder.middle-dot-divider
- = link_to @user.public_email, "mailto:#{@user.public_email}"
- - unless @user.skype.blank?
- .profile-link-holder.middle-dot-divider
- = link_to "skype:#{@user.skype}", title: "Skype" do
- = icon('skype')
- - unless @user.linkedin.blank?
- .profile-link-holder.middle-dot-divider
- = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
- = icon('linkedin-square')
- - unless @user.twitter.blank?
- .profile-link-holder.middle-dot-divider
- = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do
- = icon('twitter-square')
- - unless @user.website_url.blank?
- .profile-link-holder.middle-dot-divider
- = link_to @user.short_website_url, @user.full_website_url
- - unless @user.location.blank?
- .profile-link-holder.middle-dot-divider
- = icon('map-marker')
- = @user.location
-
- %ul.nav-links.center
- %li.active
- = link_to "#activity", 'data-toggle' => 'tab' do
- Activity
- - if @groups.any?
- %li
- = link_to "#groups", 'data-toggle' => 'tab' do
+ - unless @user.public_email.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to @user.public_email, "mailto:#{@user.public_email}"
+ - unless @user.skype.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to "skype:#{@user.skype}", title: "Skype" do
+ = icon('skype')
+ - unless @user.linkedin.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
+ = icon('linkedin-square')
+ - unless @user.twitter.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do
+ = icon('twitter-square')
+ - unless @user.website_url.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to @user.short_website_url, @user.full_website_url
+ - unless @user.location.blank?
+ .profile-link-holder.middle-dot-divider
+ = icon('map-marker')
+ = @user.location
+
+ %ul.nav-links.center.user-profile-nav
+ %li.activity-tab
+ = link_to user_calendar_activities_path, data: {target: 'div#activity', action: 'activity', toggle: 'tab'} do
+ Activity
+ %li.groups-tab
+ = link_to user_groups_path, data: {target: 'div#groups', action: 'groups', toggle: 'tab'} do
Groups
- - if @contributed_projects.present?
- %li
- = link_to "#contributed", 'data-toggle' => 'tab' do
+ %li.contributed-tab
+ = link_to user_contributed_projects_path, data: {target: 'div#contributed', action: 'contributed', toggle: 'tab'} do
Contributed projects
- - if @projects.present?
- %li
- = link_to "#personal", 'data-toggle' => 'tab' do
+ %li.projects-tab
+ = link_to user_projects_path, data: {target: 'div#projects', action: 'projects', toggle: 'tab'} do
Personal projects
-%div{ class: container_class }
- .tab-content
- .tab-pane.active#activity
- .gray-content-block.white.second-block
- %div{ class: container_class }
- .user-calendar
- %h4.center.light
- %i.fa.fa-spinner.fa-spin
- .user-calendar-activities
+ %div{ class: container_class }
+ .tab-content
+ #activity.tab-pane
+ .gray-content-block.white.second-block
+ %div{ class: container_class }
+ .user-calendar{data: {href: user_calendar_path}}
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ .user-calendar-activities
+ .content_list{ data: {href: user_path} }
+ = spinner
- .content_list
- = spinner
+ #groups.tab-pane
+ - # This tab is always loaded via AJAX
+
+ #contributed.contributed-projects.tab-pane
+ - # This tab is always loaded via AJAX
- - if @groups.any?
- .tab-pane#groups
- %ul.content-list
- - @groups.each do |group|
- = render 'shared/groups/group', group: group
-
- - if @contributed_projects.present?
- .tab-pane#contributed
- .contributed-projects
- = render 'shared/projects/list',
- projects: @contributed_projects.sort_by(&:star_count).reverse,
- projects_limit: 10, stars: true, avatar: true
-
- - if @projects.present?
- .tab-pane#personal
- .personal-projects
- = render 'shared/projects/list',
- projects: @projects.sort_by(&:star_count).reverse,
- projects_limit: 10, stars: true, avatar: true
+ #projects.tab-pane
+ - # This tab is always loaded via AJAX
+
+ .loading-status
+ = spinner
:javascript
- $(".user-calendar").load("#{user_calendar_path}");
+ var userProfile;
+
+ userProfile = new User({
+ action: "#{controller.action_name}"
+ });
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 2d44d8d4dc6..605ec4f04e5 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -141,7 +141,7 @@ class IrkerWorker
end
def files_count(commit)
- files = "#{commit.diffs.count} file"
+ files = "#{commit.diffs.real_size} file"
files += 's' if commit.diffs.count > 1
files
end
diff --git a/config/routes.rb b/config/routes.rb
index a2acf170a6b..a918b5bd3f0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -314,7 +314,7 @@ Rails.application.routes.draw do
end
end
resource :preferences, only: [:show, :update]
- resources :keys
+ resources :keys, except: [:new]
resources :emails, only: [:index, :create, :destroy]
resource :avatar, only: [:destroy]
resource :two_factor_auth, only: [:new, :create, :destroy] do
@@ -332,6 +332,15 @@ Rails.application.routes.draw do
get 'u/:username/calendar_activities' => 'users#calendar_activities', as: :user_calendar_activities,
constraints: { username: /.*/ }
+ get 'u/:username/groups' => 'users#groups', as: :user_groups,
+ constraints: { username: /.*/ }
+
+ get 'u/:username/projects' => 'users#projects', as: :user_projects,
+ constraints: { username: /.*/ }
+
+ get 'u/:username/contributed' => 'users#contributed', as: :user_contributed_projects,
+ constraints: { username: /.*/ }
+
get '/u/:username' => 'users#show', as: :user,
constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }
diff --git a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
new file mode 100644
index 00000000000..f996ae74dca
--- /dev/null
+++ b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
@@ -0,0 +1,5 @@
+class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration
+ def change
+ add_column :merge_request_diffs, :real_size, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 53a941d30de..71d9257a31e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -509,6 +509,7 @@ ActiveRecord::Schema.define(version: 20160222153918) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "base_commit_sha"
+ t.string "real_size"
end
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 009532b50c1..5c527d55481 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -515,7 +515,7 @@ Parameters:
}
```
-## Comments on merge requets
+## Comments on merge requests
Comments are done via the [notes](notes.md) resource.
diff --git a/features/groups.feature b/features/groups.feature
index 55fffb012ae..a60c3860b83 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -22,11 +22,23 @@ Feature: Groups
When I visit group "Owned" issues page
Then I should see issues from group "Owned" assigned to me
+ Scenario: I should not see issues from archived project in "Owned" group issues list
+ Given Group "Owned" has archived project
+ And the archived project have some issues
+ When I visit group "Owned" issues page
+ Then I should not see issues from the archived project
+
Scenario: I should see group "Owned" merge requests list
Given project from group "Owned" has merge requests assigned to me
When I visit group "Owned" merge requests page
Then I should see merge requests from group "Owned" assigned to me
+ Scenario: I should not see merge requests from archived project in "Owned" group merge requests list
+ Given Group "Owned" has archived project
+ And the archived project have some merge_requests
+ When I visit group "Owned" merge requests page
+ Then I should not see merge requests from the archived project
+
Scenario: I should see edit group "Owned" page
When I visit group "Owned" settings page
And I change group "Owned" name to "new-name"
diff --git a/features/profile/ssh_keys.feature b/features/profile/ssh_keys.feature
index 581503fc5f9..b0d5b748916 100644
--- a/features/profile/ssh_keys.feature
+++ b/features/profile/ssh_keys.feature
@@ -9,7 +9,7 @@ Feature: Profile SSH Keys
Then I should see my ssh keys
Scenario: Add new ssh key
- Given I click link "Add new"
+ Given I should see new ssh key form
And I submit new ssh key "Laptop"
Then I should see new ssh key "Laptop"
diff --git a/features/project/badges/build.feature b/features/project/badges/build.feature
index 9417f62d680..bcf80ed620e 100644
--- a/features/project/badges/build.feature
+++ b/features/project/badges/build.feature
@@ -20,3 +20,8 @@ Feature: Project Badges Build
And project has another build that is running
When I display builds badge for a master branch
Then I should see a build running badge
+
+ Scenario: I want to see a fresh badge on each request
+ Given recent build is successful
+ When I display builds badge for a master branch
+ Then I should see a badge that has not been cached
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 1e2a78a6029..7a6ae15ffa5 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -44,6 +44,18 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
end
+ step 'I should not see issues from the archived project' do
+ @archived_project.issues.each do |issue|
+ expect(page).not_to have_content issue.title
+ end
+ end
+
+ step 'I should not see merge requests from the archived project' do
+ @archived_project.merge_requests.each do |mr|
+ expect(page).not_to have_content mr.title
+ end
+ end
+
step 'I should see merge requests from group "Owned" assigned to me' do
assigned_to_me(:merge_requests).each do |issue|
expect(page).to have_content issue.title[0..80]
@@ -113,7 +125,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'Group "Owned" has archived project' do
group = Group.find_by(name: 'Owned')
- create(:project, namespace: group, archived: true, path: "archived-project")
+ @archived_project = create(:project, namespace: group, archived: true, path: "archived-project")
end
step 'I should see "archived" label' do
@@ -124,6 +136,21 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
visit group_path(-1)
end
+ step 'the archived project have some issues' do
+ create :issue,
+ project: @archived_project,
+ assignee: current_user,
+ author: current_user
+ end
+
+ step 'the archived project have some merge requests' do
+ create :merge_request,
+ source_project: @archived_project,
+ target_project: @archived_project,
+ assignee: current_user,
+ author: current_user
+ end
+
private
def assigned_to_me(key)
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 7895f643d0c..0c60328583a 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -13,7 +13,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
fill_in 'user_website_url', with: 'testurl'
fill_in 'user_location', with: 'Ukraine'
fill_in 'user_bio', with: 'I <3 GitLab'
- click_button 'Save changes'
+ click_button 'Update profile settings'
@user.reload
end
@@ -64,7 +64,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
page.within '.update-password' do
fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344"
- click_button "Save"
+ click_button "Save password"
end
end
@@ -73,7 +73,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344"
- click_button "Save"
+ click_button "Save password"
end
end
@@ -82,7 +82,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "password"
fill_in "user_password_confirmation", with: "confirmation"
- click_button "Save"
+ click_button "Save password"
end
end
@@ -237,7 +237,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
page.find('#user_avatar_crop_y', visible: false).set('0')
page.find('#user_avatar_crop_size', visible: false).set('256')
- click_button "Save changes"
+ click_button "Update profile settings"
@user.reload
end
diff --git a/features/steps/profile/ssh_keys.rb b/features/steps/profile/ssh_keys.rb
index c7f879d247d..a400488a532 100644
--- a/features/steps/profile/ssh_keys.rb
+++ b/features/steps/profile/ssh_keys.rb
@@ -7,8 +7,8 @@ class Spinach::Features::ProfileSshKeys < Spinach::FeatureSteps
end
end
- step 'I click link "Add new"' do
- click_link "Add SSH Key"
+ step 'I should see new ssh key form' do
+ expect(page).to have_content("Add an SSH key")
end
step 'I submit new ssh key "Laptop"' do
diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb
index cbfc35bed65..47540f356e9 100644
--- a/features/steps/project/badges/build.rb
+++ b/features/steps/project/badges/build.rb
@@ -20,6 +20,10 @@ class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
expect_badge('running')
end
+ step 'I should see a badge that has not been cached' do
+ expect(page.response_headers).to include('Cache-Control' => 'no-cache')
+ end
+
def expect_badge(status)
svg = Nokogiri::XML.parse(page.body)
expect(page.response_headers).to include('Content-Type' => 'image/svg+xml')
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index f9fd7332464..93c37bf507f 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -126,8 +126,11 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I visit big commit page' do
- stub_const('Commit::DIFF_SAFE_FILES', 20)
- visit namespace_project_commit_path(@project.namespace, @project, sample_big_commit.id)
+ # Create a temporary scope to ensure that the stub_const is removed after user
+ RSpec::Mocks.with_temporary_scope do
+ stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_lines: 1, max_files: 1 })
+ visit namespace_project_commit_path(@project.namespace, @project, sample_big_commit.id)
+ end
end
step 'I see big commit warning' do
diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb
index f0721094ee3..9856c510aa0 100644
--- a/features/steps/shared/user.rb
+++ b/features/steps/shared/user.rb
@@ -26,4 +26,20 @@ module SharedUser
step 'I have no ssh keys' do
@user.keys.delete_all
end
+
+ step 'I click on "Personal projects" tab' do
+ page.within '.nav-links' do
+ click_link 'Personal projects'
+ end
+
+ expect(page).to have_css('.tab-content #projects.active')
+ end
+
+ step 'I click on "Contributed projects" tab' do
+ page.within '.nav-links' do
+ click_link 'Contributed projects'
+ end
+
+ expect(page).to have_css('.tab-content #contributed.active')
+ end
end
diff --git a/features/user.feature b/features/user.feature
index 35eae842e77..e0cadba30a1 100644
--- a/features/user.feature
+++ b/features/user.feature
@@ -5,10 +5,12 @@ Feature: User
# Signed out
+ @javascript
Scenario: I visit user "John Doe" page while not signed in when he owns a public project
Given "John Doe" owns internal project "Internal"
And "John Doe" owns public project "Community"
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should not see project "Internal"
@@ -16,28 +18,34 @@ Feature: User
# Signed in as someone else
+ @javascript
Scenario: I visit user "John Doe" page while signed in as someone else when he owns a public project
Given "John Doe" owns public project "Community"
And "John Doe" owns internal project "Internal"
And I sign in as a user
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should see project "Internal"
And I should see project "Community"
+ @javascript
Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a public project
Given "John Doe" owns internal project "Internal"
And I sign in as a user
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should see project "Internal"
And I should not see project "Community"
+ @javascript
Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a project I can see
Given I sign in as a user
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should not see project "Internal"
@@ -45,19 +53,23 @@ Feature: User
# Signed in as the user himself
+ @javascript
Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has a public project
Given "John Doe" owns internal project "Internal"
And "John Doe" owns public project "Community"
And I sign in as "John Doe"
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should see project "Enterprise"
And I should see project "Internal"
And I should see project "Community"
+ @javascript
Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has no public project
Given I sign in as "John Doe"
When I visit user "John Doe" page
+ And I click on "Personal projects" tab
Then I should see user "John Doe" page
And I should see project "Enterprise"
And I should not see project "Internal"
@@ -68,6 +80,7 @@ Feature: User
Given I sign in as a user
And "John Doe" has contributions
When I visit user "John Doe" page
+ And I click on "Contributed projects" tab
Then I should see user "John Doe" page
And I should see contributed projects
And I should see contributions calendar
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index f4efb651eb6..4544a41b1e3 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -48,7 +48,7 @@ module API
sha = params[:sha]
commit = user_project.commit(sha)
not_found! "Commit" unless commit
- commit.diffs
+ commit.diffs.to_a
end
# Get a commit's comments
@@ -90,9 +90,9 @@ module API
}
if params[:path] && params[:line] && params[:line_type]
- commit.diffs.each do |diff|
+ commit.diffs(all_diffs: true).each do |diff|
next unless diff.new_path == params[:path]
- lines = Gitlab::Diff::Parser.new.parse(diff.diff.lines.to_a)
+ lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
lines.each do |line|
next unless line.new_pos == params[:line].to_i && line.type == params[:line_type]
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index a3b5f1eb8d3..b021db8fa5b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -181,7 +181,7 @@ module API
class MergeRequestChanges < MergeRequest
expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _|
- compare.diffs
+ compare.diffs(all_diffs: true).to_a
end
end
@@ -295,11 +295,11 @@ module API
end
expose :diffs, using: Entities::RepoDiff do |compare, options|
- compare.diffs
+ compare.diffs(all_diffs: true).to_a
end
expose :compare_timeout do |compare, options|
- compare.timeout
+ compare.diffs.overflow?
end
expose :same, as: :compare_same_ref
diff --git a/lib/gitlab/compare_result.rb b/lib/gitlab/compare_result.rb
deleted file mode 100644
index 0d696a1ee28..00000000000
--- a/lib/gitlab/compare_result.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Gitlab
- class CompareResult
- attr_reader :commits, :diffs
-
- def initialize(compare, diff_options = {})
- @commits, @diffs = compare.commits, compare.diffs(nil, diff_options)
- end
- end
-end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index a484177ae33..faa2830c16e 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -21,7 +21,7 @@ module Gitlab
# Array of Gitlab::DIff::Line objects
def diff_lines
- @lines ||= parser.parse(raw_diff.lines)
+ @lines ||= parser.parse(raw_diff.each_line).to_a
end
def highlighted_diff_lines
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index 3666063bf8b..d0f6ba23ab4 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -5,52 +5,53 @@ module Gitlab
def parse(lines)
@lines = lines
- lines_obj = []
line_obj_index = 0
line_old = 1
line_new = 1
type = nil
- @lines.each do |line|
- next if filename?(line)
-
- full_line = line.gsub(/\n/, '')
-
- if line.match(/^@@ -/)
- type = "match"
-
- line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
- line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
-
- next if line_old <= 1 && line_new <= 1 #top of file
- lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
- line_obj_index += 1
- next
- elsif line[0] == '\\'
- type = 'nonewline'
- lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
- line_obj_index += 1
- else
- type = identification_type(line)
- lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
- line_obj_index += 1
- end
-
-
- case line[0]
- when "+"
- line_new += 1
- when "-"
- line_old += 1
- when "\\"
- # No increment
- else
- line_new += 1
- line_old += 1
+ # By returning an Enumerator we make it possible to search for a single line (with #find)
+ # without having to instantiate all the others that come after it.
+ Enumerator.new do |yielder|
+ @lines.each do |line|
+ next if filename?(line)
+
+ full_line = line.gsub(/\n/, '')
+
+ if line.match(/^@@ -/)
+ type = "match"
+
+ line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
+ line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
+
+ next if line_old <= 1 && line_new <= 1 #top of file
+ yielder << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
+ line_obj_index += 1
+ next
+ elsif line[0] == '\\'
+ type = 'nonewline'
+ yielder << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
+ line_obj_index += 1
+ else
+ type = identification_type(line)
+ yielder << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
+ line_obj_index += 1
+ end
+
+
+ case line[0]
+ when "+"
+ line_new += 1
+ when "-"
+ line_old += 1
+ when "\\"
+ # No increment
+ else
+ line_new += 1
+ line_old += 1
+ end
end
end
-
- lines_obj
end
def empty?
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index a05ffeb9cd2..41f0edcaf7e 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -50,7 +50,7 @@ module Gitlab
end
def compare_timeout
- compare.timeout if compare
+ diffs.overflow? if diffs
end
def reverse_compare?
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index bbe400dad88..f09e4fcb154 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -81,7 +81,7 @@ describe Projects::CommitController do
expect(response.body).to start_with("diff --git")
# without whitespace option, there are more than 2 diff_splits
- diff_splits = assigns(:diffs)[0].diff.split("\n")
+ diff_splits = assigns(:diffs).first.diff.split("\n")
expect(diff_splits.length).to be <= 2
end
end
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index be19f1abc53..788a609ee40 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -19,7 +19,7 @@ describe Projects::CompareController do
to: ref_to)
expect(response).to be_success
- expect(assigns(:diffs).length).to be >= 1
+ expect(assigns(:diffs).first).to_not be_nil
expect(assigns(:commits).length).to be >= 1
end
@@ -32,10 +32,10 @@ describe Projects::CompareController do
w: 1)
expect(response).to be_success
- expect(assigns(:diffs).length).to be >= 1
+ expect(assigns(:diffs).first).to_not be_nil
expect(assigns(:commits).length).to be >= 1
# without whitespace option, there are more than 2 diff_splits
- diff_splits = assigns(:diffs)[0].diff.split("\n")
+ diff_splits = assigns(:diffs).first.diff.split("\n")
expect(diff_splits.length).to be <= 2
end
@@ -48,7 +48,7 @@ describe Projects::CompareController do
to: ref_to)
expect(response).to be_success
- expect(assigns(:diffs)).to eq([])
+ expect(assigns(:diffs).to_a).to eq([])
expect(assigns(:commits)).to eq([])
end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
new file mode 100644
index 00000000000..883bbaedd4e
--- /dev/null
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe Projects::ForksController do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, visibility_level: Project::PUBLIC) }
+ let(:forked_project) { Projects::ForkService.new(project, user).execute }
+ let(:group) { create(:group, owner: forked_project.creator) }
+
+ describe 'GET index' do
+ def get_forks
+ get :index,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param
+ end
+
+ context 'when fork is public' do
+ before { forked_project.update_attribute(:visibility_level, Project::PUBLIC) }
+
+ it 'should be visible for non logged in users' do
+ get_forks
+
+ expect(assigns[:forks]).to be_present
+ end
+ end
+
+ context 'when fork is private' do
+ before do
+ forked_project.update_attributes(visibility_level: Project::PRIVATE, group: group)
+ end
+
+ it 'should not be visible for non logged in users' do
+ get_forks
+
+ expect(assigns[:forks]).to be_blank
+ end
+
+ context 'when user is logged in' do
+ before { sign_in(project.creator) }
+
+ context 'when user is not a Project member neither a group member' do
+ it 'should not see the Project listed' do
+ get_forks
+
+ expect(assigns[:forks]).to be_blank
+ end
+ end
+
+ context 'when user is a member of the Project' do
+ before { forked_project.team << [project.creator, :developer] }
+
+ it 'should see the project listed' do
+ get_forks
+
+ expect(assigns[:forks]).to be_present
+ end
+ end
+
+ context 'when user is a member of the Group' do
+ before { forked_project.group.add_developer(project.creator) }
+
+ it 'should see the project listed' do
+ get_forks
+
+ expect(assigns[:forks]).to be_present
+ end
+ end
+
+ end
+ end
+ end
+
+end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 14986a74c2e..982c113e84b 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -22,65 +22,14 @@ describe DiffHelper do
end
end
- describe 'allowed_diff_size' do
+ describe 'diff_options' do
it 'should return hard limit for a diff if force diff is true' do
allow(controller).to receive(:params) { { force_show_diff: true } }
- expect(allowed_diff_size).to eq(1000)
+ expect(diff_options).to include(Commit.max_diff_options)
end
it 'should return safe limit for a diff if force diff is false' do
- expect(allowed_diff_size).to eq(100)
- end
- end
-
- describe 'allowed_diff_lines' do
- it 'should return hard limit for number of lines in a diff if force diff is true' do
- allow(controller).to receive(:params) { { force_show_diff: true } }
- expect(allowed_diff_lines).to eq(50000)
- end
-
- it 'should return safe limit for numbers of lines a diff if force diff is false' do
- expect(allowed_diff_lines).to eq(5000)
- end
- end
-
- describe 'safe_diff_files' do
- it 'should return all files from a commit that is smaller than safe limits' do
- expect(safe_diff_files(diffs, diff_refs).length).to eq(2)
- end
-
- it 'should return only the first file if the diff line count in the 2nd file takes the total beyond safe limits' do
- allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
- expect(safe_diff_files(diffs, diff_refs).length).to eq(1)
- end
-
- it 'should return all files from a commit that is beyond safe limit for numbers of lines if force diff is true' do
- allow(controller).to receive(:params) { { force_show_diff: true } }
- allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
- expect(safe_diff_files(diffs, diff_refs).length).to eq(2)
- end
-
- it 'should return only the first file if the diff line count in the 2nd file takes the total beyond hard limits' do
- allow(controller).to receive(:params) { { force_show_diff: true } }
- allow(diffs[1].diff).to receive(:lines).and_return([""] * 49999) #simulate 49999 lines
- expect(safe_diff_files(diffs, diff_refs).length).to eq(1)
- end
-
- it 'should return only a safe number of file diffs if a commit touches more files than the safe limits' do
- large_diffs = diffs * 100 #simulate 200 diffs
- expect(safe_diff_files(large_diffs, diff_refs).length).to eq(100)
- end
-
- it 'should return all file diffs if a commit touches more files than the safe limits but force diff is true' do
- allow(controller).to receive(:params) { { force_show_diff: true } }
- large_diffs = diffs * 100 #simulate 200 diffs
- expect(safe_diff_files(large_diffs, diff_refs).length).to eq(200)
- end
-
- it 'should return a limited file diffs if a commit touches more files than the hard limits and force diff is true' do
- allow(controller).to receive(:params) { { force_show_diff: true } }
- very_large_diffs = diffs * 1000 #simulate 2000 diffs
- expect(safe_diff_files(very_large_diffs, diff_refs).length).to eq(1000)
+ expect(diff_options).not_to include(:max_lines, :max_files)
end
end
diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
index b80a28a33ea..e3788bee813 100644
--- a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
+++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
@@ -1,6 +1,6 @@
-%form{ action: '/foo' }
- %input.js-quick-submit{ type: 'text' }
- %textarea.js-quick-submit
+%form.js-quick-submit{ action: '/foo' }
+ %input{ type: 'text' }
+ %textarea
%input{ type: 'submit'} Submit
%button.btn{ type: 'submit' } Submit
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index fe0dea77909..f576c39284e 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -47,7 +47,7 @@ eos
end
before do
- @lines = parser.parse(diff.lines)
+ @lines = parser.parse(diff.lines).to_a
end
it { expect(@lines.size).to eq(30) }
diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb
index 56ae2a8d121..b2d7a799810 100644
--- a/spec/lib/gitlab/email/message/repository_push_spec.rb
+++ b/spec/lib/gitlab/email/message/repository_push_spec.rb
@@ -72,7 +72,7 @@ describe Gitlab::Email::Message::RepositoryPush do
describe '#compare_timeout' do
subject { message.compare_timeout }
- it { is_expected.to eq compare.timeout }
+ it { is_expected.to eq compare.diffs.overflow? }
end
describe '#reverse_compare?' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 3ccb627a259..f9842d23afa 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -102,7 +102,7 @@ describe Project, models: true do
expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/)
end
end
-
+
describe 'project token' do
it 'should set an random token if none provided' do
project = FactoryGirl.create :empty_project, runners_token: ''
@@ -524,30 +524,30 @@ describe Project, models: true do
context 'for shared runners disabled' do
let(:shared_runners_enabled) { false }
-
+
it 'there are no runners available' do
expect(project.any_runners?).to be_falsey
end
-
+
it 'there is a specific runner' do
project.runners << specific_runner
expect(project.any_runners?).to be_truthy
end
-
+
it 'there is a shared runner, but they are prohibited to use' do
shared_runner
expect(project.any_runners?).to be_falsey
end
-
+
it 'checks the presence of specific runner' do
project.runners << specific_runner
expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
end
end
-
+
context 'for shared runners enabled' do
let(:shared_runners_enabled) { true }
-
+
it 'there is a shared runner' do
shared_runner
expect(project.any_runners?).to be_truthy
@@ -583,4 +583,67 @@ describe Project, models: true do
end
end
+
+ describe '#rename_repo' do
+ let(:project) { create(:project) }
+ let(:gitlab_shell) { Gitlab::Shell.new }
+
+ before do
+ # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
+ # call. This makes testing a bit easier.
+ allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
+ end
+
+ it 'renames a repository' do
+ allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
+
+ ns = project.namespace_dir
+
+ expect(gitlab_shell).to receive(:mv_repository).
+ ordered.
+ with("#{ns}/foo", "#{ns}/#{project.path}").
+ and_return(true)
+
+ expect(gitlab_shell).to receive(:mv_repository).
+ ordered.
+ with("#{ns}/foo.wiki", "#{ns}/#{project.path}.wiki").
+ and_return(true)
+
+ expect_any_instance_of(SystemHooksService).
+ to receive(:execute_hooks_for).
+ with(project, :rename)
+
+ expect_any_instance_of(Gitlab::UploadsTransfer).
+ to receive(:rename_project).
+ with('foo', project.path, ns)
+
+ expect(project).to receive(:expire_caches_before_rename)
+
+ project.rename_repo
+ end
+ end
+
+ describe '#expire_caches_before_rename' do
+ let(:project) { create(:project) }
+ let(:repo) { double(:repo, exists?: true) }
+ let(:wiki) { double(:wiki, exists?: true) }
+
+ it 'expires the caches of the repository and wiki' do
+ allow(Repository).to receive(:new).
+ with('foo', project).
+ and_return(repo)
+
+ allow(Repository).to receive(:new).
+ with('foo.wiki', project).
+ and_return(wiki)
+
+ expect(repo).to receive(:expire_cache)
+ expect(repo).to receive(:expire_emptiness_caches)
+
+ expect(wiki).to receive(:expire_cache)
+ expect(wiki).to receive(:expire_emptiness_caches)
+
+ project.expire_caches_before_rename('foo')
+ end
+ end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index dfa18f69e05..1527eddfa48 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -137,7 +137,6 @@ end
# keys GET /keys(.:format) keys#index
# POST /keys(.:format) keys#create
-# new_key GET /keys/new(.:format) keys#new
# edit_key GET /keys/:id/edit(.:format) keys#edit
# key GET /keys/:id(.:format) keys#show
# PUT /keys/:id(.:format) keys#update
@@ -151,10 +150,6 @@ describe Profiles::KeysController, "routing" do
expect(post("/profile/keys")).to route_to('profiles/keys#create')
end
- it "to #new" do
- expect(get("/profile/keys/new")).to route_to('profiles/keys#new')
- end
-
it "to #edit" do
expect(get("/profile/keys/1/edit")).to route_to('profiles/keys#edit', id: '1')
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 450250ba032..fea8182bd30 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -79,7 +79,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes.last.note).to include('changed to merged') }
it { expect(@merge_request).to be_merged }
- it { expect(@merge_request.diffs.length).to be > 0 }
+ it { expect(@merge_request.diffs.size).to be > 0 }
it { expect(@fork_merge_request).to be_merged }
it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 1bdc03af12d..5dcc39f5fdc 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -474,8 +474,8 @@ describe SystemNoteService, services: true do
describe "existing reference" do
before do
- message = "[#{author.name}|http://localhost/u/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]."
- WebMock.stub_request(:get, jira_api_comment_url).to_return(body: "{\"comments\":[{\"body\":\"#{message}\"}]}")
+ message = %Q{[#{author.name}|http://localhost/u/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]:\\n'#{commit.title}'}
+ WebMock.stub_request(:get, jira_api_comment_url).to_return(body: %Q({"comments":[{"body":"#{message}"}]}))
end
subject { described_class.cross_reference(jira_issue, commit, author) }