diff options
Diffstat (limited to 'app/controllers')
70 files changed, 1026 insertions, 428 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 20bc5173f1d..2eac0cabf7a 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -2,6 +2,7 @@ class AbuseReportsController < ApplicationController def new @abuse_report = AbuseReport.new @abuse_report.user_id = params[:user_id] + @ref_url = params.fetch(:ref_url, '') end def create @@ -9,12 +10,10 @@ class AbuseReportsController < ApplicationController @abuse_report.reporter = current_user if @abuse_report.save - if current_application_settings.admin_notification_email.present? - AbuseReportMailer.notify(@abuse_report.id).deliver_later - end + @abuse_report.notify message = "Thank you for your report. A GitLab administrator will look into it shortly." - redirect_to root_path, notice: message + redirect_to @abuse_report.user, notice: message else render :new end @@ -23,6 +22,9 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(:user_id, :message) + params.require(:abuse_report).permit(%i( + message + user_id + )) end end diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index 38a5a9fca08..e9b0972bdd8 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -6,11 +6,9 @@ class Admin::AbuseReportsController < Admin::ApplicationController def destroy abuse_report = AbuseReport.find(params[:id]) - if params[:remove_user] - abuse_report.user.destroy - end - + abuse_report.remove_user(deleted_by: current_user) if params[:remove_user] abuse_report.destroy + render nothing: true end end diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb new file mode 100644 index 00000000000..26cf74e4849 --- /dev/null +++ b/app/controllers/admin/appearances_controller.rb @@ -0,0 +1,57 @@ +class Admin::AppearancesController < Admin::ApplicationController + before_action :set_appearance, except: :create + + def show + end + + def preview + end + + def create + @appearance = Appearance.new(appearance_params) + + if @appearance.save + redirect_to admin_appearances_path, notice: 'Appearance was successfully created.' + else + render action: 'show' + end + end + + def update + if @appearance.update(appearance_params) + redirect_to admin_appearances_path, notice: 'Appearance was successfully updated.' + else + render action: 'show' + end + end + + def logo + @appearance.remove_logo! + + @appearance.save + + redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.' + end + + def header_logos + @appearance.remove_header_logo! + @appearance.save + + redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.' + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_appearance + @appearance = Appearance.last || Appearance.new + end + + # Only allow a trusted parameter "white list" through. + def appearance_params + params.require(:appearance).permit( + :title, :description, :logo, :logo_cache, :header_logo, :header_logo_cache, + :updated_by + ) + end +end diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 10e736fd362..04a99d8c84a 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -70,14 +70,18 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_enabled, :metrics_host, :metrics_port, - :metrics_username, - :metrics_password, :metrics_pool_size, :metrics_timeout, :metrics_method_call_threshold, + :metrics_sample_interval, :recaptcha_enabled, :recaptcha_site_key, :recaptcha_private_key, + :sentry_enabled, + :sentry_dsn, + :akismet_enabled, + :akismet_api_key, + :email_author_in_body, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 497c34f8f49..fc342924987 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -1,8 +1,12 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController - before_action :broadcast_messages + before_action :finder, only: [:edit, :update, :destroy] def index - @broadcast_message = BroadcastMessage.new + @broadcast_messages = BroadcastMessage.reorder("ends_at DESC").page(params[:page]) + @broadcast_message = BroadcastMessage.new + end + + def edit end def create @@ -15,8 +19,16 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController end end + def update + if @broadcast_message.update(broadcast_message_params) + redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully updated.' + else + render :edit + end + end + def destroy - BroadcastMessage.find(params[:id]).destroy + @broadcast_message.destroy respond_to do |format| format.html { redirect_back_or_default(default: { action: 'index' }) } @@ -24,16 +36,23 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController end end + def preview + @message = broadcast_message_params[:message] + end + protected - def broadcast_messages - @broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page]) + def finder + @broadcast_message = BroadcastMessage.find(params[:id]) end def broadcast_message_params - params.require(:broadcast_message).permit( - :alert_type, :color, :ends_at, :font, - :message, :starts_at - ) + params.require(:broadcast_message).permit(%i( + color + ends_at + font + message + starts_at + )) end end diff --git a/app/controllers/admin/builds_controller.rb b/app/controllers/admin/builds_controller.rb index 83d9684c706..0db91eaaf2e 100644 --- a/app/controllers/admin/builds_controller.rb +++ b/app/controllers/admin/builds_controller.rb @@ -5,12 +5,12 @@ class Admin::BuildsController < Admin::ApplicationController @builds = @all_builds.order('created_at DESC') @builds = case @scope - when 'all' - @builds + when 'running' + @builds.running_or_pending.reverse_order when 'finished' @builds.finished else - @builds.running_or_pending.reverse_order + @builds end @builds = @builds.page(params[:page]).per(30) end diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 4d3e48f7f81..668396a0f20 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -55,7 +55,7 @@ class Admin::GroupsController < Admin::ApplicationController private def group - @group = Group.find_by(path: params[:id]) + @group ||= Group.find_by(path: params[:id]) end def group_params diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb index e383fe38ea6..79a53556f0a 100644 --- a/app/controllers/admin/identities_controller.rb +++ b/app/controllers/admin/identities_controller.rb @@ -26,6 +26,7 @@ class Admin::IdentitiesController < Admin::ApplicationController def update if @identity.update_attributes(identity_params) + RepairLdapBlockedUserService.new(@user).execute redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.' else render :edit @@ -34,6 +35,7 @@ class Admin::IdentitiesController < Admin::ApplicationController def destroy if @identity.destroy + RepairLdapBlockedUserService.new(@user).execute redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully removed.' else redirect_to admin_user_identities_path(@user), alert: 'Failed to remove user identity.' diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb index 3b070e65d0d..d79ce2b10fe 100644 --- a/app/controllers/admin/labels_controller.rb +++ b/app/controllers/admin/labels_controller.rb @@ -53,6 +53,6 @@ class Admin::LabelsController < Admin::ApplicationController end def label_params - params[:label].permit(:title, :color) + params[:label].permit(:title, :description, :color) end end diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb new file mode 100644 index 00000000000..377e9741e5f --- /dev/null +++ b/app/controllers/admin/spam_logs_controller.rb @@ -0,0 +1,17 @@ +class Admin::SpamLogsController < Admin::ApplicationController + def index + @spam_logs = SpamLog.order(id: :desc).page(params[:page]) + end + + def destroy + spam_log = SpamLog.find(params[:id]) + + if params[:remove_user] + spam_log.remove_user + redirect_to admin_spam_logs_path, notice: "User #{spam_log.user.username} was successfully removed." + else + spam_log.destroy + render nothing: true + end + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index d7c927d444c..9abf08d0e19 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -40,7 +40,9 @@ class Admin::UsersController < Admin::ApplicationController end def unblock - if user.activate + if user.ldap_blocked? + redirect_back_or_admin_user(alert: "This user cannot be unlocked manually from GitLab") + elsif user.activate redirect_back_or_admin_user(notice: "Successfully unblocked") else redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked") @@ -117,10 +119,10 @@ class Admin::UsersController < Admin::ApplicationController end def destroy - DeleteUserService.new(current_user).execute(user) + DeleteUserWorker.perform_async(current_user.id, user.id) respond_to do |format| - format.html { redirect_to admin_users_path } + format.html { redirect_to admin_users_path, notice: "The user is being deleted." } format.json { head :ok } end end @@ -148,7 +150,7 @@ class Admin::UsersController < Admin::ApplicationController :email, :remember_me, :bio, :name, :username, :skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password, :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key, :hide_no_password, - :projects_limit, :can_create_group, :admin, :key_id + :projects_limit, :can_create_group, :admin, :key_id, :external ) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d9a37a4d45f..1f55b18e0b1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,6 +15,7 @@ class ApplicationController < ActionController::Base before_action :check_password_expiration before_action :check_2fa_requirement before_action :ldap_security_check + before_action :sentry_user_context before_action :default_headers before_action :add_gon_variables before_action :configure_permitted_parameters, if: :devise_controller? @@ -24,6 +25,7 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? + helper_method :repository, :can_collaborate_with_project? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -41,6 +43,16 @@ class ApplicationController < ActionController::Base protected + def sentry_user_context + if Rails.env.production? && current_application_settings.sentry_enabled && current_user + Raven.user_context( + id: current_user.id, + email: current_user.email, + username: current_user.username, + ) + end + end + # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example # https://gist.github.com/josevalim/fb706b1e933ef01e4fb6 def authenticate_user_from_token! @@ -48,6 +60,8 @@ class ApplicationController < ActionController::Base params[:authenticity_token].presence elsif params[:private_token].presence params[:private_token].presence + elsif request.headers['PRIVATE-TOKEN'].present? + request.headers['PRIVATE-TOKEN'] end user = user_token && User.find_by_authentication_token(user_token.to_s) @@ -115,7 +129,7 @@ class ApplicationController < ActionController::Base # localhost/group/project # if id =~ /\.git\Z/ - redirect_to request.original_url.gsub(/\.git\Z/, '') and return + redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return end project_path = "#{namespace}/#{id}" @@ -150,7 +164,7 @@ class ApplicationController < ActionController::Base end def git_not_found! - render html: "errors/git_not_found", layout: "errors", status: 404 + render "errors/git_not_found.html", layout: "errors", status: 404 end def method_missing(method_sym, *arguments, &block) @@ -232,6 +246,8 @@ class ApplicationController < ActionController::Base def ldap_security_check if current_user && current_user.requires_ldap_check? + return unless current_user.try_obtain_ldap_lease + unless Gitlab::LDAP::Access.allowed?(current_user) sign_out current_user flash[:alert] = "Access denied for your LDAP account." @@ -263,9 +279,10 @@ class ApplicationController < ActionController::Base } end - def view_to_html_string(partial) + def view_to_html_string(partial, locals = {}) render_to_string( partial, + locals: locals, layout: false, formats: [:html] ) @@ -286,7 +303,8 @@ class ApplicationController < ActionController::Base end def set_filters_params - params[:sort] ||= 'created_desc' + set_default_sort + params[:scope] = 'all' if params[:scope].blank? params[:state] = 'opened' if params[:state].blank? @@ -393,4 +411,31 @@ class ApplicationController < ActionController::Base current_user.nil? && root_path == request.path end + + def can_collaborate_with_project?(project = nil) + project ||= @project + + can?(current_user, :push_code, project) || + (current_user && current_user.already_forked?(project)) + end + + private + + def set_default_sort + key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests') + 'issuable_sort' + end + + cookies[key] = params[:sort] if key && params[:sort].present? + params[:sort] = cookies[key] if key + params[:sort] ||= 'id_desc' + end + + def is_a_listing_page_for?(page_type) + controller_name, action_name = params.values_at(:controller, :action) + + (controller_name == "projects/#{page_type}" && action_name == 'index') || + (controller_name == 'groups' && action_name == page_type) || + (controller_name == 'dashboard' && action_name == page_type) + end end diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index c420b59c3a2..5bb7d499cdc 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -3,52 +3,5 @@ module Ci def self.railtie_helpers_paths "app/helpers/ci" end - - private - - def authorize_access_project! - unless can?(current_user, :read_project, project) - return page_404 - end - end - - def authorize_manage_builds! - unless can?(current_user, :manage_builds, project) - return page_404 - end - end - - def authenticate_admin! - return render_404 unless current_user.is_admin? - end - - def authorize_manage_project! - unless can?(current_user, :admin_project, project) - return page_404 - end - end - - def page_404 - render file: "#{Rails.root}/public/404.html", status: 404, layout: false - end - - def default_headers - headers['X-Frame-Options'] = 'DENY' - headers['X-XSS-Protection'] = '1; mode=block' - end - - # JSON for infinite scroll via Pager object - def pager_json(partial, count) - html = render_to_string( - partial, - layout: false, - formats: [:html] - ) - - render json: { - html: html, - count: count - } - end end end diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index e782a51e7eb..a7af3cb8345 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -6,11 +6,13 @@ module Ci end def create - if params[:content].blank? + @content = params[:content] + + if @content.blank? @status = false @error = "Please provide content of .gitlab-ci.yml" else - @config_processor = Ci::GitlabCiYamlProcessor.new params[:content] + @config_processor = Ci::GitlabCiYamlProcessor.new(@content) @stages = @config_processor.stages @builds = @config_processor.builds @status = true diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 3004c2d27f0..081e01a75e0 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,9 +1,9 @@ module Ci class ProjectsController < Ci::ApplicationController - before_action :project, except: [:index] - before_action :authenticate_user!, except: [:index, :build, :badge] - before_action :authorize_access_project!, except: [:index, :badge] + before_action :project + before_action :authorize_read_project!, except: [:badge] before_action :no_cache, only: [:badge] + skip_before_action :authenticate_user!, only: [:badge] protect_from_forgery def show @@ -13,9 +13,14 @@ module Ci # Project status badge # Image with build status for sha or ref + # + # This action in DEPRECATED, this is here only for backwards compatibility + # with projects migrated from GitLab CI. + # def badge - image = Ci::ImageForBuildService.new.execute(@project, params) + return render_404 unless @project + image = Ci::ImageForBuildService.new.execute(@project, params) send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" end diff --git a/app/controllers/concerns/continue_params.rb b/app/controllers/concerns/continue_params.rb new file mode 100644 index 00000000000..0a995c45bdf --- /dev/null +++ b/app/controllers/concerns/continue_params.rb @@ -0,0 +1,13 @@ +module ContinueParams + extend ActiveSupport::Concern + + def continue_params + continue_params = params[:continue] + return nil unless continue_params + + continue_params = continue_params.permit(:to, :notice, :notice_now) + return unless continue_params[:to] && continue_params[:to].start_with?('/') + + continue_params + end +end diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index 62127a09081..787416c17ab 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -13,17 +13,11 @@ module CreatesCommit result = service.new(@tree_edit_project, current_user, commit_params).execute if result[:status] == :success - flash[:notice] = success_notice || "Your changes have been successfully committed." - - if create_merge_request? - success_path = new_merge_request_path - target = different_project? ? "project" : "branch" - flash[:notice] << " You can now submit a merge request to get this change into the original #{target}." - end + update_flash_notice(success_notice) respond_to do |format| - format.html { redirect_to success_path } - format.json { render json: { message: "success", filePath: success_path } } + format.html { redirect_to final_success_path(success_path) } + format.json { render json: { message: "success", filePath: final_success_path(success_path) } } end else flash[:alert] = result[:message] @@ -41,14 +35,32 @@ module CreatesCommit end def authorize_edit_tree! - return if can?(current_user, :push_code, project) - return if current_user && current_user.already_forked?(project) + return if can_collaborate_with_project? access_denied! end private + def update_flash_notice(success_notice) + flash[:notice] = success_notice || "Your changes have been successfully committed." + + if create_merge_request? + if merge_request_exists? + flash[:notice] = nil + else + target = different_project? ? "project" : "branch" + flash[:notice] << " You can now submit a merge request to get this change into the original #{target}." + end + end + end + + def final_success_path(success_path) + return success_path unless create_merge_request? + + merge_request_exists? ? existing_merge_request_path : new_merge_request_path + end + def new_merge_request_path new_namespace_project_merge_request_path( @mr_source_project.namespace, @@ -62,6 +74,19 @@ module CreatesCommit ) end + def existing_merge_request_path + namespace_project_merge_request_path(@mr_target_project.namespace, @mr_target_project, @merge_request) + end + + def merge_request_exists? + return @merge_request if defined?(@merge_request) + + @merge_request = @mr_target_project.merge_requests.opened.find_by( + source_branch: @mr_source_branch, + target_branch: @mr_target_branch + ) + end + def different_project? @mr_source_project != @mr_target_project end @@ -75,7 +100,7 @@ module CreatesCommit end def set_commit_variables - @mr_source_branch = @target_branch + @mr_source_branch ||= @target_branch if can?(current_user, :push_code, @project) # Edit file in this project @@ -89,7 +114,7 @@ module CreatesCommit else # Merge request to this project @mr_target_project = @project - @mr_target_branch = @ref + @mr_target_branch ||= @ref end else # Edit file in fork @@ -97,7 +122,7 @@ module CreatesCommit # Merge request from fork to this project @mr_source_project = @tree_edit_project @mr_target_project = @project - @mr_target_branch = @mr_target_project.repository.root_ref + @mr_target_branch ||= @ref end end end diff --git a/app/controllers/concerns/filter_projects.rb b/app/controllers/concerns/filter_projects.rb new file mode 100644 index 00000000000..f63b703d101 --- /dev/null +++ b/app/controllers/concerns/filter_projects.rb @@ -0,0 +1,15 @@ +# == FilterProjects +# +# Controller concern to handle projects filtering +# * by name +# * by archived state +# +module FilterProjects + extend ActiveSupport::Concern + + def filter_projects(projects) + projects = projects.search(params[:filter_projects]) if params[:filter_projects].present? + projects = projects.non_archived if params[:archived].blank? + projects + end +end diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index effd4721949..ef8e74a4641 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -2,10 +2,12 @@ module IssuesAction extend ActiveSupport::Concern def issues - @issues = get_issues_collection + @issues = get_issues_collection.non_archived @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE) @issues = @issues.preload(:author, :project) + @label = @issuable_finder.labels.first + respond_to do |format| format.html format.atom { render layout: false } diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index f7a25111db9..9c49596bd0b 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -2,8 +2,10 @@ module MergeRequestsAction extend ActiveSupport::Concern def merge_requests - @merge_requests = get_merge_requests_collection + @merge_requests = get_merge_requests_collection.non_archived @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE) @merge_requests = @merge_requests.preload(:author, :target_project) + + @label = @issuable_finder.labels.first end end diff --git a/app/controllers/concerns/toggle_subscription_action.rb b/app/controllers/concerns/toggle_subscription_action.rb new file mode 100644 index 00000000000..8a43c0b93c4 --- /dev/null +++ b/app/controllers/concerns/toggle_subscription_action.rb @@ -0,0 +1,17 @@ +module ToggleSubscriptionAction + extend ActiveSupport::Concern + + def toggle_subscription + return unless current_user + + subscribable_resource.toggle_subscription(current_user) + + render nothing: true + end + + private + + def subscribable_resource + raise NotImplementedError + end +end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 58e9049f158..0e8b63872ca 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,9 +1,15 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController + include FilterProjects + before_action :event_filter def index - @projects = current_user.authorized_projects.sorted_by_activity.non_archived + @projects = current_user.authorized_projects.sorted_by_activity + @projects = filter_projects(@projects) @projects = @projects.includes(:namespace) + @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]).per(PER_PAGE) + @last_push = current_user.recent_push respond_to do |format| @@ -13,13 +19,21 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController load_events render layout: false end + format.json do + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } + end end end def starred - @projects = current_user.starred_projects + @projects = current_user.starred_projects.sorted_by_activity + @projects = filter_projects(@projects) @projects = @projects.includes(:namespace, :forked_from_project, :tags) @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]).per(PER_PAGE) + @last_push = current_user.recent_push @groups = [] @@ -27,8 +41,9 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController format.html format.json do - load_events - pager_json("events/_events", @events.count) + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } end end end @@ -36,7 +51,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController private def load_events - @events = Event.in_projects(@projects.pluck(:id)) + @events = Event.in_projects(@projects) @events = @event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb new file mode 100644 index 00000000000..7857af9c5de --- /dev/null +++ b/app/controllers/dashboard/todos_controller.rb @@ -0,0 +1,44 @@ +class Dashboard::TodosController < Dashboard::ApplicationController + before_action :find_todos, only: [:index, :destroy, :destroy_all] + + def index + @todos = @todos.page(params[:page]).per(PER_PAGE) + end + + def destroy + todo.done! + + todo_notice = 'Todo was successfully marked as done.' + + respond_to do |format| + format.html { redirect_to dashboard_todos_path, notice: todo_notice } + format.js { render nothing: true } + format.json do + render json: { count: @todos.size, done_count: current_user.todos.done.count } + end + end + end + + def destroy_all + @todos.each(&:done!) + + respond_to do |format| + format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' } + format.js { render nothing: true } + format.json do + find_todos + render json: { count: @todos.size, done_count: current_user.todos.done.count } + end + end + end + + private + + def todo + @todo ||= current_user.todos.find(params[:id]) + end + + def find_todos + @todos = TodosFinder.new(current_user, params).execute + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 087da935087..139e40db180 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -23,14 +23,14 @@ class DashboardController < Dashboard::ApplicationController protected def load_events - project_ids = + projects = if params[:filter] == "starred" current_user.starred_projects else current_user.authorized_projects - end.pluck(:id) + end - @events = Event.in_projects(project_ids) + @events = Event.in_projects(projects) @events = @event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/controllers/emojis_controller.rb b/app/controllers/emojis_controller.rb new file mode 100644 index 00000000000..1bec5a7d27f --- /dev/null +++ b/app/controllers/emojis_controller.rb @@ -0,0 +1,6 @@ +class EmojisController < ApplicationController + layout false + + def index + end +end diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 9575a87ee41..a9bf4321f73 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,6 +1,6 @@ class Explore::GroupsController < Explore::ApplicationController def index - @groups = GroupsFinder.new.execute(current_user) + @groups = Group.order_id_desc @groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index a5aeaed66c5..8271ca87436 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,24 +1,53 @@ class Explore::ProjectsController < Explore::ApplicationController + include FilterProjects + def index @projects = ProjectsFinder.new.execute(current_user) @tags = @projects.tags_on(:tags) @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? - @projects = @projects.non_archived - @projects = @projects.search(params[:search]) if params[:search].present? + @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + format.json do + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } + end + end end def trending - @trending_projects = TrendingProjectsFinder.new.execute(current_user) - @trending_projects = @trending_projects.non_archived - @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE) + @projects = TrendingProjectsFinder.new.execute(current_user) + @projects = filter_projects(@projects) + @projects = @projects.page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + format.json do + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } + end + end end def starred - @starred_projects = ProjectsFinder.new.execute(current_user) - @starred_projects = @starred_projects.reorder('star_count DESC') - @starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE) + @projects = ProjectsFinder.new.execute(current_user) + @projects = filter_projects(@projects) + @projects = @projects.reorder('star_count DESC') + @projects = @projects.page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + format.json do + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } + end + end end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index fb26a4e6fc3..06c5c8be9a5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,19 +1,21 @@ class GroupsController < Groups::ApplicationController + include FilterProjects include IssuesAction include MergeRequestsAction - skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests] respond_to :html - before_action :group, except: [:new, :create] + + skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests] + before_action :group, except: [:index, :new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete] - before_action :event_filter, only: :show + before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete] + before_action :event_filter, only: [:activity] layout :determine_layout @@ -40,13 +42,19 @@ class GroupsController < Groups::ApplicationController def show @last_push = current_user.recent_push if current_user @projects = @projects.includes(:namespace) + @projects = filter_projects(@projects) + @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? + + @shared_projects = @group.shared_projects respond_to do |format| format.html format.json do - load_events - pager_json("events/_events", @events.count) + render json: { + html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) + } end format.atom do @@ -56,6 +64,17 @@ class GroupsController < Groups::ApplicationController end end + def activity + respond_to do |format| + format.html + + format.json do + load_events + pager_json("events/_events", @events.count) + end + end + end + def edit end @@ -81,14 +100,11 @@ class GroupsController < Groups::ApplicationController def group @group ||= Group.find_by(path: params[:id]) + @group || render_404 end def load_projects - @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived - end - - def project_ids - @projects.pluck(:id) + @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity end # Dont allow unauthorized access to group @@ -119,11 +135,11 @@ class GroupsController < Groups::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar, :public) + params.require(:group).permit(:name, :description, :path, :avatar, :public, :share_with_group_lock) end def load_events - @events = Event.in_projects(project_ids) + @events = Event.in_projects(@projects) @events = event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index dc22101cd5e..d1e4ac10f6c 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -8,7 +8,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController layout 'profile' def index - head :forbidden and return + set_index_vars end def create @@ -20,18 +20,11 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) redirect_to oauth_application_url(@application) else - render :new + set_index_vars + render :index end end - def destroy - if @application.destroy - flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy]) - end - - redirect_to applications_profile_url - end - private def verify_user_oauth_applications_enabled @@ -40,6 +33,17 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController redirect_to applications_profile_url end + def set_index_vars + @applications = current_user.oauth_applications + @authorized_tokens = current_user.oauth_authorized_tokens + @authorized_anonymous_tokens = @authorized_tokens.reject(&:application) + @authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?) + + # Don't overwrite a value possibly set by `create` + @application ||= Doorkeeper::Application.new + end + + # Override Doorkeeper to scope to the current user def set_application @application = current_user.oauth_applications.find(params[:id]) end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 4cad98b8e98..21135f7d607 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,4 +1,5 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController + include AuthenticatesWithTwoFactor protect_from_forgery except: [:kerberos, :saml, :cas3] @@ -21,21 +22,46 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController # We only find ourselves here # if the authentication to LDAP was successful. def ldap - @user = Gitlab::LDAP::User.new(oauth) - @user.save if @user.changed? # will also save new users - gl_user = @user.gl_user - gl_user.remember_me = params[:remember_me] if @user.persisted? + ldap_user = Gitlab::LDAP::User.new(oauth) + ldap_user.save if ldap_user.changed? # will also save new users + + @user = ldap_user.gl_user + @user.remember_me = params[:remember_me] if ldap_user.persisted? # Do additional LDAP checks for the user filter and EE features - if @user.allowed? - log_audit_event(gl_user, with: :ldap) - sign_in_and_redirect(gl_user) + if ldap_user.allowed? + if @user.two_factor_enabled? + prompt_for_two_factor(@user) + else + log_audit_event(@user, with: :ldap) + sign_in_and_redirect(@user) + end else flash[:alert] = "Access denied for your LDAP account." redirect_to new_user_session_path end end + def saml + if current_user + log_audit_event(current_user, with: :saml) + # Update SAML identity if data has changed. + identity = current_user.identities.find_by(extern_uid: oauth['uid'], provider: :saml) + if identity.nil? + current_user.identities.create(extern_uid: oauth['uid'], provider: :saml) + redirect_to profile_account_path, notice: 'Authentication method updated' + else + redirect_to after_sign_in_path_for(current_user) + end + else + saml_user = Gitlab::Saml::User.new(oauth) + saml_user.save + @user = saml_user.gl_user + + continue_login_process + end + end + def omniauth_error @provider = params[:provider] @error = params[:error] @@ -59,25 +85,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController log_audit_event(current_user, with: oauth['provider']) redirect_to profile_account_path, notice: 'Authentication method updated' else - @user = Gitlab::OAuth::User.new(oauth) - @user.save + oauth_user = Gitlab::OAuth::User.new(oauth) + oauth_user.save + @user = oauth_user.gl_user - # Only allow properly saved users to login. - if @user.persisted? && @user.valid? - log_audit_event(@user.gl_user, with: oauth['provider']) - sign_in_and_redirect(@user.gl_user) - else - 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 + continue_login_process end rescue Gitlab::OAuth::SignupDisabledError label = Gitlab::OAuth::Provider.label_for(oauth['provider']) @@ -98,6 +110,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController session[:service_tickets][provider] = ticket end + def continue_login_process + # Only allow properly saved users to login. + if @user.persisted? && @user.valid? + log_audit_event(@user, with: oauth['provider']) + sign_in_and_redirect(@user) + else + error_message = @user.errors.full_messages.to_sentence + + redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return + end + end + def oauth @oauth ||= request.env['omniauth.auth'] end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index f74daff3bd0..a8575e037e4 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -23,6 +23,14 @@ class PasswordsController < Devise::PasswordsController end end + def update + super do |resource| + if resource.valid? && resource.require_password? + resource.update_attribute(:password_automatically_set, false) + end + end + end + protected def resource_from_email diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index f3224148fda..b88c080352b 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -3,23 +3,21 @@ class Profiles::KeysController < Profiles::ApplicationController def index @keys = current_user.keys + @key = Key.new end def show @key = current_user.keys.find(params[:id]) end - def new - @key = current_user.keys.new - end - def create @key = current_user.keys.new(key_params) if @key.save redirect_to profile_key_path(@key) else - render 'new' + @keys = current_user.keys.select(&:persisted?) + render :index end end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 6e91d9b4ad9..8f83fdd02bc 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -12,11 +12,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController current_user.save! if current_user.changed? - if two_factor_grace_period_expired? - flash.now[:alert] = 'You must configure Two-Factor Authentication in your account.' - else - grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours - flash.now[:alert] = "You must configure Two-Factor Authentication in your account until #{l(grace_period_deadline)}." + if two_factor_authentication_required? + if two_factor_grace_period_expired? + flash.now[:alert] = 'You must enable Two-factor Authentication for your account.' + else + grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours + flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}." + end end @qr_code = build_qr_code diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 28803164fcf..32fca6b838e 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -8,13 +8,6 @@ class ProfilesController < Profiles::ApplicationController def show end - def applications - @applications = current_user.oauth_applications - @authorized_tokens = current_user.oauth_authorized_tokens - @authorized_anonymous_tokens = @authorized_tokens.reject(&:application) - @authorized_apps = @authorized_tokens.map(&:application).uniq - [nil] - end - def update user_params.except!(:email) if @user.ldap_user? diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index dd32d509191..a326bc58215 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -28,6 +28,11 @@ class Projects::ApplicationController < ApplicationController private + def apply_diff_view_cookie! + view = params[:view] || cookies[:diff_view] + cookies.permanent[:diff_view] = params[:view] = view if view + end + def builds_enabled return render_404 unless @project.builds_enabled? end diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb new file mode 100644 index 00000000000..cfea1266516 --- /dev/null +++ b/app/controllers/projects/artifacts_controller.rb @@ -0,0 +1,46 @@ +class Projects::ArtifactsController < Projects::ApplicationController + layout 'project' + before_action :authorize_read_build! + + def download + unless artifacts_file.file_storage? + return redirect_to artifacts_file.url + end + + unless artifacts_file.exists? + return render_404 + end + + send_file artifacts_file.path, disposition: 'attachment' + end + + def browse + return render_404 unless build.artifacts? + + directory = params[:path] ? "#{params[:path]}/" : '' + @entry = build.artifacts_metadata_entry(directory) + + return render_404 unless @entry.exists? + end + + def file + entry = build.artifacts_metadata_entry(params[:path]) + + if entry.exists? + render json: { archive: build.artifacts_file.path, + entry: Base64.encode64(entry.path) } + else + render json: {}, status: 404 + end + end + + private + + def build + @build ||= project.builds.unscoped.find_by!(id: params[:build_id]) + end + + def artifacts_file + @artifacts_file ||= build.artifacts_file + end +end diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 548f1b9ebfe..a6bebc46b06 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -1,16 +1,19 @@ class Projects::AvatarsController < Projects::ApplicationController + include BlobHelper + before_action :project def show - @blob = @project.repository.blob_at_branch('master', @project.avatar_in_git) + @blob = @repository.blob_at_branch('master', @project.avatar_in_git) if @blob headers['X-Content-Type-Options'] = 'nosniff' - send_data( - @blob.data, - type: @blob.mime_type, - disposition: 'inline', - filename: @blob.name - ) + + return if cached_blob? + + headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) + headers['Content-Disposition'] = 'inline' + headers['Content-Type'] = safe_content_type(@blob) + head :ok # 'render nothing: true' messes up the Content-Type else render_404 end diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb new file mode 100644 index 00000000000..6ff47c4033a --- /dev/null +++ b/app/controllers/projects/badges_controller.rb @@ -0,0 +1,13 @@ +class Projects::BadgesController < Projects::ApplicationController + before_action :no_cache_headers + + def build + respond_to do |format| + format.html { render_404 } + format.svg do + image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref]) + send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml') + end + end + end +end diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 9ea518e6c85..f576d0be1fc 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -8,28 +8,6 @@ class Projects::BlameController < Projects::ApplicationController def show @blob = @repository.blob_at(@commit.id, @path) - @blame = group_blame_lines - end - - def group_blame_lines - blame = Gitlab::Git::Blame.new(@repository, @commit.id, @path) - - prev_sha = nil - groups = [] - current_group = nil - - blame.each do |commit, line| - if prev_sha && prev_sha == commit.sha - current_group[:lines] << line - else - groups << current_group if current_group.present? - current_group = { commit: commit, lines: [line] } - end - - prev_sha = commit.sha - end - - groups << current_group if current_group.present? - groups + @blame_groups = Gitlab::Blame.new(@blob, @commit).groups end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index c56a3497bb2..cd8b2911674 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -33,6 +33,7 @@ class Projects::BlobController < Projects::ApplicationController def edit @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha + blob.load_all_data!(@repository) end def update @@ -51,8 +52,11 @@ class Projects::BlobController < Projects::ApplicationController def preview @content = params[:content] + @blob.load_all_data!(@repository) diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true) - @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/)) + diff_lines = diffy.diff.scan(/.*\n/)[2..-1] + diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines) + @diff_lines = Gitlab::Diff::Highlight.new(diff_lines).highlight render layout: false end @@ -65,8 +69,9 @@ class Projects::BlobController < Projects::ApplicationController end def diff - @form = UnfoldForm.new(params) - @lines = @blob.data.lines[@form.since - 1..@form.to - 1] + @form = UnfoldForm.new(params) + @lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path) + @lines = @lines[@form.since - 1..@form.to - 1] if @form.bottom? @match_line = '' @@ -82,7 +87,7 @@ class Projects::BlobController < Projects::ApplicationController private def blob - @blob ||= @repository.blob_at(@commit.id, @path) + @blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path)) if @blob @blob diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3c2849a7601..43ea717cbd2 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -9,6 +9,11 @@ class Projects::BranchesController < Projects::ApplicationController @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) + + @max_commits = @branches.reduce(0) do |memo, branch| + diverging_commit_counts = repository.diverging_commit_counts(branch) + [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max + end end def recent @@ -18,11 +23,15 @@ class Projects::BranchesController < Projects::ApplicationController def create branch_name = sanitize(strip_tags(params[:branch_name])) branch_name = Addressable::URI.unescape(branch_name) - ref = sanitize(strip_tags(params[:ref])) - ref = Addressable::URI.unescape(ref) + result = CreateBranchService.new(project, current_user). execute(branch_name, ref) + if params[:issue_iid] + issue = @project.issues.find_by(iid: params[:issue_iid]) + SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue + end + if result[:status] == :success @branch = result[:branch] redirect_to namespace_project_tree_path(@project.namespace, @project, @@ -44,4 +53,15 @@ class Projects::BranchesController < Projects::ApplicationController format.js { render status: status[:return_code] } end end + + private + + def ref + if params[:ref] + ref_escaped = sanitize(strip_tags(params[:ref])) + Addressable::URI.unescape(ref_escaped) + else + @project.default_branch + end + end end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 26ba12520c7..f159e169f6d 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -1,10 +1,8 @@ class Projects::BuildsController < Projects::ApplicationController before_action :build, except: [:index, :cancel_all] - - before_action :authorize_manage_builds!, except: [:index, :show, :status] - before_action :authorize_download_build_artifacts!, only: [:download] - - layout "project" + before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry] + before_action :authorize_update_build!, except: [:index, :show, :status] + layout 'project' def index @scope = params[:scope] @@ -12,19 +10,18 @@ class Projects::BuildsController < Projects::ApplicationController @builds = @all_builds.order('created_at DESC') @builds = case @scope - when 'all' - @builds + when 'running' + @builds.running_or_pending.reverse_order when 'finished' @builds.finished else - @builds.running_or_pending.reverse_order + @builds end @builds = @builds.page(params[:page]).per(30) end def cancel_all @project.builds.running_or_pending.each(&:cancel) - redirect_to namespace_project_builds_path(project.namespace, project) end @@ -43,34 +40,26 @@ class Projects::BuildsController < Projects::ApplicationController def retry unless @build.retryable? - return page_404 + return render_404 end build = Ci::Build.retry(@build) - redirect_to build_path(build) end - def download - unless artifacts_file.file_storage? - return redirect_to artifacts_file.url - end - - unless artifacts_file.exists? - return not_found! - end - - send_file artifacts_file.path, disposition: 'attachment' + def cancel + @build.cancel + redirect_to build_path(@build) end def status render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) end - def cancel - @build.cancel - - redirect_to build_path(@build) + def erase + @build.erase(erased_by: current_user) + redirect_to namespace_project_build_path(project.namespace, project, @build), + notice: "Build has been sucessfully erased!" end private @@ -79,27 +68,7 @@ class Projects::BuildsController < Projects::ApplicationController @build ||= project.builds.unscoped.find_by!(id: params[:id]) end - def artifacts_file - build.artifacts_file - end - def build_path(build) namespace_project_build_path(build.project.namespace, build.project, build) end - - def authorize_manage_builds! - unless can?(current_user, :manage_builds, project) - return page_404 - end - end - - def authorize_download_build_artifacts! - unless can?(current_user, :download_build_artifacts, @project) - if current_user.nil? - return authenticate_user! - else - return render_404 - end - end - end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 0aaba3792bf..576fa3cedb2 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -2,16 +2,20 @@ # # Not to be confused with CommitsController, plural. class Projects::CommitController < Projects::ApplicationController + include CreatesCommit + include DiffHelper + # Authorize before_action :require_non_empty_project - before_action :authorize_download_code!, except: [:cancel_builds] - before_action :authorize_manage_builds!, only: [:cancel_builds] + before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds] + before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds] + before_action :authorize_read_commit_status!, only: [:builds] before_action :commit - before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds] before_action :define_show_vars, only: [:show, :builds] + before_action :authorize_edit_tree!, only: [:revert] def show - return git_not_found! unless @commit + apply_diff_view_cookie! @line_notes = commit.notes.inline @note = @project.build_commit_note(commit) @@ -55,8 +59,37 @@ class Projects::CommitController < Projects::ApplicationController render layout: false end + def revert + assign_revert_commit_vars + + return render_404 if @target_branch.blank? + + create_commit(Commits::RevertService, success_notice: "The #{revert_type_title} has been successfully reverted.", + success_path: successful_revert_path, failure_path: failed_revert_path) + end + private + def revert_type_title + @commit.merged_merge_request ? 'merge request' : 'commit' + end + + def successful_revert_path + return referenced_merge_request_url if @commit.merged_merge_request + + namespace_project_commits_url(@project.namespace, @project, @target_branch) + end + + def failed_revert_path + return referenced_merge_request_url if @commit.merged_merge_request + + namespace_project_commit_url(@project.namespace, @project, params[:id]) + end + + def referenced_merge_request_url + namespace_project_merge_request_url(@project.namespace, @project, @commit.merged_merge_request) + end + def commit @commit ||= @project.commit(params[:id]) end @@ -66,20 +99,27 @@ class Projects::CommitController < Projects::ApplicationController end def define_show_vars - if params[:w].to_i == 1 - @diffs = commit.diffs({ ignore_whitespace_change: true }) - else - @diffs = commit.diffs - end + return git_not_found! unless commit + + opts = diff_options + opts[:ignore_whitespace_change] = true if params[:format] == 'diff' + @diffs = commit.diffs(opts) + @diff_refs = [commit.parent || commit, commit] @notes_count = commit.notes.count @statuses = ci_commit.statuses if ci_commit end - def authorize_manage_builds! - unless can?(current_user, :manage_builds, project) - return page_404 - end + def assign_revert_commit_vars + @commit = project.commit(params[:id]) + @target_branch = params[:target_branch] + @mr_source_branch = @commit.revert_branch_name + @mr_target_branch = @target_branch + @commit_params = { + commit: @commit, + revert_type_title: revert_type_title, + create_merge_request: params[:create_merge_request].present? || different_project? + } end end diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 04a88990bf4..1420b96840c 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -8,13 +8,22 @@ class Projects::CommitsController < Projects::ApplicationController before_action :authorize_download_code! def show - @repo = @project.repository @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i + search = params[:search] + + @commits = + if search.present? + @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact + else + @repository.commits(@ref, @path, @limit, @offset) + end - @commits = @repo.commits(@ref, @path, @limit, @offset) @note_counts = project.notes.where(commit_id: @commits.map(&:id)). group(:commit_id).count + @merge_request = @project.merge_requests.opened. + find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref) + respond_to do |format| format.html format.json { pager_json("projects/commits/_commits", @commits.size) } diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 5200d609cc9..671d5c23024 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -1,27 +1,27 @@ require 'addressable/uri' class Projects::CompareController < Projects::ApplicationController + include DiffHelper + # Authorize before_action :require_non_empty_project before_action :authorize_download_code! + before_action :assign_ref_vars, only: [:index, :show] + before_action :merge_request, only: [:index, :show] def index - @ref = Addressable::URI.unescape(params[:to]) end def show - base_ref = Addressable::URI.unescape(params[:from]) - @ref = head_ref = Addressable::URI.unescape(params[:to]) - diff_options = { ignore_whitespace_change: true } if params[:w] == '1' - - compare_result = CompareService.new. - execute(@project, head_ref, @project, base_ref, diff_options) - - if compare_result - @commits = Commit.decorate(compare_result.commits, @project) - @diffs = compare_result.diffs - @commit = @project.commit(head_ref) - @first_commit = @project.commit(base_ref) + compare = CompareService.new. + execute(@project, @head_ref, @project, @base_ref, diff_options) + + if compare + @commits = Commit.decorate(compare.commits, @project) + @commit = @project.commit(@head_ref) + @base_commit = @project.merge_base_commit(@base_ref, @head_ref) + @diffs = compare.diffs(diff_options) + @diff_refs = [@base_commit, @commit] @line_notes = [] end end @@ -30,4 +30,16 @@ class Projects::CompareController < Projects::ApplicationController redirect_to namespace_project_compare_path(@project.namespace, @project, params[:from], params[:to]) end + + private + + def assign_ref_vars + @base_ref = Addressable::URI.unescape(params[:from]) + @ref = @head_ref = Addressable::URI.unescape(params[:to]) + end + + def merge_request + @merge_request ||= @project.merge_requests.opened. + find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref) + end end diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb new file mode 100644 index 00000000000..54a0c447aee --- /dev/null +++ b/app/controllers/projects/find_file_controller.rb @@ -0,0 +1,26 @@ +# Controller for viewing a repository's file structure
+class Projects::FindFileController < Projects::ApplicationController
+ include ExtractsPath
+ include ActionView::Helpers::SanitizeHelper
+ include TreeHelper
+
+ before_action :require_non_empty_project
+ before_action :assign_ref_vars
+ before_action :authorize_download_code!
+
+ def show
+ return render_404 unless @repository.commit(@ref)
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ def list
+ file_paths = @repo.ls_files(@ref)
+
+ respond_to do |format|
+ format.json { render json: file_paths }
+ end
+ end
+end
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 750181f0c19..a1b8632df98 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -1,8 +1,33 @@ class Projects::ForksController < Projects::ApplicationController + include ContinueParams + # Authorize before_action :require_non_empty_project before_action :authorize_download_code! + def index + base_query = project.forks.includes(:creator) + + @forks = base_query.merge(ProjectsFinder.new.execute(current_user)) + @total_forks_count = base_query.size + @private_forks_count = @total_forks_count - @forks.size + @public_forks_count = @total_forks_count - @private_forks_count + + @sort = params[:sort] || 'id_desc' + @forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present? + @forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + + format.json do + render json: { + html: view_to_html_string("projects/forks/_projects", projects: @forks) + } + end + end + end + def new @namespaces = current_user.manageable_namespaces @namespaces.delete(@project.namespace) @@ -10,7 +35,7 @@ class Projects::ForksController < Projects::ApplicationController def create namespace = Namespace.find(params[:namespace_key]) - + @forked_project = namespace.projects.find_by(path: project.path) @forked_project = nil unless @forked_project && @forked_project.forked_from_project == project @@ -23,22 +48,11 @@ class Projects::ForksController < Projects::ApplicationController if continue_params redirect_to continue_params[:to], notice: continue_params[:notice] else - redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked." + redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project '#{@forked_project.name}' was successfully forked." end end else render :error end end - - private - - def continue_params - continue_params = params[:continue] - if continue_params - continue_params.permit(:to, :notice, :notice_now) - else - nil - end - end end diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb new file mode 100644 index 00000000000..4159e53bfa9 --- /dev/null +++ b/app/controllers/projects/group_links_controller.rb @@ -0,0 +1,23 @@ +class Projects::GroupLinksController < Projects::ApplicationController + layout 'project_settings' + before_action :authorize_admin_project! + + def index + @group_links = project.project_group_links.all + end + + def create + link = project.project_group_links.new + link.group_id = params[:link_group_id] + link.group_access = params[:link_group_access] + link.save + + redirect_to namespace_project_group_links_path(project.namespace, project) + end + + def destroy + project.project_group_links.find(params[:id]).destroy + + redirect_to namespace_project_group_links_path(project.namespace, project) + end +end diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 8d8035ef5ff..7756f0f0ed3 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -1,8 +1,11 @@ class Projects::ImportsController < Projects::ApplicationController + include ContinueParams + # Authorize before_action :authorize_admin_project! - before_action :require_no_repo, except: :show - before_action :redirect_if_progress, except: :show + before_action :require_no_repo, only: [:new, :create] + before_action :redirect_if_progress, only: [:new, :create] + before_action :redirect_if_no_import, only: :show def new end @@ -24,11 +27,11 @@ class Projects::ImportsController < Projects::ApplicationController end def show - if @project.repository_exists? || @project.import_finished? + if @project.import_finished? if continue_params redirect_to continue_params[:to], notice: continue_params[:notice] else - redirect_to project_path(@project), notice: "The project was successfully forked." + redirect_to namespace_project_path(@project.namespace, @project), notice: finished_notice end elsif @project.import_failed? redirect_to new_namespace_project_import_path(@project.namespace, @project) @@ -36,31 +39,36 @@ class Projects::ImportsController < Projects::ApplicationController if continue_params && continue_params[:notice_now] flash.now[:notice] = continue_params[:notice_now] end + # Render end end private - def continue_params - continue_params = params[:continue] - if continue_params - continue_params.permit(:to, :notice, :notice_now) + def finished_notice + if @project.forked? + 'The project was successfully forked.' else - nil + 'The project was successfully imported.' end end def require_no_repo - if @project.repository_exists? && !@project.import_in_progress? - redirect_to(namespace_project_path(@project.namespace, @project)) + if @project.repository_exists? + redirect_to namespace_project_path(@project.namespace, @project) end end def redirect_if_progress if @project.import_in_progress? - redirect_to namespace_project_import_path(@project.namespace, @project) && - return + redirect_to namespace_project_import_path(@project.namespace, @project) + end + end + + def redirect_if_no_import + if @project.repository_exists? && @project.no_import? + redirect_to namespace_project_path(@project.namespace, @project) end end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b59b52291fb..6603f28a082 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -1,9 +1,11 @@ class Projects::IssuesController < Projects::ApplicationController + include ToggleSubscriptionAction + before_action :module_enabled - before_action :issue, only: [:edit, :update, :show, :toggle_subscription] + before_action :issue, only: [:edit, :update, :show] # Allow read any issue - before_action :authorize_read_issue! + before_action :authorize_read_issue!, only: [:show] # Allow write(create) issue before_action :authorize_create_issue!, only: [:new, :create] @@ -32,6 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController end @issues = @issues.page(params[:page]).per(PER_PAGE) + @label = @project.labels.find_by(title: params[:label_name]) respond_to do |format| format.html @@ -49,7 +52,7 @@ class Projects::IssuesController < Projects::ApplicationController assignee_id: "" ) - @issue = @project.issues.new(issue_params) + @issue = @noteable = @project.issues.new(issue_params) respond_with(@issue) end @@ -61,7 +64,8 @@ class Projects::IssuesController < Projects::ApplicationController @note = @project.notes.new(noteable: @issue) @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue - @merge_requests = @issue.referenced_merge_requests + @merge_requests = @issue.referenced_merge_requests(current_user) + @related_branches = @issue.related_branches - @merge_requests.map(&:source_branch) respond_with(@issue) end @@ -109,12 +113,6 @@ class Projects::IssuesController < Projects::ApplicationController redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" }) end - def toggle_subscription - @issue.toggle_subscription(current_user) - - render nothing: true - end - def closed_by_merge_requests @closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user) end @@ -128,6 +126,11 @@ class Projects::IssuesController < Projects::ApplicationController redirect_old end end + alias_method :subscribable_resource, :issue + + def authorize_read_issue! + return render_404 unless can?(current_user, :read_issue, @issue) + end def authorize_update_issue! return render_404 unless can?(current_user, :update_issue, @issue) @@ -159,7 +162,7 @@ class Projects::IssuesController < Projects::ApplicationController def issue_params params.require(:issue).permit( - :title, :assignee_id, :position, :description, + :title, :assignee_id, :position, :description, :confidential, :milestone_id, :state_event, :task_num, label_ids: [] ) end diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 86d6e3e0f6b..40d8098690a 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -1,8 +1,12 @@ class Projects::LabelsController < Projects::ApplicationController + include ToggleSubscriptionAction + before_action :module_enabled before_action :label, only: [:edit, :update, :destroy] before_action :authorize_read_label! - before_action :authorize_admin_labels!, except: [:index] + before_action :authorize_admin_labels!, only: [ + :new, :create, :edit, :update, :generate, :destroy + ] respond_to :js, :html @@ -69,12 +73,13 @@ class Projects::LabelsController < Projects::ApplicationController end def label_params - params.require(:label).permit(:title, :color) + params.require(:label).permit(:title, :description, :color) end def label - @label = @project.labels.find(params[:id]) + @label ||= @project.labels.find(params[:id]) end + alias_method :subscribable_resource, :label def authorize_admin_labels! return render_404 unless can?(current_user, :admin_label, @project) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index ab5c953189c..61b82c9db46 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -1,8 +1,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController + include ToggleSubscriptionAction + include DiffHelper + before_action :module_enabled before_action :merge_request, only: [ :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check, - :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds + :ci_status, :cancel_merge_when_build_succeeds ] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] @@ -34,6 +37,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.preload(:target_project) + @label = @project.labels.find_by(title: params[:label_name]) + respond_to do |format| format.html format.json do @@ -57,8 +62,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def diffs + apply_diff_view_cookie! + @commit = @merge_request.last_commit - @first_commit = @merge_request.first_commit + @base_commit = @merge_request.diff_base_commit + + # MRs created before 8.4 don't have a diff_base_commit, + # but we need it for the "View file @ ..." link by deleted files + @base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit @comments_allowed = @reply_allowed = true @comments_target = { @@ -90,6 +101,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController def new params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute + @noteable = @merge_request @target_branches = if @merge_request.target_project @merge_request.target_project.repository.branch_names @@ -101,8 +113,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController @source_project = merge_request.source_project @commits = @merge_request.compare_commits.reverse @commit = @merge_request.last_commit - @first_commit = @merge_request.first_commit - @diffs = @merge_request.compare_diffs + @base_commit = @merge_request.diff_base_commit + @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare @ci_commit = @merge_request.ci_commit @statuses = @ci_commit.statuses if @ci_commit @@ -153,7 +165,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_check - @merge_request.check_if_can_be_merged if @merge_request.unchecked? + @merge_request.check_if_can_be_merged render partial: "projects/merge_requests/widget/show.html.haml", layout: false end @@ -172,6 +184,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController return end + TodoService.new.merge_merge_request(merge_request, current_user) + @merge_request.update(merge_error: nil) if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active? @@ -220,12 +234,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController render json: response end - def toggle_subscription - @merge_request.toggle_subscription(current_user) - - render nothing: true - end - protected def selected_target_project @@ -239,6 +247,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController def merge_request @merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) end + alias_method :subscribable_resource, :merge_request def closes_issues @closes_issues ||= @merge_request.closes_issues diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 15506bd677a..da46731d945 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -11,11 +11,12 @@ class Projects::MilestonesController < Projects::ApplicationController respond_to :html def index - @milestones = case params[:state] - when 'all'; @project.milestones.order("state, due_date DESC") - when 'closed'; @project.milestones.closed.order("due_date DESC") - else @project.milestones.active.order("due_date ASC") - end + @milestones = + case params[:state] + when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc) + when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc) + else @project.milestones.active.reorder(due_date: :asc, title: :asc) + end @milestones = @milestones.includes(:project) @milestones = @milestones.page(params[:page]).per(PER_PAGE) @@ -31,9 +32,6 @@ class Projects::MilestonesController < Projects::ApplicationController end def show - @issues = @milestone.issues - @users = @milestone.participants.uniq - @merge_requests = @milestone.merge_requests end def create diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 6f1e186d408..1b9dd568043 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -11,11 +11,9 @@ class Projects::NotesController < Projects::ApplicationController notes_json = { notes: [], last_fetched_at: current_fetched_at } @notes.each do |note| - notes_json[:notes] << { - id: note.id, - html: note_to_html(note), - valid: note.valid? - } + next if note.cross_reference_not_visible_for?(current_user) + + notes_json[:notes] << note_json(note) end render json: notes_json @@ -25,7 +23,7 @@ class Projects::NotesController < Projects::ApplicationController @note = Notes::CreateService.new(project, current_user, note_params).execute respond_to do |format| - format.json { render_note_json(@note) } + format.json { render json: note_json(@note) } format.html { redirect_back_or_default } end end @@ -34,7 +32,7 @@ class Projects::NotesController < Projects::ApplicationController @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) respond_to do |format| - format.json { render_note_json(@note) } + format.json { render json: note_json(@note) } format.html { redirect_back_or_default } end end @@ -99,6 +97,8 @@ class Projects::NotesController < Projects::ApplicationController end def note_to_discussion_html(note) + return unless note.for_diff_line? + if params[:view] == 'parallel' template = "projects/notes/_diff_notes_with_reply_parallel" locals = @@ -106,7 +106,7 @@ class Projects::NotesController < Projects::ApplicationController { notes_left: [note], notes_right: [] } else { notes_left: [], notes_right: [note] } - end + end else template = "projects/notes/_diff_notes_with_reply" locals = { notes: [note] } @@ -131,9 +131,9 @@ class Projects::NotesController < Projects::ApplicationController ) end - def render_note_json(note) + def note_json(note) if note.valid? - render json: { + { valid: true, id: note.id, discussion_id: note.discussion_id, @@ -144,7 +144,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_with_diff_html: note_to_discussion_with_diff_html(note) } else - render json: { + { valid: false, award: note.is_award, errors: note.errors @@ -163,8 +163,6 @@ class Projects::NotesController < Projects::ApplicationController ) end - private - def find_current_user_notes @notes = NotesFinder.new.execute(project, current_user, params) end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 8364fc293b7..e7bddc4a6f1 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -27,6 +27,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController end @project_member = @project.project_members.new + @project_group_links = @project.project_group_links end def create diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index be7d5c187fe..10de0e60530 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a file's raw class Projects::RawController < Projects::ApplicationController include ExtractsPath + include BlobHelper before_action :require_non_empty_project before_action :assign_ref_vars @@ -12,10 +13,15 @@ class Projects::RawController < Projects::ApplicationController if @blob headers['X-Content-Type-Options'] = 'nosniff' + return if cached_blob? + if @blob.lfs_pointer? send_lfs_object else - stream_data + headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) + headers['Content-Disposition'] = 'inline' + headers['Content-Type'] = safe_content_type(@blob) + head :ok # 'render nothing: true' messes up the Content-Type end else render_404 @@ -24,26 +30,6 @@ class Projects::RawController < Projects::ApplicationController private - def get_blob_type - if @blob.text? - 'text/plain; charset=utf-8' - elsif @blob.image? - @blob.content_type - else - 'application/octet-stream' - end - end - - def stream_data - type = get_blob_type - - send_data( - @blob.data, - type: type, - disposition: 'inline' - ) - end - def send_lfs_object lfs_object = find_lfs_object diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index c4e18c17077..00df1c9c965 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -20,6 +20,8 @@ class Projects::RefsController < Projects::ApplicationController namespace_project_network_path(@project.namespace, @project, @id, @options) when "graphs" namespace_project_graph_path(@project.namespace, @project, @id) + when "find_file" + namespace_project_find_file_path(@project.namespace, @project, @id) when "graphs_commits" commits_namespace_project_graph_path(@project.namespace, @project, @id) else @@ -62,9 +64,9 @@ class Projects::RefsController < Projects::ApplicationController } end - if @logs.present? - @log_url = namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/')) - @more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit)) + offset = (@offset + @limit) + if contents.size > offset + @more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: offset) end respond_to do |format| diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index ba9aea1c165..5c7614cfbaf 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -11,7 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController end def archive - render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute + RepositoryArchiveCacheWorker.perform_async + headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format])) + head :ok rescue => ex logger.error("#{self.class.name}: #{ex}") return git_not_found! diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index e2785caa2fb..bedeb4a295c 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -1,5 +1,5 @@ class Projects::RunnerProjectsController < Projects::ApplicationController - before_action :authorize_admin_project! + before_action :authorize_admin_build! layout 'project_settings' diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 4993b2648a5..0dd2d6a99be 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -1,6 +1,6 @@ class Projects::RunnersController < Projects::ApplicationController + before_action :authorize_admin_build! before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] - before_action :authorize_admin_project! layout 'project_settings' diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 2104c7a7a71..92b0caa2efb 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -25,7 +25,7 @@ class Projects::SnippetsController < Projects::ApplicationController end def new - @snippet = @project.snippets.build + @snippet = @noteable = @project.snippets.build end def create diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 280fe12cc7c..e580487a2c6 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -34,6 +34,11 @@ class Projects::TagsController < Projects::ApplicationController def destroy DeleteTagService.new(project, current_user).execute(params[:id]) - redirect_to namespace_project_tags_path(@project.namespace, @project) + respond_to do |format| + format.html do + redirect_to namespace_project_tags_path(@project.namespace, @project) + end + format.js + end end end diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 30adfad1daa..92359745cec 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -1,5 +1,5 @@ class Projects::TriggersController < Projects::ApplicationController - before_action :authorize_admin_project! + before_action :authorize_admin_build! layout 'project_settings' diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 10efafea9db..00234654578 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -1,5 +1,5 @@ class Projects::VariablesController < Projects::ApplicationController - before_action :authorize_admin_project! + before_action :authorize_admin_build! layout 'project_settings' diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 3004722bce0..c9930480770 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,14 +1,13 @@ class ProjectsController < ApplicationController include ExtractsPath - prepend_before_action :render_go_import, only: [:show] skip_before_action :authenticate_user!, only: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists? # Authorize - before_action :authorize_admin_project!, only: [:edit, :update] + before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping] before_action :event_filter, only: [:show, :activity] layout :determine_layout @@ -93,6 +92,10 @@ class ProjectsController < ApplicationController return end + if @project.pending_delete? + flash[:alert] = "Project queued for delete." + end + respond_to do |format| format.html do if @project.repository_exists? @@ -120,8 +123,8 @@ class ProjectsController < ApplicationController def destroy return access_denied! unless can?(current_user, :remove_project, @project) - ::Projects::DestroyService.new(@project, current_user, {}).execute - flash[:alert] = "Project '#{@project.name}' was deleted." + ::Projects::DestroyService.new(@project, current_user, {}).pending_delete! + flash[:alert] = "Project '#{@project.name}' will be deleted." redirect_to dashboard_projects_path rescue Projects::DestroyService::DestroyError => ex @@ -131,7 +134,7 @@ class ProjectsController < ApplicationController def autocomplete_sources note_type = params['type'] note_id = params['type_id'] - autocomplete = ::Projects::AutocompleteService.new(@project) + autocomplete = ::Projects::AutocompleteService.new(@project, current_user) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id) @suggestions = { @@ -166,6 +169,20 @@ class ProjectsController < ApplicationController end end + def housekeeping + ::Projects::HousekeepingService.new(@project).execute + + redirect_to( + project_path(@project), + notice: "Housekeeping successfully started" + ) + rescue ::Projects::HousekeepingService::LeaseTaken => ex + redirect_to( + edit_project_path(@project), + alert: ex.to_s + ) + end + def toggle_star current_user.toggle_star(@project) @project.reload @@ -214,6 +231,7 @@ class ProjectsController < ApplicationController :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, + :public_builds, ) end @@ -222,22 +240,12 @@ class ProjectsController < ApplicationController Emoji.emojis.map do |name, emoji| { name: name, - path: view_context.image_url("emoji/#{emoji["unicode"]}.png") + path: view_context.image_url("#{emoji["unicode"]}.png") } end end end - def render_go_import - return unless params["go-get"] == "1" - - @namespace = params[:namespace_id] - @id = params[:project_id] || params[:id] - @id = @id.gsub(/\.git\Z/, "") - - render "go_import", layout: false - end - def repo_exists? project.repository_exists? && !project.empty_repo? end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 9bb42ec86b3..e42d2d73947 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,4 +1,6 @@ class SearchController < ApplicationController + skip_before_action :authenticate_user!, :reject_blocked + include SearchHelper layout 'search' diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb new file mode 100644 index 00000000000..7271c933b9b --- /dev/null +++ b/app/controllers/sent_notifications_controller.rb @@ -0,0 +1,25 @@ +class SentNotificationsController < ApplicationController + skip_before_action :authenticate_user! + + def unsubscribe + @sent_notification = SentNotification.for(params[:id]) + return render_404 unless @sent_notification && @sent_notification.unsubscribable? + + noteable = @sent_notification.noteable + noteable.unsubscribe(@sent_notification.recipient) + + flash[:notice] = "You have been unsubscribed from this thread." + if current_user + case noteable + when Issue + redirect_to issue_path(noteable) + when MergeRequest + redirect_to merge_request_path(noteable) + else + redirect_to root_path + end + else + redirect_to new_user_session_path + end + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 825f85199be..65677a3dd3c 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -2,8 +2,12 @@ class SessionsController < Devise::SessionsController include AuthenticatesWithTwoFactor include Recaptcha::ClientHelper + skip_before_action :check_2fa_requirement, only: [:destroy] + + prepend_before_action :check_initial_setup, only: [:new] prepend_before_action :authenticate_with_two_factor, only: [:create] prepend_before_action :store_redirect_path, only: [:new] + before_action :auto_sign_in_with_provider, only: [:new] before_action :load_recaptcha @@ -31,6 +35,22 @@ class SessionsController < Devise::SessionsController private + # Handle an "initial setup" state, where there's only one user, it's an admin, + # and they require a password change. + def check_initial_setup + return unless User.count == 1 + + user = User.admins.last + + return unless user && user.require_password? + + token = user.generate_reset_token + user.save + + redirect_to edit_user_password_path(reset_password_token: token), + notice: "Please create a password for your new account." + end + def user_params params.require(:user).permit(:login, :password, :remember_me, :otp_attempt) end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 868b05929d7..509f4f412ca 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -55,14 +55,15 @@ class UploadsController < ApplicationController "user" => User, "project" => Project, "note" => Note, - "group" => Group + "group" => Group, + "appearance" => Appearance } upload_models[params[:model]] end def upload_mount - upload_mounts = %w(avatar attachment file) + upload_mounts = %w(avatar attachment file logo header_logo) if upload_mounts.include?(params[:mounted_as]) params[:mounted_as] diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 30cb869eb2a..e10c633690f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,12 +3,6 @@ class UsersController < ApplicationController before_action :set_user def show - @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) - - @projects = PersonalProjectsFinder.new(@user).execute(current_user) - - @groups = JoinedGroupsFinder.new(@user).execute(current_user) - respond_to do |format| format.html @@ -24,6 +18,45 @@ class UsersController < ApplicationController end end + def groups + load_groups + + respond_to do |format| + format.html { render 'show' } + format.json do + render json: { + html: view_to_html_string("shared/groups/_list", groups: @groups) + } + end + end + end + + def projects + load_projects + + respond_to do |format| + format.html { render 'show' } + format.json do + render json: { + html: view_to_html_string("shared/projects/_list", projects: @projects, remote: true) + } + end + end + end + + def contributed + load_contributed_projects + + respond_to do |format| + format.html { render 'show' } + format.json do + render json: { + html: view_to_html_string("shared/projects/_list", projects: @contributed_projects) + } + end + end + end + def calendar calendar = contributions_calendar @timestamps = calendar.timestamps @@ -34,12 +67,8 @@ class UsersController < ApplicationController end def calendar_activities - @calendar_date = Date.parse(params[:date]) rescue nil - @events = [] - - if @calendar_date - @events = contributions_calendar.events_by_date(@calendar_date) - end + @calendar_date = Date.parse(params[:date]) rescue Date.today + @events = contributions_calendar.events_by_date(@calendar_date) render 'calendar_activities', layout: false end @@ -56,7 +85,7 @@ class UsersController < ApplicationController def contributions_calendar @contributions_calendar ||= Gitlab::ContributionsCalendar. - new(contributed_projects.reject(&:forked?), @user) + new(contributed_projects, @user) end def load_events @@ -68,6 +97,20 @@ class UsersController < ApplicationController limit_recent(20, params[:offset]) end + def load_projects + @projects = + PersonalProjectsFinder.new(@user).execute(current_user) + .page(params[:page]).per(PER_PAGE) + end + + def load_contributed_projects + @contributed_projects = contributed_projects.joined(@user) + end + + def load_groups + @groups = @user.groups.order_id_desc + end + def projects_for_current_user ProjectsFinder.new.execute(current_user) end |