提交 3fd97b4b 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 c5223939
......@@ -8,6 +8,7 @@
# Technical writing team are the default reviewers for all markdown docs
*.md @gl-docsteam
/doc/ @gl-docsteam
# Frontend maintainers should see everything in `app/assets/`
*.scss @annabeldunstone @gitlab-org/maintainers/frontend
......
......@@ -495,6 +495,10 @@ export default class LabelsSelect {
].join(''),
);
const rightLabelTextColor = ({ label, escapeStr }) => {
return escapeStr(label.text_color === '#FFFFFF' ? label.color : label.text_color);
};
const infoIconTemplate = _.template(
[
'<a href="<%= scopedLabelsDocumentationLink %>" class="gl-link gl-label-icon" target="_blank" rel="noopener">',
......@@ -510,7 +514,7 @@ export default class LabelsSelect {
spanOpenTag,
'<%- label.title.slice(0, label.title.lastIndexOf("::")) %>',
'</span>',
'<span class="gl-label-text" style="color: <%= escapeStr(label.color) %>;">',
'<span class="gl-label-text" style="color: <%= rightLabelTextColor({ label, escapeStr }) %>;">',
'<%- label.title.slice(label.title.lastIndexOf("::") + 2) %>',
'</span>',
'</a>',
......@@ -536,7 +540,7 @@ export default class LabelsSelect {
'<% _.each(labels, function(label){ %>',
'<% if (isScopedLabel(label) && enableScopedLabels) { %>',
'<span class="d-inline-block position-relative scoped-label-wrapper">',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, infoIconTemplate, scopedLabelsDocumentationLink, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, rightLabelTextColor, infoIconTemplate, scopedLabelsDocumentationLink, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'</span>',
'<% } else { %>',
'<%= labelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, tooltipTitleTemplate, escapeStr, linkAttrs: "" }) %>',
......@@ -548,6 +552,7 @@ export default class LabelsSelect {
return tpl({
...tplData,
labelTemplate,
rightLabelTextColor,
infoIconTemplate,
scopedLabelTemplate,
tooltipTitleTemplate,
......
......@@ -18,14 +18,23 @@ export default {
unavailableFeatureText: s__(
'ContainerRegistry|Currently, the Container Registry tag expiration feature is not available for projects created before GitLab version 12.8. For updates and more information, visit Issue %{linkStart}#196124%{linkEnd}',
),
fetchSettingsErrorText: FETCH_SETTINGS_ERROR_MESSAGE,
},
data() {
return {
fetchSettingsError: false,
};
},
computed: {
...mapState(['isDisabled']),
showSettingForm() {
return !this.isDisabled && !this.fetchSettingsError;
},
},
mounted() {
this.fetchSettings().catch(() =>
this.$toast.show(FETCH_SETTINGS_ERROR_MESSAGE, { type: 'error' }),
);
this.fetchSettings().catch(() => {
this.fetchSettingsError = true;
});
},
methods: {
...mapActions(['fetchSettings']),
......@@ -48,17 +57,22 @@ export default {
}}
</li>
</ul>
<settings-form v-if="!isDisabled" />
<gl-alert v-else :dismissible="false">
<p>
<gl-sprintf :message="$options.i18n.unavailableFeatureText">
<template #link="{content}">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/issues/196124" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</gl-alert>
<settings-form v-if="showSettingForm" />
<template v-else>
<gl-alert v-if="isDisabled" :dismissible="false">
<p>
<gl-sprintf :message="$options.i18n.unavailableFeatureText">
<template #link="{content}">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/issues/196124" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</gl-alert>
<gl-alert v-else-if="fetchSettingsError" variant="warning" :dismissible="false">
<gl-sprintf :message="$options.i18n.fetchSettingsErrorText" />
</gl-alert>
</template>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import { GlLink, GlLoadingIcon } from '@gitlab/ui';
import { sprintf, n__, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
......@@ -10,6 +10,7 @@ export default {
name: 'RelatedMergeRequests',
components: {
Icon,
GlLink,
GlLoadingIcon,
RelatedIssuableItem,
},
......@@ -64,10 +65,19 @@ export default {
</script>
<template>
<div v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)">
<div
v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)"
id="related-merge-requests"
>
<div id="merge-requests" class="card card-slim mt-3">
<div class="card-header">
<div class="card-title mt-0 mb-0 h5 merge-requests-title">
<div class="card-title mt-0 mb-0 h5 merge-requests-title position-relative">
<gl-link
id="user-content-related-merge-requests"
class="anchor position-absolute text-decoration-none"
href="#related-merge-requests"
aria-hidden="true"
/>
<span class="mr-1">
{{ __('Related merge requests') }}
</span>
......
......@@ -158,10 +158,6 @@
a:not(.btn) {
color: inherit;
.gl-label-text:hover {
color: inherit;
}
&:hover {
color: $blue-800;
......
......@@ -284,3 +284,22 @@ ul.related-merge-requests > li {
text-align: right;
}
}
.issue-details {
.card-title {
a.anchor {
left: -16px;
top: 4px;
outline: none;
&::after {
content: image-url('icon_anchor.svg');
@include invisible(hidden);
}
}
&:hover > a.anchor::after {
@include invisible(visible);
}
}
}
......@@ -23,4 +23,10 @@ class ZoomMeeting < ApplicationRecord
def self.canonical_meeting_url(issue)
canonical_meeting(issue)&.url
end
def self.distinct_count_by(column = nil, fallback = -1)
distinct.count(column)
rescue ActiveRecord::StatementInvalid
fallback
end
end
---
title: Container expiration policy settings hide form on API error
merge_request: 26303
author:
type: fixed
---
title: Add anchor tags to related issues and related merge requests.
merge_request: 26756
author: Gilang Gumilar
type: added
......@@ -8,7 +8,7 @@ The Admin Area provides a web UI for administering some features of GitLab self-
To access the Admin Area, either:
- Click the Admin Area icon (the spanner or wrench icon).
- Click the Admin Area icon (**{admin}**).
- Visit `/admin` on your self-managed instance.
NOTE: **Note:**
......@@ -18,24 +18,24 @@ Only admin users can access the Admin Area.
The Admin Area is made up of the following sections:
| Section | Description |
|:--------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Overview](#overview-section) | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administering-projects), [users](#administering-users), [groups](#administering-groups), [jobs](#administering-jobs), [Runners](#administering-runners), and [Gitaly servers](#administering-gitaly-servers). |
| Monitoring | View GitLab [system information](#system-info), and information on [background jobs](#background-jobs), [logs](#logs), [health checks](monitoring/health_check.md), [requests profiles](#requests-profiles), and [audit logs](#audit-log-premium-only). |
| Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
| Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
| Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
| License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). |
| Kubernetes | Create and manage instance-level [Kubernetes clusters](../instance/clusters/index.md). |
Push Rules **(STARTER ONLY)** | Configure pre-defined Git [push rules](../../push_rules/push_rules.md) for projects. Also, configure [merge requests approvers rules](merge_requests_approvals.md). **(PREMIUM ONLY)** |
| Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). |
| Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
| Credentials **(ULTIMATE ONLY)** | View [credentials](credentials_inventory.md) that can be used to access your instance. |
| Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
| Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
| Appearance | Customize [GitLab's appearance](appearance.md). |
| Settings | Modify the [settings](settings/index.md) for your GitLab instance. |
| Section | Description |
|:-----------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **{overview}** [Overview](#overview-section) | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administering-projects), [users](#administering-users), [groups](#administering-groups), [jobs](#administering-jobs), [Runners](#administering-runners), and [Gitaly servers](#administering-gitaly-servers). |
| **{monitor}** Monitoring | View GitLab [system information](#system-info), and information on [background jobs](#background-jobs), [logs](#logs), [health checks](monitoring/health_check.md), [requests profiles](#requests-profiles), and [audit logs](#audit-log-premium-only). |
| **{messages}** Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
| **{hook}** System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
| **{applications}** Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
| **{slight-frown}** Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
| **{license}** License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). |
| **{cloud-gear}** Kubernetes | Create and manage instance-level [Kubernetes clusters](../instance/clusters/index.md). |
| **{push-rules}** Push Rules **(STARTER ONLY)** | Configure pre-defined Git [push rules](../../push_rules/push_rules.md) for projects. Also, configure [merge requests approvers rules](merge_requests_approvals.md). **(PREMIUM ONLY)** |
| **{location-dot}** Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). |
| **{key}** Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
| **{lock}** Credentials **(ULTIMATE ONLY)** | View [credentials](credentials_inventory.md) that can be used to access your instance. |
| **{template}** Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
| **{labels}** Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
| **{appearance}** Appearance | Customize [GitLab's appearance](appearance.md). |
| **{settings}** Settings | Modify the [settings](settings/index.md) for your GitLab instance. |
## Admin Dashboard
......@@ -43,7 +43,7 @@ The Dashboard provides statistics and system information about the GitLab instan
To access the Dashboard, either:
- Click the Admin Area icon (the wrench icon).
- Click the Admin Area icon (**{admin}**).
- Visit `/admin` on your self-managed instance.
The Dashboard is the default view of the Admin Area, and is made up of the following sections:
......@@ -59,13 +59,13 @@ The Dashboard is the default view of the Admin Area, and is made up of the follo
## Overview section
The following topics document the **Overview** section of the Admin Area.
The following topics document the **{overview}** **Overview** section of the Admin Area.
### Administering Projects
You can administer all projects in the GitLab instance from the Admin Area's Projects page.
To access the Projects page, go to **Admin Area > Overview > Projects**.
To access the Projects page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Projects**.
Click the **All**, **Private**, **Internal**, or **Public** tab to list only projects of that
criteria.
......@@ -105,7 +105,7 @@ You can combine the filter options. For example, to list only public projects wi
You can administer all users in the GitLab instance from the Admin Area's Users page.
To access the Users page, go to **Admin Area > Overview > Users**.
To access the Users page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Users**.
To list users matching a specific criteria, click on one of the following tabs on the **Users** page:
......@@ -138,7 +138,7 @@ you must provide the complete email address.
You can administer all groups in the GitLab instance from the Admin Area's Groups page.
To access the Groups page, go to **Admin Area > Overview > Groups**.
To access the Groups page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Groups**.
For each group, the page displays their name, description, size, number of projects in the group,
number of members, and whether the group is private, internal, or public. To edit a group, click
......@@ -157,7 +157,7 @@ To [Create a new group](../group/index.md#create-a-new-group) click **New group*
You can administer all jobs in the GitLab instance from the Admin Area's Jobs page.
To access the Jobs page, go to **Admin Area > Overview > Jobs**.
To access the Jobs page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Jobs**.
All jobs are listed, in descending order of job ID.
......@@ -182,7 +182,7 @@ For each job, the following details are listed:
You can administer all Runners in the GitLab instance from the Admin Area's **Runners** page. See
[GitLab Runner](https://docs.gitlab.com/runner/) for more information on Runner itself.
To access the **Runners** page, go to **Admin Area > Overview > Runners**.
To access the **Runners** page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Runners**.
The **Runners** page features:
......@@ -228,7 +228,7 @@ You can also edit, pause, or remove each Runner.
You can list all Gitaly servers in the GitLab instance from the Admin Area's **Gitaly Servers**
page. For more details, see [Gitaly](../../administration/gitaly/index.md).
To access the **Gitaly Servers** page, go to **Admin Area > Overview > Gitaly Servers**.
To access the **Gitaly Servers** page, go to **{admin}** **Admin Area >** **{overview}** **Overview > Gitaly Servers**.
For each Gitaly server, the following details are listed:
......@@ -242,7 +242,7 @@ For each Gitaly server, the following details are listed:
## Monitoring section
The following topics document the **Monitoring** section of the Admin Area.
The following topics document the **{monitor}** **Monitoring** section of the Admin Area.
### System Info
......
......@@ -84,7 +84,7 @@ module Gitlab
issues: count(Issue),
issues_created_from_gitlab_error_tracking_ui: count(SentryIssue),
issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue),
issues_using_zoom_quick_actions: count(ZoomMeeting.select(:issue_id).distinct, batch: false),
issues_using_zoom_quick_actions: distinct_count(ZoomMeeting, :issue_id),
issues_with_embedded_grafana_charts_approx: ::Gitlab::GrafanaEmbedUsageData.issue_count,
incident_issues: count(::Issue.authored(::User.alert_bot)),
keys: count(Key),
......
......@@ -20484,31 +20484,40 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
msgid "ThreatMonitoring|A Web Application Firewall (WAF) provides monitoring and rules to protect production applications. GitLab adds the modsecurity WAF plug-in when you install the Ingress app in your Kubernetes cluster."
msgstr ""
msgid "ThreatMonitoring|Anomalous Requests"
msgstr ""
msgid "ThreatMonitoring|At this time, threat monitoring only supports WAF data."
msgstr ""
msgid "ThreatMonitoring|Container Network Policy"
msgstr ""
msgid "ThreatMonitoring|Dropped Packets"
msgstr ""
msgid "ThreatMonitoring|Environment"
msgstr ""
msgid "ThreatMonitoring|No traffic to display"
msgstr ""
msgid "ThreatMonitoring|Operations Per Second"
msgstr ""
msgid "ThreatMonitoring|Packet Activity"
msgstr ""
msgid "ThreatMonitoring|Requests"
msgstr ""
msgid "ThreatMonitoring|Show last"
msgstr ""
msgid "ThreatMonitoring|Something went wrong, unable to fetch WAF statistics"
msgid "ThreatMonitoring|Something went wrong, unable to fetch environments"
msgstr ""
msgid "ThreatMonitoring|Something went wrong, unable to fetch environments"
msgid "ThreatMonitoring|Something went wrong, unable to fetch statistics"
msgstr ""
msgid "ThreatMonitoring|The graph below is an overview of traffic coming to your application as tracked by the Web Application Firewall (WAF). View the docs for instructions on how to access the WAF logs to see what type of malicious traffic is trying to access your app. The docs link is also accessible by clicking the \"?\" icon next to the title below."
......@@ -20520,13 +20529,25 @@ msgstr ""
msgid "ThreatMonitoring|Threat Monitoring help page link"
msgstr ""
msgid "ThreatMonitoring|Threat monitoring is not enabled"
msgstr ""
msgid "ThreatMonitoring|Threat monitoring provides security monitoring and rules to protect production applications."
msgstr ""
msgid "ThreatMonitoring|Time"
msgstr ""
msgid "ThreatMonitoring|Total Packets"
msgstr ""
msgid "ThreatMonitoring|Total Requests"
msgstr ""
msgid "ThreatMonitoring|Web Application Firewall not enabled"
msgid "ThreatMonitoring|Web Application Firewall"
msgstr ""
msgid "ThreatMonitoring|While it's rare to have no traffic coming to your application, it can happen. In any event, we ask that you double check your settings to make sure you've set up the Network Policies correctly."
msgstr ""
msgid "ThreatMonitoring|While it's rare to have no traffic coming to your application, it can happen. In any event, we ask that you double check your settings to make sure you've set up the WAF correctly."
......
......@@ -23,6 +23,16 @@ const mockScopedLabels = [
},
];
const mockScopedLabels2 = [
{
id: 28,
title: 'Foo::Bar2',
description: 'Foobar2',
color: '#FFFFFF',
text_color: '#000000',
},
];
describe('LabelsSelect', () => {
describe('getLabelTemplate', () => {
describe('when normal label is present', () => {
......@@ -108,11 +118,45 @@ describe('LabelsSelect', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color}; color: ${label.text_color};`,
);
expect(
$labelEl
.find('span.gl-label-text')
.last()
.attr('style'),
).toBe(`color: ${label.color};`);
});
it('generated label item has a badge class', () => {
expect($labelEl.find('span').hasClass('gl-label-text')).toEqual(true);
});
});
describe('when scoped label is present, with text color not white', () => {
const label = mockScopedLabels2[0];
let $labelEl;
beforeEach(() => {
$labelEl = $(
LabelsSelect.getLabelTemplate({
labels: mockScopedLabels2,
issueUpdateURL: mockUrl,
enableScopedLabels: true,
scopedLabelsDocumentationLink: 'docs-link',
}),
);
});
it('generated label item template has correct label styles', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color}; color: ${label.text_color};`,
);
expect(
$labelEl
.find('span.gl-label-text')
.last()
.attr('style'),
).toBe(`color: ${label.text_color};`);
});
});
});
});
......@@ -44,15 +44,6 @@ describe('Registry Settings App', () => {
expect(store.dispatch).toHaveBeenCalledWith('fetchSettings');
});
it('show a toast if fetchSettings fails', () => {
mountComponent({ dispatchMock: 'mockRejectedValue' });
return wrapper.vm.$nextTick().then(() =>
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(FETCH_SETTINGS_ERROR_MESSAGE, {
type: 'error',
}),
);
});
it('renders the setting form', () => {
mountComponent();
expect(findSettingsComponent().exists()).toBe(true);
......@@ -68,7 +59,23 @@ describe('Registry Settings App', () => {
});
it('shows an alert', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().html()).toContain(
'Currently, the Container Registry tag expiration feature is not available',
);
});
});
describe('fetchSettingsError', () => {
beforeEach(() => {
mountComponent({ dispatchMock: 'mockRejectedValue' });
});
it('the form is hidden', () => {
expect(findSettingsComponent().exists()).toBe(false);
});
it('shows an alert', () => {
expect(findAlert().html()).toContain(FETCH_SETTINGS_ERROR_MESSAGE);
});
});
});
......@@ -151,4 +151,51 @@ describe ZoomMeeting do
it_behaves_like 'can remove meetings'
end
end
describe '.distinct_count_by' do
let(:issue_1) { create(:issue) }
let(:issue_2) { create(:issue) }
context 'two meetings for the same issue' do
before do
create(:zoom_meeting, issue: issue_1)
create(:zoom_meeting, :removed_from_issue, issue: issue_1)
end
it 'returns a count of 1' do
expect(described_class.distinct_count_by(:issue_id)).to eq(1)
end
context 'when given no colum to count' do
it 'counts by :id and returns a count of 2' do
expect(described_class.distinct_count_by).to eq(2)
end
end
end
context 'one meeting for each issue' do
it 'returns a count of 2' do
create(:zoom_meeting, issue: issue_1)
create(:zoom_meeting, issue: issue_2)
expect(described_class.distinct_count_by(:issue_id)).to eq(2)
end
end
context 'the count query times out' do
before do
allow_next_instance_of(ActiveRecord::Relation) do |instance|
allow(instance).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
end
end
it 'does not raise an error' do
expect { described_class.distinct_count_by(:issue_id) }.not_to raise_error
end
it 'returns -1' do
expect(described_class.distinct_count_by(:issue_id)).to eq(-1)
end
end
end
end
......@@ -796,15 +796,15 @@
dependencies:
vue-eslint-parser "^7.0.0"
"@gitlab/svgs@^1.110.0":
version "1.110.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.110.0.tgz#3c4f5f0e78fcf616ec63a265754158b84ed80af8"
integrity sha512-bLVUW9Hj6j7zTdeoQELO3Bls5xDKr6AoSEU8gZbEZKLK9PV81hxRl/lJPJUo1qt4E7eJGapCTlH73tTIL4OZ3A==
"@gitlab/ui@^9.23.1":
version "9.23.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.23.1.tgz#791d0c8a6762b1dd73ed686326c1dfb3f0c7b987"
integrity sha512-7bGcV2W6qh/KK423W/vasv+S6myWJMD1tyMr5MBz1WQRg/B3eUlpr4HbjQXmtALRWiWkag8GMI/HSy0rby4WrA==
"@gitlab/svgs@^1.111.0":
version "1.111.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.111.0.tgz#10c0ba2fef9351a4d5e1f939994e39ebe5cba909"
integrity sha512-r+PMe4o7F9HAof+J9eF4aR/J068ZywAQRHBA+xU8TdyqX9gtDdBsvHBiMT65s8gr6MgLhduc3N4YlPwZua2QNQ==
"@gitlab/ui@^9.28.0":
version "9.28.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.28.0.tgz#fe307f7fdd9dc203078efb6f4d0590e0b1cb9053"
integrity sha512-de2zApLY5dD7YhguLAb/6mDROFMoA0zQ1euiGhBoVbaUwzNwSP1gremE186uPd6uGs9ZKfSp3on/qQYhXrNy9w==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册