From 71ca3067bcc6c7c7772d38fc7590505c8c7148ed Mon Sep 17 00:00:00 2001 From: Jason Song Date: Fri, 23 Dec 2022 19:35:43 +0800 Subject: [PATCH 1/5] Check primary keys for all tables and drop ForeignReference (#21721) Some dbs require that all tables have primary keys, see - #16802 - #21086 We can add a test to keep it from being broken again. Edit: ~Added missing primary key for `ForeignReference`~ Dropped the `ForeignReference` table to satisfy the check, so it closes #21086. More context can be found in comments. Signed-off-by: Andrew Thornton Co-authored-by: zeripath --- models/db/engine_test.go | 33 ++++++++++++ models/fixtures/foreign_reference.yml | 1 - models/foreignreference/error.go | 52 ------------------ models/foreignreference/foreignreference.go | 31 ----------- models/issues/issue.go | 60 ++------------------- models/issues/issue_test.go | 34 ------------ models/migrate.go | 7 --- models/migrate_test.go | 12 ----- models/migrations/migrations.go | 2 + models/migrations/v1_19/v237.go | 15 ++++++ services/migrations/gitea_uploader.go | 11 ---- 11 files changed, 55 insertions(+), 203 deletions(-) delete mode 100644 models/fixtures/foreign_reference.yml delete mode 100644 models/foreignreference/error.go delete mode 100644 models/foreignreference/foreignreference.go create mode 100644 models/migrations/v1_19/v237.go diff --git a/models/db/engine_test.go b/models/db/engine_test.go index fa1ac08a1738f..551436677782a 100644 --- a/models/db/engine_test.go +++ b/models/db/engine_test.go @@ -12,6 +12,8 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" + _ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys + "github.com/stretchr/testify/assert" ) @@ -51,3 +53,34 @@ func TestDeleteOrphanedObjects(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, countBefore, countAfter) } + +func TestPrimaryKeys(t *testing.T) { + // Some dbs require that all tables have primary keys, see + // https://github.com/go-gitea/gitea/issues/21086 + // https://github.com/go-gitea/gitea/issues/16802 + // To avoid creating tables without primary key again, this test will check them. + // Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called. + + beans, err := db.NamesToBean() + if err != nil { + t.Fatal(err) + } + + whitelist := map[string]string{ + "the_table_name_to_skip_checking": "Write a note here to explain why", + } + + for _, bean := range beans { + table, err := db.TableInfo(bean) + if err != nil { + t.Fatal(err) + } + if why, ok := whitelist[table.Name]; ok { + t.Logf("ignore %q because %q", table.Name, why) + continue + } + if len(table.PrimaryKeys) == 0 { + t.Errorf("table %q has no primary key", table.Name) + } + } +} diff --git a/models/fixtures/foreign_reference.yml b/models/fixtures/foreign_reference.yml deleted file mode 100644 index ca780a73aa0c1..0000000000000 --- a/models/fixtures/foreign_reference.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/foreignreference/error.go b/models/foreignreference/error.go deleted file mode 100644 index 07ed1052a63bf..0000000000000 --- a/models/foreignreference/error.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022 Gitea. All rights reserved. -// SPDX-License-Identifier: MIT - -package foreignreference - -import ( - "fmt" - - "code.gitea.io/gitea/modules/util" -) - -// ErrLocalIndexNotExist represents a "LocalIndexNotExist" kind of error. -type ErrLocalIndexNotExist struct { - RepoID int64 - ForeignIndex int64 - Type string -} - -// ErrLocalIndexNotExist checks if an error is a ErrLocalIndexNotExist. -func IsErrLocalIndexNotExist(err error) bool { - _, ok := err.(ErrLocalIndexNotExist) - return ok -} - -func (err ErrLocalIndexNotExist) Error() string { - return fmt.Sprintf("repository %d has no LocalIndex for ForeignIndex %d of type %s", err.RepoID, err.ForeignIndex, err.Type) -} - -func (err ErrLocalIndexNotExist) Unwrap() error { - return util.ErrNotExist -} - -// ErrForeignIndexNotExist represents a "ForeignIndexNotExist" kind of error. -type ErrForeignIndexNotExist struct { - RepoID int64 - LocalIndex int64 - Type string -} - -// ErrForeignIndexNotExist checks if an error is a ErrForeignIndexNotExist. -func IsErrForeignIndexNotExist(err error) bool { - _, ok := err.(ErrForeignIndexNotExist) - return ok -} - -func (err ErrForeignIndexNotExist) Error() string { - return fmt.Sprintf("repository %d has no ForeignIndex for LocalIndex %d of type %s", err.RepoID, err.LocalIndex, err.Type) -} - -func (err ErrForeignIndexNotExist) Unwrap() error { - return util.ErrNotExist -} diff --git a/models/foreignreference/foreignreference.go b/models/foreignreference/foreignreference.go deleted file mode 100644 index 2d2ad04c5a142..0000000000000 --- a/models/foreignreference/foreignreference.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022 Gitea. All rights reserved. -// SPDX-License-Identifier: MIT - -package foreignreference - -import ( - "code.gitea.io/gitea/models/db" -) - -// Type* are valid values for the Type field of ForeignReference -const ( - TypeIssue = "issue" - TypePullRequest = "pull_request" - TypeComment = "comment" - TypeReview = "review" - TypeReviewComment = "review_comment" - TypeRelease = "release" -) - -// ForeignReference represents external references -type ForeignReference struct { - // RepoID is the first column in all indices. now we only need 2 indices: (repo, local) and (repo, foreign, type) - RepoID int64 `xorm:"UNIQUE(repo_foreign_type) INDEX(repo_local)" ` - LocalIndex int64 `xorm:"INDEX(repo_local)"` // the resource key inside Gitea, it can be IssueIndex, or some model ID. - ForeignIndex string `xorm:"INDEX UNIQUE(repo_foreign_type)"` - Type string `xorm:"VARCHAR(16) INDEX UNIQUE(repo_foreign_type)"` -} - -func init() { - db.RegisterModel(new(ForeignReference)) -} diff --git a/models/issues/issue.go b/models/issues/issue.go index fc93fcf45465c..f45e635c0ecea 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -9,11 +9,9 @@ import ( "fmt" "regexp" "sort" - "strconv" "strings" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/foreignreference" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -136,12 +134,11 @@ type Issue struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` ClosedUnix timeutil.TimeStamp `xorm:"INDEX"` - Attachments []*repo_model.Attachment `xorm:"-"` - Comments []*Comment `xorm:"-"` - Reactions ReactionList `xorm:"-"` - TotalTrackedTime int64 `xorm:"-"` - Assignees []*user_model.User `xorm:"-"` - ForeignReference *foreignreference.ForeignReference `xorm:"-"` + Attachments []*repo_model.Attachment `xorm:"-"` + Comments []*Comment `xorm:"-"` + Reactions ReactionList `xorm:"-"` + TotalTrackedTime int64 `xorm:"-"` + Assignees []*user_model.User `xorm:"-"` // IsLocked limits commenting abilities to users on an issue // with write access @@ -321,29 +318,6 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) { return nil } -func (issue *Issue) loadForeignReference(ctx context.Context) (err error) { - if issue.ForeignReference != nil { - return nil - } - reference := &foreignreference.ForeignReference{ - RepoID: issue.RepoID, - LocalIndex: issue.Index, - Type: foreignreference.TypeIssue, - } - has, err := db.GetEngine(ctx).Get(reference) - if err != nil { - return err - } else if !has { - return foreignreference.ErrForeignIndexNotExist{ - RepoID: issue.RepoID, - LocalIndex: issue.Index, - Type: foreignreference.TypeIssue, - } - } - issue.ForeignReference = reference - return nil -} - // LoadMilestone load milestone of this issue. func (issue *Issue) LoadMilestone(ctx context.Context) (err error) { if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 { @@ -406,10 +380,6 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { } } - if err = issue.loadForeignReference(ctx); err != nil && !foreignreference.IsErrForeignIndexNotExist(err) { - return err - } - return issue.loadReactions(ctx) } @@ -1097,26 +1067,6 @@ func GetIssueByIndex(repoID, index int64) (*Issue, error) { return issue, nil } -// GetIssueByForeignIndex returns raw issue by foreign ID -func GetIssueByForeignIndex(ctx context.Context, repoID, foreignIndex int64) (*Issue, error) { - reference := &foreignreference.ForeignReference{ - RepoID: repoID, - ForeignIndex: strconv.FormatInt(foreignIndex, 10), - Type: foreignreference.TypeIssue, - } - has, err := db.GetEngine(ctx).Get(reference) - if err != nil { - return nil, err - } else if !has { - return nil, foreignreference.ErrLocalIndexNotExist{ - RepoID: repoID, - ForeignIndex: foreignIndex, - Type: foreignreference.TypeIssue, - } - } - return GetIssueByIndex(repoID, reference.LocalIndex) -} - // GetIssueWithAttrsByIndex returns issue by index in a repository. func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) { issue, err := GetIssueByIndex(repoID, index) diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 6764a9e6260ef..de1da19ab9fad 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -7,13 +7,11 @@ import ( "context" "fmt" "sort" - "strconv" "sync" "testing" "time" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/foreignreference" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" @@ -501,38 +499,6 @@ func TestCorrectIssueStats(t *testing.T) { assert.EqualValues(t, issueStats.OpenCount, issueAmount) } -func TestIssueForeignReference(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}) - assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive - - // it is fine for an issue to not have a foreign reference - err := issue.LoadAttributes(db.DefaultContext) - assert.NoError(t, err) - assert.Nil(t, issue.ForeignReference) - - var foreignIndex int64 = 12345 - _, err = issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) - assert.True(t, foreignreference.IsErrLocalIndexNotExist(err)) - - err = db.Insert(db.DefaultContext, &foreignreference.ForeignReference{ - LocalIndex: issue.Index, - ForeignIndex: strconv.FormatInt(foreignIndex, 10), - RepoID: issue.RepoID, - Type: foreignreference.TypeIssue, - }) - assert.NoError(t, err) - - err = issue.LoadAttributes(db.DefaultContext) - assert.NoError(t, err) - - assert.EqualValues(t, issue.ForeignReference.ForeignIndex, strconv.FormatInt(foreignIndex, 10)) - - found, err := issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) - assert.NoError(t, err) - assert.EqualValues(t, found.Index, issue.Index) -} - func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) miles := issues_model.MilestoneList{ diff --git a/models/migrate.go b/models/migrate.go index b1b5568126481..82cacd4a75b22 100644 --- a/models/migrate.go +++ b/models/migrate.go @@ -83,13 +83,6 @@ func insertIssue(ctx context.Context, issue *issues_model.Issue) error { } } - if issue.ForeignReference != nil { - issue.ForeignReference.LocalIndex = issue.Index - if _, err := sess.Insert(issue.ForeignReference); err != nil { - return err - } - } - return nil } diff --git a/models/migrate_test.go b/models/migrate_test.go index 48cd905e4c0d9..42102f9a7d22f 100644 --- a/models/migrate_test.go +++ b/models/migrate_test.go @@ -4,11 +4,9 @@ package models import ( - "strconv" "testing" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/foreignreference" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -48,7 +46,6 @@ func assertCreateIssues(t *testing.T, isPull bool) { UserID: owner.ID, } - foreignIndex := int64(12345) title := "issuetitle1" is := &issues_model.Issue{ RepoID: repo.ID, @@ -62,20 +59,11 @@ func assertCreateIssues(t *testing.T, isPull bool) { IsClosed: true, Labels: []*issues_model.Label{label}, Reactions: []*issues_model.Reaction{reaction}, - ForeignReference: &foreignreference.ForeignReference{ - ForeignIndex: strconv.FormatInt(foreignIndex, 10), - RepoID: repo.ID, - Type: foreignreference.TypeIssue, - }, } err := InsertIssues(is) assert.NoError(t, err) i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: title}) - assert.Nil(t, i.ForeignReference) - err = i.LoadAttributes(db.DefaultContext) - assert.NoError(t, err) - assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex) unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: owner.ID, IssueID: i.ID}) } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 591bfa3e86a63..9d9c8f5165e47 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -444,6 +444,8 @@ var migrations = []Migration{ NewMigration("Add index for access_token", v1_19.AddIndexForAccessToken), // v236 -> v237 NewMigration("Create secrets table", v1_19.CreateSecretsTable), + // v237 -> v238 + NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go new file mode 100644 index 0000000000000..b23c765aa5aac --- /dev/null +++ b/models/migrations/v1_19/v237.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_19 //nolint + +import ( + "xorm.io/xorm" +) + +func DropForeignReferenceTable(x *xorm.Engine) error { + // Drop the table introduced in `v211`, it's considered badly designed and doesn't look like to be used. + // See: https://github.com/go-gitea/gitea/issues/21086#issuecomment-1318217453 + type ForeignReference struct{} + return x.DropTables(new(ForeignReference)) +} diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index f3848e616cccd..23aa4ac2ca9d6 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/foreignreference" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -403,16 +402,6 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error { Labels: labels, CreatedUnix: timeutil.TimeStamp(issue.Created.Unix()), UpdatedUnix: timeutil.TimeStamp(issue.Updated.Unix()), - ForeignReference: &foreignreference.ForeignReference{ - LocalIndex: issue.GetLocalIndex(), - ForeignIndex: strconv.FormatInt(issue.GetForeignIndex(), 10), - RepoID: g.repo.ID, - Type: foreignreference.TypeIssue, - }, - } - - if is.ForeignReference.ForeignIndex == "0" { - is.ForeignReference.ForeignIndex = strconv.FormatInt(is.Index, 10) } if err := g.remapUser(issue, &is); err != nil { From 2cf0cf0de1fc298f1b44a0452c8dbb2c2f9dd71c Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 23 Dec 2022 17:03:11 +0100 Subject: [PATCH 2/5] JS refactors (#22227) - Replace all default exports with named exports, except for Vue SFCs - Remove names from Vue SFCs, they are automatically inferred from the filename - Misc whitespace-related tweaks --- web_src/js/components/ActivityHeatmap.vue | 1 - web_src/js/components/ContextPopup.vue | 10 +-------- web_src/js/components/DiffFileList.vue | 6 ----- web_src/js/components/DiffFileTree.vue | 6 +---- web_src/js/components/DiffFileTreeItem.vue | 7 +----- .../js/components/PullRequestMergeForm.vue | 12 +--------- web_src/js/features/clipboard.js | 2 +- web_src/js/features/colorpicker.js | 2 +- web_src/js/features/common-global.js | 2 +- web_src/js/features/comp/ColorPicker.js | 2 +- web_src/js/features/comp/EasyMDE.js | 2 +- web_src/js/features/comp/SearchUserBox.js | 1 - web_src/js/features/comp/WebHookEditor.js | 1 + web_src/js/features/contextpopup.js | 2 +- web_src/js/features/copycontent.js | 1 + web_src/js/features/dropzone.js | 2 +- web_src/js/features/emoji.js | 3 +-- web_src/js/features/file-fold.js | 1 - web_src/js/features/formatting.js | 1 - web_src/js/features/heatmap.js | 3 ++- web_src/js/features/imagediff.js | 2 +- web_src/js/features/repo-diff-filetree.js | 2 +- web_src/js/features/repo-findfile.js | 2 +- web_src/js/features/repo-graph.js | 2 +- web_src/js/features/repo-issue-pr-form.js | 2 +- web_src/js/features/repo-issue.js | 2 +- web_src/js/features/repo-legacy.js | 21 +++++++----------- web_src/js/features/repo-migration.js | 2 +- web_src/js/features/repo-projects.js | 2 +- web_src/js/features/repo-release.js | 2 +- web_src/js/features/serviceworker.js | 2 +- web_src/js/features/tablesort.js | 2 +- web_src/js/features/tribute.js | 2 +- web_src/js/index.js | 22 +++++++++---------- web_src/js/svg.js | 2 -- 35 files changed, 47 insertions(+), 89 deletions(-) diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue index 6cd72a8bf75de..d0f81c586c628 100644 --- a/web_src/js/components/ActivityHeatmap.vue +++ b/web_src/js/components/ActivityHeatmap.vue @@ -18,7 +18,6 @@ import {CalendarHeatmap} from 'vue3-calendar-heatmap'; export default { - name: 'ActivityHeatmap', components: {CalendarHeatmap}, props: { values: { diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index 0b086690a9129..07c73ff5cf152 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -46,19 +46,13 @@ const luminance = (colorString) => { const luminanceThreshold = 0.179; export default { - name: 'ContextPopup', - - components: { - SvgIcon, - }, - + components: {SvgIcon}, data: () => ({ loading: false, issue: null, i18nErrorOccurred: i18n.error_occurred, i18nErrorMessage: null, }), - computed: { createdAt() { return new Date(this.issue.created_at).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'}); @@ -107,7 +101,6 @@ export default { }); } }, - mounted() { this.$refs.root.addEventListener('us-load-context-popup', (e) => { const data = e.detail; @@ -116,7 +109,6 @@ export default { } }); }, - methods: { load(data) { this.loading = true; diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue index da53be3b46353..f95f28528d6a6 100644 --- a/web_src/js/components/DiffFileList.vue +++ b/web_src/js/components/DiffFileList.vue @@ -27,12 +27,9 @@ import {doLoadMoreFiles} from '../features/repo-diff.js'; const {pageData} = window.config; export default { - name: 'DiffFileList', - data: () => { return pageData.diffFileInfo; }, - watch: { fileListIsVisible(newValue) { if (newValue === true) { @@ -44,15 +41,12 @@ export default { } } }, - mounted() { document.getElementById('show-file-list-btn').addEventListener('click', this.toggleFileList); }, - unmounted() { document.getElementById('show-file-list-btn').removeEventListener('click', this.toggleFileList); }, - methods: { toggleFileList() { this.fileListIsVisible = !this.fileListIsVisible; diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue index 717ba70f7f6fd..e592574ac175c 100644 --- a/web_src/js/components/DiffFileTree.vue +++ b/web_src/js/components/DiffFileTree.vue @@ -21,15 +21,12 @@ const {pageData} = window.config; const LOCAL_STORAGE_KEY = 'diff_file_tree_visible'; export default { - name: 'DiffFileTree', components: {DiffFileTreeItem}, - data: () => { const fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) === 'true'; pageData.diffFileInfo.fileTreeIsVisible = fileTreeIsVisible; return pageData.diffFileInfo; }, - computed: { fileTree() { const result = []; @@ -94,7 +91,6 @@ export default { return result; } }, - mounted() { // ensure correct buttons when we are mounted to the dom this.adjustToggleButton(this.fileTreeIsVisible); @@ -125,7 +121,7 @@ export default { doLoadMoreFiles(this.link, this.diffEnd, () => { this.isLoadingNewData = false; }); - } + }, }, }; diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue index 4f20f1e66a289..4089e5141043a 100644 --- a/web_src/js/components/DiffFileTreeItem.vue +++ b/web_src/js/components/DiffFileTreeItem.vue @@ -43,11 +43,7 @@ import {SvgIcon} from '../svg.js'; export default { - name: 'DiffFileTreeItem', - components: { - SvgIcon, - }, - + components: {SvgIcon}, props: { item: { type: Object, @@ -59,7 +55,6 @@ export default { default: true } }, - data: () => ({ collapsed: false, }), diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index 1fec12dd5a0ad..1e0ebc5a47c2c 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -111,11 +111,7 @@ import {SvgIcon} from '../svg.js'; const {csrfToken, pageData} = window.config; export default { - name: 'PullRequestMergeForm', - components: { - SvgIcon, - }, - + components: {SvgIcon}, data: () => ({ csrfToken, mergeForm: pageData.pullRequestMergeForm, @@ -137,20 +133,17 @@ export default { showMergeStyleMenu: false, showActionForm: false, }), - computed: { mergeButtonStyleClass() { if (this.mergeForm.allOverridableChecksOk) return 'green'; return this.autoMergeWhenSucceed ? 'blue' : 'red'; } }, - watch: { mergeStyle(val) { this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val); } }, - created() { this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0); @@ -158,15 +151,12 @@ export default { if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name; this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow); }, - mounted() { document.addEventListener('mouseup', this.hideMergeStyleMenu); }, - unmounted() { document.removeEventListener('mouseup', this.hideMergeStyleMenu); }, - methods: { hideMergeStyleMenu() { this.showMergeStyleMenu = false; diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 75b96cb7811c4..f8486cdc6ca60 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -44,7 +44,7 @@ function fallbackCopyToClipboard(text) { // For all DOM elements with [data-clipboard-target] or [data-clipboard-text], // this copy-to-clipboard will work for them -export default function initGlobalCopyToClipboardListener() { +export function initGlobalCopyToClipboardListener() { document.addEventListener('click', (e) => { let target = e.target; // in case , so we just search diff --git a/web_src/js/features/colorpicker.js b/web_src/js/features/colorpicker.js index 11c5f26fa477e..a5fdb3f5a6e53 100644 --- a/web_src/js/features/colorpicker.js +++ b/web_src/js/features/colorpicker.js @@ -1,4 +1,4 @@ -export default async function createColorPicker($els) { +export async function createColorPicker($els) { if (!$els || !$els.length) return; await Promise.all([ diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 7efefd70840b6..2504f3be0acaf 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import 'jquery.are-you-sure'; import {mqBinarySearch} from '../utils.js'; -import createDropzone from './dropzone.js'; +import {createDropzone} from './dropzone.js'; import {initCompColorPicker} from './comp/ColorPicker.js'; import {showGlobalErrorMessage} from '../bootstrap.js'; import {attachDropdownAria} from './aria.js'; diff --git a/web_src/js/features/comp/ColorPicker.js b/web_src/js/features/comp/ColorPicker.js index 053fc6c059a9c..5665b7a24aae1 100644 --- a/web_src/js/features/comp/ColorPicker.js +++ b/web_src/js/features/comp/ColorPicker.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import createColorPicker from '../colorpicker.js'; +import {createColorPicker} from '../colorpicker.js'; export function initCompColorPicker() { createColorPicker($('.color-picker')); diff --git a/web_src/js/features/comp/EasyMDE.js b/web_src/js/features/comp/EasyMDE.js index 1f7fe45153cce..182e6b429df4e 100644 --- a/web_src/js/features/comp/EasyMDE.js +++ b/web_src/js/features/comp/EasyMDE.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import attachTribute from '../tribute.js'; +import {attachTribute} from '../tribute.js'; import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js'; /** diff --git a/web_src/js/features/comp/SearchUserBox.js b/web_src/js/features/comp/SearchUserBox.js index 46ecb8ebf4a92..0e9a005acf6e1 100644 --- a/web_src/js/features/comp/SearchUserBox.js +++ b/web_src/js/features/comp/SearchUserBox.js @@ -2,7 +2,6 @@ import $ from 'jquery'; import {htmlEscape} from 'escape-goat'; const {appSubUrl} = window.config; - const looksLikeEmailAddressCheck = /^\S+@\S+$/; export function initCompSearchUserBox() { diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js index 85a4f92809fa1..cda0fa39109e3 100644 --- a/web_src/js/features/comp/WebHookEditor.js +++ b/web_src/js/features/comp/WebHookEditor.js @@ -1,4 +1,5 @@ import $ from 'jquery'; + const {csrfToken} = window.config; export function initCompWebHookEditor() { diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js index d29da6d95141e..61f71209083c6 100644 --- a/web_src/js/features/contextpopup.js +++ b/web_src/js/features/contextpopup.js @@ -4,7 +4,7 @@ import ContextPopup from '../components/ContextPopup.vue'; import {parseIssueHref} from '../utils.js'; import {createTippy} from '../modules/tippy.js'; -export default function initContextPopups() { +export function initContextPopups() { const refIssues = $('.ref-issue'); if (!refIssues.length) return; diff --git a/web_src/js/features/copycontent.js b/web_src/js/features/copycontent.js index 9b791bedba569..5a4b99ae9b0d1 100644 --- a/web_src/js/features/copycontent.js +++ b/web_src/js/features/copycontent.js @@ -1,6 +1,7 @@ import {copyToClipboard} from './clipboard.js'; import {showTemporaryTooltip} from '../modules/tippy.js'; import {convertImage} from '../utils.js'; + const {i18n} = window.config; async function doCopy(content, btn) { diff --git a/web_src/js/features/dropzone.js b/web_src/js/features/dropzone.js index 1c80fb778cd5d..e7b8a9dde9e97 100644 --- a/web_src/js/features/dropzone.js +++ b/web_src/js/features/dropzone.js @@ -1,4 +1,4 @@ -export default async function createDropzone(el, opts) { +export async function createDropzone(el, opts) { const [{Dropzone}] = await Promise.all([ import(/* webpackChunkName: "dropzone" */'dropzone'), import(/* webpackChunkName: "dropzone" */'dropzone/dist/dropzone.css'), diff --git a/web_src/js/features/emoji.js b/web_src/js/features/emoji.js index 304c564f3d19a..d00ff6545658c 100644 --- a/web_src/js/features/emoji.js +++ b/web_src/js/features/emoji.js @@ -1,7 +1,6 @@ import emojis from '../../../assets/emoji.json'; -const {assetUrlPrefix} = window.config; -const {customEmojis} = window.config; +const {assetUrlPrefix, customEmojis} = window.config; const tempMap = {...customEmojis}; for (const {emoji, aliases} of emojis) { diff --git a/web_src/js/features/file-fold.js b/web_src/js/features/file-fold.js index 5e714a1de8da7..0d5be7cf60d28 100644 --- a/web_src/js/features/file-fold.js +++ b/web_src/js/features/file-fold.js @@ -1,6 +1,5 @@ import {svg} from '../svg.js'; - // Hides the file if newFold is true, and shows it otherwise. The actual hiding is performed using CSS. // // The fold arrow is the icon displayed on the upper left of the file box, especially intended for components having the 'fold-file' class. diff --git a/web_src/js/features/formatting.js b/web_src/js/features/formatting.js index c8f5db9e14505..837e323376041 100644 --- a/web_src/js/features/formatting.js +++ b/web_src/js/features/formatting.js @@ -1,7 +1,6 @@ import {prettyNumber} from '../utils.js'; const {lang} = document.documentElement; - const dateFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'long', day: 'numeric'}); const shortDateFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'short', day: 'numeric'}); const dateTimeFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric'}); diff --git a/web_src/js/features/heatmap.js b/web_src/js/features/heatmap.js index 368ddd0d77f10..f80089ee434eb 100644 --- a/web_src/js/features/heatmap.js +++ b/web_src/js/features/heatmap.js @@ -1,7 +1,8 @@ import {createApp} from 'vue'; import ActivityHeatmap from '../components/ActivityHeatmap.vue'; import {translateMonth, translateDay} from '../utils.js'; -export default function initHeatmap() { + +export function initHeatmap() { const el = document.getElementById('user-heatmap'); if (!el) return; diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 0b021d070fb05..03ae3b047bf88 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -34,7 +34,7 @@ function getDefaultSvgBoundsIfUndefined(svgXml, src) { return null; } -export default function initImageDiff() { +export function initImageDiff() { function createContext(image1, image2) { const size1 = { width: image1 && image1.width || 0, diff --git a/web_src/js/features/repo-diff-filetree.js b/web_src/js/features/repo-diff-filetree.js index 6059dd82e7492..5dd2c42e74e29 100644 --- a/web_src/js/features/repo-diff-filetree.js +++ b/web_src/js/features/repo-diff-filetree.js @@ -2,7 +2,7 @@ import {createApp} from 'vue'; import DiffFileTree from '../components/DiffFileTree.vue'; import DiffFileList from '../components/DiffFileList.vue'; -export default function initDiffFileTree() { +export function initDiffFileTree() { const el = document.getElementById('diff-file-tree'); if (!el) return; diff --git a/web_src/js/features/repo-findfile.js b/web_src/js/features/repo-findfile.js index 750b906cef7b8..7b8833e793c69 100644 --- a/web_src/js/features/repo-findfile.js +++ b/web_src/js/features/repo-findfile.js @@ -1,6 +1,6 @@ import $ from 'jquery'; - import {svg} from '../svg.js'; + const {csrf} = window.config; const threshold = 50; diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js index f27a986621dfc..16d35e66f2734 100644 --- a/web_src/js/features/repo-graph.js +++ b/web_src/js/features/repo-graph.js @@ -1,6 +1,6 @@ import $ from 'jquery'; -export default function initRepoGraphGit() { +export function initRepoGraphGit() { const graphContainer = document.getElementById('git-graph-container'); if (!graphContainer) return; diff --git a/web_src/js/features/repo-issue-pr-form.js b/web_src/js/features/repo-issue-pr-form.js index 59d4c7a3b4177..7b26e643c04af 100644 --- a/web_src/js/features/repo-issue-pr-form.js +++ b/web_src/js/features/repo-issue-pr-form.js @@ -1,7 +1,7 @@ import {createApp} from 'vue'; import PullRequestMergeForm from '../components/PullRequestMergeForm.vue'; -export default function initPullRequestMergeForm() { +export function initRepoPullRequestMergeForm() { const el = document.getElementById('pull-request-merge-form'); if (!el) return; diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index ca5d69c5a6d7a..56d294e81afc2 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import {htmlEscape} from 'escape-goat'; -import attachTribute from './tribute.js'; +import {attachTribute} from './tribute.js'; import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js'; import {initEasyMDEImagePaste} from './comp/ImagePaste.js'; import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 910d4bb56c8db..37366578e2d27 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -3,33 +3,28 @@ import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js'; import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; import {initEasyMDEImagePaste} from './comp/ImagePaste.js'; import { - initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, - initRepoIssueCommentDelete, - initRepoIssueComments, initRepoIssueDependencyDelete, - initRepoIssueReferenceIssue, initRepoIssueStatusButton, - initRepoIssueTitleEdit, - initRepoIssueWipToggle, initRepoPullRequestUpdate, - updateIssuesMeta, + initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete, + initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue, + initRepoIssueStatusButton, initRepoIssueTitleEdit, initRepoIssueWipToggle, + initRepoPullRequestUpdate, updateIssuesMeta, } from './repo-issue.js'; import {initUnicodeEscapeButton} from './repo-unicode-escape.js'; import {svg} from '../svg.js'; import {htmlEscape} from 'escape-goat'; import {initRepoBranchTagDropdown} from '../components/RepoBranchTagDropdown.js'; import { - initRepoCloneLink, - initRepoCommonBranchOrTagDropdown, - initRepoCommonFilterSearchDropdown, + initRepoCloneLink, initRepoCommonBranchOrTagDropdown, initRepoCommonFilterSearchDropdown, initRepoCommonLanguageStats, } from './repo-common.js'; import {initCitationFileCopyContent} from './citation.js'; import {initCompLabelEdit} from './comp/LabelEdit.js'; import {initRepoDiffConversationNav} from './repo-diff.js'; -import attachTribute from './tribute.js'; -import createDropzone from './dropzone.js'; +import {attachTribute} from './tribute.js'; +import {createDropzone} from './dropzone.js'; import {initCommentContent, initMarkupContent} from '../markup/content.js'; import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoSettingBranches} from './repo-settings.js'; -import initRepoPullRequestMergeForm from './repo-issue-pr-form.js'; +import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js'; const {csrfToken} = window.config; diff --git a/web_src/js/features/repo-migration.js b/web_src/js/features/repo-migration.js index ece01e53bd72f..c317c7245c4fe 100644 --- a/web_src/js/features/repo-migration.js +++ b/web_src/js/features/repo-migration.js @@ -10,7 +10,7 @@ const $lfsSettings = $('#lfs_settings'); const $lfsEndpoint = $('#lfs_endpoint'); const $items = $('#migrate_items').find('input[type=checkbox]'); -export default function initRepoMigration() { +export function initRepoMigration() { checkAuth(); setLFSSettingsVisibility(); diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js index b5a720c9d7fb0..f6d6c89816c7a 100644 --- a/web_src/js/features/repo-projects.js +++ b/web_src/js/features/repo-projects.js @@ -84,7 +84,7 @@ async function initRepoProjectSortable() { } } -export default function initRepoProject() { +export function initRepoProject() { if (!$('.repository.projects').length) { return; } diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js index b68a7a6cd5303..e84cc53d17312 100644 --- a/web_src/js/features/repo-release.js +++ b/web_src/js/features/repo-release.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import attachTribute from './tribute.js'; +import {attachTribute} from './tribute.js'; import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; import {initEasyMDEImagePaste} from './comp/ImagePaste.js'; import {createCommentEasyMDE} from './comp/EasyMDE.js'; diff --git a/web_src/js/features/serviceworker.js b/web_src/js/features/serviceworker.js index a072811b04c24..32d2e04cd67ab 100644 --- a/web_src/js/features/serviceworker.js +++ b/web_src/js/features/serviceworker.js @@ -35,7 +35,7 @@ async function checkCacheValidity() { } } -export default async function initServiceWorker() { +export async function initServiceWorker() { if (!('serviceWorker' in navigator)) return; if (useServiceWorker) { diff --git a/web_src/js/features/tablesort.js b/web_src/js/features/tablesort.js index 1fc2a4bd56b8c..436fe0a594850 100644 --- a/web_src/js/features/tablesort.js +++ b/web_src/js/features/tablesort.js @@ -1,4 +1,4 @@ -export default function initTableSort() { +export function initTableSort() { for (const header of document.querySelectorAll('th[data-sortt-asc]') || []) { const sorttAsc = header.getAttribute('data-sortt-asc'); const sorttDesc = header.getAttribute('data-sortt-desc'); diff --git a/web_src/js/features/tribute.js b/web_src/js/features/tribute.js index dcee7aa4a3ba3..94f3512a2e041 100644 --- a/web_src/js/features/tribute.js +++ b/web_src/js/features/tribute.js @@ -49,7 +49,7 @@ function makeCollections({mentions, emoji}) { return collections; } -export default async function attachTribute(elementOrNodeList, {mentions, emoji} = {}) { +export async function attachTribute(elementOrNodeList, {mentions, emoji} = {}) { if (!window.config.requireTribute || !elementOrNodeList) return; const nodes = Array.from('length' in elementOrNodeList ? elementOrNodeList : [elementOrNodeList]); if (!nodes.length) return; diff --git a/web_src/js/index.js b/web_src/js/index.js index f4638a60e09a7..a866184203fd6 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -6,16 +6,16 @@ import {initVueEnv} from './components/VueComponentLoader.js'; import {initRepoActivityTopAuthorsChart} from './components/RepoActivityTopAuthors.vue'; import {initDashboardRepoList} from './components/DashboardRepoList.js'; -import attachTribute from './features/tribute.js'; -import initGlobalCopyToClipboardListener from './features/clipboard.js'; -import initContextPopups from './features/contextpopup.js'; -import initRepoGraphGit from './features/repo-graph.js'; -import initHeatmap from './features/heatmap.js'; -import initImageDiff from './features/imagediff.js'; -import initRepoMigration from './features/repo-migration.js'; -import initRepoProject from './features/repo-projects.js'; -import initServiceWorker from './features/serviceworker.js'; -import initTableSort from './features/tablesort.js'; +import {attachTribute} from './features/tribute.js'; +import {initGlobalCopyToClipboardListener} from './features/clipboard.js'; +import {initContextPopups} from './features/contextpopup.js'; +import {initRepoGraphGit} from './features/repo-graph.js'; +import {initHeatmap} from './features/heatmap.js'; +import {initImageDiff} from './features/imagediff.js'; +import {initRepoMigration} from './features/repo-migration.js'; +import {initRepoProject} from './features/repo-projects.js'; +import {initServiceWorker} from './features/serviceworker.js'; +import {initTableSort} from './features/tablesort.js'; import {initAdminUserListSearchForm} from './features/admin/users.js'; import {initAdminConfigs} from './features/admin/config.js'; import {initMarkupAnchors} from './markup/anchors.js'; @@ -24,7 +24,7 @@ import {initRepoIssueContentHistory} from './features/repo-issue-content.js'; import {initStopwatch} from './features/stopwatch.js'; import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; -import initDiffFileTree from './features/repo-diff-filetree.js'; +import {initDiffFileTree} from './features/repo-diff-filetree.js'; import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js'; import { diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 60dd49f8bf0e2..0f08f64e80314 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -26,7 +26,6 @@ import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.sv import octiconTriangleDown from '../../public/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/img/svg/octicon-x.svg'; - export const svgs = { 'octicon-chevron-down': octiconChevronDown, 'octicon-chevron-right': octiconChevronRight, @@ -57,7 +56,6 @@ export const svgs = { 'octicon-x': octiconX, }; - const parser = new DOMParser(); const serializer = new XMLSerializer(); From 2b0b56319e94b7173b566af4d082f03664853789 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 24 Dec 2022 02:34:51 +0800 Subject: [PATCH 3/5] Improve testing for pgsql empty repository (#22223) --- tests/integration/empty_repo_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go index 4bc1d811bf3ba..1a83de12923de 100644 --- a/tests/integration/empty_repo_test.go +++ b/tests/integration/empty_repo_test.go @@ -11,6 +11,8 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" ) func TestEmptyRepo(t *testing.T) { @@ -21,7 +23,8 @@ func TestEmptyRepo(t *testing.T) { "commit/1ae57b34ccf7e18373", "graph", } - emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{}, unittest.Cond("is_empty = ?", true)) + emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5}) + assert.True(t, emptyRepo.IsEmpty) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: emptyRepo.OwnerID}) for _, subpath := range subpaths { req := NewRequestf(t, "GET", "/%s/%s/%s", owner.Name, emptyRepo.Name, subpath) From 3bd49f780157494a3ff923a3bcdbb8696a688173 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 25 Dec 2022 00:19:36 +0000 Subject: [PATCH 4/5] [skip ci] Updated licenses and gitignores --- options/gitignore/Godot | 4 ++++ options/license/Bitstream-Charter | 9 ++++++++ options/license/Graphics-Gems | 5 +++++ options/license/IJG-short | 35 +++++++++++++++++++++++++++++++ options/license/MIT-Wu | 28 +++++++++++++++++++++++++ options/license/TPDL | 2 ++ options/license/TTWL | 8 +++++++ 7 files changed, 91 insertions(+) create mode 100644 options/license/Bitstream-Charter create mode 100644 options/license/Graphics-Gems create mode 100644 options/license/IJG-short create mode 100644 options/license/MIT-Wu create mode 100644 options/license/TPDL create mode 100644 options/license/TTWL diff --git a/options/gitignore/Godot b/options/gitignore/Godot index 4f48ad79f8fb7..d9aac213e776e 100644 --- a/options/gitignore/Godot +++ b/options/gitignore/Godot @@ -1,3 +1,6 @@ +# Godot 4+ specific ignores +.godot/ + # Godot-specific ignores .import/ export.cfg @@ -9,3 +12,4 @@ export_presets.cfg # Mono-specific ignores .mono/ data_*/ +mono_crash.*.json diff --git a/options/license/Bitstream-Charter b/options/license/Bitstream-Charter new file mode 100644 index 0000000000000..7a0cf97a0c980 --- /dev/null +++ b/options/license/Bitstream-Charter @@ -0,0 +1,9 @@ +(c) Copyright 1989-1992, Bitstream Inc., Cambridge, MA. + +You are hereby granted permission under all Bitstream propriety rights +to use, copy, modify, sublicense, sell, and redistribute the 4 Bitstream +Charter (r) Type 1 outline fonts and the 4 Courier Type 1 outline fonts for +any purpose and without restriction; provided, that this notice is left +intact on all copies of such fonts and that Bitstream's trademark is acknowledged +as shown below on all unmodified copies of the 4 Charter Type 1 fonts. +BITSTREAM CHARTER is a registered trademark of Bitstream Inc. diff --git a/options/license/Graphics-Gems b/options/license/Graphics-Gems new file mode 100644 index 0000000000000..ec28c46563614 --- /dev/null +++ b/options/license/Graphics-Gems @@ -0,0 +1,5 @@ +LICENSE + +This code repository predates the concept of Open Source, and predates most licenses along such lines. As such, the official license truly is: + +EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes with no guarantee. diff --git a/options/license/IJG-short b/options/license/IJG-short new file mode 100644 index 0000000000000..bbb0859d804dc --- /dev/null +++ b/options/license/IJG-short @@ -0,0 +1,35 @@ +The authors make NO WARRANTY or representation, either express or +implied, with respect to this software, its quality, accuracy, +merchantability, or fitness for a particular purpose. This software is +provided "AS IS", and you, its user, assume the entire risk as to its +quality and accuracy. + +This software is copyright (C) 1991, 1992, Thomas G. Lane. All Rights +Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to +these conditions: + +(1) If any part of the source code for this software +is distributed, then this README file must be included, with this +copyright and no-warranty notice unaltered; and any additions, +deletions, or changes to the original files must be clearly indicated +in accompanying documentation. + +(2) If only executable code is +distributed, then the accompanying documentation must state that "this +software is based in part on the work of the Independent JPEG Group". + +(3) Permission for use of this software is granted only if the user +accepts full responsibility for any undesirable consequences; the +authors accept NO LIABILITY for damages of any kind. + +Permission is NOT granted for the use of any IJG author's name or +company name in advertising or publicity relating to this software or +products derived from it. This software may be referred to only as +"the Independent JPEG Group's software". + +We specifically permit and encourage the use of this software as the +basis of commercial products, provided that all warranty or liability +claims are assumed by the product vendor. diff --git a/options/license/MIT-Wu b/options/license/MIT-Wu new file mode 100644 index 0000000000000..86eec3c517a91 --- /dev/null +++ b/options/license/MIT-Wu @@ -0,0 +1,28 @@ +Copyright (c) 2003-2005 Tom Wu +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF +THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +In addition, the following condition applies: + +All redistributions must retain an intact copy of this copyright notice +and disclaimer. diff --git a/options/license/TPDL b/options/license/TPDL new file mode 100644 index 0000000000000..d950f8f19e592 --- /dev/null +++ b/options/license/TPDL @@ -0,0 +1,2 @@ +Copyright (C) 1996-2010 David Muir Sharnoff. Copyright (C) 2011 Google, Inc. +License hereby granted for anyone to use, modify or redistribute this module at their own risk. Please feed useful changes back to cpan@dave.sharnoff.org. diff --git a/options/license/TTWL b/options/license/TTWL new file mode 100644 index 0000000000000..c13d3fbe04a48 --- /dev/null +++ b/options/license/TTWL @@ -0,0 +1,8 @@ +Copyright (C) 1996-2002,2005,2006 David Muir Sharnoff. +Copyright (C) 2005 Aristotle Pagaltzis +Copyright (C) 2012-2013 Google, Inc. + +This module may be modified, used, copied, and redistributed at your own risk. +Although allowed by the preceding license, please do not publicly +redistribute modified versions of this code with the name "Text::Tabs" +unless it passes the unmodified Text::Tabs test suite. From f5cd0d93192fc4d04966ba971c7aaaae7475926d Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 25 Dec 2022 18:17:48 +0100 Subject: [PATCH 5/5] Add Mermaid copy button, avoid unnecessary tooltip hide (#22225) - Add Copy button to mermaid diagrams which copies their source. - Set tippy to not hide on click and avoid tooltip re-creation for temporary tooltips. This avoids hide and show when copying repo url. Popovers still hide the tooltip as usual. Screenshot 2022-12-23 at 14 02 32 Co-authored-by: Lauris BH Co-authored-by: KN4CK3R --- web_src/js/markup/codecopy.js | 13 ++++++++----- web_src/js/markup/mermaid.js | 10 +++++++++- web_src/js/modules/tippy.js | 3 ++- web_src/less/markup/codecopy.less | 6 ++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/web_src/js/markup/codecopy.js b/web_src/js/markup/codecopy.js index 2aa7070c72ded..a12802ef73479 100644 --- a/web_src/js/markup/codecopy.js +++ b/web_src/js/markup/codecopy.js @@ -1,15 +1,18 @@ import {svg} from '../svg.js'; -export function renderCodeCopy() { - const els = document.querySelectorAll('.markup .code-block code'); - if (!els.length) return; - +export function makeCodeCopyButton() { const button = document.createElement('button'); button.classList.add('code-copy', 'ui', 'button'); button.innerHTML = svg('octicon-copy'); + return button; +} + +export function renderCodeCopy() { + const els = document.querySelectorAll('.markup .code-block code'); + if (!els.length) return; for (const el of els) { - const btn = button.cloneNode(true); + const btn = makeCodeCopyButton(); btn.setAttribute('data-clipboard-text', el.textContent); el.after(btn); } diff --git a/web_src/js/markup/mermaid.js b/web_src/js/markup/mermaid.js index efd9abbb56920..29fa92552dca0 100644 --- a/web_src/js/markup/mermaid.js +++ b/web_src/js/markup/mermaid.js @@ -1,4 +1,6 @@ import {isDarkTheme} from '../utils.js'; +import {makeCodeCopyButton} from './codecopy.js'; + const {mermaidMaxSourceCharacters} = window.config; const iframeCss = ` @@ -58,7 +60,13 @@ export async function renderMermaid() { iframe.sandbox = 'allow-scripts'; iframe.style.height = `${Math.ceil(parseFloat(heightStr))}px`; iframe.srcdoc = `${svgStr}`; - el.closest('pre').replaceWith(iframe); + const mermaidBlock = document.createElement('div'); + mermaidBlock.classList.add('mermaid-block'); + mermaidBlock.append(iframe); + const btn = makeCodeCopyButton(); + btn.setAttribute('data-clipboard-text', source); + mermaidBlock.append(btn); + el.closest('pre').replaceWith(mermaidBlock); }); } catch (err) { displayError(el, err); diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 6a89151691a97..ce8f0369f11c9 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -6,6 +6,7 @@ export function createTippy(target, opts = {}) { placement: target.getAttribute('data-placement') || 'top-start', animation: false, allowHTML: false, + hideOnClick: false, interactiveBorder: 30, ignoreAttributes: true, maxWidth: 500, // increase over default 350px @@ -46,7 +47,7 @@ export function showTemporaryTooltip(target, content) { } tippy.setContent(content); - tippy.show(); + if (!tippy.state.isShown) tippy.show(); tippy.setProps({ onHidden: (tippy) => { if (oldContent) { diff --git a/web_src/less/markup/codecopy.less b/web_src/less/markup/codecopy.less index b2ce77aaa157b..f6f9894fc14bc 100644 --- a/web_src/less/markup/codecopy.less +++ b/web_src/less/markup/codecopy.less @@ -1,4 +1,5 @@ -.markup .code-block { +.markup .code-block, +.markup .mermaid-block { position: relative; } @@ -26,7 +27,8 @@ background: var(--color-secondary-dark-1) !important; } -.markup .code-block:hover .code-copy { +.markup .code-block:hover .code-copy, +.markup .mermaid-block:hover .code-copy { visibility: visible; animation: fadein .2s both; }