diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-16 21:09:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-16 21:09:42 +0300 |
commit | c44eade0d7b09c9d80c42aba6d1974ce9cd3146b (patch) | |
tree | da0710de0a519f225755737cf5ffd93c75d1da8f /app | |
parent | 0c87da93750c6428328a3e3cd2ebd0882f6294e3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
14 files changed, 148 insertions, 16 deletions
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue index 46ae87a36e4..0eac2cad68d 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue @@ -152,7 +152,7 @@ export default { <span class="note-headline-light note-headline-meta"> <span class="system-note-message"> <slot></slot> </span> <gl-link - class="note-timestamp system-note-separator gl-display-block gl-mb-2" + class="note-timestamp system-note-separator gl-display-block gl-mb-2 gl-font-sm" :href="`#note_${noteAnchorId}`" > <time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" /> diff --git a/app/assets/javascripts/design_management/components/design_notes/toggle_replies_widget.vue b/app/assets/javascripts/design_management/components/design_notes/toggle_replies_widget.vue index 2e366282de3..189ddda525b 100644 --- a/app/assets/javascripts/design_management/components/design_notes/toggle_replies_widget.vue +++ b/app/assets/javascripts/design_management/components/design_notes/toggle_replies_widget.vue @@ -39,31 +39,31 @@ export default { <template> <li - class="toggle-comments gl-bg-gray-50 gl-display-flex gl-align-items-center gl-py-3" + class="toggle-comments gl-bg-gray-50 gl-display-flex gl-align-items-center gl-py-3 gl-min-h-8" :class="{ expanded: !collapsed }" data-testid="toggle-comments-wrapper" > <gl-icon :name="iconName" class="gl-ml-3" @click.stop="$emit('toggle')" /> <gl-button variant="link" - class="toggle-comments-button gl-ml-2 gl-mr-2" + class="toggle-comments-button gl-ml-2 gl-mr-2 gl-font-sm!" @click.stop="$emit('toggle')" > {{ toggleText }} </gl-button> <template v-if="collapsed"> - <span class="gl-text-gray-500">{{ __('Last reply by') }}</span> + <span class="gl-text-gray-500 gl-font-sm">{{ __('Last reply by') }}</span> <gl-link :href="lastReply.author.webUrl" target="_blank" - class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-ml-2 gl-mr-2" + class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-font-sm gl-ml-2 gl-mr-2" > {{ lastReply.author.name }} </gl-link> <time-ago-tooltip :time="lastReply.createdAt" tooltip-placement="bottom" - class="gl-text-gray-500" + class="gl-text-gray-500 gl-font-sm" /> </template> </li> diff --git a/app/assets/javascripts/lib/utils/secret_detection.js b/app/assets/javascripts/lib/utils/secret_detection.js index eadd8ec1538..8e673855631 100644 --- a/app/assets/javascripts/lib/utils/secret_detection.js +++ b/app/assets/javascripts/lib/utils/secret_detection.js @@ -22,7 +22,7 @@ export const containsSensitiveToken = (message) => { { // eslint-disable-next-line @gitlab/require-i18n-strings name: 'Feed Token', - regex: 'feed_token=[0-9a-zA-Z_-]{20}', + regex: 'feed_token=((glft-)?[0-9a-zA-Z_-]{20}|glft-[a-h0-9]+-[0-9]+_)', }, ]; diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js index 2c93b3b636a..e7f2662ae09 100644 --- a/app/assets/javascripts/persistent_user_callouts.js +++ b/app/assets/javascripts/persistent_user_callouts.js @@ -11,7 +11,7 @@ const PERSISTENT_USER_CALLOUTS = [ '.js-eoa-bronze-plan-banner', '.js-security-newsletter-callout', '.js-approaching-seat-count-threshold', - '.js-storage-enforcement-banner', + '.js-storage-pre-enforcement-alert', '.js-user-over-limit-free-plan-alert', '.js-minute-limit-banner', '.js-submit-license-usage-data-banner', diff --git a/app/assets/javascripts/repository/components/blob_viewers/geo_json/constants.js b/app/assets/javascripts/repository/components/blob_viewers/geo_json/constants.js new file mode 100644 index 00000000000..fd4d111b4b0 --- /dev/null +++ b/app/assets/javascripts/repository/components/blob_viewers/geo_json/constants.js @@ -0,0 +1,24 @@ +import iconUrl from 'leaflet/dist/images/marker-icon.png'; +import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'; +import shadowUrl from 'leaflet/dist/images/marker-shadow.png'; +import { __ } from '~/locale'; + +export const RENDER_ERROR_MSG = __( + 'The map can not be displayed because there was an error loading the GeoJSON file.', +); + +export const OPEN_STREET_TILE_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; +export const ICON_CONFIG = { iconUrl, iconRetinaUrl, shadowUrl }; +export const MAP_ATTRIBUTION = __('Map data from'); +export const OPEN_STREET_COPYRIGHT_LINK = + '<a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap</a>'; + +export const POPUP_CONTENT_TEMPLATE = ` +<div class="gl-pt-4"> + <% eachFunction(popupProperties, function(value, label) { %> + <div> + <strong><%- label %>:</strong> <span><%- value %></span> + </div> + <% }); %> +</div> +`; diff --git a/app/assets/javascripts/repository/components/blob_viewers/geo_json/geo_json_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/geo_json/geo_json_viewer.vue new file mode 100644 index 00000000000..1c9fccc2c19 --- /dev/null +++ b/app/assets/javascripts/repository/components/blob_viewers/geo_json/geo_json_viewer.vue @@ -0,0 +1,32 @@ +<script> +import { createAlert } from '~/alert'; +import { RENDER_ERROR_MSG } from './constants'; +import { initLeafletMap } from './utils'; + +export default { + props: { + blob: { + type: Object, + required: true, + }, + }, + data() { + return { + hasError: false, + loading: true, + }; + }, + mounted() { + try { + initLeafletMap(this.$refs.map, JSON.parse(this.blob.rawTextBlob)); + } catch (error) { + createAlert({ message: RENDER_ERROR_MSG }); + this.hasError = true; + } + }, +}; +</script> + +<template> + <div v-if="!hasError" ref="map" class="gl-h-100vh gl-z-index-0" data-testid="map"></div> +</template> diff --git a/app/assets/javascripts/repository/components/blob_viewers/geo_json/utils.js b/app/assets/javascripts/repository/components/blob_viewers/geo_json/utils.js new file mode 100644 index 00000000000..615f7fd2b47 --- /dev/null +++ b/app/assets/javascripts/repository/components/blob_viewers/geo_json/utils.js @@ -0,0 +1,47 @@ +import { map, tileLayer, geoJson, featureGroup, Icon } from 'leaflet'; +import { template, each } from 'lodash'; +import { + OPEN_STREET_TILE_URL, + MAP_ATTRIBUTION, + OPEN_STREET_COPYRIGHT_LINK, + ICON_CONFIG, + POPUP_CONTENT_TEMPLATE, +} from './constants'; + +const generateOpenStreetMapTiles = () => { + const attribution = `${MAP_ATTRIBUTION} ${OPEN_STREET_COPYRIGHT_LINK}`; + return tileLayer(OPEN_STREET_TILE_URL, { attribution }); +}; + +export const popupContent = (popupProperties) => { + return template(POPUP_CONTENT_TEMPLATE)({ + eachFunction: each, + popupProperties, + }); +}; + +const loadGeoJsonGroupAndBounds = (geoJsonData) => { + const layers = []; + const geoJsonGroup = geoJson(geoJsonData, { + onEachFeature: (feature, layer) => { + layers.push(layer); + if (feature.properties) { + layer.bindPopup(popupContent(feature.properties)); + } + }, + }); + + return { geoJsonGroup, bounds: featureGroup(layers).getBounds() }; +}; + +export const initLeafletMap = (el, geoJsonData) => { + if (!el || !geoJsonData) return; + + import('leaflet/dist/leaflet.css'); + Icon.Default.mergeOptions(ICON_CONFIG); + const leafletMap = map(el, { layers: [generateOpenStreetMapTiles()] }); + const { bounds, geoJsonGroup } = loadGeoJsonGroupAndBounds(geoJsonData); + + geoJsonGroup.addTo(leafletMap); + leafletMap.fitBounds(bounds); +}; diff --git a/app/assets/javascripts/repository/components/blob_viewers/index.js b/app/assets/javascripts/repository/components/blob_viewers/index.js index a480710f8ac..b749702972f 100644 --- a/app/assets/javascripts/repository/components/blob_viewers/index.js +++ b/app/assets/javascripts/repository/components/blob_viewers/index.js @@ -12,6 +12,7 @@ const viewers = { sketch: () => import('./sketch_viewer.vue'), notebook: () => import('./notebook_viewer.vue'), openapi: () => import('./openapi_viewer.vue'), + geo_json: () => import('./geo_json/geo_json_viewer.vue'), }; export const loadViewer = (type, isUsingLfs) => { diff --git a/app/assets/stylesheets/page_bundles/design_management.scss b/app/assets/stylesheets/page_bundles/design_management.scss index 15363e78eb3..c19561a5e5e 100644 --- a/app/assets/stylesheets/page_bundles/design_management.scss +++ b/app/assets/stylesheets/page_bundles/design_management.scss @@ -138,7 +138,7 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); } .design-note-pin { - margin-left: $gl-padding; + margin-left: 9px; } .design-discussion { @@ -148,13 +148,13 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); content: ''; border-left: 1px solid var(--gray-100, $gray-100); position: absolute; - left: 28px; + left: 22px; top: -17px; height: 17px; } .design-note { - padding: $gl-padding; + padding: $gl-padding-8; list-style: none; transition: background $gl-transition-duration-medium $general-hover-transition-curve; border-top-left-radius: $border-radius-default; // same border radius used by .bordered-box @@ -170,7 +170,9 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); } .reply-wrapper { - padding: $gl-padding; + padding: $gl-padding-8 $gl-padding-8 $gl-padding-4; + background: $gray-10; + border-radius: 0 0 $border-radius-default $border-radius-default; } } diff --git a/app/models/blob.rb b/app/models/blob.rb index 7c88833b19d..bb8c9345573 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -33,6 +33,7 @@ class Blob < SimpleDelegator BlobViewer::Notebook, BlobViewer::SVG, BlobViewer::OpenApi, + BlobViewer::GeoJson, BlobViewer::Image, BlobViewer::Sketch, diff --git a/app/models/blob_viewer/geo_json.rb b/app/models/blob_viewer/geo_json.rb new file mode 100644 index 00000000000..01dcf7707eb --- /dev/null +++ b/app/models/blob_viewer/geo_json.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module BlobViewer + class GeoJson < Base + include Rich + include ClientSide + + self.binary = false + self.extensions = %w[geojson] + self.partial_name = 'geo_json' + end +end diff --git a/app/models/group.rb b/app/models/group.rb index 13f622c7839..85971c48567 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -38,7 +38,7 @@ class Group < Namespace has_many :users, through: :group_members has_many :owners, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, - through: :group_members, + through: :all_group_members, source: :user has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent diff --git a/app/models/user.rb b/app/models/user.rb index 3ac58b75879..96cdbb192bc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -57,10 +57,14 @@ class User < ApplicationRecord FORBIDDEN_SEARCH_STATES = %w(blocked banned ldap_blocked).freeze + INCOMING_MAIL_TOKEN_PREFIX = 'glimt-' + FEED_TOKEN_PREFIX = 'glft-' + columns_changing_default :notified_of_own_activity - add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) } - add_authentication_token_field :feed_token + # lib/tasks/tokens.rake needs to be updated when changing mail and feed tokens + add_authentication_token_field :incoming_email_token, token_generator: -> { self.generate_incoming_mail_token } + add_authentication_token_field :feed_token, format_with_prefix: :prefix_for_feed_token add_authentication_token_field :static_object_token, encrypted: :optional attribute :admin, default: false @@ -968,6 +972,10 @@ class User < ApplicationRecord def get_ids_by_ids_or_usernames(ids, usernames) by_ids_or_usernames(ids, usernames).pluck(:id) end + + def generate_incoming_mail_token + "#{INCOMING_MAIL_TOKEN_PREFIX}#{SecureRandom.hex.to_i(16).to_s(36)}" + end end # @@ -2596,6 +2604,10 @@ class User < ApplicationRecord Ci::NamespaceMirror.contains_traversal_ids(traversal_ids) end + + def prefix_for_feed_token + FEED_TOKEN_PREFIX + end end User.prepend_mod_with('User') diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 2aed0a92407..e5566882371 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -2,7 +2,8 @@ - project = @project.present(current_user: current_user) - ref = local_assigns[:ref] || @ref - expanded = params[:expanded].present? -- if blob.rich_viewer +-# If the blob has a RichViewer we preload the content except for GeoJSON since it is handled by Vue +- if blob.rich_viewer && blob.extension != 'geojson' - add_page_startup_api_call local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: blob.rich_viewer.type, format: :json)) } .info-well.d-none.d-sm-block |