jojo/templates/user/settings/access_token_edit.tmpl
Mathieu Fenniak 35b872f383 feat(ui): create repo-specific access tokens (#11696)
Adds a user interface for creating repo-specific access tokens (#11311).  When the new option "Specific repositories" is selected, a search option appears.  Each repository in the search result has an "Add" button to include it on the access token, and once included, a repository can be removed with the "Remove" button.  This is a JS-free form.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests for Go changes

(can be removed for JavaScript changes)

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Tests for JavaScript changes

- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [x] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/
README.md#end-to-end-tests)).
    - Technically there are no "JavaScript changes" in this PR, but e2e tests were added for browser interaction testing.

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
    - TODO: planning to create documentation in https://forgejo.org/docs/next/user/token-scope/; there is none for public only tokens but I think this seems like a good place to add both.
- [ ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11696
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2026-03-23 15:29:08 +01:00

180 lines
8.8 KiB
Go HTML Template

{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.generate_new_token"}}
</h4>
<div class="ui attached bottom segment">
<form id="scoped-access-form" class="ui form" action="{{.Link}}" method="post">
<div class="required field {{if .Err_Name}}error{{end}}">
<label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label>
<input id="name" name="name" value="{{.name}}" {{if eq .Autofocus "name"}}autofocus{{end}} required maxlength="255">
</div>
<div class="field">
<fieldset>
<label>{{ctx.Locale.Tr "settings.repo_and_org_access"}}</label>
<div class="field">
<div class="field ui radio checkbox">
<input id="resource-all" class="enable-system" type="radio" name="resource" value="all" {{if eq .resource "all"}}checked{{end}}>
<label for="resource-all">{{ctx.Locale.Tr "settings.permissions_access_all"}}</label>
<p class="help">{{ctx.Locale.Tr "settings.access_token.resource_all_help"}}</p>
</div>
</div>
<div class="field">
<div class="field ui radio checkbox">
<input id="resource-public-only" class="enable-system" type="radio" name="resource" value="public-only" {{if eq .resource "public-only"}}checked{{end}}>
<label for="resource-public-only">{{ctx.Locale.Tr "settings.permissions_public_only"}}</label>
<p class="help">
{{ctx.Locale.Tr "settings.access_token.resource_public_only_help"}}
{{if $.IsAdmin}}{{ctx.Locale.Tr "settings.access_token.admin_disabled"}}{{end}}
</p>
</div>
</div>
<div class="field">
<div class="field ui radio checkbox">
<input id="resource-repo-specific" class="enable-system" type="radio" name="resource" value="repo-specific" {{if eq .resource "repo-specific"}}checked{{end}}>
<label for="resource-repo-specific">{{ctx.Locale.Tr "settings.permissions_access_specific_repositories"}}</label>
<p class="help">
{{ctx.Locale.Tr "settings.access_token.resource_specific_repo_help"}}
{{if $.IsAdmin}}{{ctx.Locale.Tr "settings.access_token.admin_disabled"}}{{end}}
</p>
</div>
</div>
</fieldset>
</div>
<div id="repo-selector-wrapper" role="group" class="field tw-mt-4">
<div id="repo-selector" class="tw-flex tw-flex-wrap tw-gap-8">
<!-- left-hand side: repo list from a search -->
<div class="ui tab active list tw-flex-1">
<h5>
{{ctx.Locale.Tr "settings.access_token.available_repositories"}}
</h5>
<div class="ui small fluid action left icon input tw-mb-3">
<input type="search" name="repo_search" spellcheck="false" {{if eq .Autofocus "search"}}autofocus{{end}} placeholder="{{ctx.Locale.Tr "search.repo_kind"}}" value="{{.repo_search}}">
<i class="icon">{{svg "octicon-search" 16}}</i>
<button class="ui small icon button" aria-label="{{ctx.Locale.Tr "search.search"}}" type="submit" name="set_page" value="1" formnovalidate="true" formmethod="get">
{{svg "octicon-search" 16}}
</button>
</div>
{{if eq (len .Repos) 0}}
{{ctx.Locale.Tr "settings.access_token.no_repositories_found"}}
{{else}}
<div class="tw-grid tw-items-center" style="grid-template-columns: min-content 1fr min-content;">
{{range .Repos}}
{{template "user/settings/repo_icon" .}}
<div class="text truncate">
{{.FullName}}
</div>
<button class="ui primary button tw-ml-2 tw-my-1 tiny" type="submit" aria-label="{{ctx.Locale.Tr "repo.editor.add" .FullName}}" formnovalidate="true" name="add_selected_repo" value="{{.FullName}}" formmethod="get">
{{ctx.Locale.Tr "add"}}
</button>
{{end}}
</div>
{{end}}
{{/* Can't use base/paginate template include here because all the pagination links in
that template are simple <a href=...> links. Here, we need to turn them into form
buttons so that we can submit the current form. If a user just changed a value (eg. set
the token name, changed a selected permission) and then clicked a pagination button, the
new value that they changed needs to be submitted. base/paginate would allow preserving
old values from before the change, but not new updates. Implementing here also allows
the use of smaller styling. */}}
{{with .Page.Paginater}}
<input type="hidden" name="page" value="{{.Current}}">
<div class="center page buttons">
<div class="ui borderless pagination menu mini">
<button class="item navigation {{if .IsFirst}}disabled{{end}}" type="submit" formnovalidate="true" name="set_page" value="1" formmethod="get">
{{svg "gitea-double-chevron-left" 16 "tw-mr-1"}}
<span class="navigation_label">{{ctx.Locale.Tr "admin.first_page"}}</span>
</button>
<button class="item navigation {{if not .HasPrevious}}disabled{{end}}" type="submit" formnovalidate="true" name="set_page" value="{{.Previous}}" formmethod="get">
{{svg "octicon-chevron-left" 16 "tw-mr-1"}}
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.previous"}}</span>
</button>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<button class="item navigation {{if .IsCurrent}}active{{end}}" type="submit" formnovalidate="true" name="set_page" value="{{.Num}}" formmethod="get">
{{.Num}}
</button>
{{end}}
{{end}}
<button class="item navigation {{if not .HasNext}}disabled{{end}}" type="submit" formnovalidate="true" name="set_page" value="{{.Next}}" formmethod="get">
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.next"}}</span>
{{svg "octicon-chevron-right" 16 "tw-ml-1"}}
</button>
<button class="item navigation {{if .IsLast}}disabled{{end}}" type="submit" formnovalidate="true" name="set_page" value="{{.TotalPages}}" formmethod="get">
<span class="navigation_label">{{ctx.Locale.Tr "admin.last_page"}}</span>
{{svg "gitea-double-chevron-right" 16 "tw-ml-1"}}
</button>
</div>
</div>
{{end}}
</div>
<!-- right-hand side: selected repositories -->
<div class="tw-flex-1">
<h5>{{ctx.Locale.TrPluralString (len .SelectedRepos) "settings.access_token.selected_repositories" (len .SelectedRepos)}}</h5>
{{if eq (len .SelectedRepos) 0}}
<div class="tw-my-2">
{{ctx.Locale.Tr "settings.access_token.no_repositories_selected"}}
</div>
{{else}}
<div class="tw-grid tw-items-center tw-auto-rows-min" style="grid-template-columns: min-content 1fr min-content;">
{{range .SelectedRepos}}
<input type="hidden" name="selected_repo" value="{{.FullName}}">
{{template "user/settings/repo_icon" .}}
<div class="text tw-text-nowrap"> <!-- nowrap, !truncate, ensures that this can be fully seen and ensures this side of the control has an appropriate min-width -->
{{.FullName}}
</div>
<button class="ui button tw-ml-2 tw-my-1 tiny" type="submit" aria-label="{{ctx.Locale.Tr "settings.access_token.remove" .FullName}}" formnovalidate="true" name="remove_selected_repo" value="{{.FullName}}" formmethod="get">
{{ctx.Locale.Tr "remove"}}
</button>
{{end}}
</div>
{{end}}
</div>
</div>
</div>
<div class="field">
<label>
{{ctx.Locale.Tr "settings.select_permissions"}}
</label>
<p class="activity meta">
<p>{{ctx.Locale.Tr "settings.access_token_desc" (printf "%s/api/swagger" AppSubUrl) "https://forgejo.org/docs/latest/user/token-scope/"}}</p>
</p>
{{range .Categories}}
<div class="field tw-pl-1 tw-pb-1 access-token-category">
<label class="category-label" for="access-token-scope-{{.}}">
{{.}}
</label>
<div class="gitea-select">
<select class="ui selection access-token-select" name="scope" id="access-token-scope-{{.}}">
<option value="">
{{ctx.Locale.Tr "settings.permission_no_access"}}
</option>
<option value="read:{{.}}" {{if (SliceUtils.Contains $.scope (printf "read:%s" .))}} selected {{end}}>
{{ctx.Locale.Tr "settings.permission_read"}}
</option>
<option value="write:{{.}}" {{if (SliceUtils.Contains $.scope (printf "write:%s" .))}} selected {{end}}>
{{ctx.Locale.Tr "settings.permission_write"}}
</option>
</select>
</div>
</div>
{{end}}
</div>
<button id="scoped-access-submit" class="ui primary button">
{{ctx.Locale.Tr "settings.generate_token"}}
</button>
</form>
</div>
</div>
{{template "user/settings/layout_footer" .}}