diff options
Diffstat (limited to 'app')
102 files changed, 840 insertions, 486 deletions
diff --git a/app/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png Binary files differnew file mode 100644 index 00000000000..c56eef05eb9 --- /dev/null +++ b/app/assets/images/authbuttons/github_32.png diff --git a/app/assets/images/authbuttons/github_64.png b/app/assets/images/authbuttons/github_64.png Binary files differnew file mode 100644 index 00000000000..39de55bc796 --- /dev/null +++ b/app/assets/images/authbuttons/github_64.png diff --git a/app/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png Binary files differnew file mode 100644 index 00000000000..6225cc9c2d7 --- /dev/null +++ b/app/assets/images/authbuttons/google_32.png diff --git a/app/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png Binary files differnew file mode 100644 index 00000000000..4d608f71008 --- /dev/null +++ b/app/assets/images/authbuttons/google_64.png diff --git a/app/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png Binary files differnew file mode 100644 index 00000000000..696eb02484d --- /dev/null +++ b/app/assets/images/authbuttons/twitter_32.png diff --git a/app/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png Binary files differnew file mode 100644 index 00000000000..2893274766f --- /dev/null +++ b/app/assets/images/authbuttons/twitter_64.png diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png Binary files differnew file mode 100644 index 00000000000..d9066ad7d7b --- /dev/null +++ b/app/assets/images/bg_fallback.png diff --git a/app/assets/images/icon_sprite.png b/app/assets/images/icon_sprite.png Binary files differnew file mode 100644 index 00000000000..9ad65fc443b --- /dev/null +++ b/app/assets/images/icon_sprite.png diff --git a/app/assets/images/progress_bar.gif b/app/assets/images/progress_bar.gif Binary files differnew file mode 100644 index 00000000000..c3d43fa40b2 --- /dev/null +++ b/app/assets/images/progress_bar.gif diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png Binary files differnew file mode 100644 index 00000000000..a6d477033fa --- /dev/null +++ b/app/assets/images/slider_handles.png diff --git a/app/assets/images/ui-icons_222222_256x240.png b/app/assets/images/ui-icons_222222_256x240.png Binary files differnew file mode 100644 index 00000000000..8bc06cbf03b --- /dev/null +++ b/app/assets/images/ui-icons_222222_256x240.png diff --git a/app/assets/images/ui-icons_454545_256x240.png b/app/assets/images/ui-icons_454545_256x240.png Binary files differnew file mode 100644 index 00000000000..cfd1eaffaae --- /dev/null +++ b/app/assets/images/ui-icons_454545_256x240.png diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 4a393dbfe81..00000000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,31 +0,0 @@ -// This is a manifest file that'll be compiled into including all the files listed below. -// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically -// be included in the compiled file accessible from http://example.com/assets/application.js -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// the compiled file. -// -//= require jquery -//= require jquery.ui.all -//= require jquery_ujs -//= require jquery.cookie -//= require jquery.endless-scroll -//= require jquery.highlight -//= require jquery.history -//= require jquery.waitforimages -//= require jquery.atwho -//= require jquery.scrollto -//= require jquery.blockUI -//= require turbolinks -//= require jquery.turbolinks -//= require bootstrap -//= require modernizr -//= require select2 -//= require raphael -//= require g.raphael-min -//= require g.bar-min -//= require branch-graph -//= require highlightjs.min -//= require ace/ace -//= require_tree . -//= require d3 -//= require underscore diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/application.js.coffee index 69c731ff1a1..5042221abe4 100644 --- a/app/assets/javascripts/main.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -1,3 +1,37 @@ +# This is a manifest file that'll be compiled into including all the files listed below. +# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +# be included in the compiled file accessible from http://example.com/assets/application.js +# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +# the compiled file. +# +#= require jquery +#= require jquery.ui.all +#= require jquery_ujs +#= require jquery.cookie +#= require jquery.endless-scroll +#= require jquery.highlight +#= require jquery.history +#= require jquery.waitforimages +#= require jquery.atwho +#= require jquery.scrollto +#= require jquery.blockUI +#= require turbolinks +#= require jquery.turbolinks +#= require bootstrap +#= require modernizr +#= require select2 +#= require raphael +#= require g.raphael-min +#= require g.bar-min +#= require branch-graph +#= require highlightjs.min +#= require ace/ace +#= require d3 +#= require underscore +#= require nprogress +#= require nprogress-turbolinks +#= require_tree . + window.slugify = (text) -> text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() @@ -41,19 +75,11 @@ window.linkify = (str) -> window.simpleFormat = (str) -> linkify(sanitize(str).replace(/\n/g, '<br />')) -window.startSpinner = -> - $('.turbolink-spinner').fadeIn() - -window.stopSpinner = -> - $('.turbolink-spinner').fadeOut() - window.unbindEvents = -> $(document).unbind('scroll') $(document).off('scroll') -document.addEventListener("page:fetch", startSpinner) document.addEventListener("page:fetch", unbindEvents) -document.addEventListener("page:change", stopSpinner) $ -> # Click a .one_click_select field, select the contents @@ -62,11 +88,6 @@ $ -> $('.remove-row').bind 'ajax:success', -> $(this).closest('li').fadeOut() - # Click a .appear-link, appear-data fadeout - $(".appear-link").on 'click', (e) -> - $('.appear-data').fadeIn() - e.preventDefault() - # Initialize select2 selects $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true) @@ -121,10 +142,6 @@ $ -> $(@).next('table').show() $(@).remove() - $(".diff-content").on "click", ".js-details-expand", -> - $(@).next('.js-details-contain').removeClass("hide") - $(@).remove() - (($) -> # Disable an element and add the 'disabled' Bootstrap class $.fn.extend disable: -> diff --git a/app/assets/javascripts/behaviors/details_behavior.coffee b/app/assets/javascripts/behaviors/details_behavior.coffee index 7ad5c818946..decab3e1bed 100644 --- a/app/assets/javascripts/behaviors/details_behavior.coffee +++ b/app/assets/javascripts/behaviors/details_behavior.coffee @@ -1,5 +1,15 @@ $ -> $("body").on "click", ".js-details-target", -> container = $(@).closest(".js-details-container") - container.toggleClass("open") + + # Show details content. Hides link after click. + # + # %div + # %a.js-details-expand + # %div.js-details-content + # + $("body").on "click", ".js-details-expand", (e) -> + $(@).next('.js-details-content').removeClass("hide") + $(@).hide() + e.preventDefault() diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee index 5afb656e696..d06cb116dfe 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.coffee +++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee @@ -1,17 +1,18 @@ $ -> $("body").on "click", ".js-toggler-target", -> container = $(@).closest(".js-toggler-container") - container.toggleClass("on") - $("body").on "click", ".js-toggle-visibility-link", (e) -> + # Toggle button. Show/hide content inside parent container. + # Button does not change visibility. If button has icon - it changes chevron style. + # + # %div.js-toggle-container + # %a.js-toggle-button + # %div.js-toggle-content + # + $("body").on "click", ".js-toggle-button", (e) -> $(@).find('i'). toggleClass('icon-chevron-down'). toggleClass('icon-chevron-up') - container = $(".js-toggle-visibility-container") - container.toggleClass("hide") - e.preventDefault() - - $("body").on "click", ".js-toggle-button", (e) -> $(@).closest(".js-toggle-container").find(".js-toggle-content").toggle() e.preventDefault() diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index dd12000a1cf..00d56ae5b4b 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -6,7 +6,6 @@ GitLab.GfmAutoComplete = dataSource: '' # Emoji Emoji: - assetBase: '' template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>' # Team Members @@ -27,7 +26,7 @@ GitLab.GfmAutoComplete = tpl: @Emoji.template callbacks: before_save: (emojis) => - $.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png" + $.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path # Team Members input.atwho diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 33466714681..4b7103010bb 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -7,6 +7,8 @@ *= require select2 *= require highlightjs.min *= require_self + *= require nprogress + *= require nprogress-bootstrap */ @import "main/variables.scss"; diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 6183cefa594..4e660d0b1e0 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -298,10 +298,6 @@ img.emoji { width: 20px; } -.appear-data { - display: none; -} - .chart { overflow: hidden; height: 220px; @@ -359,3 +355,7 @@ 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/print.scss b/app/assets/stylesheets/print.scss new file mode 100644 index 00000000000..42dbf4d6ef3 --- /dev/null +++ b/app/assets/stylesheets/print.scss @@ -0,0 +1,13 @@ +/* Generic print styles */ +header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;} +.profiler-results {display: none;} + +/* Styles targeted specifically at printing files */ +.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;} +.file-title {display: none;} +.file-holder {border: none;} + +.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } +.wiki h1 {font-size: 30px;} +.wiki h2 {font-size: 22px;} +.wiki h3 {font-size: 18px; font-weight: bold; } diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss index 6fc394e2e2b..e088ef82203 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/sections/dashboard.scss @@ -61,7 +61,7 @@ } .project-row, .group-row { - padding: 10px 12px !important; + padding: 8px 12px !important; font-size: 14px; line-height: 24px; diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index 6ef12b20a5a..fe7ab426e8d 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -42,7 +42,7 @@ } } - padding: 14px 0px; + padding: 12px 0px; border-bottom: 1px solid #eee; .event-title { color: #333; diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index f8da4f0f87b..06709bd7ef6 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -273,3 +273,9 @@ header { } } } + +@media (max-width: $screen-xs-max) { + #nprogress .spinner { + right: 35px !important; + } +} diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index f39a9341060..a65771974c6 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -189,7 +189,6 @@ ul.notes { } - /** * Line note button on the side of diffs */ diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb index f2b8277efea..4c1d0df4110 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 euser,pid,pcpu,pmem,stat,start,command)) + ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9ed46c23942..5f8b2da06f8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base before_filter :check_password_expiration around_filter :set_current_user_for_thread 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 @@ -179,11 +180,30 @@ class ApplicationController < ActionController::Base end end + def ldap_security_check + if current_user && current_user.requires_ldap_check? + gitlab_ldap_access do |access| + if access.allowed?(current_user) + current_user.last_credential_check_at = Time.now + current_user.save + else + sign_out current_user + flash[:alert] = "Access denied for your LDAP account." + redirect_to new_user_session_path + end + end + end + end + def event_filter filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? @event_filter ||= EventFilter.new(filters) end + def gitlab_ldap_access(&block) + Gitlab::LDAP::Access.open { |access| block.call(access) } + end + # JSON for infinite scroll via Pager object def pager_json(partial, count) html = render_to_string( diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index a74e97ac253..233b91680f6 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -22,6 +22,8 @@ class DashboardController < ApplicationController @last_push = current_user.recent_push + @publicish_project_count = Project.publicish(current_user).count + respond_to do |format| format.html format.json { pager_json("events/_events", @events.count) } diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 00000000000..988ede3007b --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,18 @@ +class PasswordsController < Devise::PasswordsController + + def create + email = resource_params[:email] + resource_found = resource_class.find_by_email(email) + if resource_found && resource_found.ldap_user? + flash[:alert] = "Cannot reset password for LDAP user." + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return + end + + self.resource = resource_class.send_reset_password_instructions(resource_params) + if successfully_sent?(resource) + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) + else + respond_with(resource) + end + end +end diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index b4f14e649e2..6713cd7c8c7 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController begin user = User.find_by_username(params[:username]) if user.present? - render text: user.all_ssh_keys.join("\n") + render text: user.all_ssh_keys.join("\n"), content_type: "text/plain" else render_404 and return end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index f1c0336e6ea..e181a0ec7fa 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -5,7 +5,7 @@ class ProjectsController < ApplicationController # Authorize before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] before_filter :require_non_empty_project, only: [:blob, :tree, :graph] layout 'navless', only: [:new, :create, :fork] @@ -21,16 +21,9 @@ class ProjectsController < ApplicationController def create @project = ::Projects::CreateService.new(current_user, params[:project]).execute + flash[:notice] = 'Project was successfully created.' if @project.saved? respond_to do |format| - flash[:notice] = 'Project was successfully created.' if @project.saved? - format.html do - if @project.saved? - redirect_to @project - else - render "new" - end - end format.js end end @@ -55,6 +48,11 @@ class ProjectsController < ApplicationController end def show + if @project.import_in_progress? + redirect_to import_project_path(@project) + return + end + return authenticate_user! unless @project.public? || current_user limit = (params[:limit] || 20).to_i @@ -67,9 +65,7 @@ class ProjectsController < ApplicationController if @project.empty_repo? render "projects/empty", layout: user_layout else - if current_user - @last_push = current_user.recent_push(@project.id) - end + @last_push = current_user.recent_push(@project.id) if current_user render :show, layout: user_layout end end @@ -77,6 +73,28 @@ class ProjectsController < ApplicationController 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 = params[:project][:import_url] + + if @project.save + @project.reload + @project.import_retry + end + + redirect_to import_project_path(@project) + end + def destroy return access_denied! unless can?(current_user, :remove_project, project) @@ -106,7 +124,7 @@ class ProjectsController < ApplicationController def autocomplete_sources @suggestions = { - emojis: Emoji.names, + emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } }, issues: @project.issues.select([:iid, :title, :description]), mergerequests: @project.merge_requests.select([:iid, :title, :description]), members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } } diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index c1648d6c387..8df84e9884a 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -7,6 +7,7 @@ class SearchController < ApplicationController if @project return access_denied! unless can?(current_user, :download_code, @project) + @search_results = Search::ProjectService.new(@project, current_user, params).execute else @search_results = Search::GlobalService.new(current_user, params).execute diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index e54a968326f..0dd941a48e2 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -19,6 +19,9 @@ class SnippetsController < ApplicationController def user_index @user = User.find_by(username: params[:username]) + + render_404 and return unless @user + @snippets = @user.snippets.fresh.non_expired if @user == current_user diff --git a/app/finders/base_finder.rb b/app/finders/base_finder.rb index d20716fb170..7fc5840561c 100644 --- a/app/finders/base_finder.rb +++ b/app/finders/base_finder.rb @@ -47,9 +47,9 @@ class BaseFinder [] end elsif current_user && params[:authorized_only].presence - klass.of_projects(current_user.authorized_projects) + klass.of_projects(current_user.authorized_projects).references(:project) else - klass.of_projects(Project.accessible_to(current_user)) + klass.of_projects(Project.accessible_to(current_user)).references(:project) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 90b05027155..faecde299c1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -146,8 +146,7 @@ module ApplicationHelper def authbutton(provider, size = 64) file_name = "#{provider.to_s.split('_').first}_#{size}.png" - image_tag("authbuttons/#{file_name}", - alt: "Sign in with #{provider.to_s.titleize}") + image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}") end def simple_sanitize(str) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 6cad5e4658e..69425bc171d 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -35,7 +35,6 @@ module GitlabMarkdownHelper # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- filter_html: true, with_toc_data: true, - hard_wrap: true, safe_links_only: true }.merge(options)) @markdown = Redcarpet::Markdown.new(gitlab_renderer, @@ -45,7 +44,7 @@ module GitlabMarkdownHelper fenced_code_blocks: true, autolink: true, strikethrough: true, - lax_html_blocks: true, + lax_spacing: true, space_after_headers: true, superscript: true) end @@ -64,8 +63,7 @@ module GitlabMarkdownHelper # project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq # ref - name of the branch or reference, eg. stable # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from - # wiki - whether the markdown is from wiki or not - def create_relative_links(text, project, ref, requested_path, wiki = false) + def create_relative_links(text, project, ref, requested_path) @path_to_satellite = project.satellite.path project_path_with_namespace = project.path_with_namespace paths = extract_paths(text) @@ -135,12 +133,12 @@ module GitlabMarkdownHelper end # Checks if the path exists in the repo - # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link + # eg. checks if doc/README.md exists, if not then link to blob def path_with_ref(path, ref) if file_exists?(path) "#{local_path(path)}/#{correct_ref(ref)}" else - "wikis" + "blob/#{correct_ref(ref)}" end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 1381b0220d6..ba25a87f392 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -20,7 +20,7 @@ module MergeRequestsHelper target_project_id: target_project.id, source_branch: event.branch_name, target_branch: target_project.repository.root_ref, - title: event.branch_name.titleize + title: event.branch_name.humanize } end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb index dd9e03d95a8..297ae83d895 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -10,7 +10,7 @@ module ProfileHelper end def show_profile_social_tab? - Gitlab.config.omniauth.enabled && !current_user.ldap_user? + enabled_social_providers.any? && !current_user.ldap_user? end def show_profile_remove_tab? diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 2dbc1cffb16..50501dffefb 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -40,7 +40,7 @@ module TreeHelper # Returns boolean def markup?(filename) filename.downcase.end_with?(*%w(.textile .rdoc .org .creole - .mediawiki .rst .asciidoc .pod)) + .mediawiki .rst .adoc .asciidoc .pod)) end def gitlab_markdown?(filename) diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb index 1c8ae122c46..1654fc55bca 100644 --- a/app/mailers/emails/groups.rb +++ b/app/mailers/emails/groups.rb @@ -3,7 +3,7 @@ module Emails def group_access_granted_email(user_group_id) @membership = UsersGroup.find(user_group_id) @group = @membership.group - + @target_url = group_url(@group) mail(to: @membership.user.email, subject: subject("Access to group was granted")) end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 3adb47dc5b1..d684e354452 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -3,6 +3,7 @@ module Emails def new_issue_email(recipient_id, issue_id) @issue = Issue.find(issue_id) @project = @issue.project + @target_url = project_issue_url(@project, @issue) mail(from: sender(@issue.author_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -12,6 +13,7 @@ module Emails @issue = Issue.find(issue_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @issue.project + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -21,6 +23,7 @@ module Emails @issue = Issue.find issue_id @project = @issue.project @updated_by = User.find updated_by_user_id + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -31,6 +34,7 @@ module Emails @issue_status = status @project = @issue.project @updated_by = User.find updated_by_user_id + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 0845e14edc7..a97d55f1b50 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -3,6 +3,7 @@ module Emails def new_merge_request_email(recipient_id, merge_request_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project + @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(@merge_request.author_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -12,6 +13,7 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @merge_request.project + @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -21,15 +23,17 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @updated_by = User.find updated_by_user_id @project = @merge_request.project + @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) end - def merged_merge_request_email(recipient_id, merge_request_id) + def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - mail(from: sender(@merge_request.author_id_of_changes), + @target_url = project_merge_request_url(@project, @merge_request) + mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 00b127da429..ccbdadf010f 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -4,6 +4,7 @@ module Emails @note = Note.find(note_id) @commit = @note.noteable @project = @note.project + @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@commit.title} (#{@commit.short_id})")) @@ -13,6 +14,7 @@ module Emails @note = Note.find(note_id) @issue = @note.noteable @project = @note.project + @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -22,6 +24,7 @@ module Emails @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project + @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -30,6 +33,7 @@ module Emails def note_wall_email(recipient_id, note_id) @note = Note.find(note_id) @project = @note.project + @target_url = project_wall_url(@note.project, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("Note on wall")) diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index c91660a02b5..f02d95fd557 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -3,6 +3,7 @@ module Emails def new_user_email(user_id, password) @user = User.find(user_id) @password = password + @target_url = user_url(@user) mail(to: @user.email, subject: subject("Account was created for you")) end @@ -15,6 +16,7 @@ module Emails def new_ssh_key_email(key_id) @key = Key.find(key_id) @user = @key.user + @target_url = user_url(@user) mail(to: @user.email, subject: subject("SSH key was added to your account")) end end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 46f24e9fb7c..46aa34d13da 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -3,6 +3,7 @@ module Emails def project_access_granted_email(user_project_id) @users_project = UsersProject.find user_project_id @project = @users_project.project + @target_url = project_url(@project) mail(to: @users_project.user.email, subject: subject("Access to project was granted")) end @@ -10,6 +11,7 @@ module Emails def project_was_moved_email(project_id, user_id) @user = User.find user_id @project = Project.find project_id + @target_url = project_url(@project) mail(to: @user.email, subject: subject("Project was moved")) end @@ -21,6 +23,11 @@ module Emails @commits = Commit.decorate(compare.commits) @diffs = compare.diffs @branch = branch + if @commits.length > 1 + @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last) + else + @target_url = project_commit_url(@project, @compare.commit) + end mail(from: sender(author_id), to: recipient, diff --git a/app/models/ability.rb b/app/models/ability.rb index 69ada753d02..1afe8a4638f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -240,6 +240,7 @@ class Ability can_manage = group_abilities(user, group).include?(:manage_group) if can_manage && (user != target_user) rules << :modify + rules << :destroy end if !group.last_owner?(user) && (can_manage || (user == target_user)) rules << :destroy diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4774cbcf3aa..7c2648d8c7a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -133,7 +133,7 @@ class MergeRequest < ActiveRecord::Base end def reload_code - if merge_request_diff && opened? + if merge_request_diff && open? merge_request_diff.reload_content end end diff --git a/app/models/note.rb b/app/models/note.rb index 48c03c9d587..906de4855ab 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -199,7 +199,8 @@ class Note < ActiveRecord::Base def downvote? votable? && (note.start_with?('-1') || note.start_with?(':-1:') || - note.start_with?(':thumbsdown:') + note.start_with?(':thumbsdown:') || + note.start_with?(':thumbs_down_sign:') ) end @@ -249,7 +250,8 @@ class Note < ActiveRecord::Base def upvote? votable? && (note.start_with?('+1') || note.start_with?(':+1:') || - note.start_with?(':thumbsup:') + note.start_with?(':thumbsup:') || + note.start_with?(':thumbs_up_sign:') ) end diff --git a/app/models/project.rb b/app/models/project.rb index 47dc8a1fdb0..769ab217625 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,7 +28,6 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel extend Enumerize - default_value_for :imported, false default_value_for :archived, false ActsAsTaggableOn.strict_case_match = true @@ -57,15 +56,13 @@ class Project < ActiveRecord::Base has_one :flowdock_service, dependent: :destroy has_one :assembla_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy + has_one :slack_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 - # Merge Requests for target project should be removed with it has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" - # Merge requests from source project should be kept when source project was removed has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest - has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy has_many :services, dependent: :destroy has_many :events, dependent: :destroy @@ -74,10 +71,8 @@ class Project < ActiveRecord::Base has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :protected_branches, dependent: :destroy - has_many :users_projects, dependent: :destroy has_many :users, through: :users_projects - has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys, through: :deploy_keys_projects @@ -97,15 +92,12 @@ class Project < ActiveRecord::Base validates :issues_enabled, :wall_enabled, :merge_requests_enabled, :wiki_enabled, inclusion: { in: [true, false] } validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true - validates :namespace, presence: true validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id - validates :import_url, format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" }, if: :import? - validate :check_limit, on: :create # Scopes @@ -118,14 +110,36 @@ class Project < ActiveRecord::Base scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } - scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } - scope :non_archived, -> { where(archived: false) } enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab + state_machine :import_status, initial: :none do + event :import_start do + transition :none => :started + end + + event :import_finish do + transition :started => :finished + end + + event :import_fail do + transition :started => :failed + end + + event :import_retry do + transition :failed => :started + end + + state :started + state :finished + state :failed + + after_transition any => :started, :do => :add_import_job + end + class << self def public_and_internal_levels [Project::PUBLIC, Project::INTERNAL] @@ -164,15 +178,13 @@ class Project < ActiveRecord::Base end def find_with_namespace(id) - if id.include?("/") - id = id.split("/") - namespace = Namespace.find_by(path: id.first) - return nil unless namespace - - where(namespace_id: namespace.id).find_by(path: id.second) - else - where(path: id, namespace_id: nil).last - end + return nil unless id.include?("/") + + id = id.split("/") + namespace = Namespace.find_by(path: id.first) + return nil unless namespace + + where(namespace_id: namespace.id).find_by(path: id.second) end def visibility_levels @@ -202,12 +214,28 @@ class Project < ActiveRecord::Base id && persisted? end + def add_import_job + RepositoryImportWorker.perform_in(2.seconds, id) + end + def import? import_url.present? end def imported? - imported + import_finished? + end + + def import_in_progress? + import? && import_status == 'started' + end + + def import_failed? + import_status == 'failed' + end + + def import_finished? + import_status == 'finished' end def check_limit @@ -277,7 +305,7 @@ class Project < ActiveRecord::Base end def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium) + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack) end def gitlab_ci? @@ -361,18 +389,17 @@ class Project < ActiveRecord::Base branch_name = ref.gsub("refs/heads/", "") c_ids = self.repository.commits_between(oldrev, newrev).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.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } + # 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.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } - # 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.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } - true end diff --git a/app/models/project_hook.rb b/app/models/project_hook.rb index e1c9ed01bc5..4e9b22532cb 100644 --- a/app/models/project_hook.rb +++ b/app/models/project_hook.rb @@ -17,9 +17,10 @@ class ProjectHook < WebHook belongs_to :project - attr_accessible :push_events, :issues_events, :merge_requests_events + attr_accessible :push_events, :issues_events, :merge_requests_events, :tag_push_events scope :push_hooks, -> { where(push_events: true) } + scope :tag_push_hooks, -> { where(tag_push_events: true) } scope :issue_hooks, -> { where(issues_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } end diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb new file mode 100644 index 00000000000..b2b8d6fed7a --- /dev/null +++ b/app/models/project_services/slack_message.rb @@ -0,0 +1,95 @@ +require 'slack-notifier' + +class SlackMessage + def initialize(params) + @after = params.fetch(:after) + @before = params.fetch(:before) + @commits = params.fetch(:commits, []) + @project_name = params.fetch(:project_name) + @project_url = params.fetch(:project_url) + @ref = params.fetch(:ref).gsub('refs/heads/', '') + @username = params.fetch(:user_name) + end + + def compose + format(message) + end + + private + + attr_reader :after + attr_reader :before + attr_reader :commits + attr_reader :project_name + attr_reader :project_url + attr_reader :ref + attr_reader :username + + def message + if new_branch? + new_branch_message + elsif removed_branch? + removed_branch_message + else + push_message << commit_messages + end + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def new_branch_message + "#{username} pushed new branch #{branch_link} to #{project_link}" + end + + def removed_branch_message + "#{username} removed branch #{ref} from #{project_link}" + end + + def push_message + "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})" + end + + def commit_messages + commits.each_with_object('') do |commit, str| + str << compose_commit_message(commit) + end + end + + def compose_commit_message(commit) + id = commit.fetch(:id)[0..5] + message = commit.fetch(:message) + url = commit.fetch(:url) + + "\n - #{message} ([#{id}](#{url}))" + end + + def new_branch? + before =~ /000000/ + end + + def removed_branch? + after =~ /000000/ + end + + def branch_url + "#{project_url}/commits/#{ref}" + end + + def compare_url + "#{project_url}/compare/#{before}...#{after}" + end + + def branch_link + "[#{ref}](#{branch_url})" + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def compare_link + "[Compare changes](#{compare_url})" + end +end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb new file mode 100644 index 00000000000..754fd87db02 --- /dev/null +++ b/app/models/project_services/slack_service.rb @@ -0,0 +1,67 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# subdomain :string(255) +# room :string(255) +# api_key :string(255) +# + +class SlackService < Service + attr_accessible :room + attr_accessible :subdomain + + validates :room, presence: true, if: :activated? + validates :subdomain, presence: true, if: :activated? + validates :token, presence: true, if: :activated? + + def title + 'Slack' + end + + def description + 'A team communication tool for the 21st century' + end + + def to_param + 'slack' + end + + def fields + [ + { type: 'text', name: 'subdomain', placeholder: '' }, + { type: 'text', name: 'token', placeholder: '' }, + { type: 'text', name: 'room', placeholder: 'Ex. #general' }, + ] + end + + def execute(push_data) + message = SlackMessage.new(push_data.merge( + project_url: project_url, + project_name: project_name + )) + + notifier = Slack::Notifier.new(subdomain, token) + notifier.channel = room + notifier.ping(message.compose) + end + + private + + def project_name + project.name_with_namespace.gsub(/\s/, '') + end + + def project_url + project.web_url + end +end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index edc179b20fd..c1c9ba257f2 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,8 @@ class Snippet < ActiveRecord::Base attr_accessible :title, :content, :file_name, :expires_at, :private + default_value_for :private, true + belongs_to :author, class_name: "User" has_many :notes, as: :noteable, dependent: :destroy diff --git a/app/models/user.rb b/app/models/user.rb index 855fe58ffe8..d9f420759d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -185,7 +185,7 @@ class User < ActiveRecord::Base where(conditions).first end end - + def find_for_commit(email, name) # Prefer email match over name match User.where(email: email).first || @@ -249,7 +249,7 @@ class User < ActiveRecord::Base def namespace_uniq namespace_name = self.username if Namespace.find_by(path: namespace_name) - self.errors.add :username, "already exist" + self.errors.add :username, "already exists" end end @@ -275,7 +275,9 @@ class User < ActiveRecord::Base # Projects user has access to def authorized_projects @authorized_projects ||= begin - project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq + project_ids = personal_projects.pluck(:id) + project_ids += groups_projects.pluck(:id) + project_ids += projects.pluck(:id).uniq Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC') end end @@ -406,6 +408,14 @@ class User < ActiveRecord::Base end end + def requires_ldap_check? + if ldap_user? + !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now + else + false + end + end + def solo_owned_groups @solo_owned_groups ||= owned_groups.select do |group| group.owners == [self] diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index 4e3deec01bf..ad41ddad58f 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,30 +1,6 @@ class ProjectObserver < BaseObserver def after_create(project) - project.update_column(:last_activity_at, project.created_at) - - return true if project.forked? - - if project.import? - RepositoryImportWorker.perform_in(5.seconds, project.id) - else - GitlabShellWorker.perform_async( - :add_repository, - project.path_with_namespace - ) - - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") - end - - if project.wiki_enabled? - begin - # force the creation of a wiki, - GollumWiki.new(project, project.owner).wiki - rescue GollumWiki::CouldNotCreateWikiError => ex - # Prevent project observer crash - # if failed to create wiki - nil - end - end + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end def after_update(project) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb new file mode 100644 index 00000000000..62eaf9b4f51 --- /dev/null +++ b/app/services/git_tag_push_service.rb @@ -0,0 +1,40 @@ +class GitTagPushService + attr_accessor :project, :user, :push_data + + def execute(project, user, oldrev, newrev, ref) + @project, @user = project, user + @push_data = create_push_data(oldrev, newrev, ref) + + create_push_event + project.repository.expire_cache + project.execute_hooks(@push_data.dup, :tag_push_hooks) + end + + private + + def create_push_data(oldrev, newrev, ref) + data = { + ref: ref, + before: oldrev, + after: newrev, + user_id: user.id, + user_name: user.name, + project_id: project.id, + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url + } + } + end + + def create_push_event + Event.create!( + project: project, + action: Event::PUSHED, + data: push_data, + author_id: push_data[:user_id] + ) + end +end diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb index d60d61ed54a..9c9117f4687 100644 --- a/app/services/merge_requests/auto_merge_service.rb +++ b/app/services/merge_requests/auto_merge_service.rb @@ -12,7 +12,7 @@ module MergeRequests merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, current_user) create_merge_event(merge_request) execute_project_hooks(merge_request) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 1d5af04cdbb..80487f661e0 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -10,7 +10,7 @@ module MergeRequests merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, current_user) create_merge_event(merge_request) execute_project_hooks(merge_request) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 5daf573630d..6fda9868aa5 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -86,12 +86,12 @@ class NotificationService # * merge_request assignee if their notification level is not Disabled # * project team members with notification level higher then Participating # - def merge_mr(merge_request) + def merge_mr(merge_request, current_user) recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq recipients.each do |recipient| - mailer.merged_merge_request_email(recipient.id, merge_request.id) + mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id) end end @@ -111,6 +111,7 @@ 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 opts = { noteable_type: note.noteable_type, project_id: note.project_id } diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index ba131d8ffbe..4d3d518a509 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -58,6 +58,29 @@ module Projects user: current_user ) 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 + ) + + end + + if @project.wiki_enabled? + begin + # force the creation of a wiki, + GollumWiki.new(@project, @project.owner).wiki + rescue GollumWiki::CouldNotCreateWikiError => ex + # Prevent project observer crash + # if failed to create wiki + nil + end + end end @project diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index 09c7cb25dd5..8a1fce17ce7 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -14,10 +14,9 @@ module Search group = Group.find_by(id: params[:group_id]) if params[:group_id].present? projects = Project.accessible_to(current_user) projects = projects.where(namespace_id: group.id) if group - projects = projects.search(query) project_ids = projects.pluck(:id) - result[:projects] = projects.limit(20) + result[:projects] = projects.search(query).limit(20) result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).order('updated_at DESC').limit(20) result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20) result[:total_results] = %w(projects issues merge_requests).sum { |items| result[items.to_sym].size } diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index e5af56ffc5c..32f77cc1a70 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -14,27 +14,21 @@ %table.table %thead %th USER - %th %th PID - %th %th CPU - %th %th MEM - %th %th STATE - %th %th START - %th %th COMMAND - %th - - @sidekiq_processes.split("\n").each do |process| + %tbody + - @sidekiq_processes.each do |process| - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) - - data = process.gsub!(/\s+/m, '|').strip.split('|') + - data = process.strip.split(' ') %tr - - 6.times do + %td= Settings.gitlab.user + - 5.times do %td= data.shift - %td - %td= data.join(" ") + %td= data.join(' ') .clearfix %p diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index bb62a1c24fc..e0993293eab 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -34,3 +34,18 @@ = link_to new_group_path, class: "btn btn-new" do New group » +-if @publicish_project_count > 0 + %hr + %div + .dashboard-intro-icon + %i.icon-globe + %div + %p.slead + There are + %strong= @publicish_project_count + public projects on this server. + %br + Public projects are an easy way to allow everyone to have read-only access. + .link_holder + = link_to public_projects_path, class: "btn btn-new" do + Browse public projects » diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml index 935bc6af505..2b1cb9c694f 100644 --- a/app/views/devise/sessions/_oauth_providers.html.haml +++ b/app/views/devise/sessions/_oauth_providers.html.haml @@ -2,10 +2,12 @@ - if providers.present? %hr %div{:'data-no-turbolink' => 'data-no-turbolink'} - %span Sign in with: + %span Sign in with*: - providers.each do |provider| %span - if default_providers.include?(provider) = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) - else = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" + %br + %small * Make sure your email address is public diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml index 0c972622f88..2d0605b18ad 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -9,7 +9,7 @@ %hr -.clearfix +.clearfix.js-toggle-container = 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 member by name', class: 'form-control search-text-input input-mn-300' } @@ -17,11 +17,11 @@ - if current_user && current_user.can?(:manage_group, @group) .pull-right - = link_to '#', class: 'btn btn-new js-toggle-visibility-link' do + = link_to '#', class: 'btn btn-new js-toggle-button' do Add members %i.icon-chevron-down - .js-toggle-visibility-container.hide.new-group-member-holder + .js-toggle-content.hide.new-group-member-holder = render "new_group_member" .ui-box.prepend-top-20 diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 9ba20f1347d..d9a7a2d31cf 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -4,7 +4,8 @@ = "#{title} | " if defined?(title) GitLab = favicon_link_tag 'favicon.ico' - = stylesheet_link_tag "application" + = stylesheet_link_tag "application", :media => "all" + = stylesheet_link_tag "print", :media => "print" = javascript_include_tag "application" = csrf_meta_tags = include_gon diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 5080a1b7ef6..d8001fd76d7 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -15,10 +15,6 @@ .navbar-collapse.collapse %ul.nav.navbar-nav %li.hidden-sm.hidden-xs - %a - %div.hide.turbolink-spinner - %i.icon-refresh.icon-spin - %li.hidden-sm.hidden-xs = render "layouts/search" %li.visible-sm.visible-xs = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 6a20dedf62f..9e728b462bb 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,4 +1,3 @@ :javascript GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}" - GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}" GitLab.GfmAutoComplete.setup(); diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml index 99e11cd2b5c..63992a22f32 100644 --- a/app/views/layouts/_public_head_panel.html.haml +++ b/app/views/layouts/_public_head_panel.html.haml @@ -8,11 +8,15 @@ %span.separator %h1.title= title - .pull-right + %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} + %span.sr-only Toggle navigation + %i.icon-reorder + + .pull-right.hidden-xs = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new' - %ul.nav.navbar-nav - %li - %a - %div.hide.turbolink-spinner - %i.icon-refresh.icon-spin + .navbar-collapse.collapse + %ul.nav.navbar-nav + %li.visible-xs + = link_to "Sign in", new_session_path(:user) + diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index f88abeca887..09d84a3eb9f 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -3,20 +3,24 @@ %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %title GitLab + :css + p.details { + font-style:italic; + color:#777 + } + .footer p { + font-size:small; + color:#777 + } %body - %h1{style: "background: #EEE; border-bottom: 1px solid #DDD; color: #474D57; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab - - if @project - \| - = link_to @project.name_with_namespace, project_url(@project), style: 'color: #29B; text-decoration: none' - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - = yield - %br - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - %p{style: "font-size:small;color:#777"} - - if @project - You're receiving this notification because you are a member of the #{@project.name_with_namespace} project team. + %div.content + = yield + %div.footer{style: "margin-top: 10px;"} + %p + \— + %br + - if @project + You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. + - if @target_url + #{link_to "View in GitLab", @target_url} diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml index cf4ca9c7a84..2a9230244f8 100644 --- a/app/views/layouts/public_projects.html.haml +++ b/app/views/layouts/public_projects.html.haml @@ -4,7 +4,7 @@ %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/public_head_panel", title: project_title(@project) - %nav.main-nav + %nav.main-nav.navbar-collapse.collapse .container= render 'layouts/nav/project' .container .content= yield diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 9e329af2d47..5272dfa0ede 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,6 +1,2 @@ -%p - %strong #{@note.author_name} - wrote: - -%cite{style: 'color: #666'} +%div = markdown(@note.note) diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml index 325cd44eb4b..56c18cd83cd 100644 --- a/app/views/notify/closed_issue_email.html.haml +++ b/app/views/notify/closed_issue_email.html.haml @@ -1,5 +1,2 @@ %p = "Issue was closed by #{@updated_by.name}" -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml index 45770cc85de..809d46f31be 100644 --- a/app/views/notify/closed_merge_request_email.html.haml +++ b/app/views/notify/closed_merge_request_email.html.haml @@ -1,9 +1,2 @@ %p - = "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p - != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - + = "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" diff --git a/app/views/notify/group_access_granted_email.html.haml b/app/views/notify/group_access_granted_email.html.haml index 5023ec737a5..823ebf77347 100644 --- a/app/views/notify/group_access_granted_email.html.haml +++ b/app/views/notify/group_access_granted_email.html.haml @@ -1,5 +1,4 @@ %p = "You have been granted #{@membership.human_access} access to group" -%p = link_to group_url(@group) do = @group.name diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml index 7706b3f7516..482c884a9db 100644 --- a/app/views/notify/issue_status_changed_email.html.haml +++ b/app/views/notify/issue_status_changed_email.html.haml @@ -1,5 +1,2 @@ %p = "Issue was #{@issue_status} by #{@updated_by.name}" -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml index e2bc9cf5c04..0c62d439aed 100644 --- a/app/views/notify/merged_merge_request_email.html.haml +++ b/app/views/notify/merged_merge_request_email.html.haml @@ -1,9 +1,2 @@ %p - = "Merge Request #{@merge_request.iid} was merged" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p - != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - + = "Merge Request !#{@merge_request.iid} was merged" diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index 3b3c148517a..f2f8eee18c4 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,9 +1,6 @@ -%p - New Issue was created. -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title -%p - Author: #{@issue.author_name} -%p - Assignee: #{@issue.assignee_name} +-if @issue.description + = markdown(@issue.description) + +- if @issue.assignee_id.present? + %p + Assignee: #{@issue.assignee_name} diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 321f9418ded..f02d5111b22 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -1,9 +1,9 @@ -%p - = "New Merge Request ##{@merge_request.iid}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p +%p.details != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} +- if @merge_request.assignee_id.present? + %p + Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} + +-if @merge_request.description + = markdown(@merge_request.description) diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml index 620b258fc15..1d961e4424c 100644 --- a/app/views/notify/note_commit_email.html.haml +++ b/app/views/notify/note_commit_email.html.haml @@ -1,5 +1,2 @@ -%p - = "New comment for Commit #{@commit.short_id}" - = link_to_gfm truncate(@commit.title, length: 16), project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}") = render 'note_message' diff --git a/app/views/notify/note_issue_email.html.haml b/app/views/notify/note_issue_email.html.haml index b3230953e7d..2fa2f784661 100644 --- a/app/views/notify/note_issue_email.html.haml +++ b/app/views/notify/note_issue_email.html.haml @@ -1,4 +1 @@ -%p - = "New comment for Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}") = render 'note_message' diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml index d587b068486..65f0e4c4068 100644 --- a/app/views/notify/note_merge_request_email.html.haml +++ b/app/views/notify/note_merge_request_email.html.haml @@ -1,8 +1,7 @@ -%p - - if @note.for_diff_line? - = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - - else - = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - for Merge Request ##{@merge_request.iid} - %cite "#{truncate(@merge_request.title, length: 20)}" +- if @note.diff_file_name + %p.details + New comment on diff for + = link_to @note.diff_file_name, @target_url + \: + = render 'note_message' diff --git a/app/views/notify/note_wall_email.html.haml b/app/views/notify/note_wall_email.html.haml index 92200e83efa..2fa2f784661 100644 --- a/app/views/notify/note_wall_email.html.haml +++ b/app/views/notify/note_wall_email.html.haml @@ -1,5 +1 @@ -%p - New message on - = link_to "Project Wall", project_wall_url(@note.project, anchor: "note_#{@note.id}") - = render 'note_message' diff --git a/app/views/notify/reassigned_issue_email.html.haml b/app/views/notify/reassigned_issue_email.html.haml index b0edff44ce6..07227a3e68c 100644 --- a/app/views/notify/reassigned_issue_email.html.haml +++ b/app/views/notify/reassigned_issue_email.html.haml @@ -1,7 +1,4 @@ %p - = "Reassigned Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue) -%p Assignee changed - if @previous_assignee from diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml index d2d82d36c48..00aee6bc952 100644 --- a/app/views/notify/reassigned_merge_request_email.html.haml +++ b/app/views/notify/reassigned_merge_request_email.html.haml @@ -1,7 +1,4 @@ %p - = "Reassigned Merge Request ##{@merge_request.iid}" - = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) -%p Assignee changed - if @previous_assignee from diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index df44c30c005..f2b0699f136 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,7 +1,7 @@ .pull-right %div - if @notes_count > 0 - %span.btn.disabled.grouped + %span.btn.disabled.btn-grouped %i.icon-comment = @notes_count .pull-left.btn-group @@ -47,7 +47,7 @@ - if @branches.any? and in = link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand") - %span.js-details-contain.hide + %span.js-details-content.hide = commit_branches_links(@project, @branches) .commit-box diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml index dd287fcc153..466085139f9 100644 --- a/app/views/projects/commits/_diffs.html.haml +++ b/app/views/projects/commits/_diffs.html.haml @@ -44,7 +44,7 @@ - file = project.repository.blob_at(@commit.id, diff.new_path) - file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file - next unless file - .diff-file{id: "diff-#{i}"} + .diff-file.js-toggle-container{id: "diff-#{i}"} .diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"} - if diff.deleted_file %span= diff.old_path @@ -60,6 +60,11 @@ %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" .diff-btn-group + = link_to "#", class: "js-toggle-button btn btn-small" do + %i.icon-chevron-down + Diff comments + + - if @merge_request && @merge_request.source_project = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do Edit diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml index a444b8b59a6..89710d3a09a 100644 --- a/app/views/projects/create.js.haml +++ b/app/views/projects/create.js.haml @@ -1,6 +1,10 @@ - if @project.saved? - :plain - location.href = "#{project_path(@project)}"; + - 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'))}"); diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 005d994806e..10674ccae46 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -93,100 +93,101 @@ - .centered-light-block - %h3 - %i.icon-warning-sign - Dangerous settings - - %p Project settings below may result in data loss! - = link_to '#', class: 'btn js-toggle-visibility-link' do - Show it to me - %i.icon-chevron-down - - .js-toggle-visibility-container.hide - - if can? current_user, :archive_project, @project - .ui-box.ui-box-danger - .title - - if @project.archived? - Unarchive project - - else - Archive project - .body - - if @project.archived? - %p - Unarchiving the project will mark its repository as active. - %br - The project can be committed to. - %br - %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive', unarchive_project_path(@project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again." }, - method: :post, class: "btn btn-remove" - - else - %p - Archiving the project will mark its repository as read-only. - %br - It is hidden from the dashboard and doesn't show up in searches. - %br - %strong Archived projects cannot be committed to! - = link_to 'Archive', archive_project_path(@project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, - method: :post, class: "btn btn-remove" - - else - .nothing-here-block Only the project owner can archive a project - - - if can?(current_user, :change_namespace, @project) + .danger-settings.js-toggle-container + .centered-light-block + %h3 + %i.icon-warning-sign + Dangerous settings + + %p Project settings below may result in data loss! + = link_to '#', class: 'btn js-toggle-button' do + Show it to me + %i.icon-chevron-down + + .js-toggle-content.hide + - if can? current_user, :archive_project, @project + .ui-box.ui-box-danger + .title + - if @project.archived? + Unarchive project + - else + Archive project + .body + - if @project.archived? + %p + Unarchiving the project will mark its repository as active. + %br + The project can be committed to. + %br + %strong Once active this project shows up in the search and on the dashboard. + = link_to 'Unarchive', unarchive_project_path(@project), + data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again." }, + method: :post, class: "btn btn-remove" + - else + %p + Archiving the project will mark its repository as read-only. + %br + It is hidden from the dashboard and doesn't show up in searches. + %br + %strong Archived projects cannot be committed to! + = link_to 'Archive', archive_project_path(@project), + data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + method: :post, class: "btn btn-remove" + - else + .nothing-here-block Only the project owner can archive a project + + - if can?(current_user, :change_namespace, @project) + .ui-box.ui-box-danger + .title Transfer project + .errors-holder + .form-holder + = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + .form-group + = f.label :namespace_id, class: 'control-label' do + %span Namespace + .col-sm-10 + .form-group + = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' } + %ul + %li Be careful. Changing the project's namespace can have unintended side effects. + %li You can only transfer the project to namespaces you manage. + %li You will need to update your local repositories to point to the new location. + .form-actions + = f.submit 'Transfer', class: "btn btn-remove" + - else + .nothing-here-block Only the project owner can transfer a project + .ui-box.ui-box-danger - .title Transfer project + .title Rename repository .errors-holder .form-holder - = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + = form_for(@project, html: { class: 'form-horizontal' }) do |f| .form-group - = f.label :namespace_id, class: 'control-label' do - %span Namespace - .col-sm-10 + = f.label :path, class: 'control-label' do + %span Path + .col-sm-9 .form-group - = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' } + .input-group + = f.text_field :path, class: 'form-control' + %span.input-group-addon .git %ul - %li Be careful. Changing the project's namespace can have unintended side effects. - %li You can only transfer the project to namespaces you manage. + %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Transfer', class: "btn btn-remove" - - else - .nothing-here-block Only the project owner can transfer a project - - .ui-box.ui-box-danger - .title Rename repository - .errors-holder - .form-holder - = form_for(@project, html: { class: 'form-horizontal' }) do |f| - .form-group - = f.label :path, class: 'control-label' do - %span Path - .col-sm-9 - .form-group - .input-group - = f.text_field :path, class: 'form-control' - %span.input-group-addon .git - %ul - %li Be careful. Renaming a project's repository can have unintended side effects. - %li You will need to update your local repositories to point to the new location. - .form-actions - = f.submit 'Rename', class: "btn btn-remove" - - - if can?(current_user, :remove_project, @project) - .ui-box.ui-box-danger - .title Remove project - .body - %p - Removing the project will delete its repository and all related resources including issues, merge requests etc. - %br - %strong Removed projects cannot be restored! - - = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project) }, method: :delete, class: "btn btn-remove" - - else - .nothing-here-block Only project owner can remove a project + = f.submit 'Rename', class: "btn btn-remove" + + - if can?(current_user, :remove_project, @project) + .ui-box.ui-box-danger + .title Remove project + .body + %p + Removing the project will delete its repository and all related resources including issues, merge requests etc. + %br + %strong Removed projects cannot be restored! + + = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project) }, method: :delete, class: "btn btn-remove" + - else + .nothing-here-block Only project owner can remove a project .save-project-loader.hide %center diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 489b9b0e951..97dc73bce14 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,46 +1,34 @@ = render "home_panel" -- if @project.import? && !@project.imported - .save-project-loader - %center - %h2 - %i.icon-spinner.icon-spin - Importing repository. - %p.monospace git clone --bare #{@project.import_url} - %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); +%div.git-empty + %fieldset + %legend Git global setup: + %pre.dark + :preserve + git config --global user.name "#{git_user_name}" + git config --global user.email "#{git_user_email}" -- else - %div.git-empty - %fieldset - %legend Git global setup: - %pre.dark - :preserve - git config --global user.name "#{git_user_name}" - git config --global user.email "#{git_user_email}" + %fieldset + %legend Create Repository + %pre.dark + :preserve + mkdir #{@project.path} + cd #{@project.path} + git init + touch README + git add README + git commit -m 'first commit' + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Create Repository - %pre.dark - :preserve - mkdir #{@project.path} - cd #{@project.path} - git init - touch README - git add README - git commit -m 'first commit' - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master + %fieldset + %legend Existing Git Repo? + %pre.dark + :preserve + cd existing_git_repo + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Existing Git Repo? - %pre.dark - :preserve - cd existing_git_repo - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master - - - 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" +- 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/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 00e5ae27779..866fd6f6066 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -27,6 +27,13 @@ %p.light This url will be triggered by a push to the repository %div + = f.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = f.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository + %div = f.check_box :issues_events, class: 'pull-left' .prepend-left-20 = f.label :issues_events, class: 'list-label' do @@ -56,6 +63,6 @@ .clearfix %span.monospace= hook.url %p - - %w(push_events issues_events merge_requests_events).each do |trigger| + - %w(push_events tag_push_events issues_events merge_requests_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml new file mode 100644 index 00000000000..d11372be61b --- /dev/null +++ b/app/views/projects/import.html.haml @@ -0,0 +1,30 @@ +- if @project.import_in_progress? + .save-project-loader + %center + %h2 + %i.icon-spinner.icon-spin + Import in progress. + %p.monospace git clone --bare #{@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 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 2 minutes. For big repositories, use a clone/push combination. + .form-actions + = f.submit 'Retry import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 51fa29ddcbe..22502760e50 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,13 +1,18 @@ -- if @repository.contribution_guide && !@merge_request.persisted? - - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) - .alert.alert-info.col-sm-10.col-sm-offset-2 - ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe = form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f| - -if @merge_request.errors.any? - .alert.alert-danger - %ul - - @merge_request.errors.full_messages.each do |msg| - %li= msg + .row + .col-sm-2 + .col-sm-10 + - if @repository.contribution_guide && !@merge_request.persisted? + - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) + .alert.alert-info + Please review the + %strong #{link_to "guidelines for contribution", contribution_guide_url} + to this repository. + + -if @merge_request.errors.any? + .alert.alert-danger + - @merge_request.errors.full_messages.each do |msg| + %div= msg .merge-request-branches .form-group @@ -47,24 +52,6 @@ = f.text_area :description, class: "form-control js-gfm-input", rows: 14 %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. - %hr - .form-group - .merge-request-assignee - = f.label :assignee_id, class: 'control-label' do - %i.icon-user - Assign to - .col-sm-10 - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id) - - = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' - .form-group - .merge-request-milestone - = f.label :milestone_id, class: 'control-label' do - %i.icon-time - Milestone - .col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'}) - - .form-actions - if @merge_request.new_record? = f.submit 'Submit merge request', class: "btn btn-create" @@ -96,7 +83,3 @@ target_branch.on("change", function() { $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); }); - $('.assign-to-me-link').on('click', function(e){ - $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change"); - e.preventDefault(); - }); diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index d3147188d1c..1b1082baafe 100644 --- a/app/views/projects/merge_requests/branch_from.js.haml +++ b/app/views/projects/merge_requests/branch_from.js.haml @@ -3,5 +3,5 @@ var mrTitle = $('#merge_request_title'); if(mrTitle.val().length == 0) { - mrTitle.val("#{params[:ref].titleize}"); + mrTitle.val("#{params[:ref].humanize}"); } diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml index a9cbe43fb16..4b1857ccb68 100644 --- a/app/views/projects/merge_requests/show/_mr_accept.html.haml +++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml @@ -1,6 +1,10 @@ - unless @allowed_to_merge - .bs-callout - %strong You don't have permission to merge this MR + - if @project.archived? + .bs-callout.bs-callout-warning + %strong Archived projects cannot be committed to! + - else + .bs-callout + %strong You don't have permission to merge this MR - if @show_merge_controls @@ -15,18 +19,19 @@ = link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" for instructions. - %br - If you want to modify merge commit message - - %strong - = link_to "click here", "#", class: "modify-merge-commit-link js-toggle-visibility-link", title: "Modify merge commit message" - .js-toggle-visibility-container.hide - .form-group - = label_tag :merge_commit_message, "Commit message", class: 'control-label' - .col-sm-10 - = text_area_tag :merge_commit_message, @merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true - %p.hint - The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines. + .js-toggle-container + %p + If you want to modify merge commit message - + %strong + = link_to "click here", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message" + .js-toggle-content.hide + .form-group + = label_tag :merge_commit_message, "Commit message", class: 'control-label' + .col-sm-10 + = text_area_tag :merge_commit_message, @merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true + %p.hint + The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines. .accept-group .pull-left 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 38db4363ade..abdcd127a0c 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -29,13 +29,13 @@ %span %i.icon-remove Closed by #{link_to_member(@project, @merge_request.closed_event.author)} - #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}. + #{time_ago_with_tooltip(@merge_request.closed_event.created_at)} - if @merge_request.merged? .alert.alert-info %span %i.icon-ok Merged by #{link_to_member(@project, @merge_request.merge_event.author)} - #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}. + #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} - if !@closes_issues.empty? && @merge_request.open? .alert.alert-info.alert-info %span diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 9ee54fef062..5d5637c1588 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -9,18 +9,6 @@ %strong Project name .col-sm-10 = f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true - .help-inline - = link_to "#", class: 'js-toggle-visibility-link' do - %span Customize repository name? - - .form-group.js-toggle-visibility-container.hide - = f.label :path, class: 'control-label' do - %span Repository name - .col-sm-10 - .input-group - = f.text_field :path, class: 'form-control' - %span.input-group-addon .git - - if current_user.can_select_namespace? .form-group @@ -29,19 +17,42 @@ .col-sm-10 = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - .form-group - .col-sm-2 - .col-sm-10 - = link_to "#", class: 'appear-link' do - %i.icon-upload-alt - %span Import existing repository? - .form-group.appear-data.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .light - URL must be cloneable + %hr + .js-toggle-container + .form-group + .col-sm-2 + .col-sm-10 + = link_to "#", class: 'js-toggle-button' do + %i.icon-edit + %span Customize repository name? + .js-toggle-content.hide + .form-group + = f.label :path, class: 'control-label' do + %span Repository name + .col-sm-10 + .input-group + = f.text_field :path, class: 'form-control' + %span.input-group-addon .git + + .js-toggle-container + .form-group + .col-sm-2 + .col-sm-10 + = link_to "#", class: 'js-toggle-button' do + %i.icon-upload-alt + %span Import existing repository? + .js-toggle-content.hide + .form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Import existing 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 2 minutes. For big repositories, use a clone/push combination. + %hr + .form-group = f.label :description, class: 'control-label' do Description diff --git a/app/views/projects/notes/_diff_notes_with_reply.html.haml b/app/views/projects/notes/_diff_notes_with_reply.html.haml index 9537ab18caa..9acadc6a14e 100644 --- a/app/views/projects/notes/_diff_notes_with_reply.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply.html.haml @@ -1,7 +1,7 @@ - note = notes.first # example note -# Check if line want not changed since comment was left - if !defined?(line) || line == note.diff_line - %tr.notes_holder + %tr.notes_holder.js-toggle-content %td.notes_line{ colspan: 2 } %span.btn.disabled %i.icon-comment diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml index 936dbb354cd..2012aa021b9 100644 --- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml @@ -1,6 +1,6 @@ - note1 = notes1.first # example note - note2 = notes2.first # example note -%tr.notes_holder +%tr.notes_holder.js-toggle-content -# Check if line want not changed since comment was left /- if !defined?(line1) || line1 == note1.diff_line - if note1 diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 217e36e38d0..81bf0611ec6 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -23,7 +23,7 @@ %i.icon-thumbs-up \+1 - if note.downvote? - %span.vote.downvote.label.label-error + %span.vote.downvote.label.label-danger %i.icon-thumbs-down \-1 diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 8a1e1d3354b..20879d69091 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -9,8 +9,8 @@ .col-md-3.project-side.hidden-sm .clearfix - if @project.archived? - .alert - %h5 + .alert.alert-warning + %h4 %i.icon-warning-sign Archived project! %p Repository is read-only diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 6416aa608ec..3b0cf77d42e 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -29,10 +29,20 @@ class PostReceive return false end - GitPushService.new.execute(project, user, oldrev, newrev, ref) + if tag?(ref) + GitTagPushService.new.execute(project, user, oldrev, newrev, ref) + else + GitPushService.new.execute(project, user, oldrev, newrev, ref) + end end def log(message) Gitlab::GitLogger.error("POST-RECEIVE: #{message}") end + + private + + def tag?(ref) + !!(/refs\/tags\/(.*)/.match(ref)) + end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 95b80bca7c0..e66df8c4db1 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -11,11 +11,11 @@ class RepositoryImportWorker project.import_url) if result - project.imported = true + project.import_finish project.save project.satellite.create unless project.satellite.exists? else - project.imported = false + project.import_fail end end end |
