diff options
author | Phil Hughes <me@iamphill.com> | 2017-08-30 16:49:22 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-08-30 16:49:22 +0100 |
commit | 4d2d744ae9a2d4c3bb0f00805c27704b79e00d72 (patch) | |
tree | 839b1c3ab4c09c80f7d643fe69dae8a83c4e4eb4 /app | |
parent | 25a3b7fab905d09f6f064108f457a4e20c8915ff (diff) | |
parent | f7c8434c7100c3c87eb2a75cd5a128e520d8c110 (diff) | |
download | gitlab-ce-4d2d744ae9a2d4c3bb0f00805c27704b79e00d72.tar.gz |
Merge branch 'master' into enable-new-navigaton-by-defaultenable-new-navigaton-by-default
Diffstat (limited to 'app')
62 files changed, 385 insertions, 113 deletions
diff --git a/app/assets/javascripts/droplab/drop_down.js b/app/assets/javascripts/droplab/drop_down.js index 70cd337fb8a..3901bb177fe 100644 --- a/app/assets/javascripts/droplab/drop_down.js +++ b/app/assets/javascripts/droplab/drop_down.js @@ -85,6 +85,13 @@ class DropDown { const renderableList = this.list.querySelector('ul[data-dynamic]') || this.list; renderableList.innerHTML = children.join(''); + + const listEvent = new CustomEvent('render.dl', { + detail: { + list: this, + }, + }); + this.list.dispatchEvent(listEvent); } renderChildren(data) { diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js new file mode 100644 index 00000000000..f9bbbf0cbc1 --- /dev/null +++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js @@ -0,0 +1,82 @@ +/* global Flash */ + +import Ajax from '~/droplab/plugins/ajax'; +import Filter from '~/droplab/plugins/filter'; +import './filtered_search_dropdown'; + +class DropdownEmoji extends gl.FilteredSearchDropdown { + constructor(options = {}) { + super(options); + this.config = { + Ajax: { + endpoint: `${gon.relative_url_root || ''}/autocomplete/award_emojis`, + method: 'setData', + loadingTemplate: this.loadingTemplate, + onError() { + /* eslint-disable no-new */ + new Flash('An error occured fetching the dropdown data.'); + /* eslint-enable no-new */ + }, + }, + Filter: { + template: 'name', + }, + }; + + import(/* webpackChunkName: 'emoji' */ '~/emoji') + .then(({ glEmojiTag }) => { this.glEmojiTag = glEmojiTag; }) + .catch(() => { /* ignore error and leave emoji name in the search bar */ }); + + this.unbindEvents(); + this.bindEvents(); + } + + bindEvents() { + super.bindEvents(); + + this.listRenderedWrapper = this.listRendered.bind(this); + this.dropdown.addEventListener('render.dl', this.listRenderedWrapper); + } + + unbindEvents() { + this.dropdown.removeEventListener('render.dl', this.listRenderedWrapper); + super.unbindEvents(); + } + + listRendered() { + this.replaceEmojiElement(); + } + + itemClicked(e) { + super.itemClicked(e, (selected) => { + const name = selected.querySelector('.js-data-value').innerText.trim(); + return gl.DropdownUtils.getEscapedText(name); + }); + } + + renderContent(forceShowList = false) { + this.droplab.changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config); + super.renderContent(forceShowList); + } + + replaceEmojiElement() { + if (!this.glEmojiTag) return; + + // Replace empty gl-emoji tag to real content + const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')]; + dropdownItems.forEach((dropdownItem) => { + const name = dropdownItem.querySelector('.js-data-value').innerText; + const emojiTag = this.glEmojiTag(name); + const emojiElement = dropdownItem.querySelector('gl-emoji'); + emojiElement.outerHTML = emojiTag; + }); + } + + init() { + this.droplab + .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); + } +} + +window.gl = window.gl || {}; +gl.DropdownEmoji = DropdownEmoji; diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index a81389ab088..1c5ca1d3cf9 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -61,7 +61,7 @@ class DropdownHint extends gl.FilteredSearchDropdown { .map(tokenKey => ({ icon: `fa-${tokenKey.icon}`, hint: tokenKey.key, - tag: `<${tokenKey.symbol}${tokenKey.key}>`, + tag: `<${tokenKey.tag}>`, type: tokenKey.type, })); diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js index 132b6fe698a..6d5dd747224 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js +++ b/app/assets/javascripts/filtered_search/filtered_search_bundle.js @@ -1,3 +1,4 @@ +import './dropdown_emoji'; import './dropdown_hint'; import './dropdown_non_user'; import './dropdown_user'; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index dd1c067df87..46c80dfd45e 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -58,6 +58,11 @@ class FilteredSearchDropdownManager { }, element: this.container.querySelector('#js-dropdown-label'), }, + 'my-reaction': { + reference: null, + gl: 'DropdownEmoji', + element: this.container.querySelector('#js-dropdown-my-reaction'), + }, hint: { reference: null, gl: 'DropdownHint', diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index a31be2b0bc7..038239bf466 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -439,8 +439,13 @@ class FilteredSearchManager { const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam); if (match) { - const indexOf = keyParam.indexOf('_'); - const sanitizedKey = indexOf !== -1 ? keyParam.slice(0, keyParam.indexOf('_')) : keyParam; + // Use lastIndexOf because the token key is allowed to contain underscore + // e.g. 'my_reaction' is the token key of 'my_reaction_emoji' + const lastIndexOf = keyParam.lastIndexOf('_'); + let sanitizedKey = lastIndexOf !== -1 ? keyParam.slice(0, lastIndexOf) : keyParam; + // Replace underscore with hyphen in the sanitizedkey. + // e.g. 'my_reaction' => 'my-reaction' + sanitizedKey = sanitizedKey.replace('_', '-'); const symbol = match.symbol; let quotationsToUse = ''; @@ -515,7 +520,10 @@ class FilteredSearchManager { const condition = this.filteredSearchTokenKeys .searchByConditionKeyValue(token.key, token.value.toLowerCase()); const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {}; - const keyParam = param ? `${token.key}_${param}` : token.key; + // Replace hyphen with underscore to use as request parameter + // e.g. 'my-reaction' => 'my_reaction' + const underscoredKey = token.key.replace('-', '_'); + const keyParam = param ? `${underscoredKey}_${param}` : underscoredKey; let tokenPath = ''; if (condition) { diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js index 025d4d8795b..be595d7df1a 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js @@ -4,26 +4,42 @@ const tokenKeys = [{ param: 'username', symbol: '@', icon: 'pencil', + tag: '@author', }, { key: 'assignee', type: 'string', param: 'username', symbol: '@', icon: 'user', + tag: '@assignee', }, { key: 'milestone', type: 'string', param: 'title', symbol: '%', icon: 'clock-o', + tag: '%milestone', }, { key: 'label', type: 'array', param: 'name[]', symbol: '~', icon: 'tag', + tag: '~label', }]; +if (gon.current_user_id) { + // Appending tokenkeys only logged-in + tokenKeys.push({ + key: 'my-reaction', + type: 'string', + param: 'emoji', + symbol: '', + icon: 'thumbs-up', + tag: 'emoji', + }); +} + const alternativeTokenKeys = [{ key: 'label', type: 'string', @@ -84,6 +100,10 @@ class FilteredSearchTokenKeys { return tokenKeysWithAlternative.find((tokenKey) => { let tokenKeyParam = tokenKey.key; + // Replace hyphen with underscore to compare keyParam with tokenKeyParam + // e.g. 'my-reaction' => 'my_reaction' + tokenKeyParam = tokenKeyParam.replace('-', '_'); + if (tokenKey.param) { tokenKeyParam += `_${tokenKey.param}`; } diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index 243ee4d723a..28e8240169d 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -132,6 +132,23 @@ class FilteredSearchVisualTokens { .catch(() => { }); } + static updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) { + const container = tokenValueContainer; + const element = tokenValueElement; + + return import(/* webpackChunkName: 'emoji' */ '../emoji') + .then((Emoji) => { + if (!Emoji.isEmojiNameValid(tokenValue)) { + return; + } + + container.dataset.originalValue = tokenValue; + element.innerHTML = Emoji.glEmojiTag(tokenValue); + }) + // ignore error and leave emoji name in the search bar + .catch(() => { }); + } + static renderVisualTokenValue(parentElement, tokenName, tokenValue) { const tokenValueContainer = parentElement.querySelector('.value-container'); const tokenValueElement = tokenValueContainer.querySelector('.value'); @@ -144,6 +161,10 @@ class FilteredSearchVisualTokens { FilteredSearchVisualTokens.updateUserTokenAppearance( tokenValueContainer, tokenValueElement, tokenValue, ); + } else if (tokenType === 'my-reaction') { + FilteredSearchVisualTokens.updateEmojiTokenAppearance( + tokenValueContainer, tokenValueElement, tokenValue, + ); } } diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index cb133cf7535..2060410e991 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -1,10 +1,10 @@ <script> +import identicon from '../../vue_shared/components/identicon.vue'; import eventHub from '../event_hub'; -import groupIdenticon from './group_identicon.vue'; export default { components: { - groupIdenticon, + identicon, }, props: { group: { @@ -205,7 +205,7 @@ export default { class="avatar s40" :src="group.avatarUrl" /> - <group-identicon + <identicon v-else :entity-id=group.id :entity-name="group.name" diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js index ff2b66046b4..283c0ec0410 100644 --- a/app/assets/javascripts/lib/utils/sticky.js +++ b/app/assets/javascripts/lib/utils/sticky.js @@ -1,5 +1,5 @@ export const isSticky = (el, scrollY, stickyTop) => { - const top = el.offsetTop - scrollY; + const top = Math.floor(el.offsetTop - scrollY); if (top <= stickyTop) { el.classList.add('is-stuck'); diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 5a9b3d19f84..3b3620fe61b 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -253,6 +253,7 @@ import bp from './breakpoints'; loadDiff(source) { if (this.diffsLoaded) { + document.dispatchEvent(new CustomEvent('scroll')); return; } diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js index 2d1ed9e4076..b18d12b48b5 100644 --- a/app/assets/javascripts/new_sidebar.js +++ b/app/assets/javascripts/new_sidebar.js @@ -47,7 +47,6 @@ export default class NewNavSidebar { if (this.$sidebar.length) { this.$sidebar.toggleClass('sidebar-icons-only', collapsed); - this.$page.toggleClass('page-with-new-sidebar', !collapsed); this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed); } NewNavSidebar.setCollapsedCookie(collapsed); diff --git a/app/assets/javascripts/pipelines/components/navigation_tabs.vue b/app/assets/javascripts/pipelines/components/navigation_tabs.vue index d2f6d47f043..73f7e3a0cad 100644 --- a/app/assets/javascripts/pipelines/components/navigation_tabs.vue +++ b/app/assets/javascripts/pipelines/components/navigation_tabs.vue @@ -1,23 +1,29 @@ <script> -export default { - name: 'PipelineNavigationTabs', - props: { - scope: { - type: String, - required: true, + export default { + name: 'PipelineNavigationTabs', + props: { + scope: { + type: String, + required: true, + }, + count: { + type: Object, + required: true, + }, + paths: { + type: Object, + required: true, + }, }, - count: { - type: Object, - required: true, + mounted() { + $(document).trigger('init.scrolling-tabs'); }, - paths: { - type: Object, - required: true, + methods: { + shouldRenderBadge(count) { + // 0 is valid in a badge, but evaluates to false, we need to check for undefined + return count !== undefined; + }, }, - }, - mounted() { - $(document).trigger('init.scrolling-tabs'); - }, }; </script> <template> @@ -27,7 +33,9 @@ export default { :class="{ active: scope === 'all'}"> <a :href="paths.allPath"> All - <span class="badge js-totalbuilds-count"> + <span + v-if="shouldRenderBadge(count.all)" + class="badge js-totalbuilds-count"> {{count.all}} </span> </a> @@ -37,7 +45,9 @@ export default { :class="{ active: scope === 'pending'}"> <a :href="paths.pendingPath"> Pending - <span class="badge"> + <span + v-if="shouldRenderBadge(count.pending)" + class="badge"> {{count.pending}} </span> </a> @@ -47,7 +57,9 @@ export default { :class="{ active: scope === 'running'}"> <a :href="paths.runningPath"> Running - <span class="badge"> + <span + v-if="shouldRenderBadge(count.running)" + class="badge"> {{count.running}} </span> </a> @@ -57,7 +69,9 @@ export default { :class="{ active: scope === 'finished'}"> <a :href="paths.finishedPath"> Finished - <span class="badge"> + <span + v-if="shouldRenderBadge(count.finished)" + class="badge"> {{count.finished}} </span> </a> diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index 5df317a76bf..010063a0240 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -139,7 +139,9 @@ }; </script> <template> - <div :class="cssClass"> + <div + class="pipelines-container" + :class="cssClass"> <div class="top-area scrolling-tabs-container inner-page-scroll-tabs" v-if="!isLoading && !shouldRenderEmptyState"> diff --git a/app/assets/javascripts/groups/components/group_identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue index 0edd820743f..0edd820743f 100644 --- a/app/assets/javascripts/groups/components/group_identicon.vue +++ b/app/assets/javascripts/vue_shared/components/identicon.vue diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a45d5a6dca0..4cf2f46c6d3 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -771,6 +771,11 @@ &::before { top: 16px; } + + &.dropdown-menu-user-link::before { + top: 50%; + transform: translateY(-50%); + } } } } diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index a5d33d410fb..b2847c348eb 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -225,6 +225,18 @@ color: $common-gray-dark; } + gl-emoji { + display: inline-block; + font-family: inherit; + font-size: inherit; + vertical-align: inherit; + + img { + height: 18px; + width: 18px; + } + } + .form-control { position: relative; min-width: 200px; @@ -277,7 +289,7 @@ } .filtered-search-input-dropdown-menu { - max-height: 225px; + max-height: 260px; max-width: 280px; overflow: auto; @@ -478,3 +490,7 @@ padding: 8px 16px; text-align: center; } + +.issues-details-filters { + @include new-style-dropdown; +} diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 9fccc68b5b6..8018eb8ba84 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -268,6 +268,7 @@ // TODO: change global style .ajax-project-dropdown, body[data-page="projects:blob:new"] #select2-drop, +body[data-page="profiles:show"] #select2-drop, body[data-page="projects:blob:edit"] #select2-drop { &.select2-drop { color: $gl-text-color; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 71eec0e1a5e..3c0b4c82d19 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -10,8 +10,7 @@ color: $md-link-color; } - img { - /*max-width: 100%;*/ + img:not(.emoji) { margin: 0 0 8px; } @@ -26,6 +25,7 @@ min-width: inherit; min-height: inherit; background-color: inherit; + max-width: 100%; } p a:not(.no-attachment-icon) img { diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index f6b8c8ee2bc..d3cd4d507de 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -204,6 +204,8 @@ .gitlab-ci-yml-selector, .dockerfile-selector, .template-type-selector { + @include new-style-dropdown; + display: inline-block; vertical-align: top; font-family: $regular_font; diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss index bdf07a99daf..c28b1e68008 100644 --- a/app/assets/stylesheets/pages/notifications.scss +++ b/app/assets/stylesheets/pages/notifications.scss @@ -14,3 +14,7 @@ font-size: 18px; } } + +.notification-form { + @include new-style-dropdown; +} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index a408bde37d6..51656669c98 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -927,3 +927,7 @@ button.mini-pipeline-graph-dropdown-toggle { } } } + +.pipelines-container .top-area .nav-controls > .btn:last-child { + float: none; +} diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 3120916c5bb..59be955599d 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -1,5 +1,7 @@ class AutocompleteController < ApplicationController - skip_before_action :authenticate_user!, only: [:users] + AWARD_EMOJI_MAX = 100 + + skip_before_action :authenticate_user!, only: [:users, :award_emojis] before_action :load_project, only: [:users] before_action :find_users, only: [:users] @@ -48,6 +50,20 @@ class AutocompleteController < ApplicationController render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace) end + def award_emojis + emoji_with_count = AwardEmoji + .limit(AWARD_EMOJI_MAX) + .where(user: current_user) + .group(:name) + .order('count_all DESC, name ASC') + .count + + # Transform from hash to array to guarantee json order + # e.g. { 'thumbsup' => 2, 'thumbsdown' = 1 } + # => [{ name: 'thumbsup' }, { name: 'thumbsdown' }] + render json: emoji_with_count.map { |k, v| { name: k } } + end + private def find_users diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index b43b2c5621f..a34a82b7ba6 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -15,7 +15,17 @@ module IssuableCollections end def merge_requests_collection - merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :head_pipeline, target_project: :namespace, merge_request_diff: :merge_request_diff_commits) + merge_requests_finder.execute.preload( + :source_project, + :target_project, + :author, + :assignee, + :labels, + :milestone, + head_pipeline: :project, + target_project: :namespace, + merge_request_diff: :merge_request_diff_commits + ) end def issues_finder diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 5c10d7bc261..7a7bcb1a3d2 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -35,13 +35,13 @@ class Groups::MilestonesController < Groups::ApplicationController end def edit - render_404 if @milestone.is_legacy_group_milestone? + render_404 if @milestone.legacy_group_milestone? end def update # Keep this compatible with legacy group milestones where we have to update # all projects milestones states at once. - if @milestone.is_legacy_group_milestone? + if @milestone.legacy_group_milestone? update_params = milestone_params.select { |key| key == "state_event" } milestones = @milestone.milestones else @@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController end def milestone_path - if @milestone.is_legacy_group_milestone? + if @milestone.legacy_group_milestone? group_milestone_path(group, @milestone.safe_title, title: @milestone.title) else group_milestone_path(group, @milestone.iid) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 8893a514207..1afaceac567 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -202,7 +202,7 @@ class Projects::IssuesController < Projects::ApplicationController task_status: @issue.task_status } - if @issue.is_edited? + if @issue.edited? response[:updated_at] = @issue.updated_at response[:updated_by_name] = @issue.last_edited_by.name response[:updated_by_path] = user_path(@issue.last_edited_by) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 08a843ada97..7e0d3b5c979 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -18,6 +18,7 @@ # sort: string # non_archived: boolean # iids: integer[] +# my_reaction_emoji: string # class IssuableFinder include CreatedAtFilter @@ -46,6 +47,7 @@ class IssuableFinder items = by_iids(items) items = by_milestone(items) items = by_label(items) + items = by_my_reaction_emoji(items) # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far items = by_project(items) @@ -371,6 +373,14 @@ class IssuableFinder items end + def by_my_reaction_emoji(items) + if params[:my_reaction_emoji].present? && current_user + items = items.awarded(current_user, params[:my_reaction_emoji]) + end + + items + end + def by_due_date(items) if due_date? if filter_by_no_due_date? diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index def71587b81..91655c69903 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -178,7 +178,7 @@ module ApplicationHelper end def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false) - return unless object.is_edited? + return unless object.edited? content_tag :small, class: 'edited-text' do output = content_tag(:span, 'Edited ') diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 197c90c4081..2a748ce0a75 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -229,7 +229,7 @@ module IssuablesHelper end def updated_at_by(issuable) - return {} unless issuable.is_edited? + return {} unless issuable.edited? { updatedAt: issuable.updated_at.to_time.iso8601, diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 86666022a2a..446a59030a6 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -164,7 +164,7 @@ module MilestonesHelper def group_milestone_route(milestone, params = {}) params = nil if params.empty? - if milestone.is_legacy_group_milestone? + if milestone.legacy_group_milestone? group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params) else group_milestone_path(@group, milestone.iid, milestone: params) diff --git a/app/helpers/milestones_routing_helper.rb b/app/helpers/milestones_routing_helper.rb index 766d5262018..a0b2616f224 100644 --- a/app/helpers/milestones_routing_helper.rb +++ b/app/helpers/milestones_routing_helper.rb @@ -1,16 +1,16 @@ module MilestonesRoutingHelper def milestone_path(milestone, *args) - if milestone.is_group_milestone? + if milestone.group_milestone? group_milestone_path(milestone.group, milestone, *args) - elsif milestone.is_project_milestone? + elsif milestone.project_milestone? project_milestone_path(milestone.project, milestone, *args) end end def milestone_url(milestone, *args) - if milestone.is_group_milestone? + if milestone.group_milestone? group_milestone_url(milestone.group, milestone, *args) - elsif milestone.is_project_milestone? + elsif milestone.project_milestone? project_milestone_url(milestone.project, milestone, *args) end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index c6d23898560..906a76ec560 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -142,7 +142,7 @@ module Ci expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false) end - def is_runner_queue_value_latest?(value) + def runner_queue_value_latest?(value) ensure_runner_queue_value == value if value.present? end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index f4f9b037957..9adc309a22b 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -11,6 +11,21 @@ module Awardable end module ClassMethods + def awarded(user, name) + sql = <<~EOL + EXISTS ( + SELECT TRUE + FROM award_emoji + WHERE user_id = :user_id AND + name = :name AND + awardable_type = :awardable_type AND + awardable_id = #{self.arel_table.name}.id + ) + EOL + + where(sql, user_id: user.id, name: name, awardable_type: self.name) + end + def order_upvotes_desc order_votes_desc(AwardEmoji::UPVOTE_NAME) end diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb index 28623d257a6..c0a3099f676 100644 --- a/app/models/concerns/editable.rb +++ b/app/models/concerns/editable.rb @@ -1,7 +1,7 @@ module Editable extend ActiveSupport::Concern - def is_edited? + def edited? last_edited_at.present? && last_edited_at != created_at end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index f0998465822..710fc1ed647 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -70,19 +70,19 @@ module Milestoneish due_date && due_date.past? end - def is_group_milestone? + def group_milestone? false end - def is_project_milestone? + def project_milestone? false end - def is_legacy_group_milestone? + def legacy_group_milestone? false end - def is_dashboard_milestone? + def dashboard_milestone? false end diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb index fac7c5e5c85..86eb4ec76fc 100644 --- a/app/models/dashboard_milestone.rb +++ b/app/models/dashboard_milestone.rb @@ -3,7 +3,7 @@ class DashboardMilestone < GlobalMilestone { authorized_only: true } end - def is_dashboard_milestone? + def dashboard_milestone? true end end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 056c49e7162..7bcded5b5e1 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -49,7 +49,7 @@ class Deployment < ActiveRecord::Base # created before then could have a `sha` referring to a commit that no # longer exists in the repository, so just ignore those. begin - project.repository.is_ancestor?(commit.id, sha) + project.repository.ancestor?(commit.id, sha) rescue Rugged::OdbError false end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 65249bd7bfc..98135ee3c8b 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -17,7 +17,7 @@ class GroupMilestone < GlobalMilestone { group_id: group.id } end - def is_legacy_group_milestone? + def legacy_group_milestone? true end end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 01e0d0155a3..a3070a12b7c 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -163,7 +163,7 @@ class Milestone < ActiveRecord::Base # Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1" # def to_reference(from_project = nil, format: :iid, full: false) - return if is_group_milestone? && format != :name + return if group_milestone? && format != :name format_reference = milestone_format_reference(format) reference = "#{self.class.reference_prefix}#{format_reference}" @@ -207,11 +207,11 @@ class Milestone < ActiveRecord::Base group || project end - def is_group_milestone? + def group_milestone? group_id.present? end - def is_project_milestone? + def project_milestone? project_id.present? end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 0e5acb22d50..3845e485413 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -152,14 +152,14 @@ module Network end def find_free_parent_space(range, space_base, space_step, space_default) - if is_overlap?(range, space_default) + if overlap?(range, space_default) find_free_space(range, space_step, space_base, space_default) else space_default end end - def is_overlap?(range, overlap_space) + def overlap?(range, overlap_space) range.each do |i| if i != range.first && i != range.last && diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index 7b15a5dd04d..818cfb01b14 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -101,9 +101,9 @@ class ChatNotificationService < Service when "push", "tag_push" ChatMessage::PushMessage.new(data) when "issue" - ChatMessage::IssueMessage.new(data) unless is_update?(data) + ChatMessage::IssueMessage.new(data) unless update?(data) when "merge_request" - ChatMessage::MergeMessage.new(data) unless is_update?(data) + ChatMessage::MergeMessage.new(data) unless update?(data) when "note" ChatMessage::NoteMessage.new(data) when "pipeline" @@ -136,7 +136,7 @@ class ChatNotificationService < Service project.web_url end - def is_update?(data) + def update?(data) data[:object_attributes][:action] == 'update' end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index f422e0ea036..976d85246a8 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -85,9 +85,9 @@ class HipchatService < Service when "push", "tag_push" create_push_message(data) when "issue" - create_issue_message(data) unless is_update?(data) + create_issue_message(data) unless update?(data) when "merge_request" - create_merge_request_message(data) unless is_update?(data) + create_merge_request_message(data) unless update?(data) when "note" create_note_message(data) when "pipeline" @@ -282,7 +282,7 @@ class HipchatService < Service "<a href=\"#{project_url}\">#{project_name}</a>" end - def is_update?(data) + def update?(data) data[:object_attributes][:action] == 'update' end diff --git a/app/models/repository.rb b/app/models/repository.rb index 9fb2e2aa306..090e3c896bd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -60,6 +60,10 @@ class Repository @project = project end + def ==(other) + @disk_path == other.disk_path + end + def raw_repository return nil unless full_path @@ -75,6 +79,10 @@ class Repository ) end + def inspect + "#<#{self.class.name}:#{@disk_path}>" + end + # # Git repository can contains some hidden refs like: # /refs/notes/* @@ -944,7 +952,7 @@ class Repository if branch_commit same_head = branch_commit.id == root_ref_commit.id - !same_head && is_ancestor?(branch_commit.id, root_ref_commit.id) + !same_head && ancestor?(branch_commit.id, root_ref_commit.id) else nil end @@ -958,12 +966,12 @@ class Repository nil end - def is_ancestor?(ancestor_id, descendant_id) + def ancestor?(ancestor_id, descendant_id) return false if ancestor_id.nil? || descendant_id.nil? Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| if is_enabled - raw_repository.is_ancestor?(ancestor_id, descendant_id) + raw_repository.ancestor?(ancestor_id, descendant_id) else rugged_is_ancestor?(ancestor_id, descendant_id) end @@ -992,25 +1000,22 @@ class Repository end def with_repo_branch_commit(start_repository, start_branch_name) - return yield(nil) if start_repository.empty_repo? + return yield nil if start_repository.empty_repo? - branch_name_or_sha = - if start_repository == self - start_branch_name - else - tmp_ref = fetch_ref( - start_repository.path_to_repo, - "#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}", - "refs/tmp/#{SecureRandom.hex}/head" - ) + if start_repository == self + yield commit(start_branch_name) + else + sha = start_repository.commit(start_branch_name).sha - start_repository.commit(start_branch_name).sha + if branch_commit = commit(sha) + yield branch_commit + else + with_repo_tmp_commit( + start_repository, start_branch_name, sha) do |tmp_commit| + yield tmp_commit + end end - - yield(commit(branch_name_or_sha)) - - ensure - rugged.references.delete(tmp_ref) if tmp_ref + end end def add_remote(name, url) @@ -1219,4 +1224,16 @@ class Repository .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset) .map { |c| commit(c) } end + + def with_repo_tmp_commit(start_repository, start_branch_name, sha) + tmp_ref = fetch_ref( + start_repository.path_to_repo, + "#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}", + "refs/tmp/#{SecureRandom.hex}/head" + ) + + yield commit(sha) + ensure + rugged.references.delete(tmp_ref) if tmp_ref + end end diff --git a/app/models/user.rb b/app/models/user.rb index fbd08bc4d0a..70787de4b40 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ActiveRecord::Base include Gitlab::ConfigHelper include Gitlab::CurrentSettings + include Gitlab::SQL::Pattern include Avatarable include Referable include Sortable @@ -303,7 +304,7 @@ class User < ActiveRecord::Base # Returns an ActiveRecord::Relation. def search(query) table = arel_table - pattern = "%#{query}%" + pattern = User.to_pattern(query) order = <<~SQL CASE diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 0133091db57..a925fac7d3e 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -17,13 +17,13 @@ class ProjectPolicy < BasePolicy desc "Project has public builds enabled" condition(:public_builds, scope: :subject) { project.public_builds? } - # For guest access we use #is_team_member? so we can use + # For guest access we use #team_member? so we can use # project.members, which gets cached in subject scope. # This is safe because team_access_level is guaranteed # by ProjectAuthorization's validation to be at minimum # GUEST desc "User has guest access" - condition(:guest) { is_team_member? } + condition(:guest) { team_member? } desc "User has reporter access" condition(:reporter) { team_access_level >= Gitlab::Access::REPORTER } @@ -293,7 +293,7 @@ class ProjectPolicy < BasePolicy private - def is_team_member? + def team_member? return false if @user.nil? greedy_load_subject = false diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb index 59153cbbc0a..7b5482b3cd1 100644 --- a/app/services/akismet_service.rb +++ b/app/services/akismet_service.rb @@ -7,7 +7,7 @@ class AkismetService @options = options end - def is_spam? + def spam? return false unless akismet_enabled? params = { diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index e81a56672e2..bb61136e33b 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -30,7 +30,7 @@ class GitPushService < BaseService @project.repository.after_create_branch # Re-find the pushed commits. - if is_default_branch? + if default_branch? # Initial push to the default branch. Take the full history of that branch as "newly pushed". process_default_branch else @@ -50,7 +50,7 @@ class GitPushService < BaseService # Update the bare repositories info/attributes file using the contents of the default branches # .gitattributes file - update_gitattributes if is_default_branch? + update_gitattributes if default_branch? end execute_related_hooks @@ -66,7 +66,7 @@ class GitPushService < BaseService end def update_caches - if is_default_branch? + if default_branch? if push_to_new_branch? # If this is the initial push into the default branch, the file type caches # will already be reset as a result of `Project#change_head`. @@ -108,7 +108,7 @@ class GitPushService < BaseService # Schedules processing of commit messages. def process_commit_messages - default = is_default_branch? + default = default_branch? @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit| if commit.matches_cross_reference_regex? @@ -202,7 +202,7 @@ class GitPushService < BaseService Gitlab::Git.branch_ref?(params[:ref]) end - def is_default_branch? + def default_branch? Gitlab::Git.branch_ref?(params[:ref]) && (Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?) end diff --git a/app/services/milestones/close_service.rb b/app/services/milestones/close_service.rb index 776ec4b287b..5b06c4b601d 100644 --- a/app/services/milestones/close_service.rb +++ b/app/services/milestones/close_service.rb @@ -1,7 +1,7 @@ module Milestones class CloseService < Milestones::BaseService def execute(milestone) - if milestone.close && milestone.is_project_milestone? + if milestone.close && milestone.project_milestone? event_service.close_milestone(milestone, current_user) end diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb index aef3124c7e3..ed2e833d833 100644 --- a/app/services/milestones/create_service.rb +++ b/app/services/milestones/create_service.rb @@ -3,7 +3,7 @@ module Milestones def execute milestone = parent.milestones.new(params) - if milestone.save && milestone.is_project_milestone? + if milestone.save && milestone.project_milestone? event_service.open_milestone(milestone, current_user) end diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb index 600ebcfbecb..b18651476a8 100644 --- a/app/services/milestones/destroy_service.rb +++ b/app/services/milestones/destroy_service.rb @@ -1,7 +1,7 @@ module Milestones class DestroyService < Milestones::BaseService def execute(milestone) - return unless milestone.is_project_milestone? + return unless milestone.project_milestone? Milestone.transaction do update_params = { milestone: nil } diff --git a/app/services/milestones/reopen_service.rb b/app/services/milestones/reopen_service.rb index 5b8b682caaf..3efb33157c5 100644 --- a/app/services/milestones/reopen_service.rb +++ b/app/services/milestones/reopen_service.rb @@ -1,7 +1,7 @@ module Milestones class ReopenService < Milestones::BaseService def execute(milestone) - if milestone.activate && milestone.is_project_milestone? + if milestone.activate && milestone.project_milestone? event_service.reopen_milestone(milestone, current_user) end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index a0cd52014a2..71533da31b1 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -44,6 +44,8 @@ module Projects @project.namespace_id = current_user.namespace_id end + yield(@project) if block_given? + @project.creator = current_user if forked_from_project_id diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb index 3e65b7d31a3..73ea3018fbd 100644 --- a/app/services/spam_service.rb +++ b/app/services/spam_service.rb @@ -45,7 +45,7 @@ class SpamService def check(api) return false unless request && check_for_spam? - return false unless akismet.is_spam? + return false unless akismet.spam? create_spam_log(api) true diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 1763f64a4e4..1f66a2668f9 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -142,7 +142,7 @@ module SystemNoteService # # Returns the created Note object def change_milestone(noteable, project, author, milestone) - format = milestone&.is_group_milestone? ? :name : :iid + format = milestone&.group_milestone? ? :name : :iid body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}" create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone')) diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 54b1b7a734a..23b1a22240f 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,4 +1,4 @@ = render "header_title" = render 'shared/milestones/top', milestone: @milestone, group: @group -= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.is_legacy_group_milestone? += render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.legacy_group_milestone? = render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index 4db84771f4e..653452871a0 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -1,8 +1,9 @@ - breadcrumb_link = breadcrumb_title_link +- container = @no_breadcrumb_container ? 'container-fluid' : container_class - hide_top_links = @hide_top_links || false %nav.breadcrumbs{ role: "navigation" } - .breadcrumbs-container{ class: [container_class, @content_class] } + .breadcrumbs-container{ class: [container, @content_class] } - if defined?(@new_sidebar) = button_tag class: 'toggle-mobile-nav', type: 'button' do %span.sr-only Open sidebar diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml index 720a97cddb7..8dbb8aef31b 100644 --- a/app/views/profiles/gpg_keys/index.html.haml +++ b/app/views/profiles/gpg_keys/index.html.haml @@ -12,7 +12,7 @@ Add a GPG key %p.profile-settings-content Before you can add a GPG key you need to - = link_to 'generate it.', help_page_path('user/project/gpg_signed_commits/index.md') + = link_to 'generate it.', help_page_path('user/project/repository/gpg_signed_commits/index.md') = render 'form' %hr %h5 diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index 2076e46fde8..5354ec8522e 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -1,3 +1,4 @@ +- @no_breadcrumb_container = true - @no_container = true - @content_class = "issue-boards-content" - page_title "Boards" diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml index a3783b31b86..d06b29db838 100644 --- a/app/views/projects/commit/_signature_badge.html.haml +++ b/app/views/projects/commit/_signature_badge.html.haml @@ -12,7 +12,7 @@ %span.monospace= signature.gpg_key_primary_keyid - = link_to('Learn more about signing commits', help_page_path('user/project/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link') + = link_to('Learn more about signing commits', help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link') %button{ class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'auto top', title: title, content: content } } = label diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index f63b9698408..e81789ea7a2 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -93,6 +93,13 @@ %span.dropdown-label-box{ style: 'background: {{color}}' } %span.label-title.js-data-value {{title}} + #js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.btn.btn-link + %gl-emoji + %span.js-data-value.prepend-left-10 + {{name}} %button.clear-search.hidden{ type: 'button' } = icon('times') .filter-dropdown-container diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 6a85f7d0564..305e2542281 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -5,7 +5,7 @@ .row .col-sm-6 %strong= link_to truncate(milestone.title, length: 100), milestone_path - - if milestone.is_group_milestone? + - if milestone.group_milestone? %span - Group Milestone - else %span - Project Milestone @@ -18,10 +18,10 @@ · = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path .col-sm-6= milestone_progress_bar(milestone) - - if milestone.is_a?(GlobalMilestone) || milestone.is_group_milestone? + - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? .row .col-sm-6 - - if milestone.is_legacy_group_milestone? + - if milestone.legacy_group_milestone? .expiration= render('shared/milestone_expired', milestone: milestone) .projects - milestone.milestones.each do |milestone| @@ -31,7 +31,7 @@ - if @group .col-sm-6.milestone-actions - if can?(current_user, :admin_milestones, @group) - - if milestone.is_group_milestone? + - if milestone.group_milestone? = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do Edit \ diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 3014300fbe7..fd0760d83a5 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -22,7 +22,7 @@ - if group .pull-right - if can?(current_user, :admin_milestones, group) - - if milestone.is_group_milestone? + - if milestone.group_milestone? = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do Edit - if milestone.active? @@ -33,7 +33,7 @@ .detail-page-description.milestone-detail %h2.title = markdown_field(milestone, :title) - - if @milestone.is_group_milestone? && @milestone.description.present? + - if @milestone.group_milestone? && @milestone.description.present? %div .description .wiki @@ -44,7 +44,7 @@ - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' %span All issues for this milestone are closed. #{close_msg} -- if @milestone.is_legacy_group_milestone? || @milestone.is_dashboard_milestone? +- if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone? .table-holder %table.table %thead @@ -67,7 +67,7 @@ Open %td = ms.expires_at -- elsif @milestone.is_group_milestone? +- elsif @milestone.group_milestone? %br View = link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title) |