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