diff options
author | Vinnie Okada <vokada@mrvinn.com> | 2014-12-08 06:25:58 +0300 |
---|---|---|
committer | Vinnie Okada <vokada@mrvinn.com> | 2014-12-08 06:25:58 +0300 |
commit | 742e6eeed221489d5f35bdfde2e6ce55db75d25f (patch) | |
tree | 710c01fbd18e81a7590819161434e19855e35a97 /app | |
parent | 7a5072c5a8f03cd7342a5f8e74e1fde0250ce360 (diff) | |
parent | bbf9953b99d59801c72dd7b9550ee149ca77bfcf (diff) |
Merge branch 'upstream-master' into markdown-preview
Conflicts:
spec/routing/project_routing_spec.rb
Diffstat (limited to 'app')
227 files changed, 1753 insertions, 1166 deletions
diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee index fdefbfb92bd..4f76d8ce486 100644 --- a/app/assets/javascripts/activities.js.coffee +++ b/app/assets/javascripts/activities.js.coffee @@ -1,4 +1,4 @@ -class Activities +class @Activities constructor: -> Pager.init 20, true $(".event_filter_link").bind "click", (event) => @@ -27,5 +27,3 @@ class Activities event_filters.splice index, 1 $.cookie "event_filter", event_filters.join(","), { path: '/' } - -@Activities = Activities diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index a333eed87f2..bcb2e6df7c0 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -1,4 +1,4 @@ -class Admin +class @Admin constructor: -> $('input#user_force_random_password').on 'change', (elem) -> elems = $('#user_password, #user_password_confirmation') @@ -51,5 +51,3 @@ class Admin $('li.group_member').bind 'ajax:success', -> Turbolinks.visit(location.href) - -@Admin = Admin diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index ff0d0bb32b9..e9a28c12159 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -18,6 +18,7 @@ #= require jquery.turbolinks #= require turbolinks #= require bootstrap +#= require password_strength #= require select2 #= require raphael #= require g.raphael-min @@ -63,7 +64,7 @@ window.extractLast = (term) -> return split( term ).pop() window.rstrip = (val) -> - return val.replace(/\s+$/, '') + return if val then val.replace(/\s+$/, '') else val # Disable button if text field is empty window.disableButtonIfEmptyField = (field_selector, button_selector) -> diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob.js.coffee index 9db919e5a62..a5f15f80c5c 100644 --- a/app/assets/javascripts/blob.js.coffee +++ b/app/assets/javascripts/blob.js.coffee @@ -1,4 +1,4 @@ -class BlobView +class @BlobView constructor: -> # handle multi-line select handleMultiSelect = (e) -> @@ -71,6 +71,3 @@ class BlobView # Highlight the correct lines when the hash part of the URL changes $(window).on("hashchange", highlightBlobLines) - - -@BlobView = BlobView diff --git a/app/assets/javascripts/commit.js.coffee b/app/assets/javascripts/commit.js.coffee index 5f53439ca4b..0566e239191 100644 --- a/app/assets/javascripts/commit.js.coffee +++ b/app/assets/javascripts/commit.js.coffee @@ -1,6 +1,4 @@ -class Commit +class @Commit constructor: -> $('.files .diff-file').each -> new CommitFile(this) - -@Commit = Commit diff --git a/app/assets/javascripts/commit/file.js.coffee b/app/assets/javascripts/commit/file.js.coffee index 4db9116a9de..83e793863b6 100644 --- a/app/assets/javascripts/commit/file.js.coffee +++ b/app/assets/javascripts/commit/file.js.coffee @@ -1,7 +1,5 @@ -class CommitFile +class @CommitFile constructor: (file) -> if $('.image', file).length new ImageFile(file) - -@CommitFile = CommitFile diff --git a/app/assets/javascripts/commit/image-file.js.coffee b/app/assets/javascripts/commit/image-file.js.coffee index 607b85eb45c..9e5f49b1f69 100644 --- a/app/assets/javascripts/commit/image-file.js.coffee +++ b/app/assets/javascripts/commit/image-file.js.coffee @@ -1,4 +1,4 @@ -class ImageFile +class @ImageFile # Width where images must fits in, for 2-up this gets divided by 2 @availWidth = 900 @@ -124,5 +124,3 @@ class ImageFile else img.on 'load', => callback.call(this, domImg.naturalWidth, domImg.naturalHeight) - -@ImageFile = ImageFile diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index 784d7d20bb1..c183e78e513 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -1,4 +1,4 @@ -class CommitsList +class @CommitsList @data = ref: null limit: 0 @@ -53,5 +53,3 @@ class CommitsList @disable callback: => this.getOld() - -this.CommitsList = CommitsList diff --git a/app/assets/javascripts/confirm_danger_modal.js.coffee b/app/assets/javascripts/confirm_danger_modal.js.coffee index 1687b7d961c..bb99edbd09e 100644 --- a/app/assets/javascripts/confirm_danger_modal.js.coffee +++ b/app/assets/javascripts/confirm_danger_modal.js.coffee @@ -1,4 +1,4 @@ -class ConfirmDangerModal +class @ConfirmDangerModal constructor: (form, text) -> @form = form $('.js-confirm-text').text(text || '') @@ -16,5 +16,3 @@ class ConfirmDangerModal $('.js-confirm-danger-submit').on 'click', => @form.submit() - -@ConfirmDangerModal = ConfirmDangerModal diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee index c4a0ccd9c2a..6ef5a539b8f 100644 --- a/app/assets/javascripts/dashboard.js.coffee +++ b/app/assets/javascripts/dashboard.js.coffee @@ -1,4 +1,4 @@ -class Dashboard +class @Dashboard constructor: -> @initSidebarTab() @@ -28,6 +28,3 @@ class Dashboard # show tab from cookie sidebar_filter = $.cookie(key) $("#" + sidebar_filter).tab('show') if sidebar_filter - - -@Dashboard = Dashboard diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee index dbe00c487dc..52b4208524f 100644 --- a/app/assets/javascripts/diff.js.coffee +++ b/app/assets/javascripts/diff.js.coffee @@ -1,4 +1,4 @@ -class Diff +class @Diff UNFOLD_COUNT = 20 constructor: -> $(document).on('click', '.js-unfold', (event) => @@ -41,6 +41,3 @@ class Diff lines = line.children().slice(0, 2) line_numbers = ($(l).attr('data-linenumber') for l in lines) (parseInt(line_number) for line_number in line_numbers) - - -@Diff = Diff diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 00b52758fa8..e8b71a71945 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -58,15 +58,11 @@ class Dispatcher when 'groups:show', 'projects:show' new Activities() shortcut_handler = new ShortcutsNavigation() - when 'projects:new' - new Project() - when 'projects:edit' - new Project() - shortcut_handler = new ShortcutsNavigation() - when 'projects:teams:members:index' - new TeamMembers() when 'groups:members' new GroupMembers() + new UsersSelect() + when 'groups:new', 'groups:edit', 'admin:groups:edit' + new GroupAvatar() when 'projects:tree:show' new TreeView() shortcut_handler = new ShortcutsNavigation() @@ -79,13 +75,35 @@ class Dispatcher # Ensure we don't create a particular shortcut handler here. This is # already created, where the network graph is created. shortcut_handler = true + when 'projects:forks:new' + new ProjectFork() + when 'users:show' + new User() switch path.first() - when 'admin' then new Admin() + when 'admin' + new Admin() + switch path[1] + when 'groups' + new UsersSelect() + when 'projects' + new NamespaceSelect() when 'dashboard' shortcut_handler = new ShortcutsDashboardNavigation() + when 'profiles' + new Profile() when 'projects' + new Project() switch path[1] + when 'edit' + shortcut_handler = new ShortcutsNavigation() + new ProjectNew() + when 'new' + new ProjectNew() + when 'show' + new ProjectShow() + when 'issues', 'merge_requests' + new ProjectUsersSelect() when 'wikis' new Wikis() shortcut_handler = new ShortcutsNavigation() @@ -94,6 +112,7 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' shortcut_handler = new ShortcutsNavigation() + new UsersSelect() # If we haven't installed a custom shortcut handler, install the default one diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee index cf1a37eae3e..b39ab0c4475 100644 --- a/app/assets/javascripts/flash.js.coffee +++ b/app/assets/javascripts/flash.js.coffee @@ -1,4 +1,4 @@ -class Flash +class @Flash constructor: (message, type)-> flash = $(".flash-container") flash.html("") @@ -10,5 +10,3 @@ class Flash flash.click -> $(@).fadeOut() flash.show() - -@Flash = Flash diff --git a/app/assets/javascripts/group_avatar.js.coffee b/app/assets/javascripts/group_avatar.js.coffee new file mode 100644 index 00000000000..0825fd3ce52 --- /dev/null +++ b/app/assets/javascripts/group_avatar.js.coffee @@ -0,0 +1,9 @@ +class @GroupAvatar + constructor: -> + $('.js-choose-group-avatar-button').bind "click", -> + form = $(this).closest("form") + form.find(".js-group-avatar-input").click() + $('.js-group-avatar-input').bind "change", -> + form = $(this).closest("form") + filename = $(this).val().replace(/^.*[\\\/]/, '') + form.find(".js-avatar-filename").text(filename) diff --git a/app/assets/javascripts/groups.js.coffee b/app/assets/javascripts/groups.js.coffee index 4b1000f9a6a..cc905e91ea2 100644 --- a/app/assets/javascripts/groups.js.coffee +++ b/app/assets/javascripts/groups.js.coffee @@ -1,17 +1,4 @@ -class GroupMembers +class @GroupMembers constructor: -> $('li.group_member').bind 'ajax:success', -> $(this).fadeOut() - -@GroupMembers = GroupMembers - -$ -> - # avatar - $('.js-choose-group-avatar-button').bind "click", -> - form = $(this).closest("form") - form.find(".js-group-avatar-input").click() - - $('.js-group-avatar-input').bind "change", -> - form = $(this).closest("form") - filename = $(this).val().replace(/^.*[\\\/]/, '') - form.find(".js-avatar-filename").text(filename) diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 0e2a2fa792a..597b4695a6d 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -1,4 +1,4 @@ -class Issue +class @Issue constructor: -> $('.edit-issue.inline-update input[type="submit"]').hide() $(".issue-box .inline-update").on "change", "select", -> @@ -15,5 +15,3 @@ class Issue "issue" updateTaskState ) - -@Issue = Issue diff --git a/app/assets/javascripts/labels.js.coffee b/app/assets/javascripts/labels.js.coffee index d306ad64f5b..1bc8840f9ac 100644 --- a/app/assets/javascripts/labels.js.coffee +++ b/app/assets/javascripts/labels.js.coffee @@ -1,4 +1,4 @@ -class Labels +class @Labels constructor: -> form = $('.label-form') @setupLabelForm(form) @@ -31,5 +31,3 @@ class Labels # Notify the form, that color has changed $('.label-form').trigger('keyup') e.preventDefault() - -@Labels = Labels diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 9f99ff403f8..46e06424e5a 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -1,4 +1,4 @@ -class MergeRequest +class @MergeRequest constructor: (@opts) -> @initContextWidget() this.$el = $('.merge-request') @@ -132,5 +132,3 @@ class MergeRequest this.$('.automerge_widget').hide() this.$('.merge-in-progress').hide() this.$('.automerge_widget.already_cannot_be_merged').show() - -this.MergeRequest = MergeRequest diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index ea01c318d4f..c42f31933d3 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -1,4 +1,4 @@ -class Milestone +class @Milestone @updateIssue: (li, issue_url, data) -> $.ajax type: "PUT" @@ -115,5 +115,3 @@ class Milestone Milestone.updateMergeRequest(ui.item, merge_request_url, data) ).disableSelection() - -@Milestone = Milestone diff --git a/app/assets/javascripts/namespace_select.js.coffee b/app/assets/javascripts/namespace_select.js.coffee index 00d135d1449..a02c4515ccc 100644 --- a/app/assets/javascripts/namespace_select.js.coffee +++ b/app/assets/javascripts/namespace_select.js.coffee @@ -1,24 +1,25 @@ -$ -> - namespaceFormatResult = (namespace) -> - markup = "<div class='namespace-result'>" - markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" - markup += "<span class='namespace-path'>" + namespace.path + "</span>" - markup += "</div>" - markup +class @NamespaceSelect + constructor: -> + namespaceFormatResult = (namespace) -> + markup = "<div class='namespace-result'>" + markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" + markup += "<span class='namespace-path'>" + namespace.path + "</span>" + markup += "</div>" + markup - formatSelection = (namespace) -> - namespace.kind + ": " + namespace.path + formatSelection = (namespace) -> + namespace.kind + ": " + namespace.path - $('.ajax-namespace-select').each (i, select) -> - $(select).select2 - placeholder: "Search for namespace" - multiple: $(select).hasClass('multiselect') - minimumInputLength: 0 - query: (query) -> - Api.namespaces query.term, (namespaces) -> - data = { results: namespaces } - query.callback(data) + $('.ajax-namespace-select').each (i, select) -> + $(select).select2 + placeholder: "Search for namespace" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) -> + Api.namespaces query.term, (namespaces) -> + data = { results: namespaces } + query.callback(data) - dropdownCssClass: "ajax-namespace-dropdown" - formatResult: namespaceFormatResult - formatSelection: formatSelection + dropdownCssClass: "ajax-namespace-dropdown" + formatResult: namespaceFormatResult + formatSelection: formatSelection diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b6bb0c42ad4..30f8530dfda 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -1,4 +1,4 @@ -class Notes +class @Notes @interval: null constructor: (notes_url, note_ids, last_fetched_at) -> @@ -465,7 +465,3 @@ class Notes else form.find('.js-note-target-reopen').text('Reopen') form.find('.js-note-target-close').text('Close') - - - -@Notes = Notes diff --git a/app/assets/javascripts/notes_votes.js.coffee b/app/assets/javascripts/notes_votes.js.coffee index b31eb9ac9de..65c149b7886 100644 --- a/app/assets/javascripts/notes_votes.js.coffee +++ b/app/assets/javascripts/notes_votes.js.coffee @@ -1,4 +1,4 @@ -class NotesVotes +class @NotesVotes updateVotes: -> votes = $("#votes .votes") notes = $("#notes-list .note .vote") @@ -18,5 +18,3 @@ class NotesVotes # replace vote numbers votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes) votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes) - -@NotesVotes = NotesVotes diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee new file mode 100644 index 00000000000..825f5630266 --- /dev/null +++ b/app/assets/javascripts/password_strength.js.coffee @@ -0,0 +1,31 @@ +#= require pwstrength-bootstrap-1.2.2 +overwritten_messages = + wordSimilarToUsername: "Your password should not contain your username" + +overwritten_rules = + wordSequences: false + +options = + showProgressBar: false + showVerdicts: false + showPopover: true + showErrors: true + showStatus: true + errorMessages: overwritten_messages + +$(document).ready -> + profileOptions = {} + profileOptions.ui = options + profileOptions.rules = + activated: overwritten_rules + + deviseOptions = {} + deviseOptions.common = + usernameField: "#user_username" + deviseOptions.ui = options + deviseOptions.rules = + activated: overwritten_rules + + $("#user_password_profile").pwstrength profileOptions + $("#user_password_sign_up").pwstrength deviseOptions + $("#user_password_recover").pwstrength deviseOptions diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index 0e99921f899..de356fbec77 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -1,30 +1,29 @@ -$ -> - $('.edit_user .application-theme input, .edit_user .code-preview-theme input').click -> - # Submit the form - $('.edit_user').submit() +class @Profile + constructor: -> + $('.edit_user .application-theme input, .edit_user .code-preview-theme input').click -> + # Submit the form + $('.edit_user').submit() - new Flash("Appearance settings saved", "notice") + new Flash("Appearance settings saved", "notice") - $('.update-username form').on 'ajax:before', -> - $('.loading-gif').show() - $(this).find('.update-success').hide() - $(this).find('.update-failed').hide() + $('.update-username form').on 'ajax:before', -> + $('.loading-gif').show() + $(this).find('.update-success').hide() + $(this).find('.update-failed').hide() - $('.update-username form').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() - $(this).find('.loading-gif').hide() + $('.update-username form').on 'ajax:complete', -> + $(this).find('.btn-save').enableButton() + $(this).find('.loading-gif').hide() - $('.update-notifications').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() + $('.update-notifications').on 'ajax:complete', -> + $(this).find('.btn-save').enableButton() - $('.js-choose-user-avatar-button').bind "click", -> - form = $(this).closest("form") - form.find(".js-user-avatar-input").click() + $('.js-choose-user-avatar-button').bind "click", -> + form = $(this).closest("form") + form.find(".js-user-avatar-input").click() - $('.js-user-avatar-input').bind "change", -> - form = $(this).closest("form") - filename = $(this).val().replace(/^.*[\\\/]/, '') - form.find(".js-avatar-filename").text(filename) - - $('.profile-groups-avatars').tooltip("placement": "top") + $('.js-user-avatar-input').bind "change", -> + form = $(this).closest("form") + filename = $(this).val().replace(/^.*[\\\/]/, '') + form.find(".js-avatar-filename").text(filename) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index f4a8a178e76..5a9cc66c8f0 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -1,62 +1,20 @@ -class Project +class @Project constructor: -> - $('.project-edit-container').on 'ajax:before', => - $('.project-edit-container').hide() - $('.save-project-loader').show() - - @initEvents() - - - initEvents: -> - disableButtonIfEmptyField '#project_name', '.project-submit' - - $('#project_issues_enabled').change -> - if ($(this).is(':checked') == true) - $('#project_issues_tracker').removeAttr('disabled') - else - $('#project_issues_tracker').attr('disabled', 'disabled') - - $('#project_issues_tracker').change() - - $('#project_issues_tracker').change -> - if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled')) - $('#project_issues_tracker_id').attr('disabled', 'disabled') - else - $('#project_issues_tracker_id').removeAttr('disabled') - - -@Project = Project - -$ -> - # Git clone panel switcher - scope = $ '.git-clone-holder' - if scope.length > 0 - $('a, button', scope).click -> - $('a, button', scope).removeClass 'active' - $(@).addClass 'active' - $('#project_clone', scope).val $(@).data 'clone' - $(".clone").text("").append $(@).data 'clone' - - # Ref switcher - $('.project-refs-select').on 'change', -> - $(@).parents('form').submit() - - $('.hide-no-ssh-message').on 'click', (e) -> - path = '/' - $.cookie('hide_no_ssh_message', 'false', { path: path }) - $(@).parents('.no-ssh-key-message').hide() - e.preventDefault() - - $('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) -> - $(@).toggleClass('on').find('.count').html(data.star_count) - .on 'ajax:error', (e, xhr, status, error) -> - new Flash('Star toggle failed. Try again later.', 'alert') - - $("a[data-toggle='tab']").on "shown.bs.tab", (e) -> - $.cookie "default_view", $(e.target).attr("href") - - defaultView = $.cookie("default_view") - if defaultView - $("a[href=" + defaultView + "]").tab "show" - else - $("a[data-toggle='tab']:first").tab "show" + # Git clone panel switcher + scope = $ '.git-clone-holder' + if scope.length > 0 + $('a, button', scope).click -> + $('a, button', scope).removeClass 'active' + $(@).addClass 'active' + $('#project_clone', scope).val $(@).data 'clone' + $(".clone").text("").append $(@).data 'clone' + + # Ref switcher + $('.project-refs-select').on 'change', -> + $(@).parents('form').submit() + + $('.hide-no-ssh-message').on 'click', (e) -> + path = '/' + $.cookie('hide_no_ssh_message', 'false', { path: path }) + $(@).parents('.no-ssh-key-message').hide() + e.preventDefault() diff --git a/app/assets/javascripts/project_fork.js.coffee b/app/assets/javascripts/project_fork.js.coffee new file mode 100644 index 00000000000..e15a1c4ef76 --- /dev/null +++ b/app/assets/javascripts/project_fork.js.coffee @@ -0,0 +1,5 @@ +class @ProjectFork + constructor: -> + $('.fork-thumbnail a').on 'click', -> + $('.fork-namespaces').hide() + $('.save-project-loader').show() diff --git a/app/assets/javascripts/project_import.js.coffee b/app/assets/javascripts/project_import.js.coffee index 7cf44da99fe..6633564a079 100644 --- a/app/assets/javascripts/project_import.js.coffee +++ b/app/assets/javascripts/project_import.js.coffee @@ -1,7 +1,5 @@ -class ProjectImport +class @ProjectImport constructor: -> setTimeout -> Turbolinks.visit(location.href) , 5000 - -@ProjectImport = ProjectImport diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee new file mode 100644 index 00000000000..f4a2ca813d2 --- /dev/null +++ b/app/assets/javascripts/project_new.js.coffee @@ -0,0 +1,25 @@ +class @ProjectNew + constructor: -> + $('.project-edit-container').on 'ajax:before', => + $('.project-edit-container').hide() + $('.save-project-loader').show() + + @initEvents() + + + initEvents: -> + disableButtonIfEmptyField '#project_name', '.project-submit' + + $('#project_issues_enabled').change -> + if ($(this).is(':checked') == true) + $('#project_issues_tracker').removeAttr('disabled') + else + $('#project_issues_tracker').attr('disabled', 'disabled') + + $('#project_issues_tracker').change() + + $('#project_issues_tracker').change -> + if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled')) + $('#project_issues_tracker_id').attr('disabled', 'disabled') + else + $('#project_issues_tracker_id').removeAttr('disabled') diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee new file mode 100644 index 00000000000..02a7d7b731d --- /dev/null +++ b/app/assets/javascripts/project_show.js.coffee @@ -0,0 +1,15 @@ +class @ProjectShow + constructor: -> + $('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) -> + $(@).toggleClass('on').find('.count').html(data.star_count) + .on 'ajax:error', (e, xhr, status, error) -> + new Flash('Star toggle failed. Try again later.', 'alert') + + $("a[data-toggle='tab']").on "shown.bs.tab", (e) -> + $.cookie "default_view", $(e.target).attr("href") + + defaultView = $.cookie("default_view") + if defaultView + $("a[href=" + defaultView + "]").tab "show" + else + $("a[data-toggle='tab']:first").tab "show" diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee index cfbcd5108c8..7fb33926096 100644 --- a/app/assets/javascripts/project_users_select.js.coffee +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -1,6 +1,6 @@ -@projectUsersSelect = - init: -> - $('.ajax-project-users-select').each (i, select) -> +class @ProjectUsersSelect + constructor: -> + $('.ajax-project-users-select').each (i, select) => project_id = $(select).data('project-id') || $('body').data('project-id') $(select).select2 @@ -28,14 +28,16 @@ Api.user(id, callback) - formatResult: projectUsersSelect.projectUserFormatResult - formatSelection: projectUsersSelect.projectUserFormatSelection + 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 - projectUserFormatResult: (user) -> + formatResult: (user) -> if user.avatar_url avatar = user.avatar_url else @@ -52,8 +54,5 @@ <div class='user-username'>#{user.username}</div> </div>" - projectUserFormatSelection: (user) -> + formatSelection: (user) -> user.name - -$ -> - projectUsersSelect.init() diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index e144dfa1d68..c1801365266 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -1,4 +1,4 @@ -class SearchAutocomplete +class @SearchAutocomplete constructor: (search_autocomplete_path, project_id, project_ref) -> project_id = '' unless project_id project_ref = '' unless project_ref @@ -9,5 +9,3 @@ class SearchAutocomplete minLength: 1 select: (event, ui) -> location.href = ui.item.url - -@SearchAutocomplete = SearchAutocomplete diff --git a/app/assets/javascripts/stat_graph.js.coffee b/app/assets/javascripts/stat_graph.js.coffee index b129619696f..f36c71fd25e 100644 --- a/app/assets/javascripts/stat_graph.js.coffee +++ b/app/assets/javascripts/stat_graph.js.coffee @@ -1,4 +1,4 @@ -class window.StatGraph +class @StatGraph @log: {} @get_log: -> @log diff --git a/app/assets/javascripts/stat_graph_contributors.js.coffee b/app/assets/javascripts/stat_graph_contributors.js.coffee index ab785a54543..27f0fd31d50 100644 --- a/app/assets/javascripts/stat_graph_contributors.js.coffee +++ b/app/assets/javascripts/stat_graph_contributors.js.coffee @@ -1,4 +1,4 @@ -class window.ContributorsStatGraph +class @ContributorsStatGraph init: (log) -> @parsed_log = ContributorsStatGraphUtil.parse_log(log) @set_current_field("commits") diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee index 834c7e5dab0..9952fa0b00a 100644 --- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee +++ b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee @@ -1,4 +1,4 @@ -class window.ContributorsGraph +class @ContributorsGraph MARGIN: top: 20 right: 20 @@ -44,7 +44,7 @@ class window.ContributorsGraph set_data: (data) -> @data = data -class window.ContributorsMasterGraph extends ContributorsGraph +class @ContributorsMasterGraph extends ContributorsGraph constructor: (@data) -> @width = $('.container').width() - 70 @height = 200 @@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph @svg.select("path").attr("d", @area) @svg.select(".y.axis").call(@y_axis) -class window.ContributorsAuthorGraph extends ContributorsGraph +class @ContributorsAuthorGraph extends ContributorsGraph constructor: (@data) -> @width = $('.container').width()/2 - 100 @height = 200 diff --git a/app/assets/javascripts/team_members.js.coffee b/app/assets/javascripts/team_members.js.coffee deleted file mode 100644 index 5eaa8ad4ff9..00000000000 --- a/app/assets/javascripts/team_members.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -class TeamMembers - constructor: -> - $('.team-members .project-access-select').on "change", -> - $(this.form).submit() - -@TeamMembers = TeamMembers diff --git a/app/assets/javascripts/tree.js.coffee b/app/assets/javascripts/tree.js.coffee index 4852e879b68..d428db5b422 100644 --- a/app/assets/javascripts/tree.js.coffee +++ b/app/assets/javascripts/tree.js.coffee @@ -1,4 +1,4 @@ -class TreeView +class @TreeView constructor: -> @initKeyNav() @@ -39,5 +39,3 @@ class TreeView else if e.which is 13 path = $('.tree-item.selected .tree-item-file-name a').attr('href') Turbolinks.visit(path) - -@TreeView = TreeView diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee new file mode 100644 index 00000000000..8a2e2421c2e --- /dev/null +++ b/app/assets/javascripts/user.js.coffee @@ -0,0 +1,3 @@ +class @User + constructor: -> + $('.profile-groups-avatars').tooltip("placement": "top") diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 86318bd7d94..9eee7406511 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,5 +1,30 @@ -$ -> - userFormatResult = (user) -> +class @UsersSelect + constructor: -> + $('.ajax-users-select').each (i, select) => + $(select).select2 + placeholder: "Search for a user" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) -> + Api.users query.term, (users) -> + data = { results: users } + query.callback(data) + + initSelection: (element, callback) -> + id = $(element).val() + if id isnt "" + Api.user(id, callback) + + + formatResult: (args...) => + @formatResult(args...) + formatSelection: (args...) => + @formatSelection(args...) + dropdownCssClass: "ajax-users-dropdown" + 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 @@ -11,27 +36,5 @@ $ -> <div class='user-username'>#{user.username}</div> </div>" - userFormatSelection = (user) -> + formatSelection: (user) -> user.name - - $('.ajax-users-select').each (i, select) -> - $(select).select2 - placeholder: "Search for a user" - multiple: $(select).hasClass('multiselect') - minimumInputLength: 0 - query: (query) -> - Api.users query.term, (users) -> - data = { results: users } - query.callback(data) - - initSelection: (element, callback) -> - id = $(element).val() - if id isnt "" - Api.user(id, callback) - - - formatResult: userFormatResult - formatSelection: userFormatSelection - dropdownCssClass: "ajax-users-dropdown" - escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results - m diff --git a/app/assets/javascripts/wikis.js.coffee b/app/assets/javascripts/wikis.js.coffee index 17e790e5b7c..66757565d3a 100644 --- a/app/assets/javascripts/wikis.js.coffee +++ b/app/assets/javascripts/wikis.js.coffee @@ -1,4 +1,4 @@ -class Wikis +class @Wikis constructor: -> $('.build-new-wiki').bind "click", -> field = $('#new_wiki_path') @@ -7,6 +7,3 @@ class Wikis if(slug.length > 0) location.href = path + "/" + slug - - -@Wikis = Wikis diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss index be4c4d07f1c..469f4f296ae 100644 --- a/app/assets/stylesheets/behaviors.scss +++ b/app/assets/stylesheets/behaviors.scss @@ -1,12 +1,22 @@ // Details //-------- -.js-details-container .content { display: none; } -.js-details-container .content.hide { display: block; } -.js-details-container.open .content { display: block; } -.js-details-container.open .content.hide { display: none; } +.js-details-container { + .content { + display: none; + &.hide { display: block; } + } + &.open .content { + display: block; + &.hide { display: none; } + } +} // Toggle between two states. -.js-toggler-container .turn-on { display: block; } -.js-toggler-container .turn-off { display: none; } -.js-toggler-container.on .turn-on { display: none; } -.js-toggler-container.on .turn-off { display: block; } +.js-toggler-container { + .turn-on { display: block; } + .turn-off { display: none; } + &.on { + .turn-on { display: none; } + .turn-off { display: block; } + } +} diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index cd2f4e45e3c..2fc738c18d8 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -330,10 +330,6 @@ table { } } -@media (max-width: $screen-xs-max) { - .container .content { margin-top: 20px; } -} - .wiki .highlight, .note-body .highlight { margin-bottom: 9px; } diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss index e2b0ef0c5ea..1ed41272ac5 100644 --- a/app/assets/stylesheets/generic/files.scss +++ b/app/assets/stylesheets/generic/files.scss @@ -42,7 +42,6 @@ } .file-content { background: #fff; - font-size: 11px; &.image_file { background: #eee; @@ -54,8 +53,6 @@ } &.wiki { - font-size: 14px; - line-height: 1.6; padding: 25px; .highlight { diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/generic/highlight.scss index 4110bddf4f3..ae08539d454 100644 --- a/app/assets/stylesheets/generic/highlight.scss +++ b/app/assets/stylesheets/generic/highlight.scss @@ -59,6 +59,10 @@ pre { white-space: pre; word-wrap: normal; + + code { + font-family: $monospace_font; + } } } } diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss index 94149594e24..79fbad4b946 100644 --- a/app/assets/stylesheets/generic/issue_box.scss +++ b/app/assets/stylesheets/generic/issue_box.scss @@ -113,6 +113,11 @@ padding: 10px 15px; } + .cross-project-ref { + float: left; + padding: 10px 15px; + } + .creator { float: right; padding: 10px 15px; diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss new file mode 100644 index 00000000000..c164b07b104 --- /dev/null +++ b/app/assets/stylesheets/generic/mobile.scss @@ -0,0 +1,17 @@ +/** Common mobile (screen XS) styles **/ +@media (max-width: $screen-xs-max) { + .container .content { + margin-top: 20px; + } + + .nav.nav-tabs > li > a { + padding: 10px; + font-size: 12px; + margin-right: 3px; + + .badge { + display: none; + } + } +} + diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss index f29cf25fa4c..57e9e8ae5c5 100644 --- a/app/assets/stylesheets/generic/timeline.scss +++ b/app/assets/stylesheets/generic/timeline.scss @@ -75,3 +75,20 @@ } } } + +@media (max-width: $screen-xs-max) { + .timeline { + &:before { + background: none; + } + .timeline-entry .timeline-entry-inner { + .timeline-icon { + display: none; + } + + .timeline-content { + margin-left: 0; + } + } + } +} diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 36bc5df2f44..dffa2dc9ed2 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -29,28 +29,30 @@ .hljs-tag, .hljs-tag .hljs-title, - .hljs-keyword, - .hljs-literal, .hljs-strong, .hljs-change, .hljs-winutils, .hljs-flow, .lisp .hljs-title, .clojure .hljs-built_in, + .hljs-keyword, .nginx .hljs-title, .tex .hljs-special { color: #F92672; } .hljs { - color: #DDD; + color: #F8F8F2; } - .hljs .hljs-constant, - .asciidoc .hljs-code { + .asciidoc .hljs-code, + .markdown .hljs-code, + .hljs-literal, + .hljs-function .hljs-keyword { color: #66D9EF; } + .hljs-code, .hljs-class .hljs-title, .hljs-header { @@ -62,18 +64,27 @@ .hljs-symbol, .hljs-symbol .hljs-string, .hljs-value, + .hljs-constant, + .hljs-number, .hljs-regexp { - color: #BF79DB; + color: #AE81FF; + } + + .hljs-string { + color: #E6DB74; + } + + .hljs-params { + color: #fd971f; } .hljs-link_url, .hljs-tag .hljs-value, - .hljs-string, .hljs-bullet, .hljs-subst, .hljs-title, .hljs-emphasis, - .haskell .hljs-type, + .hljs-type, .hljs-preprocessor, .hljs-pragma, .ruby .hljs-class .hljs-parent, @@ -99,12 +110,12 @@ } .hljs-comment, - .java .hljs-annotation, + .hljs-annotation, .smartquote, .hljs-blockquote, .hljs-horizontal_rule, - .python .hljs-decorator, .hljs-template_comment, + .hljs-decorator, .hljs-pi, .hljs-doctype, .hljs-deletion, diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 815cf367ae8..8d5822937a0 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -186,3 +186,11 @@ } } } + +.readme-holder .wiki, .note-body, .wiki-holder { + .white { + .highlight, pre, .hljs { + background: #F9F9F9; + } + } +} diff --git a/app/assets/stylesheets/main/fonts.scss b/app/assets/stylesheets/main/fonts.scss index d90274a0db9..f945aaca848 100644 --- a/app/assets/stylesheets/main/fonts.scss +++ b/app/assets/stylesheets/main/fonts.scss @@ -1,3 +1,3 @@ /** Typo **/ -$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; +$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; $regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif; diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss index 7f607fc4e8b..5f83913b73b 100644 --- a/app/assets/stylesheets/main/mixins.scss +++ b/app/assets/stylesheets/main/mixins.scss @@ -58,8 +58,8 @@ } @mixin md-typography { - font-size: 14px; - line-height: 1.6; + font-size: 15px; + line-height: 1.5; img { max-width: 100%; @@ -93,7 +93,7 @@ blockquote p { color: #888; - font-size: 14px; + font-size: 15px; line-height: 1.5; } diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index 656aa5b18a6..a766d6e77ab 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -47,7 +47,7 @@ .event-title { @include str-truncated(72%); color: #333; - font-weight: normal; + font-weight: 500; font-size: 14px; .author_name { color: #333; @@ -56,12 +56,9 @@ .event-body { margin-left: 35px; margin-right: 100px; + color: #777; - .event-info { - color: #666; - } .event-note { - color: #666; margin-top: 5px; .md { @@ -72,7 +69,7 @@ border: none; background: #f9f9f9; border-radius: 0; - color: #666; + color: #777; margin: 0 20px; } @@ -120,7 +117,6 @@ padding: 3px; padding-left: 0; border: none; - color: #666; .commit-row-title { font-size: 12px; } @@ -186,7 +182,24 @@ } @media (max-width: $screen-xs-max) { - .event-item .event-title { - @include str-truncated(65%); + .event-item { + .event-title { + white-space: normal; + overflow: visible; + max-width: 100%; + } + .avatar { + display: none; + } + + .event-body { + margin: 0; + border-left: 2px solid #DDD; + padding-left: 10px; + } + + .event-item-timestamp { + display: none; + } } } diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index e0e0d60c387..9ad1a1db2cd 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -59,6 +59,7 @@ header { } .navbar-collapse { + margin-top: 47px; padding-right: 0; padding-left: 0; } diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index a7fa715d2e0..9a5400fffbc 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -75,7 +75,7 @@ } .participants { - margin-bottom: 10px; + margin-bottom: 20px; } .issues_bulk_update { @@ -151,4 +151,14 @@ form.edit-issue { } } } + + .issue { + &:hover .issue-actions { + display: none !important; + } + + .issue-updated-at { + display: none; + } + } } diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index 22f20a7df4d..ec844cc00b0 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -113,30 +113,36 @@ font-size: 15px; border-bottom: 1px solid #BBB; color: #777; + background-color: #F5F5F5; &.ci-success { color: $bg_success; border-color: $border_success; + background-color: #F1FAF1; } &.ci-pending { color: #548; border-color: #548; + background-color: #F4F1FA; } &.ci-running { color: $bg_warning; border-color: $border_warning; + background-color: #FAF5F1; } &.ci-failed { color: $bg_danger; border-color: $border_danger; + background-color: #FAF1F1; } &.ci-error { color: $bg_danger; border-color: $border_danger; + background-color: #FAF1F1; } } diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss index 31c0a0835db..ccd672c5f67 100644 --- a/app/assets/stylesheets/sections/nav.scss +++ b/app/assets/stylesheets/sections/nav.scss @@ -63,7 +63,6 @@ @media (max-width: $screen-xs-max) { font-size: 18px; margin: 0; - max-height: none; &, .container { @@ -86,6 +85,7 @@ color: #fff; font-weight: normal; text-shadow: none; + border: none; &:after { display: none; } } diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 65ad46a4579..e1f9c0cb258 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -36,13 +36,16 @@ ul.notes { font-size: 13px; } .author { - color: #555; + color: #333; font-weight: bold; font-size: 14px; &:hover { - color: $link_hover_color; + color: $link_color; } } + .author-username { + font-size: 14px; + } } .discussion { diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 086875582f3..b9f4e317e9c 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -111,3 +111,20 @@ height: 50px; } } + +//CSS for password-strength indicator +#password-strength { + margin-bottom: 0; +} + +.has-success input { + background-color: #D6F1D7 !important; +} + +.has-error input { + background-color: #F3CECE !important; +} + +.has-warning input { + background-color: #FFE9A4 !important; +} diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index b4ee5ccc8d7..7b894cf00bb 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -270,3 +270,41 @@ ul.nav.nav-projects-tabs { color: #999; } } + +.fork-namespaces { + .thumbnail { + + &.fork-exists-thumbnail { + border-color: #EEE; + + .caption { + color: #999; + } + } + + &.fork-thumbnail { + border-color: #AAA; + + &:hover { + background-color: $hover; + } + } + + a { + text-decoration: none; + } + } +} + +@media (max-width: $screen-xs-max) { + .project-home-panel { + .star-fork-buttons { + padding-top: 10px; + padding-right: 15px; + } + } + + .project-home-links { + display: none; + } +} diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb index 4c1d0df4110..338496013a0 100644 --- a/app/controllers/admin/background_jobs_controller.rb +++ b/app/controllers/admin/background_jobs_controller.rb @@ -1,6 +1,6 @@ class Admin::BackgroundJobsController < Admin::ApplicationController def show - ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) + ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/) end end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 2f0d344802f..7c2388e81be 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -31,17 +31,11 @@ class Admin::ProjectsController < Admin::ApplicationController protected def project - id = params[:project_id] || params[:id] - - @project = Project.find_with_namespace(id) + @project = Project.find_with_namespace(params[:id]) @project || render_404 end def group - @group ||= project.group - end - - def repository - @repository ||= project.repository + @group ||= @project.group end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 13d8d2a3e0a..f1e1bebe5ce 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,9 +5,7 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! before_filter :reject_blocked! before_filter :check_password_expiration - before_filter :add_abilities before_filter :ldap_security_check - before_filter :dev_tools if Rails.env == 'development' before_filter :default_headers before_filter :add_gon_variables before_filter :configure_permitted_parameters, if: :devise_controller? @@ -73,7 +71,7 @@ class ApplicationController < ActionController::Base end def abilities - @abilities ||= Six.new + Ability.abilities end def can?(object, action, subject) @@ -81,28 +79,31 @@ class ApplicationController < ActionController::Base end def project - id = params[:project_id] || params[:id] - - # Redirect from - # localhost/group/project.git - # to - # localhost/group/project - # - if id =~ /\.git\Z/ - redirect_to request.original_url.gsub(/\.git\Z/, '') and return - end + unless @project + id = params[:project_id] || params[:id] + + # Redirect from + # localhost/group/project.git + # to + # localhost/group/project + # + if id =~ /\.git\Z/ + redirect_to request.original_url.gsub(/\.git\Z/, '') and return + end - @project = Project.find_with_namespace(id) + @project = Project.find_with_namespace(id) - if @project and can?(current_user, :read_project, @project) - @project - elsif current_user.nil? - @project = nil - authenticate_user! - else - @project = nil - render_404 and return + if @project and can?(current_user, :read_project, @project) + @project + elsif current_user.nil? + @project = nil + authenticate_user! + else + @project = nil + render_404 and return + end end + @project end def repository @@ -111,22 +112,10 @@ class ApplicationController < ActionController::Base nil end - def add_abilities - abilities << Ability - end - def authorize_project!(action) return access_denied! unless can?(current_user, action, project) end - def authorize_code_access! - return access_denied! unless can?(current_user, :download_code, project) - end - - def authorize_push! - return access_denied! unless can?(current_user, :push_code, project) - end - def authorize_labels! # Labels should be accessible for issues and/or merge requests authorize_read_issue! || authorize_read_merge_request! @@ -170,9 +159,6 @@ class ApplicationController < ActionController::Base response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" end - def dev_tools - end - def default_headers headers['X-Frame-Options'] = 'DENY' headers['X-XSS-Protection'] = '1; mode=block' diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index f8e1a31e0b3..ada7031fea4 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,7 +1,6 @@ class Explore::GroupsController < ApplicationController skip_before_filter :authenticate_user!, - :reject_blocked, :set_current_user_for_observers, - :add_abilities + :reject_blocked, :set_current_user_for_observers layout "explore" diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index b6fa8b7e387..d75fd8e72fa 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,7 +1,6 @@ class Explore::ProjectsController < ApplicationController skip_before_filter :authenticate_user!, - :reject_blocked, - :add_abilities + :reject_blocked layout 'explore' diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 63c05d4f33b..ca88d033878 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -19,6 +19,7 @@ class Groups::GroupMembersController < ApplicationController def destroy @users_group = @group.group_members.find(params[:id]) + if can?(current_user, :destroy, @users_group) # May fail if last owner. @users_group.destroy respond_to do |format| diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index f46b36568f3..3e984e5007a 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -42,25 +42,32 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController def handle_omniauth if current_user - # Change a logged-in user's authentication method: - current_user.extern_uid = oauth['uid'] - current_user.provider = oauth['provider'] - current_user.save + # Add new authentication method + current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider']) redirect_to profile_path else @user = Gitlab::OAuth::User.new(oauth) + @user.save - if Gitlab.config.omniauth['allow_single_sign_on'] && @user.new? - @user.save - end - - if @user.valid? + # Only allow properly saved users to login. + if @user.persisted? && @user.valid? sign_in_and_redirect(@user.gl_user) else - error_message = @user.gl_user.errors.map{ |attribute, message| "#{attribute} #{message}" }.join(", ") + error_message = + if @user.gl_user.errors.any? + @user.gl_user.errors.map do |attribute, message| + "#{attribute} #{message}" + end.join(", ") + else + '' + end + redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return end end + rescue ForbiddenAction => e + flash[:notice] = e.message + redirect_to new_user_session_path end def oauth diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 7e4580017dd..6b7fe06d59f 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -29,4 +29,31 @@ class Projects::ApplicationController < ApplicationController redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch" end end + + def set_filter_variables(collection) + params[:sort] ||= 'newest' + params[:scope] = 'all' if params[:scope].blank? + params[:state] = 'opened' if params[:state].blank? + + @sort = params[:sort].humanize + + assignee_id = params[:assignee_id] + author_id = params[:author_id] + milestone_id = params[:milestone_id] + + if assignee_id.present? && !assignee_id.to_i.zero? + @assignee = @project.team.find(assignee_id) + end + + if author_id.present? && !author_id.to_i.zero? + @author = @project.team.find(assignee_id) + end + + if milestone_id.present? && !milestone_id.to_i.zero? + @milestone = @project.milestones.find(milestone_id) + end + + @assignees = User.where(id: collection.pluck(:assignee_id)) + @authors = User.where(id: collection.pluck(:author_id)) + end end diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb index 5e305934433..a7b1b7b40e8 100644 --- a/app/controllers/projects/base_tree_controller.rb +++ b/app/controllers/projects/base_tree_controller.rb @@ -1,8 +1,7 @@ class Projects::BaseTreeController < Projects::ApplicationController include ExtractsPath - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project end diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index a3c41301676..367d1295f34 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -3,8 +3,7 @@ class Projects::BlameController < Projects::ApplicationController include ExtractsPath # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def show diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 7009e3b1bc8..2412800c493 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -3,10 +3,9 @@ class Projects::BlobController < Projects::ApplicationController include ExtractsPath # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project - before_filter :authorize_push!, only: [:destroy] + before_filter :authorize_push_code!, only: [:destroy] before_filter :blob @@ -20,7 +19,7 @@ class Projects::BlobController < Projects::ApplicationController flash[:notice] = "Your changes have been successfully committed" redirect_to project_tree_path(@project, @ref) else - flash[:alert] = result[:error] + flash[:alert] = result[:message] render :show end end diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index faa0ce67ca8..cff1a907dc2 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -1,10 +1,10 @@ class Projects::BranchesController < Projects::ApplicationController + include ActionView::Helpers::SanitizeHelper # Authorize - before_filter :authorize_read_project! before_filter :require_non_empty_project - before_filter :authorize_code_access! - before_filter :authorize_push!, only: [:create, :destroy] + before_filter :authorize_download_code! + before_filter :authorize_push_code!, only: [:create, :destroy] def index @sort = params[:sort] || 'name' @@ -17,8 +17,11 @@ class Projects::BranchesController < Projects::ApplicationController end def create + branch_name = sanitize(strip_tags(params[:branch_name])) + ref = sanitize(strip_tags(params[:ref])) result = CreateBranchService.new(project, current_user). - execute(params[:branch_name], params[:ref]) + execute(branch_name, ref) + if result[:status] == :success @branch = result[:branch] redirect_to project_tree_path(@project, @branch.name) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 66c67b661db..dac858d8e16 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -3,20 +3,19 @@ # Not to be confused with CommitsController, plural. class Projects::CommitController < Projects::ApplicationController # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project before_filter :commit def show return git_not_found! unless @commit - @line_notes = project.notes.for_commit_id(commit.id).inline - @branches = project.repository.branch_names_contains(commit.id) + @line_notes = @project.notes.for_commit_id(commit.id).inline + @branches = @project.repository.branch_names_contains(commit.id) @diffs = @commit.diffs - @note = project.build_commit_note(commit) - @notes_count = project.notes.for_commit_id(commit.id).count - @notes = project.notes.for_commit_id(@commit.id).not_inline.fresh + @note = @project.build_commit_note(commit) + @notes_count = @project.notes.for_commit_id(commit.id).count + @notes = @project.notes.for_commit_id(@commit.id).not_inline.fresh @noteable = @commit @comments_allowed = @reply_allowed = true @comments_target = { @@ -32,6 +31,6 @@ class Projects::CommitController < Projects::ApplicationController end def commit - @commit ||= project.repository.commit(params[:id]) + @commit ||= @project.repository.commit(params[:id]) end end diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index b7f09eb271d..9476b6c0284 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -4,8 +4,7 @@ class Projects::CommitsController < Projects::ApplicationController include ExtractsPath # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def show diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 7a671e8455d..ffb8c2e4af1 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -1,7 +1,6 @@ class Projects::CompareController < Projects::ApplicationController # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def index diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index d20937ea8ea..024b9520d30 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController end def enable - project.deploy_keys << available_keys.find(params[:id]) + @project.deploy_keys << available_keys.find(params[:id]) redirect_to project_deploy_keys_path(@project) end diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb index 8976d7c7be8..65661c80410 100644 --- a/app/controllers/projects/edit_tree_controller.rb +++ b/app/controllers/projects/edit_tree_controller.rb @@ -1,7 +1,7 @@ class Projects::EditTreeController < Projects::BaseTreeController before_filter :require_branch_head before_filter :blob - before_filter :authorize_push! + before_filter :authorize_push_code! before_filter :from_merge_request before_filter :after_edit_path @@ -22,7 +22,7 @@ class Projects::EditTreeController < Projects::BaseTreeController redirect_to after_edit_path else - flash[:alert] = result[:error] + flash[:alert] = result[:message] render :show end end diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb new file mode 100644 index 00000000000..a0481d11582 --- /dev/null +++ b/app/controllers/projects/forks_controller.rb @@ -0,0 +1,22 @@ +class Projects::ForksController < Projects::ApplicationController + # Authorize + before_filter :authorize_download_code! + before_filter :require_non_empty_project + + def new + @namespaces = current_user.manageable_namespaces + @namespaces.delete(@project.namespace) + end + + def create + namespace = Namespace.find(params[:namespace_id]) + @forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute + + if @forked_project.saved? && @forked_project.forked? + redirect_to(@forked_project, notice: 'Project was successfully forked.') + else + @title = 'Fork project' + render :error + end + end +end diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 610b4967fea..4a318cb7d56 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -1,7 +1,6 @@ class Projects::GraphsController < Projects::ApplicationController # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def show diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index cab8fd76e6c..2d6c3111192 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -26,6 +26,7 @@ class Projects::HooksController < Projects::ApplicationController def test if !@project.empty_repo? status = TestHookService.new.execute(hook, current_user) + if status flash[:notice] = 'Hook successfully executed.' else diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb new file mode 100644 index 00000000000..b8350642804 --- /dev/null +++ b/app/controllers/projects/imports_controller.rb @@ -0,0 +1,49 @@ +class Projects::ImportsController < Projects::ApplicationController + # Authorize + before_filter :authorize_admin_project! + before_filter :require_no_repo + before_filter :redirect_if_progress, except: :show + + def new + end + + def create + @project.import_url = params[:project][:import_url] + + if @project.save + @project.reload + + if @project.import_failed? + @project.import_retry + else + @project.import_start + end + end + + redirect_to project_import_path(@project) + end + + def show + unless @project.import_in_progress? + if @project.import_finished? + redirect_to(@project) and return + else + redirect_to new_project_import_path(@project) and return + end + end + end + + private + + def require_no_repo + if @project.repository_exists? + redirect_to(@project) and return + end + end + + def redirect_if_progress + if @project.import_in_progress? + redirect_to project_import_path(@project) and return + end + end +end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index c6d526f05c5..22235123826 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -18,18 +18,12 @@ class Projects::IssuesController < Projects::ApplicationController def index terms = params['issue_search'] + set_filter_variables(@project.issues) - @issues = issues_filtered + @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id)) @issues = @issues.full_search(terms) if terms.present? @issues = @issues.page(params[:page]).per(20) - assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] - @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? - @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? - sort_param = params[:sort] || 'newest' - @sort = sort_param.humanize unless sort_param.empty? - @assignees = User.where(id: @project.issues.pluck(:assignee_id)).active - respond_to do |format| format.html format.atom { render layout: false } @@ -127,12 +121,6 @@ class Projects::IssuesController < Projects::ApplicationController return render_404 unless @project.issues_enabled end - def issues_filtered - params[:scope] = 'all' if params[:scope].blank? - params[:state] = 'opened' if params[:state].blank? - @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id)) - end - # Since iids are implemented only in 6.1 # user may navigate to issue page using old global ids. # diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 20a733b10e1..4d6f41e9de5 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -17,18 +17,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] def index - params[:sort] ||= 'newest' - params[:scope] = 'all' if params[:scope].blank? - params[:state] = 'opened' if params[:state].blank? + set_filter_variables(@project.merge_requests) @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id)) @merge_requests = @merge_requests.page(params[:page]).per(20) - - @sort = params[:sort].humanize - assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] - @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? - @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? - @assignees = User.where(id: @project.merge_requests.pluck(:assignee_id)) end def show @@ -225,6 +217,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController @allowed_to_merge = allowed_to_merge? @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge @source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name) + + if @merge_request.locked_long_ago? + @merge_request.unlock_mr + @merge_request.close + end end def allowed_to_merge? diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index d338cdedfaf..f362f449e70 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -103,7 +103,9 @@ class Projects::MilestonesController < Projects::ApplicationController end def module_enabled - return render_404 unless @project.issues_enabled + unless @project.issues_enabled || @project.merge_requests_enabled + return render_404 + end end def milestone_params diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index 9832495c64f..ada1aed0df7 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -3,8 +3,7 @@ class Projects::NetworkController < Projects::ApplicationController include ApplicationHelper # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def show diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb index 71a5c6499ec..ffba706b2f6 100644 --- a/app/controllers/projects/new_tree_controller.rb +++ b/app/controllers/projects/new_tree_controller.rb @@ -1,6 +1,6 @@ class Projects::NewTreeController < Projects::BaseTreeController before_filter :require_branch_head - before_filter :authorize_push! + before_filter :authorize_push_code! def show end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 5ec9c576a66..fdbc4c5a098 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -3,8 +3,7 @@ class Projects::RawController < Projects::ApplicationController include ExtractsPath # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def show diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 7997c726fbb..5d9336bdc49 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -2,8 +2,7 @@ class Projects::RefsController < Projects::ApplicationController include ExtractsPath # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! + before_filter :authorize_download_code! before_filter :require_non_empty_project def switch diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 4e0f190ed1c..3a90c1c806d 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -1,8 +1,14 @@ class Projects::RepositoriesController < Projects::ApplicationController # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! - before_filter :require_non_empty_project + before_filter :authorize_download_code! + before_filter :require_non_empty_project, except: :create + before_filter :authorize_admin_project!, only: :create + + def create + @project.create_repository + + redirect_to @project + end def archive unless can?(current_user, :download_code, @project) diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index b50f6286459..c50a1f1e75b 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController params.require(:service).permit( :title, :token, :type, :active, :api_key, :subdomain, :room, :recipients, :project_url, :webhook, - :user_key, :device, :priority, :sound + :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, + :build_key, :server ) end end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 537c94bda20..162ddef0fec 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -1,10 +1,8 @@ class Projects::TagsController < Projects::ApplicationController # Authorize - before_filter :authorize_read_project! before_filter :require_non_empty_project - - before_filter :authorize_code_access! - before_filter :authorize_push!, only: [:create] + before_filter :authorize_download_code! + before_filter :authorize_push_code!, only: [:create] before_filter :authorize_admin_project!, only: [:destroy] def index diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb index 7bb799eba64..0791e6080fb 100644 --- a/app/controllers/projects/team_members_controller.rb +++ b/app/controllers/projects/team_members_controller.rb @@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController end def new - @user_project_relation = project.project_members.new + @user_project_relation = @project.project_members.new end def create @@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController end def update - @user_project_relation = project.project_members.find_by(user_id: member) + @user_project_relation = @project.project_members.find_by(user_id: member) @user_project_relation.update_attributes(member_params) unless @user_project_relation.valid? @@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController end def destroy - @user_project_relation = project.project_members.find_by(user_id: member) + @user_project_relation = @project.project_members.find_by(user_id: member) @user_project_relation.destroy respond_to do |format| @@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController end def leave - project.project_members.find_by(user_id: current_user).destroy + @project.project_members.find_by(user_id: current_user).destroy respond_to do |format| format.html { redirect_to :back } diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index aca091e7d2c..fcff6952d38 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,9 +4,7 @@ class ProjectsController < ApplicationController before_filter :repository, except: [:new, :create] # Authorize - before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] - before_filter :require_non_empty_project, only: [:blob, :tree, :graph] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] layout 'navless', only: [:new, :create, :fork] before_filter :set_title, only: [:new, :create] @@ -21,10 +19,11 @@ class ProjectsController < ApplicationController def create @project = ::Projects::CreateService.new(current_user, project_params).execute - flash[:notice] = 'Project was successfully created.' if @project.saved? - respond_to do |format| - format.js + if @project.saved? + redirect_to project_path(@project), notice: 'Project was successfully created.' + else + render 'new' end end @@ -49,12 +48,10 @@ class ProjectsController < ApplicationController def show if @project.import_in_progress? - redirect_to import_project_path(@project) + redirect_to project_import_path(@project) return end - return authenticate_user! unless @project.public? || current_user - limit = (params[:limit] || 20).to_i @events = @project.events.recent @events = event_filter.apply_filter(@events) @@ -64,41 +61,24 @@ class ProjectsController < ApplicationController respond_to do |format| format.html do - if @project.empty_repo? - render "projects/empty", layout: user_layout + if @project.repository_exists? + if @project.empty_repo? + render "projects/empty", layout: user_layout + else + @last_push = current_user.recent_push(@project.id) if current_user + render :show, layout: user_layout + end else - @last_push = current_user.recent_push(@project.id) if current_user - render :show, layout: user_layout + render "projects/no_repo", layout: user_layout end end - format.json { pager_json("events/_events", @events.count) } - end - end - - def import - if project.import_finished? - redirect_to @project - return - end - end - - def retry_import - unless @project.import_failed? - redirect_to import_project_path(@project) - end - - @project.import_url = project_params[:import_url] - if @project.save - @project.reload - @project.import_retry + format.json { pager_json("events/_events", @events.count) } end - - redirect_to import_project_path(@project) end def destroy - return access_denied! unless can?(current_user, :remove_project, project) + return access_denied! unless can?(current_user, :remove_project, @project) ::Projects::DestroyService.new(@project, current_user, {}).execute @@ -115,22 +95,6 @@ class ProjectsController < ApplicationController end end - def fork - @forked_project = ::Projects::ForkService.new(project, current_user).execute - - respond_to do |format| - format.html do - if @forked_project.saved? && @forked_project.forked? - redirect_to(@forked_project, notice: 'Project was successfully forked.') - else - @title = 'Fork project' - render "fork" - end - end - format.js - end - end - def autocomplete_sources note_type = params['type'] note_id = params['type_id'] @@ -148,8 +112,8 @@ class ProjectsController < ApplicationController end def archive - return access_denied! unless can?(current_user, :archive_project, project) - project.archive! + return access_denied! unless can?(current_user, :archive_project, @project) + @project.archive! respond_to do |format| format.html { redirect_to @project } @@ -157,8 +121,8 @@ class ProjectsController < ApplicationController end def unarchive - return access_denied! unless can?(current_user, :archive_project, project) - project.unarchive! + return access_denied! unless can?(current_user, :archive_project, @project) + @project.unarchive! respond_to do |format| format.html { redirect_to @project } diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 30fb4c5552d..bf3312fedc8 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -9,7 +9,7 @@ class SnippetsController < ApplicationController before_filter :set_title - skip_before_filter :authenticate_user!, only: [:index, :user_index] + skip_before_filter :authenticate_user!, only: [:index, :user_index, :show, :raw] respond_to :html diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0b442f5383a..67af1801bda 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -20,9 +20,14 @@ class UsersController < ApplicationController # Get user activity feed for projects common for both users @events = @user.recent_events. - where(project_id: authorized_projects_ids).limit(20) + where(project_id: authorized_projects_ids).limit(30) @title = @user.name + + respond_to do |format| + format.html + format.atom { render layout: false } + end end def determine_layout diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 56c4f22120d..e1477510065 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -33,6 +33,7 @@ class IssuableFinder items = by_search(items) items = by_milestone(items) items = by_assignee(items) + items = by_author(items) items = by_label(items) items = sort(items) end @@ -48,7 +49,7 @@ class IssuableFinder else [] end - elsif current_user && params[:authorized_only].presence + elsif current_user && params[:authorized_only].presence && !current_user_related? klass.of_projects(current_user.authorized_projects).references(:project) else klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project) @@ -125,6 +126,14 @@ class IssuableFinder items end + def by_author(items) + if params[:author_id].present? + items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id])) + end + + items + end + def by_label(items) if params[:label_name].present? label_names = params[:label_name].split(",") @@ -142,4 +151,8 @@ class IssuableFinder def project Project.where(id: params[:project_id]).first if params[:project_id].present? end + + def current_user_related? + params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' + end end diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index b29ab6cf40b..4b0c69f2d2f 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -29,6 +29,8 @@ class SnippetsFinder def by_user(current_user, user, scope) snippets = user.snippets.fresh.non_expired + return snippets.are_public unless current_user + if user == current_user case scope when 'are_internal' then diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 11fbf1baae7..420ac3f77c7 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,14 +1,9 @@ module BlobHelper def highlightjs_class(blob_name) - if blob_name.include?('.') - ext = blob_name.split('.').last - return 'language-' + ext + if no_highlight_files.include?(blob_name.downcase) + 'no-highlight' else - if no_highlight_files.include?(blob_name.downcase) - 'no-highlight' - else - blob_name.downcase - end + blob_name.downcase end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 0e0532b65b2..36adeadd8a5 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -87,8 +87,8 @@ module CommitsHelper # avatar: true will prepend the avatar image # size: size of the avatar image in px def commit_person_link(commit, options = {}) - source_name = commit.send "#{options[:source]}_name".to_sym - source_email = commit.send "#{options[:source]}_email".to_sym + source_name = clean(commit.send "#{options[:source]}_name".to_sym) + source_email = clean(commit.send "#{options[:source]}_email".to_sym) user = User.find_for_commit(source_email, source_name) person_name = user.nil? ? source_name : user.name @@ -124,4 +124,8 @@ module CommitsHelper def truncate_sha(sha) Commit.truncate_sha(sha) end + + def clean(string) + Sanitize.clean(string, remove_contents: true) + end end diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb new file mode 100644 index 00000000000..24d67c21d6b --- /dev/null +++ b/app/helpers/emails_helper.rb @@ -0,0 +1,32 @@ +module EmailsHelper + + # Google Actions + # https://developers.google.com/gmail/markup/reference/go-to-action + def email_action(url) + name = action_title(url) + if name + data = { + "@context" => "http://schema.org", + "@type" => "EmailMessage", + "action" => { + "@type" => "ViewAction", + "name" => name, + "url" => url, + } + } + + content_tag :script, type: 'application/ld+json' do + data.to_json.html_safe + end + end + end + + def action_title(url) + return unless url + ["merge_requests", "issues", "commit"].each do |action| + if url.split("/").include?(action) + return "View #{action.humanize.singularize}" + end + end + end +end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 71f97fbb8c8..a3136926b38 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -145,4 +145,26 @@ module EventsHelper rescue "--broken encoding" end + + def event_to_atom(xml, event) + if event.proper? + xml.entry do + event_link = event_feed_url(event) + event_title = event_feed_title(event) + event_summary = event_feed_summary(event) + + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" + xml.link href: event_link + xml.title truncate(event_title, length: 80) + xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) + xml.author do |author| + xml.name event.author_name + xml.email event.author_email + end + + xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } + end + end + end end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb new file mode 100644 index 00000000000..09684955233 --- /dev/null +++ b/app/helpers/git_helper.rb @@ -0,0 +1,5 @@ +module GitHelper + def strip_gpg_signature(text) + text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") + end +end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 7d3cb749829..800cacdc2c2 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -254,4 +254,16 @@ module GitlabMarkdownHelper truncated end end + + def cross_project_reference(project, entity) + path = project.path_with_namespace + + if entity.kind_of?(Issue) + [path, entity.iid].join('#') + elsif entity.kind_of?(MergeRequest) + [path, entity.iid].join('!') + else + raise 'Not supported type' + end + end end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 7671033b539..a5b393c1e3c 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -62,6 +62,19 @@ module IssuesHelper '' end + def issue_timestamp(issue) + # Shows the created at time and the updated at time if different + ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}" + if issue.updated_at != issue.created_at + ts << capture_haml do + haml_tag :small do + haml_concat " (Edited #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_edited_ago')})" + end + end + end + ts.html_safe + end + # Checks if issues_tracker setting exists in gitlab.yml def external_issues_tracker_enabled? Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any? @@ -100,4 +113,19 @@ module IssuesHelper 'issue-box-open' end end + + def issue_to_atom(xml, issue) + xml.entry do + xml.id project_issue_url(issue.project, issue) + xml.link href: project_issue_url(issue.project, issue) + xml.title truncate(issue.title, length: 80) + xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) + xml.author do |author| + xml.name issue.author_name + xml.email issue.author_email + end + xml.summary issue.title + end + end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index bf25dce2301..2bcfde62830 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -25,4 +25,12 @@ module NamespacesHelper hidden_field_tag(id, value, class: css_class) end + + def namespace_icon(namespace, size = 40) + if namespace.kind_of?(Group) + group_icon(namespace.path) + else + avatar_icon(namespace.owner.email, size) + end + end end diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb index 7024483b8b3..df18db71c84 100644 --- a/app/helpers/oauth_helper.rb +++ b/app/helpers/oauth_helper.rb @@ -16,4 +16,8 @@ module OauthHelper [:twitter, :github, :google_oauth2].include?(name.to_sym) end end + + def additional_providers + enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')} + end end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb index 0b375558305..6480fd3886f 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -1,6 +1,6 @@ module ProfileHelper def oauth_active_class(provider) - if current_user.provider == provider.to_s + if current_user.identities.exists?(provider: provider.to_s) 'active' end end @@ -10,10 +10,10 @@ module ProfileHelper end def show_profile_social_tab? - enabled_social_providers.any? && !current_user.ldap_user? + enabled_social_providers.any? end def show_profile_remove_tab? - gitlab_config.signup_enabled && !current_user.ldap_user? + gitlab_config.signup_enabled end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 883c1f63af6..fb5470d98e5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -42,12 +42,12 @@ module ProjectsHelper def project_title(project) if project.group content_tag :span do - link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name + link_to(simple_sanitize(project.group.name), group_path(project.group)) + ' / ' + link_to(simple_sanitize(project.name), project_path(project)) end else owner = project.namespace.owner content_tag :span do - link_to(simple_sanitize(owner.name), user_path(owner)) + " / " + project.name + link_to(simple_sanitize(owner.name), user_path(owner)) + ' / ' + link_to(simple_sanitize(project.name), project_path(project)) end end end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb new file mode 100644 index 00000000000..492e065b713 --- /dev/null +++ b/app/helpers/sorting_helper.rb @@ -0,0 +1,17 @@ +module SortingHelper + def sort_title_oldest_updated + 'Oldest updated' + end + + def sort_title_recently_updated + 'Recently updated' + end + + def sort_title_oldest_created + 'Oldest created' + end + + def sort_title_recently_created + 'Recently created' + end +end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 9c611a1c147..8e209498323 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -66,7 +66,7 @@ module TreeHelper def tree_breadcrumbs(tree, max_links = 2) if @path.present? part_path = "" - parts = @path.split("\/") + parts = @path.split('/') yield('..', nil) if parts.count > max_links diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index f8a7d133d1d..6d7f8eb4b02 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -1,8 +1,7 @@ module Emails module Profile - def new_user_email(user_id, password, token = nil) + def new_user_email(user_id, token = nil) @user = User.find(user_id) - @password = password @target_url = user_url(@user) @token = token mail(to: @user.email, subject: subject("Account was created for you")) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index bd438bab89a..6d671e6e0bd 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -11,6 +11,7 @@ class Notify < ActionMailer::Base add_template_helper ApplicationHelper add_template_helper GitlabMarkdownHelper add_template_helper MergeRequestsHelper + add_template_helper EmailsHelper default_url_options[:host] = Gitlab.config.gitlab.host default_url_options[:protocol] = Gitlab.config.gitlab.protocol @@ -25,6 +26,14 @@ class Notify < ActionMailer::Base delay_for(2.seconds) end + def test_email(recepient_email, subject, body) + mail(to: recepient_email, + subject: subject, + body: body.html_safe, + content_type: 'text/html' + ) + end + private # The default email address to send emails from diff --git a/app/models/ability.rb b/app/models/ability.rb index e155abc1449..97a72bf3635 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -262,5 +262,13 @@ class Ability end rules end + + def abilities + @abilities ||= begin + abilities = Six.new + abilities << self + abilities + end + end end end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 553087946d6..f49708fd6eb 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -131,9 +131,10 @@ module Issuable users.concat(mentions.reduce([], :|)).uniq end - def to_hook_data + def to_hook_data(user) { object_kind: self.class.name.underscore, + user: user.hook_attrs, object_attributes: hook_attrs } end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 5938d9cb28e..6c1aa99668a 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -52,11 +52,7 @@ module Mentionable if identifier == "all" users += project.team.members.flatten else - if has_project - id = project.team.members.find_by(username: identifier).try(:id) - else - id = User.find_by(username: identifier).try(:id) - end + id = User.find_by(username: identifier).try(:id) users << User.find(id) unless id.blank? end end diff --git a/app/models/event.rb b/app/models/event.rb index c0b126713a6..65b4c2edfee 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -186,10 +186,6 @@ class Event < ActiveRecord::Base data[:ref]["refs/heads"] end - def new_branch? - commit_from =~ /^00000/ - end - def new_ref? commit_from =~ /^00000/ end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 23fa01e0b70..8479d4aecf6 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -32,7 +32,10 @@ class WebHook < ActiveRecord::Base def execute(data) parsed_url = URI.parse(url) if parsed_url.userinfo.blank? - WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false) + WebHook.post(url, + body: data.to_json, + headers: { "Content-Type" => "application/json" }, + verify: false) else post_url = url.gsub("#{parsed_url.userinfo}@", "") auth = { @@ -45,6 +48,9 @@ class WebHook < ActiveRecord::Base verify: false, basic_auth: auth) end + rescue SocketError, Errno::ECONNREFUSED => e + logger.error("WebHook Error => #{e}") + false end def async_execute(data) diff --git a/app/models/identity.rb b/app/models/identity.rb new file mode 100644 index 00000000000..5fb1850c30b --- /dev/null +++ b/app/models/identity.rb @@ -0,0 +1,5 @@ +class Identity < ActiveRecord::Base + belongs_to :user + + validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} +end
\ No newline at end of file diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 7c525b02f48..2cc427d35c2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -70,6 +70,16 @@ class MergeRequest < ActiveRecord::Base transition locked: :reopened end + after_transition any => :locked do |merge_request, transition| + merge_request.locked_at = Time.now + merge_request.save + end + + after_transition :locked => (any - :locked) do |merge_request, transition| + merge_request.locked_at = nil + merge_request.save + end + state :opened state :reopened state :closed @@ -336,4 +346,8 @@ class MergeRequest < ActiveRecord::Base source_project.repository.branch_names end end + + def locked_long_ago? + locked_at && locked_at < (Time.now - 1.day) + end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index c0c6de0ee7d..ea4b48fdd7f 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -90,4 +90,8 @@ class Namespace < ActiveRecord::Base def kind type == 'Group' ? 'group' : 'user' end + + def find_fork_of(project) + projects.joins(:forked_project_link).where('forked_project_links.forked_from_project_id = ?', project.id).first + end end diff --git a/app/models/note.rb b/app/models/note.rb index 6f1b1a4da94..5bf645bbd1d 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -80,7 +80,7 @@ class Note < ActiveRecord::Base note_options = { project: project, author: author, - note: "_mentioned in #{gfm_reference}_", + note: cross_reference_note_content(gfm_reference), system: true } @@ -90,7 +90,7 @@ class Note < ActiveRecord::Base note_options.merge!(noteable: noteable) end - create(note_options) + create(note_options) unless cross_reference_disallowed?(noteable, mentioner) end def create_milestone_change_note(noteable, project, author, milestone) @@ -165,6 +165,15 @@ class Note < ActiveRecord::Base [:discussion, type.try(:underscore), id, line_code].join("-").to_sym end + # Determine if cross reference note should be created. + # eg. mentioning a commit in MR comments which exists inside a MR + # should not create "mentioned in" note. + def cross_reference_disallowed?(noteable, mentioner) + if mentioner.kind_of?(MergeRequest) + mentioner.commits.map(&:id).include? noteable.id + end + end + # Determine whether or not a cross-reference note already exists. def cross_reference_exists?(noteable, mentioner) gfm_reference = mentioner_gfm_ref(noteable, mentioner) @@ -174,7 +183,7 @@ class Note < ActiveRecord::Base where(noteable_id: noteable.id) end - notes.where('note like ?', "_mentioned in #{gfm_reference}_"). + notes.where('note like ?', cross_reference_note_content(gfm_reference)). system.any? end @@ -182,8 +191,16 @@ class Note < ActiveRecord::Base where("note like :query", query: "%#{query}%") end + def cross_reference_note_prefix + '_mentioned in ' + end + private + def cross_reference_note_content(gfm_reference) + cross_reference_note_prefix + "#{gfm_reference}_" + end + # Prepend the mentioner's namespaced project path to the GFM reference for # cross-project references. For same-project references, return the # unmodified GFM reference. @@ -243,12 +260,16 @@ class Note < ActiveRecord::Base def commit_author @commit_author ||= - project.users.find_by(email: noteable.author_email) || - project.users.find_by(name: noteable.author_name) + project.team.users.find_by(email: noteable.author_email) || + project.team.users.find_by(name: noteable.author_name) rescue nil end + def cross_reference? + note.start_with?(self.class.cross_reference_note_prefix) + end + def find_diff return nil unless noteable && noteable.diffs.present? @@ -296,7 +317,7 @@ class Note < ActiveRecord::Base end def diff_file_index - line_code.split('_')[0] + line_code.split('_')[0] if line_code end def diff_file_name @@ -312,11 +333,11 @@ class Note < ActiveRecord::Base end def diff_old_line - line_code.split('_')[1].to_i + line_code.split('_')[1].to_i if line_code end def diff_new_line - line_code.split('_')[2].to_i + line_code.split('_')[2].to_i if line_code end def generate_line_code(line) @@ -337,6 +358,20 @@ class Note < ActiveRecord::Base @diff_line end + def diff_line_type + return @diff_line_type if @diff_line_type + + if diff + diff_lines.each do |line| + if generate_line_code(line) == self.line_code + @diff_line_type = line.type + end + end + end + + @diff_line_type + end + def truncated_diff_lines max_number_of_lines = 16 prev_match_line = nil diff --git a/app/models/project.rb b/app/models/project.rb index 90d2649ba23..32b0145ca24 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -65,6 +65,7 @@ class Project < ActiveRecord::Base has_one :gemnasium_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildbox_service, dependent: :destroy + has_one :bamboo_service, dependent: :destroy has_one :pushover_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link @@ -135,7 +136,7 @@ class Project < ActiveRecord::Base state_machine :import_status, initial: :none do event :import_start do - transition :none => :started + transition [:none, :finished] => :started end event :import_finish do @@ -173,7 +174,7 @@ class Project < ActiveRecord::Base end def with_push - includes(:events).where('events.action = ?', Event::PUSHED) + joins(:events).where('events.action = ?', Event::PUSHED) end def active @@ -313,7 +314,7 @@ class Project < ActiveRecord::Base end def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox) + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox bamboo) end def gitlab_ci? @@ -389,55 +390,14 @@ class Project < ActiveRecord::Base end def execute_services(data) - services.each do |service| - - # Call service hook only if it is active - begin - service.execute(data) if service.active - rescue => e - logger.error(e) - end + services.select(&:active).each do |service| + service.async_execute(data) end end def update_merge_requests(oldrev, newrev, ref, user) - return true unless ref =~ /heads/ - branch_name = ref.gsub("refs/heads/", "") - commits = self.repository.commits_between(oldrev, newrev) - c_ids = commits.map(&:id) - - # Close merge requests - mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } - - mrs.uniq.each do |merge_request| - MergeRequests::MergeService.new.execute(merge_request, user, nil) - end - - # Update code for merge requests into project between project branches - mrs = self.merge_requests.opened.by_branch(branch_name).to_a - # Update code for merge requests between project and project fork - mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a - - mrs.uniq.each do |merge_request| - merge_request.reload_code - merge_request.mark_as_unchecked - end - - # Add comment about pushing new commits to merge requests - comment_mr_with_commits(branch_name, commits, user) - - true - end - - def comment_mr_with_commits(branch_name, commits, user) - mrs = self.origin_merge_requests.opened.where(source_branch: branch_name).to_a - mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a - - mrs.uniq.each do |merge_request| - Note.create_new_commits_note(merge_request, merge_request.project, - user, commits) - end + MergeRequests::RefreshService.new(self, user). + execute(oldrev, newrev, ref) end def valid_repo? @@ -620,4 +580,25 @@ class Project < ActiveRecord::Base def origin_merge_requests merge_requests.where(source_project_id: self.id) end + + def create_repository + if gitlab_shell.add_repository(path_with_namespace) + true + else + errors.add(:base, "Failed to create repository") + false + end + end + + def repository_exists? + !!repository.exists? + end + + def create_wiki + ProjectWiki.new(self, self.owner).wiki + true + rescue ProjectWiki::CouldNotCreateWikiError => ex + errors.add(:base, "Failed create wiki") + false + end end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb new file mode 100644 index 00000000000..b9eec9ab21e --- /dev/null +++ b/app/models/project_services/bamboo_service.rb @@ -0,0 +1,105 @@ +class BambooService < CiService + include HTTParty + + prop_accessor :bamboo_url, :build_key, :username, :password + + validates :bamboo_url, presence: true, + format: { with: URI::regexp }, if: :activated? + validates :build_key, presence: true, if: :activated? + validates :username, presence: true, + if: ->(service) { service.password? }, if: :activated? + validates :password, presence: true, + if: ->(service) { service.username? }, if: :activated? + + attr_accessor :response + + after_save :compose_service_hook, if: :activated? + + def compose_service_hook + hook = service_hook || build_service_hook + hook.save + end + + def title + 'Atlassian Bamboo CI' + end + + def description + 'A continuous integration and build server' + end + + def help + 'You must set up automatic revision labeling and a repository trigger in Bamboo.' + end + + def to_param + 'bamboo' + end + + def fields + [ + { type: 'text', name: 'bamboo_url', + placeholder: 'Bamboo root URL like https://bamboo.example.com' }, + { type: 'text', name: 'build_key', + placeholder: 'Bamboo build plan key like KEY' }, + { type: 'text', name: 'username', + placeholder: 'A user with API access, if applicable' }, + { type: 'password', name: 'password' }, + ] + end + + def build_info(sha) + url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") + + if username.blank? && password.blank? + @response = HTTParty.get(parsed_url.to_s, verify: false) + else + get_url = "#{url}&os_authType=basic" + auth = { + username: username, + password: password, + } + @response = HTTParty.get(get_url, verify: false, basic_auth: auth) + end + end + + def build_page(sha) + build_info(sha) if @response.nil? || !@response.code + + if @response.code != 200 || @response['results']['results']['size'] == '0' + # If actual build link can't be determined, send user to build summary page. + "#{bamboo_url}/browse/#{build_key}" + else + # If actual build link is available, go to build result page. + result_key = @response['results']['results']['result']['planResultKey']['key'] + "#{bamboo_url}/browse/#{result_key}" + end + end + + def commit_status(sha) + build_info(sha) if @response.nil? || !@response.code + return :error unless @response.code == 200 || @response.code == 404 + + status = if @response.code == 404 || @response['results']['results']['size'] == '0' + 'Pending' + else + @response['results']['results']['result']['buildState'] + end + + if status.include?('Success') + 'success' + elsif status.include?('Failed') + 'failed' + elsif status.include?('Pending') + 'pending' + else + :error + end + end + + def execute(_data) + # Bamboo requires a GET and does not take any data. + self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}", + verify: false) + end +end diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb index b0f8e28c97f..0ab67b79fe4 100644 --- a/app/models/project_services/buildbox_service.rb +++ b/app/models/project_services/buildbox_service.rb @@ -12,6 +12,8 @@ # properties :text # +require "addressable/uri" + class BuildboxService < CiService prop_accessor :project_url, :token diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 0020b4482e5..86705f5dabd 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -37,13 +37,12 @@ class FlowdockService < Service end def execute(push_data) - repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git") Flowdock::Git.post( push_data[:ref], push_data[:before], push_data[:after], token: token, - repo: repo_path, + repo: project.repository.path_to_repo, repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s", diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s", diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index 6d2fc06a5d0..18fdd204ecd 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -38,14 +38,13 @@ class GemnasiumService < Service end def execute(push_data) - repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git") Gemnasium::GitlabService.execute( ref: push_data[:ref], before: push_data[:before], after: push_data[:after], token: token, api_key: api_key, - repo: repo_path + repo: project.repository.path_to_repo ) end end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index a897c4ab76b..fadebf968bc 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -28,7 +28,7 @@ class GitlabCiService < CiService end def commit_status_path(sha) - project_url + "/builds/#{sha}/status.json?token=#{token}" + project_url + "/commits/#{sha}/status.json?token=#{token}" end def get_ci_build(sha) @@ -55,7 +55,7 @@ class GitlabCiService < CiService end def build_page(sha) - project_url + "/builds/#{sha}" + project_url + "/commits/#{sha}" end def builds_path diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 4078938cdbb..a848d74044c 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -15,11 +15,11 @@ class HipchatService < Service MAX_COMMITS = 3 - prop_accessor :token, :room + prop_accessor :token, :room, :server validates :token, presence: true, if: :activated? def title - 'Hipchat' + 'HipChat' end def description @@ -33,7 +33,9 @@ class HipchatService < Service def fields [ { type: 'text', name: 'token', placeholder: '' }, - { type: 'text', name: 'room', placeholder: '' } + { type: 'text', name: 'room', placeholder: '' }, + { type: 'text', name: 'server', + placeholder: 'Leave blank for default. https://chat.hipchat.com' } ] end @@ -44,7 +46,9 @@ class HipchatService < Service private def gate - @gate ||= HipChat::Client.new(token) + options = { api_version: 'v2' } + options[:server_url] = server unless server.nil? + @gate ||= HipChat::Client.new(token, options) end def create_message(push) diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 95f3ddcef45..963f5440b6f 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -30,23 +30,20 @@ class SlackService < Service def fields [ - { type: 'text', name: 'webhook', placeholder: '' } + { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' } ] end def execute(push_data) + return unless webhook.present? + message = SlackMessage.new(push_data.merge( project_url: project_url, project_name: project_name )) - credentials = webhook.match(/(\w*).slack.com.*services\/(.*)/) - if credentials.present? - subdomain = credentials[1] - token = credentials[2].split("token=").last - notifier = Slack::Notifier.new(subdomain, token) - notifier.ping(message.pretext, attachments: message.attachments) - end + notifier = Slack::Notifier.new(webhook) + notifier.ping(message.pretext, attachments: message.attachments) end private diff --git a/app/models/service.rb b/app/models/service.rb index c489c1e96e1..71c8aa39e45 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -82,4 +82,8 @@ class Service < ActiveRecord::Base } end end + + def async_execute(data) + Sidekiq::Client.enqueue(ProjectServiceWorker, id, data) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 42faea0070e..7faeef1b5b0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -79,6 +79,7 @@ class User < ActiveRecord::Base # Profile has_many :keys, dependent: :destroy has_many :emails, dependent: :destroy + has_many :identities, dependent: :destroy # Groups has_many :members, dependent: :destroy @@ -113,7 +114,6 @@ class User < ActiveRecord::Base validates :name, presence: true validates :email, presence: true, email: {strict_mode: true}, uniqueness: true validates :bio, length: { maximum: 255 }, allow_blank: true - validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} validates :username, presence: true, uniqueness: { case_sensitive: false }, exclusion: { in: Gitlab::Blacklist.path }, @@ -124,7 +124,7 @@ class User < ActiveRecord::Base validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :avatar_type, if: ->(user) { user.avatar_changed? } validate :unique_email, if: ->(user) { user.email_changed? } - validates :avatar, file_size: { maximum: 100.kilobytes.to_i } + validates :avatar, file_size: { maximum: 200.kilobytes.to_i } before_validation :generate_password, on: :create before_validation :sanitize_attrs @@ -178,7 +178,6 @@ class User < ActiveRecord::Base scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } - scope :ldap, -> { where('provider LIKE ?', 'ldap%') } scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } # @@ -226,6 +225,11 @@ class User < ActiveRecord::Base where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%") end + def by_login(login) + where('lower(username) = :value OR lower(email) = :value', + value: login.to_s.downcase).first + end + def by_username_or_id(name_or_id) where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first end @@ -330,11 +334,7 @@ class User < ActiveRecord::Base end def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end + Ability.abilities end def can_select_namespace? @@ -406,7 +406,11 @@ class User < ActiveRecord::Base end def ldap_user? - extern_uid && provider.start_with?('ldap') + identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) + end + + def ldap_identity + @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"]) end def accessible_deploy_keys @@ -497,6 +501,14 @@ class User < ActiveRecord::Base end end + def hook_attrs + { + name: name, + username: username, + avatar_url: avatar_url + } + end + def ensure_namespace_correct # Ensure user has namespace self.create_namespace!(path: self.username, name: self.username) unless self.namespace @@ -542,4 +554,14 @@ class User < ActiveRecord::Base UsersStarProject.create!(project: project, user: self) end end + + def manageable_namespaces + @manageable_namespaces ||= + begin + namespaces = [] + namespaces << namespace + namespaces += owned_groups + namespaces += masters_groups + end + end end diff --git a/app/services/base_service.rb b/app/services/base_service.rb index ed286c04095..0d46eeaa18f 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -6,11 +6,7 @@ class BaseService end def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end + Ability.abilities end def can?(object, action, subject) diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index db6f0831f8b..bd245100955 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -10,12 +10,6 @@ module Files private - def success - out = super() - out[:error] = '' - out - end - def repository project.repository end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 8f2b0e347f6..529af1970f6 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -83,9 +83,14 @@ class GitPushService # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to # a different branch. issues_to_close = commit.closes_issues(project) - author = commit_user(commit) - if !issues_to_close.empty? && is_default_branch + # Load commit author only if needed. + # For push with 1k commits it prevents 900+ requests in database + author = nil + + if issues_to_close.present? && is_default_branch + author ||= commit_user(commit) + issues_to_close.each do |issue| Issues::CloseService.new(project, author, {}).execute(issue, commit) end @@ -96,8 +101,13 @@ class GitPushService # being pushed to a different branch). refs = commit.references(project) - issues_to_close refs.reject! { |r| commit.has_mentioned?(r) } - refs.each do |r| - Note.create_cross_reference_note(r, commit, author, project) + + if refs.present? + author ||= commit_user(commit) + + refs.each do |r| + Note.create_cross_reference_note(r, commit, author, project) + end end end end @@ -160,19 +170,19 @@ class GitPushService ref_parts = ref.split('/') # Return if this is not a push to a branch (e.g. new commits) - ref_parts[1] =~ /heads/ && oldrev != "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && oldrev != Gitlab::Git::BLANK_SHA end def push_to_new_branch?(ref, oldrev) ref_parts = ref.split('/') - ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && oldrev == Gitlab::Git::BLANK_SHA end def push_remove_branch?(ref, newrev) ref_parts = ref.split('/') - ref_parts[1] =~ /heads/ && newrev == "0000000000000000000000000000000000000000" + ref_parts[1] =~ /heads/ && newrev == Gitlab::Git::BLANK_SHA end def push_to_branch?(ref) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 41948f226a6..755c0ef45a8 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -4,7 +4,7 @@ module Issues private def execute_hooks(issue, action = 'open') - issue_data = issue.to_hook_data + issue_data = issue.to_hook_data(current_user) issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id) issue_data[:object_attributes].merge!(url: issue_url, action: action) issue.project.execute_hooks(issue_data, :issue_hooks) diff --git a/app/services/merge_requests/base_merge_service.rb b/app/services/merge_requests/base_merge_service.rb index 9bc50d3d16c..700a21ca011 100644 --- a/app/services/merge_requests/base_merge_service.rb +++ b/app/services/merge_requests/base_merge_service.rb @@ -13,7 +13,8 @@ module MergeRequests def execute_project_hooks(merge_request) if merge_request.project - merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) + hook_data = merge_request.to_hook_data(current_user) + merge_request.project.execute_hooks(hook_data, :merge_request_hooks) end end end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 694994001b0..7f3421b8e4b 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -7,7 +7,8 @@ module MergeRequests def execute_hooks(merge_request) if merge_request.project - merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) + hook_data = merge_request.to_hook_data(current_user) + merge_request.project.execute_hooks(hook_data, :merge_request_hooks) end end end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb new file mode 100644 index 00000000000..baf0936cc3d --- /dev/null +++ b/app/services/merge_requests/refresh_service.rb @@ -0,0 +1,86 @@ +module MergeRequests + class RefreshService < MergeRequests::BaseService + def execute(oldrev, newrev, ref) + return true unless ref =~ /heads/ + + @oldrev, @newrev = oldrev, newrev + @branch_name = ref.gsub("refs/heads/", "") + @fork_merge_requests = @project.fork_merge_requests.opened + @commits = @project.repository.commits_between(oldrev, newrev) + + close_merge_requests + reload_merge_requests + comment_mr_with_commits + + true + end + + private + + # Collect open merge requests that target same branch we push into + # and close if push to master include last commit from merge request + # We need this to close(as merged) merge requests that were merged into + # target branch manually + def close_merge_requests + commit_ids = @commits.map(&:id) + merge_requests = @project.merge_requests.opened.where(target_branch: @branch_name).to_a + merge_requests = merge_requests.select(&:last_commit) + + merge_requests = merge_requests.select do |merge_request| + commit_ids.include?(merge_request.last_commit.id) + end + + + merge_requests.uniq.select(&:source_project).each do |merge_request| + MergeRequests::MergeService.new.execute(merge_request, @current_user, nil) + end + end + + def force_push? + Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev) + end + + # Refresh merge request diff if we push to source or target branch of merge request + # Note: we should update merge requests from forks too + def reload_merge_requests + merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a + merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a + merge_requests = filter_merge_requests(merge_requests) + + merge_requests.each do |merge_request| + + if merge_request.source_branch == @branch_name || force_push? + merge_request.reload_code + merge_request.mark_as_unchecked + else + mr_commit_ids = merge_request.commits.map(&:id) + push_commit_ids = @commits.map(&:id) + matches = mr_commit_ids & push_commit_ids + + if matches.any? + merge_request.reload_code + merge_request.mark_as_unchecked + else + merge_request.mark_as_unchecked + end + end + end + end + + # Add comment about pushing new commits to merge requests + def comment_mr_with_commits + merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a + merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a + merge_requests = filter_merge_requests(merge_requests) + + merge_requests.each do |merge_request| + Note.create_new_commits_note(merge_request, merge_request.project, + @current_user, @commits) + end + end + + def filter_merge_requests(merge_requests) + merge_requests.uniq.select(&:source_project) + end + end +end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index fe39f83b400..d1aadd741e1 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -107,7 +107,7 @@ class NotificationService # Notify new user with email after creation def new_user(user, token = nil) # Don't email omniauth created users - mailer.new_user_email(user.id, user.password, token) unless user.extern_uid? + mailer.new_user_email(user.id, token) unless user.identities.any? end # Notify users on new note in system @@ -119,11 +119,12 @@ class NotificationService # ignore gitlab service messages return true if note.note =~ /\A_Status changed to closed_/ - return true if note.note =~ /\A_mentioned in / && note.system == true + return true if note.cross_reference? && note.system == true opts = { noteable_type: note.noteable_type, project_id: note.project_id } target = note.noteable + if target.respond_to?(:participants) recipients = target.participants else diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 12386792aab..3672b623806 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -37,35 +37,22 @@ module Projects @project.creator = current_user - if @project.save - log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") - system_hook_service.execute_hooks_for(@project, :create) + Project.transaction do + @project.save - unless @project.group - @project.team << [current_user, :master] - end - - @project.update_column(:last_activity_at, @project.created_at) - - if @project.import? - @project.import_start - else - GitlabShellWorker.perform_async( - :add_repository, - @project.path_with_namespace - ) + unless @project.import? + unless @project.create_repository + raise 'Failed to create repository' + end end + end + if @project.persisted? if @project.wiki_enabled? - begin - # force the creation of a wiki, - ProjectWiki.new(@project, @project.owner).wiki - rescue ProjectWiki::CouldNotCreateWikiError => ex - # Prevent project observer crash - # if failed to create wiki - nil - end + @project.create_wiki end + + after_create_actions end @project @@ -84,5 +71,20 @@ module Projects namespace = Namespace.find_by(id: namespace_id) current_user.can?(:create_projects, namespace) end + + def after_create_actions + log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") + system_hook_service.execute_hooks_for(@project, :create) + + unless @project.group + @project.team << [current_user, :master] + end + + @project.update_column(:last_activity_at, @project.created_at) + + if @project.import? + @project.import_start + end + end end end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index a59311bf942..4930660055a 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -2,11 +2,9 @@ module Projects class ForkService < BaseService include Gitlab::ShellAdapter - def initialize(project, user) - @from_project, @current_user = project, user - end - def execute + @from_project = @project + project_params = { visibility_level: @from_project.visibility_level, description: @from_project.description, @@ -15,8 +13,18 @@ module Projects project = Project.new(project_params) project.name = @from_project.name project.path = @from_project.path - project.namespace = current_user.namespace - project.creator = current_user + project.creator = @current_user + + if namespace = @params[:namespace] + project.namespace = namespace + else + project.namespace = @current_user.namespace + end + + unless @current_user.can?(:create_projects, project.namespace) + project.errors.add(:namespace, 'insufficient access rights') + return project + end # If the project cannot save, we do not want to trigger the project destroy # as this can have the side effect of deleting a repo attached to an existing @@ -27,7 +35,7 @@ module Projects #First save the DB entries as they can be rolled back if the repo fork fails project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id) if project.save - project.team << [current_user, :master] + project.team << [@current_user, :master] end #Now fork the repo unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path) @@ -42,8 +50,8 @@ module Projects else project.errors.add(:base, "Invalid fork destination") end - project + project end end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index a6b68749a71..44e494525b3 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -18,7 +18,7 @@ class SystemHooksService def build_event_data(model, event) data = { event_name: build_event_name(model, event), - created_at: model.created_at + created_at: model.created_at.xmlschema } case model diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb index b6b1ef29b51..17d86a7a274 100644 --- a/app/services/test_hook_service.rb +++ b/app/services/test_hook_service.rb @@ -2,8 +2,5 @@ class TestHookService def execute(hook, current_user) data = GitPushService.new.sample_data(hook.project, current_user) hook.execute(data) - true - rescue SocketError - false end end diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index 9dcf7b488ee..8db2b2a709c 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -25,7 +25,7 @@ - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) - data = process.strip.split(' ') %tr - %td= Settings.gitlab.user + %td= gitlab_config.user - 5.times do %td= data.shift %td= data.join(' ') @@ -36,7 +36,7 @@ If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'. %p %i.fa.fa-exclamation-circle - If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{Settings.gitlab.user} -f sidekiq) and restart GitLab. + If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab. diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index c56863ce274..f4d7e25fd74 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -2,39 +2,20 @@ - if @group.errors.any? .alert.alert-danger %span= @group.errors.full_messages.first - .form-group.group_name_holder - = f.label :name, class: 'control-label' do - Group name - .col-sm-10 - = f.text_field :name, placeholder: "Example Group", class: "form-control" - .form-group.group-description-holder - = f.label :description, "Details", class: 'control-label' - .col-sm-10 - = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4 + = render 'shared/group_form', f: f .form-group.group-description-holder = f.label :avatar, "Group avatar", class: 'control-label' .col-sm-10 - %a.choose-btn.btn.btn-small.js-choose-group-avatar-button - %i.fa.fa-paperclip - %span Choose File ... - - %span.file_name.js-avatar-filename File name... - = f.file_field :avatar, class: "js-group-avatar-input hidden" - .light The maximum file size allowed is 100KB. + = render 'shared/choose_group_avatar_button', f: f - if @group.new_record? .form-group .col-sm-2 .col-sm-10 .bs-callout.bs-callout-info - %ul - %li A group is a collection of several projects - %li Groups are private by default - %li Members of a group may only view projects they have permission to access - %li Group project URLs are prefixed with the group namespace - %li Existing projects may be moved into a group + = render 'shared/group_tips' .form-actions = f.submit 'Create group', class: "btn btn-create" = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 09105679bd2..1d7fef43184 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -10,7 +10,7 @@ = form_tag admin_groups_path, method: :get, class: 'form-inline' do .form-group = text_field_tag :name, params[:name], class: "form-control input-mn-300" - = submit_tag "Search", class: "btn submit btn-primary" + = button_tag "Search", class: "btn submit btn-primary" %hr diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index c1a9214b77a..8057de38805 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -64,7 +64,7 @@ %div.prepend-top-10 = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr - = submit_tag 'Add users into group', class: "btn btn-create" + = button_tag 'Add users into group', class: "btn btn-create" .panel.panel-default .panel-heading %h3.panel-title @@ -74,13 +74,13 @@ %ul.well-list.group-users-list - @members.each do |member| - user = member.user - %li{class: dom_class(user)} + %li{class: dom_class(member), id: dom_id(user)} .list-item-name %strong = link_to user.name, admin_user_path(user) %span.pull-right.light = member.human_access - = link_to group_group_members_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do + = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do %i.fa.fa-minus.fa-inverse .panel-footer = paginate @members, param_name: 'members_page', theme: 'gitlab' diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index b3f8f012f00..384c6ee9af5 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,68 +1,25 @@ +- loggers = [Gitlab::GitLogger, Gitlab::AppLogger, + Gitlab::ProductionLogger, Gitlab::SidekiqLogger] %ul.nav.nav-tabs.log-tabs - %li.active - = link_to "githost.log", "#githost", 'data-toggle' => 'tab' - %li - = link_to "application.log", "#application", 'data-toggle' => 'tab' - %li - = link_to "production.log", "#production", 'data-toggle' => 'tab' - %li - = link_to "sidekiq.log", "#sidekiq", 'data-toggle' => 'tab' - + - loggers.each do |klass| + %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } + = link_to klass::file_name, "##{klass::file_name_noext}", + 'data-toggle' => 'tab' %p.light To prevent performance issues admin logs output the last 2000 lines .tab-content - .tab-pane.active#githost - .file-holder#README - .file-title - %i.fa.fa-file - githost.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.fa.fa-arrow-down - Scroll down - .file-content.logs - %ol - - Gitlab::GitLogger.read_latest.each do |line| - %li - %p= line - .tab-pane#application - .file-holder#README - .file-title - %i.fa.fa-file - application.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.fa.fa-arrow-down - Scroll down - .file-content.logs - %ol - - Gitlab::AppLogger.read_latest.each do |line| - %li - %p= line - .tab-pane#production - .file-holder#README - .file-title - %i.fa.fa-file - production.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.fa.fa-arrow-down - Scroll down - .file-content.logs - %ol - - Gitlab::Logger.read_latest_for('production.log').each do |line| - %li - %p= line - .tab-pane#sidekiq - .file-holder#README - .file-title - %i.fa.fa-file - sidekiq.log - .pull-right - = link_to '#', class: 'log-bottom' do - %i.fa.fa-arrow-down - Scroll down - .file-content.logs - %ol - - Gitlab::Logger.read_latest_for('sidekiq.log').each do |line| - %li - %p= line + - loggers.each do |klass| + .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''), + id: klass::file_name_noext } + .file-holder#README + .file-title + %i.fa.fa-file + = klass::file_name + .pull-right + = link_to '#', class: 'log-bottom' do + %i.fa.fa-arrow-down + Scroll down + .file-content.logs + %ol + - klass.read_latest.each do |line| + %li + %p= line diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 5ca6090f8d3..aa59f38d213 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -35,7 +35,7 @@ = label %hr = hidden_field_tag :sort, params[:sort] - = submit_tag "Search", class: "btn submit btn-primary" + = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_projects_path, class: "btn btn-cancel" .col-md-9 @@ -56,13 +56,13 @@ = link_to admin_projects_path(sort: nil) do Name = link_to admin_projects_path(sort: 'newest') do - Newest + = sort_title_recently_created = link_to admin_projects_path(sort: 'oldest') do - Oldest + = sort_title_oldest_created = link_to admin_projects_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to admin_projects_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated = link_to admin_projects_path(sort: 'largest_repository') do Largest repository = link_to 'New Project', new_project_path, class: "btn btn-new" diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 92c619738a2..8e1ecb41a85 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -49,9 +49,10 @@ = link_to admin_users_path(sort: 'oldest_sign_in') do Oldest sign in = link_to admin_users_path(sort: 'recently_created') do - Recently created + = sort_title_recently_created = link_to admin_users_path(sort: 'late_created') do - Late created + = sort_title_oldest_created + = link_to 'New User', new_admin_user_path, class: "btn btn-new" %ul.well-list - @users.each do |user| diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 211d77d5185..29717aedd80 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -95,7 +95,7 @@ %li %span.light LDAP uid: %strong - = @user.extern_uid + = @user.ldap_identity.extern_uid - if @user.created_by %li diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index 711e607f0bc..5d133cd8285 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -46,5 +46,5 @@ %br Public projects are an easy way to allow everyone to have read-only access. .link_holder - = link_to explore_projects_path, class: "btn btn-new" do + = link_to trending_explore_projects_path, class: "btn btn-new" do Browse public projects » diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index f5413557783..66381310221 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,24 +1,13 @@ xml.instruct! -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do xml.title "#{current_user.name} issues" - xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml" - xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html" - xml.id issues_dashboard_url(:private_token => current_user.private_token) + xml.link href: issues_dashboard_url(:atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: issues_dashboard_url(private_token: current_user.private_token), rel: "alternate", type: "text/html" + xml.id issues_dashboard_url(private_token: current_user.private_token) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| - xml.entry do - xml.id project_issue_url(issue.project, issue) - xml.link :href => project_issue_url(issue.project, issue) - xml.title truncate(issue.title, :length => 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end + issue_to_atom(xml, issue) end end diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml index f124c688be1..5b7835b097b 100644 --- a/app/views/dashboard/projects.html.haml +++ b/app/views/dashboard/projects.html.haml @@ -14,13 +14,14 @@ = link_to projects_dashboard_filter_path(sort: nil) do Name = link_to projects_dashboard_filter_path(sort: 'newest') do - Newest + = sort_title_recently_created = link_to projects_dashboard_filter_path(sort: 'oldest') do - Oldest + = sort_title_oldest_created = link_to projects_dashboard_filter_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to projects_dashboard_filter_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated + %p.light All projects you have access to are listed here. Public projects are not included here unless you are a member %hr diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder index f4cf24ccd99..70ac66f8016 100644 --- a/app/views/dashboard/show.atom.builder +++ b/app/views/dashboard/show.atom.builder @@ -1,29 +1,12 @@ xml.instruct! -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}" - xml.link :href => dashboard_url(:atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => dashboard_url, :rel => "alternate", :type => "text/html" + xml.link href: dashboard_url(:atom), rel: "self", type: "application/atom+xml" + xml.link href: dashboard_url, rel: "alternate", type: "text/html" xml.id projects_url xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| - if event.proper? - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - event_summary = event_feed_summary(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link :href => event_link - xml.title truncate(event_title, :length => 80) - xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? } - end - end + event_to_atom(xml, event) end end diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 1326cc0aac9..f6cbf9b82ba 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -6,8 +6,8 @@ .devise-errors = devise_error_messages! = f.hidden_field :reset_password_token - %div - = f.password_field :password, class: "form-control top", placeholder: "New password", required: true + .form-group#password-strength + = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true .clearfix.append-bottom-10 diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index d6a952f3dc5..123de881f59 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -11,8 +11,8 @@ = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true %div = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true - %div - = f.password_field :password, class: "form-control middle", placeholder: "Password", required: true + .form-group#password-strength + = f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true %div = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true %div diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 01584611493..bf8a593c254 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -2,4 +2,4 @@ = text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"} %br/ - = submit_tag "LDAP Sign in", class: "btn-save btn" + = button_tag "LDAP Sign in", class: "btn-save btn" diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml index 15048a78063..8d6aaefb9ff 100644 --- a/app/views/devise/sessions/_oauth_providers.html.haml +++ b/app/views/devise/sessions/_oauth_providers.html.haml @@ -1,4 +1,4 @@ -- providers = (enabled_oauth_providers - [:ldap]) +- providers = additional_providers - if providers.present? .bs-callout.bs-callout-info{:'data-no-turbolink' => 'data-no-turbolink'} %span Sign in with: diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index b9832787446..ca7e9570b43 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -2,22 +2,22 @@ .login-heading %h3 Sign in .login-body - - if ldap_enabled? && gitlab_config.signin_enabled + - if ldap_enabled? %ul.nav.nav-tabs - @ldap_servers.each_with_index do |server, i| - %li{class: (:active if i==0)} + %li{class: (:active if i.zero?)} = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab' - %li - = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' + - if gitlab_config.signin_enabled + %li + = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' .tab-content - - @ldap_servers.each_with_index do |server,i| - %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i==0)} + - @ldap_servers.each_with_index do |server, i| + %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero?)} = render 'devise/sessions/new_ldap', provider: server['provider_name'] - %div#tab-signin.tab-pane - = render 'devise/sessions/new_base' + - if gitlab_config.signin_enabled + %div#tab-signin.tab-pane + = render 'devise/sessions/new_base' - - elsif ldap_enabled? - = render 'devise/sessions/new_ldap', ldap_servers: @ldap_servers - elsif gitlab_config.signin_enabled = render 'devise/sessions/new_base' - else diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index c8243ff782c..9b1d7d0416d 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -4,7 +4,7 @@ .form-group = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search" .form-group - = submit_tag 'Search', class: "btn btn-primary wide" + = button_tag 'Search', class: "btn btn-primary wide" .pull-right .dropdown.inline @@ -20,13 +20,13 @@ = link_to explore_groups_path(sort: nil) do Name = link_to explore_groups_path(sort: 'newest') do - Newest + = sort_title_recently_created = link_to explore_groups_path(sort: 'oldest') do - Oldest + = sort_title_oldest_created = link_to explore_groups_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to explore_groups_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated %hr diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index c8bf78385e8..02586077d8c 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -4,7 +4,7 @@ .form-group = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" .form-group - = submit_tag 'Search', class: "btn btn-primary wide" + = button_tag 'Search', class: "btn btn-primary wide" .pull-right .dropdown.inline @@ -20,13 +20,13 @@ = link_to explore_projects_path(sort: nil) do Name = link_to explore_projects_path(sort: 'newest') do - Newest + = sort_title_recently_created = link_to explore_projects_path(sort: 'oldest') do - Oldest + = sort_title_oldest_created = link_to explore_projects_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to explore_projects_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated %hr .public-projects diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 0b15affe785..eb24fd65d9e 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -11,16 +11,7 @@ - if @group.errors.any? .alert.alert-danger %span= @group.errors.full_messages.first - .form-group - = f.label :name, class: 'control-label' do - Group name - .col-sm-10 - = f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left" - - .form-group.group-description-holder - = f.label :description, "Details", class: 'control-label' - .col-sm-10 - = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4 + = render 'shared/group_form', f: f .form-group .col-sm-2 @@ -31,13 +22,7 @@ You can change your group avatar here - else You can upload a group avatar here - %a.choose-btn.btn.btn-small.js-choose-group-avatar-button - %i.fa.fa-paperclip - %span Choose File ... - - %span.file_name.js-avatar-filename File name... - = f.file_field :avatar, class: "js-group-avatar-input hidden" - .light The maximum file size allowed is 100KB. + = render 'shared/choose_group_avatar_button', f: f - if @group.avatar? %hr = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index f2005193f83..240001967f3 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -7,18 +7,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| - xml.entry do - xml.id project_issue_url(issue.project, issue) - xml.link :href => project_issue_url(issue.project, issue) - xml.title truncate(issue.title, :length => 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end + issue_to_atom(xml, issue) end end diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml index ba554cd5ef1..d2ebcdab7e1 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -13,7 +13,7 @@ = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' } - = submit_tag 'Search', class: 'btn' + = button_tag 'Search', class: 'btn' - if current_user && current_user.can?(:manage_group, @group) .pull-right diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 235e299343a..6e17cdaef6f 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -2,37 +2,18 @@ - if @group.errors.any? .alert.alert-danger %span= @group.errors.full_messages.first - .form-group - = f.label :name, class: 'control-label' do - Group name - .col-sm-10 - = f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control", tabindex: 1, autofocus: true - .form-group.group-description-holder - = f.label :description, "Details", class: 'control-label' - .col-sm-10 - = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4, tabindex: 2 + = render 'shared/group_form', f: f, autofocus: true .form-group.group-description-holder = f.label :avatar, "Group avatar", class: 'control-label' .col-sm-10 - %a.choose-btn.btn.btn-small.js-choose-group-avatar-button - %i.fa.fa-paperclip - %span Choose File ... - - %span.file_name.js-avatar-filename File name... - = f.file_field :avatar, class: "js-group-avatar-input hidden" - .light The maximum file size allowed is 100KB. + = render 'shared/choose_group_avatar_button', f: f .form-group .col-sm-2 .col-sm-10 - %ul - %li A group is a collection of several projects - %li Groups are private by default - %li Members of a group may only view projects they have permission to access - %li Group project URLs are prefixed with the group namespace - %li Existing projects may be moved into a group + = render 'shared/group_tips' .form-actions = f.submit 'Create group', class: "btn btn-create", tabindex: 3 diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index e07bb7d2fb7..e765ea8338d 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -1,28 +1,12 @@ xml.instruct! -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do xml.title "Group feed - #{@group.name}" - xml.link :href => group_path(@group, :atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => group_path(@group), :rel => "alternate", :type => "text/html" + xml.link href: group_path(@group, :atom), rel: "self", type: "application/atom+xml" + xml.link href: group_path(@group), rel: "alternate", type: "text/html" xml.id projects_url xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| - if event.proper? - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link :href => event_link - xml.title truncate(event_title, :length => 80) - xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - xml.summary event_title - end - end + event_to_atom(xml, event) end end diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 5ab82122ad7..04f79846858 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -4,11 +4,20 @@ = hidden_field_tag :group_id, @group.try(:id) - if @project && @project.persisted? = hidden_field_tag :project_id, @project.id - = hidden_field_tag :search_code, true + + - if current_controller?(:issues) + = hidden_field_tag :scope, 'issues' + - elsif current_controller?(:merge_requests) + = hidden_field_tag :scope, 'merge_requests' + - elsif current_controller?(:wikis) + = hidden_field_tag :scope, 'wiki_blobs' + - else + = hidden_field_tag :search_code, true + - if @snippet || @snippets = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref - = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' + = button_tag 'Go' if ENV['RAILS_ENV'] == 'test' .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } :javascript diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index ab421d63f1a..da451961327 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -28,3 +28,4 @@ You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, project_url(@project)} project team. - if @target_url #{link_to "View it on GitLab", @target_url} + = email_action @target_url diff --git a/app/views/notify/new_ssh_key_email.html.haml b/app/views/notify/new_ssh_key_email.html.haml index deb0822d8f2..63b0cbbd205 100644 --- a/app/views/notify/new_ssh_key_email.html.haml +++ b/app/views/notify/new_ssh_key_email.html.haml @@ -6,5 +6,5 @@ title: %code= @key.title %p - If this key was added in error, you can remove it here: + If this key was added in error, you can remove it under = link_to "SSH Keys", profile_keys_url diff --git a/app/views/notify/new_ssh_key_email.text.erb b/app/views/notify/new_ssh_key_email.text.erb index 5f0080c2b76..05b551c89a0 100644 --- a/app/views/notify/new_ssh_key_email.text.erb +++ b/app/views/notify/new_ssh_key_email.text.erb @@ -2,6 +2,6 @@ Hi <%= @user.name %>! A new public key was added to your account: -title.................. <%= @key.title %> +Title: <%= @key.title %> -If this key was added in error, you can remove it here: <%= profile_keys_url %> +If this key was added in error, you can remove it at <%= profile_keys_url %> diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 2a7d317aa3e..425200ff523 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -24,7 +24,7 @@ .form-group = f.label :password, 'New password', class: 'control-label' .col-sm-10 - = f.password_field :password, required: true, class: 'form-control' + = f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .form-group = f.label :password_confirmation, class: 'control-label' .col-sm-10 diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index aef7348fd20..42d2d0db29c 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -16,7 +16,7 @@ .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' .form-group = f.label :password, class: 'control-label' - .col-sm-10= f.password_field :password, required: true, class: 'form-control' + .col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .form-group = f.label :password_confirmation, class: 'control-label' .col-sm-10 diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index d6b52f86154..640104fdad1 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -83,7 +83,7 @@ %span.file_name.js-avatar-filename File name... = f.file_field :avatar, class: "js-user-avatar-input hidden" - .light The maximum file size allowed is 100KB. + .light The maximum file size allowed is 200KB. - if @user.avatar? %hr = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 672a91e0eef..30d063c7a36 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -16,11 +16,11 @@ - unless @project.empty_repo? .fork-buttons - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace - - if current_user.already_forked?(@project) + - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 = link_to project_path(current_user.fork_of(@project)), title: 'Go to my fork' do = link_to_toggle_fork - else - = link_to fork_project_path(@project), title: "Fork project", method: "POST" do + = link_to new_project_fork_path(@project), title: "Fork project" do = link_to_toggle_fork .star-buttons @@ -31,7 +31,7 @@ - else = link_to_toggle_star('You must sign in to star a project.', false, false) - .project-home-row + .project-home-row.hidden-xs - if current_user && !empty_repo .project-home-dropdown = render "dropdown" diff --git a/app/views/projects/_issuable_filter.html.haml b/app/views/projects/_issuable_filter.html.haml new file mode 100644 index 00000000000..b3e5efd938f --- /dev/null +++ b/app/views/projects/_issuable_filter.html.haml @@ -0,0 +1,72 @@ +.issues-filters + .dropdown.inline + %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(assignee_id: nil) do + Any + = link_to project_filter_path(assignee_id: 0) do + Unassigned + - @assignees.sort_by(&:name).each do |user| + %li + = link_to project_filter_path(assignee_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name + + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(author_id: nil) do + Any + = link_to project_filter_path(author_id: 0) do + Unassigned + - @authors.sort_by(&:name).each do |user| + %li + = link_to project_filter_path(author_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name + + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(milestone_id: nil) do + Any + = link_to project_filter_path(milestone_id: 0) do + None (backlog) + - project_active_milestones.each do |milestone| + %li + = link_to project_filter_path(milestone_id: milestone.id) do + %strong= milestone.title + %small.light= milestone.expires_at + + .pull-right + = render 'shared/sort_dropdown' diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml index 675b73a59cb..b02f52a5aff 100644 --- a/app/views/projects/_issuable_form.html.haml +++ b/app/views/projects/_issuable_form.html.haml @@ -52,7 +52,7 @@ - else %span.light No open milestones available. - = link_to 'Create new milestone', new_project_milestone_path(issuable.project) + = link_to 'Create new milestone', new_project_milestone_path(issuable.project), target: :blank .form-group = f.label :label_ids, class: 'control-label' do %i.icon-tag @@ -64,7 +64,7 @@ - else %span.light No labels yet. - = link_to 'Create new label', new_project_label_path(issuable.project) + = link_to 'Create new label', new_project_label_path(issuable.project), target: :blank .form-actions - if issuable.new_record? diff --git a/app/views/projects/_issues_nav.html.haml b/app/views/projects/_issues_nav.html.haml new file mode 100644 index 00000000000..18628eb6207 --- /dev/null +++ b/app/views/projects/_issues_nav.html.haml @@ -0,0 +1,55 @@ +%ul.nav.nav-tabs + - if project_nav_tab? :issues + = nav_link(controller: :issues) do + = link_to project_issues_path(@project), class: "tab" do + Issues + - if project_nav_tab? :merge_requests + = nav_link(controller: :merge_requests) do + = link_to project_merge_requests_path(@project), class: "tab" do + Merge Requests + = nav_link(controller: :milestones) do + = link_to 'Milestones', project_milestones_path(@project), class: "tab" + = nav_link(controller: :labels) do + = link_to 'Labels', project_labels_path(@project), class: "tab" + + - if current_controller?(:milestones) + %li.pull-right + %button.btn.btn-default.sidebar-expand-button + %i.icon.fa.fa-list + + - if current_controller?(:issues) + - if current_user + %li.hidden-xs + = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do + %i.fa.fa-rss + + %li.pull-right + .pull-right + %button.btn.btn-default.sidebar-expand-button + %i.icon.fa.fa-list + + .pull-left + = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do + .append-right-10.hidden-xs.hidden-sm + = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } + = hidden_field_tag :state, params['state'] + = hidden_field_tag :scope, params['scope'] + = hidden_field_tag :assignee_id, params['assignee_id'] + = hidden_field_tag :milestone_id, params['milestone_id'] + = hidden_field_tag :label_id, params['label_id'] + + - if can? current_user, :write_issue, @project + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do + %i.fa.fa-plus + New Issue + + - if current_controller?(:merge_requests) + %li.pull-right + .pull-right + %button.btn.btn-default.sidebar-expand-button + %i.icon.fa.fa-list + + - if can? current_user, :write_merge_request, @project + = link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do + %i.fa.fa-plus + New Merge Request diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 64c19a57803..812d88a8730 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -23,5 +23,6 @@ tree_join(@commit.sha, @path)), class: 'btn btn-small' - if allowed_tree_edit? - = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do + = button_tag class: 'remove-blob btn btn-small btn-remove', + 'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do Remove diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index da84dc4b6cf..c5568315cb1 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -15,7 +15,7 @@ .form-group .col-sm-2 .col-sm-10 - = submit_tag 'Remove file', class: 'btn btn-remove btn-remove-file' + = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 9f2b1b59292..d2aefd815a1 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -20,9 +20,9 @@ = link_to project_branches_path(sort: nil) do Name = link_to project_branches_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to project_branches_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated %hr - unless @branches.empty? %ul.bordered-list.top-list.all-branches diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index f5a530d95f2..a6623240da1 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -15,7 +15,7 @@ .col-sm-10 = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' .form-actions - = submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3 + = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' :javascript diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index da6157cf1b6..cb0a3747f7d 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -12,7 +12,7 @@ %span.input-group-addon to = text_field_tag :to, params[:to], class: "form-control" - = submit_tag "Compare", class: "btn btn-create commits-compare-btn" + = button_tag "Compare", class: "btn btn-create commits-compare-btn" - if compare_to_mr_button? = link_to compare_mr_path, class: 'prepend-left-10 btn' do %strong Make a merge request diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml deleted file mode 100644 index 89710d3a09a..00000000000 --- a/app/views/projects/create.js.haml +++ /dev/null @@ -1,13 +0,0 @@ -- if @project.saved? - - if @project.import? - :plain - location.href = "#{import_project_path(@project)}"; - - else - :plain - location.href = "#{project_path(@project)}"; -- else - :plain - $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); - $('.project-submit').enable(); - $('.save-project-loader').hide(); - $('.project-edit-container').show(); diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index c415ae2ddc8..bf7770ceedf 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -10,7 +10,10 @@ - if @commit.parent_ids.present? = view_file_btn(@commit.parent_id, diff_file, project) - else - %span= diff_file.new_path + - if diff_file.renamed_file + %span= "#{diff_file.old_path} renamed to #{diff_file.new_path}" + - else + %span= diff_file.new_path - if diff_file.mode_changed? %span.file-mode= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index f48f4bb2953..b85cf7d8d37 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -13,7 +13,7 @@ = f.label :name, class: 'control-label' do Project name .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control" + = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit" .form-group @@ -124,6 +124,12 @@ .errors-holder .panel-body = form_for(@project, html: { class: 'form-horizontal' }) do |f| + .form-group.project_name_holder + = f.label :name, class: 'control-label' do + Project name + .col-sm-9 + .form-group + = f.text_field :name, placeholder: "Example Project", class: "form-control" .form-group = f.label :path, class: 'control-label' do %span Path diff --git a/app/views/projects/fork.html.haml b/app/views/projects/fork.html.haml deleted file mode 100644 index d8f5c7b98d6..00000000000 --- a/app/views/projects/fork.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -.alert.alert-danger.alert-block - %h4 - %i.fa.fa-code-fork - Fork Error! - %p - You tried to fork - = link_to_project @project - but it failed for the following reason: - - - - if @forked_project && @forked_project.errors.any? - %p - – - = @forked_project.errors.full_messages.first - - %p - = link_to fork_project_path(@project), title: "Fork", class: "btn", method: "POST" do - %i.fa.fa-code-fork - Try to Fork again diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml new file mode 100644 index 00000000000..76d3aa5bf00 --- /dev/null +++ b/app/views/projects/forks/error.html.haml @@ -0,0 +1,20 @@ +- if @forked_project && !@forked_project.saved? + .alert.alert-danger.alert-block + %h4 + %i.fa.fa-code-fork + Fork Error! + %p + You tried to fork + = link_to_project @project + but it failed for the following reason: + + + - if @forked_project && @forked_project.errors.any? + %p + – + = @forked_project.errors.full_messages.first + + %p + = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do + %i.fa.fa-code-fork + Try to Fork again diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml new file mode 100644 index 00000000000..54f2cef023b --- /dev/null +++ b/app/views/projects/forks/new.html.haml @@ -0,0 +1,38 @@ +%h3.page-title Fork project +%p.lead Select namespace where to fork this project +%hr + +.fork-namespaces + - @namespaces.in_groups_of(6, false) do |group| + .row + - group.each do |namespace| + .col-md-2.col-sm-3 + - if fork = namespace.find_fork_of(@project) + .thumbnail.fork-exists-thumbnail + = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do + = image_tag namespace_icon(namespace, 200) + .caption + %h4=namespace.human_name + %p + = namespace.path + - else + .thumbnail.fork-thumbnail + = link_to project_fork_path(@project, namespace_id: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do + = image_tag namespace_icon(namespace, 200) + .caption + %h4=namespace.human_name + %p + = namespace.path + + %p.light + Fork is a copy of a project repository. + %br + Forking a repository allows you to do changes without affecting the original project. + +.save-project-loader.hide + .center + %h2 + %i.fa.fa-spinner.fa-spin + Forking repository + %p Please wait a moment, this page will automatically refresh when ready. + diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml deleted file mode 100644 index 4513c89e784..00000000000 --- a/app/views/projects/import.html.haml +++ /dev/null @@ -1,31 +0,0 @@ -- if @project.import_in_progress? - .save-project-loader - .center - %h2 - %i.fa.fa-spinner.fa-spin - Import in progress. - %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} - %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); - -- elsif @project.import_failed? - .save-project-loader - .center - %h2 - Import failed. Retry? - %hr - - if can?(current_user, :admin_project, @project) - = form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f| - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing git repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .bs-callout.bs-callout-info - 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. - For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"} - .form-actions - = f.submit 'Retry import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml new file mode 100644 index 00000000000..6c3083e49f5 --- /dev/null +++ b/app/views/projects/imports/new.html.haml @@ -0,0 +1,21 @@ +%h3.page-title + - if @project.import_failed? + Import failed. Retry? + - else + Import repository + +%hr + += form_for @project, url: project_import_path(@project), method: :post, html: { class: 'form-horizontal' } do |f| + .form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Import existing git repo + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' + .bs-callout.bs-callout-info + 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. + For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"} + .form-actions + = f.submit 'Start import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml new file mode 100644 index 00000000000..2d1fdafed24 --- /dev/null +++ b/app/views/projects/imports/show.html.haml @@ -0,0 +1,9 @@ +.save-project-loader + .center + %h2 + %i.fa.fa-spinner.fa-spin + Import in progress. + %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} + %p Please wait while we import the repository for you. Refresh at will. + :javascript + new ProjectImport(); diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml deleted file mode 100644 index 1d2f3ed8118..00000000000 --- a/app/views/projects/issues/_head.html.haml +++ /dev/null @@ -1,36 +0,0 @@ -%ul.nav.nav-tabs - = nav_link(controller: :issues) do - = link_to project_issues_path(@project), class: "tab" do - Browse Issues - = nav_link(controller: :milestones) do - = link_to 'Milestones', project_milestones_path(@project), class: "tab" - = nav_link(controller: :labels) do - = link_to 'Labels', project_labels_path(@project), class: "tab" - - - if current_controller?(:milestones) - %li.pull-right - %button.btn.btn-default.sidebar-expand-button - %i.icon.fa.fa-list - - - if current_controller?(:issues) - - if current_user - %li - = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do - %i.fa.fa-rss - - %li.pull-right - .pull-right - %button.btn.btn-default.sidebar-expand-button - %i.icon.fa.fa-list - = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do - .append-right-10.hidden-xs.hidden-sm - = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } - = hidden_field_tag :state, params['state'] - = hidden_field_tag :scope, params['scope'] - = hidden_field_tag :assignee_id, params['assignee_id'] - = hidden_field_tag :milestone_id, params['milestone_id'] - = hidden_field_tag :label_id, params['label_id'] - - if can? current_user, :write_issue, @project - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do - %i.fa.fa-plus - New Issue diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index b125706781c..dc6510be858 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -4,7 +4,6 @@ = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) .issue-title - %span.light= "##{issue.iid}" %span.str-truncated = link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title" - if issue.closed? @@ -12,10 +11,9 @@ CLOSED .issue-info + %span.light= "##{issue.iid}" - if issue.assignee assigned to #{link_to_member(@project, issue.assignee)} - - else - unassigned - if issue.votes_count > 0 = render 'votes/votes_inline', votable: issue - if issue.notes.any? @@ -30,7 +28,7 @@ %span.task-status = issue.task_status - .pull-right + .pull-right.issue-updated-at %small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')} .issue-labels diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index 8c3f0823386..648f459dc9e 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -19,6 +19,7 @@ = hidden_field_tag :issue_context = f.submit class: 'btn' - elsif issue.milestone - = link_to issue.milestone.title, project_milestone_path + = link_to project_milestone_path(@project, @issue.milestone) do + = @issue.milestone.title - else None diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 0bff8bdbead..15c84c7ced2 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,55 +1,7 @@ .append-bottom-10 .check-all-holder = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" - .issues-filters - .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(assignee_id: nil) do - Any - = link_to project_filter_path(assignee_id: 0) do - Unassigned - - @assignees.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(milestone_id: nil) do - Any - = link_to project_filter_path(milestone_id: 0) do - None (backlog) - - project_active_milestones.each do |milestone| - %li - = link_to project_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at - - .pull-right - = render 'shared/sort_dropdown' + = render 'projects/issuable_filter' .clearfix .issues_bulk_update.hide diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 012ba235951..61e651da932 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -7,17 +7,6 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| - xml.entry do - xml.id project_issue_url(@project, issue) - xml.link :href => project_issue_url(@project, issue) - xml.title truncate(issue.title, :length => 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end + issue_to_atom(xml, issue) end end diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 4ec362b3063..8db6241f21f 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,4 +1,4 @@ -= render "head" += render "projects/issues_nav" .row .fixed.fixed.sidebar-expand-button.hidden-lg.hidden-md.hidden-xs %i.fa.fa-list.fa-2x diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 71eb0d5c866..01a1fabda26 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -38,8 +38,12 @@ - else Open + .cross-project-ref + %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @issue) + .creator - Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)} + Created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} %h4.title = gfm escape_once(@issue.title) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 06568278de8..c7c17c7797e 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,4 +1,4 @@ -= render "projects/issues/head" += render "projects/issues_nav" - if can? current_user, :admin_label, @project = link_to new_project_label_path(@project), class: "pull-right btn btn-new" do diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 1ee2e1bdae8..dedb060a231 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,13 +1,12 @@ %li{ class: mr_css_classes(merge_request) } .merge-request-title - %span.light= "##{merge_request.iid}" = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" - if merge_request.merged? %small.pull-right %i.fa.fa-check MERGED - else - %span.pull-right + %span.pull-right.hidden-xs - if merge_request.for_fork? %span.light #{merge_request.source_project_namespace}: @@ -15,6 +14,7 @@ %i.fa.fa-angle-right.light = merge_request.target_branch .merge-request-info + %span.light= "##{merge_request.iid}" - if merge_request.author authored by #{link_to_member(merge_request.source_project, merge_request.author)} - if merge_request.votes_count > 0 @@ -31,7 +31,7 @@ %span.task-status = merge_request.task_status - .pull-right + .pull-right.hidden-xs %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')} .merge-request-labels diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index be638d7cac1..b93e0f9da3e 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,67 +1,12 @@ -- if can? current_user, :write_merge_request, @project - = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-new", title: "New Merge Request" do - %i.fa.fa-plus - New Merge Request -%h3.page-title - Merge Requests -%hr += render "projects/issues_nav" + .row - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x .col-md-3.responsive-side = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project), labels: true, redirect: 'merge_requests', entity: 'merge_request' .col-md-9 - .mr-filters.append-bottom-10 - .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(assignee_id: nil) do - Any - = link_to project_filter_path(assignee_id: 0) do - Unassigned - - @assignees.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn{href: '#', "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 project_filter_path(milestone_id: nil) do - Any - = link_to project_filter_path(milestone_id: 0) do - None (backlog) - - project_active_milestones.each do |milestone| - %li - = link_to project_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at - - .pull-right - = render 'shared/sort_dropdown' - + .append-bottom-10 + = render 'projects/issuable_filter' .panel.panel-default %ul.well-list.mr-list = render @merge_requests diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index 9540453ce3e..63db4b30968 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -10,11 +10,11 @@ - target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path %p %strong Step 1. - Checkout the branch we are going to merge and pull in the code + Fetch the code and create a new branch pointing to it %pre.dark :preserve - git checkout -b #{@merge_request.source_project_path}-#{@merge_request.source_branch} #{@merge_request.target_branch} - git pull #{@merge_request.source_project.http_url_to_repo} #{@merge_request.source_branch} + git fetch #{@merge_request.source_project.http_url_to_repo} #{@merge_request.source_branch} + git checkout -b #{@merge_request.source_project_path}-#{@merge_request.source_branch} FETCH_HEAD %p %strong Step 2. Merge the branch and push the changes to GitLab diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 7e5a4eda508..866b236d827 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -8,6 +8,10 @@ - else Open + .cross-project-ref + %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @merge_request) + .creator Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} 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 87dad6140be..a4f2a890969 100644 --- a/app/views/projects/merge_requests/show/_state_widget.html.haml +++ b/app/views/projects/merge_requests/show/_state_widget.html.haml @@ -11,14 +11,18 @@ - if @merge_request.closed? %h4 - Closed by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)} - #{time_ago_with_tooltip(@merge_request.closed_event.created_at)} + Closed + - if @merge_request.closed_event + by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)} + #{time_ago_with_tooltip(@merge_request.closed_event.created_at)} %p Changes were not merged into target branch - if @merge_request.merged? %h4 - Merged by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)} - #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} + Merged + - if @merge_request.merge_event + by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)} + #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} = render "projects/merge_requests/show/remove_source_branch" - if @merge_request.locked? @@ -44,4 +48,3 @@ Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'} = succeed '.' do != gfm(issues_sentence(@closes_issues)) - diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index 03367b7cdbf..0db0b114d63 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,4 +1,4 @@ -= render "projects/issues/head" += render "projects/issues_nav" .milestones_content %h3.page-title Milestones diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 8263f7530a2..f08ccc1d570 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,4 +1,4 @@ -= render "projects/issues/head" += render "projects/issues_nav" %h3.page-title Milestone ##{@milestone.iid} .pull-right diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index f5cd0f21e01..e77ef84f51c 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -3,7 +3,7 @@ = render 'projects/errors' .project-edit-content - = form_for @project, remote: true, html: { class: 'new_project form-horizontal' } do |f| + = form_for @project, html: { class: 'new_project form-horizontal' } do |f| .form-group.project-name-holder = f.label :name, class: 'control-label' do %strong Project name diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml index c47c0a3f642..f09d3659774 100644 --- a/app/views/projects/new_tree/show.html.haml +++ b/app/views/projects/new_tree/show.html.haml @@ -19,14 +19,14 @@ Encoding .col-sm-10 = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control' - = render 'shared/commit_message_container', params: params, - placeholder: 'Add new file' .file-holder .file-title %i.fa.fa-file .file-content.code %pre#editor= params[:content] + = render 'shared/commit_message_container', params: params, + placeholder: 'Add new file' = hidden_field_tag 'content', '', id: 'file-content' = render 'projects/commit_button', ref: @ref, cancel_path: project_tree_path(@project, @id) diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml new file mode 100644 index 00000000000..dd576243510 --- /dev/null +++ b/app/views/projects/no_repo.html.haml @@ -0,0 +1,22 @@ +%h2 + %i.fa.fa-warning + No repository + +%p.slead + The repository for this project does not exist. + %br + This means you can not push code until you create an empty repository or import existing one. +%hr + +.no-repo-actions + = link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do + Create empty bare repository + + %strong.prepend-left-10.append-right-10 or + + = link_to new_project_import_path(@project), class: 'btn' do + Import repository + +- if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 05946162d3b..47ffe1fd2f3 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -19,7 +19,7 @@ = yield(:note_actions) %a.btn.grouped.js-close-discussion-note-form Cancel - .note-form-option + .note-form-option.hidden-xs %a.choose-btn.btn.js-choose-note-attachment-button %i.fa.fa-paperclip %span Choose File ... diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index aa52ff35d0c..354afd3e2c9 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -18,6 +18,8 @@ %i.fa.fa-trash-o.cred Remove = link_to_member(@project, note.author, avatar: false) + %span.author-username + = '@' + note.author.username %span.note-last-update = note_timestamp(note) @@ -60,7 +62,7 @@ - if note.attachment.image? = link_to note.attachment.secure_url, target: '_blank' do = image_tag note.attachment.secure_url, class: 'note-image-attach' - .attachment.pull-right + .attachment = link_to note.attachment.secure_url, target: "_blank" do %i.fa.fa-paperclip = note.attachment_identifier diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 16d59d1fe9d..1151f22c7e8 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -28,7 +28,7 @@ - @service.fields.each do |field| - name = field[:name] - - value = @service.send(name) + - value = @service.send(name) unless field[:type] == 'password' - type = field[:type] - placeholder = field[:placeholder] - choices = field[:choices] @@ -45,6 +45,8 @@ = f.check_box name - elsif type == 'select' = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } + - elsif type == 'password' + = f.password_field name, class: 'form-control' .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index bce105a033b..4ab102ba96c 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -4,6 +4,9 @@ = link_to project_commits_path(@project, tag.name), class: "" do %i.fa.fa-tag = tag.name + - if tag.message.present? + + = strip_gpg_signature(tag.message) .pull-right - if can? current_user, :download_code, @project = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-small' diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index aa08b397763..ad7ff8d3db8 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -21,7 +21,7 @@ = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' .light (Optional) Entering a message will create an annotated tag. .form-actions - = submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3 + = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' :javascript diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml index 5f29b58de32..7a9c0939ba0 100644 --- a/app/views/projects/team_members/_team_member.html.haml +++ b/app/views/projects/team_members/_team_member.html.haml @@ -5,7 +5,7 @@ - unless @project.personal? && user == current_user .pull-left = form_for(member, as: :project_member, url: project_team_member_path(@project, member.user)) do |f| - = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "medium project-access-select span2 trigger-submit" + = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "trigger-submit" = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do %i.fa.fa-minus.fa-inverse diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/team_members/import.html.haml index 510b579fe2f..d1f46c61b2e 100644 --- a/app/views/projects/team_members/import.html.haml +++ b/app/views/projects/team_members/import.html.haml @@ -9,6 +9,6 @@ .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions - = submit_tag 'Import project members', class: "btn btn-create" + = button_tag 'Import project members', class: "btn btn-create" = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" diff --git a/app/views/search/_project_filter.html.haml b/app/views/search/_project_filter.html.haml index c201b3d6c47..ad933502a28 100644 --- a/app/views/search/_project_filter.html.haml +++ b/app/views/search/_project_filter.html.haml @@ -25,6 +25,7 @@ = @search_results.notes_count %li{class: ("active" if @scope == 'wiki_blobs')} = link_to search_filter_path(scope: 'wiki_blobs') do + %i.fa.fa-book Wiki .pull-right = @search_results.wiki_blobs_count diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index bae57917a4c..5b4816e4c40 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -6,7 +6,7 @@ .col-sm-6 = search_field_tag :search, params[:search], placeholder: "issue 143", class: "form-control search-text-input", id: "dashboard_search" .col-sm-4 - = submit_tag 'Search', class: "btn btn-create" + = button_tag 'Search', class: "btn btn-create" .form-group .col-sm-2 - unless params[:snippets].eql? 'true' diff --git a/app/views/shared/_choose_group_avatar_button.html.haml b/app/views/shared/_choose_group_avatar_button.html.haml new file mode 100644 index 00000000000..299c0bd42a2 --- /dev/null +++ b/app/views/shared/_choose_group_avatar_button.html.haml @@ -0,0 +1,7 @@ +%a.choose-btn.btn.btn-small.js-choose-group-avatar-button + %i.fa.fa-paperclip + %span Choose File ... + +%span.file_name.js-avatar-filename File name... += f.file_field :avatar, class: 'js-group-avatar-input hidden' +.light The maximum file size allowed is 200KB. diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml new file mode 100644 index 00000000000..93294e42505 --- /dev/null +++ b/app/views/shared/_group_form.html.haml @@ -0,0 +1,12 @@ +.form-group + = f.label :name, class: 'control-label' do + Group name + .col-sm-10 + = f.text_field :name, placeholder: 'Example Group', class: 'form-control', + autofocus: local_assigns[:autofocus] || false + +.form-group.group-description-holder + = f.label :description, 'Details', class: 'control-label' + .col-sm-10 + = f.text_area :description, maxlength: 250, + class: 'form-control js-gfm-input', rows: 4 diff --git a/app/views/shared/_group_tips.html.haml b/app/views/shared/_group_tips.html.haml new file mode 100644 index 00000000000..e5cf783beb7 --- /dev/null +++ b/app/views/shared/_group_tips.html.haml @@ -0,0 +1,6 @@ +%ul + %li A group is a collection of several projects + %li Groups are private by default + %li Members of a group may only view projects they have permission to access + %li Group project URLs are prefixed with the group namespace + %li Existing projects may be moved into a group diff --git a/app/views/shared/_promo.html.haml b/app/views/shared/_promo.html.haml index 3400c345c4c..3596aabe309 100644 --- a/app/views/shared/_promo.html.haml +++ b/app/views/shared/_promo.html.haml @@ -1,5 +1,5 @@ .gitlab-promo = link_to 'Homepage', promo_url = link_to "Blog", promo_url + '/blog/' - = link_to "@gitlabhq", "https://twitter.com/gitlabhq" + = link_to "@gitlab", "https://twitter.com/gitlab" = link_to "Requests", "http://feedback.gitlab.com/" diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index 7b37b39780e..54f59245690 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -9,13 +9,13 @@ %ul.dropdown-menu %li = link_to project_filter_path(sort: 'newest') do - Newest + = sort_title_recently_created = link_to project_filter_path(sort: 'oldest') do - Oldest + = sort_title_oldest_created = link_to project_filter_path(sort: 'recently_updated') do - Recently updated + = sort_title_recently_updated = link_to project_filter_path(sort: 'last_updated') do - Last updated + = sort_title_oldest_updated = link_to project_filter_path(sort: 'milestone_due_soon') do Milestone due soon = link_to project_filter_path(sort: 'milestone_due_later') do diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml index 09b2985d498..ea008c2dede 100644 --- a/app/views/users/_groups.html.haml +++ b/app/views/users/_groups.html.haml @@ -1,3 +1,3 @@ - groups.each do |group| = link_to group, class: 'profile-groups-avatars', :title => group.name do - = image_tag group_icon(group.path) + - image_tag group_icon(group.path) diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder new file mode 100644 index 00000000000..b7216a88765 --- /dev/null +++ b/app/views/users/show.atom.builder @@ -0,0 +1,12 @@ +xml.instruct! +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do + xml.title "Activity feed for #{@user.name}" + xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml" + xml.link href: user_url(@user), rel: "alternate", type: "text/html" + xml.id projects_url + xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + + @events.each do |event| + event_to_atom(xml, event) + end +end diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index cb49c030af2..54f2666ce5d 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -18,7 +18,15 @@ %h4 Groups: = render 'groups', groups: @groups %hr - %h4 User Activity: + %h4 + User Activity: + + - if current_user + %span.rss-icon.pull-right + = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do + %strong + %i.fa.fa-rss + = render @events .col-md-4 = render 'profile', user: @user diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 2947c8e3ecd..e3f6f3a6aef 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -21,5 +21,8 @@ class EmailsOnPushWorker recipients.split(" ").each do |recipient| Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver end + ensure + compare = nil + GC.start end end diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb new file mode 100644 index 00000000000..cc0a7f25664 --- /dev/null +++ b/app/workers/project_service_worker.rb @@ -0,0 +1,9 @@ +class ProjectServiceWorker + include Sidekiq::Worker + + sidekiq_options queue: :project_web_hook + + def perform(hook_id, data) + Service.find(hook_id).execute(data) + end +end |