diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-21 03:13:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-21 03:13:46 +0300 |
commit | 4aa6fba6d825b88d23ff37668e78c851bec102b0 (patch) | |
tree | 2588fec6fc68f27fbfc23e89daf9b9af34d5466b /app | |
parent | faf60c19a9a1a29ce07d1b51ea3a69466e7129f3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
21 files changed, 73 insertions, 54 deletions
diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js index d2c31314bba..134962721d2 100644 --- a/app/assets/javascripts/pages/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -8,7 +8,7 @@ import UserTabs from './user_tabs'; function initUserProfile(action) { // TODO: Remove both Vue and legacy JS tabs code/feature flag uses with the // removal of the old navigation. - // See https://gitlab.com/groups/gitlab-org/-/epics/11875. + // See https://gitlab.com/gitlab-org/gitlab/-/issues/435899. if (gon.features?.profileTabsVue) { initProfileTabs(); diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 79eb3902116..f9e22808b0d 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -1,5 +1,5 @@ // TODO: Remove this with the removal of the old navigation. -// See https://gitlab.com/groups/gitlab-org/-/epics/11875. +// See https://gitlab.com/gitlab-org/gitlab/-/issues/435899. import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; diff --git a/app/assets/stylesheets/page_bundles/issuable_list.scss b/app/assets/stylesheets/page_bundles/issuable_list.scss index 1ca0c5e7ce6..9084bffa951 100644 --- a/app/assets/stylesheets/page_bundles/issuable_list.scss +++ b/app/assets/stylesheets/page_bundles/issuable_list.scss @@ -90,12 +90,19 @@ .issuable-list li, .issuable-info-container .controls { .avatar-counter { - display: inline-block; - vertical-align: middle; - min-width: 16px; + @include gl-pl-1 + @include gl-pr-2; + @include gl-h-5; + @include gl-min-w-5; line-height: 14px; - height: 16px; - padding-left: 2px; - padding-right: 2px; + } +} + +.merge-request { + .issuable-info-container .controls { + .avatar-counter { + @include gl-line-height-normal; + border: 0; + } } } diff --git a/app/assets/stylesheets/page_bundles/profile.scss b/app/assets/stylesheets/page_bundles/profile.scss index 9a8eeb9c9d6..912f0145bf1 100644 --- a/app/assets/stylesheets/page_bundles/profile.scss +++ b/app/assets/stylesheets/page_bundles/profile.scss @@ -164,12 +164,6 @@ .user-profile { .profile-header { - margin: 0 $gl-padding; - - &.with-no-profile-tabs { - margin-bottom: $gl-padding-24; - } - .avatar-holder { margin: 0 auto 10px; } diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb index ba2743e1002..01657df28fd 100644 --- a/app/controllers/import/bitbucket_server_controller.rb +++ b/app/controllers/import/bitbucket_server_controller.rb @@ -49,6 +49,9 @@ class Import::BitbucketServerController < Import::BaseController session[bitbucket_server_username_key] = params[:bitbucket_server_username] session[bitbucket_server_url_key] = params[:bitbucket_server_url] + experiment(:default_to_import_tab, actor: current_user) + .track(:authentication, property: provider_name) + redirect_to status_import_bitbucket_server_path(namespace_id: params[:namespace_id]) end diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index 34fdf513313..05ba317057d 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -22,6 +22,9 @@ class Import::FogbugzController < Import::BaseController session[:fogbugz_token] = res.get_token.to_s session[:fogbugz_uri] = params[:uri] + experiment(:default_to_import_tab, actor: current_user) + .track(:successfully_authenticated, property: provider_name) + redirect_to new_user_map_import_fogbugz_path(namespace_id: params[:namespace_id]) end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 2b72ceceb5a..0159c1913af 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -41,6 +41,9 @@ class Import::GithubController < Import::BaseController end def personal_access_token + experiment(:default_to_import_tab, actor: current_user) + .track(:authentication, property: provider_name) + session[access_token_key] = params[:personal_access_token]&.strip redirect_to status_import_url end diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index d1b182a57d8..71d66dc3db8 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -21,6 +21,9 @@ class Import::GitlabProjectsController < Import::BaseController @project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute if @project.saved? + experiment(:default_to_import_tab, actor: current_user) + .track(:successfully_imported, property: 'gitlab_export') + redirect_to( project_path(@project), notice: _("Project '%{project_name}' is being imported.") % { project_name: @project.name } diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb index 03884717e54..7d3c91a7f5c 100644 --- a/app/controllers/import/manifest_controller.rb +++ b/app/controllers/import/manifest_controller.rb @@ -31,6 +31,9 @@ class Import::ManifestController < Import::BaseController if manifest.valid? manifest_import_metadata.save(manifest.projects, group.id) + experiment(:default_to_import_tab, actor: current_user) + .track(:successfully_imported, property: provider_name) + redirect_to status_import_manifest_path else @errors = manifest.errors diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 88a8851607b..83cd84c396a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -261,7 +261,8 @@ class UsersController < ApplicationController end def load_groups - @groups = JoinedGroupsFinder.new(user).execute(current_user) + groups = JoinedGroupsFinder.new(user).execute(current_user) + @groups = groups.with_route.page(params[:page]).without_count prepare_groups_for_rendering(@groups) end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index cb9a270253f..424a3f5f8c5 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -53,13 +53,6 @@ module NavHelper %w[system_info background_migrations background_jobs health_check] end - def show_super_sidebar?(_user = current_user) - # The new navigation is now enabled for everyone. - # We are working on cleaning up the use of this helper and other related code. - # See https://gitlab.com/groups/gitlab-org/-/epics/11875 - true - end - private def get_header_links diff --git a/app/policies/organizations/organization_policy.rb b/app/policies/organizations/organization_policy.rb index d538b786f78..afd8c6e144f 100644 --- a/app/policies/organizations/organization_policy.rb +++ b/app/policies/organizations/organization_policy.rb @@ -13,12 +13,14 @@ module Organizations rule { admin }.policy do enable :admin_organization + enable :create_group enable :read_organization enable :read_organization_user end rule { organization_user }.policy do enable :admin_organization + enable :create_group enable :read_organization enable :read_organization_user end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 21d3c6499a0..bb577b41fa8 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -92,6 +92,16 @@ module Groups end end + if @group.organization && !can?(current_user, :create_group, @group.organization) + # We are unsetting this here to match behavior of invalid parent_id above and protect against possible + # committing to the database of a value that isn't allowed. + @group.organization = nil + message = s_("CreateGroup|You don't have permission to create a group in the provided organization.") + @group.errors.add(:organization_id, message) + + return false + end + true end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index a7caa797a46..3af04db4cfd 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,16 +1,13 @@ .layout-page{ class: page_with_sidebar_class } - - if show_super_sidebar? - -# Render the parent group sidebar while creating a new subgroup/project, see GroupsController#new. - - group = @parent_group || @group + -# Render the parent group sidebar while creating a new subgroup/project, see GroupsController#new. + - group = @parent_group || @group - - sidebar_panel = super_sidebar_nav_panel(nav: nav, user: current_user, group: group, project: @project, current_ref: current_ref, ref_type: @ref_type, viewed_user: @user, organization: @organization) - - sidebar_data = super_sidebar_context(current_user, group: group, project: @project, panel: sidebar_panel, panel_type: nav).to_json - %aside.js-super-sidebar.super-sidebar.super-sidebar-loading{ data: { root_path: root_path, sidebar: sidebar_data, force_desktop_expanded_sidebar: @force_desktop_expanded_sidebar.to_s, command_palette: command_palette_data(project: @project).to_json } } + - sidebar_panel = super_sidebar_nav_panel(nav: nav, user: current_user, group: group, project: @project, current_ref: current_ref, ref_type: @ref_type, viewed_user: @user, organization: @organization) + - sidebar_data = super_sidebar_context(current_user, group: group, project: @project, panel: sidebar_panel, panel_type: nav).to_json + %aside.js-super-sidebar.super-sidebar.super-sidebar-loading{ data: { root_path: root_path, sidebar: sidebar_data, force_desktop_expanded_sidebar: @force_desktop_expanded_sidebar.to_s, command_palette: command_palette_data(project: @project).to_json } } - = render_if_exists "layouts/tanuki_bot_chat" + = render_if_exists "layouts/tanuki_bot_chat" - - elsif defined?(nav) && nav - = render "layouts/nav/sidebar/#{nav}" .content-wrapper{ class: "#{@content_wrapper_class}" } .mobile-overlay = dispensable_render_if_exists 'layouts/header/verification_reminder' diff --git a/app/views/layouts/_snowplow.html.haml b/app/views/layouts/_snowplow.html.haml index 3582deea902..7cb4abd06d6 100644 --- a/app/views/layouts/_snowplow.html.haml +++ b/app/views/layouts/_snowplow.html.haml @@ -13,7 +13,6 @@ namespace_id: namespace&.id, plan_name: namespace&.actual_plan_name, project_id: @project&.id, - user_id: current_user&.id, - new_nav: show_super_sidebar? + user_id: current_user&.id ).to_context.to_json.to_json} gl.snowplowPseudonymizedPageUrl = #{masked_page_url(group: namespace, project: @project).to_json}; diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml index 3e92ef25552..b9abaa07c2c 100644 --- a/app/views/projects/_import_project_pane.html.haml +++ b/app/views/projects/_import_project_pane.html.haml @@ -18,42 +18,42 @@ .import-buttons - if gitlab_project_import_enabled? .import_gitlab_project.has-tooltip{ data: { container: 'body', testid: 'gitlab-import-button' } } - = render Pajamas::ButtonComponent.new(href: '#', icon: 'tanuki', button_options: { class: 'btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } }) do + = render Pajamas::ButtonComponent.new(href: '#', icon: 'tanuki', button_options: { class: 'btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } }) do = _('GitLab export') - if github_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: new_import_github_path(namespace_id: namespace_id), icon: 'github', button_options: { class: 'js-import-github js-import-project-btn', data: { platform: 'github', **tracking_attrs_data(track_label, 'click_button', 'github') } }) do + = render Pajamas::ButtonComponent.new(href: new_import_github_path(namespace_id: namespace_id), icon: 'github', button_options: { class: 'js-import-github js-import-project-btn', data: { platform: 'github', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'github') } }) do GitHub - if bitbucket_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: status_import_bitbucket_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: "import_bitbucket js-import-project-btn #{'js-how-to-import-link' unless bitbucket_import_configured?}", data: { modal_title: _("Import projects from Bitbucket"), modal_message: import_from_bitbucket_message, platform: 'bitbucket_cloud', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_cloud') } }) do + = render Pajamas::ButtonComponent.new(href: status_import_bitbucket_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: "import_bitbucket js-import-project-btn #{'js-how-to-import-link' unless bitbucket_import_configured?}", data: { modal_title: _("Import projects from Bitbucket"), modal_message: import_from_bitbucket_message, platform: 'bitbucket_cloud', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'bitbucket_cloud') } }) do Bitbucket Cloud - if bitbucket_server_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: status_import_bitbucket_server_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: 'import_bitbucket js-import-project-btn', data: { platform: 'bitbucket_server', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } }) do + = render Pajamas::ButtonComponent.new(href: status_import_bitbucket_server_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: 'import_bitbucket js-import-project-btn', data: { platform: 'bitbucket_server', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } }) do Bitbucket Server - if fogbugz_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: new_import_fogbugz_path(namespace_id: namespace_id), icon: 'bug', button_options: { class: 'import_fogbugz js-import-project-btn', data: { platform: 'fogbugz', **tracking_attrs_data(track_label, 'click_button', 'fogbugz') } }) do + = render Pajamas::ButtonComponent.new(href: new_import_fogbugz_path(namespace_id: namespace_id), icon: 'bug', button_options: { class: 'import_fogbugz js-import-project-btn', data: { platform: 'fogbugz', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'fogbugz') } }) do FogBugz - if gitea_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: new_import_gitea_path(namespace_id: namespace_id), icon: 'gitea', button_options: { class: 'import_gitea js-import-project-btn', data: { platform: 'gitea', **tracking_attrs_data(track_label, 'click_button', 'gitea') } }) do + = render Pajamas::ButtonComponent.new(href: new_import_gitea_path(namespace_id: namespace_id), icon: 'gitea', button_options: { class: 'import_gitea js-import-project-btn', data: { platform: 'gitea', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'gitea') } }) do Gitea - if git_import_enabled? %div - = render Pajamas::ButtonComponent.new(icon: 'link', button_options: { class: 'js-toggle-button js-import-git-toggle-button js-import-project-btn', data: { platform: 'repo_url', toggle_open_class: 'active', **tracking_attrs_data(track_label, 'click_button', 'repo_url') } }) do + = render Pajamas::ButtonComponent.new(icon: 'link', button_options: { class: 'js-toggle-button js-import-git-toggle-button js-import-project-btn', data: { platform: 'repo_url', toggle_open_class: 'active', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'repo_url') } }) do = _('Repository by URL') - if manifest_import_enabled? %div - = render Pajamas::ButtonComponent.new(href: new_import_manifest_path(namespace_id: namespace_id), icon: 'doc-text', button_options: { class: 'import_manifest js-import-project-btn', data: { platform: 'manifest_file', **tracking_attrs_data(track_label, 'click_button', 'manifest_file') } }) do + = render Pajamas::ButtonComponent.new(href: new_import_manifest_path(namespace_id: namespace_id), icon: 'doc-text', button_options: { class: 'import_manifest js-import-project-btn', data: { platform: 'manifest_file', track_experiment: local_assigns[:track_experiment], **tracking_attrs_data(track_label, 'click_button', 'manifest_file') } }) do = _('Manifest file') = render_if_exists "projects/gitee_import_button", namespace_id: namespace_id, track_label: track_label @@ -63,4 +63,4 @@ = gitlab_ui_form_for @project, html: { class: 'new_project gl-show-field-errors js-project-import' } do |f| %hr = render "shared/import_form", f: f - = render 'projects/new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label + = render 'projects/new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: 'import_project' diff --git a/app/views/projects/buttons/_code.html.haml b/app/views/projects/buttons/_code.html.haml index a78e3861e94..9cdbe1d5f6b 100644 --- a/app/views/projects/buttons/_code.html.haml +++ b/app/views/projects/buttons/_code.html.haml @@ -8,9 +8,9 @@ %span.js-clone-dropdown-label = _('Code') = sprite_icon("chevron-down", css_class: "icon") - %ul.dropdown-menu.dropdown-menu-large.clone-options-dropdown{ class: dropdown_class, data: { testid: 'clone-dropdown-content' } } + %ul.dropdown-menu.dropdown-menu-large.clone-options-dropdown{ role: 'menu', class: dropdown_class, data: { testid: 'clone-dropdown-content' } } - if ssh_enabled? - %li.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' } + %li.gl-dropdown-item.js-clone-links{ role: 'menuitem', class: 'gl-px-4!' } %label.label-bold = _('Clone with SSH') .input-group.btn-group @@ -19,7 +19,7 @@ = clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), category: :primary, size: :medium) = render_if_exists 'projects/buttons/geo' - if http_enabled? - %li.pt-2.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' } + %li.pt-2.gl-dropdown-item.js-clone-links{ role: 'menuitem', class: 'gl-px-4!' } %label.label-bold = _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase } .input-group.btn-group @@ -28,8 +28,8 @@ = clipboard_button(target: '#http_project_clone', title: _("Copy URL"), category: :primary, size: :medium) = render_if_exists 'projects/buttons/geo' = render_if_exists 'projects/buttons/kerberos_clone_field' - %li.divider.mt-2 - %li.pt-2.gl-dropdown-item.js-clone-links + %li.divider.mt-2{ role: 'presentation' } + %li.pt-2.gl-dropdown-item.js-clone-links{ role: 'menuitem' } %label.label-bold{ class: 'gl-px-4!' } = _('Open in your IDE') - if ssh_enabled? @@ -55,5 +55,5 @@ .gl-dropdown-item-text-wrapper = _("Xcode") - if !project.empty_repo? && can?(current_user, :download_code, project) - %li.divider.mt-2 + %li.divider.mt-2{ role: 'presentation' } = render 'projects/buttons/download_menu_items', project: project, ref: ref diff --git a/app/views/projects/buttons/_download_menu_items.html.haml b/app/views/projects/buttons/_download_menu_items.html.haml index f5f8efca073..7d7033da9cd 100644 --- a/app/views/projects/buttons/_download_menu_items.html.haml +++ b/app/views/projects/buttons/_download_menu_items.html.haml @@ -2,7 +2,7 @@ - ref = local_assigns.fetch(:ref) - archive_prefix = "#{project.path}-#{ref.tr('/', '-')}" -%li.gl-dropdown-item{ role: 'menuitem' } - %h3.h5.m-0.dropdown-bold-header= _('Download source code') +%li.gl-dropdown-item{ class: 'gl-pt-3!', role: 'menuitem' } + %label.label-bold{ class: 'gl-px-4!' }= _('Download source code') = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil .js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } } diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml index f2c2700b012..4f722ba901d 100644 --- a/app/views/projects/merge_requests/creations/new.html.haml +++ b/app/views/projects/merge_requests/creations/new.html.haml @@ -11,7 +11,7 @@ = render 'new_submit' - else - if conflicting_mr - - link_to_mr = link_to(conflicting_mr.to_reference, project_merge_request_path(@project, conflicting_mr)) + - link_to_mr = link_to(conflicting_mr.to_reference, project_merge_request_path(conflicting_mr.target_project, conflicting_mr)) - flash.now[:alert] = safe_format(s_("These branches already have an open merge request: %{link_to_mr}. Select a different source or target branch."), link_to_mr: link_to_mr) = render 'new_compare' diff --git a/app/views/shared/groups/_list.html.haml b/app/views/shared/groups/_list.html.haml index 550f079bf3b..ecd722ec53e 100644 --- a/app/views/shared/groups/_list.html.haml +++ b/app/views/shared/groups/_list.html.haml @@ -11,6 +11,7 @@ %ul.content-list - groups.each_with_index do |group, i| = render "shared/groups/group", group: group, user: user + = paginate_collection(groups) - else = render partial: 'shared/empty_states/profile_tabs', locals: { illustration_path: illustration_path, current_user_empty_message_header: current_user_empty_message_header, diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 99097ac397c..3aee73b0b96 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -26,7 +26,7 @@ = render 'users/view_user_in_admin_area' .js-user-profile-actions{ data: user_profile_actions_data(@user) } - .profile-header{ class: [('with-no-profile-tabs' if profile_tabs.empty?), ('gl-mb-4!' if show_super_sidebar?)] } + .profile-header.gl-mx-5.gl-mb-4{ class: [('gl-mb-6' if profile_tabs.empty?)] } .gl-display-inline-block.gl-mx-8.gl-vertical-align-top .avatar-holder = link_to avatar_icon_for_user(@user, 400, current_user: current_user), target: '_blank', rel: 'noopener noreferrer' do @@ -113,9 +113,9 @@ = @user.bio -# TODO: Remove this with the removal of the old navigation. - -# See https://gitlab.com/groups/gitlab-org/-/epics/11875. + -# See https://gitlab.com/gitlab-org/gitlab/-/issues/435899. - if !profile_tabs.empty? && !Feature.enabled?(:profile_tabs_vue, current_user) - .scrolling-tabs-container{ class: [('gl-display-none' if show_super_sidebar?)] } + .scrolling-tabs-container.gl-display-none %button.fade-left{ type: 'button', title: _('Scroll left'), 'aria-label': _('Scroll left') } = sprite_icon('chevron-lg-left', size: 12) %button.fade-right{ type: 'button', title: _('Scroll right'), 'aria-label': _('Scroll right') } |