diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-10-05 14:27:42 +0200 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-10-05 14:27:42 +0200 |
commit | aba71835f310d5aad9c2af9e22c81bd44e6f0686 (patch) | |
tree | 40abb714359b8bed16bc5f43b7c4a085eb12d473 /app | |
parent | b229637b0837065a9993d06573b41218ab7e9dfb (diff) | |
parent | 1fe7a42afca83fbbda638d9569d58a87039cd8f2 (diff) | |
download | gitlab-ce-aba71835f310d5aad9c2af9e22c81bd44e6f0686.tar.gz |
Merge remote-tracking branch 'origin/master' into feature/sm/35954-create-kubernetes-cluster-on-gke-from-k8s-service
Diffstat (limited to 'app')
39 files changed, 263 insertions, 158 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index f80a26b3fd4..442ed86d50c 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -29,6 +29,7 @@ showEmptyState: true, updateAspectRatio: false, updatedAspectRatios: 0, + hoverData: {}, resizeThrottled: {}, }; }, @@ -64,6 +65,10 @@ this.updatedAspectRatios = 0; } }, + + hoverChanged(data) { + this.hoverData = data; + }, }, created() { @@ -72,10 +77,12 @@ deploymentEndpoint: this.deploymentEndpoint, }); eventHub.$on('toggleAspectRatio', this.toggleAspectRatio); + eventHub.$on('hoverChanged', this.hoverChanged); }, beforeDestroy() { eventHub.$off('toggleAspectRatio', this.toggleAspectRatio); + eventHub.$off('hoverChanged', this.hoverChanged); window.removeEventListener('resize', this.resizeThrottled, false); }, @@ -102,6 +109,7 @@ v-for="(graphData, index) in groupData.metrics" :key="index" :graph-data="graphData" + :hover-data="hoverData" :update-aspect-ratio="updateAspectRatio" :deployment-data="store.deploymentData" /> diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index a7b483f6786..a18164482a2 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -73,34 +73,22 @@ <template> <div class="prometheus-state"> - <div class="row"> - <div class="col-md-4 col-md-offset-4 state-svg svg-content"> - <img :src="currentState.svgUrl"/> - </div> + <div class="state-svg svg-content"> + <img :src="currentState.svgUrl"/> </div> - <div class="row"> - <div class="col-md-6 col-md-offset-3"> - <h4 class="text-center state-title"> - {{currentState.title}} - </h4> - </div> - </div> - <div class="row"> - <div class="col-md-6 col-md-offset-3"> - <div class="description-text text-center state-description"> - {{currentState.description}} - <a v-if="showButtonDescription" :href="settingsPath"> - Prometheus server - </a> - </div> - </div> - </div> - <div class="row state-button-section"> - <div class="col-md-4 col-md-offset-4 text-center state-button"> - <a class="btn btn-success" :href="buttonPath"> - {{currentState.buttonText}} - </a> - </div> + <h4 class="state-title"> + {{currentState.title}} + </h4> + <p class="state-description"> + {{currentState.description}} + <a v-if="showButtonDescription" :href="settingsPath"> + Prometheus server + </a> + </p> + <div class="state-button"> + <a class="btn btn-success" :href="buttonPath"> + {{currentState.buttonText}} + </a> </div> </div> </template> diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index 6b3e341f936..33611c408ef 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -7,12 +7,10 @@ import MonitoringMixin from '../mixins/monitoring_mixins'; import eventHub from '../event_hub'; import measurements from '../utils/measurements'; - import { timeScaleFormat } from '../utils/date_time_formatters'; + import { timeScaleFormat, bisectDate } from '../utils/date_time_formatters'; import createTimeSeries from '../utils/multiple_time_series'; import bp from '../../breakpoints'; - const bisectDate = d3.bisector(d => d.time).left; - export default { props: { graphData: { @@ -27,6 +25,11 @@ type: Array, required: true, }, + hoverData: { + type: Object, + required: false, + default: () => ({}), + }, }, mixins: [MonitoringMixin], @@ -52,6 +55,7 @@ currentXCoordinate: 0, currentFlagPosition: 0, showFlag: false, + showFlagContent: false, showDeployInfo: true, timeSeries: [], }; @@ -122,22 +126,14 @@ const d1 = firstTimeSeries.values[overlayIndex]; if (d0 === undefined || d1 === undefined) return; const evalTime = timeValueOverlay - d0[0] > d1[0] - timeValueOverlay; - this.currentData = evalTime ? d1 : d0; - this.currentDataIndex = evalTime ? overlayIndex : (overlayIndex - 1); - this.currentXCoordinate = Math.floor(firstTimeSeries.timeSeriesScaleX(this.currentData.time)); + const hoveredDataIndex = evalTime ? overlayIndex : (overlayIndex - 1); + const hoveredDate = firstTimeSeries.values[hoveredDataIndex].time; const currentDeployXPos = this.mouseOverDeployInfo(point.x); - if (this.currentXCoordinate > (this.graphWidth - 200)) { - this.currentFlagPosition = this.currentXCoordinate - 103; - } else { - this.currentFlagPosition = this.currentXCoordinate; - } - - if (currentDeployXPos) { - this.showFlag = false; - } else { - this.showFlag = true; - } + eventHub.$emit('hoverChanged', { + hoveredDate, + currentDeployXPos, + }); }, renderAxesPaths() { @@ -194,6 +190,10 @@ eventHub.$emit('toggleAspectRatio'); } }, + + hoverData() { + this.positionFlag(); + }, }, mounted() { @@ -203,7 +203,10 @@ </script> <template> - <div class="prometheus-graph"> + <div + class="prometheus-graph" + @mouseover="showFlagContent = true" + @mouseleave="showFlagContent = false"> <h5 class="text-center graph-title"> {{graphData.title}} </h5> @@ -247,6 +250,7 @@ <graph-deployment :show-deploy-info="showDeployInfo" :deployment-data="reducedDeploymentData" + :graph-width="graphWidth" :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" /> @@ -257,6 +261,7 @@ :current-flag-position="currentFlagPosition" :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" + :show-flag-content="showFlagContent" /> <rect class="prometheus-graph-overlay" diff --git a/app/assets/javascripts/monitoring/components/graph/deployment.vue b/app/assets/javascripts/monitoring/components/graph/deployment.vue index 3623d2ed946..e3b8be0c7fb 100644 --- a/app/assets/javascripts/monitoring/components/graph/deployment.vue +++ b/app/assets/javascripts/monitoring/components/graph/deployment.vue @@ -19,6 +19,10 @@ type: Number, required: true, }, + graphWidth: { + type: Number, + required: true, + }, }, computed: { @@ -47,6 +51,14 @@ transformDeploymentGroup(deployment) { return `translate(${Math.floor(deployment.xPos) + 1}, 20)`; }, + + positionFlag(deployment) { + let xPosition = 3; + if (deployment.xPos > (this.graphWidth - 200)) { + xPosition = -97; + } + return xPosition; + }, }, }; </script> @@ -77,7 +89,7 @@ <svg v-if="deployment.showDeploymentFlag" class="js-deploy-info-box" - x="3" + :x="positionFlag(deployment)" y="0" width="92" height="60"> diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue index a98e3d06c18..10fb7ff6803 100644 --- a/app/assets/javascripts/monitoring/components/graph/flag.vue +++ b/app/assets/javascripts/monitoring/components/graph/flag.vue @@ -23,6 +23,10 @@ type: Number, required: true, }, + showFlagContent: { + type: Boolean, + required: true, + }, }, data() { @@ -57,6 +61,7 @@ transform="translate(-5, 20)"> </line> <svg + v-if="showFlagContent" class="rect-text-metric" :x="currentFlagPosition" y="0"> diff --git a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js index 345a0b37a76..31f38aca5d6 100644 --- a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js +++ b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js @@ -1,3 +1,5 @@ +import { bisectDate } from '../utils/date_time_formatters'; + const mixins = { methods: { mouseOverDeployInfo(mouseXPos) { @@ -18,6 +20,7 @@ const mixins = { return dataFound; }, + formatDeployments() { this.reducedDeploymentData = this.deploymentData.reduce((deploymentDataArray, deployment) => { const time = new Date(deployment.created_at); @@ -40,6 +43,25 @@ const mixins = { return deploymentDataArray; }, []); }, + + positionFlag() { + const timeSeries = this.timeSeries[0]; + const hoveredDataIndex = bisectDate(timeSeries.values, this.hoverData.hoveredDate, 1); + this.currentData = timeSeries.values[hoveredDataIndex]; + this.currentDataIndex = hoveredDataIndex; + this.currentXCoordinate = Math.floor(timeSeries.timeSeriesScaleX(this.currentData.time)); + if (this.currentXCoordinate > (this.graphWidth - 200)) { + this.currentFlagPosition = this.currentXCoordinate - 103; + } else { + this.currentFlagPosition = this.currentXCoordinate; + } + + if (this.hoverData.currentDeployXPos) { + this.showFlag = false; + } else { + this.showFlag = true; + } + }, }, }; diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js index ef280e02092..104432ef5de 100644 --- a/app/assets/javascripts/monitoring/monitoring_bundle.js +++ b/app/assets/javascripts/monitoring/monitoring_bundle.js @@ -3,8 +3,5 @@ import Dashboard from './components/dashboard.vue'; document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#prometheus-graphs', - components: { - Dashboard, - }, - render: createElement => createElement('dashboard'), + render: createElement => createElement(Dashboard), })); diff --git a/app/assets/javascripts/monitoring/stores/monitoring_store.js b/app/assets/javascripts/monitoring/stores/monitoring_store.js index 7592af5878e..854636e9a89 100644 --- a/app/assets/javascripts/monitoring/stores/monitoring_store.js +++ b/app/assets/javascripts/monitoring/stores/monitoring_store.js @@ -13,7 +13,7 @@ function normalizeMetrics(metrics) { ...result, values: result.values.map(([timestamp, value]) => ({ time: new Date(timestamp * 1000), - value, + value: Number(value), })), })), })), diff --git a/app/assets/javascripts/monitoring/utils/date_time_formatters.js b/app/assets/javascripts/monitoring/utils/date_time_formatters.js index 26bcaa02511..c4c6b1ac1f5 100644 --- a/app/assets/javascripts/monitoring/utils/date_time_formatters.js +++ b/app/assets/javascripts/monitoring/utils/date_time_formatters.js @@ -2,6 +2,7 @@ import d3 from 'd3'; export const dateFormat = d3.time.format('%b %-d, %Y'); export const timeFormat = d3.time.format('%-I:%M%p'); +export const bisectDate = d3.bisector(d => d.time).left; export const timeScaleFormat = d3.time.format.multi([ ['.%L', d => d.getMilliseconds()], diff --git a/app/assets/javascripts/repo/components/repo_sidebar.vue b/app/assets/javascripts/repo/components/repo_sidebar.vue index 1e40814b95f..685f6ff806f 100644 --- a/app/assets/javascripts/repo/components/repo_sidebar.vue +++ b/app/assets/javascripts/repo/components/repo_sidebar.vue @@ -74,8 +74,8 @@ export default { <thead v-if="!isMini"> <tr> <th class="name">Name</th> - <th class="hidden-sm hidden-xs last-commit">Last Commit</th> - <th class="hidden-xs last-update text-right">Last Update</th> + <th class="hidden-sm hidden-xs last-commit">Last commit</th> + <th class="hidden-xs last-update text-right">Last update</th> </tr> </thead> <tbody> diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 9362d80d4e6..6a9c2578d95 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -207,10 +207,13 @@ } .prometheus-state { - margin-top: 10px; + max-width: 430px; + margin: 10px auto; + text-align: center; - .state-button-section { - margin-top: 10px; + .state-svg { + max-width: 80vw; + margin: 0 auto; } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 67abe6e88ed..6c521bb06ee 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -392,11 +392,11 @@ table.u2f-registrations { } } -.gpg-email-badge { +.email-badge { display: inline; margin-right: $gl-padding / 2; - .gpg-email-badge-email { + .email-badge-email { display: inline; margin-right: $gl-padding / 4; } diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 676a7203c7d..156a8e2c515 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -155,7 +155,7 @@ class Admin::UsersController < Admin::ApplicationController def remove_email email = user.emails.find(params[:email_id]) - success = Emails::DestroyService.new(current_user, user: user, email: email.email).execute + success = Emails::DestroyService.new(current_user, user: user).execute(email) respond_to do |format| if success diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 771c6f3034a..967fe39256a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -85,12 +85,21 @@ class ApplicationController < ActionController::Base super payload[:remote_ip] = request.remote_ip - if current_user.present? - payload[:user_id] = current_user.id - payload[:username] = current_user.username + logged_user = auth_user + + if logged_user.present? + payload[:user_id] = logged_user.try(:id) + payload[:username] = logged_user.try(:username) end end + # Controllers such as GitHttpController may use alternative methods + # (e.g. tokens) to authenticate the user, whereas Devise sets current_user + def auth_user + return current_user if current_user.present? + return try(:authenticated_user) + end + # This filter handles both private tokens and personal access tokens def authenticate_user_from_private_token! token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index 0c2646d7bf0..80ab681ed87 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -10,13 +10,14 @@ class ConfirmationsController < Devise::ConfirmationsController users_almost_there_path end - def after_confirmation_path_for(resource_name, resource) - if signed_in?(resource_name) + def after_confirmation_path_for(_resource_name, resource) + # incoming resource can either be a :user or an :email + if signed_in?(:user) after_sign_in(resource) else Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}") flash[:notice] += " Please sign in." - new_session_path(resource_name) + new_session_path(:user) end end diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index 97db84b92d4..bbd7ba49d77 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -1,15 +1,14 @@ class Profiles::EmailsController < Profiles::ApplicationController + before_action :find_email, only: [:destroy, :resend_confirmation_instructions] + def index - @primary = current_user.email + @primary_email = current_user.email @emails = current_user.emails.order_id_desc end def create @email = Emails::CreateService.new(current_user, email_params.merge(user: current_user)).execute - - if @email.errors.blank? - NotificationService.new.new_email(@email) - else + unless @email.errors.blank? flash[:alert] = @email.errors.full_messages.first end @@ -17,9 +16,7 @@ class Profiles::EmailsController < Profiles::ApplicationController end def destroy - @email = current_user.emails.find(params[:id]) - - Emails::DestroyService.new(current_user, user: current_user, email: @email.email).execute + Emails::DestroyService.new(current_user, user: current_user).execute(@email) respond_to do |format| format.html { redirect_to profile_emails_url, status: 302 } @@ -27,9 +24,23 @@ class Profiles::EmailsController < Profiles::ApplicationController end end + def resend_confirmation_instructions + if Emails::ConfirmService.new(current_user, user: current_user).execute(@email) + flash[:notice] = "Confirmation email sent to #{@email.email}" + else + flash[:alert] = "There was a problem sending the confirmation email" + end + + redirect_to profile_emails_url + end + private def email_params params.require(:email).permit(:email) end + + def find_email + @email = current_user.emails.find(params[:id]) + end end diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb index 7d0e2b3e2ef..95d7a02e9e9 100644 --- a/app/controllers/projects/git_http_client_controller.rb +++ b/app/controllers/projects/git_http_client_controller.rb @@ -9,6 +9,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true alias_method :user, :actor + alias_method :authenticated_user, :actor # Git clients will not know what authenticity token to send along skip_before_action :verify_authenticity_token diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index c401030e34a..4f5edeb9bda 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -7,12 +7,6 @@ module Emails mail(to: @user.notification_email, subject: subject("Account was created for you")) end - def new_email_email(email_id) - @email = Email.find(email_id) - @current_user = @user = @email.user - mail(to: @user.notification_email, subject: subject("Email was added to your account")) - end - def new_ssh_key_email(key_id) @key = Key.find_by(id: key_id) diff --git a/app/models/email.rb b/app/models/email.rb index 826d4f16edb..384f38f2db7 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -7,6 +7,13 @@ class Email < ActiveRecord::Base validates :email, presence: true, uniqueness: true, email: true validate :unique_email, if: ->(email) { email.email_changed? } + scope :confirmed, -> { where.not(confirmed_at: nil) } + + after_commit :update_invalid_gpg_signatures, if: -> { previous_changes.key?('confirmed_at') } + + devise :confirmable + self.reconfirmable = false # currently email can't be changed, no need to reconfirm + def email=(value) write_attribute(:email, value.downcase.strip) end @@ -14,4 +21,9 @@ class Email < ActiveRecord::Base def unique_email self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email) end + + # once email is confirmed, update the gpg signatures + def update_invalid_gpg_signatures + user.update_invalid_gpg_signatures if confirmed? + end end diff --git a/app/models/user.rb b/app/models/user.rb index 4e71a3e11c2..4ba9130a75a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -163,15 +163,16 @@ class User < ActiveRecord::Base before_validation :sanitize_attrs before_validation :set_notification_email, if: :email_changed? before_validation :set_public_email, if: :public_email_changed? - - after_update :update_emails_with_primary_email, if: :email_changed? before_save :ensure_authentication_token, :ensure_incoming_email_token before_save :ensure_user_rights_and_limits, if: :external_changed? before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) } + before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? } after_save :ensure_namespace_correct + after_destroy :post_destroy_hook + after_commit :update_emails_with_primary_email, on: :update, if: -> { previous_changes.key?('email') } after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') } + after_initialize :set_projects_limit - after_destroy :post_destroy_hook # User's Layout preference enum layout: [:fixed, :fluid] @@ -525,12 +526,24 @@ class User < ActiveRecord::Base errors.add(:public_email, "is not an email you own") unless all_emails.include?(public_email) end + # see if the new email is already a verified secondary email + def check_for_verified_email + skip_reconfirmation! if emails.confirmed.where(email: self.email).any? + end + + # Note: the use of the Emails services will cause `saves` on the user object, running + # through the callbacks again and can have side effects, such as the `previous_changes` + # hash and `_was` variables getting munged. + # By using an `after_commit` instead of `after_update`, we avoid the recursive callback + # scenario, though it then requires us to use the `previous_changes` hash def update_emails_with_primary_email + previous_email = previous_changes[:email][0] # grab this before the DestroyService is called primary_email_record = emails.find_by(email: email) - if primary_email_record - Emails::DestroyService.new(self, user: self, email: email).execute - Emails::CreateService.new(self, user: self, email: email_was).execute - end + Emails::DestroyService.new(self, user: self).execute(primary_email_record) if primary_email_record + + # the original primary email was confirmed, and we want that to carry over. We don't + # have access to the original confirmation values at this point, so just set confirmed_at + Emails::CreateService.new(self, user: self, email: previous_email).execute(confirmed_at: confirmed_at) end def update_invalid_gpg_signatures @@ -816,6 +829,10 @@ class User < ActiveRecord::Base avatar_path(args) || GravatarService.new.execute(email, size, scale, username: username) end + def primary_email_verified? + confirmed? && !temp_oauth_email? + end + def all_emails all_emails = [] all_emails << email unless temp_oauth_email? @@ -823,6 +840,18 @@ class User < ActiveRecord::Base all_emails end + def verified_emails + verified_emails = [] + verified_emails << email if primary_email_verified? + verified_emails.concat(emails.confirmed.pluck(:email)) + verified_emails + end + + def verified_email?(check_email) + downcased = check_email.downcase + email == downcased ? primary_email_verified? : emails.confirmed.where(email: downcased).exists? + end + def hook_attrs { name: name, @@ -1047,10 +1076,6 @@ class User < ActiveRecord::Base ensure_rss_token! end - def verified_email?(email) - self.email == email - end - def sync_attribute?(attribute) return true if ldap_user? && attribute == :email diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb index e4e9d8ef90a..c8dd98cc04d 100644 --- a/app/serializers/commit_entity.rb +++ b/app/serializers/commit_entity.rb @@ -1,4 +1,4 @@ -class CommitEntity < API::Entities::RepoCommit +class CommitEntity < API::Entities::Commit include RequestAwareEntity expose :author, using: UserEntity diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb index 7f591c89411..5bbceeb3b3f 100644 --- a/app/services/emails/base_service.rb +++ b/app/services/emails/base_service.rb @@ -1,9 +1,8 @@ module Emails class BaseService - def initialize(current_user, opts) - @current_user = current_user - @user = opts.delete(:user) - @email = opts[:email] + def initialize(current_user, params = {}) + @current_user, @params = current_user, params.dup + @user = params.delete(:user) end end end diff --git a/app/services/emails/confirm_service.rb b/app/services/emails/confirm_service.rb new file mode 100644 index 00000000000..b5301bf2b82 --- /dev/null +++ b/app/services/emails/confirm_service.rb @@ -0,0 +1,7 @@ +module Emails + class ConfirmService < ::Emails::BaseService + def execute(email) + email.resend_confirmation_instructions + end + end +end diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb index b6491ee9804..94a841af7c3 100644 --- a/app/services/emails/create_service.rb +++ b/app/services/emails/create_service.rb @@ -1,7 +1,7 @@ module Emails class CreateService < ::Emails::BaseService - def execute - @user.emails.create(email: @email) + def execute(extra_params = {}) + @user.emails.create(@params.merge(extra_params)) end end end diff --git a/app/services/emails/destroy_service.rb b/app/services/emails/destroy_service.rb index 44011cc36c8..1ed131fe326 100644 --- a/app/services/emails/destroy_service.rb +++ b/app/services/emails/destroy_service.rb @@ -1,7 +1,7 @@ module Emails class DestroyService < ::Emails::BaseService - def execute - update_secondary_emails! if Email.find_by_email!(@email).destroy + def execute(email) + email.destroy && update_secondary_emails! end private diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index e2a80db06a6..8d5da459882 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -31,13 +31,6 @@ class NotificationService end end - # Always notify user about email added to profile - def new_email(email) - if email.user&.can?(:receive_notifications) - mailer.new_email_email(email.id).deliver_later - end - end - # When create an issue we should send an email to: # # * issue assignee if their notification level is not Disabled diff --git a/app/views/devise/mailer/_confirmation_instructions_account.html.haml b/app/views/devise/mailer/_confirmation_instructions_account.html.haml new file mode 100644 index 00000000000..65565b7b8a8 --- /dev/null +++ b/app/views/devise/mailer/_confirmation_instructions_account.html.haml @@ -0,0 +1,16 @@ +- confirmation_link = confirmation_url(@resource, confirmation_token: @token) +- if @resource.unconfirmed_email.present? + #content + = email_default_heading(@resource.unconfirmed_email) + %p Click the link below to confirm your email address. + #cta + = link_to 'Confirm your email address', confirmation_link +- else + #content + - if Gitlab.com? + = email_default_heading('Thanks for signing up to GitLab!') + - else + = email_default_heading("Welcome, #{@resource.name}!") + %p To get started, click the link below to confirm your account. + #cta + = link_to 'Confirm your account', confirmation_link diff --git a/app/views/devise/mailer/_confirmation_instructions_account.text.erb b/app/views/devise/mailer/_confirmation_instructions_account.text.erb new file mode 100644 index 00000000000..01f09aa763d --- /dev/null +++ b/app/views/devise/mailer/_confirmation_instructions_account.text.erb @@ -0,0 +1,14 @@ +<% if @resource.unconfirmed_email.present? %> +<%= @resource.unconfirmed_email %>, + +Use the link below to confirm your email address. +<% else %> + <% if Gitlab.com? %> +Thanks for signing up to GitLab! + <% else %> +Welcome, <%= @resource.name %>! + <% end %> +To get started, use the link below to confirm your account. +<% end %> + +<%= confirmation_url(@resource, confirmation_token: @token) %> diff --git a/app/views/devise/mailer/_confirmation_instructions_secondary.html.haml b/app/views/devise/mailer/_confirmation_instructions_secondary.html.haml new file mode 100644 index 00000000000..3d0a1f622a5 --- /dev/null +++ b/app/views/devise/mailer/_confirmation_instructions_secondary.html.haml @@ -0,0 +1,8 @@ +#content + = email_default_heading("#{@resource.user.name}, you've added an additional email!") + %p Click the link below to confirm your email address (#{@resource.email}) + #cta + = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) + %p + If this email was added in error, you can remove it here: + = link_to "Emails", profile_emails_url diff --git a/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb b/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb new file mode 100644 index 00000000000..a3b28cb0b84 --- /dev/null +++ b/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb @@ -0,0 +1,7 @@ +<%= @resource.user.name %>, you've added an additional email! + +Use the link below to confirm your email address (<%= @resource.email %>) + +<%= confirmation_url(@resource, confirmation_token: @token) %> + +If this email was added in error, you can remove it here: <%= profile_emails_url %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml index 4d1037807be..50ee7b53d8f 100644 --- a/app/views/devise/mailer/confirmation_instructions.html.haml +++ b/app/views/devise/mailer/confirmation_instructions.html.haml @@ -1,16 +1 @@ -- confirmation_link = confirmation_url(@resource, confirmation_token: @token) -- if @resource.unconfirmed_email.present? - #content - = email_default_heading(@resource.unconfirmed_email) - %p Click the link below to confirm your email address. - #cta - = link_to confirmation_link, confirmation_link -- else - #content - - if Gitlab.com? - = email_default_heading('Thanks for signing up to GitLab!') - - else - = email_default_heading("Welcome, #{@resource.name}!") - %p To get started, click the link below to confirm your account. - #cta - = link_to confirmation_link, confirmation_link += render partial: "confirmation_instructions_#{@resource.is_a?(User) ? 'account' : 'secondary'}" diff --git a/app/views/devise/mailer/confirmation_instructions.text.erb b/app/views/devise/mailer/confirmation_instructions.text.erb index 9f76edb76a4..05fddddf415 100644 --- a/app/views/devise/mailer/confirmation_instructions.text.erb +++ b/app/views/devise/mailer/confirmation_instructions.text.erb @@ -1,9 +1 @@ -Welcome, <%= @resource.name %>! - -<% if @resource.unconfirmed_email.present? %> -You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below: -<% else %> -You can confirm your account through the link below: -<% end %> - -<%= confirmation_url(@resource, confirmation_token: @token) %> +<%= render partial: "confirmation_instructions_#{@resource.is_a?(User) ? 'account' : 'secondary'}" %>
\ No newline at end of file diff --git a/app/views/groups/milestones/_header_title.html.haml b/app/views/groups/milestones/_header_title.html.haml index d7fabf53587..24eb39b8e2f 100644 --- a/app/views/groups/milestones/_header_title.html.haml +++ b/app/views/groups/milestones/_header_title.html.haml @@ -1 +1,2 @@ -- header_title group_title(@group, "Milestones", group_milestones_path(@group)) +- breadcrumb_title @milestone.title +- add_to_breadcrumbs "Milestones", group_milestones_path(@group) diff --git a/app/views/notify/new_email_email.html.haml b/app/views/notify/new_email_email.html.haml deleted file mode 100644 index 4a0448a573c..00000000000 --- a/app/views/notify/new_email_email.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%p - Hi #{@user.name}! -%p - A new email was added to your account: -%p - email: - %code= @email.email -%p - If this email was added in error, you can remove it here: - = link_to "Emails", profile_emails_url diff --git a/app/views/notify/new_email_email.text.erb b/app/views/notify/new_email_email.text.erb deleted file mode 100644 index 51cba99ad0d..00000000000 --- a/app/views/notify/new_email_email.text.erb +++ /dev/null @@ -1,7 +0,0 @@ -Hi <%= @user.name %>! - -A new email was added to your account: - -email.................. <%= @email.email %> - -If this email was added in error, you can remove it here: <%= profile_emails_url %> diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index 612ecbbb96a..df1df4f5d72 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -32,19 +32,25 @@ All email addresses will be used to identify your commits. %ul.well-list %li - = @primary + = render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? } %span.pull-right %span.label.label-success Primary email - - if @primary === current_user.public_email + - if @primary_email === current_user.public_email %span.label.label-info Public email - - if @primary === current_user.notification_email + - if @primary_email === current_user.notification_email %span.label.label-info Notification email - @emails.each do |email| %li - = email.email + = render partial: 'shared/email_with_badge', locals: { email: email.email, verified: email.confirmed? } %span.pull-right - if email.email === current_user.public_email %span.label.label-info Public email - if email.email === current_user.notification_email %span.label.label-info Notification email - = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-warning prepend-left-10' + - unless email.confirmed? + - confirm_title = "#{email.confirmation_sent_at ? 'Resend' : 'Send'} confirmation email" + = link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'btn btn-sm btn-warning prepend-left-10' + + = link_to profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-danger prepend-left-10' do + %span.sr-only Remove + = icon('trash') diff --git a/app/views/profiles/gpg_keys/_key.html.haml b/app/views/profiles/gpg_keys/_key.html.haml index b04981f90e3..970e92aadaa 100644 --- a/app/views/profiles/gpg_keys/_key.html.haml +++ b/app/views/profiles/gpg_keys/_key.html.haml @@ -3,7 +3,7 @@ = icon 'key', class: "settings-list-icon hidden-xs" .key-list-item-info - key.emails_with_verified_status.map do |email, verified| - = render partial: 'email_with_badge', locals: { email: email, verified: verified } + = render partial: 'shared/email_with_badge', locals: { email: email, verified: verified } .description %code= key.fingerprint diff --git a/app/views/projects/tree/_old_tree_content.html.haml b/app/views/projects/tree/_old_tree_content.html.haml index 820b947804e..6ea78851b8d 100644 --- a/app/views/projects/tree/_old_tree_content.html.haml +++ b/app/views/projects/tree/_old_tree_content.html.haml @@ -6,7 +6,7 @@ %th= s_('ProjectFileTree|Name') %th.hidden-xs .pull-left= _('Last commit') - %th.text-right= _('Last Update') + %th.text-right= _('Last update') - if @path.present? %tr.tree-item %td.tree-item-file-name diff --git a/app/views/profiles/gpg_keys/_email_with_badge.html.haml b/app/views/shared/_email_with_badge.html.haml index 5f7844584e1..b7bbc109238 100644 --- a/app/views/profiles/gpg_keys/_email_with_badge.html.haml +++ b/app/views/shared/_email_with_badge.html.haml @@ -2,7 +2,7 @@ - css_classes << (verified ? 'verified': 'unverified') - text = verified ? 'Verified' : 'Unverified' -.gpg-email-badge - .gpg-email-badge-email= email +.email-badge + .email-badge-email= email %div{ class: css_classes } = text |