diff options
Diffstat (limited to 'app')
264 files changed, 1407 insertions, 1059 deletions
diff --git a/app/assets/images/cluster_app_logos/elasticsearch.png b/app/assets/images/cluster_app_logos/elasticsearch.png Binary files differnew file mode 100644 index 00000000000..96e9e0ff934 --- /dev/null +++ b/app/assets/images/cluster_app_logos/elasticsearch.png diff --git a/app/assets/images/cluster_app_logos/gitlab.png b/app/assets/images/cluster_app_logos/gitlab.png Binary files differnew file mode 100644 index 00000000000..cb2195fc6a2 --- /dev/null +++ b/app/assets/images/cluster_app_logos/gitlab.png diff --git a/app/assets/images/cluster_app_logos/helm.png b/app/assets/images/cluster_app_logos/helm.png Binary files differnew file mode 100644 index 00000000000..2989cae7b93 --- /dev/null +++ b/app/assets/images/cluster_app_logos/helm.png diff --git a/app/assets/images/cluster_app_logos/jeager.png b/app/assets/images/cluster_app_logos/jeager.png Binary files differnew file mode 100644 index 00000000000..be5bf2a0c9c --- /dev/null +++ b/app/assets/images/cluster_app_logos/jeager.png diff --git a/app/assets/images/cluster_app_logos/jupyterhub.png b/app/assets/images/cluster_app_logos/jupyterhub.png Binary files differnew file mode 100644 index 00000000000..80c7343067f --- /dev/null +++ b/app/assets/images/cluster_app_logos/jupyterhub.png diff --git a/app/assets/images/cluster_app_logos/kubernetes.png b/app/assets/images/cluster_app_logos/kubernetes.png Binary files differnew file mode 100644 index 00000000000..4d774909c10 --- /dev/null +++ b/app/assets/images/cluster_app_logos/kubernetes.png diff --git a/app/assets/images/cluster_app_logos/meltano.png b/app/assets/images/cluster_app_logos/meltano.png Binary files differnew file mode 100644 index 00000000000..7a2d82fbe27 --- /dev/null +++ b/app/assets/images/cluster_app_logos/meltano.png diff --git a/app/assets/images/cluster_app_logos/prometheus.png b/app/assets/images/cluster_app_logos/prometheus.png Binary files differnew file mode 100644 index 00000000000..a8663449b88 --- /dev/null +++ b/app/assets/images/cluster_app_logos/prometheus.png diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js index 84fef4d8b4f..8c4eccc34a3 100644 --- a/app/assets/javascripts/behaviors/index.js +++ b/app/assets/javascripts/behaviors/index.js @@ -1,15 +1,19 @@ import './autosize'; import './bind_in_out'; import './markdown/render_gfm'; +import initGFMInput from './markdown/gfm_auto_complete'; import initCopyAsGFM from './markdown/copy_as_gfm'; import initCopyToClipboard from './copy_to_clipboard'; import './details_behavior'; import installGlEmojiElement from './gl_emoji'; import './quick_submit'; import './requires_input'; +import initPageShortcuts from './shortcuts'; import './toggler_behavior'; -import '../preview_markdown'; +import './preview_markdown'; installGlEmojiElement(); +initGFMInput(); initCopyAsGFM(); initCopyToClipboard(); +initPageShortcuts(); diff --git a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js new file mode 100644 index 00000000000..a303e504cc7 --- /dev/null +++ b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js @@ -0,0 +1,19 @@ +import $ from 'jquery'; +import { convertPermissionToBoolean } from '~/lib/utils/common_utils'; +import GfmAutoComplete from '~/gfm_auto_complete'; + +export default function initGFMInput() { + $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { + const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources); + const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete); + + gfm.setup($(el), { + emojis: true, + members: enableGFM, + issues: enableGFM, + milestones: enableGFM, + mergeRequests: enableGFM, + labels: enableGFM, + }); + }); +} diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/behaviors/preview_markdown.js index 0964baf8954..0964baf8954 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/behaviors/preview_markdown.js diff --git a/app/assets/javascripts/behaviors/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts.js new file mode 100644 index 00000000000..7987a533ae5 --- /dev/null +++ b/app/assets/javascripts/behaviors/shortcuts.js @@ -0,0 +1,35 @@ +import Shortcuts from './shortcuts/shortcuts'; + +export default function initPageShortcuts() { + const { page } = document.body.dataset; + const pagesWithCustomShortcuts = [ + 'projects:activity', + 'projects:artifacts:browse', + 'projects:artifacts:file', + 'projects:blame:show', + 'projects:blob:show', + 'projects:commit:show', + 'projects:commits:show', + 'projects:find_file:show', + 'projects:issues:edit', + 'projects:issues:index', + 'projects:issues:new', + 'projects:issues:show', + 'projects:merge_requests:creations:diffs', + 'projects:merge_requests:creations:new', + 'projects:merge_requests:edit', + 'projects:merge_requests:index', + 'projects:merge_requests:show', + 'projects:network:show', + 'projects:show', + 'projects:tree:show', + 'groups:show', + ]; + + // the pages above have their own shortcuts sub-classes instantiated elsewhere + // TODO: replace this whitelist with something more automated/maintainable + if (page && !pagesWithCustomShortcuts.includes(page)) { + return new Shortcuts(); + } + return false; +} diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index 99c71d6524a..6719bfd6d22 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -1,9 +1,9 @@ import $ from 'jquery'; import Cookies from 'js-cookie'; import Mousetrap from 'mousetrap'; -import axios from './lib/utils/axios_utils'; -import { refreshCurrentPage, visitUrl } from './lib/utils/url_utility'; -import findAndFollowLink from './shortcuts_dashboard_navigation'; +import axios from '../../lib/utils/axios_utils'; +import { refreshCurrentPage, visitUrl } from '../../lib/utils/url_utility'; +import findAndFollowLink from '../../lib/utils/navigation_utility'; const defaultStopCallback = Mousetrap.stopCallback; Mousetrap.stopCallback = (e, element, combo) => { diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js index 908b9cab93d..052e33b4a2b 100644 --- a/app/assets/javascripts/shortcuts_blob.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js @@ -1,5 +1,5 @@ import Mousetrap from 'mousetrap'; -import { getLocationHash, visitUrl } from './lib/utils/url_utility'; +import { getLocationHash, visitUrl } from '../../lib/utils/url_utility'; import Shortcuts from './shortcuts'; const defaults = { diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js index 8658081c6c2..8658081c6c2 100644 --- a/app/assets/javascripts/shortcuts_find_file.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js index e9451be31fd..5e48bf5a35c 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js @@ -1,9 +1,9 @@ import $ from 'jquery'; import Mousetrap from 'mousetrap'; import _ from 'underscore'; -import Sidebar from './right_sidebar'; +import Sidebar from '../../right_sidebar'; import Shortcuts from './shortcuts'; -import { CopyAsGFM } from './behaviors/markdown/copy_as_gfm'; +import { CopyAsGFM } from '../markdown/copy_as_gfm'; export default class ShortcutsIssuable extends Shortcuts { constructor(isMergeRequest) { diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js index 6b595764bc5..fa9b2c9f755 100644 --- a/app/assets/javascripts/shortcuts_navigation.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js @@ -1,5 +1,5 @@ import Mousetrap from 'mousetrap'; -import findAndFollowLink from './shortcuts_dashboard_navigation'; +import findAndFollowLink from '../../lib/utils/navigation_utility'; import Shortcuts from './shortcuts'; export default class ShortcutsNavigation extends Shortcuts { diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js index a88c280fa3b..a88c280fa3b 100644 --- a/app/assets/javascripts/shortcuts_network.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js diff --git a/app/assets/javascripts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js index 41865dcf4ba..8b7e6a56d25 100644 --- a/app/assets/javascripts/shortcuts_wiki.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js @@ -1,6 +1,6 @@ import Mousetrap from 'mousetrap'; import ShortcutsNavigation from './shortcuts_navigation'; -import findAndFollowLink from './shortcuts_dashboard_navigation'; +import findAndFollowLink from '../../lib/utils/navigation_utility'; export default class ShortcutsWiki extends ShortcutsNavigation { constructor() { diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js index 68d4ddad551..1bdf1aeb76c 100644 --- a/app/assets/javascripts/blob/3d_viewer/index.js +++ b/app/assets/javascripts/blob/3d_viewer/index.js @@ -29,12 +29,12 @@ export default class Renderer { this.scene.add(this.camera); - // Setup the viewer + // Set up the viewer this.setupRenderer(); this.setupGrid(); this.setupLight(); - // Setup OrbitControls + // Set up OrbitControls this.controls = new OrbitControls( this.camera, this.renderer.domElement, diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue index 286529b4d13..cde22725a89 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.vue +++ b/app/assets/javascripts/boards/components/board_blank_state.vue @@ -83,7 +83,7 @@ export default { right on the way to making the most of your board. </p> <button - class="btn btn-create btn-inverted btn-block" + class="btn btn-success btn-inverted btn-block" type="button" @click.stop="addDefaultLists"> Add default lists diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index bc263cbbfea..8be99566f09 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -229,7 +229,7 @@ export default () => { template: ` <div class="board-extra-actions"> <button - class="btn btn-create prepend-left-10" + class="btn btn-success prepend-left-10" type="button" data-placement="bottom" ref="addIssuesButton" diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index 651f3b50236..0452729d3ff 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -2,6 +2,7 @@ /* eslint-disable vue/require-default-prop */ import { s__, sprintf } from '../../locale'; import eventHub from '../event_hub'; + import identicon from '../../vue_shared/components/identicon.vue'; import loadingButton from '../../vue_shared/components/loading_button.vue'; import { APPLICATION_STATUS, @@ -13,6 +14,7 @@ export default { components: { loadingButton, + identicon, }, props: { id: { @@ -31,6 +33,16 @@ type: String, required: false, }, + logoUrl: { + type: String, + required: false, + default: null, + }, + disabled: { + type: Boolean, + required: false, + default: false, + }, status: { type: String, required: false, @@ -60,6 +72,18 @@ isKnownStatus() { return Object.values(APPLICATION_STATUS).includes(this.status); }, + isInstalled() { + return ( + this.status === APPLICATION_STATUS.INSTALLED || this.status === APPLICATION_STATUS.UPDATED + ); + }, + hasLogo() { + return !!this.logoUrl; + }, + identiconId() { + // generate a deterministic integer id for the identicon background + return this.id.charCodeAt(0); + }, rowJsClass() { return `js-cluster-application-row-${this.id}`; }, @@ -128,37 +152,81 @@ <template> <div - :class="rowJsClass" - class="gl-responsive-table-row gl-responsive-table-row-col-span" + :class="[ + rowJsClass, + isInstalled && 'cluster-application-installed', + disabled && 'cluster-application-disabled' + ]" + class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span" > <div class="gl-responsive-table-row-layout" role="row" > - <a - v-if="titleLink" - :href="titleLink" - target="blank" - rel="noopener noreferrer" + <div + class="table-section append-right-8 section-align-top" role="gridcell" - class="table-section section-15 section-align-top js-cluster-application-title" > - {{ title }} - </a> - <span - v-else - class="table-section section-15 section-align-top js-cluster-application-title" - > - {{ title }} - </span> + <img + v-if="hasLogo" + :src="logoUrl" + :alt="`${title} logo`" + class="cluster-application-logo avatar s40" + /> + <identicon + v-else + :entity-id="identiconId" + :entity-name="title" + size-class="s40" + /> + </div> <div - class="table-section section-wrap" + class="table-section cluster-application-description section-wrap" role="gridcell" > + <strong> + <a + v-if="titleLink" + :href="titleLink" + target="blank" + rel="noopener noreferrer" + class="js-cluster-application-title" + > + {{ title }} + </a> + <span + v-else + class="js-cluster-application-title" + > + {{ title }} + </span> + </strong> <slot name="description"></slot> + <div + v-if="hasError || isUnknownStatus" + class="cluster-application-error text-danger prepend-top-10" + > + <p class="js-cluster-application-general-error-message append-bottom-0"> + {{ generalErrorDescription }} + </p> + <ul v-if="statusReason || requestReason"> + <li + v-if="statusReason" + class="js-cluster-application-status-error-message" + > + {{ statusReason }} + </li> + <li + v-if="requestReason" + class="js-cluster-application-request-error-message" + > + {{ requestReason }} + </li> + </ul> + </div> </div> <div - :class="{ 'section-20': showManageButton, 'section-15': !showManageButton }" + :class="{ 'section-25': showManageButton, 'section-15': !showManageButton }" class="table-section table-button-footer section-align-top" role="gridcell" > @@ -168,6 +236,7 @@ > <a :href="manageLink" + :class="{ disabled: disabled }" class="btn" > {{ manageButtonLabel }} @@ -176,7 +245,7 @@ <div class="btn-group table-action-buttons"> <loading-button :loading="installButtonLoading" - :disabled="installButtonDisabled" + :disabled="disabled || installButtonDisabled" :label="installButtonLabel" class="js-cluster-application-install-button" @click="installClicked" @@ -184,35 +253,5 @@ </div> </div> </div> - <div - v-if="hasError || isUnknownStatus" - class="gl-responsive-table-row-layout" - role="row" - > - <div - class="alert alert-danger alert-block append-bottom-0 clusters-error-alert" - role="gridcell" - > - <div> - <p class="js-cluster-application-general-error-message"> - {{ generalErrorDescription }} - </p> - <ul v-if="statusReason || requestReason"> - <li - v-if="statusReason" - class="js-cluster-application-status-error-message" - > - {{ statusReason }} - </li> - <li - v-if="requestReason" - class="js-cluster-application-request-error-message" - > - {{ requestReason }} - </li> - </ul> - </div> - </div> - </div> </div> </template> diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index d708a9e595a..a1069985178 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -1,5 +1,14 @@ <script> import _ from 'underscore'; +import helmInstallIllustration from '@gitlab-org/gitlab-svgs/illustrations/kubernetes-installation.svg'; +import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png'; +import gitlabLogo from 'images/cluster_app_logos/gitlab.png'; +import helmLogo from 'images/cluster_app_logos/helm.png'; +import jeagerLogo from 'images/cluster_app_logos/jeager.png'; +import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png'; +import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png'; +import meltanoLogo from 'images/cluster_app_logos/meltano.png'; +import prometheusLogo from 'images/cluster_app_logos/prometheus.png'; import { s__, sprintf } from '../../locale'; import applicationRow from './application_row.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; @@ -37,21 +46,21 @@ export default { default: '', }, }, + data: () => ({ + elasticsearchLogo, + gitlabLogo, + helmLogo, + jeagerLogo, + jupyterhubLogo, + kubernetesLogo, + meltanoLogo, + prometheusLogo, + }), computed: { - generalApplicationDescription() { - return sprintf( - _.escape( - s__( - `ClusterIntegration|Install applications on your Kubernetes cluster. - Read more about %{helpLink}`, - ), - ), - { - helpLink: `<a href="${this.helpPath}"> - ${_.escape(s__('ClusterIntegration|installing applications'))} - </a>`, - }, - false, + helmInstalled() { + return ( + this.applications.helm.status === APPLICATION_STATUS.INSTALLED || + this.applications.helm.status === APPLICATION_STATUS.UPDATED ); }, ingressId() { @@ -128,224 +137,240 @@ export default { return this.applications.jupyter.hostname; }, }, + created() { + this.helmInstallIllustration = helmInstallIllustration; + }, }; </script> <template> - <section - id="cluster-applications" - class="settings no-animate expanded" - > - <div class="settings-header"> - <h4> - {{ s__('ClusterIntegration|Applications') }} - </h4> - <p - class="append-bottom-0" - v-html="generalApplicationDescription" - > - </p> - </div> + <section id="cluster-applications"> + <h4> + {{ s__('ClusterIntegration|Applications') }} + </h4> + <p class="append-bottom-0"> + {{ s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster. + Helm Tiller is required to install any of the following applications.`) }} + <a :href="helpPath"> + {{ __('More information') }} + </a> + </p> - <div class="settings-content"> - <div class="append-bottom-20"> - <application-row - id="helm" - :title="applications.helm.title" - :status="applications.helm.status" - :status-reason="applications.helm.statusReason" - :request-status="applications.helm.requestStatus" - :request-reason="applications.helm.requestReason" - title-link="https://docs.helm.sh/" - > - <div slot="description"> - {{ s__(`ClusterIntegration|Helm streamlines installing - and managing Kubernetes applications. - Tiller runs inside of your Kubernetes Cluster, - and manages releases of your charts.`) }} - </div> - </application-row> - <application-row - :id="ingressId" - :title="applications.ingress.title" - :status="applications.ingress.status" - :status-reason="applications.ingress.statusReason" - :request-status="applications.ingress.requestStatus" - :request-reason="applications.ingress.requestReason" - title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/" + <div class="cluster-application-list prepend-top-10"> + <application-row + id="helm" + :logo-url="helmLogo" + :title="applications.helm.title" + :status="applications.helm.status" + :status-reason="applications.helm.statusReason" + :request-status="applications.helm.requestStatus" + :request-reason="applications.helm.requestReason" + class="rounded-top" + title-link="https://docs.helm.sh/" + > + <div slot="description"> + {{ s__(`ClusterIntegration|Helm streamlines installing + and managing Kubernetes applications. + Tiller runs inside of your Kubernetes Cluster, + and manages releases of your charts.`) }} + </div> + </application-row> + <div + v-show="!helmInstalled" + class="cluster-application-warning" + > + <div + class="svg-container" + v-html="helmInstallIllustration" > - <div slot="description"> - <p> - {{ s__(`ClusterIntegration|Ingress gives you a way to route - requests to services based on the request host or path, - centralizing a number of services into a single entrypoint.`) }} - </p> + </div> + {{ s__(`ClusterIntegration|You must first install Helm Tiller before + installing the applications below`) }} + </div> + <application-row + :id="ingressId" + :logo-url="kubernetesLogo" + :title="applications.ingress.title" + :status="applications.ingress.status" + :status-reason="applications.ingress.statusReason" + :request-status="applications.ingress.requestStatus" + :request-reason="applications.ingress.requestReason" + :disabled="!helmInstalled" + title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/" + > + <div slot="description"> + <p> + {{ s__(`ClusterIntegration|Ingress gives you a way to route + requests to services based on the request host or path, + centralizing a number of services into a single entrypoint.`) }} + </p> - <template v-if="ingressInstalled"> - <div class="form-group"> - <label for="ingress-ip-address"> - {{ s__('ClusterIntegration|Ingress IP Address') }} - </label> - <div - v-if="ingressExternalIp" - class="input-group" - > - <input - id="ingress-ip-address" - :value="ingressExternalIp" - type="text" - class="form-control js-ip-address" - readonly - /> - <span class="input-group-append"> - <clipboard-button - :text="ingressExternalIp" - :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')" - class="input-group-text js-clipboard-btn" - /> - </span> - </div> + <template v-if="ingressInstalled"> + <div class="form-group"> + <label for="ingress-ip-address"> + {{ s__('ClusterIntegration|Ingress IP Address') }} + </label> + <div + v-if="ingressExternalIp" + class="input-group" + > <input - v-else + id="ingress-ip-address" + :value="ingressExternalIp" type="text" class="form-control js-ip-address" readonly - value="?" /> + <span class="input-group-append"> + <clipboard-button + :text="ingressExternalIp" + :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')" + class="input-group-text js-clipboard-btn" + /> + </span> </div> + <input + v-else + type="text" + class="form-control js-ip-address" + readonly + value="?" + /> + </div> - <p - v-if="!ingressExternalIp" - class="settings-message js-no-ip-message" - > - {{ s__(`ClusterIntegration|The IP address is in - the process of being assigned. Please check your Kubernetes - cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }} + <p + v-if="!ingressExternalIp" + class="settings-message js-no-ip-message" + > + {{ s__(`ClusterIntegration|The IP address is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }} - <a - :href="ingressHelpPath" - target="_blank" - rel="noopener noreferrer" - > - {{ __('More information') }} - </a> - </p> + <a + :href="ingressHelpPath" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + </a> + </p> - <p> - {{ s__(`ClusterIntegration|Point a wildcard DNS to this - generated IP address in order to access - your application after it has been deployed.`) }} - <a - :href="ingressDnsHelpPath" - target="_blank" - rel="noopener noreferrer" - > - {{ __('More information') }} - </a> - </p> + <p> + {{ s__(`ClusterIntegration|Point a wildcard DNS to this + generated IP address in order to access + your application after it has been deployed.`) }} + <a + :href="ingressDnsHelpPath" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + </a> + </p> - </template> - <div - v-else - v-html="ingressDescription" - > - </div> - </div> - </application-row> - <application-row - id="prometheus" - :title="applications.prometheus.title" - :manage-link="managePrometheusPath" - :status="applications.prometheus.status" - :status-reason="applications.prometheus.statusReason" - :request-status="applications.prometheus.requestStatus" - :request-reason="applications.prometheus.requestReason" - title-link="https://prometheus.io/docs/introduction/overview/" - > + </template> <div - slot="description" - v-html="prometheusDescription" + v-html="ingressDescription" > </div> - </application-row> - <application-row - id="runner" - :title="applications.runner.title" - :status="applications.runner.status" - :status-reason="applications.runner.statusReason" - :request-status="applications.runner.requestStatus" - :request-reason="applications.runner.requestReason" - title-link="https://docs.gitlab.com/runner/" - > - <div slot="description"> - {{ s__(`ClusterIntegration|GitLab Runner connects to this - project's repository and executes CI/CD jobs, - pushing results back and deploying, - applications to production.`) }} - </div> - </application-row> - <application-row - id="jupyter" - :title="applications.jupyter.title" - :status="applications.jupyter.status" - :status-reason="applications.jupyter.statusReason" - :request-status="applications.jupyter.requestStatus" - :request-reason="applications.jupyter.requestReason" - :install-application-request-params="{ hostname: applications.jupyter.hostname }" - title-link="https://jupyterhub.readthedocs.io/en/stable/" + </div> + </application-row> + <application-row + id="prometheus" + :logo-url="prometheusLogo" + :title="applications.prometheus.title" + :manage-link="managePrometheusPath" + :status="applications.prometheus.status" + :status-reason="applications.prometheus.statusReason" + :request-status="applications.prometheus.requestStatus" + :request-reason="applications.prometheus.requestReason" + :disabled="!helmInstalled" + title-link="https://prometheus.io/docs/introduction/overview/" + > + <div + slot="description" + v-html="prometheusDescription" > - <div slot="description"> - <p> - {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns, - manages, and proxies multiple instances of the single-user - Jupyter notebook server. JupyterHub can be used to serve - notebooks to a class of students, a corporate data science group, - or a scientific research group.`) }} - </p> + </div> + </application-row> + <application-row + id="runner" + :logo-url="gitlabLogo" + :title="applications.runner.title" + :status="applications.runner.status" + :status-reason="applications.runner.statusReason" + :request-status="applications.runner.requestStatus" + :request-reason="applications.runner.requestReason" + :disabled="!helmInstalled" + title-link="https://docs.gitlab.com/runner/" + > + <div slot="description"> + {{ s__(`ClusterIntegration|GitLab Runner connects to this + project's repository and executes CI/CD jobs, + pushing results back and deploying, + applications to production.`) }} + </div> + </application-row> + <application-row + id="jupyter" + :logo-url="jupyterhubLogo" + :title="applications.jupyter.title" + :status="applications.jupyter.status" + :status-reason="applications.jupyter.statusReason" + :request-status="applications.jupyter.requestStatus" + :request-reason="applications.jupyter.requestReason" + :install-application-request-params="{ hostname: applications.jupyter.hostname }" + :disabled="!helmInstalled" + class="hide-bottom-border rounded-bottom" + title-link="https://jupyterhub.readthedocs.io/en/stable/" + > + <div slot="description"> + <p> + {{ s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns, + manages, and proxies multiple instances of the single-user + Jupyter notebook server. JupyterHub can be used to serve + notebooks to a class of students, a corporate data science group, + or a scientific research group.`) }} + </p> - <template v-if="ingressExternalIp"> - <div class="form-group"> - <label for="jupyter-hostname"> - {{ s__('ClusterIntegration|Jupyter Hostname') }} - </label> + <template v-if="ingressExternalIp"> + <div class="form-group"> + <label for="jupyter-hostname"> + {{ s__('ClusterIntegration|Jupyter Hostname') }} + </label> - <div class="input-group"> - <input - v-model="applications.jupyter.hostname" - :readonly="jupyterInstalled" - type="text" - class="form-control js-hostname" + <div class="input-group"> + <input + v-model="applications.jupyter.hostname" + :readonly="jupyterInstalled" + type="text" + class="form-control js-hostname" + /> + <span + class="input-group-btn" + > + <clipboard-button + :text="jupyterHostname" + :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')" + class="js-clipboard-btn" /> - <span - class="input-group-btn" - > - <clipboard-button - :text="jupyterHostname" - :title="s__('ClusterIntegration|Copy Jupyter Hostname to clipboard')" - class="js-clipboard-btn" - /> - </span> - </div> + </span> </div> - <p v-if="ingressInstalled"> - {{ s__(`ClusterIntegration|Replace this with your own hostname if you want. - If you do so, point hostname to Ingress IP Address from above.`) }} - <a - :href="ingressDnsHelpPath" - target="_blank" - rel="noopener noreferrer" - > - {{ __('More information') }} - </a> - </p> - </template> - </div> - </application-row> - <!-- - NOTE: Don't forget to update `clusters.scss` - min-height for this block and uncomment `application_spec` tests - --> - </div> + </div> + <p v-if="ingressInstalled"> + {{ s__(`ClusterIntegration|Replace this with your own hostname if you want. + If you do so, point hostname to Ingress IP Address from above.`) }} + <a + :href="ingressDnsHelpPath" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + </a> + </p> + </template> + </div> + </application-row> </div> </section> </template> diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js index 742cf490ad2..539d0d29e0d 100644 --- a/app/assets/javascripts/commons/polyfills.js +++ b/app/assets/javascripts/commons/polyfills.js @@ -14,10 +14,10 @@ import 'core-js/es6/map'; import 'core-js/es6/weak-map'; // Browser polyfills -import 'classlist-polyfill'; import 'formdata-polyfill'; import './polyfills/custom_event'; import './polyfills/element'; import './polyfills/event'; import './polyfills/nodelist'; import './polyfills/request_idle_callback'; +import './polyfills/svg'; diff --git a/app/assets/javascripts/commons/polyfills/element.js b/app/assets/javascripts/commons/polyfills/element.js index b593bde6aa2..dde5e8f54f9 100644 --- a/app/assets/javascripts/commons/polyfills/element.js +++ b/app/assets/javascripts/commons/polyfills/element.js @@ -1,12 +1,17 @@ -Element.prototype.closest = Element.prototype.closest || +// polyfill Element.classList and DOMTokenList with classList.js +import 'classlist-polyfill'; + +Element.prototype.closest = + Element.prototype.closest || function closest(selector, selectedElement = this) { if (!selectedElement) return null; - return selectedElement.matches(selector) ? - selectedElement : - Element.prototype.closest(selector, selectedElement.parentElement); + return selectedElement.matches(selector) + ? selectedElement + : Element.prototype.closest(selector, selectedElement.parentElement); }; -Element.prototype.matches = Element.prototype.matches || +Element.prototype.matches = + Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || @@ -15,13 +20,15 @@ Element.prototype.matches = Element.prototype.matches || function matches(selector) { const elms = (this.document || this.ownerDocument).querySelectorAll(selector); let i = elms.length - 1; - while (i >= 0 && elms.item(i) !== this) { i -= 1; } + while (i >= 0 && elms.item(i) !== this) { + i -= 1; + } return i > -1; }; // From the polyfill on MDN, https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill -((arr) => { - arr.forEach((item) => { +(arr => { + arr.forEach(item => { if (Object.prototype.hasOwnProperty.call(item, 'remove')) { return; } diff --git a/app/assets/javascripts/commons/polyfills/svg.js b/app/assets/javascripts/commons/polyfills/svg.js new file mode 100644 index 00000000000..8648a568f6f --- /dev/null +++ b/app/assets/javascripts/commons/polyfills/svg.js @@ -0,0 +1,5 @@ +import svg4everybody from 'svg4everybody'; + +// polyfill support for external SVG file references via <use xlink:href> +// @see https://css-tricks.com/svg-use-external-source/ +svg4everybody(); diff --git a/app/assets/javascripts/diffs/components/no_changes.vue b/app/assets/javascripts/diffs/components/no_changes.vue index d817157fbcd..6905630ad8c 100644 --- a/app/assets/javascripts/diffs/components/no_changes.vue +++ b/app/assets/javascripts/diffs/components/no_changes.vue @@ -38,7 +38,7 @@ export default { <div class="text-center"> <a :href="newBlobPath" - class="btn btn-save" + class="btn btn-success" > {{ __('Create commit') }} </a> diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js deleted file mode 100644 index a5af37e80b6..00000000000 --- a/app/assets/javascripts/dispatcher.js +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint-disable consistent-return, no-new */ - -import $ from 'jquery'; -import GfmAutoComplete from './gfm_auto_complete'; -import { convertPermissionToBoolean } from './lib/utils/common_utils'; -import GlFieldErrors from './gl_field_errors'; -import Shortcuts from './shortcuts'; -import SearchAutocomplete from './search_autocomplete'; -import performanceBar from './performance_bar'; - -function initSearch() { - // Only when search form is present - if ($('.search').length) { - return new SearchAutocomplete(); - } -} - -function initFieldErrors() { - $('.gl-show-field-errors').each((i, form) => { - new GlFieldErrors(form); - }); -} - -function initPageShortcuts(page) { - const pagesWithCustomShortcuts = [ - 'projects:activity', - 'projects:artifacts:browse', - 'projects:artifacts:file', - 'projects:blame:show', - 'projects:blob:show', - 'projects:commit:show', - 'projects:commits:show', - 'projects:find_file:show', - 'projects:issues:edit', - 'projects:issues:index', - 'projects:issues:new', - 'projects:issues:show', - 'projects:merge_requests:creations:diffs', - 'projects:merge_requests:creations:new', - 'projects:merge_requests:edit', - 'projects:merge_requests:index', - 'projects:merge_requests:show', - 'projects:network:show', - 'projects:show', - 'projects:tree:show', - 'groups:show', - ]; - - if (pagesWithCustomShortcuts.indexOf(page) === -1) { - new Shortcuts(); - } -} - -function initGFMInput() { - $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { - const gfm = new GfmAutoComplete( - gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources, - ); - const enableGFM = convertPermissionToBoolean( - el.dataset.supportsAutocomplete, - ); - gfm.setup($(el), { - emojis: true, - members: enableGFM, - issues: enableGFM, - milestones: enableGFM, - mergeRequests: enableGFM, - labels: enableGFM, - }); - }); -} - -function initPerformanceBar() { - if (document.querySelector('#js-peek')) { - performanceBar({ container: '#js-peek' }); - } -} - -export default () => { - initSearch(); - initFieldErrors(); - - const page = $('body').attr('data-page'); - if (page) { - initPageShortcuts(page); - initGFMInput(); - initPerformanceBar(); - } -}; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index ff969bb94a4..d2778bcdf1c 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import Dropzone from 'dropzone'; import _ from 'underscore'; -import './preview_markdown'; +import './behaviors/preview_markdown'; import csrf from './lib/utils/csrf'; import axios from './lib/utils/axios_utils'; diff --git a/app/assets/javascripts/environments/components/empty_state.vue b/app/assets/javascripts/environments/components/empty_state.vue index 00e63c3467a..cf78f89981e 100644 --- a/app/assets/javascripts/environments/components/empty_state.vue +++ b/app/assets/javascripts/environments/components/empty_state.vue @@ -35,7 +35,7 @@ code gets deployed, such as staging or production.`) }} <a v-if="canCreateEnvironment" :href="newPath" - class="btn btn-create js-new-environment-button" + class="btn btn-success js-new-environment-button" > {{ s__("Environments|New environment") }} </a> diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index 8efdfb8abe0..e2ecf426e64 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -107,7 +107,7 @@ > <a :href="newEnvironmentPath" - class="btn btn-create" + class="btn btn-success" > {{ s__("Environments|New environment") }} </a> diff --git a/app/assets/javascripts/feature_highlight/feature_highlight.js b/app/assets/javascripts/feature_highlight/feature_highlight.js index 2f27c9351bc..03dfa942d69 100644 --- a/app/assets/javascripts/feature_highlight/feature_highlight.js +++ b/app/assets/javascripts/feature_highlight/feature_highlight.js @@ -16,7 +16,7 @@ export function setupFeatureHighlightPopover(id, debounceTimeout = 300) { const hideOnScroll = togglePopover.bind($selector, false); $selector - // Setup popover + // Set up popover .data('content', $popoverContent.prop('outerHTML')) .popover({ html: true, diff --git a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js index 1f9c3f41e52..b4588cc1318 100644 --- a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js @@ -5,7 +5,7 @@ const tokenKeys = [{ type: 'string', param: 'status', symbol: '', - icon: 'signal', + icon: 'messages', tag: 'status', }]; diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 184b34b7b5e..8aecf9725e6 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -62,7 +62,7 @@ export default class DropdownHint extends FilteredSearchDropdown { renderContent() { const dropdownData = this.tokenKeys.get() .map(tokenKey => ({ - icon: `fa-${tokenKey.icon}`, + icon: `${gon.sprite_icons}#${tokenKey.icon}`, hint: tokenKey.key, tag: `:${tokenKey.tag}`, type: tokenKey.type, diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js index cce2c07dc3e..cc7291c9f59 100644 --- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js @@ -1,6 +1,6 @@ import FilteredSearchTokenKeys from './filtered_search_token_keys'; -const tokenKeys = [{ +export const tokenKeys = [{ key: 'author', type: 'string', param: 'username', @@ -19,14 +19,14 @@ const tokenKeys = [{ type: 'string', param: 'title', symbol: '%', - icon: 'clock-o', + icon: 'clock', tag: '%milestone', }, { key: 'label', type: 'array', param: 'name[]', symbol: '~', - icon: 'tag', + icon: 'labels', tag: '~label', }]; @@ -37,19 +37,19 @@ if (gon.current_user_id) { type: 'string', param: 'emoji', symbol: '', - icon: 'thumbs-up', + icon: 'thumb-up', tag: 'emoji', }); } -const alternativeTokenKeys = [{ +export const alternativeTokenKeys = [{ key: 'label', type: 'string', param: 'name', symbol: '~', }]; -const conditions = [{ +export const conditions = [{ url: 'assignee_id=0', tokenKey: 'assignee', value: 'none', diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index c74de7ac34d..e672284a2d0 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -18,7 +18,7 @@ export default class GLForm { }); // Before we start, we should clean up any previous data for this form this.destroy(); - // Setup the form + // Set up the form this.setupForm(); this.form.data('glForm', this); } diff --git a/app/assets/javascripts/ide/components/file_row_extra.vue b/app/assets/javascripts/ide/components/file_row_extra.vue new file mode 100644 index 00000000000..44a360ab909 --- /dev/null +++ b/app/assets/javascripts/ide/components/file_row_extra.vue @@ -0,0 +1,104 @@ +<script> +import { mapGetters } from 'vuex'; +import { n__, __, sprintf } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import Icon from '~/vue_shared/components/icon.vue'; +import NewDropdown from './new_dropdown/index.vue'; +import ChangedFileIcon from './changed_file_icon.vue'; +import MrFileIcon from './mr_file_icon.vue'; + +export default { + name: 'FileRowExtra', + directives: { + tooltip, + }, + components: { + Icon, + NewDropdown, + ChangedFileIcon, + MrFileIcon, + }, + props: { + file: { + type: Object, + required: true, + }, + mouseOver: { + type: Boolean, + required: true, + }, + }, + computed: { + ...mapGetters([ + 'getChangesInFolder', + 'getUnstagedFilesCountForPath', + 'getStagedFilesCountForPath', + ]), + folderUnstagedCount() { + return this.getUnstagedFilesCountForPath(this.file.path); + }, + folderStagedCount() { + return this.getStagedFilesCountForPath(this.file.path); + }, + changesCount() { + return this.getChangesInFolder(this.file.path); + }, + folderChangesTooltip() { + if (this.changesCount === 0) return undefined; + + if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) { + return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount); + } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) { + return n__('%d staged change', '%d staged changes', this.folderStagedCount); + } + + return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), { + unstaged: this.folderUnstagedCount, + staged: this.folderStagedCount, + }); + }, + showTreeChangesCount() { + return this.file.type === 'tree' && this.changesCount > 0 && !this.file.opened; + }, + showChangedFileIcon() { + return this.file.changed || this.file.tempFile || this.file.staged; + }, + }, +}; +</script> + +<template> + <div class="float-right ide-file-icon-holder"> + <mr-file-icon + v-if="file.mrChange" + /> + <span + v-if="showTreeChangesCount" + class="ide-tree-changes" + > + {{ changesCount }} + <icon + v-tooltip + :title="folderChangesTooltip" + :size="12" + data-container="body" + data-placement="right" + name="file-modified" + css-classes="prepend-left-5 ide-file-modified" + /> + </span> + <changed-file-icon + v-else-if="showChangedFileIcon" + :file="file" + :show-tooltip="true" + :show-staged-icon="true" + :force-modified-icon="true" + /> + <new-dropdown + :type="file.type" + :path="file.path" + :mouse-over="mouseOver" + class="prepend-left-8" + /> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index 00ae5ea2c15..e658d1bf956 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -2,15 +2,16 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; -import RepoFile from './repo_file.vue'; +import FileRow from '~/vue_shared/components/file_row.vue'; import NavDropdown from './nav_dropdown.vue'; +import FileRowExtra from './file_row_extra.vue'; export default { components: { Icon, - RepoFile, SkeletonLoadingContainer, NavDropdown, + FileRow, }, props: { viewerType: { @@ -34,8 +35,9 @@ export default { this.updateViewer(this.viewerType); }, methods: { - ...mapActions(['updateViewer']), + ...mapActions(['updateViewer', 'toggleTreeOpen']), }, + FileRowExtra, }; </script> @@ -63,11 +65,13 @@ export default { <div class="ide-tree-body h-100" > - <repo-file + <file-row v-for="file in currentTree.tree" :key="file.key" :file="file" :level="0" + :extra-component="$options.FileRowExtra" + @toggleTreeOpen="toggleTreeOpen" /> </div> </template> diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue deleted file mode 100644 index 110eda83bb4..00000000000 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ /dev/null @@ -1,227 +0,0 @@ -<script> -import { mapActions, mapGetters } from 'vuex'; -import { n__, __, sprintf } from '~/locale'; -import tooltip from '~/vue_shared/directives/tooltip'; -import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; -import Icon from '~/vue_shared/components/icon.vue'; -import FileIcon from '~/vue_shared/components/file_icon.vue'; -import router from '../ide_router'; -import NewDropdown from './new_dropdown/index.vue'; -import FileStatusIcon from './repo_file_status_icon.vue'; -import ChangedFileIcon from './changed_file_icon.vue'; -import MrFileIcon from './mr_file_icon.vue'; - -export default { - name: 'RepoFile', - directives: { - tooltip, - }, - components: { - SkeletonLoadingContainer, - NewDropdown, - FileStatusIcon, - FileIcon, - ChangedFileIcon, - MrFileIcon, - Icon, - }, - props: { - file: { - type: Object, - required: true, - }, - level: { - type: Number, - required: true, - }, - }, - data() { - return { - mouseOver: false, - }; - }, - computed: { - ...mapGetters([ - 'getChangesInFolder', - 'getUnstagedFilesCountForPath', - 'getStagedFilesCountForPath', - ]), - folderUnstagedCount() { - return this.getUnstagedFilesCountForPath(this.file.path); - }, - folderStagedCount() { - return this.getStagedFilesCountForPath(this.file.path); - }, - changesCount() { - return this.getChangesInFolder(this.file.path); - }, - folderChangesTooltip() { - if (this.changesCount === 0) return undefined; - - if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) { - return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount); - } else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) { - return n__('%d staged change', '%d staged changes', this.folderStagedCount); - } - - return sprintf(__('%{unstaged} unstaged and %{staged} staged changes'), { - unstaged: this.folderUnstagedCount, - staged: this.folderStagedCount, - }); - }, - isTree() { - return this.file.type === 'tree'; - }, - isBlob() { - return this.file.type === 'blob'; - }, - levelIndentation() { - return { - marginLeft: `${this.level * 16}px`, - }; - }, - fileClass() { - return { - 'file-open': this.isBlob && this.file.opened, - 'file-active': this.isBlob && this.file.active, - folder: this.isTree, - 'is-open': this.file.opened, - }; - }, - showTreeChangesCount() { - return this.isTree && this.changesCount > 0 && !this.file.opened; - }, - showChangedFileIcon() { - return this.file.changed || this.file.tempFile || this.file.staged; - }, - }, - watch: { - 'file.active': function fileActiveWatch(active) { - if (this.file.type === 'blob' && active) { - this.scrollIntoView(); - } - }, - }, - mounted() { - if (this.hasPathAtCurrentRoute()) { - this.scrollIntoView(true); - } - }, - methods: { - ...mapActions(['toggleTreeOpen']), - clickFile() { - // Manual Action if a tree is selected/opened - if (this.isTree && this.hasUrlAtCurrentRoute()) { - this.toggleTreeOpen(this.file.path); - } - - router.push(`/project${this.file.url}`); - }, - scrollIntoView(isInit = false) { - const block = isInit && this.isTree ? 'center' : 'nearest'; - - this.$el.scrollIntoView({ - behavior: 'smooth', - block, - }); - }, - hasPathAtCurrentRoute() { - if (!this.$router || !this.$router.currentRoute) { - return false; - } - - // - strip route up to "/-/" and ending "/" - const routePath = this.$router.currentRoute.path - .replace(/^.*?[/]-[/]/g, '') - .replace(/[/]$/g, ''); - - // - strip ending "/" - const filePath = this.file.path.replace(/[/]$/g, ''); - - return filePath === routePath; - }, - hasUrlAtCurrentRoute() { - return this.$router.currentRoute.path === `/project${this.file.url}`; - }, - toggleHover(over) { - this.mouseOver = over; - }, - }, -}; -</script> - -<template> - <div> - <div - :class="fileClass" - class="file" - role="button" - @click="clickFile" - @mouseover="toggleHover(true)" - @mouseout="toggleHover(false)" - > - <div - class="file-name" - > - <span - :style="levelIndentation" - class="ide-file-name str-truncated" - > - <file-icon - :file-name="file.name" - :loading="file.loading" - :folder="isTree" - :opened="file.opened" - :size="16" - /> - {{ file.name }} - <file-status-icon - :file="file" - /> - </span> - <span class="float-right ide-file-icon-holder"> - <mr-file-icon - v-if="file.mrChange" - /> - <span - v-if="showTreeChangesCount" - class="ide-tree-changes" - > - {{ changesCount }} - <icon - v-tooltip - :title="folderChangesTooltip" - :size="12" - data-container="body" - data-placement="right" - name="file-modified" - css-classes="prepend-left-5 ide-file-modified" - /> - </span> - <changed-file-icon - v-else-if="showChangedFileIcon" - :file="file" - :show-tooltip="true" - :show-staged-icon="true" - :force-modified-icon="true" - class="float-right" - /> - </span> - <new-dropdown - :type="file.type" - :path="file.path" - :mouse-over="mouseOver" - class="float-right prepend-left-8" - /> - </div> - </div> - <template v-if="file.opened"> - <repo-file - v-for="childFile in file.tree" - :key="childFile.key" - :file="childFile" - :level="level + 1" - /> - </template> - </div> -</template> diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue index 597c6d69a81..7fd3ea61aa7 100644 --- a/app/assets/javascripts/issue_show/components/edit_actions.vue +++ b/app/assets/javascripts/issue_show/components/edit_actions.vue @@ -53,7 +53,7 @@ <button :class="{ disabled: formState.updateLoading || !isSubmitEnabled }" :disabled="formState.updateLoading || !isSubmitEnabled" - class="btn btn-save float-left" + class="btn btn-success float-left" type="submit" @click.prevent="updateIssuable"> Save changes diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue index 3e49b04e44e..63324e68d68 100644 --- a/app/assets/javascripts/jobs/components/header.vue +++ b/app/assets/javascripts/jobs/components/header.vue @@ -57,7 +57,7 @@ export default { actions.push({ label: 'New issue', path: this.job.new_issue_path, - cssClass: 'js-new-issue btn btn-new btn-inverted d-none d-md-block d-lg-block d-xl-block', + cssClass: 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block', type: 'link', }); } diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue index 1210ccd038a..80c2a5fb48b 100644 --- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue +++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue @@ -130,7 +130,7 @@ export default { <a v-if="job.new_issue_path" :href="job.new_issue_path" - class="js-new-issue btn btn-new btn-inverted" + class="js-new-issue btn btn-success btn-inverted" > {{ __('New issue') }} </a> diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/lib/utils/navigation_utility.js index 9f69f110d06..1579b225e44 100644 --- a/app/assets/javascripts/shortcuts_dashboard_navigation.js +++ b/app/assets/javascripts/lib/utils/navigation_utility.js @@ -1,4 +1,4 @@ -import { visitUrl } from './lib/utils/url_utility'; +import { visitUrl } from './url_utility'; /** * Helper function that finds the href of the fiven selector and updates the location. diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index c5a5f64abac..e8aac51a299 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -2,7 +2,6 @@ import jQuery from 'jquery'; import Cookies from 'js-cookie'; -import svg4everybody from 'svg4everybody'; // bootstrap webpack, common libs, polyfills, and behaviors import './webpack'; @@ -25,11 +24,12 @@ import initLayoutNav from './layout_nav'; import './feature_highlight/feature_highlight_options'; import LazyLoader from './lazy_loader'; import initLogoAnimation from './logo'; -import './milestone_select'; import './frequent_items'; import initBreadcrumbs from './breadcrumb'; -import initDispatcher from './dispatcher'; import initUsagePingConsent from './usage_ping_consent'; +import initPerformanceBar from './performance_bar'; +import initSearchAutocomplete from './search_autocomplete'; +import GlFieldErrors from './gl_field_errors'; // expose jQuery as global (TODO: remove these) window.jQuery = jQuery; @@ -41,8 +41,6 @@ if (process.env.NODE_ENV !== 'production' && gon && gon.test_env) { import(/* webpackMode: "eager" */ './test_utils/'); } -svg4everybody(); - document.addEventListener('beforeunload', () => { // Unbind scroll events $(document).off('scroll'); @@ -81,6 +79,9 @@ document.addEventListener('DOMContentLoaded', () => { initLogoAnimation(); initUsagePingConsent(); + if (document.querySelector('.search')) initSearchAutocomplete(); + if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' }); + // Set the default path for all cookies to GitLab's root directory Cookies.defaults.path = gon.relative_url_root || '/'; @@ -270,5 +271,6 @@ document.addEventListener('DOMContentLoaded', () => { }); } - initDispatcher(); + // initialize field errors + $('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form)); }); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 8b1d8f6055e..0c966e0808a 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -631,7 +631,7 @@ export default class Notes { * * deactivates the submit button when text is empty * hides the preview button when text is empty - * setup GFM auto complete + * set up GFM auto complete * show the form */ setupNoteForm(form, enableGFM = defaultAutocompleteConfig) { @@ -954,7 +954,7 @@ export default class Notes { * Note: dataHolder must have the "discussionId" and "lineCode" data attributes set. */ setupDiscussionNoteForm(dataHolder, form) { - // setup note target + // set up note target let diffFileData = dataHolder.closest('.text-file'); if (diffFileData.length === 0) { @@ -1036,7 +1036,7 @@ export default class Notes { $diffFile[0].dispatchEvent(clickEvent); - // Setup comment form + // Set up comment form let newForm; const $noteContainer = $link.closest('.diff-viewer').find('.note-container'); const $form = $noteContainer.find('> .discussion-form'); diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 6612bc44e0b..7735133c470 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -374,7 +374,7 @@ js-gfm-input js-autosize markdown-area js-vue-textarea" append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"> <button :disabled="isSubmitButtonDisabled" - class="btn btn-create comment-btn js-comment-button js-comment-submit-button" + class="btn btn-success comment-btn js-comment-button js-comment-submit-button" type="submit" @click.prevent="handleSave()"> {{ __(commentButtonTitle) }} diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index c41ed070383..29595a2ba73 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -188,7 +188,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" <button :disabled="isDisabled" type="button" - class="js-vue-issue-save btn btn-save js-comment-button " + class="js-vue-issue-save btn btn-success js-comment-button " @click="handleUpdate()"> {{ saveButtonTitle }} </button> diff --git a/app/assets/javascripts/pages/groups/boards/index.js b/app/assets/javascripts/pages/groups/boards/index.js index 5cfe8723204..79c3be771d0 100644 --- a/app/assets/javascripts/pages/groups/boards/index.js +++ b/app/assets/javascripts/pages/groups/boards/index.js @@ -1,5 +1,5 @@ import UsersSelect from '~/users_select'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initBoards from '~/boards'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js index 5b8c2ae7e81..3a45fd70d02 100644 --- a/app/assets/javascripts/pages/groups/show/index.js +++ b/app/assets/javascripts/pages/groups/show/index.js @@ -6,7 +6,7 @@ import NewGroupChild from '~/groups/new_group_child'; import notificationsDropdown from '~/notifications_dropdown'; import NotificationsForm from '~/notifications_form'; import ProjectsList from '~/projects_list'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import GroupTabs from './group_tabs'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js index 5543ad82428..d39ea3d10bf 100644 --- a/app/assets/javascripts/pages/projects/activity/index.js +++ b/app/assets/javascripts/pages/projects/activity/index.js @@ -1,5 +1,5 @@ import Activities from '~/activities'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; document.addEventListener('DOMContentLoaded', () => { new Activities(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/artifacts/browse/index.js b/app/assets/javascripts/pages/projects/artifacts/browse/index.js index ea7458fe9b8..26dc90a56d7 100644 --- a/app/assets/javascripts/pages/projects/artifacts/browse/index.js +++ b/app/assets/javascripts/pages/projects/artifacts/browse/index.js @@ -1,5 +1,5 @@ import BuildArtifacts from '~/build_artifacts'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; document.addEventListener('DOMContentLoaded', () => { new ShortcutsNavigation(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/artifacts/file/index.js b/app/assets/javascripts/pages/projects/artifacts/file/index.js index 8484e5e9848..249900d6cb7 100644 --- a/app/assets/javascripts/pages/projects/artifacts/file/index.js +++ b/app/assets/javascripts/pages/projects/artifacts/file/index.js @@ -1,5 +1,5 @@ import BlobViewer from '~/blob/viewer/index'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; document.addEventListener('DOMContentLoaded', () => { new ShortcutsNavigation(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js index 5cfe8723204..79c3be771d0 100644 --- a/app/assets/javascripts/pages/projects/boards/index.js +++ b/app/assets/javascripts/pages/projects/boards/index.js @@ -1,5 +1,5 @@ import UsersSelect from '~/users_select'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initBoards from '~/boards'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js index 2e23cce11ce..f477424811d 100644 --- a/app/assets/javascripts/pages/projects/commit/show/index.js +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -3,7 +3,7 @@ import $ from 'jquery'; import Diff from '~/diff'; import ZenMode from '~/zen_mode'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; import initNotes from '~/init_notes'; import initChangesDropdown from '~/init_changes_dropdown'; diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js index 3682020579b..ad671ce9351 100644 --- a/app/assets/javascripts/pages/projects/commits/show/index.js +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -1,6 +1,6 @@ import CommitsList from '~/commits'; import GpgBadges from '~/gpg_badges'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; document.addEventListener('DOMContentLoaded', () => { new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js index 24630c2aa05..388d7d7bdda 100644 --- a/app/assets/javascripts/pages/projects/find_file/show/index.js +++ b/app/assets/javascripts/pages/projects/find_file/show/index.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import ProjectFindFile from '~/project_find_file'; -import ShortcutsFindFile from '~/shortcuts_find_file'; +import ShortcutsFindFile from '~/behaviors/shortcuts/shortcuts_find_file'; document.addEventListener('DOMContentLoaded', () => { const findElement = document.querySelector('.js-file-finder'); diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js index 9c074b74c3b..5659e13981a 100644 --- a/app/assets/javascripts/pages/projects/index.js +++ b/app/assets/javascripts/pages/projects/index.js @@ -1,7 +1,7 @@ import initDismissableCallout from '~/dismissable_callout'; import initGkeDropdowns from '~/projects/gke_cluster_dropdowns'; import Project from './project'; -import ShortcutsNavigation from '../../shortcuts_navigation'; +import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation'; document.addEventListener('DOMContentLoaded', () => { const { page } = document.body.dataset; diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js index 56ab3fcdfcb..bc08ccf3584 100644 --- a/app/assets/javascripts/pages/projects/init_blob.js +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -1,7 +1,7 @@ import LineHighlighter from '~/line_highlighter'; import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater'; -import ShortcutsNavigation from '~/shortcuts_navigation'; -import ShortcutsBlob from '~/shortcuts_blob'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; +import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob'; import BlobForkSuggestion from '~/blob/blob_fork_suggestion'; import initBlobBundle from '~/blob_edit/blob_bundle'; diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js index b2b8e5d2300..197bfa8a394 100644 --- a/app/assets/javascripts/pages/projects/issues/form.js +++ b/app/assets/javascripts/pages/projects/issues/form.js @@ -5,7 +5,7 @@ import GLForm from '~/gl_form'; import IssuableForm from '~/issuable_form'; import LabelsSelect from '~/labels_select'; import MilestoneSelect from '~/milestone_select'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import IssuableTemplateSelectors from '~/templates/issuable_template_selectors'; export default () => { diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js index 23edbdc5cad..a56c0bb6be8 100644 --- a/app/assets/javascripts/pages/projects/issues/index/index.js +++ b/app/assets/javascripts/pages/projects/issues/index/index.js @@ -1,7 +1,7 @@ /* eslint-disable no-new */ import IssuableIndex from '~/issuable_index'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import UsersSelect from '~/users_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js index 500fbd27340..74b3a515e84 100644 --- a/app/assets/javascripts/pages/projects/issues/show.js +++ b/app/assets/javascripts/pages/projects/issues/show.js @@ -1,6 +1,6 @@ import initIssuableSidebar from '~/init_issuable_sidebar'; import Issue from '~/issue'; -import ShortcutsIssuable from '~/shortcuts_issuable'; +import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable'; import ZenMode from '~/zen_mode'; import '~/notes/index'; import '~/issue_show/index'; diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js index 1fad0fb7297..3647048a872 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -1,5 +1,5 @@ import IssuableIndex from '~/issuable_index'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import UsersSelect from '~/users_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js index 3a3c21f2202..e3971618da5 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js +++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import Diff from '~/diff'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import GLForm from '~/gl_form'; import IssuableForm from '~/issuable_form'; import LabelsSelect from '~/labels_select'; diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js index 26ead75cec4..7bfb83a2204 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js +++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js @@ -1,6 +1,6 @@ import ZenMode from '~/zen_mode'; import initIssuableSidebar from '~/init_issuable_sidebar'; -import ShortcutsIssuable from '~/shortcuts_issuable'; +import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable'; import { handleLocationHash } from '~/lib/utils/common_utils'; import howToMerge from '~/how_to_merge'; import initPipelines from '~/commit/pipelines/pipelines_bundle'; diff --git a/app/assets/javascripts/pages/projects/network/show/index.js b/app/assets/javascripts/pages/projects/network/show/index.js index a0b14fed10f..9f05f63b742 100644 --- a/app/assets/javascripts/pages/projects/network/show/index.js +++ b/app/assets/javascripts/pages/projects/network/show/index.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import ShortcutsNetwork from '../../../../shortcuts_network'; +import ShortcutsNetwork from '~/behaviors/shortcuts/shortcuts_network'; import Network from '../network'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 0507f67843f..7302c1ab202 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import initBlob from '~/blob_edit/blob_bundle'; -import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import NotificationsForm from '~/notifications_form'; import UserCallout from '~/user_callout'; import TreeView from '~/tree'; diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 33d69d891d8..400aed35e32 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -4,7 +4,7 @@ import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import GpgBadges from '~/gpg_badges'; import TreeView from '../../../../tree'; -import ShortcutsNavigation from '../../../../shortcuts_navigation'; +import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation'; import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; import { ajaxGet } from '../../../../lib/utils/common_utils'; diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js index b0a323a71cd..c2629090f01 100644 --- a/app/assets/javascripts/pages/projects/wikis/index.js +++ b/app/assets/javascripts/pages/projects/wikis/index.js @@ -2,8 +2,8 @@ import $ from 'jquery'; import Vue from 'vue'; import Translate from '~/vue_shared/translate'; import csrf from '~/lib/utils/csrf'; +import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki'; import Wikis from './wikis'; -import ShortcutsWiki from '../../../shortcuts_wiki'; import ZenMode from '../../../zen_mode'; import GLForm from '../../../gl_form'; import deleteWikiModal from './components/delete_wiki_modal.vue'; diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue index 9501afb7493..efb80d3a3c0 100644 --- a/app/assets/javascripts/pipelines/components/nav_controls.vue +++ b/app/assets/javascripts/pipelines/components/nav_controls.vue @@ -43,7 +43,7 @@ export default { <a v-if="newPipelinePath" :href="newPipelinePath" - class="btn btn-create js-run-pipeline" + class="btn btn-success js-run-pipeline" > {{ s__('Pipelines|Run Pipeline') }} </a> diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index aec09b8bc0a..50dd3c12382 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -68,7 +68,7 @@ function setSearchOptions() { } } -export default class SearchAutocomplete { +export class SearchAutocomplete { constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { setSearchOptions(); this.bindEventContext(); @@ -499,3 +499,7 @@ export default class SearchAutocomplete { this.dropdownMenu.toggleClass('fade-out', !this.isScrolledUp()); } } + +export default function initSearchAutocomplete(opts) { + return new SearchAutocomplete(opts); +} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue index 086dbabe77e..e73b7e410d5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue @@ -37,7 +37,7 @@ export default { <a v-if="mr.newBlobPath" :href="mr.newBlobPath" - class="btn btn-inverted btn-save"> + class="btn btn-inverted btn-success"> Create file </a> </div> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue index d3cbe3c7e74..cfc5343217c 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue @@ -46,7 +46,7 @@ export default { } }, basePath() { - // We might get the project path from rails with the relative url already setup + // We might get the project path from rails with the relative url already set up return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`; }, fullOldPath() { diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue new file mode 100644 index 00000000000..6f7bdbc2c4d --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -0,0 +1,210 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; +import FileIcon from '~/vue_shared/components/file_icon.vue'; + +export default { + name: 'FileRow', + components: { + FileIcon, + Icon, + }, + props: { + file: { + type: Object, + required: true, + }, + level: { + type: Number, + required: true, + }, + extraComponent: { + type: Object, + required: false, + default: null, + }, + }, + data() { + return { + mouseOver: false, + }; + }, + computed: { + isTree() { + return this.file.type === 'tree'; + }, + isBlob() { + return this.file.type === 'blob'; + }, + levelIndentation() { + return { + marginLeft: `${this.level * 16}px`, + }; + }, + fileClass() { + return { + 'file-open': this.isBlob && this.file.opened, + 'is-active': this.isBlob && this.file.active, + folder: this.isTree, + 'is-open': this.file.opened, + }; + }, + }, + watch: { + 'file.active': function fileActiveWatch(active) { + if (this.file.type === 'blob' && active) { + this.scrollIntoView(); + } + }, + }, + mounted() { + if (this.hasPathAtCurrentRoute()) { + this.scrollIntoView(true); + } + }, + methods: { + toggleTreeOpen(path) { + this.$emit('toggleTreeOpen', path); + }, + clickFile() { + // Manual Action if a tree is selected/opened + if (this.isTree && this.hasUrlAtCurrentRoute()) { + this.toggleTreeOpen(this.file.path); + } + + if (this.$router) this.$router.push(`/project${this.file.url}`); + }, + scrollIntoView(isInit = false) { + const block = isInit && this.isTree ? 'center' : 'nearest'; + + this.$el.scrollIntoView({ + behavior: 'smooth', + block, + }); + }, + hasPathAtCurrentRoute() { + if (!this.$router || !this.$router.currentRoute) { + return false; + } + + // - strip route up to "/-/" and ending "/" + const routePath = this.$router.currentRoute.path + .replace(/^.*?[/]-[/]/g, '') + .replace(/[/]$/g, ''); + + // - strip ending "/" + const filePath = this.file.path.replace(/[/]$/g, ''); + + return filePath === routePath; + }, + hasUrlAtCurrentRoute() { + if (!this.$router || !this.$router.currentRoute) return true; + + return this.$router.currentRoute.path === `/project${this.file.url}`; + }, + toggleHover(over) { + this.mouseOver = over; + }, + }, +}; +</script> + +<template> + <div> + <div + :class="fileClass" + class="file-row" + role="button" + @click="clickFile" + @mouseover="toggleHover(true)" + @mouseout="toggleHover(false)" + > + <div + class="file-row-name-container" + > + <span + :style="levelIndentation" + class="file-row-name str-truncated" + > + <file-icon + :file-name="file.name" + :loading="file.loading" + :folder="isTree" + :opened="file.opened" + :size="16" + /> + {{ file.name }} + </span> + <component + v-if="extraComponent" + :is="extraComponent" + :file="file" + :mouse-over="mouseOver" + /> + </div> + </div> + <template v-if="file.opened"> + <file-row + v-for="childFile in file.tree" + :key="childFile.key" + :file="childFile" + :level="level + 1" + :extra-component="extraComponent" + @toggleTreeOpen="toggleTreeOpen" + /> + </template> + </div> +</template> + +<style> +.file-row { + display: flex; + align-items: center; + height: 32px; + padding: 4px 8px; + margin-left: -8px; + margin-right: -8px; + border-radius: 3px; + text-align: left; + cursor: pointer; +} + +.file-row:hover, +.file-row:focus { + background: #f2f2f2; +} + +.file-row:active { + background: #dfdfdf; +} + +.file-row.is-active { + background: #f2f2f2; +} + +.file-row-name-container { + display: flex; + width: 100%; + align-items: center; + overflow: visible; +} + +.file-row-name { + display: inline-block; + flex: 1; + max-width: inherit; + height: 18px; + line-height: 16px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-row-name svg { + margin-right: 2px; + vertical-align: middle; +} + +.file-row-name .loading-container { + display: inline-block; + margin-right: 4px; +} +</style> diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index ab62ca07573..686ce0c63a4 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -147,18 +147,12 @@ } &.btn-success, - &.btn-new, - &.btn-create, - &.btn-save, &.btn-register { @include btn-green; } &.btn-inverted { - &.btn-success, - &.btn-new, - &.btn-create, - &.btn-save { + &.btn-success { @include btn-outline($white-light, $green-600, $green-500, $green-500, $white-light, $green-600, $green-600, $green-700); } diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index e9b074236cc..d5693a5d1a1 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -389,9 +389,8 @@ .btn { text-overflow: ellipsis; - .fa { - width: 15px; - line-height: $line-height-base; + svg { + margin-right: $gl-padding-8; } .dropdown-label-box { diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 7152ef9bcfd..36ab38f1c9d 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -45,7 +45,7 @@ } } -.snippet-scope-menu .btn-new { +.snippet-scope-menu .btn-success { margin-top: 15px; } diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss index 45df8391f9a..65f0a0d18e2 100644 --- a/app/assets/stylesheets/page_bundles/ide.scss +++ b/app/assets/stylesheets/page_bundles/ide.scss @@ -53,83 +53,9 @@ $ide-commit-header-height: 48px; flex: 1; min-height: 0; // firefox fix - .file { - height: 32px; - cursor: pointer; - - &.file-active { - background: $theme-gray-100; - } - - .ide-file-name { - flex: 1; - white-space: nowrap; - text-overflow: ellipsis; - max-width: inherit; - line-height: 16px; - display: inline-block; - height: 18px; - - svg { - vertical-align: middle; - margin-right: 2px; - } - - .loading-container { - margin-right: 4px; - display: inline-block; - } - } - - .ide-file-icon-holder { - display: flex; - align-items: center; - color: $theme-gray-700; - } - - .ide-file-changed-icon { - margin-left: auto; - - > svg { - display: block; - } - } - - .ide-new-btn { - display: none; - - .btn { - padding: 2px 5px; - } - } - - &:hover, - &:focus { - .ide-new-btn { - display: block; - } - } - - .folder-icon { - fill: $gl-text-color-secondary; - } - } - a { color: $gl-text-color; } - - th { - position: sticky; - top: 0; - } -} - -.file-name { - display: flex; - overflow: visible; - align-items: center; - width: 100%; } .multi-file-loading-container { @@ -625,8 +551,7 @@ $ide-commit-header-height: 48px; } } -.multi-file-commit-list-path, -.ide-file-list .file { +.multi-file-commit-list-path { display: flex; align-items: center; margin-left: -$grid-size; @@ -634,28 +559,14 @@ $ide-commit-header-height: 48px; padding: $grid-size / 2 $grid-size; border-radius: $border-radius-default; text-align: left; - - &:hover, - &:focus { - background: $theme-gray-100; - } - - &:active { - background: $theme-gray-200; - } -} - -.multi-file-commit-list-path { cursor: pointer; height: $ide-commit-row-height; padding-right: 0; - &.is-active { - background-color: $white-normal; - } - &:hover, &:focus { + background: $theme-gray-100; + outline: 0; .multi-file-discard-btn { @@ -665,6 +576,14 @@ $ide-commit-header-height: 48px; } } + &:active { + background: $theme-gray-200; + } + + &.is-active { + background-color: $white-normal; + } + svg { min-width: 16px; vertical-align: middle; @@ -1398,9 +1317,17 @@ $ide-commit-header-height: 48px; } } -.ide-new-btn .dropdown.show .ide-entry-dropdown-toggle { - color: $white-normal; - background-color: $blue-500; +.ide-new-btn { + display: none; + + .btn { + padding: 2px 5px; + } + + .dropdown.show .ide-entry-dropdown-toggle { + color: $white-normal; + background-color: $blue-500; + } } .ide-preview-header { @@ -1465,3 +1392,28 @@ $ide-commit-header-height: 48px; width: $ide-commit-row-height; height: $ide-commit-row-height; } + +.ide-file-icon-holder { + display: flex; + align-items: center; + color: $theme-gray-700; +} + +.ide-file-changed-icon { + margin-left: auto; + + > svg { + display: block; + } +} + +.file-row:hover, +.file-row:focus { + .ide-new-btn { + display: block; + } + + .folder-icon { + fill: $gl-text-color-secondary; + } +} diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss index 0f22fe21143..71a3fd544f2 100644 --- a/app/assets/stylesheets/pages/clusters.scss +++ b/app/assets/stylesheets/pages/clusters.scss @@ -4,9 +4,60 @@ } } -.cluster-applications-table { - // Wait for the Vue to kick-in and render the applications block - min-height: 628px; +.cluster-application-row { + background: $gray-lighter; + + &.cluster-application-installed { + background: none; + } + + .settings-message { + padding: $gl-vert-padding $gl-padding-8; + } +} + +@media (min-width: map-get($grid-breakpoints, md)) { + .cluster-application-list { + border: 1px solid $border-color; + border-radius: $border-radius-default; + } + + .cluster-application-row { + border-bottom: 1px solid $border-color; + padding: $gl-padding; + } +} + +.cluster-application-logo { + border: 3px solid $white-light; + box-shadow: 0 0 0 1px $gray-normal; + + &.avatar:hover { + border-color: $white-light; + } +} + +.cluster-application-warning { + font-weight: bold; + text-align: center; + padding: $gl-padding; + border-bottom: 1px solid $white-normal; + + .svg-container { + display: inline-block; + vertical-align: middle; + margin-right: $gl-padding-8; + width: 40px; + height: 40px; + } +} + +.cluster-application-description { + flex: 1; +} + +.cluster-application-disabled { + opacity: 0.5; } .clusters-dropdown-menu { diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 394c99268be..fe792a53b44 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -106,7 +106,7 @@ &, .dropdown, .dropdown .dropdown-toggle, - .btn-new { + .btn-success { display: block; } @@ -118,7 +118,7 @@ .group-filter-form, .dropdown .dropdown-toggle, - .btn-new { + .btn-success { width: 100%; } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 5fdb2b4a90a..99609a96976 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -4,7 +4,7 @@ } .users-project-form { - .btn-create { + .btn-success { margin-right: 10px; } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index ac7b701c2e2..4268e194ed7 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -2,7 +2,7 @@ * Note Form */ .comment-btn { - @extend .btn-create; + @extend .btn-success; } .diff-file .diff-content { diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 17f34319050..caa839c32a5 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -279,6 +279,10 @@ table.u2f-registrations { } } +.codes { + padding-top: 14px; +} + .oauth-application-show { .scope-name { font-weight: $gl-font-weight-bold; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 77119aea9e2..04151b1cd59 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -218,7 +218,7 @@ input[type='checkbox']:hover { } .btn-search, - .btn-new { + .btn-success { width: 100%; margin-top: 5px; diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index f22df992fe9..f21c38a4c27 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -11,7 +11,7 @@ class Import::GitlabProjectsController < Import::BaseController def create unless file_is_valid? - return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." }) + return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive (ending in .gz)." }) end @project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute @@ -29,7 +29,11 @@ class Import::GitlabProjectsController < Import::BaseController private def file_is_valid? - project_params[:file] && project_params[:file].respond_to?(:read) + return false unless project_params[:file] && project_params[:file].respond_to?(:read) + + filename = project_params[:file].original_filename + + ImportExportUploader::EXTENSION_WHITELIST.include?(File.extname(filename).delete('.')) end def verify_gitlab_project_import_enabled diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 2cc721dfeec..5b2091d68f8 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -96,7 +96,7 @@ class Projects::PipelinesController < Projects::ApplicationController render json: StageSerializer .new(project: @project, current_user: @current_user) - .represent(@stage, details: true) + .represent(@stage, details: true, retried: params[:retried]) end # TODO: This endpoint is used by mini-pipeline-graph diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e2f9d501c4b..64d9b16a041 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -196,7 +196,7 @@ class ProjectsController < Projects::ApplicationController def download_export if @project.export_file_exists? - send_upload(@project.export_file) + send_upload(@project.export_file, attachment: @project.export_file.filename) else redirect_to( edit_project_path(@project, anchor: 'js-export-project'), diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 48777838d60..f90a7868102 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -18,7 +18,7 @@ class MembersFinder group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants) # rubocop: disable CodeReuse/Finder group_members = group_members.non_invite - union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) + union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) # rubocop: disable Gitlab/Union sql = distinct_on(union) diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index 715dffb972f..3528e4228b2 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -89,9 +89,7 @@ class SnippetsFinder < UnionFinder # We use a UNION here instead of OR clauses since this results in better # performance. - union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')]) - - Project.from("(#{union.to_sql}) AS #{Project.table_name}") + Project.from_union([authorized_projects, visible_projects]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb index 0d9f16fdce7..74baf79e4f2 100644 --- a/app/finders/todos_finder.rb +++ b/app/finders/todos_finder.rb @@ -164,16 +164,13 @@ class TodosFinder # rubocop: disable CodeReuse/ActiveRecord def by_group(items) - if group? - groups = group.self_and_descendants - project_todos = items.where(project_id: Project.where(group: groups).select(:id)) - group_todos = items.where(group_id: groups.select(:id)) + return items unless group? - union = Gitlab::SQL::Union.new([project_todos, group_todos]) - items = Todo.from("(#{union.to_sql}) #{Todo.table_name}") - end + groups = group.self_and_descendants + project_todos = items.where(project_id: Project.where(group: groups).select(:id)) + group_todos = items.where(group_id: groups.select(:id)) - items + Todo.from_union([project_todos, group_todos]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/union_finder.rb b/app/finders/union_finder.rb index 798c3eba0f3..c3b02f7e52f 100644 --- a/app/finders/union_finder.rb +++ b/app/finders/union_finder.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true class UnionFinder - # rubocop: disable CodeReuse/ActiveRecord def find_union(segments, klass) - if segments.length > 1 - union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) + unless klass < FromUnion + raise TypeError, "#{klass.inspect} must include the FromUnion module" + end - klass.where("#{klass.table_name}.id IN (#{union.to_sql})") + if segments.length > 1 + klass.from_union(segments) else segments.first end end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bb401b03709..ef4560023af 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -13,7 +13,7 @@ module ApplicationHelper # Check if a particular controller is the current one # - # args - One or more controller names to check + # args - One or more controller names to check (using path notation when inside namespaces) # # Examples # @@ -21,6 +21,11 @@ module ApplicationHelper # current_controller?(:tree) # => true # current_controller?(:commits) # => false # current_controller?(:commits, :tree) # => true + # + # # On Admin::ApplicationController + # current_controller?(:application) # => true + # current_controller?('admin/application') # => true + # current_controller?('gitlab/application') # => false def current_controller?(*args) args.any? do |v| v.to_s.downcase == controller.controller_name || v.to_s.downcase == controller.controller_path diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f8d36dce45d..136772e1ec3 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -123,11 +123,6 @@ module CiStatusHelper render_status_with_link('pipeline', pipeline.status, path, tooltip_placement: tooltip_placement) end - def no_runners_for_project?(project) - project.runners.blank? && - Ci::Runner.instance_type.blank? - end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 8b554e1aaa9..d4b50b7ecfb 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -32,7 +32,7 @@ module ServicesHelper end def service_save_button(service) - button_tag(class: 'btn btn-save', type: 'submit', disabled: service.deprecated?) do + button_tag(class: 'btn btn-success', type: 'submit', disabled: service.deprecated?) do icon('spinner spin', class: 'hidden js-btn-spinner') + content_tag(:span, 'Save changes', class: 'js-btn-label') end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index e310fda51d7..d91f0f78db7 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -8,7 +8,7 @@ module TabHelper # element is the value passed to the block. # # options - The options hash used to determine if the element is "active" (default: {}) - # :controller - One or more controller names to check (optional). + # :controller - One or more controller names to check, use path notation when namespaced (optional). # :action - One or more action names to check (optional). # :path - A shorthand path, such as 'dashboard#index', to check (optional). # :html_options - Extra options to be passed to the list element (optional). @@ -42,6 +42,20 @@ module TabHelper # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } # # => '<li class="home active">Hello</li>' # + # # For namespaced controllers like Admin::AppearancesController#show + # + # # Controller and namespace matches + # nav_link(controller: 'admin/appearances') { "Hello" } + # # => '<li class="active">Hello</li>' + # + # # Controller and namespace matches but action doesn't + # nav_link(controller: 'admin/appearances', action: :edit) { "Hello" } + # # => '<li>Hello</li>' + # + # # Shorthand path with namespace + # nav_link(path: 'admin/appearances#show') { "Hello"} + # # => '<li class="active">Hello</li>' + # # Returns a list item element String def nav_link(options = {}, &block) klass = active_nav_link?(options) ? 'active' : '' diff --git a/app/models/badge.rb b/app/models/badge.rb index 7e3b6b659e4..f016654206b 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Badge < ActiveRecord::Base + include FromUnion + # This structure sets the placeholders that the urls # can have. This hash also sets which action to ask when # the placeholder is found. diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index ab738c2fad8..63aaa0f7bcc 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -654,8 +654,31 @@ module Ci end end + # Virtual deployment status depending on the environment status. + def deployment_status + return nil unless starts_environment? + + if success? + return successful_deployment_status + elsif complete? && !success? + return :failed + end + + :creating + end + private + def successful_deployment_status + if success? && last_deployment&.last? + return :last + elsif success? && last_deployment.present? + return :out_of_date + end + + :creating + end + def each_test_report Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type| public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index eabb41c29d7..3e815937f4b 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -7,6 +7,7 @@ module Ci include IgnorableColumn include RedisCacheable include ChronicDurationAttribute + include FromUnion RUNNER_QUEUE_EXPIRY_TIME = 60.minutes ONLINE_CONTACT_TIMEOUT = 1.hour @@ -57,18 +58,26 @@ module Ci } scope :owned_or_instance_wide, -> (project_id) do - union = Gitlab::SQL::Union.new( - [belonging_to_project(project_id), belonging_to_parent_group_of_project(project_id), instance_type], + from_union( + [ + belonging_to_project(project_id), + belonging_to_parent_group_of_project(project_id), + instance_type + ], remove_duplicates: false ) - from("(#{union.to_sql}) ci_runners") end scope :assignable_for, ->(project) do # FIXME: That `to_sql` is needed to workaround a weird Rails bug. # Without that, placeholders would miss one and couldn't match. + # + # We use "unscoped" here so that any current Ci::Runner filters don't + # apply to the inner query, which is not necessary. + exclude_runners = unscoped { project.runners.select(:id) }.to_sql + where(locked: false) - .where.not("ci_runners.id IN (#{project.runners.select(:id).to_sql})") + .where.not("ci_runners.id IN (#{exclude_runners})") .project_type end diff --git a/app/models/concerns/from_union.rb b/app/models/concerns/from_union.rb new file mode 100644 index 00000000000..9b8595b1211 --- /dev/null +++ b/app/models/concerns/from_union.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module FromUnion + extend ActiveSupport::Concern + + class_methods do + # Produces a query that uses a FROM to select data using a UNION. + # + # Using a FROM for a UNION has in the past lead to better query plans. As + # such, we generally recommend this pattern instead of using a WHERE IN. + # + # Example: + # users = User.from_union([User.where(id: 1), User.where(id: 2)]) + # + # This would produce the following SQL query: + # + # SELECT * + # FROM ( + # SELECT * + # FROM users + # WHERE id = 1 + # + # UNION + # + # SELECT * + # FROM users + # WHERE id = 2 + # ) users; + # + # members - An Array of ActiveRecord::Relation objects to use in the UNION. + # + # remove_duplicates - A boolean indicating if duplicate entries should be + # removed. Defaults to true. + # + # alias_as - The alias to use for the sub query. Defaults to the name of the + # table of the current model. + # rubocop: disable Gitlab/Union + def from_union(members, remove_duplicates: true, alias_as: table_name) + union = Gitlab::SQL::Union + .new(members, remove_duplicates: remove_duplicates) + .to_sql + + # This pattern is necessary as a bug in Rails 4 can cause the use of + # `from("string here").includes(:foo)` to break ActiveRecord. This is + # fixed in https://github.com/rails/rails/pull/25374, which is released as + # part of Rails 5. + from([Arel.sql("(#{union}) #{alias_as}")]) + end + # rubocop: enable Gitlab/Union + end +end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 393607e82c4..298d0d42d90 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -61,7 +61,7 @@ module Mentionable cache_key: [self, attr], author: author, skip_project_check: skip_project_check? - ) + ).merge(mentionable_params) extractor.analyze(text, options) end @@ -86,12 +86,11 @@ module Mentionable return [] unless matches_cross_reference_regex? refs = all_references(current_user) - refs = (refs.issues + refs.merge_requests + refs.commits) # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. - refs.reject { |ref| ref == local_reference } + extracted_mentionables(refs).reject { |ref| ref == local_reference } end # Uses regex to quickly determine if mentionables might be referenced @@ -134,6 +133,10 @@ module Mentionable private + def extracted_mentionables(refs) + refs.issues + refs.merge_requests + refs.commits + end + # Returns a Hash of changed mentionable fields # # Preference is given to the `changes` Hash, but falls back to @@ -161,4 +164,8 @@ module Mentionable def skip_project_check? false end + + def mentionable_params + {} + end end diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index f6fd28bac33..fe8fbb71184 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -5,13 +5,19 @@ module Mentionable def self.reference_pattern(link_patterns, issue_pattern) Regexp.union(link_patterns, issue_pattern, - Commit.reference_pattern, - MergeRequest.reference_pattern) + *other_patterns) + end + + def self.other_patterns + [ + Commit.reference_pattern, + MergeRequest.reference_pattern + ] end DEFAULT_PATTERN = begin issue_pattern = Issue.reference_pattern - link_patterns = Regexp.union([Issue, Commit, MergeRequest].map(&:link_reference_pattern)) + link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact) reference_pattern(link_patterns, issue_pattern) end diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb index efa666fb3f2..583751ea6ac 100644 --- a/app/models/concerns/protected_ref_access.rb +++ b/app/models/concerns/protected_ref_access.rb @@ -3,18 +3,22 @@ module ProtectedRefAccess extend ActiveSupport::Concern - ALLOWED_ACCESS_LEVELS = [ - Gitlab::Access::MAINTAINER, - Gitlab::Access::DEVELOPER, - Gitlab::Access::NO_ACCESS - ].freeze - HUMAN_ACCESS_LEVELS = { Gitlab::Access::MAINTAINER => "Maintainers".freeze, Gitlab::Access::DEVELOPER => "Developers + Maintainers".freeze, Gitlab::Access::NO_ACCESS => "No one".freeze }.freeze + class_methods do + def allowed_access_levels + [ + Gitlab::Access::MAINTAINER, + Gitlab::Access::DEVELOPER, + Gitlab::Access::NO_ACCESS + ] + end + end + included do scope :master, -> { maintainer } # @deprecated scope :maintainer, -> { where(access_level: Gitlab::Access::MAINTAINER) } @@ -26,7 +30,7 @@ module ProtectedRefAccess scope :for_group, -> { where.not(group_id: nil) } validates :access_level, presence: true, if: :role?, inclusion: { - in: ALLOWED_ACCESS_LEVELS + in: self.allowed_access_levels } end diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb index 067e14dda1c..32e8104125c 100644 --- a/app/models/dashboard_group_milestone.rb +++ b/app/models/dashboard_group_milestone.rb @@ -13,7 +13,11 @@ class DashboardGroupMilestone < GlobalMilestone end def self.build_collection(groups) - MilestonesFinder.new(group_ids: groups.select(:id)).execute.map { |m| new(m) } # rubocop: disable CodeReuse/Finder + Milestone.of_groups(groups.select(:id)) + .reorder_by_due_date_asc + .order_by_name_asc + .active + .map { |m| new(m) } end override :group_milestone? diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 716cf6574d3..047d353b4b5 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -131,7 +131,7 @@ class DiffNote < Note # As an extra benefit, the returned `diff_file` already # has `highlighted_diff_lines` data set from Redis on # `Diff::FileCollection::MergeRequestDiff`. - noteable.diffs(paths: original_position.paths, expanded: true).diff_files.first + noteable.diffs(original_position.diff_options).diff_files.first else original_position.diff_file(self.project.repository) end diff --git a/app/models/epic.rb b/app/models/epic.rb index f027993376c..ccd10593434 100644 --- a/app/models/epic.rb +++ b/app/models/epic.rb @@ -3,6 +3,10 @@ # Placeholder class for model that is implemented in EE # It reserves '&' as a reference prefix, but the table does not exists in CE class Epic < ActiveRecord::Base + def self.link_reference_pattern + nil + end + def self.reference_prefix '&' end diff --git a/app/models/event.rb b/app/models/event.rb index 041dac6941b..596155a9525 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -3,6 +3,7 @@ class Event < ActiveRecord::Base include Sortable include IgnorableColumn + include FromUnion default_scope { reorder(nil) } CREATED = 1 diff --git a/app/models/group.rb b/app/models/group.rb index 024e77188b8..62af20d2142 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -304,14 +304,12 @@ class Group < Namespace # 3. They belong to a sub-group or project in such sub-group # 4. They belong to an ancestor group def direct_and_indirect_users - union = Gitlab::SQL::Union.new([ + User.from_union([ User .where(id: direct_and_indirect_members.select(:user_id)) .reorder(nil), project_users_with_descendants ]) - - User.from("(#{union.to_sql}) #{User.table_name}") end # Returns all users that are members of projects diff --git a/app/models/label.rb b/app/models/label.rb index 8dc7ded53ad..9ef57a05b3e 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -7,6 +7,7 @@ class Label < ActiveRecord::Base include Gitlab::SQL::Pattern include OptionallySearch include Sortable + include FromUnion # Represents a "No Label" state used for filtering Issues and Merge # Requests that have no label assigned. diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e19bf62dcd0..dd5d494997d 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -14,6 +14,7 @@ class MergeRequest < ActiveRecord::Base include Gitlab::Utils::StrongMemoize include LabelEventable include ReactiveCaching + include FromUnion self.reactive_cache_key = ->(model) { [model.project.id, model.iid] } self.reactive_cache_refresh_interval = 10.minutes @@ -237,11 +238,10 @@ class MergeRequest < ActiveRecord::Base def self.in_projects(relation) # unscoping unnecessary conditions that'll be applied # when executing `where("merge_requests.id IN (#{union.to_sql})")` - source = unscoped.where(source_project_id: relation).select(:id) - target = unscoped.where(target_project_id: relation).select(:id) - union = Gitlab::SQL::Union.new([source, target]) + source = unscoped.where(source_project_id: relation) + target = unscoped.where(target_project_id: relation) - where("merge_requests.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection + from_union([source, target]) end # This is used after project import, to reset the IDs to the correct @@ -740,11 +740,8 @@ class MergeRequest < ActiveRecord::Base # compared to using OR statements. We're using UNION ALL since the queries # used won't produce any duplicates (e.g. a note for a commit can't also be # a note for an MR). - union = Gitlab::SQL::Union - .new([notes, commit_notes], remove_duplicates: false) - .to_sql - - Note.from("(#{union}) #{Note.table_name}") + Note + .from_union([notes, commit_notes], remove_duplicates: false) .includes(:noteable) end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index cb1def1b422..892a680f221 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -46,6 +46,9 @@ class Milestone < ActiveRecord::Base where(conditions.reduce(:or)) end + scope :order_by_name_asc, -> { order(Arel::Nodes::Ascending.new(arel_table[:title].lower)) } + scope :reorder_by_due_date_asc, -> { reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC')) } + validates :group, presence: true, unless: :project validates :project, presence: true, unless: :group @@ -149,7 +152,7 @@ class Milestone < ActiveRecord::Base sorted = case method.to_s when 'due_date_asc' - reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC')) + reorder_by_due_date_asc when 'due_date_desc' reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC')) when 'name_asc' diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 76920c3c039..0289f29211d 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -11,6 +11,7 @@ class Namespace < ActiveRecord::Base include Gitlab::SQL::Pattern include IgnorableColumn include FeatureGate + include FromUnion ignore_column :deleted_at diff --git a/app/models/note.rb b/app/models/note.rb index 4429c1dcb07..bea02d69b65 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -17,6 +17,7 @@ class Note < ActiveRecord::Base include Editable include Gitlab::SQL::Pattern include ThrottledTouch + include FromUnion module SpecialRole FIRST_TIME_CONTRIBUTOR = :first_time_contributor diff --git a/app/models/project.rb b/app/models/project.rb index c37915e111f..9e4c7f7a2d0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -29,6 +29,7 @@ class Project < ActiveRecord::Base include BatchDestroyDependentAssociations include FeatureGate include OptionallySearch + include FromUnion extend Gitlab::Cache::RequestCache extend Gitlab::ConfigHelper @@ -1493,8 +1494,7 @@ class Project < ActiveRecord::Base end def all_runners - union = Gitlab::SQL::Union.new([runners, group_runners, shared_runners]) - Ci::Runner.from("(#{union.to_sql}) ci_runners") + Ci::Runner.from_union([runners, group_runners, shared_runners]) end def active_runners @@ -2022,12 +2022,10 @@ class Project < ActiveRecord::Base def badges return project_badges unless group - group_badges_rel = GroupBadge.where(group: group.self_and_ancestors) - - union = Gitlab::SQL::Union.new([project_badges.select(:id), - group_badges_rel.select(:id)]) - - Badge.where("id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection + Badge.from_union([ + project_badges, + GroupBadge.where(group: group.self_and_ancestors) + ]) end def merge_requests_allowing_push_to_user(user) diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb index 746bb4584c9..2c590008db2 100644 --- a/app/models/project_authorization.rb +++ b/app/models/project_authorization.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ProjectAuthorization < ActiveRecord::Base + include FromUnion + belongs_to :user belongs_to :project @@ -8,9 +10,9 @@ class ProjectAuthorization < ActiveRecord::Base validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true - def self.select_from_union(union) - select(['project_id', 'MAX(access_level) AS access_level']) - .from("(#{union.to_sql}) #{ProjectAuthorization.table_name}") + def self.select_from_union(relations) + from_union(relations) + .select(['project_id', 'MAX(access_level) AS access_level']) .group(:project_id) end diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb index 58631e09538..cc88d57faf8 100644 --- a/app/models/project_services/chat_message/merge_message.rb +++ b/app/models/project_services/chat_message/merge_message.rb @@ -48,7 +48,7 @@ module ChatMessage end def merge_request_message - "#{user_combined_name} #{state} #{merge_request_link} in #{project_link}: #{title}" + "#{user_combined_name} #{state_or_action_text} #{merge_request_link} in #{project_link}" end def merge_request_link @@ -62,5 +62,10 @@ module ChatMessage def merge_request_url "#{project_url}/merge_requests/#{merge_request_iid}" end + + # overridden in EE + def state_or_action_text + state + end end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index f4b3421f04b..761359b3c9f 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -184,11 +184,12 @@ class ProjectWiki def commit_details(action, message = nil, title = nil) commit_message = message || default_message(action, title) + git_user = Gitlab::Git::User.from_gitlab(@user) Gitlab::Git::Wiki::CommitDetails.new(@user.id, - @user.username, - @user.name, - @user.email, + git_user.username, + git_user.name, + git_user.email, commit_message) end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 5b394e3fa79..e9533ee7c77 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -12,6 +12,7 @@ class Snippet < ActiveRecord::Base include Spammable include Editable include Gitlab::SQL::Pattern + include FromUnion cache_markdown_field :title, pipeline: :single_line cache_markdown_field :description diff --git a/app/models/todo.rb b/app/models/todo.rb index 48d92ad04b3..265fb932f7c 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -2,6 +2,7 @@ class Todo < ActiveRecord::Base include Sortable + include FromUnion ASSIGNED = 1 MENTIONED = 2 diff --git a/app/models/user.rb b/app/models/user.rb index d68108a8e8e..eeac87e2e52 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -20,6 +20,7 @@ class User < ActiveRecord::Base include BlocksJsonSerialization include WithUploads include OptionallySearch + include FromUnion DEFAULT_NOTIFICATION_LEVEL = :participating @@ -286,11 +287,9 @@ class User < ActiveRecord::Base # user_id - The ID of the user to include. def self.union_with_user(user_id = nil) if user_id.present? - union = Gitlab::SQL::Union.new([all, User.unscoped.where(id: user_id)]) - # We use "unscoped" here so that any inner conditions are not repeated for # the outer query, which would be redundant. - User.unscoped.from("(#{union.to_sql}) #{User.table_name}") + User.unscoped.from_union([all, User.unscoped.where(id: user_id)]) else all end @@ -354,9 +353,8 @@ class User < ActiveRecord::Base emails = joins(:emails).where(emails: { email: email }) emails = emails.confirmed if confirmed - union = Gitlab::SQL::Union.new([users, emails]) - from("(#{union.to_sql}) #{table_name}") + from_union([users, emails]) end def filter(filter_name) @@ -635,7 +633,7 @@ class User < ActiveRecord::Base # possibility of the commit_email column not existing. def commit_email - return unless has_attribute?(:commit_email) + return self.email unless has_attribute?(:commit_email) # The commit email is the same as the primary email if undefined super.presence || self.email @@ -676,10 +674,10 @@ class User < ActiveRecord::Base # Returns the groups a user has access to, either through a membership or a project authorization def authorized_groups - union = Gitlab::SQL::Union - .new([groups.select(:id), authorized_projects.select(:namespace_id)]) - - Group.where("namespaces.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection + Group.from_union([ + groups, + authorized_projects.joins(:namespace).select('namespaces.*') + ]) end # Returns the groups a user is a member of, either directly or through a parent group @@ -744,7 +742,15 @@ class User < ActiveRecord::Base end def owned_projects - @owned_projects ||= Project.from("(#{owned_projects_union.to_sql}) AS projects") + @owned_projects ||= Project.from_union( + [ + Project.where(namespace: namespace), + Project.joins(:project_authorizations) + .where("projects.namespace_id <> ?", namespace.id) + .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER }) + ], + remove_duplicates: false + ) end # Returns projects which user can admin issues on (for example to move an issue to that project). @@ -1138,17 +1144,17 @@ class User < ActiveRecord::Base def ci_owned_runners @ci_owned_runners ||= begin - project_runner_ids = Ci::RunnerProject + project_runners = Ci::RunnerProject .where(project: authorized_projects(Gitlab::Access::MAINTAINER)) - .select(:runner_id) + .joins(:runner) + .select('ci_runners.*') - group_runner_ids = Ci::RunnerNamespace + group_runners = Ci::RunnerNamespace .where(namespace_id: owned_or_maintainers_groups.select(:id)) - .select(:runner_id) + .joins(:runner) + .select('ci_runners.*') - union = Gitlab::SQL::Union.new([project_runner_ids, group_runner_ids]) - - Ci::Runner.where("ci_runners.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection + Ci::Runner.from_union([project_runners, group_runners]) end end @@ -1176,13 +1182,13 @@ class User < ActiveRecord::Base def assigned_open_merge_requests_count(force: false) Rails.cache.fetch(['users', id, 'assigned_open_merge_requests_count'], force: force, expires_in: 20.minutes) do - MergeRequestsFinder.new(self, assignee_id: self.id, state: 'opened').execute.count + MergeRequestsFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count end end def assigned_open_issues_count(force: false) Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do - IssuesFinder.new(self, assignee_id: self.id, state: 'opened').execute.count + IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count end end @@ -1380,15 +1386,6 @@ class User < ActiveRecord::Base Gitlab::CurrentSettings.usage_stats_set_by_user_id == self.id end - def owned_projects_union - Gitlab::SQL::Union.new([ - Project.where(namespace: namespace), - Project.joins(:project_authorizations) - .where("projects.namespace_id <> ?", namespace.id) - .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER }) - ], remove_duplicates: false) - end - # Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration def send_devise_notification(notification, *args) return true unless can?(:receive_notifications) diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 6f8194d9856..00a441a9a1e 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -1,12 +1,26 @@ # frozen_string_literal: true class BuildDetailsEntity < JobEntity + include EnvironmentHelper + include RequestAwareEntity + include CiStatusHelper + expose :coverage, :erased_at, :duration expose :tag_list, as: :tags expose :user, using: UserEntity expose :runner, using: RunnerEntity expose :pipeline, using: PipelineEntity + expose :deployment_status, if: -> (*) { build.has_environment? } do + expose :deployment_status, as: :status + + expose :icon do |build| + ci_label_for_status(build.status) + end + + expose :persisted_environment, as: :environment, with: EnvironmentEntity + end + expose :metadata, using: BuildMetadataEntity expose :artifact, if: -> (*) { can?(current_user, :read_build, build) } do @@ -65,6 +79,20 @@ class BuildDetailsEntity < JobEntity expose :trigger_variables, as: :variables, using: TriggerVariableEntity end + expose :runners do + expose :online do |build| + build.any_runners_online? + end + + expose :available do |build| + project.any_runners? + end + + expose :settings_path, if: -> (*) { can_admin_build? } do |build| + project_runners_path(project) + end + end + private def build_failed_issue_options @@ -83,4 +111,8 @@ class BuildDetailsEntity < JobEntity def can_create_build_terminal? can?(current_user, :create_build_terminal, build) && build.has_terminal? end + + def can_admin_build? + can?(request.current_user, :admin_build, project) + end end diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index 04ec80e0efa..97e5b336a35 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -5,8 +5,7 @@ class RunnerEntity < Grape::Entity expose :id, :description - expose :edit_path, - if: -> (*) { can?(request.current_user, :admin_build, project) && runner.project_type? } do |runner| + expose :edit_path, if: -> (*) { can_edit_runner? } do |runner| edit_project_runner_path(project, runner) end @@ -17,4 +16,8 @@ class RunnerEntity < Grape::Entity def project request.project end + + def can_edit_runner? + can?(request.current_user, :update_runner, runner) && runner.project_type? + end end diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index ca8fa7e7877..029dd3d0684 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -19,6 +19,12 @@ class StageEntity < Grape::Entity latest_statuses end + expose :retried, + if: -> (_, opts) { opts[:retried] }, + with: JobEntity do |stage| + retried_statuses + end + expose :detailed_status, as: :status, with: DetailedStatusEntity expose :path do |stage| @@ -48,9 +54,19 @@ class StageEntity < Grape::Entity @grouped_statuses ||= stage.statuses.latest_ordered.group_by(&:status) end + def grouped_retried_statuses + @grouped_retried_statuses ||= stage.statuses.retried_ordered.group_by(&:status) + end + def latest_statuses HasStatus::ORDERED_STATUSES.map do |ordered_status| grouped_statuses.fetch(ordered_status, []) end.flatten end + + def retried_statuses + HasStatus::ORDERED_STATUSES.map do |ordered_status| + grouped_retried_statuses.fetch(ordered_status, []) + end.flatten + end end diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb index 4e352f2dc63..0b69661bbd0 100644 --- a/app/services/boards/issues/list_service.rb +++ b/app/services/boards/issues/list_service.rb @@ -56,6 +56,7 @@ module Boards set_parent set_state set_scope + set_non_archived params end @@ -76,6 +77,10 @@ module Boards params[:include_subgroups] = board.group_board? end + def set_non_archived + params[:non_archived] = parent.is_a?(Group) + end + # rubocop: disable CodeReuse/ActiveRecord def board_label_ids @board_label_ids ||= board.lists.movable.pluck(:label_id) diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index fc7b236f7da..39e614d6569 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -7,8 +7,10 @@ module Files def initialize(*args) super - @author_email = params[:author_email] || current_user&.email - @author_name = params[:author_name] || current_user&.name + git_user = Gitlab::Git::User.from_gitlab(current_user) if current_user.present? + + @author_email = params[:author_email] || git_user&.email + @author_name = params[:author_name] || git_user&.name @commit_message = params[:commit_message] @last_commit_sha = params[:last_commit_sha] diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb index aec0282b31b..52360f775dc 100644 --- a/app/services/labels/transfer_service.rb +++ b/app/services/labels/transfer_service.rb @@ -34,13 +34,13 @@ module Labels # rubocop: disable CodeReuse/ActiveRecord def labels_to_transfer - label_ids = [] - label_ids << group_labels_applied_to_issues.select(:id) - label_ids << group_labels_applied_to_merge_requests.select(:id) - - union = Gitlab::SQL::Union.new(label_ids) - - Label.where("labels.id IN (#{union.to_sql})").reorder(nil).uniq # rubocop:disable GitlabSecurity/SqlInjection + Label + .from_union([ + group_labels_applied_to_issues, + group_labels_applied_to_merge_requests + ]) + .reorder(nil) + .uniq end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb index c350b14d12b..b4d48fe92ad 100644 --- a/app/services/merge_requests/reload_diffs_service.rb +++ b/app/services/merge_requests/reload_diffs_service.rb @@ -31,7 +31,7 @@ module MergeRequests def clear_cache(new_diff) # Executing the iteration we cache highlighted diffs for each diff file of # MergeRequestDiff. - new_diff.diffs_collection.write_cache + cacheable_collection(new_diff).write_cache # Remove cache for all diffs on this MR. Do not use the association on the # model, as that will interfere with other actions happening when @@ -39,9 +39,15 @@ module MergeRequests MergeRequestDiff.where(merge_request: merge_request).each do |merge_request_diff| next if merge_request_diff == new_diff - merge_request_diff.diffs_collection.clear_cache + cacheable_collection(merge_request_diff).clear_cache end end # rubocop: enable CodeReuse/ActiveRecord + + def cacheable_collection(diff) + # There are scenarios where we don't need to request Diff Stats. + # Mainly when clearing / writing diff caches. + diff.diffs(include_stats: false) + end end end diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb index faaf1283078..216acf79cbd 100644 --- a/app/validators/url_validator.rb +++ b/app/validators/url_validator.rb @@ -41,12 +41,13 @@ class UrlValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) @record = record - if value.present? - value.strip! - else + unless value.present? record.errors.add(attribute, 'must be a valid URL') + return end + value = strip_value!(record, attribute, value) + Gitlab::UrlBlocker.validate!(value, blocker_args) rescue Gitlab::UrlBlocker::BlockedUrlError => e record.errors.add(attribute, "is blocked: #{e.message}") @@ -54,6 +55,13 @@ class UrlValidator < ActiveModel::EachValidator private + def strip_value!(record, attribute, value) + new_value = value.strip + return value if new_value == value + + record.public_send("#{attribute}=", new_value) # rubocop:disable GitlabSecurity/PublicSend + end + def default_options # By default the validator doesn't block any url based on the ip address { diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml index 278ad210543..391115a67b5 100644 --- a/app/views/abuse_reports/new.html.haml +++ b/app/views/abuse_reports/new.html.haml @@ -19,4 +19,4 @@ Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment. .form-actions - = f.submit "Send report", class: "btn btn-create" + = f.submit "Send report", class: "btn btn-success" diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index a0861870ba4..d71dac2574c 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -75,7 +75,7 @@ Guidelines parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}. .form-actions - = f.submit 'Save', class: 'btn btn-save append-right-10' + = f.submit 'Save', class: 'btn btn-success append-right-10' - if @appearance.persisted? Preview last save: = link_to 'Sign-in page', preview_sign_in_admin_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer' diff --git a/app/views/admin/appearances/preview_sign_in.html.haml b/app/views/admin/appearances/preview_sign_in.html.haml index 1af7dd5bb67..2cd95071c73 100644 --- a/app/views/admin/appearances/preview_sign_in.html.haml +++ b/app/views/admin/appearances/preview_sign_in.html.haml @@ -8,5 +8,5 @@ = label_tag :password = password_field_tag :password, nil, class: "form-control bottom", title: 'This field is required.' .form-group - = button_tag "Sign in", class: "btn-create btn" + = button_tag "Sign in", class: "btn-success btn" diff --git a/app/views/admin/application_settings/_influx.html.haml b/app/views/admin/application_settings/_influx.html.haml index a1eeacd8290..dc5cbb8fa94 100644 --- a/app/views/admin/application_settings/_influx.html.haml +++ b/app/views/admin/application_settings/_influx.html.haml @@ -3,7 +3,7 @@ %fieldset %p - Setup InfluxDB to measure a wide variety of statistics like the time spent + Set up InfluxDB to measure a wide variety of statistics like the time spent in running SQL queries. These settings require a = link_to 'restart', help_page_path('administration/restart_gitlab') to take effect. diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml index c94f4c74820..615aa6317b0 100644 --- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml +++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml @@ -7,9 +7,9 @@ .form-check = f.check_box :mirror_available, class: 'form-check-input' = f.label :mirror_available, class: 'form-check-label' do - Allow mirrors to be setup for projects + Allow mirrors to be set up for projects %span.form-text.text-muted - If disabled, only admins will be able to setup mirrors in projects. + If disabled, only admins will be able to set up mirrors in projects. = link_to icon('question-circle'), help_page_path('workflow/repository_mirroring') = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml index 635a6751e5b..5f36358f599 100644 --- a/app/views/admin/application_settings/_signin.html.haml +++ b/app/views/admin/application_settings/_signin.html.haml @@ -31,7 +31,7 @@ .form-check = f.check_box :require_two_factor_authentication, class: 'form-check-input' = f.label :require_two_factor_authentication, class: 'form-check-label' do - Require all users to setup Two-factor authentication + Require all users to set up Two-factor authentication .form-group = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-bold' = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0' diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index 7f14cddebd8..585576ec799 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -33,5 +33,5 @@ = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes .form-actions - = f.submit 'Submit', class: "btn btn-save wide" + = f.submit 'Submit', class: "btn btn-success wide" = link_to "Cancel", admin_applications_path, class: "btn btn-cancel" diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml index 7f34357f147..c465d9f51d6 100644 --- a/app/views/admin/broadcast_messages/_form.html.haml +++ b/app/views/admin/broadcast_messages/_form.html.haml @@ -36,6 +36,6 @@ = f.datetime_select :ends_at, {}, class: 'form-control form-control-inline' .form-actions - if @broadcast_message.persisted? - = f.submit "Update broadcast message", class: "btn btn-create" + = f.submit "Update broadcast message", class: "btn btn-success" - else - = f.submit "Add broadcast message", class: "btn btn-create" + = f.submit "Add broadcast message", class: "btn btn-success" diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index fac61f9d249..85c04f8a01d 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -14,7 +14,7 @@ Projects: = approximate_count_with_delimiters(@counts, Project) %hr - = link_to('New project', new_project_path, class: "btn btn-new") + = link_to('New project', new_project_path, class: "btn btn-success") .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -24,7 +24,7 @@ = approximate_count_with_delimiters(@counts, User) = render_if_exists 'admin/dashboard/users_statistics' %hr - = link_to 'New user', new_admin_user_path, class: "btn btn-new" + = link_to 'New user', new_admin_user_path, class: "btn btn-success" .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -33,7 +33,7 @@ Groups: = approximate_count_with_delimiters(@counts, Group) %hr - = link_to 'New group', new_admin_group_path, class: "btn btn-new" + = link_to 'New group', new_admin_group_path, class: "btn btn-success" .row .col-md-4 .info-well diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml index b50adef362f..7c04ef03947 100644 --- a/app/views/admin/deploy_keys/edit.html.haml +++ b/app/views/admin/deploy_keys/edit.html.haml @@ -6,5 +6,5 @@ = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f| = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key } .form-actions - = f.submit 'Save changes', class: 'btn-save btn' + = f.submit 'Save changes', class: 'btn-success btn' = link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel' diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 52ab8bae119..01013be06d6 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -3,7 +3,7 @@ %h3.page-title.deploy-keys-title Public deploy keys (#{@deploy_keys.count}) .float-right - = link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted' + = link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-success btn-sm btn-inverted' - if @deploy_keys.any? .table-holder.deploy-keys-list diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml index d4f8e340b69..9a563a5bc78 100644 --- a/app/views/admin/deploy_keys/new.html.haml +++ b/app/views/admin/deploy_keys/new.html.haml @@ -6,5 +6,5 @@ = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f| = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key } .form-actions - = f.submit 'Create', class: 'btn-create btn' + = f.submit 'Create', class: 'btn-success btn' = link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel' diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index a3773e90cfb..2a117c1414e 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -26,12 +26,12 @@ .alert.alert-info = render 'shared/group_tips' .form-actions - = f.submit _('Create group'), class: "btn btn-create" + = f.submit _('Create group'), class: "btn btn-success" = link_to _('Cancel'), admin_groups_path, class: "btn btn-cancel" - else .form-actions - = f.submit _('Save changes'), class: "btn btn-save" + = f.submit _('Save changes'), class: "btn btn-success" = link_to _('Cancel'), admin_group_path(@group), class: "btn btn-cancel" = render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 6a9b85b4109..cb833ffd9ac 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -12,7 +12,7 @@ = search_field_tag :name, project_name, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name' = icon("search", class: "search-icon") = render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash - = link_to new_admin_group_path, class: "btn btn-new" do + = link_to new_admin_group_path, class: "btn btn-success" do = _('New group') %ul.content-list = render @groups diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 72b068ea6b5..0c683f86252 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -111,7 +111,7 @@ .prepend-top-10 = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" %hr - = button_tag _('Add users to group'), class: "btn btn-create" + = button_tag _('Add users to group'), class: "btn btn-success" = render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true .card diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml index b9a650e1f1f..486d0477f20 100644 --- a/app/views/admin/hooks/edit.html.haml +++ b/app/views/admin/hooks/edit.html.haml @@ -12,7 +12,7 @@ = form_for @hook, as: :hook, url: admin_hook_path do |f| = render partial: 'form', locals: { form: f, hook: @hook } .form-actions - = f.submit 'Save changes', class: 'btn btn-create' + = f.submit 'Save changes', class: 'btn btn-success' = render 'shared/web_hooks/test_button', triggers: SystemHook.triggers, hook: @hook = link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: 'Are you sure?' } diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index 87f9b0e86a7..5d462d7b732 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -10,7 +10,7 @@ .col-lg-8.append-bottom-default = form_for @hook, as: :hook, url: admin_hooks_path do |f| = render partial: 'form', locals: { form: f, hook: @hook } - = f.submit 'Add system hook', class: 'btn btn-create' + = f.submit 'Add system hook', class: 'btn btn-success' %hr diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml index 946d868da01..3ab7990d9e2 100644 --- a/app/views/admin/identities/_form.html.haml +++ b/app/views/admin/identities/_form.html.haml @@ -12,5 +12,5 @@ = f.text_field :extern_uid, class: 'form-control', required: true .form-actions - = f.submit _('Save changes'), class: "btn btn-save" + = f.submit _('Save changes'), class: "btn btn-success" diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml index df3df159947..9543bbcf977 100644 --- a/app/views/admin/identities/index.html.haml +++ b/app/views/admin/identities/index.html.haml @@ -3,7 +3,7 @@ - page_title _("Identities"), @user.name, _("Users") = render 'admin/users/head' -= link_to _('New identity'), new_admin_user_identity_path, class: 'float-right btn btn-new' += link_to _('New identity'), new_admin_user_identity_path, class: 'float-right btn btn-success' - if @identities.present? .table-holder %table.table diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml index ee2d4c8430a..5e7b4817461 100644 --- a/app/views/admin/labels/_form.html.haml +++ b/app/views/admin/labels/_form.html.haml @@ -27,5 +27,5 @@ .form-actions - = f.submit _('Save'), class: 'btn btn-save js-save-button' + = f.submit _('Save'), class: 'btn btn-success js-save-button' = link_to _("Cancel"), admin_labels_path, class: 'btn btn-cancel' diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index f1b8658f84e..5a5b3d18c5f 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -1,7 +1,7 @@ - page_title _("Labels") %div - = link_to new_admin_label_path, class: "float-right btn btn-nr btn-new" do + = link_to new_admin_label_path, class: "float-right btn btn-nr btn-success" do = _('New label') %h3.page-title = _('Labels') diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 57de792f92d..46bb57c78a8 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -21,7 +21,7 @@ = dropdown_content = dropdown_loading = render 'shared/projects/dropdown' - = link_to new_project_path, class: 'btn btn-new' do + = link_to new_project_path, class: 'btn btn-success' do New Project = button_tag "Search", class: "btn btn-primary btn-search hide" diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 4dc076c95c5..48e0d7ce12e 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -4,7 +4,7 @@ %div{ class: container_class } .bs-callout %p - = (_"A 'Runner' is a process which runs a job. You can setup as many Runners as you need.") + = (_"A 'Runner' is a process which runs a job. You can set up as many Runners as you need.") %br = _('Runners can be placed on separate users, servers, even on your local machine.') %br @@ -69,7 +69,7 @@ %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { action: 'submit' } } = button_tag class: %w[btn btn-link] do - = icon('search') + = sprite_icon('search') %span = _('Press Enter or click to search') %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } @@ -77,7 +77,8 @@ = button_tag class: %w[btn btn-link] do -# Encapsulate static class name `{{icon}}` inside #{} to bypass -# haml lint's ClassAttributeWithStaticValue - %i.fa{ class: "#{'{{icon}}'}" } + %svg + %use{ 'xlink:href': "#{'{{icon}}'}" } %span.js-filter-hint {{hint}} %span.js-filter-tag.dropdown-light-content diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml index 993006e8745..1798b44bbb7 100644 --- a/app/views/admin/services/_form.html.haml +++ b/app/views/admin/services/_form.html.haml @@ -7,4 +7,4 @@ = render 'shared/service_settings', form: form, subject: @service .footer-block.row-content-block - = form.submit 'Save', class: 'btn btn-save' + = form.submit 'Save', class: 'btn btn-success' diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 7f21bdb91c8..296ef073144 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -75,8 +75,8 @@ .form-actions - if @user.new_record? - = f.submit 'Create user', class: "btn btn-create" + = f.submit 'Create user', class: "btn btn-success" = link_to 'Cancel', admin_users_path, class: "btn btn-cancel" - else - = f.submit 'Save changes', class: "btn btn-save" + = f.submit 'Save changes', class: "btn btn-success" = link_to 'Cancel', admin_user_path(@user), class: "btn btn-cancel" diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index faeb82656ba..f910e90d6ca 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -31,7 +31,7 @@ = sort_title_recently_updated = link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do = sort_title_oldest_updated - = link_to 'New user', new_admin_user_path, class: 'btn btn-new btn-search' + = link_to 'New user', new_admin_user_path, class: 'btn btn-success btn-search' .top-area.scrolling-tabs-container.inner-page-scroll-tabs .fade-left diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml index c26eb873718..b1b142460b0 100644 --- a/app/views/ci/runner/_how_to_setup_runner.html.haml +++ b/app/views/ci/runner/_how_to_setup_runner.html.haml @@ -1,6 +1,6 @@ - link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank' .append-bottom-10 - %h4= _("Setup a %{type} Runner manually") % { type: type } + %h4= _("Set up a %{type} Runner manually") % { type: type } %ol %li diff --git a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml index e765a353fe4..afe57bdfa01 100644 --- a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml +++ b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml @@ -1,6 +1,6 @@ .bs-callout.help-callout .append-bottom-10 - %h4= _('Setup a specific Runner automatically') + %h4= _('Set up a specific Runner automatically') %p - link_to_help_page = link_to(_('Learn more about Kubernetes'), diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index d8f1e50544c..727784141bb 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -10,4 +10,4 @@ = render 'shared/groups/search_form' = render 'shared/groups/dropdown' - if current_user.can_create_group? - = link_to _("New group"), new_group_path, class: "btn btn-new" + = link_to _("New group"), new_group_path, class: "btn btn-success" diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 9b1d9b659f9..69a2e408073 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -19,4 +19,4 @@ = render 'shared/projects/search_form' = render 'shared/projects/dropdown' - if current_user.can_create_project? - = link_to "New project", new_project_path, class: "btn btn-new" + = link_to "New project", new_project_path, class: "btn btn-success" diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml index e7e323a8683..4f38339b87a 100644 --- a/app/views/dashboard/_snippets_head.html.haml +++ b/app/views/dashboard/_snippets_head.html.haml @@ -9,4 +9,4 @@ - if current_user .nav-controls.d-none.d-sm-block - = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet" + = link_to "New snippet", new_snippet_path, class: "btn btn-success", title: "New snippet" diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index 4391624196b..b11dc2c8e9b 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -7,7 +7,7 @@ .d-block.d-sm-none - = link_to new_snippet_path, class: "btn btn-new btn-block", title: "New snippet" do + = link_to new_snippet_path, class: "btn btn-success btn-block", title: "New snippet" do New snippet = render partial: 'snippets/snippets', locals: { link_project: true } diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 35dafb3e980..4b8ad5acd5b 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -7,12 +7,12 @@ = f.hidden_field :reset_password_token .form-group = f.label 'New password', for: "user_password" - = f.password_field :password, class: "form-control top", required: true, title: 'This field is required' + = f.password_field :password, class: "form-control top qa-password-field", required: true, title: 'This field is required' .form-group = f.label 'Confirm new password', for: "user_password_confirmation" - = f.password_field :password_confirmation, class: "form-control bottom", title: 'This field is required', required: true + = f.password_field :password_confirmation, class: "form-control bottom qa-password-confirmation", title: 'This field is required', required: true .clearfix - = f.submit "Change your password", class: "btn btn-primary" + = f.submit "Change your password", class: "btn btn-primary qa-change-password-button" .clearfix.prepend-top-20 %p diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 17a9c8df872..7dacd0b1d72 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -1,10 +1,10 @@ = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f| .form-group = f.label "Username or email", for: "user_login", class: 'label-bold' - = f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required." + = f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required." .form-group = f.label :password, class: 'label-bold' - = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required." + = f.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required." - if devise_mapping.rememberable? .remember-me %label{ for: "user_remember_me" } @@ -17,4 +17,4 @@ = recaptcha_tags .submit-container.move-submit-down - = f.submit "Sign in", class: "btn btn-save" + = f.submit "Sign in", class: "btn btn-success qa-sign-in-button" diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml index 36ff42090be..131544ac0c0 100644 --- a/app/views/devise/sessions/_new_crowd.html.haml +++ b/app/views/devise/sessions/_new_crowd.html.haml @@ -10,4 +10,4 @@ %label{ for: "remember_me" } = check_box_tag :remember_me, '1', false, id: 'remember_me' %span Remember me - = submit_tag "Sign in", class: "btn-save btn" + = submit_tag "Sign in", class: "btn-success btn" diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 6bf7349f602..796c0cadda8 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -1,13 +1,13 @@ = form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do .form-group = label_tag :username, "#{server['label']} Username" - = text_field_tag :username, nil, { class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true } + = text_field_tag :username, nil, { class: "form-control top qa-username-field", title: "This field is required.", autofocus: "autofocus", required: true } .form-group = label_tag :password - = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true } + = password_field_tag :password, nil, { class: "form-control bottom qa-password-field", title: "This field is required.", required: true } - if devise_mapping.rememberable? .remember-me %label{ for: "remember_me" } = check_box_tag :remember_me, '1', false, id: 'remember_me' %span Remember me - = submit_tag "Sign in", class: "btn-save btn" + = submit_tag "Sign in", class: "btn-success btn qa-sign-in-button" diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index ba168c4eab8..fefdf5f9531 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -11,7 +11,7 @@ = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.' %p.form-text.text-muted.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. .prepend-top-20 - = f.submit "Verify code", class: "btn btn-save" + = f.submit "Verify code", class: "btn btn-success" - if @user.two_factor_u2f_enabled? = render "u2f/authenticate", locals: { params: params, resource: resource, resource_name: resource_name } diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index 58c585a29ff..3764e86dd8b 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -4,10 +4,10 @@ = link_to "Crowd", "#crowd", class: 'nav-link active', 'data-toggle' => 'tab' - @ldap_servers.each_with_index do |server, i| %li.nav-item - = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && !crowd_enabled?)}", 'data-toggle' => 'tab' + = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && !crowd_enabled?)} qa-ldap-tab", 'data-toggle' => 'tab' - if password_authentication_enabled_for_web? %li.nav-item - = link_to 'Standard', '#login-pane', class: 'nav-link', 'data-toggle' => 'tab' + = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab' - if allow_signup? %li.nav-item = link_to 'Register', '#register-pane', class: 'nav-link', 'data-toggle' => 'tab' diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml index 284d4fa1b89..8745a4e9d3e 100644 --- a/app/views/devise/shared/_tabs_normal.html.haml +++ b/app/views/devise/shared/_tabs_normal.html.haml @@ -1,6 +1,6 @@ %ul.nav-links.new-session-tabs.nav-tabs.nav{ role: 'tablist' } %li.nav-item{ role: 'presentation' } - %a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in + %a.nav-link.qa-sign-in-tab.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in - if allow_signup? %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register + %a.nav-link.qa-register-tab{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 0bc057a8864..78904f550c7 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -20,4 +20,4 @@ = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes .prepend-top-default - = f.submit _('Save application'), class: "btn btn-create" + = f.submit _('Save application'), class: "btn btn-success" diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index f7cc62c6929..935a8889d79 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -17,7 +17,7 @@ = f.check_box :require_two_factor_authentication, class: 'form-check-input' = f.label :require_two_factor_authentication, class: 'form-check-label' do %strong - Require all users in this group to setup Two-factor authentication + Require all users in this group to set up Two-factor authentication = link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group') .form-group.row .offset-sm-2.col-sm-10 diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml index aa03f8365f9..04683ec5a9a 100644 --- a/app/views/groups/group_members/_new_group_member.html.haml +++ b/app/views/groups/group_members/_new_group_member.html.haml @@ -19,4 +19,4 @@ On this date, the member(s) will automatically lose access to this group and all of its projects. .col-md-2 - = f.submit 'Add to group', class: "btn btn-create btn-block" + = f.submit 'Add to group', class: "btn btn-success btn-block" diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 86178eb2ffd..003bd25dd06 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -7,7 +7,7 @@ - if can_admin_label - content_for(:header_content) do .nav-controls - = link_to _('New label'), new_group_label_path(@group), class: "btn btn-new" + = link_to _('New label'), new_group_label_path(@group), class: "btn btn-success" - if @labels.exists? || search.present? #promote-label-modal diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml index 6d35457a0ec..39e3af5f6d2 100644 --- a/app/views/groups/milestones/_form.html.haml +++ b/app/views/groups/milestones/_form.html.haml @@ -19,9 +19,9 @@ .form-actions - if @milestone.new_record? - = f.submit 'Create milestone', class: "btn-create btn" + = f.submit 'Create milestone', class: "btn-success btn" = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel" - else - = f.submit 'Update milestone', class: "btn-create btn" + = f.submit 'Update milestone', class: "btn-success btn" = link_to "Cancel", group_milestone_path(@group, @milestone), class: "btn btn-cancel" diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index b6424df55cd..af4fe8f2ef8 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -6,7 +6,7 @@ .nav-controls = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @group) - = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new" + = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-success" .milestones %ul.content-list diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 53f54db1ddf..683129fdf6e 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -36,5 +36,5 @@ = render 'shared/group_tips' .form-actions - = f.submit 'Create group', class: "btn btn-create" + = f.submit 'Create group', class: "btn btn-success" = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel' diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index b32b602ceb3..506f580b246 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -189,7 +189,7 @@ %li = link_to 'Sort by date', '#' - = link_to 'New issue', '#', class: 'btn btn-new btn-inverted' + = link_to 'New issue', '#', class: 'btn btn-success btn-inverted' .lead Only nav links without button and search diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index b54b1af1e0c..626080c284b 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -21,4 +21,4 @@ .col-md-4 = password_field_tag :password, nil, class: 'form-control' .form-actions - = submit_tag _('Continue to the next step'), class: 'btn btn-create' + = submit_tag _('Continue to the next step'), class: 'btn btn-success' diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index ff2f989c509..8ed9dc68bb3 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -39,4 +39,4 @@ scope: :all, email_user: true, selected: user[:gitlab_user]) .form-actions - = submit_tag _('Continue to the next step'), class: 'btn btn-create' + = submit_tag _('Continue to the next step'), class: 'btn btn-success' diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml index 2b3102f9af9..a88b04eccbb 100644 --- a/app/views/import/gitea/new.html.haml +++ b/app/views/import/gitea/new.html.haml @@ -19,4 +19,4 @@ .col-sm-4 = text_field_tag :personal_access_token, nil, class: 'form-control' .form-actions - = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-create' + = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-success' diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index c4218f3d787..877d945a09b 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -41,5 +41,5 @@ = file_field_tag :file, class: '' .row .form-actions.col-sm-12 - = submit_tag _('Import project'), class: 'btn btn-create' + = submit_tag _('Import project'), class: 'btn btn-success' = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel' diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml index fd6e4726fc5..7a6ad28f0aa 100644 --- a/app/views/import/google_code/new.html.haml +++ b/app/views/import/google_code/new.html.haml @@ -59,4 +59,4 @@ = _('Yes, let me map Google Code users to full names or GitLab users.') %li %p - = submit_tag _('Continue to the next step'), class: "btn btn-create" + = submit_tag _('Continue to the next step'), class: "btn btn-success" diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml index baaaf6bdc63..f523b993aa7 100644 --- a/app/views/import/google_code/new_user_map.html.haml +++ b/app/views/import/google_code/new_user_map.html.haml @@ -33,4 +33,4 @@ = text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15 .form-actions - = submit_tag _('Continue to the next step'), class: "btn btn-create" + = submit_tag _('Continue to the next step'), class: "btn btn-success" diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 30e0e9fca27..25cd53b378a 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -158,7 +158,7 @@ - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do - = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do + = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines' do .nav-icon-container = sprite_icon('rocket') %span.nav-item-name @@ -245,7 +245,7 @@ = link_to _('Auto DevOps'), help_page_path('topics/autodevops/index.md') %span= _('uses Kubernetes clusters to deploy your code!') %hr - %button.btn.btn-create.btn-sm.dismiss-feature-highlight{ type: 'button' } + %button.btn.btn-success.btn-sm.dismiss-feature-highlight{ type: 'button' } %span= _("Got it!") = sprite_icon('thumb-up') diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index c8faf2b3af3..1823f191fb3 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -15,7 +15,7 @@ = f.label :email, class: 'label-bold' = f.text_field :email, class: 'form-control' .prepend-top-default - = f.submit 'Add email address', class: 'btn btn-create' + = f.submit 'Add email address', class: 'btn btn-success' %hr %h4.prepend-top-0 Linked emails (#{@emails.count + 1}) diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml index aa9b0aad034..6c4cb614a2b 100644 --- a/app/views/profiles/gpg_keys/_form.html.haml +++ b/app/views/profiles/gpg_keys/_form.html.haml @@ -7,4 +7,4 @@ = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'." .prepend-top-default - = f.submit 'Add key', class: "btn btn-create" + = f.submit 'Add key', class: "btn btn-success" diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index 5207921d6fe..21eef08983c 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -5,10 +5,10 @@ .form-group = f.label :key, class: 'label-bold' %p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.") - = f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"') + = f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"') .form-group = f.label :title, class: 'label-bold' - = f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key') + = f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key') %p.form-text.text-muted= _('Name your individual key via a title') .js-add-ssh-key-validation-warning.hide @@ -16,7 +16,7 @@ %strong= _('Oops, are you sure?') %p= s_("Profiles|This doesn't look like a public SSH key, are you sure you want to add it?") - %button.btn.btn-create.js-add-ssh-key-validation-confirm-submit= _("Yes, add it") + %button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it") .prepend-top-default - = f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit" + = f.submit s_('Profiles|Add key'), class: "btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button" diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml index 2ac514d3f6f..88473c7f72d 100644 --- a/app/views/profiles/keys/_key_details.html.haml +++ b/app/views/profiles/keys/_key_details.html.haml @@ -24,4 +24,4 @@ = @key.key .col-md-12 .float-right - = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key" + = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key qa-delete-key-button" diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 9c8cc9c059b..0b4b9841ea1 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -29,6 +29,6 @@ = f.label :password_confirmation, class: 'label-bold' = f.password_field :password_confirmation, required: true, class: 'form-control' .prepend-top-default.append-bottom-default - = f.submit 'Save password', class: "btn btn-create append-right-10" + = f.submit 'Save password', class: "btn btn-success append-right-10" - unless @user.password_automatically_set? = link_to "I forgot my password", reset_profile_password_path, method: :put, class: "account-btn-link" diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index 2176d7f8a31..d265f3c44ba 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -1,6 +1,6 @@ - page_title "New Password" - header_title "New Password" -%h3.page-title Setup new password +%h3.page-title Set up new password %hr = form_for @user, url: profile_password_path, method: :post do |f| %p.slead @@ -22,4 +22,4 @@ .col-sm-10 = f.password_field :password_confirmation, required: true, class: 'form-control' .form-actions - = f.submit 'Set new password', class: "btn btn-create" + = f.submit 'Set new password', class: "btn btn-success" diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index fd6dd74e1c5..156c0d05b02 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -58,4 +58,4 @@ .form-text.text-muted Choose what content you want to see on a project’s overview page .form-group - = f.submit 'Save changes', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-success' diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml index 93722d7b034..fb4fff12027 100644 --- a/app/views/profiles/two_factor_auths/_codes.html.haml +++ b/app/views/profiles/two_factor_auths/_codes.html.haml @@ -10,4 +10,6 @@ %li %span.monospace= code -= link_to 'Proceed', profile_account_path, class: 'btn btn-success' +.d-flex + = link_to 'Proceed', profile_account_path, class: 'btn btn-success append-right-10' + = link_to 'Download codes', "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'btn btn-default' diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml index b387e38c1a6..1e27c71d20d 100644 --- a/app/views/projects/_commit_button.html.haml +++ b/app/views/projects/_commit_button.html.haml @@ -1,5 +1,5 @@ .form-actions - = button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-create' + = button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-success' = link_to 'Cancel', cancel_path, class: 'btn btn-cancel', data: {confirm: leave_edit_message} diff --git a/app/views/projects/_fork_suggestion.html.haml b/app/views/projects/_fork_suggestion.html.haml index c855bfaf067..0b616a0c1ce 100644 --- a/app/views/projects/_fork_suggestion.html.haml +++ b/app/views/projects/_fork_suggestion.html.haml @@ -6,6 +6,6 @@ edit files in this project directly. Please fork this project, make your changes there, and submit a merge request. - = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new' + = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-success' %button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' } Cancel diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index 001e65c0f66..db07c475866 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -60,5 +60,5 @@ .option-description Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository. -= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 += f.submit 'Create project', class: "btn btn-success project-submit", tabindex: 4 = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index 705338c083e..32624ac225b 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -20,4 +20,4 @@ distributed with computer software, forming part of its documentation. GitLab will render it here instead of this message. %p - = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-new' + = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-success' diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 6f3a691518b..e9010dc63fc 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -15,7 +15,7 @@ = render 'shared/new_commit_form', placeholder: _("Add new directory") .form-actions - = submit_tag _("Create directory"), class: 'btn btn-create' + = submit_tag _("Create directory"), class: 'btn btn-success' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = render 'shared/projects/edit_information' diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 0a5c73c9037..d2b3c8ef96b 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -20,7 +20,7 @@ = render 'shared/new_commit_form', placeholder: placeholder .form-actions - = button_tag class: 'btn btn-create btn-upload-file', id: 'submit-all', type: 'button' do + = button_tag class: 'btn btn-success btn-upload-file', id: 'submit-all', type: 'button' do = icon('spin spinner', class: 'js-loading-icon hidden' ) = button_title = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal" diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index d6568c9f64a..ca867961f6b 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -41,7 +41,7 @@ data: { confirm: s_('Branches|Deleting the merged branches cannot be undone. Are you sure?'), container: 'body' } do = s_('Branches|Delete merged branches') - = link_to new_project_branch_path(@project), class: 'btn btn-create' do + = link_to new_project_branch_path(@project), class: 'btn btn-success' do = s_('Branches|New branch') - if can?(current_user, :admin_project, @project) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 65b414c8af2..500536a5dbc 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -26,7 +26,7 @@ = render 'shared/ref_dropdown', dropdown_class: 'wide' .form-text.text-muted Existing branch name, tag, or commit SHA .form-actions - = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 + = button_tag 'Create branch', class: 'btn btn-success', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' -# haml-lint:disable InlineJavaScript %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml index afd70ef5774..e71615dd1c5 100644 --- a/app/views/projects/commit/_change.html.haml +++ b/app/views/projects/commit/_change.html.haml @@ -33,7 +33,7 @@ - else = hidden_field_tag 'create_merge_request', 1, id: nil .form-actions - = submit_tag label, class: 'btn btn-create' + = submit_tag label, class: 'btn btn-success' = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal" = render 'shared/projects/edit_information' diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index 07112c98804..d24ee4a3251 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -22,7 +22,7 @@ .dropdown-toggle-text.str-truncated= params[:from] || _("Select branch/tag") = render 'shared/ref_dropdown' - = button_tag s_("CompareBranches|Compare"), class: "btn btn-create commits-compare-btn" + = button_tag s_("CompareBranches|Compare"), class: "btn btn-success commits-compare-btn" - if @merge_request.present? = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'prepend-left-10 btn' - elsif create_mr_button? diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index f8ab0c1ec54..568930595a2 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -21,4 +21,4 @@ Allow this key to push to repository as well? (Default only allows pull access.) .form-group.row - = f.submit "Add key", class: "btn-create btn" + = f.submit "Add key", class: "btn-success btn" diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml index e009b6fef0e..3e7872ebc1c 100644 --- a/app/views/projects/deploy_keys/edit.html.haml +++ b/app/views/projects/deploy_keys/edit.html.haml @@ -6,5 +6,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, @deploy_key], html: { class: 'js-requires-input' } do |f| = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key } .form-actions - = f.submit 'Save changes', class: 'btn-save btn' + = f.submit 'Save changes', class: 'btn-success btn' = link_to 'Cancel', project_settings_repository_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index acdde9e0f70..96ab582b050 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -59,7 +59,7 @@ - if @project.avatar? %hr = link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted" - = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings" + = f.submit 'Save changes', class: "btn btn-success js-btn-success-general-project-settings" %section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded) } .settings-header @@ -75,7 +75,7 @@ -# haml-lint:disable InlineJavaScript %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project) .js-project-permissions-form - = f.submit 'Save changes', class: "btn btn-save" + = f.submit 'Save changes', class: "btn btn-success" = render_if_exists 'projects/issues_settings' @@ -93,7 +93,7 @@ = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f| %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' } = render 'projects/merge_request_settings', form: f - = f.submit 'Save changes', class: "btn btn-save qa-save-merge-request-changes" + = f.submit 'Save changes', class: "btn btn-success qa-save-merge-request-changes" = render_if_exists 'projects/service_desk_settings' diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml index 0586dbdf0e2..f942b936037 100644 --- a/app/views/projects/environments/_form.html.haml +++ b/app/views/projects/environments/_form.html.haml @@ -18,5 +18,5 @@ = f.url_field :external_url, class: 'form-control' .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save', class: 'btn btn-success' = link_to 'Cancel', project_environments_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index 5b680189bc8..e40d631a1a1 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -2,7 +2,7 @@ - page_title "Terminal for environment", @environment.name - content_for :page_specific_javascripts do - = stylesheet_link_tag "xterm/xterm" + = stylesheet_link_tag "xterm.css" %div{ class: container_class } .top-area diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 57afc7ac9c3..b44ea89510b 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -30,11 +30,11 @@ - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-success' do = sprite_icon('fork', size: 12) %span Fork - else - = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-new' do + = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-success' do = sprite_icon('fork', size: 12) %span Fork diff --git a/app/views/projects/hooks/_index.html.haml b/app/views/projects/hooks/_index.html.haml index 5990582fd55..0ab7863b77c 100644 --- a/app/views/projects/hooks/_index.html.haml +++ b/app/views/projects/hooks/_index.html.haml @@ -9,7 +9,7 @@ .col-lg-8.append-bottom-default = form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f| = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } - = f.submit 'Add webhook', class: 'btn btn-create' + = f.submit 'Add webhook', class: 'btn btn-success' %hr %h5.prepend-top-default diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml index c31aef60453..57311284e11 100644 --- a/app/views/projects/hooks/edit.html.haml +++ b/app/views/projects/hooks/edit.html.haml @@ -11,7 +11,7 @@ = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f| = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } - = f.submit 'Save changes', class: 'btn btn-create' + = f.submit 'Save changes', class: 'btn btn-success' = render 'shared/web_hooks/test_button', triggers: ProjectHook.triggers, hook: @hook = link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: 'Are you sure?' } diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 8ce822c43b7..1c50cfbde85 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -16,4 +16,4 @@ = render "shared/import_form", f: f .form-actions - = f.submit 'Start import', class: "btn btn-create", tabindex: 4 + = f.submit 'Start import', class: "btn btn-success", tabindex: 4 diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index 0dd2d2e6c5d..e4a0d4b8479 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -6,6 +6,6 @@ = link_to "New issue", new_project_issue_path(@project, issue: { assignee_id: finder.assignee.try(:id), milestone_id: finder.milestones.first.try(:id) }), - class: "btn btn-new", + class: "btn btn-success", title: "New issue", id: "new_issue_link" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index b81d1a188f0..c39fd0063be 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,7 +55,7 @@ - if can_report_spam = link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'd-none d-sm-none d-md-block btn btn-grouped btn-spam', title: 'Submit as spam' - if can_create_issue - = link_to new_project_issue_path(@project), class: 'd-none d-sm-none d-md-block btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do + = link_to new_project_issue_path(@project), class: 'd-none d-sm-none d-md-block btn btn-grouped new-issue-link btn-success btn-inverted', title: 'New issue', id: 'new_issue_link' do New issue .issue-details.issuable-details diff --git a/app/views/projects/jobs/_header.html.haml b/app/views/projects/jobs/_header.html.haml index b83e8dddccb..e7245622b80 100644 --- a/app/views/projects/jobs/_header.html.haml +++ b/app/views/projects/jobs/_header.html.haml @@ -24,7 +24,7 @@ - if show_controls .nav-controls - if can?(current_user, :create_issue, @project) && @build.failed? - = link_to "New issue", new_project_issue_path(@project, issue: build_failed_issue_options), class: 'btn btn-new btn-inverted' + = link_to "New issue", new_project_issue_path(@project, issue: build_failed_issue_options), class: 'btn btn-success btn-inverted' - if can?(current_user, :update_build, @build) && @build.retryable? = link_to "Retry job", retry_project_job_path(@project, @build), class: 'btn btn-inverted-secondary', method: :post %button.btn.btn-default.float-right.d-block.d-sm-none.d-md-none.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" } diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 078f40c4477..cf8d42976f8 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -10,7 +10,7 @@ - unless @build.any_runners_online? .bs-callout.bs-callout-warning.js-build-stuck %p - - if no_runners_for_project?(@build.project) + - if @project.any_runners? This job is stuck, because the project doesn't have any runners online assigned to it. - elsif @build.tags.any? This job is stuck, because you don't have any active runners online with any of these tags assigned to them: diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 1bfd8a85f0f..683dda4f166 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -6,7 +6,7 @@ - if can_admin_label - content_for(:header_content) do .nav-controls - = link_to _('New label'), new_project_label_path(@project), class: "btn btn-new" + = link_to _('New label'), new_project_label_path(@project), class: "btn btn-success" - if @labels.exists? || @prioritized_labels.exists? || search.present? #promote-label-modal diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index 37c09f12f63..d0a7f89df31 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -43,4 +43,4 @@ .clearfix .float-right = link_to 'Cancel', edit_project_service_path(@project, @service), class: 'btn btn-lg' - = f.submit 'Install', class: 'btn btn-save btn-lg' + = f.submit 'Install', class: 'btn btn-success btn-lg' diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml index e73dab8ad4a..b7498216334 100644 --- a/app/views/projects/merge_requests/_nav_btns.html.haml +++ b/app/views/projects/merge_requests/_nav_btns.html.haml @@ -1,5 +1,5 @@ - if @can_bulk_update = button_tag "Edit merge requests", class: "btn append-right-10 js-bulk-update-toggle" - if merge_project - = link_to new_merge_request_path, class: "btn btn-new", title: "New merge request" do + = link_to new_merge_request_path, class: "btn btn-success", title: "New merge request" do New merge request diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index afa7eb06cb4..1fd71a38472 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -61,4 +61,4 @@ - if @merge_request.errors.any? = form_errors(@merge_request) - = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" + = f.submit 'Compare branches and continue', class: "btn btn-success mr-compare-btn" diff --git a/app/views/projects/merge_requests/diffs/_diffs.html.haml b/app/views/projects/merge_requests/diffs/_diffs.html.haml index bf3df0abf86..9ebd91dea0b 100644 --- a/app/views/projects/merge_requests/diffs/_diffs.html.haml +++ b/app/views/projects/merge_requests/diffs/_diffs.html.haml @@ -14,7 +14,7 @@ %span.ref-name= @merge_request.source_branch and %span.ref-name= @merge_request.target_branch - .text-center= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-save' + .text-center= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-success' - else - diff_viewable = @merge_request_diff ? @merge_request_diff.viewable? : true - if diff_viewable diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 28f0a167128..ebd3229e42b 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -20,8 +20,8 @@ .form-actions - if @milestone.new_record? - = f.submit 'Create milestone', class: "btn-create btn qa-milestone-create-button" + = f.submit 'Create milestone', class: "btn-success btn qa-milestone-create-button" = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel" - else - = f.submit 'Save changes', class: "btn-save btn" + = f.submit 'Save changes', class: "btn-success btn" = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel" diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index 26d2ea8447b..57f3c640696 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -8,7 +8,7 @@ .nav-controls = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @project) - = link_to new_project_milestone_path(@project), class: "btn btn-new qa-new-project-milestone", title: 'New milestone' do + = link_to new_project_milestone_path(@project), class: "btn btn-success qa-new-project-milestone", title: 'New milestone' do New milestone .milestones diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml index c6764c7607a..d523df1cd90 100644 --- a/app/views/projects/mirrors/_mirror_repos.html.haml +++ b/app/views/projects/mirrors/_mirror_repos.html.haml @@ -32,7 +32,7 @@ = link_to icon('question-circle'), help_page_path('user/project/protected_branches') .panel-footer - = f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror + = f.submit _('Mirror repository'), class: 'btn btn-success', name: :update_remote_mirror .panel.panel-default .table-responsive diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 7e1a3b9bea6..88ab486a248 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -4,7 +4,7 @@ Pages - if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) - = link_to new_project_pages_domain_path(@project), class: 'btn btn-new float-right', title: 'New Domain' do + = link_to new_project_pages_domain_path(@project), class: 'btn btn-success float-right', title: 'New Domain' do New Domain %p.light diff --git a/app/views/projects/pages_domains/edit.html.haml b/app/views/projects/pages_domains/edit.html.haml index ee70de22f13..342b1482df7 100644 --- a/app/views/projects/pages_domains/edit.html.haml +++ b/app/views/projects/pages_domains/edit.html.haml @@ -8,4 +8,4 @@ = form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f| = render 'form', { f: f } .form-actions - = f.submit 'Save Changes', class: "btn btn-save" + = f.submit 'Save Changes', class: "btn btn-success" diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml index 376ce3f68aa..94ad1470052 100644 --- a/app/views/projects/pages_domains/new.html.haml +++ b/app/views/projects/pages_domains/new.html.haml @@ -7,6 +7,6 @@ = form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f| = render 'form', { f: f } .form-actions - = f.submit 'Create New Domain', class: "btn btn-save" + = f.submit 'Create New Domain', class: "btn btn-success" .float-right = link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index 9a981d53ab6..259979417e0 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -39,5 +39,5 @@ = f.check_box :active, required: false, value: @schedule.active? = _('Active') .footer-block.row-content-block - = f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3 + = f.submit _('Save pipeline schedule'), class: 'btn btn-success', tabindex: 3 = link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index 3677666070e..0580c15ad15 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -11,7 +11,7 @@ - if can?(current_user, :create_pipeline_schedule, @project) .nav-controls - = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do + = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-success' do %span= _('New schedule') - if @schedules.present? diff --git a/app/views/projects/project_members/_new_project_group.html.haml b/app/views/projects/project_members/_new_project_group.html.haml index 7de24ec5ad2..74570769117 100644 --- a/app/views/projects/project_members/_new_project_group.html.haml +++ b/app/views/projects/project_members/_new_project_group.html.haml @@ -17,4 +17,4 @@ .clearable-input = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: _('Expiration date'), id: 'expires_at_groups' %i.clear-icon.js-clear-input - = submit_tag _("Invite"), class: "btn btn-create" + = submit_tag _("Invite"), class: "btn btn-success" diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index 6272687be1c..517fd249f6e 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -17,5 +17,5 @@ = label_tag :expires_at, 'Access expiration date', class: 'label-bold' = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' %i.clear-icon.js-clear-input - = f.submit "Add to project", class: "btn btn-create" + = f.submit "Add to project", class: "btn btn-success" = link_to "Import", import_project_project_members_path(@project), class: "btn btn-default", title: "Import members from another project" diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml index 6a52e72bfd8..8b93e81cd31 100644 --- a/app/views/projects/project_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -11,5 +11,5 @@ .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(@projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions - = button_tag 'Import project members', class: "btn btn-create" + = button_tag 'Import project members', class: "btn btn-success" = link_to "Cancel", project_project_members_path(@project), class: "btn btn-cancel" diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml index df2dcf19ed4..c3b8f2f8964 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -30,4 +30,4 @@ = yield :push_access_levels .card-footer - = f.submit 'Protect', class: 'btn-create btn', disabled: true + = f.submit 'Protect', class: 'btn-success btn', disabled: true diff --git a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml index f98781b77f4..b274c73d035 100644 --- a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml +++ b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml @@ -26,4 +26,4 @@ = yield :create_access_levels .card-footer - = f.submit 'Protect', class: 'btn-create btn', disabled: true + = f.submit 'Protect', class: 'btn-success btn', disabled: true diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 8093cc2c2d7..52c6c7ec424 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -19,5 +19,5 @@ = render 'shared/notes/hints' .error-alert .prepend-top-default - = f.submit 'Save changes', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-success' = link_to "Cancel", project_tag_path(@project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/projects/runners/_group_runners.html.haml b/app/views/projects/runners/_group_runners.html.haml index 86de71c732b..a6c16c70313 100644 --- a/app/views/projects/runners/_group_runners.html.haml +++ b/app/views/projects/runners/_group_runners.html.haml @@ -28,7 +28,7 @@ - group_link = link_to _('Group CI/CD settings'), group_settings_ci_cd_path(@project.group) = _('Group maintainers can register group runners in the %{link}').html_safe % { link: group_link } - else - = _('Ask your group maintainer to setup a group Runner.') + = _('Ask your group maintainer to set up a group Runner.') - else %h4.underlined-title diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml index 9314804c5dd..9409418bbcc 100644 --- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml @@ -1,6 +1,6 @@ - run_actions_text = "Perform common operations on GitLab project: #{@project.full_name}" -%p To setup this service: +%p To set up this service: %ul.list-unstyled.indent-list %li 1. diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml index f25d2ecdfb1..9a7004f89c0 100644 --- a/app/views/projects/services/slack_slash_commands/_help.html.haml +++ b/app/views/projects/services/slack_slash_commands/_help.html.haml @@ -14,7 +14,7 @@ by entering %kbd.inline /<command> help - unless @service.template? - %p To setup this service: + %p To set up this service: %ul.list-unstyled.indent-list %li 1. diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml index 9134257b631..ae923d8e6dc 100644 --- a/app/views/projects/settings/ci_cd/_form.html.haml +++ b/app/views/projects/settings/ci_cd/_form.html.haml @@ -121,7 +121,7 @@ go test -cover (Go) %code coverage: \d+.\d+% of statements - = f.submit _('Save changes'), class: "btn btn-save" + = f.submit _('Save changes'), class: "btn btn-success" %hr diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml index 4a3aa3dc626..ea963510a68 100644 --- a/app/views/projects/snippets/_actions.html.haml +++ b/app/views/projects/snippets/_actions.html.haml @@ -8,7 +8,7 @@ = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do = _('Delete') - if can?(current_user, :create_project_snippet, @project) - = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-create', title: _("New snippet") do + = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-success', title: _("New snippet") do = _('New snippet') - if @snippet.submittable_as_spam_by?(current_user) = link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam') diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 1c4c73dc776..a4974d89c1a 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -7,6 +7,6 @@ .nav-controls - if can?(current_user, :create_project_snippet, @project) - = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-new", title: _("New snippet") + = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet") = render 'snippets/snippets' diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 20b4705521c..37535370940 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -25,7 +25,7 @@ %li = link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value) - if can?(current_user, :push_code, @project) - = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do + = link_to new_project_tag_path(@project), class: 'btn btn-success new-tag-btn' do = s_('TagsPage|New tag') = link_to project_tags_path(@project, rss_url_options), title: _("Tags feed"), class: 'btn rss-btn has-tooltip' do = icon("rss") diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index da822ac5675..24724394259 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -41,7 +41,7 @@ .form-text.text-muted = s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.') .form-actions - = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create' + = button_tag s_('TagsPage|Create tag'), class: 'btn btn-success' = link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel' -# haml-lint:disable InlineJavaScript %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml index 1a5fc56f429..a9abfac239c 100644 --- a/app/views/projects/triggers/_form.html.haml +++ b/app/views/projects/triggers/_form.html.haml @@ -8,4 +8,4 @@ .form-group = f.label :key, "Description", class: "label-bold" = f.text_field :description, class: "form-control", required: true, title: 'Trigger description is required.', placeholder: "Trigger description" - = f.submit btn_text, class: "btn btn-save" + = f.submit btn_text, class: "btn btn-success" diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index e8681da6528..70f1bf8ef46 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -7,4 +7,4 @@ $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); $('.save-project-loader').hide(); $('.project-edit-container').show(); - $('.edit-project .js-btn-save-general-project-settings').enable(); + $('.edit-project .js-btn-success-general-project-settings').enable(); diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 35872d70db4..7d8826e540c 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -51,10 +51,10 @@ .form-actions - if @page && @page.persisted? - = f.submit _("Save changes"), class: 'btn-save btn' + = f.submit _("Save changes"), class: 'btn-success btn' .float-right = link_to _("Cancel"), project_wiki_path(@project, @page), class: 'btn btn-cancel btn-grouped' - else - = f.submit s_("Wiki|Create page"), class: 'btn-create btn' + = f.submit s_("Wiki|Create page"), class: 'btn-success btn' .float-right = link_to _("Cancel"), project_wiki_path(@project, :home), class: 'btn btn-cancel' diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 8d91f411f89..643b51e01d1 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -1,6 +1,6 @@ - if (@page && @page.persisted?) - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-success", "data-toggle" => "modal" do = s_("Wiki|New page") = link_to project_wiki_history_path(@project, @page), class: "btn" do = s_("Wiki|Page history") diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 38382aae67c..dc12e368b35 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -15,4 +15,4 @@ = icon('lightbulb-o') = s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.") .form-actions - = button_tag s_("Wiki|Create page"), class: "build-new-wiki btn btn-create" + = button_tag s_("Wiki|Create page"), class: "build-new-wiki btn btn-success" diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index e12b8f2858c..80aa1500d53 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -22,7 +22,7 @@ .nav-controls - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-success", "data-toggle" => "modal" do = s_("Wiki|New page") - if @page.persisted? = link_to project_wiki_history_path(@project, @page), class: "btn" do diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml index d38d161047b..9bc67a7c715 100644 --- a/app/views/shared/_new_project_item_select.html.haml +++ b/app/views/shared/_new_project_item_select.html.haml @@ -1,7 +1,7 @@ - if any_projects?(@projects) .project-item-select-holder.btn-group - %a.btn.btn-new.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } } + %a.btn.btn-success.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } } = icon('spinner spin') = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled] - %button.btn.btn-new.new-project-item-select-button.qa-new-project-item-select-button + %button.btn.btn-success.new-project-item-select-button.qa-new-project-item-select-button = icon('caret-down') diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml index 58d310fac16..f4df7bdcd83 100644 --- a/app/views/shared/_personal_access_tokens_form.html.haml +++ b/app/views/shared/_personal_access_tokens_form.html.haml @@ -26,4 +26,4 @@ = render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes .prepend-top-default - = f.submit "Create #{type} token", class: "btn btn-create" + = f.submit "Create #{type} token", class: "btn btn-success" diff --git a/app/views/shared/_recaptcha_form.html.haml b/app/views/shared/_recaptcha_form.html.haml index a0ba1afc284..10f358402c1 100644 --- a/app/views/shared/_recaptcha_form.html.haml +++ b/app/views/shared/_recaptcha_form.html.haml @@ -17,4 +17,4 @@ - if has_submit .row-content-block.footer-block - = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create' + = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-success' diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml index e8749ee3956..b629ceafeb3 100644 --- a/app/views/shared/empty_states/_labels.html.haml +++ b/app/views/shared/empty_states/_labels.html.haml @@ -7,5 +7,5 @@ %h4= _("Labels can be applied to issues and merge requests to categorize them.") %p= _("You can also star a label to make it a priority label.") - if can?(current_user, :admin_label, @project) - = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link' + = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success', title: _('New label'), id: 'new_label_link' = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link' diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml index 186139f3526..421a1b2415b 100644 --- a/app/views/shared/empty_states/_merge_requests.html.haml +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -17,7 +17,7 @@ - if project_select_button = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests, with_feature_enabled: 'merge_requests' - else - = link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link' + = link_to _('New merge request'), button_path, class: 'btn btn-success', title: _('New merge request'), id: 'new_merge_request_link' - else %h4.text-center = _("There are no merge requests to show") diff --git a/app/views/shared/empty_states/_wikis.html.haml b/app/views/shared/empty_states/_wikis.html.haml index f1a41074c28..5351c9ce6a4 100644 --- a/app/views/shared/empty_states/_wikis.html.haml +++ b/app/views/shared/empty_states/_wikis.html.haml @@ -2,7 +2,7 @@ - if can?(current_user, :create_wiki, @project) - create_path = project_wiki_path(@project, params[:id], { view: 'create' }) - - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-new', title: s_('WikiEmpty|Create your first page') + - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-success', title: s_('WikiEmpty|Create your first page') = render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do %h4 @@ -13,7 +13,7 @@ - elsif can?(current_user, :read_issue, @project) - issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project) - - new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-new', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement') + - new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-success', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement') = render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do %h4 diff --git a/app/views/shared/issuable/_board_create_list_dropdown.html.haml b/app/views/shared/issuable/_board_create_list_dropdown.html.haml index 23b2e1b91e5..4597d9439fa 100644 --- a/app/views/shared/issuable/_board_create_list_dropdown.html.haml +++ b/app/views/shared/issuable/_board_create_list_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.prepend-left-10#js-add-list - %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data } + %button.btn.btn-success.btn-inverted.js-new-board-list{ type: "button", data: board_list_data } Add list .dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable.js-tab-container-labels = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" } diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index b49e47a7266..5b28a43a361 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -69,9 +69,9 @@ %span.append-right-10 - if issuable.new_record? - = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create qa-issuable-create-button' + = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-success qa-issuable-create-button' - else - = form.submit 'Save changes', class: 'btn btn-save' + = form.submit 'Save changes', class: 'btn btn-success' - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = issuable.project.present.contribution_guide_path) .inline.prepend-top-10 diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 9ce7f6fe269..659e03fd67d 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -34,7 +34,7 @@ %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { action: 'submit' } } %button.btn.btn-link - = icon('search') + = sprite_icon('search') %span Press Enter or click to search %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } @@ -42,7 +42,8 @@ %button.btn.btn-link -# Encapsulate static class name `{{icon}}` inside #{} to bypass -# haml lint's ClassAttributeWithStaticValue - %i.fa{ class: "#{'{{icon}}'}" } + %svg + %use{ 'xlink:href': "#{'{{icon}}'}" } %span.js-filter-hint {{hint}} %span.js-filter-tag.dropdown-light-content diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 0ca35ea1298..32b609eed0d 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -159,7 +159,7 @@ = dropdown_content = dropdown_loading = dropdown_footer add_content_class: true do - %button.btn.btn-new.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ disabled: true } + %button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ disabled: true } = _('Move') = icon('spinner spin', class: 'sidebar-move-issue-confirmation-loading-icon') diff --git a/app/views/shared/labels/_form.html.haml b/app/views/shared/labels/_form.html.haml index 2bf5efae1e6..335c34a4632 100644 --- a/app/views/shared/labels/_form.html.haml +++ b/app/views/shared/labels/_form.html.haml @@ -28,7 +28,7 @@ .form-actions - if @label.persisted? - = f.submit 'Save changes', class: 'btn btn-save js-save-button' + = f.submit 'Save changes', class: 'btn btn-success js-save-button' - else - = f.submit 'Create label', class: 'btn btn-create js-save-button' + = f.submit 'Create label', class: 'btn btn-success js-save-button' = link_to 'Cancel', back_path, class: 'btn btn-cancel' diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml index ed336df4e9d..0674c822d63 100644 --- a/app/views/shared/notes/_comment_button.html.haml +++ b/app/views/shared/notes/_comment_button.html.haml @@ -1,7 +1,7 @@ - noteable_name = @note.noteable.human_class_name .float-left.btn-group.append-right-10.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown - %input.btn.btn-nr.btn-create.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' } + %input.btn.btn-nr.btn-success.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' } - if @note.can_be_discussion_note? = button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => 'Open comment type dropdown' do diff --git a/app/views/shared/notes/_edit_form.html.haml b/app/views/shared/notes/_edit_form.html.haml index 71a5b94e958..fec966069b9 100644 --- a/app/views/shared/notes/_edit_form.html.haml +++ b/app/views/shared/notes/_edit_form.html.haml @@ -9,6 +9,6 @@ .note-form-actions.clearfix .settings-message.note-edit-warning.js-finish-edit-warning Finish editing this message first! - = submit_tag 'Save comment', class: 'btn btn-nr btn-save js-comment-save-button' + = submit_tag 'Save comment', class: 'btn btn-nr btn-success js-comment-save-button' %button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' } Cancel diff --git a/app/views/shared/runners/_form.html.haml b/app/views/shared/runners/_form.html.haml index fa93307be31..daf08d9bb2c 100644 --- a/app/views/shared/runners/_form.html.haml +++ b/app/views/shared/runners/_form.html.haml @@ -51,6 +51,6 @@ = _('Tags') .col-sm-10 = f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control' - .form-text.text-muted= _('You can setup jobs to only use Runners with specific tags. Separate tags with commas.') + .form-text.text-muted= _('You can set up jobs to only use Runners with specific tags. Separate tags with commas.') .form-actions = f.submit _('Save changes'), class: 'btn btn-success' diff --git a/app/views/shared/runners/_runner_description.html.haml b/app/views/shared/runners/_runner_description.html.haml index da5c032add5..5935750ca06 100644 --- a/app/views/shared/runners/_runner_description.html.haml +++ b/app/views/shared/runners/_runner_description.html.haml @@ -1,6 +1,6 @@ .light.prepend-top-default %p - = _("A 'Runner' is a process which runs a job. You can setup as many Runners as you need.") + = _("A 'Runner' is a process which runs a job. You can set up as many Runners as you need.") %br = _('Runners can be placed on separate users, servers, and even on your local machine.') diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 5e5c050d5c3..1b66d3acd40 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -32,9 +32,9 @@ .form-actions - if @snippet.new_record? - = f.submit 'Create snippet', class: "btn-create btn" + = f.submit 'Create snippet', class: "btn-success btn" - else - = f.submit 'Save changes', class: "btn-save btn" + = f.submit 'Save changes', class: "btn-success btn" - if @snippet.project_id = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel" diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml index ae69d0d07c7..0ce13ee7a53 100644 --- a/app/views/snippets/_actions.html.haml +++ b/app/views/snippets/_actions.html.haml @@ -7,7 +7,7 @@ - if can?(current_user, :admin_personal_snippet, @snippet) = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do Delete - = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do + = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-success", title: "New snippet" do New snippet - if @snippet.submittable_as_spam_by?(current_user) = link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index cc0e93c0755..39d4d82a77d 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -8,13 +8,13 @@ - if current_user.two_factor_otp_enabled? .row.append-bottom-10 .col-md-4 - %button#js-setup-u2f-device.btn.btn-info.btn-block Setup new U2F device + %button#js-setup-u2f-device.btn.btn-info.btn-block Set up new U2F device .col-md-8 %p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left. - else .row.append-bottom-10 .col-md-4 - %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Setup new U2F device + %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Set up new U2F device .col-md-8 %p.text-warning You need to register a two-factor authentication app before you can set up a U2F device. diff --git a/app/workers/new_merge_request_worker.rb b/app/workers/new_merge_request_worker.rb index 62f9d9b6f57..fa48c1b29a8 100644 --- a/app/workers/new_merge_request_worker.rb +++ b/app/workers/new_merge_request_worker.rb @@ -10,7 +10,7 @@ class NewMergeRequestWorker EventCreateService.new.open_mr(issuable, user) NotificationService.new.new_merge_request(issuable, user) - issuable.diffs.write_cache + issuable.diffs(include_stats: false).write_cache issuable.create_cross_references!(user) end |