diff options
Diffstat (limited to 'app')
13 files changed, 176 insertions, 85 deletions
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue index 0ece64692ae..9d898d1a1a1 100644 --- a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue +++ b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue @@ -58,7 +58,7 @@ export default { <template> <div class="frequent-items-list-container"> - <ul class="list-unstyled"> + <ul ref="frequentItemsList" class="list-unstyled"> <li v-if="isListEmpty" :class="{ 'section-failure': isFetchFailed }" class="section-empty"> {{ listEmptyMessage }} </li> diff --git a/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js index 432a9254558..98bcb8348e2 100644 --- a/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js +++ b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js @@ -117,3 +117,23 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => { return scaledFormatter(units); }; + +/** + * Returns a function that formats a number scaled using SI units notation. + */ +export const scaledBinaryFormatter = (unit = '', prefixOffset = 0) => { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + const multiplicative = ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + const symbols = ['', ...multiplicative]; + + const units = symbols.slice(prefixOffset).map(prefix => { + return `${prefix}${unit}`; + }); + + if (!units.length) { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + throw new RangeError('The unit cannot be converted, please try a different scale'); + } + + return scaledFormatter(units, 1024); +}; diff --git a/app/assets/javascripts/lib/utils/unit_format/index.js b/app/assets/javascripts/lib/utils/unit_format/index.js index daf70ebb5d7..d3aea37e677 100644 --- a/app/assets/javascripts/lib/utils/unit_format/index.js +++ b/app/assets/javascripts/lib/utils/unit_format/index.js @@ -1,9 +1,18 @@ import { s__ } from '~/locale'; -import { suffixFormatter, scaledSIFormatter, numberFormatter } from './formatter_factory'; +import { + suffixFormatter, + scaledSIFormatter, + scaledBinaryFormatter, + numberFormatter, +} from './formatter_factory'; /** * Supported formats + * + * Based on: + * + * https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier */ export const SUPPORTED_FORMATS = { // Number @@ -13,15 +22,23 @@ export const SUPPORTED_FORMATS = { // Duration seconds: 'seconds', - miliseconds: 'miliseconds', + milliseconds: 'milliseconds', - // Digital - bytes: 'bytes', + // Digital (Metric) + decimalBytes: 'decimalBytes', kilobytes: 'kilobytes', megabytes: 'megabytes', gigabytes: 'gigabytes', terabytes: 'terabytes', petabytes: 'petabytes', + + // Digital (IEC) + bytes: 'bytes', + kibibytes: 'kibibytes', + mebibytes: 'mebibytes', + gibibytes: 'gibibytes', + tebibytes: 'tebibytes', + pebibytes: 'pebibytes', }; /** @@ -32,6 +49,7 @@ export const SUPPORTED_FORMATS = { */ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { // Number + if (format === SUPPORTED_FORMATS.number) { /** * Formats a number @@ -70,6 +88,7 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { } // Durations + if (format === SUPPORTED_FORMATS.seconds) { /** * Formats a number of seconds @@ -82,9 +101,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { */ return suffixFormatter(s__('Units|s')); } - if (format === SUPPORTED_FORMATS.miliseconds) { + if (format === SUPPORTED_FORMATS.milliseconds) { /** - * Formats a number of miliseconds with ms as units + * Formats a number of milliseconds with ms as units * * @function * @param {Number} value - Number to format, `1` is formatted as `1ms` @@ -95,8 +114,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { return suffixFormatter(s__('Units|ms')); } - // Digital - if (format === SUPPORTED_FORMATS.bytes) { + // Digital (Metric) + + if (format === SUPPORTED_FORMATS.decimalBytes) { /** * Formats a number of bytes scaled up to larger digital * units for larger numbers. @@ -162,6 +182,76 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { */ return scaledSIFormatter('B', 5); } + + // Digital (IEC) + + if (format === SUPPORTED_FORMATS.bytes) { + /** + * Formats a number of bytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1B` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B'); + } + if (format === SUPPORTED_FORMATS.kibibytes) { + /** + * Formats a number of kilobytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1kB` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B', 1); + } + if (format === SUPPORTED_FORMATS.mebibytes) { + /** + * Formats a number of megabytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1MB` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B', 2); + } + if (format === SUPPORTED_FORMATS.gibibytes) { + /** + * Formats a number of gigabytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1GB` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B', 3); + } + if (format === SUPPORTED_FORMATS.tebibytes) { + /** + * Formats a number of terabytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1GB` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B', 4); + } + if (format === SUPPORTED_FORMATS.pebibytes) { + /** + * Formats a number of petabytes scaled up to larger digital + * units for larger numbers. + * + * @function + * @param {Number} value - Number to format, `1` is formatted as `1PB` + * @param {Number} fractionDigits - number of precision decimals + */ + return scaledBinaryFormatter('B', 5); + } + // Fail so client library addresses issue throw TypeError(`${format} is not a valid number format`); }; diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js deleted file mode 100644 index 4d3dbec435f..00000000000 --- a/app/assets/javascripts/notes/services/notes_service.js +++ /dev/null @@ -1,41 +0,0 @@ -import axios from '~/lib/utils/axios_utils'; -import * as constants from '../constants'; - -export default { - fetchDiscussions(endpoint, filter, persistFilter = true) { - const config = - filter !== undefined - ? { params: { notes_filter: filter, persist_filter: persistFilter } } - : null; - return axios.get(endpoint, config); - }, - replyToDiscussion(endpoint, data) { - return axios.post(endpoint, data); - }, - updateNote(endpoint, data) { - return axios.put(endpoint, data); - }, - createNewNote(endpoint, data) { - return axios.post(endpoint, data); - }, - toggleResolveNote(endpoint, isResolved) { - const { RESOLVE_NOTE_METHOD_NAME, UNRESOLVE_NOTE_METHOD_NAME } = constants; - const method = isResolved ? UNRESOLVE_NOTE_METHOD_NAME : RESOLVE_NOTE_METHOD_NAME; - - return axios[method](endpoint); - }, - poll(data = {}) { - const endpoint = data.notesData.notesPath; - const { lastFetchedAt } = data; - const options = { - headers: { - 'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined, - }, - }; - - return axios.get(endpoint, options); - }, - toggleIssueState(endpoint, data) { - return axios.put(endpoint, data); - }, -}; diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index 594e3a14d56..a4b9c64645c 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -8,7 +8,6 @@ import Poll from '../../lib/utils/poll'; import * as types from './mutation_types'; import * as utils from './utils'; import * as constants from '../constants'; -import service from '../services/notes_service'; import loadAwardsHandler from '../../awards_handler'; import sidebarTimeTrackingEventHub from '../../sidebar/event_hub'; import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils'; @@ -47,11 +46,17 @@ export const setNotesFetchedState = ({ commit }, state) => export const toggleDiscussion = ({ commit }, data) => commit(types.TOGGLE_DISCUSSION, data); -export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) => - service.fetchDiscussions(path, filter, persistFilter).then(({ data }) => { +export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) => { + const config = + filter !== undefined + ? { params: { notes_filter: filter, persist_filter: persistFilter } } + : null; + + return axios.get(path, config).then(({ data }) => { commit(types.SET_INITIAL_DISCUSSIONS, data); dispatch('updateResolvableDiscussionsCounts'); }); +}; export const updateDiscussion = ({ commit, state }, discussion) => { commit(types.UPDATE_DISCUSSION, discussion); @@ -78,7 +83,7 @@ export const deleteNote = ({ dispatch }, note) => }); export const updateNote = ({ commit, dispatch }, { endpoint, note }) => - service.updateNote(endpoint, note).then(({ data }) => { + axios.put(endpoint, note).then(({ data }) => { commit(types.UPDATE_NOTE, data); dispatch('startTaskList'); }); @@ -109,7 +114,7 @@ export const replyToDiscussion = ( { commit, state, getters, dispatch }, { endpoint, data: reply }, ) => - service.replyToDiscussion(endpoint, reply).then(({ data }) => { + axios.post(endpoint, reply).then(({ data }) => { if (data.discussion) { commit(types.UPDATE_DISCUSSION, data.discussion); @@ -126,7 +131,7 @@ export const replyToDiscussion = ( }); export const createNewNote = ({ commit, dispatch }, { endpoint, data: reply }) => - service.createNewNote(endpoint, reply).then(({ data }) => { + axios.post(endpoint, reply).then(({ data }) => { if (!data.errors) { commit(types.ADD_NEW_NOTE, data); @@ -156,20 +161,24 @@ export const resolveDiscussion = ({ state, dispatch, getters }, { discussionId } }); }; -export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) => - service.toggleResolveNote(endpoint, isResolved).then(({ data }) => { - const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE; +export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) => { + const method = isResolved + ? constants.UNRESOLVE_NOTE_METHOD_NAME + : constants.RESOLVE_NOTE_METHOD_NAME; + const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE; + return axios[method](endpoint).then(({ data }) => { commit(mutationType, data); dispatch('updateResolvableDiscussionsCounts'); dispatch('updateMergeRequestWidget'); }); +}; export const closeIssue = ({ commit, dispatch, state }) => { dispatch('toggleStateButtonLoading', true); - return service.toggleIssueState(state.notesData.closePath).then(({ data }) => { + return axios.put(state.notesData.closePath).then(({ data }) => { commit(types.CLOSE_ISSUE); dispatch('emitStateChangedEvent', data); dispatch('toggleStateButtonLoading', false); @@ -178,7 +187,7 @@ export const closeIssue = ({ commit, dispatch, state }) => { export const reopenIssue = ({ commit, dispatch, state }) => { dispatch('toggleStateButtonLoading', true); - return service.toggleIssueState(state.notesData.reopenPath).then(({ data }) => { + return axios.put(state.notesData.reopenPath).then(({ data }) => { commit(types.REOPEN_ISSUE); dispatch('emitStateChangedEvent', data); dispatch('toggleStateButtonLoading', false); @@ -355,11 +364,35 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => { return resp; }; +const getFetchDataParams = state => { + const endpoint = state.notesData.notesPath; + const options = { + headers: { + 'X-Last-Fetched-At': state.lastFetchedAt ? `${state.lastFetchedAt}` : undefined, + }, + }; + + return { endpoint, options }; +}; + +export const fetchData = ({ commit, state, getters }) => { + const { endpoint, options } = getFetchDataParams(state); + + axios + .get(endpoint, options) + .then(({ data }) => pollSuccessCallBack(data, commit, state, getters)) + .catch(() => Flash(__('Something went wrong while fetching latest comments.'))); +}; + export const poll = ({ commit, state, getters, dispatch }) => { eTagPoll = new Poll({ - resource: service, + resource: { + poll: () => { + const { endpoint, options } = getFetchDataParams(state); + return axios.get(endpoint, options); + }, + }, method: 'poll', - data: state, successCallback: ({ data }) => pollSuccessCallBack(data, commit, state, getters, dispatch), errorCallback: () => Flash(__('Something went wrong while fetching latest comments.')), }); @@ -367,7 +400,7 @@ export const poll = ({ commit, state, getters, dispatch }) => { if (!Visibility.hidden()) { eTagPoll.makeRequest(); } else { - service.poll(state); + fetchData({ commit, state, getters }); } Visibility.change(() => { @@ -387,18 +420,6 @@ export const restartPolling = () => { if (eTagPoll) eTagPoll.restart(); }; -export const fetchData = ({ commit, state, getters }) => { - const requestData = { - endpoint: state.notesData.notesPath, - lastFetchedAt: state.lastFetchedAt, - }; - - service - .poll(requestData) - .then(({ data }) => pollSuccessCallBack(data, commit, state, getters)) - .catch(() => Flash(__('Something went wrong while fetching latest comments.'))); -}; - export const toggleAward = ({ commit, getters }, { awardName, noteId }) => { commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] }); }; diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js index 8380cfb6c59..8d779e04673 100644 --- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js +++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import _ from 'underscore'; +import { escape } from 'lodash'; import { s__, n__, sprintf } from '~/locale'; import axios from '../lib/utils/axios_utils'; import PANEL_STATE from './constants'; @@ -69,13 +69,13 @@ export default class PrometheusMetrics { if (metric.active_metrics > 0) { totalExporters += 1; this.$monitoredMetricsList.append( - `<li>${_.escape(metric.group)}<span class="badge">${_.escape( + `<li>${escape(metric.group)}<span class="badge">${escape( metric.active_metrics, )}</span></li>`, ); totalMonitoredMetrics += metric.active_metrics; if (metric.metrics_missing_requirements > 0) { - this.$missingEnvVarMetricsList.append(`<li>${_.escape(metric.group)}</li>`); + this.$missingEnvVarMetricsList.append(`<li>${escape(metric.group)}</li>`); totalMissingEnvVarMetrics += 1; } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index a1bfa03a5ac..0de5aae4b0e 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -59,7 +59,7 @@ max-width: 100%; } - &:not(.md-file) img:not(.emoji) { + &:not(.md) img:not(.emoji) { border: 1px solid $white-normal; padding: 5px; margin: 5px 0; diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 507e227c952..4d49c96d268 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -312,6 +312,7 @@ class ProjectPolicy < BasePolicy enable :destroy_artifacts enable :daily_statistics enable :admin_operations + enable :read_deploy_token end rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml index 6103d86bf5a..57a5d3e2e83 100644 --- a/app/views/projects/_wiki.html.haml +++ b/app/views/projects/_wiki.html.haml @@ -1,6 +1,6 @@ - if @wiki_home.present? %div{ class: container_class } - .md.md-file.prepend-top-default.append-bottom-default + .md.prepend-top-default.append-bottom-default = render_wiki_content(@wiki_home) - else - can_create_wiki = can?(current_user, :create_wiki, @project) diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml index 46e76e4d175..41a0045be89 100644 --- a/app/views/projects/blob/preview.html.haml +++ b/app/views/projects/blob/preview.html.haml @@ -1,5 +1,5 @@ - if markup?(@blob.name) - .file-content.md.md-file + .file-content.md = markup(@blob.name, @content) - else .diff-file diff --git a/app/views/projects/blob/viewers/_markup.html.haml b/app/views/projects/blob/viewers/_markup.html.haml index c71df29354b..8134adcbc32 100644 --- a/app/views/projects/blob/viewers/_markup.html.haml +++ b/app/views/projects/blob/viewers/_markup.html.haml @@ -1,4 +1,4 @@ - blob = viewer.blob - context = blob.respond_to?(:rendered_markup) ? { rendered: blob.rendered_markup } : {} -.file-content.md.md-file +.file-content.md = markup(blob.name, blob.data, context) diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index ebd99cf8605..74798311c2e 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -26,7 +26,7 @@ = (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe .prepend-top-default.append-bottom-default - .md.md-file{ data: { qa_selector: 'wiki_page_content' } } + .md{ data: { qa_selector: 'wiki_page_content' } } = render_wiki_content(@page) = render 'sidebar' diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 5126351b0bb..fa77566dddb 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -23,7 +23,7 @@ %i.fa.fa-file %strong= snippet.file_name - if markup?(snippet.file_name) - .file-content.md.md-file + .file-content.md - snippet_chunks.each do |chunk| - unless chunk[:data].empty? = markup(snippet.file_name, chunk[:data]) |