Merge branch 'main' into application-wizard-2

* main:
  web/flows: fix identification stage band color (#6489)
  providers/proxy: only intercept auth header when a value is set (#6488)
  web: bump @goauthentik/api from 2023.6.1-1691242648 to 2023.6.1-1691266058 in /web (#6486)
  providers/proxy: set outpost session cookie to httponly and secure wh… (#6482)
  web: bump @esbuild/linux-arm64 from 0.18.17 to 0.18.18 in /web (#6483)
  web/admin: fix user sorting by active field (#6485)
  web: bump @esbuild/darwin-arm64 from 0.18.17 to 0.18.18 in /web (#6484)
  web: bump storybook (#6481)
  web: bump the sentry group in /web with 2 updates (#6480)
  web: bump API Client version (#6479)
  api: optimise pagination in API schema (#6478)
  website/dev-docs: tweaks to template (#6474)
  website: bump react-tooltip from 5.19.0 to 5.20.0 in /website (#6471)
  website: bump prettier from 3.0.0 to 3.0.1 in /website (#6472)
This commit is contained in:
Ken Sternberg 2023-08-07 08:23:00 -07:00
commit fa0a9166d1
22 changed files with 2289 additions and 4844 deletions

View File

@ -2,6 +2,43 @@
from rest_framework import pagination from rest_framework import pagination
from rest_framework.response import Response from rest_framework.response import Response
PAGINATION_COMPONENT_NAME = "Pagination"
PAGINATION_SCHEMA = {
"type": "object",
"properties": {
"next": {
"type": "number",
},
"previous": {
"type": "number",
},
"count": {
"type": "number",
},
"current": {
"type": "number",
},
"total_pages": {
"type": "number",
},
"start_index": {
"type": "number",
},
"end_index": {
"type": "number",
},
},
"required": [
"next",
"previous",
"count",
"current",
"total_pages",
"start_index",
"end_index",
],
}
class Pagination(pagination.PageNumberPagination): class Pagination(pagination.PageNumberPagination):
"""Pagination which includes total pages and current page""" """Pagination which includes total pages and current page"""
@ -35,41 +72,7 @@ class Pagination(pagination.PageNumberPagination):
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
"pagination": { "pagination": {"$ref": f"#/components/schemas/{PAGINATION_COMPONENT_NAME}"},
"type": "object",
"properties": {
"next": {
"type": "number",
},
"previous": {
"type": "number",
},
"count": {
"type": "number",
},
"current": {
"type": "number",
},
"total_pages": {
"type": "number",
},
"start_index": {
"type": "number",
},
"end_index": {
"type": "number",
},
},
"required": [
"next",
"previous",
"count",
"current",
"total_pages",
"start_index",
"end_index",
],
},
"results": schema, "results": schema,
}, },
"required": ["pagination", "results"], "required": ["pagination", "results"],

View File

@ -1,5 +1,6 @@
"""Error Response schema, from https://github.com/axnsan12/drf-yasg/issues/224""" """Error Response schema, from https://github.com/axnsan12/drf-yasg/issues/224"""
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from drf_spectacular.generators import SchemaGenerator
from drf_spectacular.plumbing import ( from drf_spectacular.plumbing import (
ResolvedComponent, ResolvedComponent,
build_array_type, build_array_type,
@ -9,6 +10,8 @@ from drf_spectacular.plumbing import (
from drf_spectacular.settings import spectacular_settings from drf_spectacular.settings import spectacular_settings
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from authentik.api.pagination import PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA
def build_standard_type(obj, **kwargs): def build_standard_type(obj, **kwargs):
"""Build a basic type with optional add owns.""" """Build a basic type with optional add owns."""
@ -36,15 +39,7 @@ VALIDATION_ERROR = build_object_type(
) )
def postprocess_schema_responses(result, generator, **kwargs): # noqa: W0613 def create_component(generator: SchemaGenerator, name, schema, type_=ResolvedComponent.SCHEMA):
"""Workaround to set a default response for endpoints.
Workaround suggested at
<https://github.com/tfranzel/drf-spectacular/issues/119#issuecomment-656970357>
for the missing drf-spectacular feature discussed in
<https://github.com/tfranzel/drf-spectacular/issues/101>.
"""
def create_component(name, schema, type_=ResolvedComponent.SCHEMA):
"""Register a component and return a reference to it.""" """Register a component and return a reference to it."""
component = ResolvedComponent( component = ResolvedComponent(
name=name, name=name,
@ -55,8 +50,19 @@ def postprocess_schema_responses(result, generator, **kwargs): # noqa: W0613
generator.registry.register_on_missing(component) generator.registry.register_on_missing(component)
return component return component
generic_error = create_component("GenericError", GENERIC_ERROR)
validation_error = create_component("ValidationError", VALIDATION_ERROR) def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs): # noqa: W0613
"""Workaround to set a default response for endpoints.
Workaround suggested at
<https://github.com/tfranzel/drf-spectacular/issues/119#issuecomment-656970357>
for the missing drf-spectacular feature discussed in
<https://github.com/tfranzel/drf-spectacular/issues/101>.
"""
create_component(generator, PAGINATION_COMPONENT_NAME, PAGINATION_SCHEMA)
generic_error = create_component(generator, "GenericError", GENERIC_ERROR)
validation_error = create_component(generator, "ValidationError", VALIDATION_ERROR)
for path in result["paths"].values(): for path in result["paths"].values():
for method in path.values(): for method in path.values():

View File

@ -15,7 +15,13 @@ from django.utils.http import urlencode
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_filters.filters import BooleanFilter, CharFilter, ModelMultipleChoiceFilter, UUIDFilter from django_filters.filters import (
BooleanFilter,
CharFilter,
ModelMultipleChoiceFilter,
MultipleChoiceFilter,
UUIDFilter,
)
from django_filters.filterset import FilterSet from django_filters.filterset import FilterSet
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import ( from drf_spectacular.utils import (
@ -300,11 +306,11 @@ class UsersFilter(FilterSet):
is_superuser = BooleanFilter(field_name="ak_groups", lookup_expr="is_superuser") is_superuser = BooleanFilter(field_name="ak_groups", lookup_expr="is_superuser")
uuid = UUIDFilter(field_name="uuid") uuid = UUIDFilter(field_name="uuid")
path = CharFilter( path = CharFilter(field_name="path")
field_name="path",
)
path_startswith = CharFilter(field_name="path", lookup_expr="startswith") path_startswith = CharFilter(field_name="path", lookup_expr="startswith")
type = MultipleChoiceFilter(field_name="type")
groups_by_name = ModelMultipleChoiceFilter( groups_by_name = ModelMultipleChoiceFilter(
field_name="ak_groups__name", field_name="ak_groups__name",
to_field_name="name", to_field_name="name",

View File

@ -13,6 +13,30 @@ import (
"goauthentik.io/internal/constants" "goauthentik.io/internal/constants"
) )
// Attempt to set basic auth based on user's attributes
func (a *Application) setAuthorizationHeader(headers http.Header, c *Claims) {
if !*a.proxyConfig.BasicAuthEnabled {
return
}
userAttributes := c.Proxy.UserAttributes
var ok bool
var password string
if password, ok = userAttributes[*a.proxyConfig.BasicAuthPasswordAttribute].(string); !ok {
password = ""
}
// Check if we should use email or a custom attribute as username
var username string
if username, ok = userAttributes[*a.proxyConfig.BasicAuthUserAttribute].(string); !ok {
username = c.Email
}
if username == "" && password == "" {
return
}
authVal := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
a.log.WithField("username", username).Trace("setting http basic auth")
headers.Set("Authorization", fmt.Sprintf("Basic %s", authVal))
}
func (a *Application) addHeaders(headers http.Header, c *Claims) { func (a *Application) addHeaders(headers http.Header, c *Claims) {
// https://goauthentik.io/docs/providers/proxy/proxy // https://goauthentik.io/docs/providers/proxy/proxy
headers.Set("X-authentik-username", c.PreferredUsername) headers.Set("X-authentik-username", c.PreferredUsername)
@ -33,22 +57,7 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) {
return return
} }
userAttributes := c.Proxy.UserAttributes userAttributes := c.Proxy.UserAttributes
// Attempt to set basic auth based on user's attributes a.setAuthorizationHeader(headers, c)
if *a.proxyConfig.BasicAuthEnabled {
var ok bool
var password string
if password, ok = userAttributes[*a.proxyConfig.BasicAuthPasswordAttribute].(string); !ok {
password = ""
}
// Check if we should use email or a custom attribute as username
var username string
if username, ok = userAttributes[*a.proxyConfig.BasicAuthUserAttribute].(string); !ok {
username = c.Email
}
authVal := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
a.log.WithField("username", username).Trace("setting http basic auth")
headers.Set("Authorization", fmt.Sprintf("Basic %s", authVal))
}
// Check if user has additional headers set that we should sent // Check if user has additional headers set that we should sent
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]interface{}); ok { if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]interface{}); ok {
a.log.WithField("headers", additionalHeaders).Trace("setting additional headers") a.log.WithField("headers", additionalHeaders).Trace("setting additional headers")

View File

@ -3,6 +3,7 @@ package application
import ( import (
"fmt" "fmt"
"math" "math"
"net/http"
"net/url" "net/url"
"os" "os"
"path" "path"
@ -37,7 +38,12 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
rs.SetMaxLength(math.MaxInt) rs.SetMaxLength(math.MaxInt)
rs.SetKeyPrefix(RedisKeyPrefix) rs.SetKeyPrefix(RedisKeyPrefix)
rs.Options.HttpOnly = true
if strings.ToLower(externalHost.Scheme) == "https" {
rs.Options.Secure = true
}
rs.Options.Domain = *p.CookieDomain rs.Options.Domain = *p.CookieDomain
rs.Options.SameSite = http.SameSiteLaxMode
a.log.Trace("using redis session backend") a.log.Trace("using redis session backend")
return rs return rs
} }
@ -51,7 +57,12 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk // Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
cs.MaxLength(math.MaxInt) cs.MaxLength(math.MaxInt)
cs.Options.HttpOnly = true
if strings.ToLower(externalHost.Scheme) == "https" {
cs.Options.Secure = true
}
cs.Options.Domain = *p.CookieDomain cs.Options.Domain = *p.CookieDomain
cs.Options.SameSite = http.SameSiteLaxMode
a.log.WithField("dir", dir).Trace("using filesystem session backend") a.log.WithField("dir", dir).Trace("using filesystem session backend")
return cs return cs
} }

2071
schema.yml

File diff suppressed because it is too large Load Diff

4735
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
"tsc:execute": "tsc --noEmit -p .", "tsc:execute": "tsc --noEmit -p .",
"tsc": "run-s build-locales tsc:execute", "tsc": "run-s build-locales tsc:execute",
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"storybook:build": "storybook build", "storybook:build": "node --max-old-space-size=4096 ./node_modules/.bin/storybook build",
"background-image": "sharp resize 2650 --mozjpeg -i src/assets/images/flow_background.jpg -o src/assets/images/flow_background.jpg" "background-image": "sharp resize 2650 --mozjpeg -i src/assets/images/flow_background.jpg -o src/assets/images/flow_background.jpg"
}, },
"dependencies": { "dependencies": {
@ -35,14 +35,14 @@
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.0", "@formatjs/intl-listformat": "^7.4.0",
"@fortawesome/fontawesome-free": "^6.4.2", "@fortawesome/fontawesome-free": "^6.4.2",
"@goauthentik/api": "^2023.6.1-1690455444", "@goauthentik/api": "^2023.6.1-1691266058",
"@lit-labs/context": "^0.3.3", "@lit-labs/context": "^0.3.3",
"@lit-labs/task": "^3.0.0", "@lit-labs/task": "^3.0.0",
"@lit/localize": "^0.11.4", "@lit/localize": "^0.11.4",
"@patternfly/elements": "^2.3.2", "@patternfly/elements": "^2.3.2",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^7.61.0", "@sentry/browser": "^7.61.1",
"@sentry/tracing": "^7.61.0", "@sentry/tracing": "^7.61.1",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"chart.js": "^4.3.3", "chart.js": "^4.3.3",
@ -77,11 +77,11 @@
"@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.1.2", "@rollup/plugin-typescript": "^11.1.2",
"@storybook/addon-essentials": "^7.1.1", "@storybook/addon-essentials": "^7.2.1",
"@storybook/addon-links": "^7.1.1", "@storybook/addon-links": "^7.2.1",
"@storybook/blocks": "^7.1.1", "@storybook/blocks": "^7.1.1",
"@storybook/web-components": "^7.1.0", "@storybook/web-components": "^7.2.1",
"@storybook/web-components-vite": "^7.1.1", "@storybook/web-components-vite": "^7.2.1",
"@trivago/prettier-plugin-sort-imports": "^4.2.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/chart.js": "^2.9.37", "@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.8", "@types/codemirror": "5.60.8",
@ -108,7 +108,7 @@
"rollup-plugin-postcss-lit": "^2.1.0", "rollup-plugin-postcss-lit": "^2.1.0",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"sharp-cli": "^4.1.1", "sharp-cli": "^4.1.1",
"storybook": "^7.1.1", "storybook": "^7.2.1",
"storybook-addon-mock": "^4.1.0", "storybook-addon-mock": "^4.1.0",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.6.1", "tslib": "^2.6.1",
@ -117,8 +117,8 @@
"vite-tsconfig-paths": "^4.2.0" "vite-tsconfig-paths": "^4.2.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/darwin-arm64": "^0.18.17", "@esbuild/darwin-arm64": "^0.18.18",
"@esbuild/linux-amd64": "^0.18.11", "@esbuild/linux-amd64": "^0.18.11",
"@esbuild/linux-arm64": "^0.18.17" "@esbuild/linux-arm64": "^0.18.18"
} }
} }

View File

@ -39,7 +39,7 @@ export class MemberSelectTable extends TableModal<User> {
columns(): TableColumn[] { columns(): TableColumn[] {
return [ return [
new TableColumn(msg("Name"), "username"), new TableColumn(msg("Name"), "username"),
new TableColumn(msg("Active"), "active"), new TableColumn(msg("Active"), "is_active"),
new TableColumn(msg("Last login"), "last_login"), new TableColumn(msg("Last login"), "last_login"),
]; ];
} }

View File

@ -49,6 +49,8 @@ export class SystemTaskListPage extends TablePage<Task> {
startIndex: 1, startIndex: 1,
endIndex: tasks.length, endIndex: tasks.length,
current: page, current: page,
next: 0,
previous: 0,
}, },
results: tasks, results: tasks,
}; };

View File

@ -31,7 +31,14 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { CapabilitiesEnum, CoreApi, Group, ResponseError, User } from "@goauthentik/api"; import {
CapabilitiesEnum,
CoreApi,
CoreUsersListTypeEnum,
Group,
ResponseError,
User,
} from "@goauthentik/api";
@customElement("ak-user-related-add") @customElement("ak-user-related-add")
export class RelatedUserAdd extends Form<{ users: number[] }> { export class RelatedUserAdd extends Form<{ users: number[] }> {
@ -127,10 +134,8 @@ export class RelatedUserList extends Table<User> {
pageSize: (await uiConfig()).pagination.perPage, pageSize: (await uiConfig()).pagination.perPage,
search: this.search || "", search: this.search || "",
groupsByPk: this.targetGroup ? [this.targetGroup.pk] : [], groupsByPk: this.targetGroup ? [this.targetGroup.pk] : [],
attributes: this.hideServiceAccounts type: this.hideServiceAccounts
? JSON.stringify({ ? [CoreUsersListTypeEnum.External, CoreUsersListTypeEnum.Internal]
"goauthentik.io/user/service-account__isnull": true,
})
: undefined, : undefined,
}); });
} }
@ -138,7 +143,7 @@ export class RelatedUserList extends Table<User> {
columns(): TableColumn[] { columns(): TableColumn[] {
return [ return [
new TableColumn(msg("Name"), "username"), new TableColumn(msg("Name"), "username"),
new TableColumn(msg("Active"), "active"), new TableColumn(msg("Active"), "is_active"),
new TableColumn(msg("Last login"), "last_login"), new TableColumn(msg("Last login"), "last_login"),
new TableColumn(msg("Actions")), new TableColumn(msg("Actions")),
]; ];

View File

@ -94,7 +94,7 @@ export class UserListPage extends TablePage<User> {
columns(): TableColumn[] { columns(): TableColumn[] {
return [ return [
new TableColumn(msg("Name"), "username"), new TableColumn(msg("Name"), "username"),
new TableColumn(msg("Active"), "active"), new TableColumn(msg("Active"), "is_active"),
new TableColumn(msg("Last login"), "last_login"), new TableColumn(msg("Last login"), "last_login"),
new TableColumn(msg("Actions")), new TableColumn(msg("Actions")),
]; ];

View File

@ -264,7 +264,7 @@ select[multiple] option:checked {
filter: invert(1); filter: invert(1);
} }
.pf-c-login__main-footer-band { .pf-c-login__main-footer-band {
background-color: var(--ak-dark-background-lighter); --pf-c-login__main-footer-band--BackgroundColor: var(--ak-dark-background-lighter);
color: var(--ak-dark-foreground); color: var(--ak-dark-foreground);
} }
.form-control-static { .form-control-static {

View File

@ -47,6 +47,8 @@ export class DeleteObjectsTable<T> extends Table<T> {
totalPages: 1, totalPages: 1,
startIndex: 1, startIndex: 1,
endIndex: this.objects.length, endIndex: this.objects.length,
next: 0,
previous: 0,
}, },
results: this.objects, results: this.objects,
}); });

View File

@ -7,7 +7,6 @@ import "@goauthentik/elements/chips/Chip";
import "@goauthentik/elements/chips/ChipGroup"; import "@goauthentik/elements/chips/ChipGroup";
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch"; import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
import "@goauthentik/elements/table/TablePagination"; import "@goauthentik/elements/table/TablePagination";
import { Pagination } from "@goauthentik/elements/table/TablePagination";
import "@goauthentik/elements/table/TableSearch"; import "@goauthentik/elements/table/TableSearch";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
@ -24,6 +23,8 @@ import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { Pagination } from "@goauthentik/api";
export class TableColumn { export class TableColumn {
title: string; title: string;
orderBy?: string; orderBy?: string;

View File

@ -8,17 +8,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css"; import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
export interface Pagination { import { Pagination } from "@goauthentik/api";
next?: number;
previous?: number;
count: number;
current: number;
totalPages: number;
startIndex: number;
endIndex: number;
}
@customElement("ak-table-pagination") @customElement("ak-table-pagination")
export class TablePagination extends AKElement { export class TablePagination extends AKElement {

View File

@ -28,6 +28,8 @@ export class UserDeviceList extends MFADevicesPage {
totalPages: 1, totalPages: 1,
startIndex: 1, startIndex: 1,
endIndex: res.length, endIndex: res.length,
next: 0,
previous: 0,
}, },
results: res, results: res,
}; };

View File

@ -49,6 +49,8 @@ export class MFADevicesPage extends Table<Device> {
totalPages: 1, totalPages: 1,
startIndex: 1, startIndex: 1,
endIndex: devices.length, endIndex: devices.length,
next: 0,
previous: 0,
}, },
results: devices, results: devices,
}; };

View File

@ -8,7 +8,7 @@ The most common types are:
- [**Procedural**](./procedural.md): these are How To docs, the HOW information, with step-by-step instructions for accomplishing a task. This is what most people are looking for when they open the docs... and best practice is to separate the procedural docs from long, lengthy conceptual or reference docs. - [**Procedural**](./procedural.md): these are How To docs, the HOW information, with step-by-step instructions for accomplishing a task. This is what most people are looking for when they open the docs... and best practice is to separate the procedural docs from long, lengthy conceptual or reference docs.
- **Conceptual**: these docs provide the WHY information, and explain when to use a feature (or when not to!), and general concepts behind the fature or functioanlity. - **Conceptual**: these docs provide the WHY information, and explain when to use a feature (or when not to!), and general concepts behind the feature or functioanlity.
- **Reference**: this is typically tables or lists of reference information, such as configuration values, or most commmonly APIs. - **Reference**: this is typically tables or lists of reference information, such as configuration values, or most commmonly APIs.

View File

@ -2,9 +2,9 @@
title: "Procedural topic" title: "Procedural topic"
--- ---
Use a title that focuses on the task you are writing about... for example, "Add a new Group" or "Edit user profiles". For procedural docs, there should be a verb in the tilte, and usually the noun (the thing you are working on). For the title (and all headings) use the infinitive form of the verb (i.e. "add") not the gerund form (i.e. "adding"). Use a title that focuses on the task you are writing about... for example, "Add a new Group" or "Edit user profiles". For procedural docs, there should be a verb in the title, and usually the noun (the component or object you are working on). For the title (and all headings) use the infinitive form of the verb (i.e. "add") not the gerund form (i.e. "adding").
In this first section write one or two sentences about the task. Keep it brief; if it goes on too long, then create a separate conceptual topic, in a separate `.md` file. We don't want readers to have to scroll through paragraphs of conceptual info before they get to Step 1. In this first section, right after the title, write one or two sentences about the task. Keep it brief; if it goes on too long, then create a separate conceptual topic, in a separate `.md` file. We don't want readers to have to scroll through paragraphs of conceptual info before they get to Step 1.
## Prerequisites (optional section) ## Prerequisites (optional section)
@ -16,7 +16,7 @@ If the task is quite long or complex, it might be good to add a bullet list of t
## first several group steps ## first several group steps
If the task involves a lot of steps, try to group them into simalr steps and have a Head3 or Hedad4 title for each group. If the task involves a lot of steps, try to group them into similar steps and have a Head3 or Head4 title for each group.
In this section, help the reader get oriented... where do they need to be (i.e. in the GUI, on a CLI, etc). In this section, help the reader get oriented... where do they need to be (i.e. in the GUI, on a CLI, etc).
@ -34,4 +34,4 @@ Use screenshots sparingly, only for complex UIs where it is difficult to describ
## verify the steps ## verify the steps
Whenever possible, it is useful to add verification steps at the end of a procedural topic. For example, if the procedural was about installing a product, use this section to tell them how they can verify that the install was successful Whenever possible, it is useful to add verification steps at the end of a procedural topic. For example, if the procedural was about installing a product, use this section to tell them how they can verify that the install was successful.

View File

@ -22,11 +22,11 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-toggle": "^4.1.3", "react-toggle": "^4.1.3",
"react-tooltip": "^5.19.0", "react-tooltip": "^5.20.0",
"remark-github": "^11.2.4" "remark-github": "^11.2.4"
}, },
"devDependencies": { "devDependencies": {
"prettier": "3.0.0" "prettier": "3.0.1"
} }
}, },
"node_modules/@algolia/autocomplete-core": { "node_modules/@algolia/autocomplete-core": {
@ -10008,9 +10008,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
"dev": true, "dev": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
@ -10591,9 +10591,9 @@
} }
}, },
"node_modules/react-tooltip": { "node_modules/react-tooltip": {
"version": "5.19.0", "version": "5.20.0",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.19.0.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.20.0.tgz",
"integrity": "sha512-NSUk77GMpxYKHFKJVNHL++QQXRuH2QW1qDrXPtJnp2s/MJvUnU73N5TTADwDyrO2+xGlr0xHhjvQphkF60cMEA==", "integrity": "sha512-LWBIHEZjwDW9ZJ/Dn2xeZrsz+WKMii61CIsx2XPfs1IiIRnWyvKJXrgy6uEGOXYvrnCd4jiEvurn8Y+zJ1bw5Q==",
"dependencies": { "dependencies": {
"@floating-ui/dom": "^1.0.0", "@floating-ui/dom": "^1.0.0",
"classnames": "^2.3.0" "classnames": "^2.3.0"
@ -20927,9 +20927,9 @@
"integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
}, },
"prettier": { "prettier": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
"dev": true "dev": true
}, },
"pretty-error": { "pretty-error": {
@ -21361,9 +21361,9 @@
} }
}, },
"react-tooltip": { "react-tooltip": {
"version": "5.19.0", "version": "5.20.0",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.19.0.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.20.0.tgz",
"integrity": "sha512-NSUk77GMpxYKHFKJVNHL++QQXRuH2QW1qDrXPtJnp2s/MJvUnU73N5TTADwDyrO2+xGlr0xHhjvQphkF60cMEA==", "integrity": "sha512-LWBIHEZjwDW9ZJ/Dn2xeZrsz+WKMii61CIsx2XPfs1IiIRnWyvKJXrgy6uEGOXYvrnCd4jiEvurn8Y+zJ1bw5Q==",
"requires": { "requires": {
"@floating-ui/dom": "^1.0.0", "@floating-ui/dom": "^1.0.0",
"classnames": "^2.3.0" "classnames": "^2.3.0"

View File

@ -29,7 +29,7 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-toggle": "^4.1.3", "react-toggle": "^4.1.3",
"react-tooltip": "^5.19.0", "react-tooltip": "^5.20.0",
"remark-github": "^11.2.4" "remark-github": "^11.2.4"
}, },
"browserslist": { "browserslist": {
@ -45,6 +45,6 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"prettier": "3.0.0" "prettier": "3.0.1"
} }
} }