diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-17 12:07:33 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-17 12:07:33 +0000 |
commit | 6b75320f525f841454f1ab162d141d3610f2e77b (patch) | |
tree | 4971c27759e4fbc18b85e71800c3b9c12346317e /app | |
parent | 4226aca420920c1844e8eade4798a2dff188a6fc (diff) | |
download | gitlab-ce-6b75320f525f841454f1ab162d141d3610f2e77b.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
51 files changed, 452 insertions, 190 deletions
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js index 702f00888d0..77c68cac4a6 100644 --- a/app/assets/javascripts/jobs/store/mutations.js +++ b/app/assets/javascripts/jobs/store/mutations.js @@ -26,7 +26,7 @@ export default { if (log.append) { if (isNewJobLogActive()) { - state.trace = updateIncrementalTrace(log.lines, state.trace); + state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace; } else { state.trace += log.html; } @@ -35,9 +35,9 @@ export default { // When the job still does not have a trace // the trace response will not have a defined // html or size. We keep the old value otherwise these - // will be set to `undefined` + // will be set to `null` if (isNewJobLogActive()) { - state.trace = logLinesParser(log.lines) || state.trace; + state.trace = log.lines ? logLinesParser(log.lines) : state.trace; } else { state.trace = log.html || state.trace; } diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js index 12069e0c123..58e49f54d96 100644 --- a/app/assets/javascripts/jobs/store/utils.js +++ b/app/assets/javascripts/jobs/store/utils.js @@ -147,13 +147,15 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => { const firstNew = newLog[0]; - if (last.offset === firstNew.offset || (last.line && last.line.offset === firstNew.offset)) { - cloneOldLog.splice(lastIndex); - } else if (last.lines && last.lines.length) { - const lastNestedIndex = last.lines.length - 1; - const lastNested = last.lines[lastNestedIndex]; - if (lastNested.offset === firstNew.offset) { - last.lines.splice(lastNestedIndex); + if (last && firstNew) { + if (last.offset === firstNew.offset || (last.line && last.line.offset === firstNew.offset)) { + cloneOldLog.splice(lastIndex); + } else if (last.lines && last.lines.length) { + const lastNestedIndex = last.lines.length - 1; + const lastNested = last.lines[lastNestedIndex]; + if (lastNested.offset === firstNew.offset) { + last.lines.splice(lastNestedIndex); + } } } @@ -170,7 +172,7 @@ export const findOffsetAndRemove = (newLog = [], oldParsed = []) => { * @param array oldLog * @param array newLog */ -export const updateIncrementalTrace = (newLog, oldParsed = []) => { +export const updateIncrementalTrace = (newLog = [], oldParsed = []) => { const parsedLog = findOffsetAndRemove(newLog, oldParsed); return logLinesParser(newLog, parsedLog); diff --git a/app/assets/javascripts/pages/groups/registry/repositories/index.js b/app/assets/javascripts/pages/groups/registry/repositories/index.js new file mode 100644 index 00000000000..b663defad0e --- /dev/null +++ b/app/assets/javascripts/pages/groups/registry/repositories/index.js @@ -0,0 +1,3 @@ +import initRegistryImages from '~/registry'; + +document.addEventListener('DOMContentLoaded', initRegistryImages); diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index a20bae9e37e..11b2c3b7016 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -2,17 +2,19 @@ import { mapGetters, mapActions } from 'vuex'; import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui'; import store from '../stores'; -import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import CollapsibleContainer from './collapsible_container.vue'; +import ProjectEmptyState from './project_empty_state.vue'; +import GroupEmptyState from './group_empty_state.vue'; import { s__, sprintf } from '../../locale'; export default { name: 'RegistryListApp', components: { - clipboardButton, CollapsibleContainer, GlEmptyState, GlLoadingIcon, + ProjectEmptyState, + GroupEmptyState, }, props: { characterError: { @@ -38,19 +40,27 @@ export default { }, personalAccessTokensHelpLink: { type: String, - required: true, + required: false, + default: null, }, registryHostUrlWithPort: { type: String, - required: true, + required: false, + default: null, }, repositoryUrl: { type: String, required: true, }, + isGroupPage: { + type: Boolean, + default: false, + required: false, + }, twoFactorAuthHelpLink: { type: String, - required: true, + required: false, + default: null, }, }, store, @@ -91,37 +101,10 @@ export default { false, ); }, - notLoggedInToRegistryText() { - return sprintf( - s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to - the Container Registry by using your GitLab username and password. If you have - %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a - %{personalAccessTokensDocLinkStart}Personal Access Token - %{personalAccessTokensDocLinkEnd}instead of a password.`), - { - twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`, - twofaDocLinkEnd: '</a>', - personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`, - personalAccessTokensDocLinkEnd: '</a>', - }, - false, - ); - }, - dockerLoginCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings - return `docker login ${this.registryHostUrlWithPort}`; - }, - dockerBuildCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings - return `docker build -t ${this.repositoryUrl} .`; - }, - dockerPushCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings - return `docker push ${this.repositoryUrl}`; - }, }, created() { this.setMainEndpoint(this.endpoint); + this.setIsDeleteDisabled(this.isGroupPage); }, mounted() { if (!this.characterError) { @@ -129,7 +112,7 @@ export default { } }, methods: { - ...mapActions(['setMainEndpoint', 'fetchRepos']), + ...mapActions(['setMainEndpoint', 'fetchRepos', 'setIsDeleteDisabled']), }, }; </script> @@ -152,57 +135,19 @@ export default { <p v-html="introText"></p> <collapsible-container v-for="item in repos" :key="item.id" :repo="item" /> </div> - - <gl-empty-state - v-else - :title="s__('ContainerRegistry|There are no container images stored for this project')" - :svg-path="noContainersImage" - class="container-message" - > - <template #description> - <p class="js-no-container-images-text" v-html="noContainerImagesText"></p> - <h5>{{ s__('ContainerRegistry|Quick Start') }}</h5> - <p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p> - <div class="input-group append-bottom-10"> - <input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly /> - <span class="input-group-append"> - <clipboard-button - :text="dockerLoginCommand" - :title="s__('ContainerRegistry|Copy login command')" - class="input-group-text" - /> - </span> - </div> - <p> - {{ - s__( - 'ContainerRegistry|You can add an image to this registry with the following commands:', - ) - }} - </p> - - <div class="input-group append-bottom-10"> - <input :value="dockerBuildCommand" type="text" class="form-control monospace" readonly /> - <span class="input-group-append"> - <clipboard-button - :text="dockerBuildCommand" - :title="s__('ContainerRegistry|Copy build command')" - class="input-group-text" - /> - </span> - </div> - - <div class="input-group"> - <input :value="dockerPushCommand" type="text" class="form-control monospace" readonly /> - <span class="input-group-append"> - <clipboard-button - :text="dockerPushCommand" - :title="s__('ContainerRegistry|Copy push command')" - class="input-group-text" - /> - </span> - </div> - </template> - </gl-empty-state> + <project-empty-state + v-else-if="!isGroupPage" + :no-containers-image="noContainersImage" + :help-page-path="helpPagePath" + :repository-url="repositoryUrl" + :two-factor-auth-help-link="twoFactorAuthHelpLink" + :personal-access-tokens-help-link="personalAccessTokensHelpLink" + :registry-host-url-with-port="registryHostUrlWithPort" + /> + <group-empty-state + v-else-if="isGroupPage" + :no-containers-image="noContainersImage" + :help-page-path="helpPagePath" + /> </div> </template> diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index 3e31d24088e..ed48331f459 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -1,5 +1,5 @@ <script> -import { mapActions } from 'vuex'; +import { mapActions, mapGetters } from 'vuex'; import { GlLoadingIcon, GlButton, GlTooltipDirective, GlModal, GlModalDirective } from '@gitlab/ui'; import createFlash from '../../flash'; import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; @@ -35,9 +35,13 @@ export default { }; }, computed: { + ...mapGetters(['isDeleteDisabled']), iconName() { return this.isOpen ? 'angle-up' : 'angle-right'; }, + canDeleteRepo() { + return this.repo.canDelete && !this.isDeleteDisabled; + }, }, methods: { ...mapActions(['fetchRepos', 'fetchList', 'deleteItem']), @@ -80,7 +84,7 @@ export default { <div class="controls d-none d-sm-block float-right"> <gl-button - v-if="repo.canDelete" + v-if="canDeleteRepo" v-gl-tooltip v-gl-modal="modalId" :title="s__('ContainerRegistry|Remove repository')" @@ -98,7 +102,7 @@ export default { <gl-loading-icon v-if="repo.isLoading" size="md" class="append-bottom-20" /> <div v-else-if="!repo.isLoading && isOpen" class="container-image-tags"> - <table-registry v-if="repo.list.length" :repo="repo" /> + <table-registry v-if="repo.list.length" :repo="repo" :can-delete-repo="canDeleteRepo" /> <div v-else class="nothing-here-block"> {{ s__('ContainerRegistry|No tags in Container Registry for this container image.') }} diff --git a/app/assets/javascripts/registry/components/group_empty_state.vue b/app/assets/javascripts/registry/components/group_empty_state.vue new file mode 100644 index 00000000000..7885fd2146d --- /dev/null +++ b/app/assets/javascripts/registry/components/group_empty_state.vue @@ -0,0 +1,46 @@ +<script> +import { GlEmptyState } from '@gitlab/ui'; +import { s__, sprintf } from '~/locale'; + +export default { + name: 'GroupEmptyState', + components: { + GlEmptyState, + }, + props: { + noContainersImage: { + type: String, + required: true, + }, + helpPagePath: { + type: String, + required: true, + }, + }, + computed: { + noContainerImagesText() { + return sprintf( + s__( + `ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here. %{docLinkStart}More Information%{docLinkEnd}`, + ), + { + docLinkStart: `<a href="${this.helpPagePath}" target="_blank">`, + docLinkEnd: '</a>', + }, + false, + ); + }, + }, +}; +</script> +<template> + <gl-empty-state + :title="s__('ContainerRegistry|There are no container images available in this group')" + :svg-path="noContainersImage" + class="container-message" + > + <template #description> + <p class="js-no-container-images-text" v-html="noContainerImagesText"></p> + </template> + </gl-empty-state> +</template> diff --git a/app/assets/javascripts/registry/components/project_empty_state.vue b/app/assets/javascripts/registry/components/project_empty_state.vue new file mode 100644 index 00000000000..80ef31004c8 --- /dev/null +++ b/app/assets/javascripts/registry/components/project_empty_state.vue @@ -0,0 +1,133 @@ +<script> +import { GlEmptyState } from '@gitlab/ui'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { s__, sprintf } from '~/locale'; + +export default { + name: 'ProjectEmptyState', + components: { + ClipboardButton, + GlEmptyState, + }, + props: { + noContainersImage: { + type: String, + required: true, + }, + repositoryUrl: { + type: String, + required: true, + }, + helpPagePath: { + type: String, + required: true, + }, + twoFactorAuthHelpLink: { + type: String, + required: true, + }, + personalAccessTokensHelpLink: { + type: String, + required: true, + }, + registryHostUrlWithPort: { + type: String, + required: true, + }, + }, + computed: { + dockerBuildCommand() { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + return `docker build -t ${this.repositoryUrl} .`; + }, + dockerPushCommand() { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + return `docker push ${this.repositoryUrl}`; + }, + dockerLoginCommand() { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + return `docker login ${this.registryHostUrlWithPort}`; + }, + noContainerImagesText() { + return sprintf( + s__(`ContainerRegistry|With the Container Registry, every project can have its own space to + store its Docker images. %{docLinkStart}More Information%{docLinkEnd}`), + { + docLinkStart: `<a href="${this.helpPagePath}" target="_blank">`, + docLinkEnd: '</a>', + }, + false, + ); + }, + notLoggedInToRegistryText() { + return sprintf( + s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to + the Container Registry by using your GitLab username and password. If you have + %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a + %{personalAccessTokensDocLinkStart}Personal Access Token%{personalAccessTokensDocLinkEnd} + instead of a password.`), + { + twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`, + twofaDocLinkEnd: '</a>', + personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`, + personalAccessTokensDocLinkEnd: '</a>', + }, + false, + ); + }, + }, +}; +</script> +<template> + <gl-empty-state + :title="s__('ContainerRegistry|There are no container images stored for this project')" + :svg-path="noContainersImage" + class="container-message" + > + <template #description> + <p class="js-no-container-images-text" v-html="noContainerImagesText"></p> + <h5>{{ s__('ContainerRegistry|Quick Start') }}</h5> + <p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p> + <div class="input-group append-bottom-10"> + <input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly /> + <span class="input-group-append"> + <clipboard-button + :text="dockerLoginCommand" + :title="s__('ContainerRegistry|Copy login command')" + class="input-group-text" + /> + </span> + </div> + <p></p> + <p> + {{ + s__( + 'ContainerRegistry|You can add an image to this registry with the following commands:', + ) + }} + </p> + + <div class="input-group append-bottom-10"> + <input :value="dockerBuildCommand" type="text" class="form-control monospace" readonly /> + <span class="input-group-append"> + <clipboard-button + :text="dockerBuildCommand" + :title="s__('ContainerRegistry|Copy build command')" + class="input-group-text" + /> + </span> + </div> + + <div class="input-group"> + <input :value="dockerPushCommand" type="text" class="form-control monospace" readonly /> + <span class="input-group-append"> + <clipboard-button + :text="dockerPushCommand" + :title="s__('ContainerRegistry|Copy push command')" + class="input-group-text" + /> + </span> + </div> + </template> + </gl-empty-state> +</template> diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 00acc0eb04a..ac7272c4d29 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -1,5 +1,5 @@ <script> -import { mapActions } from 'vuex'; +import { mapActions, mapGetters } from 'vuex'; import { GlButton, GlFormCheckbox, @@ -35,6 +35,11 @@ export default { type: Object, required: true, }, + canDeleteRepo: { + type: Boolean, + default: false, + required: false, + }, }, data() { return { @@ -45,6 +50,7 @@ export default { }; }, computed: { + ...mapGetters(['isDeleteDisabled']), bulkDeletePath() { return this.repo.tagsPath ? this.repo.tagsPath.replace('?format=json', '/bulk_destroy') : ''; }, @@ -165,6 +171,9 @@ export default { } } }, + canDeleteRow(item) { + return item && item.canDelete && !this.isDeleteDisabled; + }, }, }; </script> @@ -175,7 +184,7 @@ export default { <tr> <th> <gl-form-checkbox - v-if="repo.canDelete" + v-if="canDeleteRepo" class="js-select-all-checkbox" :checked="selectAllChecked" @change="onSelectAllChange" @@ -187,7 +196,7 @@ export default { <th>{{ s__('ContainerRegistry|Last Updated') }}</th> <th> <gl-button - v-if="repo.canDelete" + v-if="canDeleteRepo" v-gl-tooltip v-gl-modal="modalId" :disabled="!itemsToBeDeleted || itemsToBeDeleted.length === 0" @@ -208,7 +217,7 @@ export default { <tr v-for="(item, index) in repo.list" :key="item.tag" class="registry-image-row"> <td class="check"> <gl-form-checkbox - v-if="item.canDelete" + v-if="canDeleteRow(item)" class="js-select-checkbox" :checked="itemsToBeDeleted && itemsToBeDeleted.includes(index)" @change="updateItemsToBeDeleted(index)" @@ -244,7 +253,7 @@ export default { <td class="content action-buttons"> <gl-button - v-if="item.canDelete" + v-if="canDeleteRow(item)" v-gl-modal="modalId" :title="s__('ContainerRegistry|Remove tag')" :aria-label="s__('ContainerRegistry|Remove tag')" diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js index 38c3d67042c..18fd360f586 100644 --- a/app/assets/javascripts/registry/index.js +++ b/app/assets/javascripts/registry/index.js @@ -13,29 +13,24 @@ export default () => data() { const { dataset } = document.querySelector(this.$options.el); return { - characterError: Boolean(dataset.characterError), - containersErrorImage: dataset.containersErrorImage, - endpoint: dataset.endpoint, - helpPagePath: dataset.helpPagePath, - noContainersImage: dataset.noContainersImage, - personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink, - registryHostUrlWithPort: dataset.registryHostUrlWithPort, - repositoryUrl: dataset.repositoryUrl, - twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink, + registryData: { + endpoint: dataset.endpoint, + characterError: Boolean(dataset.characterError), + helpPagePath: dataset.helpPagePath, + noContainersImage: dataset.noContainersImage, + containersErrorImage: dataset.containersErrorImage, + repositoryUrl: dataset.repositoryUrl, + isGroupPage: dataset.isGroupPage, + personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink, + registryHostUrlWithPort: dataset.registryHostUrlWithPort, + twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink, + }, }; }, render(createElement) { return createElement('registry-app', { props: { - characterError: this.characterError, - containersErrorImage: this.containersErrorImage, - endpoint: this.endpoint, - helpPagePath: this.helpPagePath, - noContainersImage: this.noContainersImage, - personalAccessTokensHelpLink: this.personalAccessTokensHelpLink, - registryHostUrlWithPort: this.registryHostUrlWithPort, - repositoryUrl: this.repositoryUrl, - twoFactorAuthHelpLink: this.twoFactorAuthHelpLink, + ...this.registryData, }, }); }, diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js index a2e0130e79e..2121f518a7a 100644 --- a/app/assets/javascripts/registry/stores/actions.js +++ b/app/assets/javascripts/registry/stores/actions.js @@ -20,7 +20,6 @@ export const fetchRepos = ({ commit, state }) => { export const fetchList = ({ commit }, { repo, page }) => { commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); - return axios .get(repo.tagsPath, { params: { page } }) .then(response => { @@ -40,6 +39,7 @@ export const multiDeleteItems = (_, { path, items }) => axios.delete(path, { params: { ids: items } }); export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); +export const setIsDeleteDisabled = ({ commit }, data) => commit(types.SET_IS_DELETE_DISABLED, data); export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING); // prevent babel-plugin-rewire from generating an invalid default during karma tests diff --git a/app/assets/javascripts/registry/stores/getters.js b/app/assets/javascripts/registry/stores/getters.js index f4923512578..ac90bde1b2a 100644 --- a/app/assets/javascripts/registry/stores/getters.js +++ b/app/assets/javascripts/registry/stores/getters.js @@ -1,5 +1,6 @@ export const isLoading = state => state.isLoading; export const repos = state => state.repos; +export const isDeleteDisabled = state => state.isDeleteDisabled; // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/registry/stores/mutation_types.js b/app/assets/javascripts/registry/stores/mutation_types.js index 2c69bf11807..6740bfede1a 100644 --- a/app/assets/javascripts/registry/stores/mutation_types.js +++ b/app/assets/javascripts/registry/stores/mutation_types.js @@ -1,4 +1,5 @@ export const SET_MAIN_ENDPOINT = 'SET_MAIN_ENDPOINT'; +export const SET_IS_DELETE_DISABLED = 'SET_IS_DELETE_DISABLED'; export const SET_REPOS_LIST = 'SET_REPOS_LIST'; export const TOGGLE_MAIN_LOADING = 'TOGGLE_MAIN_LOADING'; diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js index 8ace6657ad1..ea5925247d1 100644 --- a/app/assets/javascripts/registry/stores/mutations.js +++ b/app/assets/javascripts/registry/stores/mutations.js @@ -6,6 +6,10 @@ export default { Object.assign(state, { endpoint }); }, + [types.SET_IS_DELETE_DISABLED](state, isDeleteDisabled) { + Object.assign(state, { isDeleteDisabled }); + }, + [types.SET_REPOS_LIST](state, list) { Object.assign(state, { repos: list.map(el => ({ @@ -17,6 +21,7 @@ export default { location: el.location, name: el.path, tagsPath: el.tags_path, + projectId: el.project_id, })), }); }, diff --git a/app/assets/javascripts/registry/stores/state.js b/app/assets/javascripts/registry/stores/state.js index feeac10cbe1..724c64b4994 100644 --- a/app/assets/javascripts/registry/stores/state.js +++ b/app/assets/javascripts/registry/stores/state.js @@ -1,6 +1,7 @@ export default () => ({ isLoading: false, endpoint: '', // initial endpoint to fetch the repos list + isDeleteDisabled: false, // controls the delete buttons in the registry /** * Each object in `repos` has the following strucure: * { diff --git a/app/controllers/boards/application_controller.rb b/app/controllers/boards/application_controller.rb index eab908ba5ed..15ef6698472 100644 --- a/app/controllers/boards/application_controller.rb +++ b/app/controllers/boards/application_controller.rb @@ -13,7 +13,7 @@ module Boards end def board_parent - @board_parent ||= board.parent + @board_parent ||= board.resource_parent end def record_not_found(exception) diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb index 90e04414d8d..880f7500708 100644 --- a/app/controllers/boards/lists_controller.rb +++ b/app/controllers/boards/lists_controller.rb @@ -9,7 +9,7 @@ module Boards skip_before_action :authenticate_user!, only: [:index] def index - lists = Boards::Lists::ListService.new(board.parent, current_user).execute(board) + lists = Boards::Lists::ListService.new(board.resource_parent, current_user).execute(board) List.preload_preferences_for_user(lists, current_user) @@ -17,7 +17,7 @@ module Boards end def create - list = Boards::Lists::CreateService.new(board.parent, current_user, create_list_params).execute(board) + list = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board) if list.valid? render json: serialize_as_json(list) diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 1ead631663e..672d31ec779 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -35,7 +35,7 @@ module MilestoneActions render json: tabs_json("shared/milestones/_labels_tab", { labels: milestone_labels.map do |label| - label.present(issuable_subject: @milestone.parent) + label.present(issuable_subject: @milestone.resource_parent) end }) end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 1eacae06457..1e9d51cf970 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -44,7 +44,7 @@ class Groups::MilestonesController < Groups::ApplicationController # all projects milestones states at once. milestones, update_params = get_milestones_for_update milestones.each do |milestone| - Milestones::UpdateService.new(milestone.parent, current_user, update_params).execute(milestone) + Milestones::UpdateService.new(milestone.resource_parent, current_user, update_params).execute(milestone) end redirect_to milestone_path diff --git a/app/controllers/groups/registry/repositories_controller.rb b/app/controllers/groups/registry/repositories_controller.rb new file mode 100644 index 00000000000..39f6963ee0a --- /dev/null +++ b/app/controllers/groups/registry/repositories_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true +module Groups + module Registry + class RepositoriesController < Groups::ApplicationController + before_action :verify_container_registry_enabled! + before_action :authorize_read_container_image! + + def index + track_event(:list_repositories) + + respond_to do |format| + format.html + format.json do + @images = group.container_repositories.with_api_entity_associations + + render json: ContainerRepositoriesSerializer + .new(current_user: current_user) + .represent(@images) + end + end + end + + private + + def verify_container_registry_enabled! + render_404 unless Gitlab.config.registry.enabled + end + + def authorize_read_container_image! + return render_404 unless can?(current_user, :read_container_image, group) + end + end + end +end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 3c70ff3b59f..115d1031a8a 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -290,7 +290,8 @@ module ApplicationSettingsHelper :snowplow_cookie_domain, :snowplow_enabled, :snowplow_site_id, - :push_event_hooks_limit + :push_event_hooks_limit, + :push_event_activities_limit ] end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index f05218efe0c..4f31cc67ccc 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -76,10 +76,10 @@ module GitlabRoutingHelper end def edit_milestone_path(entity, *args) - if entity.parent.is_a?(Group) - edit_group_milestone_path(entity.parent, entity, *args) + if entity.resource_parent.is_a?(Group) + edit_group_milestone_path(entity.resource_parent, entity, *args) else - edit_project_milestone_path(entity.parent, entity, *args) + edit_project_milestone_path(entity.resource_parent, entity, *args) end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 9cba87ac444..811467ca03a 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -15,6 +15,16 @@ module GroupsHelper %w[groups#projects groups#edit badges#index ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index] end + def group_packages_nav_link_paths + %w[ + groups/container_registries#index + ] + end + + def group_container_registry_nav? + Gitlab.config.registry.enabled && can?(current_user, :read_container_image, @group) + end + def group_sidebar_links @group_sidebar_links ||= get_group_sidebar_links end diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index a919c068c42..dce0842060d 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -45,8 +45,8 @@ module TodosHelper end def todo_parent_path(todo) - if todo.parent.is_a?(Group) - link_to todo.parent.name, group_path(todo.parent) + if todo.resource_parent.is_a?(Group) + link_to todo.resource_parent.name, group_path(todo.resource_parent) else link_to_project(todo.project) end @@ -64,7 +64,7 @@ module TodosHelper if todo.for_commit? project_commit_path(todo.project, todo.target, path_options) else - path = [todo.parent, todo.target] + path = [todo.resource_parent, todo.target] path.unshift(:pipelines) if todo.build_failed? diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 0724ee8f39d..a07933d4975 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -217,6 +217,9 @@ class ApplicationSetting < ApplicationRecord validates :push_event_hooks_limit, numericality: { greater_than_or_equal_to: 0 } + validates :push_event_activities_limit, + numericality: { greater_than_or_equal_to: 0 } + SUPPORTED_KEY_TYPES.each do |type| validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index e9aab4a3d05..b341cf04403 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -83,6 +83,7 @@ module ApplicationSettingImplementation project_export_enabled: true, protected_ci_variables: false, push_event_hooks_limit: 3, + push_event_activities_limit: 3, raw_blob_request_limit: 300, recaptcha_enabled: false, login_recaptcha_protection_enabled: false, diff --git a/app/models/board.rb b/app/models/board.rb index 31011dc4742..f3f938224a4 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -16,10 +16,9 @@ class Board < ApplicationRecord !group end - def parent - @parent ||= group || project + def resource_parent + @resource_parent ||= group || project end - alias_method :resource_parent, :parent def group_board? group_id.present? diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index e055b66989b..27bb76835c7 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -11,6 +11,7 @@ class ContainerRepository < ApplicationRecord delegate :client, to: :registry scope :ordered, -> { order(:name) } + scope :with_api_entity_associations, -> { preload(:project) } # rubocop: disable CodeReuse/ServiceClass def registry diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 1d553fc8312..7d766e1f25c 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -11,7 +11,7 @@ class GlobalMilestone delegate :title, :state, :due_date, :start_date, :participants, :project, :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, - :milestoneish_id, :parent, to: :milestone + :milestoneish_id, :resource_parent, to: :milestone def to_hash { diff --git a/app/models/gpg_signature.rb b/app/models/gpg_signature.rb index 46cac1d41bb..0c36e51120f 100644 --- a/app/models/gpg_signature.rb +++ b/app/models/gpg_signature.rb @@ -23,6 +23,8 @@ class GpgSignature < ApplicationRecord validates :project_id, presence: true validates :gpg_key_primary_keyid, presence: true + scope :by_commit_sha, ->(shas) { where(commit_sha: shas) } + def self.with_key_and_subkeys(gpg_key) subkey_ids = gpg_key.subkeys.pluck(:id) diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index ca50820a879..fe8ba9765b7 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -136,6 +136,7 @@ class MergeRequestDiff < ApplicationRecord # All diff information is collected from repository after object is created. # It allows you to override variables like head_commit_sha before getting diff. after_create :save_git_content, unless: :importing? + after_create_commit :set_as_latest_diff after_save :update_external_diff_store, if: -> { !importing? && saved_change_to_external_diff? } @@ -150,10 +151,6 @@ class MergeRequestDiff < ApplicationRecord # Collect information about commits and diff from repository # and save it to the database as serialized data def save_git_content - MergeRequest - .where('id = ? AND COALESCE(latest_merge_request_diff_id, 0) < ?', self.merge_request_id, self.id) - .update_all(latest_merge_request_diff_id: self.id) - ensure_commit_shas save_commits save_diffs @@ -168,6 +165,12 @@ class MergeRequestDiff < ApplicationRecord keep_around_commits end + def set_as_latest_diff + MergeRequest + .where('id = ? AND COALESCE(latest_merge_request_diff_id, 0) < ?', self.merge_request_id, self.id) + .update_all(latest_merge_request_diff_id: self.id) + end + def ensure_commit_shas self.start_commit_sha ||= merge_request.target_branch_sha self.head_commit_sha ||= merge_request.source_branch_sha @@ -502,11 +505,6 @@ class MergeRequestDiff < ApplicationRecord merge_request.closed? && merge_request.metrics.latest_closed_at < EXTERNAL_DIFF_CUTOFF.ago end - # We can't rely on `merge_request.latest_merge_request_diff_id` because that - # may have been changed in `save_git_content` without being reflected in - # the association's instance. This query is always subject to races, but - # the worst case is that we *don't* make a diff external when we could. The - # background worker will make it external at a later date. def old_version? latest_id = MergeRequest .where(id: merge_request_id) @@ -514,7 +512,7 @@ class MergeRequestDiff < ApplicationRecord .pluck(:latest_merge_request_diff_id) .first - self.id != latest_id + latest_id && self.id < latest_id end def load_diffs(options) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 916c11a8d03..2fa0cfc9b93 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -257,10 +257,9 @@ class Milestone < ApplicationRecord title.to_slug.normalize.to_s end - def parent + def resource_parent group || project end - alias_method :resource_parent, :parent def group_milestone? group_id.present? diff --git a/app/models/note.rb b/app/models/note.rb index 3e645d79e15..43f349c6fa2 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -483,10 +483,9 @@ class Note < ApplicationRecord Upload.find_by(model: self, path: paths) end - def parent + def resource_parent project end - alias_method :resource_parent, :parent private diff --git a/app/models/push_event.rb b/app/models/push_event.rb index 4698df39730..6f7365a2763 100644 --- a/app/models/push_event.rb +++ b/app/models/push_event.rb @@ -26,6 +26,8 @@ class PushEvent < Event delegate :commit_count, to: :push_event_payload alias_method :commits_count, :commit_count + delegate :ref_count, to: :push_event_payload + # Returns events of pushes that either pushed to an existing ref or created a # new one. def self.created_or_pushed diff --git a/app/models/todo.rb b/app/models/todo.rb index 6b71845856a..456115872d1 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -144,10 +144,9 @@ class Todo < ApplicationRecord end end - def parent + def resource_parent project end - alias_method :resource_parent, :parent def unmergeable? action == UNMERGEABLE diff --git a/app/policies/board_policy.rb b/app/policies/board_policy.rb index b8435dad3f1..e2b16249c85 100644 --- a/app/policies/board_policy.rb +++ b/app/policies/board_policy.rb @@ -3,7 +3,7 @@ class BoardPolicy < BasePolicy include FindGroupProjects - delegate { @subject.parent } + delegate { @subject.resource_parent } condition(:is_group_board) { @subject.group_board? } condition(:is_project_board) { @subject.project_board? } @@ -19,7 +19,7 @@ class BoardPolicy < BasePolicy condition(:reporter_of_group_projects) do next unless @user - group_projects_for(user: @user, group: @subject.parent) + group_projects_for(user: @user, group: @subject.resource_parent) .visible_to_user_and_access_level(@user, ::Gitlab::Access::REPORTER) .exists? end diff --git a/app/policies/milestone_policy.rb b/app/policies/milestone_policy.rb index 2d56eea6a78..9cea8ddd7b3 100644 --- a/app/policies/milestone_policy.rb +++ b/app/policies/milestone_policy.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class MilestonePolicy < BasePolicy - delegate { @subject.parent } + delegate { @subject.resource_parent } end diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb index cc746698a05..db9cf1c7835 100644 --- a/app/serializers/container_repository_entity.rb +++ b/app/serializers/container_repository_entity.rb @@ -18,7 +18,7 @@ class ContainerRepositoryEntity < Grape::Entity alias_method :repository, :object def project - request.project + request.respond_to?(:project) ? request.project : object.project end def can_destroy? diff --git a/app/services/bulk_push_event_payload_service.rb b/app/services/bulk_push_event_payload_service.rb new file mode 100644 index 00000000000..54157bc23f9 --- /dev/null +++ b/app/services/bulk_push_event_payload_service.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class BulkPushEventPayloadService + def initialize(event, push_data) + @event = event + @push_data = push_data + end + + def execute + @event.build_push_event_payload( + action: @push_data[:action], + commit_count: 0, + ref_count: @push_data[:ref_count], + ref_type: @push_data[:ref_type] + ) + + @event.push_event_payload.tap(&:save!) + end +end diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 395c5fe09ac..f7282c22a52 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -73,15 +73,27 @@ class EventCreateService end def push(project, current_user, push_data) + create_push_event(PushEventPayloadService, project, current_user, push_data) + end + + def bulk_push(project, current_user, push_data) + create_push_event(BulkPushEventPayloadService, project, current_user, push_data) + end + + private + + def create_record_event(record, current_user, status) + create_event(record.resource_parent, current_user, status, target_id: record.id, target_type: record.class.name) + end + + def create_push_event(service_class, project, current_user, push_data) # We're using an explicit transaction here so that any errors that may occur # when creating push payload data will result in the event creation being # rolled back as well. event = Event.transaction do new_event = create_event(project, current_user, Event::PUSHED) - PushEventPayloadService - .new(new_event, push_data) - .execute + service_class.new(new_event, push_data).execute new_event end @@ -92,12 +104,6 @@ class EventCreateService Users::ActivityService.new(current_user, 'push').execute end - private - - def create_record_event(record, current_user, status) - create_event(record.resource_parent, current_user, status, target_id: record.id, target_type: record.class.name) - end - def create_event(resource_parent, current_user, status, attributes = {}) attributes.reverse_merge!( action: status, diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb index b1faef58e33..0801fd4d03f 100644 --- a/app/services/git/base_hooks_service.rb +++ b/app/services/git/base_hooks_service.rb @@ -48,6 +48,8 @@ module Git # Push events in the activity feed only show information for the # last commit. def create_events + return unless params.fetch(:create_push_event, true) + EventCreateService.new.push(project, current_user, event_push_data) end diff --git a/app/services/git/process_ref_changes_service.rb b/app/services/git/process_ref_changes_service.rb index 62159d4e7e5..3052bed51bc 100644 --- a/app/services/git/process_ref_changes_service.rb +++ b/app/services/git/process_ref_changes_service.rb @@ -16,8 +16,8 @@ module Git def process_changes_by_action(ref_type, changes) changes_by_action = group_changes_by_action(changes) - changes_by_action.each do |_, changes| - process_changes(ref_type, changes, execute_project_hooks: execute_project_hooks?(changes)) if changes.any? + changes_by_action.each do |action, changes| + process_changes(ref_type, action, changes, execute_project_hooks: execute_project_hooks?(changes)) if changes.any? end end @@ -38,9 +38,11 @@ module Git (changes.size <= Gitlab::CurrentSettings.push_event_hooks_limit) || Feature.enabled?(:git_push_execute_all_project_hooks, project) end - def process_changes(ref_type, changes, execute_project_hooks:) + def process_changes(ref_type, action, changes, execute_project_hooks:) push_service_class = push_service_class_for(ref_type) + create_bulk_push_event = changes.size > Gitlab::CurrentSettings.push_event_activities_limit + changes.each do |change| push_service_class.new( project, @@ -48,9 +50,20 @@ module Git change: change, push_options: params[:push_options], create_pipelines: change[:index] < PIPELINE_PROCESS_LIMIT || Feature.enabled?(:git_push_create_all_pipelines, project), - execute_project_hooks: execute_project_hooks + execute_project_hooks: execute_project_hooks, + create_push_event: !create_bulk_push_event ).execute end + + create_bulk_push_event(ref_type, action, changes) if create_bulk_push_event + end + + def create_bulk_push_event(ref_type, action, changes) + EventCreateService.new.bulk_push( + project, + current_user, + Gitlab::DataBuilder::Push.build_bulk(action: action, ref_type: ref_type, changes: changes) + ) end def push_service_class_for(ref_type) diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb index 076df10bf6f..7e6568b5b25 100644 --- a/app/services/notes/quick_actions_service.rb +++ b/app/services/notes/quick_actions_service.rb @@ -50,7 +50,7 @@ module Notes return if update_params.empty? return unless supported?(note) - self.class.noteable_update_service(note).new(note.parent, current_user, update_params).execute(note.noteable) + self.class.noteable_update_service(note).new(note.resource_parent, current_user, update_params).execute(note.noteable) end end end diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb index 3c8847d3c18..e686d3bf7c2 100644 --- a/app/services/search/snippet_service.rb +++ b/app/services/search/snippet_service.rb @@ -1,13 +1,7 @@ # frozen_string_literal: true module Search - class SnippetService - attr_accessor :current_user, :params - - def initialize(user, params) - @current_user, @params = user, params.dup - end - + class SnippetService < Search::GlobalService def execute Gitlab::SnippetSearchResults.new(current_user, params[:search]) end diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml index 22458223b93..6b02521a0f0 100644 --- a/app/views/admin/application_settings/_performance.html.haml +++ b/app/views/admin/application_settings/_performance.html.haml @@ -25,5 +25,10 @@ = f.number_field :push_event_hooks_limit, class: 'form-control' .form-text.text-muted = _("Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value.") + .form-group + = f.label :push_event_activities_limit, class: 'label-bold' + = f.number_field :push_event_activities_limit, class: 'form-control' + .form-text.text-muted + = _('Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push event will be created. Bulk push event will be created if it surpasses that value.') = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 21c418cb0e4..b9e88f3fc47 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -6,11 +6,13 @@ .event-title.d-flex.flex-wrap = inline_event_icon(event) - %span.event-type.d-inline-block.append-right-4.pushed #{event.action_name} #{event.ref_type} - %span.append-right-4 - - commits_link = project_commits_path(project, event.ref_name) - - should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name) - = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name' + - many_refs = event.ref_count.to_i > 1 + %span.event-type.d-inline-block.append-right-4.pushed= many_refs ? "#{event.action_name} #{event.ref_count} #{event.ref_type.pluralize}" : "#{event.action_name} #{event.ref_type}" + - unless many_refs + %span.append-right-4 + - commits_link = project_commits_path(project, event.ref_name) + - should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name) + = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name' = render "events/event_scope", event: event diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml new file mode 100644 index 00000000000..e85b0713230 --- /dev/null +++ b/app/views/groups/registry/repositories/index.html.haml @@ -0,0 +1,12 @@ +- page_title _("Container Registry") + +%section + .row.registry-placeholder.prepend-bottom-10 + .col-12 + #js-vue-registry-images{ data: { endpoint: group_container_registries_path(@group, format: :json), + "help_page_path" => help_page_path('user/packages/container_registry/index'), + "no_containers_image" => image_path('illustrations/docker-empty-state.svg'), + "containers_error_image" => image_path('illustrations/docker-error-state.svg'), + "repository_url" => "", + is_group_page: true, + character_error: @character_error.to_s } } diff --git a/app/views/groups/sidebar/_packages.html.haml b/app/views/groups/sidebar/_packages.html.haml new file mode 100644 index 00000000000..16b902a18b9 --- /dev/null +++ b/app/views/groups/sidebar/_packages.html.haml @@ -0,0 +1,16 @@ +- if group_container_registry_nav? + = nav_link(path: group_packages_nav_link_paths) do + = link_to group_container_registries_path(@group), title: _('Container Registry') do + .nav-icon-container + = sprite_icon('package') + %span.nav-item-name + = _('Packages') + %ul.sidebar-sub-level-items + = nav_link(controller: [:packages, :repositories], html_options: { class: "fly-out-top-item" } ) do + = link_to group_container_registries_path(@group), title: _('Container Registry') do + %strong.fly-out-top-item-name + = _('Packages') + %li.divider.fly-out-top-item + = nav_link(controller: 'groups/container_registries') do + = link_to group_container_registries_path(@group), title: _('Container Registry') do + %span= _('Container Registry') diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 7cc7d1783c4..4930c6cf5f7 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -118,7 +118,7 @@ %strong.fly-out-top-item-name = _('Kubernetes') - = render_if_exists 'groups/sidebar/packages' # EE-specific + = render_if_exists 'groups/sidebar/packages' - if group_sidebar_link?(:group_members) = nav_link(path: 'group_members#index') do diff --git a/app/views/shared/boards/_switcher.html.haml b/app/views/shared/boards/_switcher.html.haml index 79118630762..09a365a290a 100644 --- a/app/views/shared/boards/_switcher.html.haml +++ b/app/views/shared/boards/_switcher.html.haml @@ -1,4 +1,4 @@ -- parent = board.parent +- parent = board.resource_parent - milestone_filter_opts = { format: :json } - milestone_filter_opts = milestone_filter_opts.merge(only_group_milestones: true) if board.group_board? - weights = Gitlab.ee? ? ([Issue::WEIGHT_ANY] + Issue.weight_options) : [] 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 416b4a34651..ae0e5e45afe 100644 --- a/app/views/shared/issuable/_board_create_list_dropdown.html.haml +++ b/app/views/shared/issuable/_board_create_list_dropdown.html.haml @@ -3,6 +3,6 @@ Add list .dropdown-menu.dropdown-extended-height.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" } - - if can?(current_user, :admin_label, board.parent) + - if can?(current_user, :admin_label, board.resource_parent) = render partial: "shared/issuable/label_page_create", locals: { show_add_list: true, add_list: true, add_list_class: 'd-none' } = dropdown_loading diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index c9458475aa5..9165147ef2a 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -2,7 +2,7 @@ - board = local_assigns.fetch(:board, nil) - is_not_boards_modal_or_productivity_analytics = type != :boards_modal && type != :productivity_analytics - block_css_class = is_not_boards_modal_or_productivity_analytics ? 'row-content-block second-block' : '' -- user_can_admin_list = board && can?(current_user, :admin_list, board.parent) +- user_can_admin_list = board && can?(current_user, :admin_list, board.resource_parent) .issues-filters{ class: ("w-100" if type == :boards_modal) } .issues-details-filters.filtered-search-block.d-flex.flex-column.flex-md-row{ class: block_css_class, "v-pre" => type == :boards_modal } |